summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2019-11-11 21:22:20 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2019-11-11 21:22:20 +0000
commit25a7e2b566d096c3863e968f8cb553a5a862e353 (patch)
treea26da1d669e88b34b10b0f6c76b06abd8b694473
parenta640a99bcc56aa9626b60250e6806d7dd5d4cb80 (diff)
parenta19d32b0216a289c888612cdd078679b850d7218 (diff)
downloadart-android10-mainline-resolv-release.tar.gz
Snap for 6001391 from a19d32b0216a289c888612cdd078679b850d7218 to qt-aml-resolv-releaseandroid-mainline-10.0.0_r8android10-mainline-resolv-release
Change-Id: I01ad349d71688759e45df341e3827c0af26a590a
-rw-r--r--CleanSpec.mk5
-rw-r--r--adbconnection/Android.bp21
-rw-r--r--adbconnection/adbconnection_server.cc6
-rw-r--r--adbconnection/libadbconnection_server.map.txt22
-rw-r--r--build/Android.bp3
-rw-r--r--build/Android.oat.mk77
-rw-r--r--build/apex/Android.bp47
-rwxr-xr-xbuild/apex/art_apex_test.py111
-rw-r--r--build/apex/art_postinstall_hook.sh48
-rw-r--r--build/apex/art_preinstall_hook.sh49
-rw-r--r--build/apex/art_preinstall_hook_boot.sh73
-rw-r--r--build/apex/art_preinstall_hook_system_server.sh84
-rw-r--r--build/apex/art_prepostinstall_utils.sh70
-rw-r--r--build/apex/manifest-art.json4
-rw-r--r--build/art.go14
-rw-r--r--compiler/optimizing/code_generator_arm64.cc30
-rw-r--r--compiler/optimizing/code_generator_arm64.h12
-rw-r--r--compiler/optimizing/code_generator_vector_x86.cc31
-rw-r--r--compiler/optimizing/code_generator_vector_x86_64.cc31
-rw-r--r--compiler/optimizing/common_arm64.h24
-rw-r--r--compiler/optimizing/intrinsics_arm64.cc40
-rw-r--r--compiler/optimizing/intrinsics_arm_vixl.cc1
-rw-r--r--compiler/optimizing/intrinsics_mips.cc1
-rw-r--r--compiler/optimizing/intrinsics_mips64.cc1
-rw-r--r--compiler/optimizing/intrinsics_x86.cc1
-rw-r--r--compiler/optimizing/intrinsics_x86_64.cc1
-rw-r--r--compiler/optimizing/loop_optimization.cc12
-rw-r--r--compiler/optimizing/scheduler.cc38
-rw-r--r--compiler/optimizing/scheduler.h23
-rw-r--r--compiler/utils/arm64/assembler_arm64.h8
-rw-r--r--compiler/utils/arm64/jni_macro_assembler_arm64.cc4
-rw-r--r--compiler/utils/x86/assembler_x86.cc14
-rw-r--r--compiler/utils/x86/assembler_x86.h1
-rw-r--r--compiler/utils/x86/assembler_x86_test.cc5
-rw-r--r--compiler/utils/x86_64/assembler_x86_64.cc33
-rw-r--r--compiler/utils/x86_64/assembler_x86_64.h1
-rw-r--r--compiler/utils/x86_64/assembler_x86_64_test.cc5
-rw-r--r--dex2oat/dex2oat.cc179
-rw-r--r--dex2oat/dex2oat_image_test.cc286
-rw-r--r--dex2oat/dex2oat_test.cc91
-rw-r--r--dex2oat/linker/image_test.cc2
-rw-r--r--dex2oat/linker/image_test.h6
-rw-r--r--dex2oat/linker/image_writer.cc18
-rw-r--r--dex2oat/linker/oat_writer.cc4
-rw-r--r--dex2oat/linker/oat_writer.h2
-rw-r--r--dex2oat/linker/oat_writer_test.cc1
-rw-r--r--dex2oat/linker/relative_patcher_test.h1
-rw-r--r--libartbase/Android.bp2
-rw-r--r--libartbase/base/hiddenapi_flags.h11
-rw-r--r--libartbase/base/memfd.h38
-rw-r--r--libartbase/base/utils.cc19
-rw-r--r--libartbase/base/utils.h3
-rw-r--r--libartpalette/Android.bp4
-rw-r--r--libdexfile/Android.bp7
-rw-r--r--libdexfile/dex/modifiers.h2
-rw-r--r--libelffile/elf/elf_builder.h14
-rw-r--r--openjdkjvmti/events-inl.h99
-rw-r--r--openjdkjvmti/events.cc5
-rw-r--r--openjdkjvmti/events.h20
-rw-r--r--openjdkjvmti/ti_class.cc3
-rw-r--r--openjdkjvmti/ti_class_definition.h16
-rw-r--r--openjdkjvmti/ti_extension.cc69
-rw-r--r--openjdkjvmti/ti_heap.cc5
-rw-r--r--openjdkjvmti/ti_redefine.cc207
-rw-r--r--openjdkjvmti/ti_redefine.h15
-rw-r--r--openjdkjvmti/transform.cc48
-rw-r--r--openjdkjvmti/transform.h2
-rw-r--r--runtime/Android.bp2
-rw-r--r--runtime/art_field.h4
-rw-r--r--runtime/art_method-inl.h1
-rw-r--r--runtime/art_method.h157
-rw-r--r--runtime/class_linker.cc3
-rw-r--r--runtime/dexopt_test.cc31
-rw-r--r--runtime/entrypoints/quick/quick_trampoline_entrypoints.cc5
-rw-r--r--runtime/gc/collector/concurrent_copying.cc8
-rw-r--r--runtime/gc/collector/immune_spaces_test.cc2
-rw-r--r--runtime/gc/heap-inl.h12
-rw-r--r--runtime/gc/heap.cc43
-rw-r--r--runtime/gc/heap.h17
-rw-r--r--runtime/gc/space/image_space.cc1501
-rw-r--r--runtime/gc/space/image_space.h105
-rw-r--r--runtime/hidden_api.cc21
-rw-r--r--runtime/hidden_api.h1
-rw-r--r--runtime/hidden_api_test.cc52
-rw-r--r--runtime/image.cc13
-rw-r--r--runtime/image.h23
-rw-r--r--runtime/interpreter/interpreter_intrinsics.cc1
-rw-r--r--runtime/intrinsics_list.h1
-rw-r--r--runtime/jit/jit.cc269
-rw-r--r--runtime/jit/jit.h35
-rw-r--r--runtime/jit/jit_code_cache.cc19
-rw-r--r--runtime/jit/jit_code_cache.h40
-rw-r--r--runtime/jit/jit_memory_region.cc74
-rw-r--r--runtime/method_handles.cc10
-rw-r--r--runtime/mirror/class.cc18
-rw-r--r--runtime/mirror/class.h7
-rw-r--r--runtime/mirror/dex_cache.cc18
-rw-r--r--runtime/mirror/method_handle_impl-inl.h6
-rw-r--r--runtime/mirror/method_handle_impl.h2
-rw-r--r--runtime/native/dalvik_system_ZygoteHooks.cc44
-rw-r--r--runtime/oat.cc5
-rw-r--r--runtime/oat.h29
-rw-r--r--runtime/oat_file.cc20
-rw-r--r--runtime/oat_file.h26
-rw-r--r--runtime/oat_file_assistant.cc101
-rw-r--r--runtime/oat_file_assistant.h2
-rw-r--r--runtime/oat_file_assistant_test.cc1
-rw-r--r--runtime/oat_file_manager.cc2
-rw-r--r--runtime/reflective_value_visitor.h4
-rw-r--r--runtime/runtime.cc27
-rw-r--r--runtime/runtime.h16
-rw-r--r--runtime/thread.cc12
-rw-r--r--runtime/vdex_file.cc11
-rw-r--r--runtime/verifier/class_verifier.cc85
-rw-r--r--runtime/verifier/class_verifier.h36
-rw-r--r--runtime/verifier/method_verifier.cc22
-rw-r--r--runtime/verifier/method_verifier.h14
-rwxr-xr-xtest/1948-obsolete-const-method-handle/util-src/build-classes2
-rw-r--r--test/1988-multi-structural-redefine/expected.txt5
-rw-r--r--test/1988-multi-structural-redefine/info.txt1
-rwxr-xr-xtest/1988-multi-structural-redefine/run18
-rw-r--r--test/1988-multi-structural-redefine/src/Main.java21
l---------test/1988-multi-structural-redefine/src/art/Redefinition.java1
-rw-r--r--test/1988-multi-structural-redefine/src/art/Test1988.java126
-rw-r--r--test/1990-structural-bad-verify/expected.txt2
-rw-r--r--test/1990-structural-bad-verify/info.txt6
-rwxr-xr-xtest/1990-structural-bad-verify/run17
-rw-r--r--test/1990-structural-bad-verify/src/Main.java21
l---------test/1990-structural-bad-verify/src/art/Redefinition.java1
-rw-r--r--test/1990-structural-bad-verify/src/art/Test1990.java123
-rw-r--r--test/1991-hello-structural-retransform/expected.txt2
-rw-r--r--test/1991-hello-structural-retransform/info.txt1
-rwxr-xr-xtest/1991-hello-structural-retransform/run17
-rw-r--r--test/1991-hello-structural-retransform/src/Main.java21
l---------test/1991-hello-structural-retransform/src/art/Redefinition.java1
-rw-r--r--test/1991-hello-structural-retransform/src/art/Test1991.java79
-rw-r--r--test/1992-retransform-no-such-field/expected.txt2
-rw-r--r--test/1992-retransform-no-such-field/info.txt1
-rwxr-xr-xtest/1992-retransform-no-such-field/run17
-rw-r--r--test/1992-retransform-no-such-field/src/Main.java21
l---------test/1992-retransform-no-such-field/src/art/Redefinition.java1
-rw-r--r--test/1992-retransform-no-such-field/src/art/Test1992.java89
-rw-r--r--test/1993-fallback-non-structural/expected.txt3
-rw-r--r--test/1993-fallback-non-structural/info.txt4
-rwxr-xr-xtest/1993-fallback-non-structural/run17
-rw-r--r--test/1993-fallback-non-structural/src/Main.java21
l---------test/1993-fallback-non-structural/src/art/Redefinition.java1
-rw-r--r--test/1993-fallback-non-structural/src/art/Test1993.java77
-rw-r--r--test/1997-structural-shadow-method/expected.txt6
-rw-r--r--test/1997-structural-shadow-method/info.txt1
-rwxr-xr-xtest/1997-structural-shadow-method/run17
-rw-r--r--test/1997-structural-shadow-method/src/Main.java21
l---------test/1997-structural-shadow-method/src/art/Redefinition.java1
-rw-r--r--test/1997-structural-shadow-method/src/art/Test1997.java84
-rw-r--r--test/1998-structural-shadow-field/expected.txt4
-rw-r--r--test/1998-structural-shadow-field/info.txt1
-rwxr-xr-xtest/1998-structural-shadow-field/run17
-rw-r--r--test/1998-structural-shadow-field/src/Main.java21
l---------test/1998-structural-shadow-field/src/art/Redefinition.java1
-rw-r--r--test/1998-structural-shadow-field/src/art/Test1998.java65
-rw-r--r--test/580-fp16/src-art/Main.java70
-rw-r--r--test/684-checker-simd-dotprod/src/Main.java2
-rw-r--r--test/684-checker-simd-dotprod/src/other/TestFloatDouble.java93
-rw-r--r--test/706-checker-scheduler/src/Main.java119
-rw-r--r--test/906-iterate-heap/src/art/Test906.java9
-rw-r--r--test/956-methodhandles/src/Main.java48
-rw-r--r--test/Android.bp110
-rw-r--r--test/common/runtime_state.cc1
-rw-r--r--test/jvmti-common/Redefinition.java24
-rw-r--r--test/knownfailures.json16
-rwxr-xr-xtest/run-test4
-rw-r--r--test/ti-agent/jvmti_helper.cc20
-rw-r--r--test/ti-agent/jvmti_helper.h2
-rw-r--r--test/ti-agent/redefinition_helper.cc127
-rwxr-xr-xtools/generate_cmake_lists.py2
-rw-r--r--tools/jvmti-agents/ti-alloc-sample/Android.bp73
-rw-r--r--tools/jvmti-agents/ti-alloc-sample/README.md79
-rwxr-xr-xtools/jvmti-agents/ti-alloc-sample/mkflame.py213
-rw-r--r--tools/jvmti-agents/ti-alloc-sample/ti_alloc_sample.cc461
-rw-r--r--tools/libcore_no_getrandom_failures.txt15
180 files changed, 6015 insertions, 1519 deletions
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 43204fd597..5aff4cfbff 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -97,6 +97,11 @@ $(call add-clean-step, rm -rf $(HOST_OUT)/apex)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/apex)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/symbols/apex)
+# Remove dex2oat artifacts for boot image extensions (workaround for broken dependencies).
+$(call add-clean-step, find $(OUT_DIR) -name "*.oat" -o -name "*.odex" -o -name "*.art" -o -name '*.vdex' | xargs rm -f)
+$(call add-clean-step, find $(OUT_DIR) -name "*.oat" -o -name "*.odex" -o -name "*.art" -o -name '*.vdex' | xargs rm -f)
+$(call add-clean-step, find $(OUT_DIR) -name "*.oat" -o -name "*.odex" -o -name "*.art" -o -name '*.vdex' | xargs rm -f)
+
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
diff --git a/adbconnection/Android.bp b/adbconnection/Android.bp
index da73619f00..239cecb2b9 100644
--- a/adbconnection/Android.bp
+++ b/adbconnection/Android.bp
@@ -55,15 +55,28 @@ art_cc_library {
],
}
+// We export a library to do the server-side socket handling that gets loaded
+// by adbd from the art apex, so that we can update the socket handling
+// independently from the adbd apex.
cc_library {
name: "libadbconnection_server",
+ srcs: ["adbconnection_server.cc"],
+
+ export_include_dirs: ["include"],
+
+ stl: "libc++_static",
+ shared_libs: ["liblog"],
+ whole_static_libs: ["libbase"],
+
+ defaults: ["art_defaults"],
visibility: [
- // TODO(b/133140750): Clean this up.
"//system/core/adb",
],
- srcs: ["adbconnection_server.cc"],
- export_include_dirs: ["include"],
- shared_libs: ["libbase"],
+ stubs: {
+ symbol_file: "libadbconnection_server.map.txt",
+ versions: ["1"],
+ },
+
host_supported: true,
recovery_available: true,
}
diff --git a/adbconnection/adbconnection_server.cc b/adbconnection/adbconnection_server.cc
index 9c1aff913c..f69f4a7196 100644
--- a/adbconnection/adbconnection_server.cc
+++ b/adbconnection/adbconnection_server.cc
@@ -72,12 +72,12 @@ void adbconnection_listen(void (*callback)(int fd, pid_t pid)) {
}
while (true) {
- int rc = TEMP_FAILURE_RETRY(epoll_wait(epfd.get(), events.data(), events.size(), -1));
- if (rc == -1) {
+ int epoll_rc = TEMP_FAILURE_RETRY(epoll_wait(epfd.get(), events.data(), events.size(), -1));
+ if (epoll_rc == -1) {
PLOG(FATAL) << "epoll_wait failed";
}
- for (int i = 0; i < rc; ++i) {
+ for (int i = 0; i < epoll_rc; ++i) {
const epoll_event& event = events[i];
if (event.data.fd == -1) {
unique_fd client(TEMP_FAILURE_RETRY(
diff --git a/adbconnection/libadbconnection_server.map.txt b/adbconnection/libadbconnection_server.map.txt
new file mode 100644
index 0000000000..b631581031
--- /dev/null
+++ b/adbconnection/libadbconnection_server.map.txt
@@ -0,0 +1,22 @@
+#
+# Copyright (C) 2019 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.
+#
+
+LIBADBCONNECTION_SERVER_1 {
+ global:
+ adbconnection_listen;
+ local:
+ *;
+};
diff --git a/build/Android.bp b/build/Android.bp
index e2767ffc24..d686f34668 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -81,7 +81,8 @@ art_global_defaults {
// Warn about thread safety violations with clang.
"-Wthread-safety",
- "-Wthread-safety-negative",
+ // TODO(b/144045034): turn on -Wthread-safety-negative
+ //"-Wthread-safety-negative",
// Warn if switch fallthroughs aren't annotated.
"-Wimplicit-fallthrough",
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index c29cd299ea..7c3e791e78 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -63,6 +63,7 @@ define create-core-oat-host-rules
$$(error found $(1) expected interpreter, interp-ac, or optimizing)
endif
+ core_image_location := $(HOST_OUT_JAVA_LIBRARIES)/core$$(core_infix)$(CORE_IMG_SUFFIX)
core_image_name := $($(2)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$(CORE_IMG_SUFFIX)
core_oat_name := $($(2)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$(CORE_OAT_SUFFIX)
@@ -76,18 +77,46 @@ define create-core-oat-host-rules
HOST_CORE_OAT_OUTS += $$(core_oat_name)
$$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options)
+$$(core_image_name): PRIVATE_CORE_IMAGE_LOCATION := $$(core_image_location)
$$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name)
$$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name)
-$$(core_image_name): $$(HOST_CORE_IMG_DEX_LOCATIONS) $$(core_dex2oat_dependency)
+# In addition to the primary core image containing HOST_CORE_IMG_DEX_FILES,
+# also build a boot image extension for the remaining HOST_CORE_DEX_FILES.
+$$(core_image_name): $$(HOST_CORE_DEX_LOCATIONS) $$(core_dex2oat_dependency)
@echo "host dex2oat: $$@"
@mkdir -p $$(dir $$@)
- $$(hide) ANDROID_LOG_TAGS="*:e" $$(DEX2OAT) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
+ $$(hide) ANDROID_LOG_TAGS="*:e" $$(DEX2OAT) \
+ --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
--runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
$$(addprefix --dex-file=,$$(HOST_CORE_IMG_DEX_FILES)) \
$$(addprefix --dex-location=,$$(HOST_CORE_IMG_DEX_LOCATIONS)) \
--oat-file=$$(PRIVATE_CORE_OAT_NAME) \
- --oat-location=$$(PRIVATE_CORE_OAT_NAME) --image=$$(PRIVATE_CORE_IMG_NAME) \
- --base=$$(LIBART_IMG_HOST_BASE_ADDRESS) --instruction-set=$$($(2)ART_HOST_ARCH) \
+ --oat-location=$$(PRIVATE_CORE_OAT_NAME) \
+ --image=$$(PRIVATE_CORE_IMG_NAME) \
+ --base=$$(LIBART_IMG_HOST_BASE_ADDRESS) \
+ --instruction-set=$$($(2)ART_HOST_ARCH) \
+ $$(LOCAL_$(2)DEX2OAT_HOST_INSTRUCTION_SET_FEATURES_OPTION) \
+ --host --android-root=$$(HOST_OUT) \
+ --generate-debug-info --generate-build-id \
+ --runtime-arg -XX:SlowDebug=true \
+ --no-inline-from=core-oj-hostdex.jar \
+ $$(PRIVATE_CORE_COMPILE_OPTIONS) && \
+ ANDROID_LOG_TAGS="*:e" $$(DEX2OAT) \
+ --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
+ --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
+ --runtime-arg -Xbootclasspath:$$(subst $$(space),:,$$(strip \
+ $$(HOST_CORE_DEX_FILES))) \
+ --runtime-arg -Xbootclasspath-locations:$$(subst $$(space),:,$$(strip \
+ $$(HOST_CORE_DEX_LOCATIONS))) \
+ $$(addprefix --dex-file=, \
+ $$(filter-out $$(HOST_CORE_IMG_DEX_FILES),$$(HOST_CORE_DEX_FILES))) \
+ $$(addprefix --dex-location=, \
+ $$(filter-out $$(HOST_CORE_IMG_DEX_LOCATIONS),$$(HOST_CORE_DEX_LOCATIONS))) \
+ --oat-file=$$(PRIVATE_CORE_OAT_NAME) \
+ --oat-location=$$(PRIVATE_CORE_OAT_NAME) \
+ --boot-image=$$(PRIVATE_CORE_IMAGE_LOCATION) \
+ --image=$$(PRIVATE_CORE_IMG_NAME) \
+ --instruction-set=$$($(2)ART_HOST_ARCH) \
$$(LOCAL_$(2)DEX2OAT_HOST_INSTRUCTION_SET_FEATURES_OPTION) \
--host --android-root=$$(HOST_OUT) \
--generate-debug-info --generate-build-id \
@@ -149,6 +178,7 @@ define create-core-oat-target-rules
$$(error found $(1) expected interpreter, interp-ac, or optimizing)
endif
+ core_image_location := $(ART_TARGET_TEST_OUT)/core$$(core_infix)$(CORE_IMG_SUFFIX)
core_image_name := $($(2)TARGET_CORE_IMG_OUT_BASE)$$(core_infix)$(CORE_IMG_SUFFIX)
core_oat_name := $($(2)TARGET_CORE_OAT_OUT_BASE)$$(core_infix)$(CORE_OAT_SUFFIX)
@@ -166,24 +196,53 @@ define create-core-oat-target-rules
TARGET_CORE_OAT_OUTS += $$(core_oat_name)
$$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options)
+$$(core_image_name): PRIVATE_CORE_IMAGE_LOCATION := $$(core_image_location)
$$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name)
$$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name)
-$$(core_image_name): $$(TARGET_CORE_IMG_DEX_FILES) $$(core_dex2oat_dependency)
+# In addition to the primary core image containing TARGET_CORE_IMG_DEX_FILES,
+# also build a boot image extension for the remaining TARGET_CORE_DEX_FILES.
+$$(core_image_name): $$(TARGET_CORE_DEX_FILES) $$(core_dex2oat_dependency)
@echo "target dex2oat: $$@"
@mkdir -p $$(dir $$@)
- $$(hide) $$(DEX2OAT) --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
+ $$(hide) $$(DEX2OAT) \
+ --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
--runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
$$(addprefix --dex-file=,$$(TARGET_CORE_IMG_DEX_FILES)) \
$$(addprefix --dex-location=,$$(TARGET_CORE_IMG_DEX_LOCATIONS)) \
--oat-file=$$(PRIVATE_CORE_OAT_NAME) \
- --oat-location=$$(PRIVATE_CORE_OAT_NAME) --image=$$(PRIVATE_CORE_IMG_NAME) \
- --base=$$(LIBART_IMG_TARGET_BASE_ADDRESS) --instruction-set=$$($(2)TARGET_ARCH) \
+ --oat-location=$$(PRIVATE_CORE_OAT_NAME) \
+ --image=$$(PRIVATE_CORE_IMG_NAME) \
+ --base=$$(LIBART_IMG_TARGET_BASE_ADDRESS) \
+ --instruction-set=$$($(2)TARGET_ARCH) \
+ --instruction-set-variant=$$($(2)DEX2OAT_TARGET_CPU_VARIANT) \
+ --instruction-set-features=$$($(2)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \
+ --android-root=$$(PRODUCT_OUT)/system \
+ --generate-debug-info --generate-build-id \
+ --runtime-arg -XX:SlowDebug=true \
+ $$(PRIVATE_CORE_COMPILE_OPTIONS) && \
+ $$(DEX2OAT) \
+ --runtime-arg -Xms$(DEX2OAT_IMAGE_XMS) \
+ --runtime-arg -Xmx$(DEX2OAT_IMAGE_XMX) \
+ --runtime-arg -Xbootclasspath:$$(subst $$(space),:,$$(strip \
+ $$(TARGET_CORE_DEX_FILES))) \
+ --runtime-arg -Xbootclasspath-locations:$$(subst $$(space),:,$$(strip \
+ $$(TARGET_CORE_DEX_LOCATIONS))) \
+ $$(addprefix --dex-file=, \
+ $$(filter-out $$(TARGET_CORE_IMG_DEX_FILES),$$(TARGET_CORE_DEX_FILES))) \
+ $$(addprefix --dex-location=, \
+ $$(filter-out $$(TARGET_CORE_IMG_DEX_LOCATIONS),$$(TARGET_CORE_DEX_LOCATIONS))) \
+ --oat-file=$$(PRIVATE_CORE_OAT_NAME) \
+ --oat-location=$$(PRIVATE_CORE_OAT_NAME) \
+ --boot-image=$$(PRIVATE_CORE_IMAGE_LOCATION) \
+ --image=$$(PRIVATE_CORE_IMG_NAME) \
+ --instruction-set=$$($(2)TARGET_ARCH) \
--instruction-set-variant=$$($(2)DEX2OAT_TARGET_CPU_VARIANT) \
--instruction-set-features=$$($(2)DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \
--android-root=$$(PRODUCT_OUT)/system \
--generate-debug-info --generate-build-id \
--runtime-arg -XX:SlowDebug=true \
- $$(PRIVATE_CORE_COMPILE_OPTIONS) || (rm $$(PRIVATE_CORE_OAT_NAME); exit 1)
+ $$(PRIVATE_CORE_COMPILE_OPTIONS) || \
+ (rm $$(PRIVATE_CORE_OAT_NAME); exit 1)
$$(core_oat_name): $$(core_image_name)
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index ceb3093294..809b8fce40 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -27,6 +27,7 @@ art_runtime_base_binaries_prefer32 = [
// the ART APEX.
art_runtime_base_native_shared_libs = [
// External API (having APEX stubs).
+ "libadbconnection_server",
"libdexfile_external",
"libnativebridge",
"libnativehelper",
@@ -228,13 +229,6 @@ apex_defaults {
art_tools_device_only_binaries,
},
},
- binaries: [
- "art_postinstall_hook",
- "art_preinstall_hook",
- "art_preinstall_hook_boot",
- "art_preinstall_hook_system_server",
- "art_prepostinstall_utils",
- ],
prebuilts: ["com.android.art.ld.config.txt"],
key: "com.android.art.key",
required: [
@@ -269,7 +263,7 @@ apex_defaults {
// Release version of the ART APEX module (not containing debug
// variants nor tools), included in user builds. Also used for
// storage-constrained devices in userdebug and eng builds.
-apex {
+art_apex {
name: "com.android.art.release",
defaults: ["com.android.art-defaults"],
certificate: ":com.android.art.certificate",
@@ -278,7 +272,7 @@ apex {
// "Debug" version of the ART APEX module (containing both release and
// debug variants, as well as additional tools), included in userdebug and
// eng build.
-apex {
+art_apex {
name: "com.android.art.debug",
defaults: ["com.android.art-dev-defaults"],
certificate: ":com.android.art.certificate",
@@ -311,7 +305,7 @@ art_gtests = [
// "Testing" version of the ART APEX module (containing both release
// and debug variants, additional tools, and ART gtests), for testing
// purposes only.
-apex_test {
+art_apex_test {
name: "com.android.art.testing",
defaults: ["com.android.art-dev-defaults"],
file_contexts: "com.android.art.debug",
@@ -462,36 +456,3 @@ cc_prebuilt_binary {
defaults: ["art-check-apex-gen-fakebin-defaults"],
srcs: [":art-check-testing-apex-gen"],
}
-
-// Pre-install scripts.
-
-sh_binary {
- name: "art_preinstall_hook",
- src: "art_preinstall_hook.sh",
-}
-
-sh_binary {
- name: "art_preinstall_hook_boot",
- src: "art_preinstall_hook_boot.sh",
-}
-
-sh_binary {
- name: "art_preinstall_hook_system_server",
- src: "art_preinstall_hook_system_server.sh",
-}
-
-sh_binary {
- name: "art_prepostinstall_utils",
- src: "art_prepostinstall_utils.sh",
-}
-
-sh_binary {
- name: "art_postinstall_hook",
- src: "art_postinstall_hook.sh",
-}
-
-sh_binary {
- name: "art_apex_boot_integrity",
- src: "art_apex_boot_integrity.sh",
- init_rc: ["art_apex_boot_integrity.rc"],
-}
diff --git a/build/apex/art_apex_test.py b/build/apex/art_apex_test.py
index 248fab5275..7d2c79af23 100755
--- a/build/apex/art_apex_test.py
+++ b/build/apex/art_apex_test.py
@@ -267,6 +267,14 @@ class Checker:
return False, '%s is a directory'
return True, ''
+ def is_dir(self, path):
+ fs_object = self._provider.get(path)
+ if fs_object is None:
+ return False, 'Could not find %s'
+ if not fs_object.is_dir:
+ return False, '%s is not a directory'
+ return True, ''
+
def check_file(self, path):
ok, msg = self.is_file(path)
if not ok:
@@ -294,27 +302,30 @@ class Checker:
self.fail('%s is not a symlink', path)
self._expected_file_globs.add(path)
- def check_art_test_executable(self, filename):
- # This is a simplistic implementation, as we declare victory as soon as the
- # test binary is found for one of the supported (not built) architectures.
- # Ideally we would propagate the built architectures from the build system
- # to this script and require test binaries for all of them to be present.
- # Note that this behavior is not specific to this method: there are other
- # places in this script where we rely on this simplified strategy.
+ def arch_dirs_for_path(self, path):
+ # Look for target-specific subdirectories for the given directory path.
+ # This is needed because the list of build targets is not propagated
+ # to this script.
#
- # TODO: Implement the suggestion above (here and in other places in this
- # script).
- test_found = False
+ # TODO: Pass build target information to this script and fix all places
+ # where this function in used (or similar workarounds).
+ dirs = []
for arch in ARCHS:
- test_path = '%s/%s/%s' % (ART_TEST_DIR, arch, filename)
- test_is_file, _ = self.is_file(test_path)
- if test_is_file:
- test_found = True
- self._expected_file_globs.add(test_path)
- if not self._provider.get(test_path).is_exec:
- self.fail('%s is not executable', test_path)
- if not test_found:
+ dir = '%s/%s' % (path, arch)
+ found, _ = self.is_dir(dir)
+ if found:
+ dirs.append(dir)
+ return dirs
+
+ def check_art_test_executable(self, filename):
+ dirs = self.arch_dirs_for_path(ART_TEST_DIR)
+ if not dirs:
self.fail('ART test binary missing: %s', filename)
+ for dir in dirs:
+ test_path = '%s/%s' % (dir, filename)
+ self._expected_file_globs.add(test_path)
+ if not self._provider.get(test_path).is_exec:
+ self.fail('%s is not executable', test_path)
def check_single_library(self, filename):
lib_path = 'lib/%s' % filename
@@ -328,6 +339,14 @@ class Checker:
if not lib_is_file and not lib64_is_file:
self.fail('Library missing: %s', filename)
+ def check_dexpreopt(self, basename):
+ dirs = self.arch_dirs_for_path('javalib')
+ if not dirs:
+ self.fail('Could not find javalib directory for any arch.')
+ for dir in dirs:
+ for ext in ['art', 'oat', 'vdex']:
+ self.check_file('%s/%s.%s' % (dir, basename, ext))
+
def check_java_library(self, basename):
return self.check_file('javalib/%s.jar' % basename)
@@ -467,6 +486,7 @@ class ReleaseChecker:
self._checker.check_native_library('libnativebridge')
self._checker.check_native_library('libnativehelper')
self._checker.check_native_library('libnativeloader')
+ self._checker.check_native_library('libadbconnection_server')
# Check internal libraries for ART.
self._checker.check_native_library('libadbconnection')
@@ -522,6 +542,17 @@ class ReleaseChecker:
self._checker.check_optional_native_library('libclang_rt.hwasan*')
self._checker.check_optional_native_library('libclang_rt.ubsan*')
+ # Check dexpreopt files for libcore bootclasspath jars, unless this is a
+ # coverage build with EMMA_INSTRUMENT_FRAMEWORK=true (in that case we do not
+ # generate dexpreopt files because ART boot jars depend on framework and
+ # cannot be dexpreopted in isolation).
+ if 'EMMA_INSTRUMENT_FRAMEWORK' not in os.environ or not os.environ['EMMA_INSTRUMENT_FRAMEWORK']:
+ self._checker.check_dexpreopt('boot')
+ self._checker.check_dexpreopt('boot-apache-xml')
+ self._checker.check_dexpreopt('boot-bouncycastle')
+ self._checker.check_dexpreopt('boot-core-icu4j')
+ self._checker.check_dexpreopt('boot-core-libart')
+ self._checker.check_dexpreopt('boot-okhttp')
class ReleaseTargetChecker:
def __init__(self, checker):
@@ -531,13 +562,6 @@ class ReleaseTargetChecker:
return 'Release (Target) Checker'
def run(self):
- # Check the APEX package scripts.
- self._checker.check_executable('art_postinstall_hook')
- self._checker.check_executable('art_preinstall_hook')
- self._checker.check_executable('art_preinstall_hook_boot')
- self._checker.check_executable('art_preinstall_hook_system_server')
- self._checker.check_executable('art_prepostinstall_utils')
-
# Check binaries for ART.
self._checker.check_executable('oatdump')
@@ -1045,36 +1069,12 @@ def art_apex_test_main(test_args):
if test_args.host and test_args.flattened:
logging.error("Both of --host and --flattened set")
return 1
- if test_args.tree and test_args.debug:
- logging.error("Both of --tree and --debug set")
- return 1
- if test_args.tree and test_args.testing:
- logging.error("Both of --tree and --testing set")
- return 1
- if test_args.list and test_args.debug:
- logging.error("Both of --list and --debug set")
- return 1
- if test_args.list and test_args.testing:
- logging.error("Both of --list and --testing set")
- return 1
if test_args.list and test_args.tree:
logging.error("Both of --list and --tree set")
return 1
if test_args.size and not (test_args.list or test_args.tree):
logging.error("--size set but neither --list nor --tree set")
return 1
- if test_args.host and test_args.testing:
- logging.error("Both of --host and --testing set")
- return 1
- if test_args.debug and test_args.testing:
- logging.error("Both of --debug and --testing set")
- return 1
- if test_args.flavor and test_args.debug:
- logging.error("Both of --flavor and --debug set")
- return 1
- if test_args.flavor and test_args.testing:
- logging.error("Both of --flavor and --testing set")
- return 1
if not test_args.flattened and not test_args.tmpdir:
logging.error("Need a tmpdir.")
return 1
@@ -1082,15 +1082,6 @@ def art_apex_test_main(test_args):
logging.error("Need debugfs.")
return 1
- # Handle legacy flavor flags.
- if test_args.debug:
- logging.warning('Using deprecated option --debug')
- test_args.flavor = FLAVOR_DEBUG
- if test_args.testing:
- logging.warning('Using deprecated option --testing')
- test_args.flavor = FLAVOR_TESTING
-
- # Handle new flavor flag.
if test_args.flavor == FLAVOR_AUTO:
logging.warning('--flavor=auto, trying to autodetect. This may be incorrect!')
for flavor in [ FLAVOR_RELEASE, FLAVOR_DEBUG, FLAVOR_TESTING ]:
@@ -1248,10 +1239,6 @@ if __name__ == "__main__":
parser.add_argument('--flavor', help='Check as FLAVOR APEX', choices=FLAVORS_ALL,
default=FLAVOR_AUTO)
- # Deprecated flavor flags.
- # TODO: Stop supporting those flags eventually.
- parser.add_argument('--debug', help='Check as debug APEX', action='store_true')
- parser.add_argument('--testing', help='Check as testing APEX', action='store_true')
parser.add_argument('--list', help='List all files', action='store_true')
parser.add_argument('--tree', help='Print directory tree', action='store_true')
diff --git a/build/apex/art_postinstall_hook.sh b/build/apex/art_postinstall_hook.sh
deleted file mode 100644
index cb3b887b76..0000000000
--- a/build/apex/art_postinstall_hook.sh
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/system/bin/sh
-
-# Copyright (C) 2019 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.
-#
-
-. `dirname $0`/art_prepostinstall_utils || exit 100
-
-log_info "=== ART runtime post-install ==="
-
-# Check for OTA base folder.
-if [ ! -d /data/ota/dalvik-cache ] ; then
- log_error "Postinstall dalvik-cache does not exist or is not a directory"
- exit 101
-fi
-
-log_info "Checking fsverity"
-
-# Measure (and enable) fsverity to see if things are installed. Enable is not
-# idempotent, and we'd need to parse the error string to see whether it says
-# data was installed. Rather do a two-step.
-FILES=`find /data/ota/dalvik-cache -type f`
-for FILE in $FILES ; do
- fsverity measure $FILE && continue
- ENABLE_MSG=`fsverity enable $FILE 2>&1` && continue
-
- # No installed data, can't enable. Clean up and fail.
- log_error "Enable failed: $ENABLE_MSG"
- rm -rf /data/ota/dalvik-cache
- exit 200
-done
-
-log_info "Moving dalvik-cache"
-
-rm -rf /data/dalvik-cache/* || exit 102
-mv /data/ota/dalvik-cache/* /data/dalvik-cache/ || exit 103
-restorecon -R -F /data/dalvik-cache/* || exit 104
diff --git a/build/apex/art_preinstall_hook.sh b/build/apex/art_preinstall_hook.sh
deleted file mode 100644
index 94a1b210ec..0000000000
--- a/build/apex/art_preinstall_hook.sh
+++ /dev/null
@@ -1,49 +0,0 @@
-#!/system/bin/sh
-
-# Copyright (C) 2019 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.
-#
-
-. `dirname $0`/art_prepostinstall_utils || exit 100
-
-log_info "=== ART runtime pre-install ==="
-
-set_arches || exit 101
-log_info "Arches = `echo $ARCHES`"
-
-# The runtime update uses /data/ota as a staging directory, similar to
-# A/B OTA. (There is no overlap, as A/B uses slot prefixes.)
-
-# Create OTA base folder.
-mkdir -p /data/ota/dalvik-cache || exit 102
-# Bind-mount to perceive as normal structure.
-mount -o bind /data/ota/dalvik-cache /data/dalvik-cache || exit 103
-
-for ARCH in $ARCHES ; do
- log_info "Preparing compilation output directories for $ARCH"
-
- # Create OTA folders.
- mkdir -p /data/ota/dalvik-cache/$ARCH || exit 104
- rm -rf /data/ota/dalvik-cache/$ARCH/* || exit 105
-
- `dirname $0`/art_preinstall_hook_boot $ARCH || exit 200
-done
-
-PRIMARY_ARCH=`echo $ARCHES | sed -e 's/ .*//'`
-`dirname $0`/art_preinstall_hook_system_server $PRIMARY_ARCH || exit 300
-
-FILES=`find /data/dalvik-cache -type f`
-for FILE in $FILES ; do
- setup_fsverity $FILE || exit 400
-done
diff --git a/build/apex/art_preinstall_hook_boot.sh b/build/apex/art_preinstall_hook_boot.sh
deleted file mode 100644
index 0985befb9d..0000000000
--- a/build/apex/art_preinstall_hook_boot.sh
+++ /dev/null
@@ -1,73 +0,0 @@
-#!/system/bin/sh
-
-# Copyright (C) 2019 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.
-#
-
-. `dirname $0`/art_prepostinstall_utils || exit 100
-
-log_info "Preparing boot image compilation parameters"
-
-# Prefer DEX2OATBOOTCLASSPATH, then BOOTCLASSPATH.
-USED_CLASSPATH=$DEX2OATBOOTCLASSPATH
-if [ -z "$USED_CLASSPATH" ] ; then
- USED_CLASSPATH=$BOOTCLASSPATH
- if [ -z "$USED_CLASSPATH" ] ; then
- log_error "Could not find boot class-path to compile"
- exit 101
- fi
-fi
-BOOTCP=`echo $USED_CLASSPATH | tr ":" "\n"`
-
-DEX_FILES=
-DEX_LOCATIONS=
-for component in $BOOTCP ; do
- DEX_FILES="$DEX_FILES --dex-file=$component"
- DEX_LOCATIONS="$DEX_LOCATIONS --dex-location=$component"
-done
-
-PROFILING=
-if [ -f "/system/etc/boot-image.prof" ] ; then
- PROFILING="--compiler-filter=speed-profile --profile-file=/system/etc/boot-image.prof"
-fi
-if [ -f "/system/etc/dirty-image-objects" ] ; then
- PROFILING="$PROFILING --dirty-image-objects=/system/etc/dirty-image-objects"
-fi
-
-DEX2OAT_IMAGE_XMX=`getprop dalvik.vm.image-dex2oat-Xmx`
-
-DEX2OAT_TARGET_ARCH=$1
-DEX2OAT_TARGET_CPU_VARIANT=`getprop dalvik.vm.isa.${DEX2OAT_TARGET_ARCH}.variant`
-DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES=`getprop dalvik.vm.isa.${DEX2OAT_TARGET_ARCH}.features`
-
-log_info "Compiling boot image for $DEX2OAT_TARGET_ARCH"
-
-dex2oat \
- --avoid-storing-invocation \
- --runtime-arg -Xmx$DEX2OAT_IMAGE_XMX \
- $PROFILING \
- $DEX_FILES \
- $DEX_LOCATIONS \
- --generate-mini-debug-info \
- --strip \
- --oat-file=/data/dalvik-cache/$DEX2OAT_TARGET_ARCH/system@framework@boot.oat \
- --oat-location=/data/dalvik-cache/$DEX2OAT_TARGET_ARCH/system@framework@boot.oat \
- --image=/data/dalvik-cache/$DEX2OAT_TARGET_ARCH/system@framework@boot.art --base=0x70000000 \
- --instruction-set=$DEX2OAT_TARGET_ARCH \
- --instruction-set-variant=$DEX2OAT_TARGET_CPU_VARIANT \
- --instruction-set-features=$DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES \
- --android-root=/system \
- --no-inline-from=core-oj.jar \
- --abort-on-hard-verifier-error \
- --force-determinism || { log_error "Dex2oat failed" ; exit 102 ; }
diff --git a/build/apex/art_preinstall_hook_system_server.sh b/build/apex/art_preinstall_hook_system_server.sh
deleted file mode 100644
index 9462c3b885..0000000000
--- a/build/apex/art_preinstall_hook_system_server.sh
+++ /dev/null
@@ -1,84 +0,0 @@
-#!/system/bin/sh
-
-# Copyright (C) 2019 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.
-#
-
-. `dirname $0`/art_prepostinstall_utils || exit 100
-
-function dalvik_cache_name {
- local input=$1
- # Strip first /, replace rest with @.
- DALVIK_CACHE_NAME=`echo $input | sed -e 's,^/,,' -e 's,/,@,g'`
- # Append @classes.dex.
- DALVIK_CACHE_NAME="${DALVIK_CACHE_NAME}@classes.dex"
-}
-
-log_info "Preparing system server compilation parameters"
-
-if [ "x$SYSTEMSERVERCLASSPATH" = "x" ] ; then
- log_info "SYSTEMSERVERCLASSPATH is not set! Trying to retrieve from init.environ.rc."
- SYSTEMSERVERCLASSPATH=`grep "export SYSTEMSERVERCLASSPATH" init.environ.rc | sed -e "s/.* //"`
- if [ "x$SYSTEMSERVERCLASSPATH" = "x" ] ; then
- log_error "Could not find SYSTEMSERVERCLASSPATH"
- exit 101
- fi
-fi
-SYSCP=`echo $SYSTEMSERVERCLASSPATH | tr ":" "\n"`
-
-BOOTCPPARAM=
-if [ ! -z "$DEX2OATBOOTCLASSPATH" ] ; then
- BOOTCPPARAM="--runtime-arg -Xbootclasspath:$DEX2OATBOOTCLASSPATH"
-fi
-
-DEX2OAT_IMAGE_XMX=`getprop dalvik.vm.dex2oat-Xmx`
-
-DEX2OAT_TARGET_ARCH=$1
-DEX2OAT_TARGET_CPU_VARIANT=`getprop dalvik.vm.isa.${DEX2OAT_TARGET_ARCH}.variant`
-DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES=`getprop dalvik.vm.isa.${DEX2OAT_TARGET_ARCH}.features`
-
-# Do this like preopt: speed compile, no classpath, possibly pick up profiles.
-
-# TODO: App image? Would have to scan /system for an existing image.
-
-for COMPONENT in $SYSCP ; do
- log_info "Compiling $COMPONENT"
- dalvik_cache_name $COMPONENT
- PROFILING=
- if [ -f "${COMPONENT}.prof" ] ; then
- PROFILING="--profile-file=${COMPONENT}.prof"
- fi
- dex2oat \
- --avoid-storing-invocation \
- --runtime-arg -Xmx$DEX2OAT_IMAGE_XMX \
- $BOOTCPPARAM \
- --class-loader-context=\& \
- --boot-image=/data/dalvik-cache/system@framework@boot.art \
- --dex-file=$COMPONENT \
- --dex-location=$COMPONENT \
- --oat-file=/data/dalvik-cache/$DEX2OAT_TARGET_ARCH/$DALVIK_CACHE_NAME \
- --android-root=/system \
- --instruction-set=$DEX2OAT_TARGET_ARCH \
- --instruction-set-variant=$DEX2OAT_TARGET_CPU_VARIANT \
- --instruction-set-features=$DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES \
- --no-generate-debug-info \
- --abort-on-hard-verifier-error \
- --force-determinism \
- --no-inline-from=core-oj.jar \
- --copy-dex-files=false \
- --compiler-filter=speed \
- --generate-mini-debug-info \
- $PROFILING \
- || { log_error "Dex2oat failed" ; exit 102 ; }
-done
diff --git a/build/apex/art_prepostinstall_utils.sh b/build/apex/art_prepostinstall_utils.sh
deleted file mode 100644
index f5a94d12e3..0000000000
--- a/build/apex/art_prepostinstall_utils.sh
+++ /dev/null
@@ -1,70 +0,0 @@
-#!/system/bin/sh
-
-# Copyright (C) 2019 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.
-#
-
-alias log_info="log -t art_apex -p i"
-alias log_error="log -t art_apex -p f"
-
-# Set |ARCHES| to a string containing the architectures of the device.
-function set_arches {
- # Derive architectures. For now, stop at two.
- local abilist_prop=`getprop ro.product.cpu.abilist`
- local abilist=`echo $abilist_prop | tr "," "\n"`
- ARCHES=""
- for abi in $abilist ; do
- case "$abi" in
- arm64-v8a)
- ARCHES="$ARCHES\narm64"
- ;;
- armeabi-v7a|armeabi)
- ARCHES="$ARCHES\narm"
- ;;
- x86)
- ARCHES="$ARCHES\nx86"
- ;;
- x86_64)
- ARCHES="$ARCHES\nx86_64"
- ;;
- *)
- log_error "Unsupported ABI $abi"
- return 1
- ;;
- esac
- done
- ARCHES=`echo $ARCHES | uniq`
- return 0
-}
-
-function setup_fsverity {
- local full_shell_path=`readlink -f $0`
- local bin_dir=`dirname $full_shell_path`
- local apex_dir=`dirname $bin_dir`
- local sig_dir="${apex_dir}.signatures"
- local file=$1
- local signature_file="$sig_dir/$file.sig"
- # Setup.
- log_info "fsverity setup for $file"
- SETUP_MSG=`fsverity setup $file --signature=$signature_file --hash=sha256 2>&1` || \
- { log_error "Setup failed: $SETUP_MSG" ; return 300 ; }
- # Enable.
- log_info "fsverity enable for $file"
- ENABLE_MSG=`fsverity enable $file 2>&1` || \
- { log_error "Enable failed: $ENABLE_MSG" ; return 301 ; }
- # Test integrity.
- INTEGRITY_MSG=`dd if=$file of=/dev/null bs=4k 2>&1` || \
- { log_error "Integrity failed: $INTEGRITY_MSG" ; return 302 ; }
- return 0
-}
diff --git a/build/apex/manifest-art.json b/build/apex/manifest-art.json
index ebba1b27ef..59cbfac435 100644
--- a/build/apex/manifest-art.json
+++ b/build/apex/manifest-art.json
@@ -1,6 +1,4 @@
{
"name": "com.android.art",
- "version": 1,
- "preInstallHook": "bin/art_preinstall_hook",
- "postInstallHook": "bin/art_postinstall_hook"
+ "version": 1
}
diff --git a/build/art.go b/build/art.go
index 56eec54138..4c1099bc98 100644
--- a/build/art.go
+++ b/build/art.go
@@ -312,6 +312,10 @@ func init() {
android.RegisterModuleType("art_global_defaults", artGlobalDefaultsFactory)
android.RegisterModuleType("art_debug_defaults", artDebugDefaultsFactory)
+ // ART apex is special because it must include dexpreopt files for bootclasspath jars.
+ android.RegisterModuleType("art_apex", artApexBundleFactory)
+ android.RegisterModuleType("art_apex_test", artTestApexBundleFactory)
+
// TODO: This makes the module disable itself for host if HOST_PREFER_32_BIT is
// set. We need this because the multilib types of binaries listed in the apex
// rule must match the declared type. This is normally not difficult but HOST_PREFER_32_BIT
@@ -321,8 +325,16 @@ func init() {
android.RegisterModuleType("art_apex_test_host", artHostTestApexBundleFactory)
}
+func artApexBundleFactory() android.Module {
+ return apex.ApexBundleFactory(false /*testApex*/, true /*artApex*/)
+}
+
+func artTestApexBundleFactory() android.Module {
+ return apex.ApexBundleFactory(true /*testApex*/, true /*artApex*/)
+}
+
func artHostTestApexBundleFactory() android.Module {
- module := apex.ApexBundleFactory( /*testApex*/ true)
+ module := apex.ApexBundleFactory(true /*testApex*/, true /*artApex*/)
android.AddLoadHook(module, func(ctx android.LoadHookContext) {
if envTrue(ctx, "HOST_PREFER_32_BIT") {
type props struct {
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 3a2988fbf5..10397e8f74 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -1183,7 +1183,7 @@ CPURegList CodeGeneratorARM64::GetFramePreservedCoreRegisters() const {
CPURegList CodeGeneratorARM64::GetFramePreservedFPRegisters() const {
DCHECK(ArtVixlRegCodeCoherentForRegSet(0, 0, fpu_spill_mask_,
GetNumberOfFloatingPointRegisters()));
- return CPURegList(CPURegister::kFPRegister, kDRegSize,
+ return CPURegList(CPURegister::kVRegister, kDRegSize,
fpu_spill_mask_);
}
@@ -1316,10 +1316,10 @@ void CodeGeneratorARM64::MoveConstant(CPURegister destination, HConstant* consta
} else if (constant->IsNullConstant()) {
__ Mov(Register(destination), 0);
} else if (constant->IsFloatConstant()) {
- __ Fmov(FPRegister(destination), constant->AsFloatConstant()->GetValue());
+ __ Fmov(VRegister(destination), constant->AsFloatConstant()->GetValue());
} else {
DCHECK(constant->IsDoubleConstant());
- __ Fmov(FPRegister(destination), constant->AsDoubleConstant()->GetValue());
+ __ Fmov(VRegister(destination), constant->AsDoubleConstant()->GetValue());
}
}
@@ -1343,7 +1343,7 @@ static bool CoherentConstantAndType(Location constant, DataType::Type type) {
static CPURegister AcquireFPOrCoreCPURegisterOfSize(vixl::aarch64::MacroAssembler* masm,
vixl::aarch64::UseScratchRegisterScope* temps,
int size_in_bits) {
- return masm->GetScratchFPRegisterList()->IsEmpty()
+ return masm->GetScratchVRegisterList()->IsEmpty()
? CPURegister(temps->AcquireRegisterOfSize(size_in_bits))
: CPURegister(temps->AcquireVRegisterOfSize(size_in_bits));
}
@@ -1411,7 +1411,7 @@ void CodeGeneratorARM64::MoveLocation(Location destination,
if (GetGraph()->HasSIMD()) {
__ Mov(QRegisterFrom(destination), QRegisterFrom(source));
} else {
- __ Fmov(FPRegister(dst), FPRegisterFrom(source, dst_type));
+ __ Fmov(VRegister(dst), FPRegisterFrom(source, dst_type));
}
}
}
@@ -1421,14 +1421,14 @@ void CodeGeneratorARM64::MoveLocation(Location destination,
} else {
DCHECK(source.IsSIMDStackSlot());
UseScratchRegisterScope temps(GetVIXLAssembler());
- if (GetVIXLAssembler()->GetScratchFPRegisterList()->IsEmpty()) {
+ if (GetVIXLAssembler()->GetScratchVRegisterList()->IsEmpty()) {
Register temp = temps.AcquireX();
__ Ldr(temp, MemOperand(sp, source.GetStackIndex()));
__ Str(temp, MemOperand(sp, destination.GetStackIndex()));
__ Ldr(temp, MemOperand(sp, source.GetStackIndex() + kArm64WordSize));
__ Str(temp, MemOperand(sp, destination.GetStackIndex() + kArm64WordSize));
} else {
- FPRegister temp = temps.AcquireVRegisterOfSize(kQRegSize);
+ VRegister temp = temps.AcquireVRegisterOfSize(kQRegSize);
__ Ldr(temp, StackOperandFrom(source));
__ Str(temp, StackOperandFrom(destination));
}
@@ -1602,7 +1602,7 @@ void CodeGeneratorARM64::LoadAcquire(HInstruction* instruction,
MaybeRecordImplicitNullCheck(instruction);
}
}
- __ Fmov(FPRegister(dst), temp);
+ __ Fmov(VRegister(dst), temp);
break;
}
case DataType::Type::kUint32:
@@ -1702,7 +1702,7 @@ void CodeGeneratorARM64::StoreRelease(HInstruction* instruction,
} else {
DCHECK(src.IsFPRegister());
temp_src = src.Is64Bits() ? temps.AcquireX() : temps.AcquireW();
- __ Fmov(temp_src, FPRegister(src));
+ __ Fmov(temp_src, VRegister(src));
}
{
ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize);
@@ -2057,9 +2057,9 @@ void InstructionCodeGeneratorARM64::HandleBinaryOp(HBinaryOperation* instr) {
}
case DataType::Type::kFloat32:
case DataType::Type::kFloat64: {
- FPRegister dst = OutputFPRegister(instr);
- FPRegister lhs = InputFPRegisterAt(instr, 0);
- FPRegister rhs = InputFPRegisterAt(instr, 1);
+ VRegister dst = OutputFPRegister(instr);
+ VRegister lhs = InputFPRegisterAt(instr, 0);
+ VRegister rhs = InputFPRegisterAt(instr, 1);
if (instr->IsAdd()) {
__ Fadd(dst, lhs, rhs);
} else if (instr->IsSub()) {
@@ -2805,7 +2805,7 @@ static bool IsFloatingPointZeroConstant(HInstruction* inst) {
}
void InstructionCodeGeneratorARM64::GenerateFcmp(HInstruction* instruction) {
- FPRegister lhs_reg = InputFPRegisterAt(instruction, 0);
+ VRegister lhs_reg = InputFPRegisterAt(instruction, 0);
Location rhs_loc = instruction->GetLocations()->InAt(1);
if (rhs_loc.IsConstant()) {
// 0.0 is the only immediate that can be encoded directly in
@@ -5428,8 +5428,8 @@ void InstructionCodeGeneratorARM64::VisitAbs(HAbs* abs) {
}
case DataType::Type::kFloat32:
case DataType::Type::kFloat64: {
- FPRegister in_reg = InputFPRegisterAt(abs, 0);
- FPRegister out_reg = OutputFPRegister(abs);
+ VRegister in_reg = InputFPRegisterAt(abs, 0);
+ VRegister out_reg = OutputFPRegister(abs);
__ Fabs(out_reg, in_reg);
break;
}
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 1a9b7006dc..a669094509 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -63,7 +63,7 @@ static const vixl::aarch64::Register kParameterCoreRegisters[] = {
vixl::aarch64::x7
};
static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters);
-static const vixl::aarch64::FPRegister kParameterFPRegisters[] = {
+static const vixl::aarch64::VRegister kParameterFPRegisters[] = {
vixl::aarch64::d0,
vixl::aarch64::d1,
vixl::aarch64::d2,
@@ -111,7 +111,7 @@ const vixl::aarch64::CPURegList callee_saved_core_registers(
? vixl::aarch64::x21.GetCode()
: vixl::aarch64::x20.GetCode()),
vixl::aarch64::x30.GetCode());
-const vixl::aarch64::CPURegList callee_saved_fp_registers(vixl::aarch64::CPURegister::kFPRegister,
+const vixl::aarch64::CPURegList callee_saved_fp_registers(vixl::aarch64::CPURegister::kVRegister,
vixl::aarch64::kDRegSize,
vixl::aarch64::d8.GetCode(),
vixl::aarch64::d15.GetCode());
@@ -162,7 +162,7 @@ static const vixl::aarch64::Register kRuntimeParameterCoreRegisters[] =
vixl::aarch64::x7 };
static constexpr size_t kRuntimeParameterCoreRegistersLength =
arraysize(kRuntimeParameterCoreRegisters);
-static const vixl::aarch64::FPRegister kRuntimeParameterFpuRegisters[] =
+static const vixl::aarch64::VRegister kRuntimeParameterFpuRegisters[] =
{ vixl::aarch64::d0,
vixl::aarch64::d1,
vixl::aarch64::d2,
@@ -175,7 +175,7 @@ static constexpr size_t kRuntimeParameterFpuRegistersLength =
arraysize(kRuntimeParameterCoreRegisters);
class InvokeRuntimeCallingConvention : public CallingConvention<vixl::aarch64::Register,
- vixl::aarch64::FPRegister> {
+ vixl::aarch64::VRegister> {
public:
static constexpr size_t kParameterCoreRegistersLength = arraysize(kParameterCoreRegisters);
@@ -193,7 +193,7 @@ class InvokeRuntimeCallingConvention : public CallingConvention<vixl::aarch64::R
};
class InvokeDexCallingConvention : public CallingConvention<vixl::aarch64::Register,
- vixl::aarch64::FPRegister> {
+ vixl::aarch64::VRegister> {
public:
InvokeDexCallingConvention()
: CallingConvention(kParameterCoreRegisters,
@@ -480,7 +480,7 @@ class CodeGeneratorARM64 : public CodeGenerator {
// requirements, etc.). This also facilitates our task as all other registers
// can easily be mapped via to or from their type and index or code.
static const int kNumberOfAllocatableRegisters = vixl::aarch64::kNumberOfRegisters - 1;
- static const int kNumberOfAllocatableFPRegisters = vixl::aarch64::kNumberOfFPRegisters;
+ static const int kNumberOfAllocatableFPRegisters = vixl::aarch64::kNumberOfVRegisters;
static constexpr int kNumberOfAllocatableRegisterPairs = 0;
void DumpCoreRegister(std::ostream& stream, int reg) const override;
diff --git a/compiler/optimizing/code_generator_vector_x86.cc b/compiler/optimizing/code_generator_vector_x86.cc
index 68aef779f2..1390af2435 100644
--- a/compiler/optimizing/code_generator_vector_x86.cc
+++ b/compiler/optimizing/code_generator_vector_x86.cc
@@ -1201,11 +1201,38 @@ void InstructionCodeGeneratorX86::VisitVecSADAccumulate(HVecSADAccumulate* instr
}
void LocationsBuilderX86::VisitVecDotProd(HVecDotProd* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ locations->SetInAt(2, Location::RequiresFpuRegister());
+ locations->SetOut(Location::SameAsFirstInput());
+ locations->AddTemp(Location::RequiresFpuRegister());
}
void InstructionCodeGeneratorX86::VisitVecDotProd(HVecDotProd* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ bool cpu_has_avx = CpuHasAvxFeatureFlag();
+ LocationSummary* locations = instruction->GetLocations();
+ XmmRegister acc = locations->InAt(0).AsFpuRegister<XmmRegister>();
+ XmmRegister left = locations->InAt(1).AsFpuRegister<XmmRegister>();
+ XmmRegister right = locations->InAt(2).AsFpuRegister<XmmRegister>();
+ switch (instruction->GetPackedType()) {
+ case DataType::Type::kInt32: {
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+ if (!cpu_has_avx) {
+ __ movaps(tmp, right);
+ __ pmaddwd(tmp, left);
+ __ paddd(acc, tmp);
+ } else {
+ __ vpmaddwd(tmp, left, right);
+ __ vpaddd(acc, acc, tmp);
+ }
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unsupported SIMD Type" << instruction->GetPackedType();
+ UNREACHABLE();
+ }
}
// Helper to set up locations for vector memory operations.
diff --git a/compiler/optimizing/code_generator_vector_x86_64.cc b/compiler/optimizing/code_generator_vector_x86_64.cc
index 19dfd1d2a8..7fac44dea8 100644
--- a/compiler/optimizing/code_generator_vector_x86_64.cc
+++ b/compiler/optimizing/code_generator_vector_x86_64.cc
@@ -1174,11 +1174,38 @@ void InstructionCodeGeneratorX86_64::VisitVecSADAccumulate(HVecSADAccumulate* in
}
void LocationsBuilderX86_64::VisitVecDotProd(HVecDotProd* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetInAt(1, Location::RequiresFpuRegister());
+ locations->SetInAt(2, Location::RequiresFpuRegister());
+ locations->SetOut(Location::SameAsFirstInput());
+ locations->AddTemp(Location::RequiresFpuRegister());
}
void InstructionCodeGeneratorX86_64::VisitVecDotProd(HVecDotProd* instruction) {
- LOG(FATAL) << "No SIMD for " << instruction->GetId();
+ bool cpu_has_avx = CpuHasAvxFeatureFlag();
+ LocationSummary* locations = instruction->GetLocations();
+ XmmRegister acc = locations->InAt(0).AsFpuRegister<XmmRegister>();
+ XmmRegister left = locations->InAt(1).AsFpuRegister<XmmRegister>();
+ XmmRegister right = locations->InAt(2).AsFpuRegister<XmmRegister>();
+ switch (instruction->GetPackedType()) {
+ case DataType::Type::kInt32: {
+ DCHECK_EQ(4u, instruction->GetVectorLength());
+ XmmRegister tmp = locations->GetTemp(0).AsFpuRegister<XmmRegister>();
+ if (!cpu_has_avx) {
+ __ movaps(tmp, right);
+ __ pmaddwd(tmp, left);
+ __ paddd(acc, tmp);
+ } else {
+ __ vpmaddwd(tmp, left, right);
+ __ vpaddd(acc, acc, tmp);
+ }
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unsupported SIMD Type" << instruction->GetPackedType();
+ UNREACHABLE();
+ }
}
// Helper to set up locations for vector memory operations.
diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h
index 5556f16740..9c80f323ba 100644
--- a/compiler/optimizing/common_arm64.h
+++ b/compiler/optimizing/common_arm64.h
@@ -87,36 +87,36 @@ inline vixl::aarch64::Register InputRegisterAt(HInstruction* instr, int input_in
instr->InputAt(input_index)->GetType());
}
-inline vixl::aarch64::FPRegister DRegisterFrom(Location location) {
+inline vixl::aarch64::VRegister DRegisterFrom(Location location) {
DCHECK(location.IsFpuRegister()) << location;
- return vixl::aarch64::FPRegister::GetDRegFromCode(location.reg());
+ return vixl::aarch64::VRegister::GetDRegFromCode(location.reg());
}
-inline vixl::aarch64::FPRegister QRegisterFrom(Location location) {
+inline vixl::aarch64::VRegister QRegisterFrom(Location location) {
DCHECK(location.IsFpuRegister()) << location;
- return vixl::aarch64::FPRegister::GetQRegFromCode(location.reg());
+ return vixl::aarch64::VRegister::GetQRegFromCode(location.reg());
}
-inline vixl::aarch64::FPRegister VRegisterFrom(Location location) {
+inline vixl::aarch64::VRegister VRegisterFrom(Location location) {
DCHECK(location.IsFpuRegister()) << location;
- return vixl::aarch64::FPRegister::GetVRegFromCode(location.reg());
+ return vixl::aarch64::VRegister::GetVRegFromCode(location.reg());
}
-inline vixl::aarch64::FPRegister SRegisterFrom(Location location) {
+inline vixl::aarch64::VRegister SRegisterFrom(Location location) {
DCHECK(location.IsFpuRegister()) << location;
- return vixl::aarch64::FPRegister::GetSRegFromCode(location.reg());
+ return vixl::aarch64::VRegister::GetSRegFromCode(location.reg());
}
-inline vixl::aarch64::FPRegister FPRegisterFrom(Location location, DataType::Type type) {
+inline vixl::aarch64::VRegister FPRegisterFrom(Location location, DataType::Type type) {
DCHECK(DataType::IsFloatingPointType(type)) << type;
return type == DataType::Type::kFloat64 ? DRegisterFrom(location) : SRegisterFrom(location);
}
-inline vixl::aarch64::FPRegister OutputFPRegister(HInstruction* instr) {
+inline vixl::aarch64::VRegister OutputFPRegister(HInstruction* instr) {
return FPRegisterFrom(instr->GetLocations()->Out(), instr->GetType());
}
-inline vixl::aarch64::FPRegister InputFPRegisterAt(HInstruction* instr, int input_index) {
+inline vixl::aarch64::VRegister InputFPRegisterAt(HInstruction* instr, int input_index) {
return FPRegisterFrom(instr->GetLocations()->InAt(input_index),
instr->InputAt(input_index)->GetType());
}
@@ -201,7 +201,7 @@ inline Location LocationFrom(const vixl::aarch64::Register& reg) {
return Location::RegisterLocation(ARTRegCodeFromVIXL(reg.GetCode()));
}
-inline Location LocationFrom(const vixl::aarch64::FPRegister& fpreg) {
+inline Location LocationFrom(const vixl::aarch64::VRegister& fpreg) {
return Location::FpuRegisterLocation(fpreg.GetCode());
}
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 185d487dff..c48aaf5904 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -90,8 +90,8 @@ static void MoveFromReturnRegister(Location trg,
Register res_reg = RegisterFrom(ARM64ReturnLocation(type), type);
__ Mov(trg_reg, res_reg, kDiscardForSameWReg);
} else {
- FPRegister trg_reg = FPRegisterFrom(trg, type);
- FPRegister res_reg = FPRegisterFrom(ARM64ReturnLocation(type), type);
+ VRegister trg_reg = FPRegisterFrom(trg, type);
+ VRegister res_reg = FPRegisterFrom(ARM64ReturnLocation(type), type);
__ Fmov(trg_reg, res_reg);
}
}
@@ -435,7 +435,7 @@ static void GenBitCount(HInvoke* instr, DataType::Type type, MacroAssembler* mas
Register src = InputRegisterAt(instr, 0);
Register dst = RegisterFrom(instr->GetLocations()->Out(), type);
- FPRegister fpr = (type == DataType::Type::kInt64) ? temps.AcquireD() : temps.AcquireS();
+ VRegister fpr = (type == DataType::Type::kInt64) ? temps.AcquireD() : temps.AcquireS();
__ Fmov(fpr, src);
__ Cnt(fpr.V8B(), fpr.V8B());
@@ -591,8 +591,8 @@ static void GenMathRound(HInvoke* invoke, bool is_double, vixl::aarch64::MacroAs
// For example, FCVTPS(-1.9) = -1 and FCVTPS(1.1) = 2.
// If we were using this instruction, for most inputs, more handling code would be needed.
LocationSummary* l = invoke->GetLocations();
- FPRegister in_reg = is_double ? DRegisterFrom(l->InAt(0)) : SRegisterFrom(l->InAt(0));
- FPRegister tmp_fp = is_double ? DRegisterFrom(l->GetTemp(0)) : SRegisterFrom(l->GetTemp(0));
+ VRegister in_reg = is_double ? DRegisterFrom(l->InAt(0)) : SRegisterFrom(l->InAt(0));
+ VRegister tmp_fp = is_double ? DRegisterFrom(l->GetTemp(0)) : SRegisterFrom(l->GetTemp(0));
Register out_reg = is_double ? XRegisterFrom(l->Out()) : WRegisterFrom(l->Out());
vixl::aarch64::Label done;
@@ -2015,7 +2015,7 @@ void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
if (mirror::kUseStringCompression) {
// For compressed strings, acquire a SIMD temporary register.
- FPRegister vtmp1 = temps.AcquireVRegisterOfSize(kQRegSize);
+ VRegister vtmp1 = temps.AcquireVRegisterOfSize(kQRegSize);
const size_t c_char_size = DataType::Size(DataType::Type::kInt8);
DCHECK_EQ(c_char_size, 1u);
__ Bind(&compressed_string_preloop);
@@ -3210,12 +3210,36 @@ void IntrinsicCodeGeneratorARM64::VisitFP16ToFloat(HInvoke* invoke) {
MacroAssembler* masm = GetVIXLAssembler();
UseScratchRegisterScope scratch_scope(masm);
Register bits = InputRegisterAt(invoke, 0);
- FPRegister out = SRegisterFrom(invoke->GetLocations()->Out());
- FPRegister half = scratch_scope.AcquireH();
+ VRegister out = SRegisterFrom(invoke->GetLocations()->Out());
+ VRegister half = scratch_scope.AcquireH();
__ Fmov(half, bits); // ARMv8.2
__ Fcvt(out, half);
}
+void IntrinsicLocationsBuilderARM64::VisitFP16ToHalf(HInvoke* invoke) {
+ if (!codegen_->GetInstructionSetFeatures().HasFP16()) {
+ return;
+ }
+
+ LocationSummary* locations = new (allocator_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::RequiresRegister());
+}
+
+void IntrinsicCodeGeneratorARM64::VisitFP16ToHalf(HInvoke* invoke) {
+ DCHECK(codegen_->GetInstructionSetFeatures().HasFP16());
+ MacroAssembler* masm = GetVIXLAssembler();
+ UseScratchRegisterScope scratch_scope(masm);
+ VRegister in = SRegisterFrom(invoke->GetLocations()->InAt(0));
+ VRegister half = scratch_scope.AcquireH();
+ Register out = WRegisterFrom(invoke->GetLocations()->Out());
+ __ Fcvt(half, in);
+ __ Fmov(out, half);
+ __ Sxth(out, out); // sign extend due to returning a short type.
+}
+
UNIMPLEMENTED_INTRINSIC(ARM64, ReferenceGetReferent)
UNIMPLEMENTED_INTRINSIC(ARM64, StringStringIndexOf);
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index 65f388837d..74e861fa8e 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -3071,6 +3071,7 @@ UNIMPLEMENTED_INTRINSIC(ARMVIXL, CRC32Update)
UNIMPLEMENTED_INTRINSIC(ARMVIXL, CRC32UpdateBytes)
UNIMPLEMENTED_INTRINSIC(ARMVIXL, CRC32UpdateByteBuffer)
UNIMPLEMENTED_INTRINSIC(ARMVIXL, FP16ToFloat)
+UNIMPLEMENTED_INTRINSIC(ARMVIXL, FP16ToHalf)
UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOf);
UNIMPLEMENTED_INTRINSIC(ARMVIXL, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index f71d281d5a..b18bbdde2d 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -2708,6 +2708,7 @@ UNIMPLEMENTED_INTRINSIC(MIPS, CRC32Update)
UNIMPLEMENTED_INTRINSIC(MIPS, CRC32UpdateBytes)
UNIMPLEMENTED_INTRINSIC(MIPS, CRC32UpdateByteBuffer)
UNIMPLEMENTED_INTRINSIC(MIPS, FP16ToFloat)
+UNIMPLEMENTED_INTRINSIC(MIPS, FP16ToHalf)
UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOf);
UNIMPLEMENTED_INTRINSIC(MIPS, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 7b87b03b50..e4627db33f 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -2358,6 +2358,7 @@ UNIMPLEMENTED_INTRINSIC(MIPS64, CRC32Update)
UNIMPLEMENTED_INTRINSIC(MIPS64, CRC32UpdateBytes)
UNIMPLEMENTED_INTRINSIC(MIPS64, CRC32UpdateByteBuffer)
UNIMPLEMENTED_INTRINSIC(MIPS64, FP16ToFloat)
+UNIMPLEMENTED_INTRINSIC(MIPS64, FP16ToHalf)
UNIMPLEMENTED_INTRINSIC(MIPS64, StringStringIndexOf);
UNIMPLEMENTED_INTRINSIC(MIPS64, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 5a622ca6d1..95aa4c0eaa 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -3082,6 +3082,7 @@ UNIMPLEMENTED_INTRINSIC(X86, CRC32Update)
UNIMPLEMENTED_INTRINSIC(X86, CRC32UpdateBytes)
UNIMPLEMENTED_INTRINSIC(X86, CRC32UpdateByteBuffer)
UNIMPLEMENTED_INTRINSIC(X86, FP16ToFloat)
+UNIMPLEMENTED_INTRINSIC(X86, FP16ToHalf)
UNIMPLEMENTED_INTRINSIC(X86, StringStringIndexOf);
UNIMPLEMENTED_INTRINSIC(X86, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index cbf66069fe..8dbc0d3062 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -2749,6 +2749,7 @@ UNIMPLEMENTED_INTRINSIC(X86_64, CRC32Update)
UNIMPLEMENTED_INTRINSIC(X86_64, CRC32UpdateBytes)
UNIMPLEMENTED_INTRINSIC(X86_64, CRC32UpdateByteBuffer)
UNIMPLEMENTED_INTRINSIC(X86_64, FP16ToFloat)
+UNIMPLEMENTED_INTRINSIC(X86_64, FP16ToHalf)
UNIMPLEMENTED_INTRINSIC(X86_64, StringStringIndexOf);
UNIMPLEMENTED_INTRINSIC(X86_64, StringStringIndexOfAfter);
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 9c4e9d25f7..567a41e2fd 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -1623,14 +1623,20 @@ bool HLoopOptimization::TrySetVectorType(DataType::Type type, uint64_t* restrict
kNoDotProd;
return TrySetVectorLength(16);
case DataType::Type::kUint16:
- case DataType::Type::kInt16:
*restrictions |= kNoDiv |
kNoAbs |
kNoSignedHAdd |
kNoUnroundedHAdd |
- kNoSAD|
+ kNoSAD |
kNoDotProd;
return TrySetVectorLength(8);
+ case DataType::Type::kInt16:
+ *restrictions |= kNoDiv |
+ kNoAbs |
+ kNoSignedHAdd |
+ kNoUnroundedHAdd |
+ kNoSAD;
+ return TrySetVectorLength(8);
case DataType::Type::kInt32:
*restrictions |= kNoDiv | kNoSAD;
return TrySetVectorLength(4);
@@ -2166,7 +2172,7 @@ bool HLoopOptimization::VectorizeDotProdIdiom(LoopNode* node,
bool generate_code,
DataType::Type reduction_type,
uint64_t restrictions) {
- if (!instruction->IsAdd() || (reduction_type != DataType::Type::kInt32)) {
+ if (!instruction->IsAdd() || reduction_type != DataType::Type::kInt32) {
return false;
}
diff --git a/compiler/optimizing/scheduler.cc b/compiler/optimizing/scheduler.cc
index e5ff8a886a..f722cf91a7 100644
--- a/compiler/optimizing/scheduler.cc
+++ b/compiler/optimizing/scheduler.cc
@@ -282,6 +282,36 @@ static bool IsBetterCandidateWithMoreLikelyDependencies(HInstruction* new_candid
}
}
+void SchedulingGraph::AddCrossIterationDependencies(SchedulingNode* node) {
+ for (HInstruction* instruction : node->GetInstruction()->GetInputs()) {
+ // Having a phi-function from a loop header as an input means the current node of the
+ // scheduling graph has a cross-iteration dependency because such phi-functions bring values
+ // from the previous iteration to the current iteration.
+ if (!instruction->IsLoopHeaderPhi()) {
+ continue;
+ }
+ for (HInstruction* phi_input : instruction->GetInputs()) {
+ // As a scheduling graph of the current basic block is built by
+ // processing instructions bottom-up, nullptr returned by GetNode means
+ // an instruction defining a value for the phi is either before the
+ // instruction represented by node or it is in a different basic block.
+ SchedulingNode* def_node = GetNode(phi_input);
+
+ // We don't create a dependency if there are uses besides the use in phi.
+ // In such cases a register to hold phi_input is usually allocated and
+ // a MOV instruction is generated. In cases with multiple uses and no MOV
+ // instruction, reordering creating a MOV instruction can improve
+ // performance more than an attempt to avoid a MOV instruction.
+ if (def_node != nullptr && def_node != node && phi_input->GetUses().HasExactlyOneElement()) {
+ // We have an implicit data dependency between node and def_node.
+ // AddAddDataDependency cannot be used because it is for explicit data dependencies.
+ // So AddOtherDependency is used.
+ AddOtherDependency(def_node, node);
+ }
+ }
+ }
+}
+
void SchedulingGraph::AddDependencies(SchedulingNode* instruction_node,
bool is_scheduling_barrier) {
HInstruction* instruction = instruction_node->GetInstruction();
@@ -340,7 +370,11 @@ void SchedulingGraph::AddDependencies(SchedulingNode* instruction_node,
if (other_node->IsSchedulingBarrier()) {
// We have reached a scheduling barrier so we can stop further
// processing.
- DCHECK(other_node->HasOtherDependency(instruction_node));
+ //
+ // As a "other" dependency is not set up if a data dependency exists, we need to check that
+ // one of them must exist.
+ DCHECK(other_node->HasOtherDependency(instruction_node)
+ || other_node->HasDataDependency(instruction_node));
break;
}
if (side_effect_dependency_analysis_.HasSideEffectDependency(other, instruction)) {
@@ -372,6 +406,8 @@ void SchedulingGraph::AddDependencies(SchedulingNode* instruction_node,
AddOtherDependency(GetNode(use.GetUser()->GetHolder()), instruction_node);
}
}
+
+ AddCrossIterationDependencies(instruction_node);
}
static const std::string InstructionTypeId(const HInstruction* instruction) {
diff --git a/compiler/optimizing/scheduler.h b/compiler/optimizing/scheduler.h
index a97eda7e85..f7180a02d7 100644
--- a/compiler/optimizing/scheduler.h
+++ b/compiler/optimizing/scheduler.h
@@ -183,7 +183,9 @@ class SchedulingNode : public DeletableArenaObject<kArenaAllocScheduler> {
void AddOtherPredecessor(SchedulingNode* predecessor) {
// Check whether the predecessor has been added earlier.
- if (HasOtherDependency(predecessor)) {
+ // As an optimization of the scheduling graph, we don't need to create another dependency if
+ // there is a data dependency between scheduling nodes.
+ if (HasOtherDependency(predecessor) || HasDataDependency(predecessor)) {
return;
}
other_predecessors_.push_back(predecessor);
@@ -362,6 +364,25 @@ class SchedulingGraph : public ValueObject {
AddDependency(node, dependency, /*is_data_dependency*/false);
}
+ // Analyze whether the scheduling node has cross-iteration dependencies which mean it uses
+ // values defined on the previous iteration.
+ //
+ // Supported cases:
+ //
+ // L:
+ // v2 = loop_head_phi(v1)
+ // instr1(v2)
+ // v1 = instr2
+ // goto L
+ //
+ // In such cases moving instr2 before instr1 creates intersecting live ranges
+ // of v1 and v2. As a result a separate register is needed to keep the value
+ // defined by instr2 which is only used on the next iteration.
+ // If instr2 is not moved, no additional register is needed. The register
+ // used by instr1 is reused.
+ // To prevent such a situation a "other" dependency between instr1 and instr2 must be set.
+ void AddCrossIterationDependencies(SchedulingNode* node);
+
// Add dependencies nodes for the given `SchedulingNode`: inputs, environments, and side-effects.
void AddDependencies(SchedulingNode* node, bool is_scheduling_barrier = false);
diff --git a/compiler/utils/arm64/assembler_arm64.h b/compiler/utils/arm64/assembler_arm64.h
index 594c6b4b75..fe2f1766c2 100644
--- a/compiler/utils/arm64/assembler_arm64.h
+++ b/compiler/utils/arm64/assembler_arm64.h
@@ -150,12 +150,12 @@ class Arm64Assembler final : public Assembler {
return vixl::aarch64::Register::GetWRegFromCode(code);
}
- static vixl::aarch64::FPRegister reg_d(int code) {
- return vixl::aarch64::FPRegister::GetDRegFromCode(code);
+ static vixl::aarch64::VRegister reg_d(int code) {
+ return vixl::aarch64::VRegister::GetDRegFromCode(code);
}
- static vixl::aarch64::FPRegister reg_s(int code) {
- return vixl::aarch64::FPRegister::GetSRegFromCode(code);
+ static vixl::aarch64::VRegister reg_s(int code) {
+ return vixl::aarch64::VRegister::GetSRegFromCode(code);
}
private:
diff --git a/compiler/utils/arm64/jni_macro_assembler_arm64.cc b/compiler/utils/arm64/jni_macro_assembler_arm64.cc
index d6ce03387c..0eab49f27d 100644
--- a/compiler/utils/arm64/jni_macro_assembler_arm64.cc
+++ b/compiler/utils/arm64/jni_macro_assembler_arm64.cc
@@ -689,7 +689,7 @@ void Arm64JNIMacroAssembler::BuildFrame(size_t frame_size,
const ManagedRegisterEntrySpills& entry_spills) {
// Setup VIXL CPURegList for callee-saves.
CPURegList core_reg_list(CPURegister::kRegister, kXRegSize, 0);
- CPURegList fp_reg_list(CPURegister::kFPRegister, kDRegSize, 0);
+ CPURegList fp_reg_list(CPURegister::kVRegister, kDRegSize, 0);
for (auto r : callee_save_regs) {
Arm64ManagedRegister reg = r.AsArm64();
if (reg.IsXRegister()) {
@@ -745,7 +745,7 @@ void Arm64JNIMacroAssembler::RemoveFrame(size_t frame_size,
bool may_suspend) {
// Setup VIXL CPURegList for callee-saves.
CPURegList core_reg_list(CPURegister::kRegister, kXRegSize, 0);
- CPURegList fp_reg_list(CPURegister::kFPRegister, kDRegSize, 0);
+ CPURegList fp_reg_list(CPURegister::kVRegister, kDRegSize, 0);
for (auto r : callee_save_regs) {
Arm64ManagedRegister reg = r.AsArm64();
if (reg.IsXRegister()) {
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index 166aec81a7..55f7691514 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -2268,6 +2268,20 @@ void X86Assembler::pmaddwd(XmmRegister dst, XmmRegister src) {
}
+void X86Assembler::vpmaddwd(XmmRegister dst, XmmRegister src1, XmmRegister src2) {
+ DCHECK(CpuHasAVXorAVX2FeatureFlag());
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ uint8_t ByteZero = 0x00, ByteOne = 0x00;
+ ByteZero = EmitVexPrefixByteZero(/* is_twobyte_form=*/ true);
+ X86ManagedRegister vvvv_reg = X86ManagedRegister::FromXmmRegister(src1);
+ ByteOne = EmitVexPrefixByteOne(/*R=*/ false, vvvv_reg, SET_VEX_L_128, SET_VEX_PP_66);
+ EmitUint8(ByteZero);
+ EmitUint8(ByteOne);
+ EmitUint8(0xF5);
+ EmitXmmRegisterOperand(dst, src2);
+}
+
+
void X86Assembler::phaddw(XmmRegister dst, XmmRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x66);
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index 1b6941c2e6..27fde26c80 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -577,6 +577,7 @@ class X86Assembler final : public Assembler {
void pavgw(XmmRegister dst, XmmRegister src);
void psadbw(XmmRegister dst, XmmRegister src);
void pmaddwd(XmmRegister dst, XmmRegister src);
+ void vpmaddwd(XmmRegister dst, XmmRegister src1, XmmRegister src2);
void phaddw(XmmRegister dst, XmmRegister src);
void phaddd(XmmRegister dst, XmmRegister src);
void haddps(XmmRegister dst, XmmRegister src);
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index 12d9646ace..9253730797 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -965,6 +965,11 @@ TEST_F(AssemblerX86Test, PMAddWD) {
DriverStr(RepeatFF(&x86::X86Assembler::pmaddwd, "pmaddwd %{reg2}, %{reg1}"), "pmaddwd");
}
+TEST_F(AssemblerX86AVXTest, VPMAddWD) {
+ DriverStr(
+ RepeatFFF(&x86::X86Assembler::vpmaddwd, "vpmaddwd %{reg3}, %{reg2}, %{reg1}"), "vpmaddwd");
+}
+
TEST_F(AssemblerX86Test, PHAddW) {
DriverStr(RepeatFF(&x86::X86Assembler::phaddw, "phaddw %{reg2}, %{reg1}"), "phaddw");
}
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index 64246aae0e..2c5dd9e949 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -71,6 +71,7 @@ bool X86_64Assembler::CpuHasAVXorAVX2FeatureFlag() {
return false;
}
+
void X86_64Assembler::call(CpuRegister reg) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitOptionalRex32(reg);
@@ -758,7 +759,6 @@ void X86_64Assembler::movd(CpuRegister dst, XmmRegister src, bool is64bit) {
EmitOperand(src.LowBits(), Operand(dst));
}
-
void X86_64Assembler::addss(XmmRegister dst, XmmRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xF3);
@@ -768,7 +768,6 @@ void X86_64Assembler::addss(XmmRegister dst, XmmRegister src) {
EmitXmmRegisterOperand(dst.LowBits(), src);
}
-
void X86_64Assembler::addss(XmmRegister dst, const Address& src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0xF3);
@@ -2633,7 +2632,6 @@ void X86_64Assembler::xorps(XmmRegister dst, XmmRegister src) {
EmitXmmRegisterOperand(dst.LowBits(), src);
}
-
void X86_64Assembler::pxor(XmmRegister dst, XmmRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x66);
@@ -3145,6 +3143,35 @@ void X86_64Assembler::pmaddwd(XmmRegister dst, XmmRegister src) {
EmitXmmRegisterOperand(dst.LowBits(), src);
}
+void X86_64Assembler::vpmaddwd(XmmRegister dst, XmmRegister src1, XmmRegister src2) {
+ DCHECK(CpuHasAVXorAVX2FeatureFlag());
+ AssemblerBuffer::EnsureCapacity ensured(&buffer_);
+ bool is_twobyte_form = false;
+ uint8_t ByteZero = 0x00, ByteOne = 0x00, ByteTwo = 0x00;
+ if (!src2.NeedsRex()) {
+ is_twobyte_form = true;
+ }
+ ByteZero = EmitVexPrefixByteZero(is_twobyte_form);
+ X86_64ManagedRegister vvvv_reg =
+ X86_64ManagedRegister::FromXmmRegister(src1.AsFloatRegister());
+ if (is_twobyte_form) {
+ ByteOne = EmitVexPrefixByteOne(dst.NeedsRex(), vvvv_reg, SET_VEX_L_128, SET_VEX_PP_66);
+ } else {
+ ByteOne = EmitVexPrefixByteOne(dst.NeedsRex(),
+ /*X=*/ false,
+ src2.NeedsRex(),
+ SET_VEX_M_0F);
+ ByteTwo = EmitVexPrefixByteTwo(/*W=*/ false, vvvv_reg, SET_VEX_L_128, SET_VEX_PP_66);
+ }
+ EmitUint8(ByteZero);
+ EmitUint8(ByteOne);
+ if (!is_twobyte_form) {
+ EmitUint8(ByteTwo);
+ }
+ EmitUint8(0xF5);
+ EmitXmmRegisterOperand(dst.LowBits(), src2);
+}
+
void X86_64Assembler::phaddw(XmmRegister dst, XmmRegister src) {
AssemblerBuffer::EnsureCapacity ensured(&buffer_);
EmitUint8(0x66);
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index 15f3ab9f25..70072d9224 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -615,6 +615,7 @@ class X86_64Assembler final : public Assembler {
void pavgw(XmmRegister dst, XmmRegister src);
void psadbw(XmmRegister dst, XmmRegister src);
void pmaddwd(XmmRegister dst, XmmRegister src);
+ void vpmaddwd(XmmRegister dst, XmmRegister src1, XmmRegister src2);
void phaddw(XmmRegister dst, XmmRegister src);
void phaddd(XmmRegister dst, XmmRegister src);
void haddps(XmmRegister dst, XmmRegister src);
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index e3b8390468..3921c4afb0 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -1740,6 +1740,11 @@ TEST_F(AssemblerX86_64Test, Pmaddwd) {
DriverStr(RepeatFF(&x86_64::X86_64Assembler::pmaddwd, "pmaddwd %{reg2}, %{reg1}"), "pmadwd");
}
+TEST_F(AssemblerX86_64AVXTest, VPmaddwd) {
+ DriverStr(RepeatFFF(&x86_64::X86_64Assembler::vpmaddwd,
+ "vpmaddwd %{reg3}, %{reg2}, %{reg1}"), "vpmaddwd");
+}
+
TEST_F(AssemblerX86_64Test, Phaddw) {
DriverStr(RepeatFF(&x86_64::X86_64Assembler::phaddw, "phaddw %{reg2}, %{reg1}"), "phaddw");
}
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index cd8f98d763..095f027ca3 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -63,6 +63,7 @@
#include "debug/method_debug_info.h"
#include "dex/descriptors_names.h"
#include "dex/dex_file-inl.h"
+#include "dex/dex_file_loader.h"
#include "dex/quick_compiler_callbacks.h"
#include "dex/verification_results.h"
#include "dex2oat_options.h"
@@ -86,6 +87,7 @@
#include "mirror/class_loader.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
+#include "oat.h"
#include "oat_file.h"
#include "oat_file_assistant.h"
#include "profile/profile_compilation_info.h"
@@ -280,6 +282,14 @@ NO_RETURN static void Usage(const char* fmt, ...) {
UsageError(" Do not include the arch as part of the name, it is added automatically.");
UsageError(" Example: --boot-image=/system/framework/boot.art");
UsageError(" (specifies /system/framework/<arch>/boot.art as the image file)");
+ UsageError(" Example: --boot-image=boot.art:boot-framework.art");
+ UsageError(" (specifies <bcp-path1>/<arch>/boot.art as the image file and");
+ UsageError(" <bcp-path2>/<arch>/boot-framework.art as the image extension file");
+ UsageError(" with paths taken from corresponding boot class path components)");
+ UsageError(" Example: --boot-image=/apex/com.android.art/boot.art:/system/framework/*:*");
+ UsageError(" (specifies /apex/com.android.art/<arch>/boot.art as the image");
+ UsageError(" file and search for extensions in /framework/system and boot");
+ UsageError(" class path components' paths)");
UsageError(" Default: $ANDROID_ROOT/system/framework/boot.art");
UsageError("");
UsageError(" --android-root=<path>: used to locate libraries for portable linking.");
@@ -752,16 +762,31 @@ class Dex2Oat final {
void ProcessOptions(ParserOptions* parser_options) {
compiler_options_->compile_pic_ = true; // All AOT compilation is PIC.
+
+ if (android_root_.empty()) {
+ const char* android_root_env_var = getenv("ANDROID_ROOT");
+ if (android_root_env_var == nullptr) {
+ Usage("--android-root unspecified and ANDROID_ROOT not set");
+ }
+ android_root_ += android_root_env_var;
+ }
+
+ if (!parser_options->boot_image_filename.empty()) {
+ boot_image_filename_ = parser_options->boot_image_filename;
+ }
+
DCHECK(compiler_options_->image_type_ == CompilerOptions::ImageType::kNone);
if (!image_filenames_.empty()) {
- if (android::base::EndsWith(image_filenames_[0], "apex.art")) {
+ if (!boot_image_filename_.empty()) {
+ compiler_options_->image_type_ = CompilerOptions::ImageType::kBootImageExtension;
+ } else if (android::base::EndsWith(image_filenames_[0], "apex.art")) {
compiler_options_->image_type_ = CompilerOptions::ImageType::kApexBootImage;
} else {
compiler_options_->image_type_ = CompilerOptions::ImageType::kBootImage;
}
}
if (app_image_fd_ != -1 || !app_image_file_name_.empty()) {
- if (compiler_options_->IsBootImage()) {
+ if (compiler_options_->IsBootImage() || compiler_options_->IsBootImageExtension()) {
Usage("Can't have both --image and (--app-image-fd or --app-image-file)");
}
compiler_options_->image_type_ = CompilerOptions::ImageType::kAppImage;
@@ -818,19 +843,9 @@ class Dex2Oat final {
Usage("--oat-file arguments do not match --image arguments");
}
- if (android_root_.empty()) {
- const char* android_root_env_var = getenv("ANDROID_ROOT");
- if (android_root_env_var == nullptr) {
- Usage("--android-root unspecified and ANDROID_ROOT not set");
- }
- android_root_ += android_root_env_var;
- }
-
- if (!IsBootImage() && parser_options->boot_image_filename.empty()) {
- parser_options->boot_image_filename = GetDefaultBootImageLocation(android_root_);
- }
- if (!parser_options->boot_image_filename.empty()) {
- boot_image_filename_ = parser_options->boot_image_filename;
+ if (!IsBootImage() && boot_image_filename_.empty()) {
+ DCHECK(!IsBootImageExtension());
+ boot_image_filename_ = GetDefaultBootImageLocation(android_root_);
}
if (dex_filenames_.empty() && zip_fd_ == -1) {
@@ -937,8 +952,8 @@ class Dex2Oat final {
// Fill some values into the key-value store for the oat header.
key_value_store_.reset(new SafeMap<std::string, std::string>());
- // Automatically force determinism for the boot image in a host build.
- if (!kIsTargetBuild && IsBootImage()) {
+ // Automatically force determinism for the boot image and boot image extensions in a host build.
+ if (!kIsTargetBuild && (IsBootImage() || IsBootImageExtension())) {
force_determinism_ = true;
}
compiler_options_->force_determinism_ = force_determinism_;
@@ -961,18 +976,21 @@ class Dex2Oat final {
if (image_filenames_[0].rfind('/') == std::string::npos) {
Usage("Unusable boot image filename %s", image_filenames_[0].c_str());
}
- image_filenames_ = ImageSpace::ExpandMultiImageLocations(dex_locations_, image_filenames_[0]);
+ image_filenames_ = ImageSpace::ExpandMultiImageLocations(
+ ArrayRef<const std::string>(dex_locations_), image_filenames_[0], IsBootImageExtension());
if (oat_filenames_[0].rfind('/') == std::string::npos) {
Usage("Unusable boot image oat filename %s", oat_filenames_[0].c_str());
}
- oat_filenames_ = ImageSpace::ExpandMultiImageLocations(dex_locations_, oat_filenames_[0]);
+ oat_filenames_ = ImageSpace::ExpandMultiImageLocations(
+ ArrayRef<const std::string>(dex_locations_), oat_filenames_[0], IsBootImageExtension());
if (!oat_unstripped_.empty()) {
if (oat_unstripped_[0].rfind('/') == std::string::npos) {
Usage("Unusable boot image symbol filename %s", oat_unstripped_[0].c_str());
}
- oat_unstripped_ = ImageSpace::ExpandMultiImageLocations(dex_locations_, oat_unstripped_[0]);
+ oat_unstripped_ = ImageSpace::ExpandMultiImageLocations(
+ ArrayRef<const std::string>(dex_locations_), oat_unstripped_[0], IsBootImageExtension());
}
}
@@ -1206,7 +1224,7 @@ class Dex2Oat final {
PruneNonExistentDexFiles();
// Expand oat and image filenames for multi image.
- if (IsBootImage() && image_filenames_.size() == 1) {
+ if ((IsBootImage() || IsBootImageExtension()) && image_filenames_.size() == 1) {
ExpandOatAndImageFilenames();
}
@@ -1431,12 +1449,13 @@ class Dex2Oat final {
return dex2oat::ReturnCode::kOther;
}
- // Verification results are null since we don't know if we will need them yet as the compler
+ // Verification results are null since we don't know if we will need them yet as the compiler
// filter may change.
callbacks_.reset(new QuickCompilerCallbacks(
- IsBootImage() ?
- CompilerCallbacks::CallbackMode::kCompileBootImage :
- CompilerCallbacks::CallbackMode::kCompileApp));
+ // For class verification purposes, boot image extension is the same as boot image.
+ (IsBootImage() || IsBootImageExtension())
+ ? CompilerCallbacks::CallbackMode::kCompileBootImage
+ : CompilerCallbacks::CallbackMode::kCompileApp));
RuntimeArgumentMap runtime_options;
if (!PrepareRuntimeOptions(&runtime_options, callbacks_.get())) {
@@ -1489,7 +1508,7 @@ class Dex2Oat final {
// store which is used for determining whether the oat file is up to date,
// together with the boot class path locations and checksums stored below.
CompilerFilter::Filter original_compiler_filter = compiler_options_->GetCompilerFilter();
- if (!IsBootImage() && IsVeryLarge(dex_files)) {
+ if (!IsBootImage() && !IsBootImageExtension() && IsVeryLarge(dex_files)) {
// Disable app image to make sure dex2oat unloading is enabled.
compiler_options_->image_type_ = CompilerOptions::ImageType::kNone;
@@ -1512,14 +1531,64 @@ class Dex2Oat final {
callbacks_->SetVerificationResults(verification_results_.get());
}
- if (IsBootImage()) {
- // For boot image, pass opened dex files to the Runtime::Create().
+ if (IsBootImage() || IsBootImageExtension()) {
+ // For boot image or boot image extension, pass opened dex files to the Runtime::Create().
// Note: Runtime acquires ownership of these dex files.
runtime_options.Set(RuntimeArgumentMap::BootClassPathDexList, &opened_dex_files_);
}
if (!CreateRuntime(std::move(runtime_options))) {
return dex2oat::ReturnCode::kCreateRuntime;
}
+ ArrayRef<const DexFile* const> bcp_dex_files(runtime_->GetClassLinker()->GetBootClassPath());
+ if (IsBootImage() || IsBootImageExtension()) {
+ // Check boot class path dex files and, if compiling an extension, the images it depends on.
+ if ((IsBootImage() && bcp_dex_files.size() != dex_files.size()) ||
+ (IsBootImageExtension() && bcp_dex_files.size() <= dex_files.size())) {
+ LOG(ERROR) << "Unexpected number of boot class path dex files for boot image or extension, "
+ << bcp_dex_files.size() << (IsBootImage() ? " != " : " <= ") << dex_files.size();
+ return dex2oat::ReturnCode::kOther;
+ }
+ if (!std::equal(dex_files.begin(), dex_files.end(), bcp_dex_files.end() - dex_files.size())) {
+ LOG(ERROR) << "Boot class path dex files do not end with the compiled dex files.";
+ return dex2oat::ReturnCode::kOther;
+ }
+ size_t bcp_df_pos = 0u;
+ size_t bcp_df_end = bcp_dex_files.size();
+ for (const std::string& bcp_location : runtime_->GetBootClassPathLocations()) {
+ if (bcp_df_pos == bcp_df_end || bcp_dex_files[bcp_df_pos]->GetLocation() != bcp_location) {
+ LOG(ERROR) << "Missing dex file for boot class component " << bcp_location;
+ return dex2oat::ReturnCode::kOther;
+ }
+ CHECK(!DexFileLoader::IsMultiDexLocation(bcp_dex_files[bcp_df_pos]->GetLocation().c_str()));
+ ++bcp_df_pos;
+ while (bcp_df_pos != bcp_df_end &&
+ DexFileLoader::IsMultiDexLocation(bcp_dex_files[bcp_df_pos]->GetLocation().c_str())) {
+ ++bcp_df_pos;
+ }
+ }
+ if (bcp_df_pos != bcp_df_end) {
+ LOG(ERROR) << "Unexpected dex file in boot class path "
+ << bcp_dex_files[bcp_df_pos]->GetLocation();
+ return dex2oat::ReturnCode::kOther;
+ }
+ auto lacks_image = [](const DexFile* df) {
+ if (kIsDebugBuild && df->GetOatDexFile() != nullptr) {
+ const OatFile* oat_file = df->GetOatDexFile()->GetOatFile();
+ CHECK(oat_file != nullptr);
+ const auto& image_spaces = Runtime::Current()->GetHeap()->GetBootImageSpaces();
+ CHECK(std::any_of(image_spaces.begin(),
+ image_spaces.end(),
+ [=](const ImageSpace* space) {
+ return oat_file == space->GetOatFile();
+ }));
+ }
+ return df->GetOatDexFile() == nullptr;
+ };
+ if (std::any_of(bcp_dex_files.begin(), bcp_dex_files.end() - dex_files.size(), lacks_image)) {
+ LOG(ERROR) << "Missing required boot image(s) for boot image extension.";
+ return dex2oat::ReturnCode::kOther;
+ }
+ }
if (!compilation_reason_.empty()) {
key_value_store_->Put(OatHeader::kCompilationReasonKey, compilation_reason_);
@@ -1529,17 +1598,32 @@ class Dex2Oat final {
// If we're compiling the boot image, store the boot classpath into the Key-Value store.
// We use this when loading the boot image.
key_value_store_->Put(OatHeader::kBootClassPathKey, android::base::Join(dex_locations_, ':'));
- }
-
- if (!IsBootImage()) {
+ } else if (IsBootImageExtension()) {
+ // Validate the boot class path and record the dependency on the loaded boot images.
+ TimingLogger::ScopedTiming t3("Loading image checksum", timings_);
+ Runtime* runtime = Runtime::Current();
+ std::string full_bcp = android::base::Join(runtime->GetBootClassPathLocations(), ':');
+ std::string extension_part = ":" + android::base::Join(dex_locations_, ':');
+ if (!android::base::EndsWith(full_bcp, extension_part)) {
+ LOG(ERROR) << "Full boot class path does not end with extension parts, full: " << full_bcp
+ << ", extension: " << extension_part.substr(1u);
+ return dex2oat::ReturnCode::kOther;
+ }
+ std::string bcp_dependency = full_bcp.substr(0u, full_bcp.size() - extension_part.size());
+ key_value_store_->Put(OatHeader::kBootClassPathKey, bcp_dependency);
+ ArrayRef<const DexFile* const> bcp_dex_files_dependency =
+ bcp_dex_files.SubArray(/*pos=*/ 0u, bcp_dex_files.size() - dex_files.size());
+ ArrayRef<ImageSpace* const> image_spaces(runtime->GetHeap()->GetBootImageSpaces());
+ key_value_store_->Put(
+ OatHeader::kBootClassPathChecksumsKey,
+ gc::space::ImageSpace::GetBootClassPathChecksums(image_spaces, bcp_dex_files_dependency));
+ } else {
if (CompilerFilter::DependsOnImageChecksum(original_compiler_filter)) {
TimingLogger::ScopedTiming t3("Loading image checksum", timings_);
Runtime* runtime = Runtime::Current();
key_value_store_->Put(OatHeader::kBootClassPathKey,
android::base::Join(runtime->GetBootClassPathLocations(), ':'));
- std::vector<ImageSpace*> image_spaces = runtime->GetHeap()->GetBootImageSpaces();
- const std::vector<const DexFile*>& bcp_dex_files =
- runtime->GetClassLinker()->GetBootClassPath();
+ ArrayRef<ImageSpace* const> image_spaces(runtime->GetHeap()->GetBootImageSpaces());
key_value_store_->Put(
OatHeader::kBootClassPathChecksumsKey,
gc::space::ImageSpace::GetBootClassPathChecksums(image_spaces, bcp_dex_files));
@@ -1607,7 +1691,7 @@ class Dex2Oat final {
CHECK(driver_ == nullptr);
// If we use a swap file, ensure we are above the threshold to make it necessary.
if (swap_fd_ != -1) {
- if (!UseSwap(IsBootImage(), dex_files)) {
+ if (!UseSwap(IsBootImage() || IsBootImageExtension(), dex_files)) {
close(swap_fd_);
swap_fd_ = -1;
VLOG(compiler) << "Decided to run without swap.";
@@ -1624,7 +1708,7 @@ class Dex2Oat final {
Thread* self = Thread::Current();
WellKnownClasses::Init(self->GetJniEnv());
- if (!IsBootImage()) {
+ if (!IsBootImage() && !IsBootImageExtension()) {
constexpr bool kSaveDexInput = false;
if (kSaveDexInput) {
SaveDexInput();
@@ -1713,7 +1797,7 @@ class Dex2Oat final {
if (!no_inline_filters.empty()) {
std::vector<const DexFile*> class_path_files;
- if (!IsBootImage()) {
+ if (!IsBootImage() && !IsBootImageExtension()) {
// The class loader context is used only for apps.
class_path_files = class_loader_context_->FlattenOpenedDexFiles();
}
@@ -1758,7 +1842,7 @@ class Dex2Oat final {
compiler_kind_,
thread_count_,
swap_fd_));
- if (!IsBootImage()) {
+ if (!IsBootImage() && !IsBootImageExtension()) {
driver_->SetClasspathDexFiles(class_loader_context_->FlattenOpenedDexFiles());
}
@@ -1806,7 +1890,7 @@ class Dex2Oat final {
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
jobject class_loader = nullptr;
- if (!IsBootImage()) {
+ if (!IsBootImage() && !IsBootImageExtension()) {
class_loader =
class_loader_context_->CreateClassLoader(compiler_options_->dex_files_for_oat_file_);
callbacks_->SetDexFiles(&dex_files);
@@ -1950,7 +2034,7 @@ class Dex2Oat final {
{
TimingLogger::ScopedTiming t2("dex2oat Write VDEX", timings_);
- DCHECK(IsBootImage() || oat_files_.size() == 1u);
+ DCHECK(IsBootImage() || IsBootImageExtension() || oat_files_.size() == 1u);
verifier::VerifierDeps* verifier_deps = callbacks_->GetVerifierDeps();
for (size_t i = 0, size = oat_files_.size(); i != size; ++i) {
File* vdex_file = vdex_files_[i].get();
@@ -2174,7 +2258,7 @@ class Dex2Oat final {
}
bool IsImage() const {
- return IsAppImage() || IsBootImage();
+ return IsAppImage() || IsBootImage() || IsBootImageExtension();
}
bool IsAppImage() const {
@@ -2185,6 +2269,10 @@ class Dex2Oat final {
return compiler_options_->IsBootImage();
}
+ bool IsBootImageExtension() const {
+ return compiler_options_->IsBootImageExtension();
+ }
+
bool IsHost() const {
return is_host_;
}
@@ -2389,7 +2477,7 @@ class Dex2Oat final {
bool PrepareRuntimeOptions(RuntimeArgumentMap* runtime_options,
QuickCompilerCallbacks* callbacks) {
RuntimeOptions raw_options;
- if (boot_image_filename_.empty()) {
+ if (IsBootImage()) {
std::string boot_class_path = "-Xbootclasspath:";
boot_class_path += android::base::Join(dex_filenames_, ':');
raw_options.push_back(std::make_pair(boot_class_path, nullptr));
@@ -2479,7 +2567,7 @@ class Dex2Oat final {
bool CreateImageFile()
REQUIRES(!Locks::mutator_lock_) {
CHECK(image_writer_ != nullptr);
- if (!IsBootImage()) {
+ if (!IsBootImage() && !IsBootImageExtension()) {
CHECK(image_filenames_.empty());
image_filenames_.push_back(app_image_file_name_);
}
@@ -2862,7 +2950,10 @@ static dex2oat::ReturnCode Dex2oat(int argc, char** argv) {
// 3) Compiling with --host
// 4) Compiling on the host (not a target build)
// Otherwise, print a stripped command line.
- if (kIsDebugBuild || dex2oat->IsBootImage() || dex2oat->IsHost() || !kIsTargetBuild) {
+ if (kIsDebugBuild ||
+ dex2oat->IsBootImage() || dex2oat->IsBootImageExtension() ||
+ dex2oat->IsHost() ||
+ !kIsTargetBuild) {
LOG(INFO) << CommandLine();
} else {
LOG(INFO) << StrippedCommandLine();
diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index d0d50812d6..1a0a9c8892 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -14,31 +14,44 @@
* limitations under the License.
*/
+#include <fstream>
#include <regex>
#include <sstream>
#include <string>
#include <vector>
+#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>
#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include "common_runtime_test.h"
+#include "base/array_ref.h"
#include "base/file_utils.h"
#include "base/macros.h"
+#include "base/mem_map.h"
+#include "base/string_view_cpp20.h"
#include "base/unix_file/fd_file.h"
#include "base/utils.h"
#include "dex/art_dex_file_loader.h"
#include "dex/dex_file-inl.h"
#include "dex/dex_file_loader.h"
#include "dex/method_reference.h"
+#include "gc/space/image_space.h"
#include "profile/profile_compilation_info.h"
#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-current-inl.h"
namespace art {
+// A suitable address for loading the core images.
+constexpr uint32_t kBaseAddress = ART_BASE_ADDRESS;
+
struct ImageSizes {
size_t art_size = 0;
size_t oat_size = 0;
@@ -132,8 +145,12 @@ class Dex2oatImageTest : public CommonRuntimeTest {
scratch_dir.pop_back();
}
CHECK(!scratch_dir.empty()) << "No directory " << scratch.GetFilename();
+ std::vector<std::string> libcore_dex_files = GetLibCoreDexFileNames();
+ ArrayRef<const std::string> dex_files(libcore_dex_files);
+ std::vector<std::string> local_extra_args = extra_args;
+ local_extra_args.push_back(android::base::StringPrintf("--base=0x%08x", kBaseAddress));
std::string error_msg;
- if (!CompileBootImage(extra_args, scratch.GetFilename(), &error_msg)) {
+ if (!CompileBootImage(local_extra_args, scratch.GetFilename(), dex_files, &error_msg)) {
LOG(ERROR) << "Failed to compile image " << scratch.GetFilename() << error_msg;
}
std::string art_file = scratch.GetFilename() + ".art";
@@ -151,19 +168,19 @@ class Dex2oatImageTest : public CommonRuntimeTest {
scratch.Close();
// Clear image files since we compile the image multiple times and don't want to leave any
// artifacts behind.
- ClearDirectory(scratch_dir.c_str(), /*recursive*/ false);
+ ClearDirectory(scratch_dir.c_str(), /*recursive=*/ false);
return ret;
}
bool CompileBootImage(const std::vector<std::string>& extra_args,
const std::string& image_file_name_prefix,
+ ArrayRef<const std::string> dex_files,
std::string* error_msg) {
Runtime* const runtime = Runtime::Current();
std::vector<std::string> argv;
argv.push_back(runtime->GetCompilerExecutable());
AddRuntimeArg(argv, "-Xms64m");
AddRuntimeArg(argv, "-Xmx64m");
- std::vector<std::string> dex_files = GetLibCoreDexFileNames();
for (const std::string& dex_file : dex_files) {
argv.push_back("--dex-file=" + dex_file);
argv.push_back("--dex-location=" + dex_file);
@@ -182,7 +199,6 @@ class Dex2oatImageTest : public CommonRuntimeTest {
argv.push_back("--image=" + image_file_name_prefix + ".art");
argv.push_back("--oat-file=" + image_file_name_prefix + ".oat");
argv.push_back("--oat-location=" + image_file_name_prefix + ".oat");
- argv.push_back("--base=0x60000000");
std::vector<std::string> compiler_options = runtime->GetCompilerOptions();
argv.insert(argv.end(), compiler_options.begin(), compiler_options.end());
@@ -265,4 +281,266 @@ TEST_F(Dex2oatImageTest, TestModesAndFilters) {
}
}
+TEST_F(Dex2oatImageTest, TestExtension) {
+ constexpr size_t kReservationSize = 256 * MB; // This should be enough for the compiled images.
+ // Extend to both directions for maximum relocation difference.
+ static_assert(ART_BASE_ADDRESS_MIN_DELTA < 0);
+ static_assert(ART_BASE_ADDRESS_MAX_DELTA > 0);
+ static_assert(IsAligned<kPageSize>(ART_BASE_ADDRESS_MIN_DELTA));
+ static_assert(IsAligned<kPageSize>(ART_BASE_ADDRESS_MAX_DELTA));
+ constexpr size_t kExtra = ART_BASE_ADDRESS_MAX_DELTA - ART_BASE_ADDRESS_MIN_DELTA;
+ uint32_t min_relocated_address = kBaseAddress + ART_BASE_ADDRESS_MIN_DELTA;
+ std::string error_msg;
+ MemMap reservation = MemMap::MapAnonymous("Reservation",
+ reinterpret_cast<uint8_t*>(min_relocated_address),
+ kReservationSize + kExtra,
+ PROT_NONE,
+ /*low_4gb=*/ true,
+ /*reuse=*/ false,
+ /*reservation=*/ nullptr,
+ &error_msg);
+ ASSERT_TRUE(reservation.IsValid());
+
+ ScratchFile scratch;
+ std::string scratch_dir = scratch.GetFilename() + "-d";
+ int mkdir_result = mkdir(scratch_dir.c_str(), 0700);
+ ASSERT_EQ(0, mkdir_result);
+ scratch_dir += '/';
+ std::string image_dir = scratch_dir + GetInstructionSetString(kRuntimeISA);
+ mkdir_result = mkdir(image_dir.c_str(), 0700);
+ ASSERT_EQ(0, mkdir_result);
+ std::string filename_prefix = image_dir + "/core";
+
+ // Copy the libcore dex files to a custom dir inside `scratch_dir` so that we do not
+ // accidentally load pre-compiled core images from their original directory based on BCP paths.
+ std::string jar_dir = scratch_dir + "jars";
+ mkdir_result = mkdir(jar_dir.c_str(), 0700);
+ ASSERT_EQ(0, mkdir_result);
+ jar_dir += '/';
+ std::vector<std::string> libcore_dex_files = GetLibCoreDexFileNames();
+ for (std::string& dex_file : libcore_dex_files) {
+ size_t slash_pos = dex_file.rfind('/');
+ ASSERT_NE(std::string::npos, slash_pos);
+ std::string new_location = jar_dir + dex_file.substr(slash_pos + 1u);
+ std::ifstream src_stream(dex_file, std::ios::binary);
+ std::ofstream dst_stream(new_location, std::ios::binary);
+ dst_stream << src_stream.rdbuf();
+ dex_file = new_location;
+ }
+
+ ArrayRef<const std::string> full_bcp(libcore_dex_files);
+ size_t total_dex_files = full_bcp.size();
+ ASSERT_GE(total_dex_files, 4u); // 2 for "head", 1 for "tail", at least one for "mid", see below.
+
+ // The primary image must contain at least core-oj and core-libart to initialize the runtime.
+ ASSERT_NE(std::string::npos, full_bcp[0].find("core-oj"));
+ ASSERT_NE(std::string::npos, full_bcp[1].find("core-libart"));
+ ArrayRef<const std::string> head_dex_files = full_bcp.SubArray(/*pos=*/ 0u, /*length=*/ 2u);
+ // Middle part is everything else except for conscrypt.
+ ASSERT_NE(std::string::npos, full_bcp[full_bcp.size() - 1u].find("conscrypt"));
+ ArrayRef<const std::string> mid_bcp =
+ full_bcp.SubArray(/*pos=*/ 0u, /*length=*/ total_dex_files - 1u);
+ ArrayRef<const std::string> mid_dex_files = mid_bcp.SubArray(/*pos=*/ 2u);
+ // Tail is just the conscrypt.
+ ArrayRef<const std::string> tail_dex_files =
+ full_bcp.SubArray(/*pos=*/ total_dex_files - 1u, /*length=*/ 1u);
+
+ // Prepare the "head", "mid" and "tail" names and locations.
+ std::string base_name = "core.art";
+ std::string base_location = scratch_dir + base_name;
+ std::vector<std::string> expanded_mid = gc::space::ImageSpace::ExpandMultiImageLocations(
+ mid_dex_files.SubArray(/*pos=*/ 0u, /*length=*/ 1u),
+ base_location,
+ /*boot_image_extension=*/ true);
+ CHECK_EQ(1u, expanded_mid.size());
+ std::string mid_location = expanded_mid[0];
+ size_t mid_slash_pos = mid_location.rfind('/');
+ ASSERT_NE(std::string::npos, mid_slash_pos);
+ std::string mid_name = mid_location.substr(mid_slash_pos + 1u);
+ CHECK_EQ(1u, tail_dex_files.size());
+ std::vector<std::string> expanded_tail = gc::space::ImageSpace::ExpandMultiImageLocations(
+ tail_dex_files, base_location, /*boot_image_extension=*/ true);
+ CHECK_EQ(1u, expanded_tail.size());
+ std::string tail_location = expanded_tail[0];
+ size_t tail_slash_pos = tail_location.rfind('/');
+ ASSERT_NE(std::string::npos, tail_slash_pos);
+ std::string tail_name = tail_location.substr(tail_slash_pos + 1u);
+
+ // Compile the "head", i.e. the primary boot image.
+ std::string base = android::base::StringPrintf("--base=0x%08x", kBaseAddress);
+ bool head_ok = CompileBootImage({base}, filename_prefix, head_dex_files, &error_msg);
+ ASSERT_TRUE(head_ok) << error_msg;
+
+ // Compile the "mid", i.e. the first extension.
+ std::string mid_bcp_string = android::base::Join(mid_bcp, ':');
+ std::vector<std::string> extra_args;
+ AddRuntimeArg(extra_args, "-Xbootclasspath:" + mid_bcp_string);
+ AddRuntimeArg(extra_args, "-Xbootclasspath-locations:" + mid_bcp_string);
+ extra_args.push_back("--boot-image=" + base_location);
+ bool mid_ok = CompileBootImage(extra_args, filename_prefix, mid_dex_files, &error_msg);
+ ASSERT_TRUE(mid_ok) << error_msg;
+
+ // Try to compile the "tail" without specifying the "mid" extension. This shall fail.
+ std::string full_bcp_string = android::base::Join(full_bcp, ':');
+ extra_args.clear();
+ AddRuntimeArg(extra_args, "-Xbootclasspath:" + full_bcp_string);
+ AddRuntimeArg(extra_args, "-Xbootclasspath-locations:" + full_bcp_string);
+ extra_args.push_back("--boot-image=" + base_location);
+ bool tail_ok = CompileBootImage(extra_args, filename_prefix, tail_dex_files, &error_msg);
+ ASSERT_FALSE(tail_ok) << error_msg;
+
+ // Now compile the tail against both "head" and "mid".
+ CHECK(StartsWith(extra_args.back(), "--boot-image="));
+ extra_args.back() = "--boot-image=" + base_location + ':' + mid_location;
+ tail_ok = CompileBootImage(extra_args, filename_prefix, tail_dex_files, &error_msg);
+ ASSERT_TRUE(tail_ok) << error_msg;
+
+ reservation = MemMap::Invalid(); // Free the reserved memory for loading images.
+
+ // Try to load the boot image with different image locations.
+ std::vector<std::string> boot_class_path = libcore_dex_files;
+ std::vector<std::unique_ptr<gc::space::ImageSpace>> boot_image_spaces;
+ bool relocate = false;
+ MemMap extra_reservation;
+ auto load = [&](const std::string& image_location) {
+ boot_image_spaces.clear();
+ extra_reservation = MemMap::Invalid();
+ ScopedObjectAccess soa(Thread::Current());
+ return gc::space::ImageSpace::LoadBootImage(/*boot_class_path=*/ boot_class_path,
+ /*boot_class_path_locations=*/ libcore_dex_files,
+ image_location,
+ kRuntimeISA,
+ gc::space::ImageSpaceLoadingOrder::kSystemFirst,
+ relocate,
+ /*executable=*/ true,
+ /*is_zygote=*/ false,
+ /*extra_reservation_size=*/ 0u,
+ &boot_image_spaces,
+ &extra_reservation);
+ };
+
+ for (bool r : { false, true}) {
+ relocate = r;
+
+ // Load primary image with full path.
+ bool load_ok = load(base_location);
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_FALSE(extra_reservation.IsValid());
+ ASSERT_EQ(head_dex_files.size(), boot_image_spaces.size());
+
+ // Fail to load primary image with just the name.
+ load_ok = load(base_name);
+ ASSERT_FALSE(load_ok);
+
+ // Fail to load primary image with a search path.
+ load_ok = load("*");
+ ASSERT_FALSE(load_ok);
+ load_ok = load(scratch_dir + "*");
+ ASSERT_FALSE(load_ok);
+
+ // Load the primary and first extension with full path.
+ load_ok = load(base_location + ':' + mid_location);
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(mid_bcp.size(), boot_image_spaces.size());
+
+ // Load the primary with full path and fail to load first extension without full path.
+ load_ok = load(base_location + ':' + mid_name);
+ ASSERT_TRUE(load_ok) << error_msg; // Primary image loaded successfully.
+ ASSERT_EQ(head_dex_files.size(), boot_image_spaces.size()); // But only the primary image.
+
+ // Load all the libcore images with full paths.
+ load_ok = load(base_location + ':' + mid_location + ':' + tail_location);
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
+
+ // Load the primary and first extension with full paths, fail to load second extension by name.
+ load_ok = load(base_location + ':' + mid_location + ':' + tail_name);
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(mid_bcp.size(), boot_image_spaces.size());
+
+ // Load the primary with full path and fail to load first extension without full path,
+ // fail to load second extension because it depends on the first.
+ load_ok = load(base_location + ':' + mid_name + ':' + tail_location);
+ ASSERT_TRUE(load_ok) << error_msg; // Primary image loaded successfully.
+ ASSERT_EQ(head_dex_files.size(), boot_image_spaces.size()); // But only the primary image.
+
+ // Load the primary with full path and extensions with a specified search path.
+ load_ok = load(base_location + ':' + scratch_dir + '*');
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
+
+ // Load the primary with full path and fail to find extensions in BCP path.
+ load_ok = load(base_location + ":*");
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(head_dex_files.size(), boot_image_spaces.size());
+ }
+
+ // Now copy the libcore dex files to the `scratch_dir` and retry loading the boot image
+ // with BCP in the scratch_dir so that the images can be found based on BCP paths.
+ for (std::string& bcp_component : boot_class_path) {
+ size_t slash_pos = bcp_component.rfind('/');
+ ASSERT_NE(std::string::npos, slash_pos);
+ std::string new_location = scratch_dir + bcp_component.substr(slash_pos + 1u);
+ std::ifstream src_stream(bcp_component, std::ios::binary);
+ std::ofstream dst_stream(new_location, std::ios::binary);
+ dst_stream << src_stream.rdbuf();
+ bcp_component = new_location;
+ }
+
+ for (bool r : { false, true}) {
+ relocate = r;
+
+ // Loading the primary image with just the name now succeeds.
+ bool load_ok = load(base_name);
+ ASSERT_TRUE(load_ok) << error_msg;
+
+ // Loading the primary image with a search path still fails.
+ load_ok = load("*");
+ ASSERT_FALSE(load_ok);
+ load_ok = load(scratch_dir + "*");
+ ASSERT_FALSE(load_ok);
+
+ // Load the primary and first extension without paths.
+ load_ok = load(base_name + ':' + mid_name);
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(mid_bcp.size(), boot_image_spaces.size());
+
+ // Load the primary with full path and the first extension without full path.
+ load_ok = load(base_location + ':' + mid_name);
+ ASSERT_TRUE(load_ok) << error_msg; // Loaded successfully.
+ ASSERT_EQ(mid_bcp.size(), boot_image_spaces.size()); // Including the extension.
+
+ // Load all the libcore images without paths.
+ load_ok = load(base_name + ':' + mid_name + ':' + tail_name);
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
+
+ // Load the primary and first extension with full paths and second extension by name.
+ load_ok = load(base_location + ':' + mid_location + ':' + tail_name);
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
+
+ // Load the primary with full path, first extension without path,
+ // and second extension with full path.
+ load_ok = load(base_location + ':' + mid_name + ':' + tail_location);
+ ASSERT_TRUE(load_ok) << error_msg; // Loaded successfully.
+ ASSERT_EQ(full_bcp.size(), boot_image_spaces.size()); // Including both extensions.
+
+ // Load the primary with full path and find both extensions in BCP path.
+ load_ok = load(base_location + ":*");
+ ASSERT_TRUE(load_ok) << error_msg;
+ ASSERT_EQ(full_bcp.size(), boot_image_spaces.size());
+
+ // Fail to load any images with invalid image locations (named component after search paths).
+ load_ok = load(base_location + ":*:" + tail_location);
+ ASSERT_FALSE(load_ok);
+ load_ok = load(base_location + ':' + scratch_dir + "*:" + tail_location);
+ ASSERT_FALSE(load_ok);
+ }
+
+ ClearDirectory(scratch_dir.c_str());
+ int rmdir_result = rmdir(scratch_dir.c_str());
+ ASSERT_EQ(0, rmdir_result);
+}
+
} // namespace art
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 81366e4590..bd439b72b3 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -32,6 +32,7 @@
#include "arch/instruction_set_features.h"
#include "base/macros.h"
#include "base/mutex-inl.h"
+#include "base/string_view_cpp20.h"
#include "base/utils.h"
#include "dex/art_dex_file_loader.h"
#include "dex/base64_test_util.h"
@@ -111,13 +112,15 @@ class Dex2oatTest : public Dex2oatEnvironmentTest {
CompilerFilter::Filter filter,
const std::vector<std::string>& extra_args = {},
bool expect_success = true,
- bool use_fd = false) WARN_UNUSED {
+ bool use_fd = false,
+ bool use_zip_fd = false) WARN_UNUSED {
return GenerateOdexForTest(dex_location,
odex_location,
filter,
extra_args,
expect_success,
use_fd,
+ use_zip_fd,
[](const OatFile&) {});
}
@@ -131,9 +134,22 @@ class Dex2oatTest : public Dex2oatEnvironmentTest {
const std::vector<std::string>& extra_args,
bool expect_success,
bool use_fd,
+ bool use_zip_fd,
T check_oat) WARN_UNUSED {
+ std::vector<std::string> dex_locations;
+ if (use_zip_fd) {
+ std::string loc_arg = "--zip-location=" + dex_location;
+ CHECK(std::any_of(extra_args.begin(),
+ extra_args.end(),
+ [&](const std::string& s) { return s == loc_arg; }));
+ CHECK(std::any_of(extra_args.begin(),
+ extra_args.end(),
+ [](const std::string& s) { return StartsWith(s, "--zip-fd="); }));
+ } else {
+ dex_locations.push_back(dex_location);
+ }
std::string error_msg;
- int status = GenerateOdexForTestWithStatus({dex_location},
+ int status = GenerateOdexForTestWithStatus(dex_locations,
odex_location,
filter,
&error_msg,
@@ -1082,7 +1098,8 @@ class Dex2oatClassLoaderContextTest : public Dex2oatTest {
CompilerFilter::kQuicken,
extra_args,
expected_success,
- /*use_fd*/ false,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ false,
check_oat));
}
@@ -1602,8 +1619,9 @@ TEST_F(Dex2oatDedupeCode, DedupeTest) {
base_oat_name,
CompilerFilter::Filter::kSpeed,
{ "--deduplicate-code=false" },
- true, // expect_success
- false, // use_fd
+ /*expect_success=*/ true,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ false,
[&no_dedupe_size](const OatFile& o) {
no_dedupe_size = o.Size();
}));
@@ -1613,8 +1631,9 @@ TEST_F(Dex2oatDedupeCode, DedupeTest) {
base_oat_name,
CompilerFilter::Filter::kSpeed,
{ "--deduplicate-code=true" },
- true, // expect_success
- false, // use_fd
+ /*expect_success=*/ true,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ false,
[&dedupe_size](const OatFile& o) {
dedupe_size = o.Size();
}));
@@ -1630,8 +1649,9 @@ TEST_F(Dex2oatTest, UncompressedTest) {
base_oat_name,
CompilerFilter::Filter::kQuicken,
{ },
- true, // expect_success
- false, // use_fd
+ /*expect_success=*/ true,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ false,
[](const OatFile& o) {
CHECK(!o.ContainsDexCode());
}));
@@ -1750,8 +1770,9 @@ TEST_F(Dex2oatTest, CompactDexGenerationFailure) {
oat_filename,
CompilerFilter::Filter::kVerify,
{ },
- true, // expect_success
- false, // use_fd
+ /*expect_success=*/ true,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ false,
[](const OatFile& o) {
CHECK(o.ContainsDexCode());
}));
@@ -1882,8 +1903,9 @@ TEST_F(Dex2oatTest, DontExtract) {
odex_location,
CompilerFilter::Filter::kVerify,
{ "--copy-dex-files=false" },
- true, // expect_success
- false, // use_fd
+ /*expect_success=*/ true,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ false,
[](const OatFile&) {}));
{
// Check the vdex doesn't have dex.
@@ -1944,8 +1966,9 @@ TEST_F(Dex2oatTest, DontExtract) {
// target.
"--runtime-arg",
"-Xuse-stderr-logger" },
- true, // expect_success
- false, // use_fd
+ /*expect_success=*/ true,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ false,
[](const OatFile& o) {
CHECK(o.ContainsDexCode());
}));
@@ -2131,8 +2154,9 @@ TEST_F(Dex2oatTest, AppImageNoProfile) {
odex_location,
CompilerFilter::Filter::kSpeedProfile,
{ "--app-image-fd=" + std::to_string(app_image_file.GetFd()) },
- true, // expect_success
- false, // use_fd
+ /*expect_success=*/ true,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ false,
[](const OatFile&) {}));
// Open our generated oat file.
std::string error_msg;
@@ -2155,6 +2179,24 @@ TEST_F(Dex2oatTest, AppImageNoProfile) {
EXPECT_EQ(header.GetImageSection(ImageHeader::kSectionArtFields).Size(), 0u);
}
+TEST_F(Dex2oatTest, ZipFd) {
+ std::string zip_location = GetTestDexFileName("MainUncompressed");
+ std::unique_ptr<File> dex_file(OS::OpenFileForReading(zip_location.c_str()));
+ std::vector<std::string> extra_args{
+ StringPrintf("--zip-fd=%d", dex_file->Fd()),
+ "--zip-location=" + zip_location,
+ };
+ std::string out_dir = GetScratchDir();
+ const std::string base_oat_name = out_dir + "/base.oat";
+ ASSERT_TRUE(GenerateOdexForTest(zip_location,
+ base_oat_name,
+ CompilerFilter::Filter::kQuicken,
+ extra_args,
+ /*expect_success=*/ true,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ true));
+}
+
TEST_F(Dex2oatTest, AppImageResolveStrings) {
using Hotness = ProfileCompilationInfo::MethodHotness;
// Create a profile with the startup method marked.
@@ -2225,8 +2267,9 @@ TEST_F(Dex2oatTest, AppImageResolveStrings) {
{ "--app-image-file=" + app_image_location,
"--resolve-startup-const-strings=true",
"--profile-file=" + profile_file.GetFilename()},
- /* expect_success= */ true,
- /* use_fd= */ false,
+ /*expect_success=*/ true,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ false,
[](const OatFile&) {}));
// Open our generated oat file.
std::string error_msg;
@@ -2338,8 +2381,9 @@ TEST_F(Dex2oatClassLoaderContextTest, StoredClassLoaderContext) {
odex_location,
CompilerFilter::Filter::kQuicken,
{ "--class-loader-context=" + stored_context },
- true, // expect_success
- false, // use_fd
+ /*expect_success=*/ true,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ false,
[&](const OatFile& oat_file) {
EXPECT_NE(oat_file.GetClassLoaderContext(), stored_context) << output_;
EXPECT_NE(oat_file.GetClassLoaderContext(), valid_context) << output_;
@@ -2350,8 +2394,9 @@ TEST_F(Dex2oatClassLoaderContextTest, StoredClassLoaderContext) {
CompilerFilter::Filter::kQuicken,
{ "--class-loader-context=" + valid_context,
"--stored-class-loader-context=" + stored_context },
- true, // expect_success
- false, // use_fd
+ /*expect_success=*/ true,
+ /*use_fd=*/ false,
+ /*use_zip_fd=*/ false,
[&](const OatFile& oat_file) {
EXPECT_EQ(oat_file.GetClassLoaderContext(), expected_stored_context) << output_;
}));
diff --git a/dex2oat/linker/image_test.cc b/dex2oat/linker/image_test.cc
index 1a5701dd38..33d122bdbe 100644
--- a/dex2oat/linker/image_test.cc
+++ b/dex2oat/linker/image_test.cc
@@ -87,6 +87,8 @@ TEST_F(ImageTest, ImageHeaderIsValid) {
oat_file_end,
/*boot_image_begin=*/ 0u,
/*boot_image_size=*/ 0u,
+ /*boot_image_component_count=*/ 0u,
+ /*boot_image_checksum=*/ 0u,
sizeof(void*));
ASSERT_TRUE(image_header.IsValid());
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index 9e5da2774c..9ec6e56922 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -48,6 +48,7 @@
#include "linker/multi_oat_relative_patcher.h"
#include "lock_word.h"
#include "mirror/object-inl.h"
+#include "oat.h"
#include "oat_writer.h"
#include "scoped_thread_state_change-inl.h"
#include "signal_catcher.h"
@@ -171,8 +172,9 @@ inline void ImageTest::DoCompile(ImageHeader::StorageMode storage_mode,
// Create a generic tmp file, to be the base of the .art and .oat temporary files.
ScratchFile location;
std::vector<std::string> image_locations =
- gc::space::ImageSpace::ExpandMultiImageLocations(out_helper.dex_file_locations,
- location.GetFilename() + ".art");
+ gc::space::ImageSpace::ExpandMultiImageLocations(
+ ArrayRef<const std::string>(out_helper.dex_file_locations),
+ location.GetFilename() + ".art");
for (size_t i = 0u; i != class_path.size(); ++i) {
out_helper.image_locations.push_back(ScratchFile(image_locations[i]));
}
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index ede5ef7704..eb87b82565 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -2653,6 +2653,22 @@ void ImageWriter::CreateHeader(size_t oat_index) {
}
}
+ // Compute boot image checksums for the primary component, leave as 0 otherwise.
+ uint32_t boot_image_components = 0u;
+ uint32_t boot_image_checksums = 0u;
+ if (oat_index == 0u) {
+ const std::vector<gc::space::ImageSpace*>& image_spaces =
+ Runtime::Current()->GetHeap()->GetBootImageSpaces();
+ boot_image_components = dchecked_integral_cast<uint32_t>(image_spaces.size());
+ DCHECK_EQ(boot_image_components == 0u, compiler_options_.IsBootImage());
+ for (uint32_t i = 0; i != boot_image_components; ) {
+ const ImageHeader& header = image_spaces[i]->GetImageHeader();
+ boot_image_checksums ^= header.GetImageChecksum();
+ DCHECK_LE(header.GetComponentCount(), boot_image_components - i);
+ i += header.GetComponentCount();
+ }
+ }
+
// Create the image sections.
auto section_info_pair = image_info.CreateImageSections();
const size_t image_end = section_info_pair.first;
@@ -2695,6 +2711,8 @@ void ImageWriter::CreateHeader(size_t oat_index) {
PointerToLowMemUInt32(oat_file_end),
boot_image_begin_,
boot_image_size_,
+ boot_image_components,
+ boot_image_checksums,
static_cast<uint32_t>(target_ptr_size_));
}
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 4183dd9ad7..92de151416 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -61,6 +61,7 @@
#include "mirror/class_loader.h"
#include "mirror/dex_cache-inl.h"
#include "mirror/object-inl.h"
+#include "oat.h"
#include "oat_quick_method_header.h"
#include "profile/profile_compilation_info.h"
#include "quicken_info.h"
@@ -1611,7 +1612,7 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor {
// Assign a pointer to quick code for copied methods
// not handled in the method StartClass
- void Postprocess() {
+ void Postprocess() REQUIRES_SHARED(Locks::mutator_lock_) {
for (std::pair<ArtMethod*, ArtMethod*>& p : methods_to_process_) {
ArtMethod* method = p.first;
ArtMethod* origin = p.second;
@@ -2271,6 +2272,7 @@ size_t OatWriter::InitOatCodeDexFiles(size_t offset) {
}
if (HasImage()) {
+ ScopedObjectAccess soa(Thread::Current());
ScopedAssertNoThreadSuspension sants("Init image method visitor", Thread::Current());
InitImageMethodVisitor image_visitor(this, offset, dex_files_);
success = VisitDexMethods(&image_visitor);
diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h
index fb263d9cd9..19c0cfefa2 100644
--- a/dex2oat/linker/oat_writer.h
+++ b/dex2oat/linker/oat_writer.h
@@ -35,7 +35,6 @@
#include "dex/type_reference.h"
#include "linker/relative_patcher.h" // For RelativePatcherTargetProvider.
#include "mirror/class.h"
-#include "oat.h"
namespace art {
@@ -44,6 +43,7 @@ class CompiledMethod;
class CompilerDriver;
class CompilerOptions;
class DexContainer;
+class OatHeader;
class OutputStream;
class ProfileCompilationInfo;
class TimingLogger;
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index 2eb4476667..11600a8e01 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -41,6 +41,7 @@
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
+#include "oat.h"
#include "oat_file-inl.h"
#include "oat_writer.h"
#include "profile/profile_compilation_info.h"
diff --git a/dex2oat/linker/relative_patcher_test.h b/dex2oat/linker/relative_patcher_test.h
index dc53ac4321..f34e6eb059 100644
--- a/dex2oat/linker/relative_patcher_test.h
+++ b/dex2oat/linker/relative_patcher_test.h
@@ -29,7 +29,6 @@
#include "dex/string_reference.h"
#include "driver/compiled_method_storage.h"
#include "linker/relative_patcher.h"
-#include "oat.h"
#include "oat_quick_method_header.h"
#include "stream/vector_output_stream.h"
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index fc63f54e5d..d8cf12366b 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -168,8 +168,6 @@ art_cc_library {
defaults: ["libartbase_defaults"],
visibility: [
// TODO(b/133140750): Clean this up.
- "//cts/tests/tests/appop",
- "//frameworks/base/startop/view_compiler",
"//packages/modules/NetworkStack/tests:__subpackages__",
],
diff --git a/libartbase/base/hiddenapi_flags.h b/libartbase/base/hiddenapi_flags.h
index 3b85e38cf8..a9a903b71f 100644
--- a/libartbase/base/hiddenapi_flags.h
+++ b/libartbase/base/hiddenapi_flags.h
@@ -193,6 +193,7 @@ class ApiList {
static ApiList Blacklist() { return ApiList(Value::kBlacklist); }
static ApiList GreylistMaxO() { return ApiList(Value::kGreylistMaxO); }
static ApiList GreylistMaxP() { return ApiList(Value::kGreylistMaxP); }
+ static ApiList GreylistMaxQ() { return ApiList(Value::kGreylistMaxQ); }
static ApiList CorePlatformApi() { return ApiList(DomainApi::kCorePlatformApi); }
static ApiList TestApi() { return ApiList(DomainApi::kTestApi); }
@@ -289,6 +290,16 @@ class ApiList {
// Returns true when no ApiList is specified and no domain_api flags either.
bool IsEmpty() const { return (GetValue() == Value::kInvalid) && (GetDomainApis() == 0); }
+ // Returns true if the ApiList is on blacklist.
+ bool IsBlacklisted() const {
+ return GetValue() == Value::kBlacklist;
+ }
+
+ // Returns true if the ApiList is a test API.
+ bool IsTestApi() const {
+ return helper::MatchesBitMask(helper::ToBit(DomainApi::kTestApi), dex_flags_);
+ }
+
// Returns the maximum target SDK version allowed to access this ApiList.
SdkVersion GetMaxAllowedSdkVersion() const { return kMaxSdkVersions[GetIntValue()]; }
diff --git a/libartbase/base/memfd.h b/libartbase/base/memfd.h
index 53cfe9cda1..0bb336d45a 100644
--- a/libartbase/base/memfd.h
+++ b/libartbase/base/memfd.h
@@ -17,8 +17,46 @@
#ifndef ART_LIBARTBASE_BASE_MEMFD_H_
#define ART_LIBARTBASE_BASE_MEMFD_H_
+#include <fcntl.h>
+#include <unistd.h>
+
#if defined(__BIONIC__)
#include <linux/memfd.h> // To access memfd flags.
+#else
+
+// If memfd flags don't exist in the current toolchain, define them ourselves.
+#ifndef F_ADD_SEALS
+# define F_ADD_SEALS (1033)
+#endif
+
+#ifndef F_GET_SEALS
+# define F_GET_SEALS (1034)
+#endif
+
+#ifndef F_SEAL_SEAL
+# define F_SEAL_SEAL 0x0001
+#endif
+
+#ifndef F_SEAL_SHRINK
+# define F_SEAL_SHRINK 0x0002
+#endif
+
+#ifndef F_SEAL_GROW
+# define F_SEAL_GROW 0x0004
+#endif
+
+#ifndef F_SEAL_WRITE
+# define F_SEAL_WRITE 0x0008
+#endif
+
+#ifndef F_SEAL_FUTURE_WRITE
+# define F_SEAL_FUTURE_WRITE 0x0010
+#endif
+
+#ifndef MFD_ALLOW_SEALING
+# define MFD_ALLOW_SEALING 0x0002U
+#endif
+
#endif
namespace art {
diff --git a/libartbase/base/utils.cc b/libartbase/base/utils.cc
index a0884340cb..7d4dd59530 100644
--- a/libartbase/base/utils.cc
+++ b/libartbase/base/utils.cc
@@ -16,6 +16,7 @@
#include "utils.h"
+#include <dirent.h>
#include <inttypes.h>
#include <pthread.h>
#include <sys/stat.h>
@@ -355,4 +356,22 @@ bool IsAddressKnownBackedByFileOrShared(const void* addr) {
return (flags & (1LL << 61)) != 0;
}
+int GetTaskCount() {
+ DIR* directory = opendir("/proc/self/task");
+ if (directory == nullptr) {
+ return -1;
+ }
+
+ uint32_t count = 0;
+ struct dirent* entry = nullptr;
+ while ((entry = readdir(directory)) != nullptr) {
+ if ((strcmp(entry->d_name, ".") == 0) || (strcmp(entry->d_name, "..") == 0)) {
+ continue;
+ }
+ ++count;
+ }
+ closedir(directory);
+ return count;
+}
+
} // namespace art
diff --git a/libartbase/base/utils.h b/libartbase/base/utils.h
index 6fc537af03..4bcb9151b7 100644
--- a/libartbase/base/utils.h
+++ b/libartbase/base/utils.h
@@ -159,6 +159,9 @@ std::string GetProcessStatus(const char* key);
// following accesses repopulate the memory or return zero.
bool IsAddressKnownBackedByFileOrShared(const void* addr);
+// Returns the number of threads running.
+int GetTaskCount();
+
} // namespace art
#endif // ART_LIBARTBASE_BASE_UTILS_H_
diff --git a/libartpalette/Android.bp b/libartpalette/Android.bp
index ef140168db..0a45b8880b 100644
--- a/libartpalette/Android.bp
+++ b/libartpalette/Android.bp
@@ -63,10 +63,6 @@ art_cc_library {
art_cc_library {
name: "libartpalette",
defaults: ["libartpalette_defaults"],
- visibility: [
- // TODO(b/133140750): Clean this up.
- "//frameworks/base/startop/view_compiler",
- ],
required: ["libartpalette-system"], // libartpalette.so dlopen()'s libartpalette-system.
header_libs: ["libbase_headers"],
target: {
diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp
index ffb616c31c..a08f89faa6 100644
--- a/libdexfile/Android.bp
+++ b/libdexfile/Android.bp
@@ -110,8 +110,6 @@ cc_defaults {
],
defaults_visibility: [
"//art:__subpackages__",
- // TODO(b/133140750): Clean this up.
- "//frameworks/base/startop/view_compiler",
],
static_libs: ["libdexfile"],
}
@@ -143,11 +141,6 @@ gensrcs {
art_cc_library {
name: "libdexfile",
defaults: ["libdexfile_defaults"],
- visibility: [
- // TODO(b/133140750): Clean this up.
- "//cts/tests/tests/appop",
- "//frameworks/base/startop/view_compiler",
- ],
// Leave the symbols in the shared library so that stack unwinders can
// produce meaningful name resolution.
strip: {
diff --git a/libdexfile/dex/modifiers.h b/libdexfile/dex/modifiers.h
index 918feb34f4..bdb3781c75 100644
--- a/libdexfile/dex/modifiers.h
+++ b/libdexfile/dex/modifiers.h
@@ -62,6 +62,8 @@ static constexpr uint32_t kAccObsoleteObject = 0x00200000; // class (run
// that it was copied from its declaring class into another class. All methods marked kAccMiranda
// and kAccDefaultConflict will have this bit set. Any kAccDefault method contained in the methods_
// array of a concrete class will also have this bit set.
+// We need copies of the original method because the method may end up in
+// different places in classes vtables, and the vtable index is set in ArtMethod.method_index.
static constexpr uint32_t kAccCopied = 0x00100000; // method (runtime)
static constexpr uint32_t kAccMiranda = 0x00200000; // method (runtime, not native)
static constexpr uint32_t kAccDefault = 0x00400000; // method (runtime)
diff --git a/libelffile/elf/elf_builder.h b/libelffile/elf/elf_builder.h
index b528f6a221..07f0d00b23 100644
--- a/libelffile/elf/elf_builder.h
+++ b/libelffile/elf/elf_builder.h
@@ -746,13 +746,13 @@ class ElfBuilder final {
hash_.AllocateVirtualMemory(hash_.GetCacheSize());
Elf_Dyn dyns[] = {
- { .d_tag = DT_HASH, .d_un.d_ptr = hash_.GetAddress() },
- { .d_tag = DT_STRTAB, .d_un.d_ptr = dynstr_.GetAddress() },
- { .d_tag = DT_SYMTAB, .d_un.d_ptr = dynsym_.GetAddress() },
- { .d_tag = DT_SYMENT, .d_un.d_ptr = sizeof(Elf_Sym) },
- { .d_tag = DT_STRSZ, .d_un.d_ptr = dynstr_.GetCacheSize() },
- { .d_tag = DT_SONAME, .d_un.d_ptr = soname_offset },
- { .d_tag = DT_NULL, .d_un.d_ptr = 0 },
+ { .d_tag = DT_HASH, .d_un = { .d_ptr = hash_.GetAddress() }, },
+ { .d_tag = DT_STRTAB, .d_un = { .d_ptr = dynstr_.GetAddress() }, },
+ { .d_tag = DT_SYMTAB, .d_un = { .d_ptr = dynsym_.GetAddress() }, },
+ { .d_tag = DT_SYMENT, .d_un = { .d_ptr = sizeof(Elf_Sym) }, },
+ { .d_tag = DT_STRSZ, .d_un = { .d_ptr = dynstr_.GetCacheSize() }, },
+ { .d_tag = DT_SONAME, .d_un = { .d_ptr = soname_offset }, },
+ { .d_tag = DT_NULL, .d_un = { .d_ptr = 0 }, },
};
dynamic_.Add(&dyns, sizeof(dyns));
dynamic_.AllocateVirtualMemory(dynamic_.GetCacheSize());
diff --git a/openjdkjvmti/events-inl.h b/openjdkjvmti/events-inl.h
index 22822f8b40..23f7151c99 100644
--- a/openjdkjvmti/events-inl.h
+++ b/openjdkjvmti/events-inl.h
@@ -90,41 +90,42 @@ class ScopedEventDispatchEnvironment final : public art::ValueObject {
// Infrastructure to achieve type safety for event dispatch.
-#define FORALL_EVENT_TYPES(fn) \
- fn(VMInit, ArtJvmtiEvent::kVmInit) \
- fn(VMDeath, ArtJvmtiEvent::kVmDeath) \
- fn(ThreadStart, ArtJvmtiEvent::kThreadStart) \
- fn(ThreadEnd, ArtJvmtiEvent::kThreadEnd) \
- fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookRetransformable) \
- fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookNonRetransformable) \
- fn(ClassLoad, ArtJvmtiEvent::kClassLoad) \
- fn(ClassPrepare, ArtJvmtiEvent::kClassPrepare) \
- fn(VMStart, ArtJvmtiEvent::kVmStart) \
- fn(Exception, ArtJvmtiEvent::kException) \
- fn(ExceptionCatch, ArtJvmtiEvent::kExceptionCatch) \
- fn(SingleStep, ArtJvmtiEvent::kSingleStep) \
- fn(FramePop, ArtJvmtiEvent::kFramePop) \
- fn(Breakpoint, ArtJvmtiEvent::kBreakpoint) \
- fn(FieldAccess, ArtJvmtiEvent::kFieldAccess) \
- fn(FieldModification, ArtJvmtiEvent::kFieldModification) \
- fn(MethodEntry, ArtJvmtiEvent::kMethodEntry) \
- fn(MethodExit, ArtJvmtiEvent::kMethodExit) \
- fn(NativeMethodBind, ArtJvmtiEvent::kNativeMethodBind) \
- fn(CompiledMethodLoad, ArtJvmtiEvent::kCompiledMethodLoad) \
- fn(CompiledMethodUnload, ArtJvmtiEvent::kCompiledMethodUnload) \
- fn(DynamicCodeGenerated, ArtJvmtiEvent::kDynamicCodeGenerated) \
- fn(DataDumpRequest, ArtJvmtiEvent::kDataDumpRequest) \
- fn(MonitorWait, ArtJvmtiEvent::kMonitorWait) \
- fn(MonitorWaited, ArtJvmtiEvent::kMonitorWaited) \
- fn(MonitorContendedEnter, ArtJvmtiEvent::kMonitorContendedEnter) \
- fn(MonitorContendedEntered, ArtJvmtiEvent::kMonitorContendedEntered) \
- fn(ResourceExhausted, ArtJvmtiEvent::kResourceExhausted) \
- fn(GarbageCollectionStart, ArtJvmtiEvent::kGarbageCollectionStart) \
- fn(GarbageCollectionFinish, ArtJvmtiEvent::kGarbageCollectionFinish) \
- fn(ObjectFree, ArtJvmtiEvent::kObjectFree) \
- fn(VMObjectAlloc, ArtJvmtiEvent::kVmObjectAlloc) \
- fn(DdmPublishChunk, ArtJvmtiEvent::kDdmPublishChunk) \
- fn(ObsoleteObjectCreated, ArtJvmtiEvent::kObsoleteObjectCreated)
+#define FORALL_EVENT_TYPES(fn) \
+ fn(VMInit, ArtJvmtiEvent::kVmInit) \
+ fn(VMDeath, ArtJvmtiEvent::kVmDeath) \
+ fn(ThreadStart, ArtJvmtiEvent::kThreadStart) \
+ fn(ThreadEnd, ArtJvmtiEvent::kThreadEnd) \
+ fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookRetransformable) \
+ fn(ClassFileLoadHook, ArtJvmtiEvent::kClassFileLoadHookNonRetransformable) \
+ fn(ClassLoad, ArtJvmtiEvent::kClassLoad) \
+ fn(ClassPrepare, ArtJvmtiEvent::kClassPrepare) \
+ fn(VMStart, ArtJvmtiEvent::kVmStart) \
+ fn(Exception, ArtJvmtiEvent::kException) \
+ fn(ExceptionCatch, ArtJvmtiEvent::kExceptionCatch) \
+ fn(SingleStep, ArtJvmtiEvent::kSingleStep) \
+ fn(FramePop, ArtJvmtiEvent::kFramePop) \
+ fn(Breakpoint, ArtJvmtiEvent::kBreakpoint) \
+ fn(FieldAccess, ArtJvmtiEvent::kFieldAccess) \
+ fn(FieldModification, ArtJvmtiEvent::kFieldModification) \
+ fn(MethodEntry, ArtJvmtiEvent::kMethodEntry) \
+ fn(MethodExit, ArtJvmtiEvent::kMethodExit) \
+ fn(NativeMethodBind, ArtJvmtiEvent::kNativeMethodBind) \
+ fn(CompiledMethodLoad, ArtJvmtiEvent::kCompiledMethodLoad) \
+ fn(CompiledMethodUnload, ArtJvmtiEvent::kCompiledMethodUnload) \
+ fn(DynamicCodeGenerated, ArtJvmtiEvent::kDynamicCodeGenerated) \
+ fn(DataDumpRequest, ArtJvmtiEvent::kDataDumpRequest) \
+ fn(MonitorWait, ArtJvmtiEvent::kMonitorWait) \
+ fn(MonitorWaited, ArtJvmtiEvent::kMonitorWaited) \
+ fn(MonitorContendedEnter, ArtJvmtiEvent::kMonitorContendedEnter) \
+ fn(MonitorContendedEntered, ArtJvmtiEvent::kMonitorContendedEntered) \
+ fn(ResourceExhausted, ArtJvmtiEvent::kResourceExhausted) \
+ fn(GarbageCollectionStart, ArtJvmtiEvent::kGarbageCollectionStart) \
+ fn(GarbageCollectionFinish, ArtJvmtiEvent::kGarbageCollectionFinish) \
+ fn(ObjectFree, ArtJvmtiEvent::kObjectFree) \
+ fn(VMObjectAlloc, ArtJvmtiEvent::kVmObjectAlloc) \
+ fn(DdmPublishChunk, ArtJvmtiEvent::kDdmPublishChunk) \
+ fn(ObsoleteObjectCreated, ArtJvmtiEvent::kObsoleteObjectCreated) \
+ fn(StructuralDexFileLoadHook, ArtJvmtiEvent::kStructuralDexFileLoadHook)
template <ArtJvmtiEvent kEvent>
struct EventFnType {
@@ -217,7 +218,8 @@ inline void EventHandler::DispatchClassFileLoadHookEvent(art::Thread* thread,
unsigned char** new_class_data) const {
art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative);
static_assert(kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable ||
- kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable, "Unsupported event");
+ kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable ||
+ kEvent == ArtJvmtiEvent::kStructuralDexFileLoadHook, "Unsupported event");
DCHECK(*new_class_data == nullptr);
jint current_len = class_data_len;
unsigned char* current_class_data = const_cast<unsigned char*>(class_data);
@@ -588,6 +590,31 @@ inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassFileLoadHookNonRetr
new_class_data);
}
+template <>
+inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kStructuralDexFileLoadHook>(
+ art::Thread* thread,
+ JNIEnv* jnienv,
+ jclass class_being_redefined,
+ jobject loader,
+ const char* name,
+ jobject protection_domain,
+ jint class_data_len,
+ const unsigned char* class_data,
+ jint* new_class_data_len,
+ unsigned char** new_class_data) const {
+ return DispatchClassFileLoadHookEvent<ArtJvmtiEvent::kStructuralDexFileLoadHook>(
+ thread,
+ jnienv,
+ class_being_redefined,
+ loader,
+ name,
+ protection_domain,
+ class_data_len,
+ class_data,
+ new_class_data_len,
+ new_class_data);
+}
+
template <ArtJvmtiEvent kEvent>
inline bool EventHandler::ShouldDispatchOnThread(ArtJvmTiEnv* env, art::Thread* thread) const {
bool dispatch = env->event_masks.global_event_mask.Test(kEvent);
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc
index 3f205eb6f4..56406fc81d 100644
--- a/openjdkjvmti/events.cc
+++ b/openjdkjvmti/events.cc
@@ -100,6 +100,9 @@ jvmtiError ArtJvmtiEventCallbacks::Set(jint index, jvmtiExtensionEvent cb) {
case static_cast<jint>(ArtJvmtiEvent::kDdmPublishChunk):
DdmPublishChunk = reinterpret_cast<ArtJvmtiEventDdmPublishChunk>(cb);
return OK;
+ case static_cast<jint>(ArtJvmtiEvent::kStructuralDexFileLoadHook):
+ StructuralDexFileLoadHook = reinterpret_cast<ArtJvmtiEventStructuralDexFileLoadHook>(cb);
+ return OK;
default:
return ERR(ILLEGAL_ARGUMENT);
}
@@ -116,6 +119,7 @@ bool IsExtensionEvent(ArtJvmtiEvent e) {
switch (e) {
case ArtJvmtiEvent::kDdmPublishChunk:
case ArtJvmtiEvent::kObsoleteObjectCreated:
+ case ArtJvmtiEvent::kStructuralDexFileLoadHook:
return true;
default:
return false;
@@ -1175,6 +1179,7 @@ static DeoptRequirement GetDeoptRequirement(ArtJvmtiEvent event, jthread thread)
case ArtJvmtiEvent::kClassFileLoadHookRetransformable:
case ArtJvmtiEvent::kDdmPublishChunk:
case ArtJvmtiEvent::kObsoleteObjectCreated:
+ case ArtJvmtiEvent::kStructuralDexFileLoadHook:
return DeoptRequirement::kNone;
}
}
diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h
index d5ab4fbc98..c9d587af94 100644
--- a/openjdkjvmti/events.h
+++ b/openjdkjvmti/events.h
@@ -81,7 +81,8 @@ enum class ArtJvmtiEvent : jint {
kClassFileLoadHookRetransformable = JVMTI_MAX_EVENT_TYPE_VAL + 1,
kDdmPublishChunk = JVMTI_MAX_EVENT_TYPE_VAL + 2,
kObsoleteObjectCreated = JVMTI_MAX_EVENT_TYPE_VAL + 3,
- kMaxNormalEventTypeVal = kObsoleteObjectCreated,
+ kStructuralDexFileLoadHook = JVMTI_MAX_EVENT_TYPE_VAL + 4,
+ kMaxNormalEventTypeVal = kStructuralDexFileLoadHook,
// All that follow are events used to implement internal JVMTI functions. They are not settable
// directly by agents.
@@ -107,6 +108,17 @@ using ArtJvmtiEventObsoleteObjectCreated = void (*)(jvmtiEnv *jvmti_env,
jlong* obsolete_tag,
jlong* new_tag);
+using ArtJvmtiEventStructuralDexFileLoadHook = void (*)(jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jclass class_being_redefined,
+ jobject loader,
+ const char* name,
+ jobject protection_domain,
+ jint dex_data_len,
+ const unsigned char* dex_data,
+ jint* new_dex_data_len,
+ unsigned char** new_dex_data);
+
// It is not enough to store a Thread pointer, as these may be reused. Use the pointer and the
// thread id.
// Note: We could just use the tid like tracing does.
@@ -119,7 +131,10 @@ struct UniqueThreadHasher {
};
struct ArtJvmtiEventCallbacks : jvmtiEventCallbacks {
- ArtJvmtiEventCallbacks() : DdmPublishChunk(nullptr), ObsoleteObjectCreated(nullptr) {
+ ArtJvmtiEventCallbacks()
+ : DdmPublishChunk(nullptr),
+ ObsoleteObjectCreated(nullptr),
+ StructuralDexFileLoadHook(nullptr) {
memset(this, 0, sizeof(jvmtiEventCallbacks));
}
@@ -131,6 +146,7 @@ struct ArtJvmtiEventCallbacks : jvmtiEventCallbacks {
ArtJvmtiEventDdmPublishChunk DdmPublishChunk;
ArtJvmtiEventObsoleteObjectCreated ObsoleteObjectCreated;
+ ArtJvmtiEventStructuralDexFileLoadHook StructuralDexFileLoadHook;
};
bool IsExtensionEvent(jint e);
diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc
index 988274b4d7..82ce916ccd 100644
--- a/openjdkjvmti/ti_class.cc
+++ b/openjdkjvmti/ti_class.cc
@@ -204,6 +204,9 @@ struct ClassCallback : public art::ClassLoadCallback {
memcpy(post_non_retransform.data(), def.GetDexData().data(), post_non_retransform.size());
}
+ // Call all structural transformation agents.
+ Transformer::TransformSingleClassDirect<ArtJvmtiEvent::kStructuralDexFileLoadHook>(
+ event_handler, self, &def);
// Call all retransformable agents.
Transformer::TransformSingleClassDirect<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
event_handler, self, &def);
diff --git a/openjdkjvmti/ti_class_definition.h b/openjdkjvmti/ti_class_definition.h
index 224e664459..cb0853bdb5 100644
--- a/openjdkjvmti/ti_class_definition.h
+++ b/openjdkjvmti/ti_class_definition.h
@@ -40,6 +40,7 @@
#include "base/array_ref.h"
#include "base/mem_map.h"
+#include "events.h"
namespace openjdkjvmti {
@@ -65,7 +66,8 @@ class ArtClassDefinition {
current_dex_file_(),
redefined_(false),
from_class_ext_(false),
- initialized_(false) {}
+ initialized_(false),
+ structural_transform_update_(false) {}
void InitFirstLoad(const char* descriptor,
art::Handle<art::mirror::ClassLoader> klass_loader,
@@ -76,7 +78,7 @@ class ArtClassDefinition {
ArtClassDefinition(ArtClassDefinition&& o) = default;
ArtClassDefinition& operator=(ArtClassDefinition&& o) = default;
- void SetNewDexData(jint new_dex_len, unsigned char* new_dex_data) {
+ void SetNewDexData(jint new_dex_len, unsigned char* new_dex_data, ArtJvmtiEvent event) {
DCHECK(IsInitialized());
if (new_dex_data == nullptr) {
return;
@@ -86,10 +88,17 @@ class ArtClassDefinition {
dex_data_memory_.resize(new_dex_len);
memcpy(dex_data_memory_.data(), new_dex_data, new_dex_len);
dex_data_ = art::ArrayRef<const unsigned char>(dex_data_memory_);
+ if (event == ArtJvmtiEvent::kStructuralDexFileLoadHook) {
+ structural_transform_update_ = true;
+ }
}
}
}
+ bool HasStructuralChanges() const {
+ return structural_transform_update_;
+ }
+
art::ArrayRef<const unsigned char> GetNewOriginalDexFile() const {
DCHECK(IsInitialized());
if (redefined_) {
@@ -187,6 +196,9 @@ class ArtClassDefinition {
bool initialized_;
+ // Set if we had a new dex from the given transform type.
+ bool structural_transform_update_;
+
DISALLOW_COPY_AND_ASSIGN(ArtClassDefinition);
};
diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc
index 5dc7445681..058a188630 100644
--- a/openjdkjvmti/ti_extension.cc
+++ b/openjdkjvmti/ti_extension.cc
@@ -30,6 +30,7 @@
#include <vector>
+#include "jvmti.h"
#include "ti_extension.h"
#include "art_jvmti.h"
@@ -45,6 +46,7 @@
#include "ti_monitor.h"
#include "ti_redefine.h"
#include "ti_search.h"
+#include "transform.h"
#include "thread-inl.h"
@@ -416,7 +418,40 @@ jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env,
return error;
}
- // StructurallyRedefineClass
+ // StructurallyRedefineClasses
+ error = add_extension(
+ reinterpret_cast<jvmtiExtensionFunction>(Redefiner::StructurallyRedefineClasses),
+ "com.android.art.class.structurally_redefine_classes",
+ "Entrypoint for structural class redefinition. Has the same signature as RedefineClasses."
+ " Currently this only supports adding new static fields to a class without any instance"
+ " fields or methods. After calling this com.android.art.structural_dex_file_load_hook"
+ " events will be triggered, followed by re-transformable ClassFileLoadHook events. After"
+ " this method completes subsequent RetransformClasses calls will use the input to this"
+ " function as the initial class definition.",
+ {
+ { "num_classes", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false },
+ { "class_definitions", JVMTI_KIND_IN_BUF, JVMTI_TYPE_CVOID, false },
+ },
+ {
+ ERR(CLASS_LOADER_UNSUPPORTED),
+ ERR(FAILS_VERIFICATION),
+ ERR(ILLEGAL_ARGUMENT),
+ ERR(INVALID_CLASS),
+ ERR(MUST_POSSESS_CAPABILITY),
+ ERR(MUST_POSSESS_CAPABILITY),
+ ERR(NULL_POINTER),
+ ERR(OUT_OF_MEMORY),
+ ERR(UNMODIFIABLE_CLASS),
+ ERR(UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED),
+ ERR(UNSUPPORTED_REDEFINITION_METHOD_ADDED),
+ ERR(UNSUPPORTED_REDEFINITION_METHOD_DELETED),
+ ERR(UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED),
+ });
+ if (error != ERR(NONE)) {
+ return error;
+ }
+
+ // StructurallyRedefineClassDirect
error = add_extension(
reinterpret_cast<jvmtiExtensionFunction>(Redefiner::StructurallyRedefineClassDirect),
"com.android.art.UNSAFE.class.structurally_redefine_class_direct",
@@ -494,7 +529,7 @@ jvmtiError ExtensionUtil::GetExtensionEvents(jvmtiEnv* env,
const char* id,
const char* short_description,
const std::vector<CParamInfo>& params) {
- DCHECK(IsExtensionEvent(extension_event_index));
+ DCHECK(IsExtensionEvent(extension_event_index)) << static_cast<jint>(extension_event_index);
jvmtiExtensionEventInfo event_info;
jvmtiError error;
@@ -592,7 +627,35 @@ jvmtiError ExtensionUtil::GetExtensionEvents(jvmtiEnv* env,
if (error != OK) {
return error;
}
-
+ art::Runtime* runtime = art::Runtime::Current();
+ if (runtime->GetJniIdType() == art::JniIdType::kIndices &&
+ (runtime->GetInstrumentation()->IsForcedInterpretOnly() || runtime->IsJavaDebuggable())) {
+ error = add_extension(
+ ArtJvmtiEvent::kStructuralDexFileLoadHook,
+ "com.android.art.class.structural_dex_file_load_hook",
+ "Called during class load, after a 'RetransformClasses' call, or after a 'RedefineClasses'"
+ " call in order to allow the agent to modify the class. This event is called after any"
+ " non-can_retransform_classes ClassFileLoadHookEvents and before any"
+ " can_retransform_classes ClassFileLoadHookEvents. The transformations applied are"
+ " restricted in the same way that transformations applied via the "
+ " 'com.android.art.class.structurally_redefine_classes' extension function. The arguments"
+ " to the event are identical to the ones in the ClassFileLoadHook and have the same"
+ " semantics.",
+ {
+ { "jni_env", JVMTI_KIND_IN, JVMTI_TYPE_JNIENV, false },
+ { "class_being_redefined", JVMTI_KIND_IN, JVMTI_TYPE_JCLASS, true },
+ { "loader", JVMTI_KIND_IN, JVMTI_TYPE_JOBJECT, false },
+ { "name", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CCHAR, false },
+ { "protection_domain", JVMTI_KIND_IN, JVMTI_TYPE_JOBJECT, true },
+ { "dex_data_len", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false },
+ { "dex_data", JVMTI_KIND_IN_BUF, JVMTI_TYPE_CCHAR, false },
+ { "new_dex_data_len", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false },
+ { "new_dex_data", JVMTI_KIND_ALLOC_BUF, JVMTI_TYPE_CCHAR, true },
+ });
+ } else {
+ LOG(INFO) << "debuggable & jni-type indices are required to implement structural "
+ << "class redefinition extensions.";
+ }
// Copy into output buffer.
*extension_count_ptr = ext_vector.size();
diff --git a/openjdkjvmti/ti_heap.cc b/openjdkjvmti/ti_heap.cc
index bbb2ced61a..702a1c462b 100644
--- a/openjdkjvmti/ti_heap.cc
+++ b/openjdkjvmti/ti_heap.cc
@@ -700,11 +700,6 @@ jvmtiError HeapUtil::IterateOverInstancesOfClass(jvmtiEnv* env,
return ERR(INVALID_CLASS);
}
art::Handle<art::mirror::Class> filter_klass(hs.NewHandle(klass_ptr->AsClass()));
- if (filter_klass->IsInterface()) {
- // nothing is an 'instance' of an interface so just return without walking anything.
- return OK;
- }
-
ObjectTagTable* tag_table = ArtJvmTiEnv::AsArtJvmTiEnv(env)->object_tag_table.get();
bool stop_reports = false;
auto visitor = [&](art::mirror::Object* obj) REQUIRES_SHARED(art::Locks::mutator_lock_) {
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index df25261654..50dc09c3c2 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -112,6 +112,7 @@
#include "non_debuggable_classes.h"
#include "obj_ptr.h"
#include "object_lock.h"
+#include "reflective_value_visitor.h"
#include "runtime.h"
#include "runtime_globals.h"
#include "stack.h"
@@ -376,7 +377,7 @@ jvmtiError Redefiner::GetClassRedefinitionError(jclass klass, /*out*/ std::strin
return ERR(INVALID_CLASS);
}
art::Handle<art::mirror::Class> h_klass(hs.NewHandle(obj->AsClass()));
- return Redefiner::GetClassRedefinitionError(h_klass, error_msg);
+ return Redefiner::GetClassRedefinitionError<kType>(h_klass, error_msg);
}
template <RedefinitionType kType>
@@ -574,9 +575,10 @@ Redefiner::ClassRedefinition::~ClassRedefinition() {
}
}
-jvmtiError Redefiner::RedefineClasses(jvmtiEnv* jenv,
- jint class_count,
- const jvmtiClassDefinition* definitions) {
+template<RedefinitionType kType>
+jvmtiError Redefiner::RedefineClassesGeneric(jvmtiEnv* jenv,
+ jint class_count,
+ const jvmtiClassDefinition* definitions) {
art::Runtime* runtime = art::Runtime::Current();
art::Thread* self = art::Thread::Current();
ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(jenv);
@@ -597,7 +599,8 @@ jvmtiError Redefiner::RedefineClasses(jvmtiEnv* jenv,
std::vector<ArtClassDefinition> def_vector;
def_vector.reserve(class_count);
for (jint i = 0; i < class_count; i++) {
- jvmtiError res = Redefiner::GetClassRedefinitionError(definitions[i].klass, &error_msg);
+ jvmtiError res = Redefiner::GetClassRedefinitionError<RedefinitionType::kNormal>(
+ definitions[i].klass, &error_msg);
if (res != OK) {
JVMTI_LOG(WARNING, env) << "FAILURE TO REDEFINE " << error_msg;
return res;
@@ -611,15 +614,35 @@ jvmtiError Redefiner::RedefineClasses(jvmtiEnv* jenv,
def_vector.push_back(std::move(def));
}
// Call all the transformation events.
- Transformer::RetransformClassesDirect(self, &def_vector);
- jvmtiError res = RedefineClassesDirect(
- env, runtime, self, def_vector, RedefinitionType::kNormal, &error_msg);
+ Transformer::RetransformClassesDirect<kType>(self, &def_vector);
+ if (kType == RedefinitionType::kStructural) {
+ Transformer::RetransformClassesDirect<RedefinitionType::kNormal>(self, &def_vector);
+ }
+ jvmtiError res = RedefineClassesDirect(env, runtime, self, def_vector, kType, &error_msg);
if (res != OK) {
JVMTI_LOG(WARNING, env) << "FAILURE TO REDEFINE " << error_msg;
}
return res;
}
+jvmtiError Redefiner::StructurallyRedefineClasses(jvmtiEnv* jenv,
+ jint class_count,
+ const jvmtiClassDefinition* definitions) {
+ ArtJvmTiEnv* art_env = ArtJvmTiEnv::AsArtJvmTiEnv(jenv);
+ if (art_env == nullptr) {
+ return ERR(INVALID_ENVIRONMENT);
+ } else if (art_env->capabilities.can_redefine_classes != 1) {
+ return ERR(MUST_POSSESS_CAPABILITY);
+ }
+ return RedefineClassesGeneric<RedefinitionType::kStructural>(jenv, class_count, definitions);
+}
+
+jvmtiError Redefiner::RedefineClasses(jvmtiEnv* jenv,
+ jint class_count,
+ const jvmtiClassDefinition* definitions) {
+ return RedefineClassesGeneric<RedefinitionType::kNormal>(jenv, class_count, definitions);
+}
+
jvmtiError Redefiner::StructurallyRedefineClassDirect(jvmtiEnv* env,
jclass klass,
const unsigned char* data,
@@ -663,6 +686,11 @@ jvmtiError Redefiner::RedefineClassesDirect(ArtJvmTiEnv* env,
// We don't actually need to do anything. Just return OK.
return OK;
}
+ // We need to fiddle with the verification class flags. To do this we need to make sure there are
+ // no concurrent redefinitions of the same class at the same time. For simplicity and because
+ // this is not expected to be a common occurrence we will just wrap the whole thing in a TOP-level
+ // lock.
+
// Stop JIT for the duration of this redefine since the JIT might concurrently compile a method we
// are going to redefine.
// TODO We should prevent user-code suspensions to make sure this isn't held for too long.
@@ -1149,13 +1177,10 @@ bool Redefiner::ClassRedefinition::CheckRedefinable() {
art::Handle<art::mirror::Class> h_klass(hs.NewHandle(GetMirrorClass()));
jvmtiError res;
- switch (driver_->type_) {
- case RedefinitionType::kNormal:
- res = Redefiner::GetClassRedefinitionError<RedefinitionType::kNormal>(h_klass, &err);
- break;
- case RedefinitionType::kStructural:
+ if (driver_->type_ == RedefinitionType::kStructural && this->IsStructuralRedefinition()) {
res = Redefiner::GetClassRedefinitionError<RedefinitionType::kStructural>(h_klass, &err);
- break;
+ } else {
+ res = Redefiner::GetClassRedefinitionError<RedefinitionType::kNormal>(h_klass, &err);
}
if (res != OK) {
RecordFailure(res, err);
@@ -1166,7 +1191,7 @@ bool Redefiner::ClassRedefinition::CheckRedefinable() {
}
bool Redefiner::ClassRedefinition::CheckRedefinitionIsValid() {
- return CheckRedefinable() && CheckClass() && CheckFields() && CheckMethods();
+ return CheckClass() && CheckFields() && CheckMethods() && CheckRedefinable();
}
class RedefinitionDataIter;
@@ -1616,6 +1641,11 @@ bool Redefiner::ClassRedefinition::FinishRemainingAllocations(
}
cur_data->SetNewClassObject(nc.Get());
+ // We really want to be able to resolve to the new class-object using this dex-cache for
+ // verification work. Since we haven't put it in the class-table yet we wll just manually add it
+ // to the dex-cache.
+ // TODO: We should maybe do this in a better spot.
+ cur_data->GetNewDexCache()->SetResolvedType(nc->GetDexTypeIndex(), nc.Get());
}
return true;
}
@@ -1863,31 +1893,33 @@ jvmtiError Redefiner::Run() {
}
UnregisterAllBreakpoints();
- // Disable GC and wait for it to be done if we are a moving GC. This is fine since we are done
- // allocating so no deadlocks.
- ScopedDisableConcurrentAndMovingGc sdcamgc(runtime_->GetHeap(), self_);
-
- // Do transition to final suspension
- // TODO We might want to give this its own suspended state!
- // TODO This isn't right. We need to change state without any chance of suspend ideally!
- art::ScopedThreadSuspension sts(self_, art::ThreadState::kNative);
- art::ScopedSuspendAll ssa("Final installation of redefined Classes!", /*long_suspend=*/true);
- for (RedefinitionDataIter data = holder.begin(); data != holder.end(); ++data) {
- art::ScopedAssertNoThreadSuspension nts("Updating runtime objects for redefinition");
- ClassRedefinition& redef = data.GetRedefinition();
- if (data.GetSourceClassLoader() != nullptr) {
- ClassLoaderHelper::UpdateJavaDexFile(data.GetJavaDexFile(), data.GetNewDexFileCookie());
+ {
+ // Disable GC and wait for it to be done if we are a moving GC. This is fine since we are done
+ // allocating so no deadlocks.
+ ScopedDisableConcurrentAndMovingGc sdcamgc(runtime_->GetHeap(), self_);
+
+ // Do transition to final suspension
+ // TODO We might want to give this its own suspended state!
+ // TODO This isn't right. We need to change state without any chance of suspend ideally!
+ art::ScopedThreadSuspension sts(self_, art::ThreadState::kNative);
+ art::ScopedSuspendAll ssa("Final installation of redefined Classes!", /*long_suspend=*/true);
+ for (RedefinitionDataIter data = holder.begin(); data != holder.end(); ++data) {
+ art::ScopedAssertNoThreadSuspension nts("Updating runtime objects for redefinition");
+ ClassRedefinition& redef = data.GetRedefinition();
+ if (data.GetSourceClassLoader() != nullptr) {
+ ClassLoaderHelper::UpdateJavaDexFile(data.GetJavaDexFile(), data.GetNewDexFileCookie());
+ }
+ redef.UpdateClass(data);
}
- redef.UpdateClass(data);
- }
- RestoreObsoleteMethodMapsIfUnneeded(holder);
- // TODO We should check for if any of the redefined methods are intrinsic methods here and, if any
- // are, force a full-world deoptimization before finishing redefinition. If we don't do this then
- // methods that have been jitted prior to the current redefinition being applied might continue
- // to use the old versions of the intrinsics!
- // TODO Do the dex_file release at a more reasonable place. This works but it muddles who really
- // owns the DexFile and when ownership is transferred.
- ReleaseAllDexFiles();
+ RestoreObsoleteMethodMapsIfUnneeded(holder);
+ // TODO We should check for if any of the redefined methods are intrinsic methods here and, if
+ // any are, force a full-world deoptimization before finishing redefinition. If we don't do this
+ // then methods that have been jitted prior to the current redefinition being applied might
+ // continue to use the old versions of the intrinsics!
+ // TODO Do the dex_file release at a more reasonable place. This works but it muddles who really
+ // owns the DexFile and when ownership is transferred.
+ ReleaseAllDexFiles();
+ }
// By now the class-linker knows about all the classes so we can safetly retry verification and
// update method flags.
ReverifyClasses(holder);
@@ -1895,7 +1927,6 @@ jvmtiError Redefiner::Run() {
}
void Redefiner::ReverifyClasses(RedefinitionDataHolder& holder) {
- art::ScopedAssertNoThreadSuspension nts("Updating method flags");
for (RedefinitionDataIter data = holder.begin(); data != holder.end(); ++data) {
data.GetRedefinition().ReverifyClass(data);
}
@@ -2062,25 +2093,84 @@ void Redefiner::ClassRedefinition::UpdateClassStructurally(const RedefinitionDat
replacement->SetLockWord(orig->GetLockWord(false), false);
orig->SetLockWord(art::LockWord::Default(), false);
// Update live pointers in ART code.
+ auto could_change_resolution_of = [&](auto* field_or_method,
+ const auto& info) REQUIRES(art::Locks::mutator_lock_) {
+ constexpr bool is_method = std::is_same_v<art::ArtMethod*, decltype(field_or_method)>;
+ static_assert(is_method || std::is_same_v<art::ArtField*, decltype(field_or_method)>,
+ "Input is not field or method!");
+ // Only dex-cache is used for resolution
+ if (LIKELY(info.GetType() != art::ReflectionSourceType::kSourceDexCacheResolvedField &&
+ info.GetType() != art::ReflectionSourceType::kSourceDexCacheResolvedMethod)) {
+ return false;
+ }
+ if constexpr (is_method) {
+ // Only direct methods are used without further indirection through a vtable/IFTable.
+ // Constructors cannot be shadowed.
+ if (LIKELY(!field_or_method->IsDirect() || field_or_method->IsConstructor())) {
+ return false;
+ }
+ } else {
+ // Only non-private fields can be shadowed in a manner that's visible.
+ if (LIKELY(field_or_method->IsPrivate())) {
+ return false;
+ }
+ }
+ // We can only shadow things from our superclasses
+ if (LIKELY(!field_or_method->GetDeclaringClass()->IsAssignableFrom(orig))) {
+ return false;
+ }
+ if constexpr (is_method) {
+ auto direct_methods = replacement->GetDirectMethods(art::kRuntimePointerSize);
+ return std::find_if(direct_methods.begin(),
+ direct_methods.end(),
+ [&](art::ArtMethod& m) REQUIRES(art::Locks::mutator_lock_) {
+ return UNLIKELY(m.HasSameNameAndSignature(field_or_method));
+ }) != direct_methods.end();
+ } else {
+ auto pred = [&](art::ArtField& f) REQUIRES(art::Locks::mutator_lock_) {
+ return std::string_view(f.GetName()) == std::string_view(field_or_method->GetName()) &&
+ std::string_view(f.GetTypeDescriptor()) ==
+ std::string_view(field_or_method->GetTypeDescriptor());
+ };
+ if (field_or_method->IsStatic()) {
+ auto sfields = replacement->GetSFields();
+ return std::find_if(sfields.begin(), sfields.end(), pred) != sfields.end();
+ } else {
+ auto ifields = replacement->GetIFields();
+ return std::find_if(ifields.begin(), ifields.end(), pred) != ifields.end();
+ }
+ }
+ };
// TODO Performing 2 stack-walks back to back isn't the greatest. We might want to try to combine
// it with the one ReplaceReferences does. Doing so would be rather complicated though.
driver_->runtime_->VisitReflectiveTargets(
[&](art::ArtField* f, const auto& info) REQUIRES(art::Locks::mutator_lock_) {
+ DCHECK(f != nullptr) << info;
auto it = field_map.find(f);
- if (it == field_map.end()) {
- return f;
+ if (it != field_map.end()) {
+ VLOG(plugin) << "Updating " << info << " object for (field) "
+ << it->second->PrettyField();
+ return it->second;
+ } else if (UNLIKELY(could_change_resolution_of(f, info))) {
+ // Resolution might change. Just clear the resolved value.
+ VLOG(plugin) << "Clearing resolution " << info << " for (field) " << f->PrettyField();
+ return static_cast<art::ArtField*>(nullptr);
}
- VLOG(plugin) << "Updating " << info << " object for (field) " << it->second->PrettyField();
- return it->second;
+ return f;
},
[&](art::ArtMethod* m, const auto& info) REQUIRES(art::Locks::mutator_lock_) {
+ DCHECK(m != nullptr) << info;
auto it = method_map.find(m);
- if (it == method_map.end()) {
- return m;
+ if (it != method_map.end()) {
+ VLOG(plugin) << "Updating " << info << " object for (method) "
+ << it->second->PrettyMethod();
+ return it->second;
+ } else if (UNLIKELY(could_change_resolution_of(m, info))) {
+ // Resolution might change. Just clear the resolved value.
+ VLOG(plugin) << "Clearing resolution " << info << " for (method) " << m->PrettyMethod();
+ return static_cast<art::ArtMethod*>(nullptr);
}
- VLOG(plugin) << "Updating " << info << " object for (method) "
- << it->second->PrettyMethod();
- return it->second;
+ return m;
});
// Force every frame of every thread to deoptimize (any frame might have eg offsets compiled in).
@@ -2208,6 +2298,25 @@ void Redefiner::ClassRedefinition::UpdateClass(const RedefinitionDataIter& holde
} else {
UpdateClassInPlace(holder);
}
+ UpdateClassCommon(holder);
+}
+
+void Redefiner::ClassRedefinition::UpdateClassCommon(const RedefinitionDataIter &cur_data) {
+ // NB This is after we've already replaced all old-refs with new-refs in the structural case.
+ art::ObjPtr<art::mirror::Class> klass(cur_data.GetMirrorClass());
+ DCHECK(!IsStructuralRedefinition() || klass == cur_data.GetNewClassObject());
+ if (!needs_reverify_) {
+ return;
+ }
+ // Force the most restrictive interpreter environment. We don't know what the final verification
+ // will allow. We will clear these after retrying verification once we drop the mutator-lock.
+ klass->VisitMethods([](art::ArtMethod* m) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ if (!m->IsNative() && m->IsInvokable() && !m->IsObsolete()) {
+ m->ClearSkipAccessChecks();
+ m->SetDontCompile();
+ m->SetMustCountLocks();
+ }
+ }, art::kRuntimePointerSize);
}
// Restores the old obsolete methods maps if it turns out they weren't needed (ie there were no new
diff --git a/openjdkjvmti/ti_redefine.h b/openjdkjvmti/ti_redefine.h
index dad085de59..58a688c1a0 100644
--- a/openjdkjvmti/ti_redefine.h
+++ b/openjdkjvmti/ti_redefine.h
@@ -87,6 +87,9 @@ class Redefiner {
static jvmtiError RedefineClasses(jvmtiEnv* env,
jint class_count,
const jvmtiClassDefinition* definitions);
+ static jvmtiError StructurallyRedefineClasses(jvmtiEnv* env,
+ jint class_count,
+ const jvmtiClassDefinition* definitions);
static jvmtiError IsModifiableClass(jvmtiEnv* env, jclass klass, jboolean* is_redefinable);
static jvmtiError IsStructurallyModifiableClass(jvmtiEnv* env,
@@ -209,9 +212,12 @@ class Redefiner {
void UpdateClass(const RedefinitionDataIter& cur_data)
REQUIRES(art::Locks::mutator_lock_);
- void ReverifyClass(const RedefinitionDataIter& cur_data)
+ void UpdateClassCommon(const RedefinitionDataIter& cur_data)
REQUIRES(art::Locks::mutator_lock_);
+ void ReverifyClass(const RedefinitionDataIter& cur_data)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
+
void CollectNewFieldAndMethodMappings(const RedefinitionDataIter& data,
std::map<art::ArtMethod*, art::ArtMethod*>* method_map,
std::map<art::ArtField*, art::ArtField*>* field_map)
@@ -284,6 +290,11 @@ class Redefiner {
REQUIRES_SHARED(art::Locks::mutator_lock_);
template<RedefinitionType kType = RedefinitionType::kNormal>
+ static jvmtiError RedefineClassesGeneric(jvmtiEnv* env,
+ jint class_count,
+ const jvmtiClassDefinition* definitions);
+
+ template<RedefinitionType kType = RedefinitionType::kNormal>
static jvmtiError IsModifiableClassGeneric(jvmtiEnv* env, jclass klass, jboolean* is_redefinable);
template<RedefinitionType kType = RedefinitionType::kNormal>
@@ -301,7 +312,7 @@ class Redefiner {
bool FinishAllRemainingAllocations(RedefinitionDataHolder& holder)
REQUIRES_SHARED(art::Locks::mutator_lock_);
void ReleaseAllDexFiles() REQUIRES_SHARED(art::Locks::mutator_lock_);
- void ReverifyClasses(RedefinitionDataHolder& holder) REQUIRES(art::Locks::mutator_lock_);
+ void ReverifyClasses(RedefinitionDataHolder& holder) REQUIRES_SHARED(art::Locks::mutator_lock_);
void UnregisterAllBreakpoints() REQUIRES_SHARED(art::Locks::mutator_lock_);
// Restores the old obsolete methods maps if it turns out they weren't needed (ie there were no
// new obsolete methods).
diff --git a/openjdkjvmti/transform.cc b/openjdkjvmti/transform.cc
index aa37793e49..613368525e 100644
--- a/openjdkjvmti/transform.cc
+++ b/openjdkjvmti/transform.cc
@@ -255,13 +255,17 @@ void Transformer::TransformSingleClassDirect<ArtJvmtiEvent::kClassFileLoadHookNo
template
void Transformer::TransformSingleClassDirect<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
EventHandler* event_handler, art::Thread* self, /*in-out*/ArtClassDefinition* def);
+template
+void Transformer::TransformSingleClassDirect<ArtJvmtiEvent::kStructuralDexFileLoadHook>(
+ EventHandler* event_handler, art::Thread* self, /*in-out*/ArtClassDefinition* def);
template<ArtJvmtiEvent kEvent>
void Transformer::TransformSingleClassDirect(EventHandler* event_handler,
art::Thread* self,
/*in-out*/ArtClassDefinition* def) {
static_assert(kEvent == ArtJvmtiEvent::kClassFileLoadHookNonRetransformable ||
- kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable,
+ kEvent == ArtJvmtiEvent::kClassFileLoadHookRetransformable ||
+ kEvent == ArtJvmtiEvent::kStructuralDexFileLoadHook,
"bad event type");
// We don't want to do transitions between calling the event and setting the new data so change to
// native state early. This also avoids any problems that the FaultHandler might have in
@@ -282,25 +286,30 @@ void Transformer::TransformSingleClassDirect(EventHandler* event_handler,
dex_data.data(),
/*out*/&new_len,
/*out*/&new_data);
- def->SetNewDexData(new_len, new_data);
+ def->SetNewDexData(new_len, new_data, kEvent);
}
+template <RedefinitionType kType>
void Transformer::RetransformClassesDirect(
- art::Thread* self,
- /*in-out*/std::vector<ArtClassDefinition>* definitions) {
+ art::Thread* self,
+ /*in-out*/ std::vector<ArtClassDefinition>* definitions) {
+ constexpr ArtJvmtiEvent kEvent = kType == RedefinitionType::kNormal
+ ? ArtJvmtiEvent::kClassFileLoadHookRetransformable
+ : ArtJvmtiEvent::kStructuralDexFileLoadHook;
for (ArtClassDefinition& def : *definitions) {
- TransformSingleClassDirect<ArtJvmtiEvent::kClassFileLoadHookRetransformable>(
- gEventHandler, self, &def);
+ TransformSingleClassDirect<kEvent>(gEventHandler, self, &def);
}
}
+template void Transformer::RetransformClassesDirect<RedefinitionType::kNormal>(
+ art::Thread* self, /*in-out*/std::vector<ArtClassDefinition>* definitions);
+template void Transformer::RetransformClassesDirect<RedefinitionType::kStructural>(
+ art::Thread* self, /*in-out*/std::vector<ArtClassDefinition>* definitions);
+
jvmtiError Transformer::RetransformClasses(jvmtiEnv* env,
jint class_count,
const jclass* classes) {
- if (env == nullptr) {
- JVMTI_LOG(WARNING, env) << "FAILURE TO RETRANSFORM env was null!";
- return ERR(INVALID_ENVIRONMENT);
- } else if (class_count < 0) {
+ if (class_count < 0) {
JVMTI_LOG(WARNING, env) << "FAILURE TO RETRANSFORM class_count was less then 0";
return ERR(ILLEGAL_ARGUMENT);
} else if (class_count == 0) {
@@ -317,7 +326,7 @@ jvmtiError Transformer::RetransformClasses(jvmtiEnv* env,
std::vector<ArtClassDefinition> definitions;
jvmtiError res = OK;
for (jint i = 0; i < class_count; i++) {
- res = Redefiner::GetClassRedefinitionError(classes[i], &error_msg);
+ res = Redefiner::GetClassRedefinitionError<RedefinitionType::kNormal>(classes[i], &error_msg);
if (res != OK) {
JVMTI_LOG(WARNING, env) << "FAILURE TO RETRANSFORM " << error_msg;
return res;
@@ -330,13 +339,16 @@ jvmtiError Transformer::RetransformClasses(jvmtiEnv* env,
}
definitions.push_back(std::move(def));
}
- RetransformClassesDirect(self, &definitions);
- res = Redefiner::RedefineClassesDirect(ArtJvmTiEnv::AsArtJvmTiEnv(env),
- runtime,
- self,
- definitions,
- RedefinitionType::kNormal,
- &error_msg);
+ RetransformClassesDirect<RedefinitionType::kStructural>(self, &definitions);
+ RetransformClassesDirect<RedefinitionType::kNormal>(self, &definitions);
+ RedefinitionType redef_type =
+ std::any_of(definitions.cbegin(),
+ definitions.cend(),
+ [](const auto& it) { return it.HasStructuralChanges(); })
+ ? RedefinitionType::kStructural
+ : RedefinitionType::kNormal;
+ res = Redefiner::RedefineClassesDirect(
+ ArtJvmTiEnv::AsArtJvmTiEnv(env), runtime, self, definitions, redef_type, &error_msg);
if (res != OK) {
JVMTI_LOG(WARNING, env) << "FAILURE TO RETRANSFORM " << error_msg;
}
diff --git a/openjdkjvmti/transform.h b/openjdkjvmti/transform.h
index 40c7267ad1..a58b50ea10 100644
--- a/openjdkjvmti/transform.h
+++ b/openjdkjvmti/transform.h
@@ -39,6 +39,7 @@
#include "art_jvmti.h"
#include "ti_class_definition.h"
+#include "ti_redefine.h"
namespace openjdkjvmti {
@@ -56,6 +57,7 @@ class Transformer {
art::Thread* self,
/*in-out*/ArtClassDefinition* def);
+ template<RedefinitionType kType>
static void RetransformClassesDirect(
art::Thread* self,
/*in-out*/std::vector<ArtClassDefinition>* definitions);
diff --git a/runtime/Android.bp b/runtime/Android.bp
index cfa16f87dc..0546c69b41 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -497,7 +497,7 @@ gensrcs {
"jdwp/jdwp_constants.h",
"jni_id_type.h",
"lock_word.h",
- "oat.h",
+ "oat_file.h",
"object_callbacks.h",
"process_state.h",
"reflective_value_visitor.h",
diff --git a/runtime/art_field.h b/runtime/art_field.h
index e44517e10b..bc2c399b74 100644
--- a/runtime/art_field.h
+++ b/runtime/art_field.h
@@ -75,6 +75,10 @@ class ArtField final {
return (GetAccessFlags() & kAccFinal) != 0;
}
+ bool IsPrivate() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return (GetAccessFlags() & kAccPrivate) != 0;
+ }
+
uint32_t GetDexFieldIndex() {
return field_dex_idx_;
}
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 3aec1c3211..dfadc622d1 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -39,7 +39,6 @@
#include "mirror/object-inl.h"
#include "mirror/object_array.h"
#include "mirror/string.h"
-#include "oat.h"
#include "obj_ptr-inl.h"
#include "quick/quick_method_frame_info.h"
#include "read_barrier-inl.h"
diff --git a/runtime/art_method.h b/runtime/art_method.h
index d4fb5d7b90..7b435d5893 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -110,14 +110,14 @@ class ArtMethod final {
return MemberOffset(OFFSETOF_MEMBER(ArtMethod, declaring_class_));
}
- uint32_t GetAccessFlags() {
+ uint32_t GetAccessFlags() const {
return access_flags_.load(std::memory_order_relaxed);
}
// This version should only be called when it's certain there is no
// concurrency so there is no need to guarantee atomicity. For example,
// before the method is linked.
- void SetAccessFlags(uint32_t new_access_flags) {
+ void SetAccessFlags(uint32_t new_access_flags) REQUIRES_SHARED(Locks::mutator_lock_) {
access_flags_.store(new_access_flags, std::memory_order_relaxed);
}
@@ -129,32 +129,32 @@ class ArtMethod final {
InvokeType GetInvokeType() REQUIRES_SHARED(Locks::mutator_lock_);
// Returns true if the method is declared public.
- bool IsPublic() {
+ bool IsPublic() const {
return (GetAccessFlags() & kAccPublic) != 0;
}
// Returns true if the method is declared private.
- bool IsPrivate() {
+ bool IsPrivate() const {
return (GetAccessFlags() & kAccPrivate) != 0;
}
// Returns true if the method is declared static.
- bool IsStatic() {
+ bool IsStatic() const {
return (GetAccessFlags() & kAccStatic) != 0;
}
// Returns true if the method is a constructor according to access flags.
- bool IsConstructor() {
+ bool IsConstructor() const {
return (GetAccessFlags() & kAccConstructor) != 0;
}
// Returns true if the method is a class initializer according to access flags.
- bool IsClassInitializer() {
+ bool IsClassInitializer() const {
return IsConstructor() && IsStatic();
}
// Returns true if the method is static, private, or a constructor.
- bool IsDirect() {
+ bool IsDirect() const {
return IsDirect(GetAccessFlags());
}
@@ -164,22 +164,22 @@ class ArtMethod final {
}
// Returns true if the method is declared synchronized.
- bool IsSynchronized() {
+ bool IsSynchronized() const {
constexpr uint32_t synchonized = kAccSynchronized | kAccDeclaredSynchronized;
return (GetAccessFlags() & synchonized) != 0;
}
- bool IsFinal() {
+ bool IsFinal() const {
return (GetAccessFlags() & kAccFinal) != 0;
}
- bool IsIntrinsic() {
+ bool IsIntrinsic() const {
return (GetAccessFlags() & kAccIntrinsic) != 0;
}
ALWAYS_INLINE void SetIntrinsic(uint32_t intrinsic) REQUIRES_SHARED(Locks::mutator_lock_);
- uint32_t GetIntrinsic() {
+ uint32_t GetIntrinsic() const {
static const int kAccFlagsShift = CTZ(kAccIntrinsicBits);
static_assert(IsPowerOfTwo((kAccIntrinsicBits >> kAccFlagsShift) + 1),
"kAccIntrinsicBits are not continuous");
@@ -191,7 +191,7 @@ class ArtMethod final {
void SetNotIntrinsic() REQUIRES_SHARED(Locks::mutator_lock_);
- bool IsCopied() {
+ bool IsCopied() const {
static_assert((kAccCopied & (kAccIntrinsic | kAccIntrinsicBits)) == 0,
"kAccCopied conflicts with intrinsic modifier");
const bool copied = (GetAccessFlags() & kAccCopied) != 0;
@@ -201,7 +201,7 @@ class ArtMethod final {
return copied;
}
- bool IsMiranda() {
+ bool IsMiranda() const {
// The kAccMiranda flag value is used with a different meaning for native methods and methods
// marked kAccCompileDontBother, so we need to check these flags as well.
return (GetAccessFlags() & (kAccNative | kAccMiranda | kAccCompileDontBother)) == kAccMiranda;
@@ -209,26 +209,26 @@ class ArtMethod final {
// Returns true if invoking this method will not throw an AbstractMethodError or
// IncompatibleClassChangeError.
- bool IsInvokable() {
+ bool IsInvokable() const {
return !IsAbstract() && !IsDefaultConflicting();
}
- bool IsPreCompiled() {
+ bool IsPreCompiled() const {
uint32_t expected = (kAccPreCompiled | kAccCompileDontBother);
return (GetAccessFlags() & expected) == expected;
}
- void SetPreCompiled() {
+ void SetPreCompiled() REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(IsInvokable());
DCHECK(IsCompilable());
AddAccessFlags(kAccPreCompiled | kAccCompileDontBother);
}
- void ClearPreCompiled() {
+ void ClearPreCompiled() REQUIRES_SHARED(Locks::mutator_lock_) {
ClearAccessFlags(kAccPreCompiled | kAccCompileDontBother);
}
- bool IsCompilable() {
+ bool IsCompilable() const {
if (IsIntrinsic()) {
// kAccCompileDontBother overlaps with kAccIntrinsicBits.
return true;
@@ -239,7 +239,12 @@ class ArtMethod final {
return (GetAccessFlags() & kAccCompileDontBother) == 0;
}
- void SetDontCompile() {
+ void ClearDontCompile() REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(!IsMiranda());
+ ClearAccessFlags(kAccCompileDontBother);
+ }
+
+ void SetDontCompile() REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(!IsMiranda());
AddAccessFlags(kAccCompileDontBother);
}
@@ -247,7 +252,7 @@ class ArtMethod final {
// A default conflict method is a special sentinel method that stands for a conflict between
// multiple default methods. It cannot be invoked, throwing an IncompatibleClassChangeError if one
// attempts to do so.
- bool IsDefaultConflicting() {
+ bool IsDefaultConflicting() const {
if (IsIntrinsic()) {
return false;
}
@@ -255,26 +260,26 @@ class ArtMethod final {
}
// This is set by the class linker.
- bool IsDefault() {
+ bool IsDefault() const {
static_assert((kAccDefault & (kAccIntrinsic | kAccIntrinsicBits)) == 0,
"kAccDefault conflicts with intrinsic modifier");
return (GetAccessFlags() & kAccDefault) != 0;
}
- bool IsObsolete() {
+ bool IsObsolete() const {
return (GetAccessFlags() & kAccObsoleteMethod) != 0;
}
- void SetIsObsolete() {
+ void SetIsObsolete() REQUIRES_SHARED(Locks::mutator_lock_) {
AddAccessFlags(kAccObsoleteMethod);
}
- bool IsNative() {
+ bool IsNative() const {
return (GetAccessFlags() & kAccNative) != 0;
}
// Checks to see if the method was annotated with @dalvik.annotation.optimization.FastNative.
- bool IsFastNative() {
+ bool IsFastNative() const {
// The presence of the annotation is checked by ClassLinker and recorded in access flags.
// The kAccFastNative flag value is used with a different meaning for non-native methods,
// so we need to check the kAccNative flag as well.
@@ -283,7 +288,7 @@ class ArtMethod final {
}
// Checks to see if the method was annotated with @dalvik.annotation.optimization.CriticalNative.
- bool IsCriticalNative() {
+ bool IsCriticalNative() const {
// The presence of the annotation is checked by ClassLinker and recorded in access flags.
// The kAccCriticalNative flag value is used with a different meaning for non-native methods,
// so we need to check the kAccNative flag as well.
@@ -291,15 +296,15 @@ class ArtMethod final {
return (GetAccessFlags() & mask) == mask;
}
- bool IsAbstract() {
+ bool IsAbstract() const {
return (GetAccessFlags() & kAccAbstract) != 0;
}
- bool IsSynthetic() {
+ bool IsSynthetic() const {
return (GetAccessFlags() & kAccSynthetic) != 0;
}
- bool IsVarargs() {
+ bool IsVarargs() const {
return (GetAccessFlags() & kAccVarargs) != 0;
}
@@ -307,41 +312,41 @@ class ArtMethod final {
bool IsPolymorphicSignature() REQUIRES_SHARED(Locks::mutator_lock_);
- bool UseFastInterpreterToInterpreterInvoke() {
+ bool UseFastInterpreterToInterpreterInvoke() const {
// The bit is applicable only if the method is not intrinsic.
constexpr uint32_t mask = kAccFastInterpreterToInterpreterInvoke | kAccIntrinsic;
return (GetAccessFlags() & mask) == kAccFastInterpreterToInterpreterInvoke;
}
- void SetFastInterpreterToInterpreterInvokeFlag() {
+ void SetFastInterpreterToInterpreterInvokeFlag() REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(!IsIntrinsic());
AddAccessFlags(kAccFastInterpreterToInterpreterInvoke);
}
- void ClearFastInterpreterToInterpreterInvokeFlag() {
+ void ClearFastInterpreterToInterpreterInvokeFlag() REQUIRES_SHARED(Locks::mutator_lock_) {
if (!IsIntrinsic()) {
ClearAccessFlags(kAccFastInterpreterToInterpreterInvoke);
}
}
- bool SkipAccessChecks() {
+ bool SkipAccessChecks() const {
// The kAccSkipAccessChecks flag value is used with a different meaning for native methods,
// so we need to check the kAccNative flag as well.
return (GetAccessFlags() & (kAccSkipAccessChecks | kAccNative)) == kAccSkipAccessChecks;
}
- void SetSkipAccessChecks() {
+ void SetSkipAccessChecks() REQUIRES_SHARED(Locks::mutator_lock_) {
// SkipAccessChecks() is applicable only to non-native methods.
DCHECK(!IsNative());
AddAccessFlags(kAccSkipAccessChecks);
}
- void ClearSkipAccessChecks() {
+ void ClearSkipAccessChecks() REQUIRES_SHARED(Locks::mutator_lock_) {
// SkipAccessChecks() is applicable only to non-native methods.
DCHECK(!IsNative());
ClearAccessFlags(kAccSkipAccessChecks);
}
- bool PreviouslyWarm() {
+ bool PreviouslyWarm() const {
if (IsIntrinsic()) {
// kAccPreviouslyWarm overlaps with kAccIntrinsicBits.
return true;
@@ -349,7 +354,7 @@ class ArtMethod final {
return (GetAccessFlags() & kAccPreviouslyWarm) != 0;
}
- void SetPreviouslyWarm() {
+ void SetPreviouslyWarm() REQUIRES_SHARED(Locks::mutator_lock_) {
if (IsIntrinsic()) {
// kAccPreviouslyWarm overlaps with kAccIntrinsicBits.
return;
@@ -359,14 +364,18 @@ class ArtMethod final {
// Should this method be run in the interpreter and count locks (e.g., failed structured-
// locking verification)?
- bool MustCountLocks() {
+ bool MustCountLocks() const {
if (IsIntrinsic()) {
return false;
}
return (GetAccessFlags() & kAccMustCountLocks) != 0;
}
- void SetMustCountLocks() {
+ void ClearMustCountLocks() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ClearAccessFlags(kAccMustCountLocks);
+ }
+
+ void SetMustCountLocks() REQUIRES_SHARED(Locks::mutator_lock_) {
AddAccessFlags(kAccMustCountLocks);
ClearAccessFlags(kAccSkipAccessChecks);
}
@@ -402,11 +411,11 @@ class ArtMethod final {
return MemberOffset(OFFSETOF_MEMBER(ArtMethod, method_index_));
}
- uint32_t GetCodeItemOffset() {
+ uint32_t GetCodeItemOffset() const {
return dex_code_item_offset_;
}
- void SetCodeItemOffset(uint32_t new_code_off) {
+ void SetCodeItemOffset(uint32_t new_code_off) REQUIRES_SHARED(Locks::mutator_lock_) {
// Not called within a transaction.
dex_code_item_offset_ = new_code_off;
}
@@ -414,11 +423,11 @@ class ArtMethod final {
// Number of 32bit registers that would be required to hold all the arguments
static size_t NumArgRegisters(const char* shorty);
- ALWAYS_INLINE uint32_t GetDexMethodIndex() {
+ ALWAYS_INLINE uint32_t GetDexMethodIndex() const {
return dex_method_index_;
}
- void SetDexMethodIndex(uint32_t new_idx) {
+ void SetDexMethodIndex(uint32_t new_idx) REQUIRES_SHARED(Locks::mutator_lock_) {
// Not called within a transaction.
dex_method_index_ = new_idx;
}
@@ -448,20 +457,23 @@ class ArtMethod final {
void Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result, const char* shorty)
REQUIRES_SHARED(Locks::mutator_lock_);
- const void* GetEntryPointFromQuickCompiledCode() {
+ const void* GetEntryPointFromQuickCompiledCode() const {
return GetEntryPointFromQuickCompiledCodePtrSize(kRuntimePointerSize);
}
- ALWAYS_INLINE const void* GetEntryPointFromQuickCompiledCodePtrSize(PointerSize pointer_size) {
+ ALWAYS_INLINE
+ const void* GetEntryPointFromQuickCompiledCodePtrSize(PointerSize pointer_size) const {
return GetNativePointer<const void*>(
EntryPointFromQuickCompiledCodeOffset(pointer_size), pointer_size);
}
- void SetEntryPointFromQuickCompiledCode(const void* entry_point_from_quick_compiled_code) {
+ void SetEntryPointFromQuickCompiledCode(const void* entry_point_from_quick_compiled_code)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
SetEntryPointFromQuickCompiledCodePtrSize(entry_point_from_quick_compiled_code,
kRuntimePointerSize);
}
ALWAYS_INLINE void SetEntryPointFromQuickCompiledCodePtrSize(
- const void* entry_point_from_quick_compiled_code, PointerSize pointer_size) {
+ const void* entry_point_from_quick_compiled_code, PointerSize pointer_size)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
SetNativePointer(EntryPointFromQuickCompiledCodeOffset(pointer_size),
entry_point_from_quick_compiled_code,
pointer_size);
@@ -491,12 +503,13 @@ class ArtMethod final {
* static_cast<size_t>(pointer_size));
}
- ImtConflictTable* GetImtConflictTable(PointerSize pointer_size) {
+ ImtConflictTable* GetImtConflictTable(PointerSize pointer_size) const {
DCHECK(IsRuntimeMethod());
return reinterpret_cast<ImtConflictTable*>(GetDataPtrSize(pointer_size));
}
- ALWAYS_INLINE void SetImtConflictTable(ImtConflictTable* table, PointerSize pointer_size) {
+ ALWAYS_INLINE void SetImtConflictTable(ImtConflictTable* table, PointerSize pointer_size)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(IsRuntimeMethod());
SetDataPtrSize(table, pointer_size);
}
@@ -508,11 +521,12 @@ class ArtMethod final {
return reinterpret_cast<ProfilingInfo*>(GetDataPtrSize(pointer_size));
}
- ALWAYS_INLINE void SetProfilingInfo(ProfilingInfo* info) {
+ ALWAYS_INLINE void SetProfilingInfo(ProfilingInfo* info) REQUIRES_SHARED(Locks::mutator_lock_) {
SetDataPtrSize(info, kRuntimePointerSize);
}
- ALWAYS_INLINE void SetProfilingInfoPtrSize(ProfilingInfo* info, PointerSize pointer_size) {
+ ALWAYS_INLINE void SetProfilingInfoPtrSize(ProfilingInfo* info, PointerSize pointer_size)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
SetDataPtrSize(info, pointer_size);
}
@@ -524,7 +538,8 @@ class ArtMethod final {
template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
ALWAYS_INLINE bool HasSingleImplementation() REQUIRES_SHARED(Locks::mutator_lock_);
- ALWAYS_INLINE void SetHasSingleImplementation(bool single_impl) {
+ ALWAYS_INLINE void SetHasSingleImplementation(bool single_impl)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(!IsIntrinsic()) << "conflict with intrinsic bits";
if (single_impl) {
AddAccessFlags(kAccSingleImplementation);
@@ -533,6 +548,10 @@ class ArtMethod final {
}
}
+ ALWAYS_INLINE bool HasSingleImplementationFlag() const {
+ return (GetAccessFlags() & kAccSingleImplementation) != 0;
+ }
+
// Takes a method and returns a 'canonical' one if the method is default (and therefore
// potentially copied from some other class). For example, this ensures that the debugger does not
// get confused as to which method we are in.
@@ -541,44 +560,48 @@ class ArtMethod final {
ArtMethod* GetSingleImplementation(PointerSize pointer_size);
- ALWAYS_INLINE void SetSingleImplementation(ArtMethod* method, PointerSize pointer_size) {
+ ALWAYS_INLINE void SetSingleImplementation(ArtMethod* method, PointerSize pointer_size)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(!IsNative());
// Non-abstract method's single implementation is just itself.
DCHECK(IsAbstract());
SetDataPtrSize(method, pointer_size);
}
- void* GetEntryPointFromJni() {
+ void* GetEntryPointFromJni() const {
DCHECK(IsNative());
return GetEntryPointFromJniPtrSize(kRuntimePointerSize);
}
- ALWAYS_INLINE void* GetEntryPointFromJniPtrSize(PointerSize pointer_size) {
+ ALWAYS_INLINE void* GetEntryPointFromJniPtrSize(PointerSize pointer_size) const {
return GetDataPtrSize(pointer_size);
}
- void SetEntryPointFromJni(const void* entrypoint) {
+ void SetEntryPointFromJni(const void* entrypoint)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(IsNative());
SetEntryPointFromJniPtrSize(entrypoint, kRuntimePointerSize);
}
- ALWAYS_INLINE void SetEntryPointFromJniPtrSize(const void* entrypoint, PointerSize pointer_size) {
+ ALWAYS_INLINE void SetEntryPointFromJniPtrSize(const void* entrypoint, PointerSize pointer_size)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
SetDataPtrSize(entrypoint, pointer_size);
}
- ALWAYS_INLINE void* GetDataPtrSize(PointerSize pointer_size) {
+ ALWAYS_INLINE void* GetDataPtrSize(PointerSize pointer_size) const {
DCHECK(IsImagePointerSize(pointer_size));
return GetNativePointer<void*>(DataOffset(pointer_size), pointer_size);
}
- ALWAYS_INLINE void SetDataPtrSize(const void* data, PointerSize pointer_size) {
+ ALWAYS_INLINE void SetDataPtrSize(const void* data, PointerSize pointer_size)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(IsImagePointerSize(pointer_size));
SetNativePointer(DataOffset(pointer_size), data, pointer_size);
}
// Is this a CalleSaveMethod or ResolutionMethod and therefore doesn't adhere to normal
// conventions for a method of managed code. Returns false for Proxy methods.
- ALWAYS_INLINE bool IsRuntimeMethod() {
+ ALWAYS_INLINE bool IsRuntimeMethod() const {
return dex_method_index_ == kRuntimeMethodDexMethodIndex;
}
@@ -726,13 +749,14 @@ class ArtMethod final {
// Update entry points by passing them through the visitor.
template <typename Visitor>
- ALWAYS_INLINE void UpdateEntrypoints(const Visitor& visitor, PointerSize pointer_size);
+ ALWAYS_INLINE void UpdateEntrypoints(const Visitor& visitor, PointerSize pointer_size)
+ REQUIRES_SHARED(Locks::mutator_lock_);
// Visit the individual members of an ArtMethod. Used by imgdiag.
// As imgdiag does not support mixing instruction sets or pointer sizes (e.g., using imgdiag32
// to inspect 64-bit images, etc.), we can go beneath the accessors directly to the class members.
template <typename VisitorFunc>
- void VisitMembers(VisitorFunc& visitor) {
+ void VisitMembers(VisitorFunc& visitor) REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(IsImagePointerSize(kRuntimePointerSize));
visitor(this, &declaring_class_, "declaring_class_");
visitor(this, &access_flags_, "access_flags_");
@@ -843,7 +867,8 @@ class ArtMethod final {
}
template<typename T>
- ALWAYS_INLINE void SetNativePointer(MemberOffset offset, T new_value, PointerSize pointer_size) {
+ ALWAYS_INLINE void SetNativePointer(MemberOffset offset, T new_value, PointerSize pointer_size)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
static_assert(std::is_pointer<T>::value, "T must be a pointer type");
const auto addr = reinterpret_cast<uintptr_t>(this) + offset.Uint32Value();
if (pointer_size == PointerSize::k32) {
@@ -864,7 +889,7 @@ class ArtMethod final {
}
// This setter guarantees atomicity.
- void AddAccessFlags(uint32_t flag) {
+ void AddAccessFlags(uint32_t flag) REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(!IsIntrinsic() ||
!OverlapsIntrinsicBits(flag) ||
IsValidIntrinsicUpdate(flag));
@@ -877,7 +902,7 @@ class ArtMethod final {
}
// This setter guarantees atomicity.
- void ClearAccessFlags(uint32_t flag) {
+ void ClearAccessFlags(uint32_t flag) REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(!IsIntrinsic() || !OverlapsIntrinsicBits(flag) || IsValidIntrinsicUpdate(flag));
uint32_t old_access_flags;
uint32_t new_access_flags;
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 9c61386f4e..c87d0f7fd3 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3759,7 +3759,8 @@ void ClassLinker::FixupStaticTrampolines(ObjPtr<mirror::Class> klass) {
// Does anything needed to make sure that the compiler will not generate a direct invoke to this
// method. Should only be called on non-invokable methods.
-inline void EnsureThrowsInvocationError(ClassLinker* class_linker, ArtMethod* method) {
+inline void EnsureThrowsInvocationError(ClassLinker* class_linker, ArtMethod* method)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(method != nullptr);
DCHECK(!method->IsInvokable());
method->SetEntryPointFromQuickCompiledCodePtrSize(
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc
index df7aee8446..4f438e7aec 100644
--- a/runtime/dexopt_test.cc
+++ b/runtime/dexopt_test.cc
@@ -30,6 +30,7 @@
#include "dexopt_test.h"
#include "gc/space/image_space.h"
#include "hidden_api.h"
+#include "oat.h"
namespace art {
void DexoptTest::SetUp() {
@@ -116,24 +117,24 @@ void DexoptTest::GenerateOatForTest(const std::string& dex_location,
ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
EXPECT_EQ(filter, odex_file->GetCompilerFilter());
- std::string boot_image_checksums = gc::space::ImageSpace::GetBootClassPathChecksums(
- ArrayRef<const std::string>(Runtime::Current()->GetBootClassPath()),
- image_location,
- kRuntimeISA,
- gc::space::ImageSpaceLoadingOrder::kSystemFirst,
- &error_msg);
- ASSERT_FALSE(boot_image_checksums.empty()) << error_msg;
-
- const OatHeader& oat_header = odex_file->GetOatHeader();
-
if (CompilerFilter::DependsOnImageChecksum(filter)) {
+ const OatHeader& oat_header = odex_file->GetOatHeader();
+ const char* oat_bcp = oat_header.GetStoreValueByKey(OatHeader::kBootClassPathKey);
+ ASSERT_TRUE(oat_bcp != nullptr);
+ ASSERT_EQ(oat_bcp, android::base::Join(Runtime::Current()->GetBootClassPathLocations(), ':'));
const char* checksums = oat_header.GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey);
ASSERT_TRUE(checksums != nullptr);
- if (with_alternate_image) {
- EXPECT_NE(boot_image_checksums, checksums);
- } else {
- EXPECT_EQ(boot_image_checksums, checksums);
- }
+
+ bool match = gc::space::ImageSpace::VerifyBootClassPathChecksums(
+ checksums,
+ oat_bcp,
+ image_location,
+ ArrayRef<const std::string>(Runtime::Current()->GetBootClassPathLocations()),
+ ArrayRef<const std::string>(Runtime::Current()->GetBootClassPath()),
+ kRuntimeISA,
+ gc::space::ImageSpaceLoadingOrder::kSystemFirst,
+ &error_msg);
+ ASSERT_EQ(!with_alternate_image, match) << error_msg;
}
}
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 34a165f5a9..6a52d24f38 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -48,6 +48,7 @@
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/var_handle.h"
+#include "oat.h"
#include "oat_file.h"
#include "oat_quick_method_header.h"
#include "quick_exception_handler.h"
@@ -2727,6 +2728,10 @@ extern "C" TwoWordReturn artInvokeInterfaceTrampoline(ArtMethod* interface_metho
// We arrive here if we have found an implementation, and it is not in the ImtConflictTable.
// We create a new table with the new pair { interface_method, method }.
DCHECK(conflict_method->IsRuntimeMethod());
+
+ // Classes in the boot image should never need to update conflict methods in
+ // their IMT.
+ CHECK(!Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(cls.Get()));
ArtMethod* new_conflict_method = Runtime::Current()->GetClassLinker()->AddMethodToConflictTable(
cls.Get(),
conflict_method,
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index a1663c8503..53a03fd007 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -2023,12 +2023,12 @@ void ConcurrentCopying::RevokeThreadLocalMarkStacks(bool disable_weak_ref_access
}
void ConcurrentCopying::RevokeThreadLocalMarkStack(Thread* thread) {
- Thread* self = Thread::Current();
- CHECK_EQ(self, thread);
accounting::AtomicStack<mirror::Object>* tl_mark_stack = thread->GetThreadLocalMarkStack();
if (tl_mark_stack != nullptr) {
- CHECK(is_marking_);
- MutexLock mu(self, mark_stack_lock_);
+ // With 2-phase CC change, we cannot assert that is_marking_ will always be true
+ // as we perform thread stack scan even before enabling the read-barrier.
+ CHECK(is_marking_ || (use_generational_cc_ && !young_gen_));
+ MutexLock mu(Thread::Current(), mark_stack_lock_);
revoked_mark_stacks_.push_back(tl_mark_stack);
RemoveThreadMarkStackMapping(thread, tl_mark_stack);
thread->SetThreadLocalMarkStack(nullptr);
diff --git a/runtime/gc/collector/immune_spaces_test.cc b/runtime/gc/collector/immune_spaces_test.cc
index 88f5d4e0d0..b3a14e26d5 100644
--- a/runtime/gc/collector/immune_spaces_test.cc
+++ b/runtime/gc/collector/immune_spaces_test.cc
@@ -126,6 +126,8 @@ class ImmuneSpacesTest : public CommonRuntimeTest {
/*oat_file_end=*/ PointerToLowMemUInt32(oat_map.Begin() + oat_size),
/*boot_image_begin=*/ 0u,
/*boot_image_size=*/ 0u,
+ /*boot_image_component_count=*/ 0u,
+ /*boot_image_checksum=*/ 0u,
/*pointer_size=*/ sizeof(void*));
return new DummyImageSpace(std::move(image_map),
std::move(live_bitmap),
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index 08cbea2172..c1b3a63307 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -47,6 +47,12 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self,
size_t byte_count,
AllocatorType allocator,
const PreFenceVisitor& pre_fence_visitor) {
+ auto no_suspend_pre_fence_visitor =
+ [&pre_fence_visitor](auto... x) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ScopedAssertNoThreadSuspension sants("No thread suspension during pre-fence visitor");
+ pre_fence_visitor(x...);
+ };
+
if (kIsDebugBuild) {
CheckPreconditionsForAllocObject(klass, byte_count);
// Since allocation can cause a GC which will need to SuspendAll, make sure all allocations are
@@ -92,7 +98,7 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self,
}
bytes_allocated = byte_count;
usable_size = bytes_allocated;
- pre_fence_visitor(obj, usable_size);
+ no_suspend_pre_fence_visitor(obj, usable_size);
QuasiAtomic::ThreadFenceForConstructor();
} else if (
!kInstrumented && allocator == kAllocatorTypeRosAlloc &&
@@ -104,7 +110,7 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self,
obj->AssertReadBarrierState();
}
usable_size = bytes_allocated;
- pre_fence_visitor(obj, usable_size);
+ no_suspend_pre_fence_visitor(obj, usable_size);
QuasiAtomic::ThreadFenceForConstructor();
} else {
// Bytes allocated that includes bulk thread-local buffer allocations in addition to direct
@@ -148,7 +154,7 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self,
// case the object is non movable and points to a recently allocated movable class.
WriteBarrier::ForFieldWrite(obj, mirror::Object::ClassOffset(), klass);
}
- pre_fence_visitor(obj, usable_size);
+ no_suspend_pre_fence_visitor(obj, usable_size);
QuasiAtomic::ThreadFenceForConstructor();
if (bytes_tl_bulk_allocated > 0) {
size_t num_bytes_allocated_before =
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 4b63138dd6..85b79da329 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -1205,8 +1205,8 @@ void Heap::ResetGcPerformanceInfo() {
post_gc_last_process_cpu_time_ns_ = process_cpu_start_time_ns_;
post_gc_weighted_allocated_bytes_ = 0u;
- total_bytes_freed_ever_ = 0;
- total_objects_freed_ever_ = 0;
+ total_bytes_freed_ever_.store(0);
+ total_objects_freed_ever_.store(0);
total_wait_time_ = 0;
blocking_gc_count_ = 0;
blocking_gc_time_ = 0;
@@ -1903,7 +1903,21 @@ uint64_t Heap::GetObjectsAllocatedEver() const {
}
uint64_t Heap::GetBytesAllocatedEver() const {
- return GetBytesFreedEver() + GetBytesAllocated();
+ // Force the returned value to be monotonically increasing, in the sense that if this is called
+ // at A and B, such that A happens-before B, then the call at B returns a value no smaller than
+ // that at A. This is not otherwise guaranteed, since num_bytes_allocated_ is decremented first,
+ // and total_bytes_freed_ever_ is incremented later.
+ static std::atomic<uint64_t> max_bytes_so_far(0);
+ uint64_t so_far = max_bytes_so_far.load(std::memory_order_relaxed);
+ uint64_t current_bytes = GetBytesFreedEver(std::memory_order_acquire);
+ current_bytes += GetBytesAllocated();
+ do {
+ if (current_bytes <= so_far) {
+ return so_far;
+ }
+ } while (!max_bytes_so_far.compare_exchange_weak(so_far /* updated */,
+ current_bytes, std::memory_order_relaxed));
+ return current_bytes;
}
// Check whether the given object is an instance of the given class.
@@ -2239,6 +2253,19 @@ void Heap::UnBindBitmaps() {
}
}
+void Heap::IncrementFreedEver() {
+ // Counters are updated only by us, but may be read concurrently.
+ // The updates should become visible after the corresponding live object info.
+ total_objects_freed_ever_.store(total_objects_freed_ever_.load(std::memory_order_relaxed)
+ + GetCurrentGcIteration()->GetFreedObjects()
+ + GetCurrentGcIteration()->GetFreedLargeObjects(),
+ std::memory_order_release);
+ total_bytes_freed_ever_.store(total_bytes_freed_ever_.load(std::memory_order_relaxed)
+ + GetCurrentGcIteration()->GetFreedBytes()
+ + GetCurrentGcIteration()->GetFreedLargeObjectBytes(),
+ std::memory_order_release);
+}
+
void Heap::PreZygoteFork() {
if (!HasZygoteSpace()) {
// We still want to GC in case there is some unreachable non moving objects that could cause a
@@ -2313,10 +2340,7 @@ void Heap::PreZygoteFork() {
if (temp_space_ != nullptr) {
CHECK(temp_space_->IsEmpty());
}
- total_objects_freed_ever_ += GetCurrentGcIteration()->GetFreedObjects() +
- GetCurrentGcIteration()->GetFreedLargeObjects();
- total_bytes_freed_ever_ += GetCurrentGcIteration()->GetFreedBytes() +
- GetCurrentGcIteration()->GetFreedLargeObjectBytes();
+ IncrementFreedEver();
// Update the end and write out image.
non_moving_space_->SetEnd(target_space.End());
non_moving_space_->SetLimit(target_space.Limit());
@@ -2588,10 +2612,7 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type,
<< "Could not find garbage collector with collector_type="
<< static_cast<size_t>(collector_type_) << " and gc_type=" << gc_type;
collector->Run(gc_cause, clear_soft_references || runtime->IsZygote());
- total_objects_freed_ever_ += GetCurrentGcIteration()->GetFreedObjects() +
- GetCurrentGcIteration()->GetFreedLargeObjects();
- total_bytes_freed_ever_ += GetCurrentGcIteration()->GetFreedBytes() +
- GetCurrentGcIteration()->GetFreedLargeObjectBytes();
+ IncrementFreedEver();
RequestTrim(self);
// Collect cleared references.
SelfDeletingTask* clear = reference_processor_->CollectClearedReferences(self);
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 6c3290f8ee..9ef6af5c97 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -553,13 +553,15 @@ class Heap {
uint64_t GetBytesAllocatedEver() const;
// Returns the total number of objects freed since the heap was created.
- uint64_t GetObjectsFreedEver() const {
- return total_objects_freed_ever_;
+ // With default memory order, this should be viewed only as a hint.
+ uint64_t GetObjectsFreedEver(std::memory_order mo = std::memory_order_relaxed) const {
+ return total_objects_freed_ever_.load(mo);
}
// Returns the total number of bytes freed since the heap was created.
- uint64_t GetBytesFreedEver() const {
- return total_bytes_freed_ever_;
+ // With default memory order, this should be viewed only as a hint.
+ uint64_t GetBytesFreedEver(std::memory_order mo = std::memory_order_relaxed) const {
+ return total_bytes_freed_ever_.load(mo);
}
space::RegionSpace* GetRegionSpace() const {
@@ -1189,6 +1191,9 @@ class Heap {
ALWAYS_INLINE void IncrementNumberOfBytesFreedRevoke(size_t freed_bytes_revoke);
+ // Update *_freed_ever_ counters to reflect current GC values.
+ void IncrementFreedEver();
+
// Remove a vlog code from heap-inl.h which is transitively included in half the world.
static void VlogHeapGrowth(size_t max_allowed_footprint, size_t new_footprint, size_t alloc_size);
@@ -1342,10 +1347,10 @@ class Heap {
size_t concurrent_start_bytes_;
// Since the heap was created, how many bytes have been freed.
- uint64_t total_bytes_freed_ever_;
+ std::atomic<uint64_t> total_bytes_freed_ever_;
// Since the heap was created, how many objects have been freed.
- uint64_t total_objects_freed_ever_;
+ std::atomic<uint64_t> total_objects_freed_ever_;
// Number of bytes currently allocated and not yet reclaimed. Includes active
// TLABS in their entirety, even if they have not yet been parceled out.
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 63fc90c2dd..9ff799c650 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -37,6 +37,7 @@
#include "base/os.h"
#include "base/scoped_flock.h"
#include "base/stl_util.h"
+#include "base/string_view_cpp20.h"
#include "base/systrace.h"
#include "base/time_utils.h"
#include "base/utils.h"
@@ -53,6 +54,7 @@
#include "mirror/executable-inl.h"
#include "mirror/object-inl.h"
#include "mirror/object-refvisitor-inl.h"
+#include "oat.h"
#include "oat_file.h"
#include "runtime.h"
#include "space-inl.h"
@@ -64,6 +66,10 @@ namespace space {
using android::base::StringAppendF;
using android::base::StringPrintf;
+// We do not allow the boot image and extensions to take more than 1GiB. They are
+// supposed to be much smaller and allocating more that this would likely fail anyway.
+static constexpr size_t kMaxTotalImageReservationSize = 1 * GB;
+
Atomic<uint32_t> ImageSpace::bitmap_index_(0);
ImageSpace::ImageSpace(const std::string& image_filename,
@@ -813,6 +819,10 @@ class ImageSpace::Loader {
image_filename);
return nullptr;
}
+ if (!ValidateBootImageChecksum(image_filename, *image_header, oat_file, error_msg)) {
+ DCHECK(!error_msg->empty());
+ return nullptr;
+ }
}
if (VLOG_IS_ON(startup)) {
@@ -936,6 +946,72 @@ class ImageSpace::Loader {
}
private:
+ static bool ValidateBootImageChecksum(const char* image_filename,
+ const ImageHeader& image_header,
+ const OatFile* oat_file,
+ /*out*/std::string* error_msg) {
+ // Use the boot image component count to calculate the checksum from
+ // the appropriate number of boot image chunks.
+ const std::vector<ImageSpace*>& image_spaces =
+ Runtime::Current()->GetHeap()->GetBootImageSpaces();
+ uint32_t boot_image_component_count = image_header.GetBootImageComponentCount();
+ size_t image_spaces_size = image_spaces.size();
+ if (boot_image_component_count > image_spaces_size) {
+ *error_msg = StringPrintf("Too many boot image dependencies (%u > %zu) in image %s",
+ boot_image_component_count,
+ image_spaces_size,
+ image_filename);
+ return false;
+ }
+ uint32_t checksum = 0u;
+ size_t chunk_count = 0u;
+ for (size_t component_count = 0u; component_count != boot_image_component_count; ) {
+ const ImageHeader& current_header = image_spaces[component_count]->GetImageHeader();
+ if (current_header.GetComponentCount() > boot_image_component_count - component_count) {
+ *error_msg = StringPrintf("Boot image component count in %s ends in the middle of a chunk, "
+ "%u is between %zu and %zu",
+ image_filename,
+ boot_image_component_count,
+ component_count,
+ component_count + current_header.GetComponentCount());
+ return false;
+ }
+ component_count += current_header.GetComponentCount();
+ checksum ^= current_header.GetImageChecksum();
+ chunk_count += 1u;
+ }
+ if (image_header.GetBootImageChecksum() != checksum) {
+ *error_msg = StringPrintf("Boot image checksum mismatch (0x%x != 0x%x) in image %s",
+ image_header.GetBootImageChecksum(),
+ checksum,
+ image_filename);
+ return false;
+ }
+ // Oat checksums, if present, have already been validated, so we know that
+ // they match the loaded image spaces. Therefore, we just verify that they
+ // are consistent in the number of boot image chunks they list by looking
+ // for the kImageChecksumPrefix at the start of each component.
+ const char* oat_boot_class_path_checksums =
+ oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey);
+ if (oat_boot_class_path_checksums != nullptr) {
+ size_t oat_bcp_chunk_count = 0u;
+ while (*oat_boot_class_path_checksums == kImageChecksumPrefix) {
+ oat_bcp_chunk_count += 1u;
+ // Find the start of the next component if any.
+ const char* separator = strchr(oat_boot_class_path_checksums, ':');
+ oat_boot_class_path_checksums = (separator != nullptr) ? separator + 1u : "";
+ }
+ if (oat_bcp_chunk_count != chunk_count) {
+ *error_msg = StringPrintf("Boot image chunk count mismatch (%zu != %zu) in image %s",
+ oat_bcp_chunk_count,
+ chunk_count,
+ image_filename);
+ return false;
+ }
+ }
+ return true;
+ }
+
static MemMap LoadImageFile(const char* image_filename,
const char* image_location,
const ImageHeader& image_header,
@@ -1036,9 +1112,9 @@ class ImageSpace::Loader {
template <typename Range0, typename Range1 = EmptyRange, typename Range2 = EmptyRange>
class ForwardAddress {
public:
- ForwardAddress(const Range0& range0 = Range0(),
- const Range1& range1 = Range1(),
- const Range2& range2 = Range2())
+ explicit ForwardAddress(const Range0& range0 = Range0(),
+ const Range1& range1 = Range1(),
+ const Range2& range2 = Range2())
: range0_(range0), range1_(range1), range2_(range2) {}
// Return the relocated address of a heap object.
@@ -1370,6 +1446,592 @@ class ImageSpace::Loader {
}
};
+static void AppendImageChecksum(uint32_t component_count,
+ uint32_t checksum,
+ /*inout*/std::string* checksums) {
+ static_assert(ImageSpace::kImageChecksumPrefix == 'i', "Format prefix check.");
+ StringAppendF(checksums, "i;%u/%08x", component_count, checksum);
+}
+
+static bool CheckAndRemoveImageChecksum(uint32_t component_count,
+ uint32_t checksum,
+ /*inout*/std::string_view* oat_checksums,
+ /*out*/std::string* error_msg) {
+ std::string image_checksum;
+ AppendImageChecksum(component_count, checksum, &image_checksum);
+ if (!StartsWith(*oat_checksums, image_checksum)) {
+ *error_msg = StringPrintf("Image checksum mismatch, expected %s to start with %s",
+ std::string(*oat_checksums).c_str(),
+ image_checksum.c_str());
+ return false;
+ }
+ oat_checksums->remove_prefix(image_checksum.size());
+ return true;
+}
+
+// Helper class to find the primary boot image and boot image extensions
+// and determine the boot image layout.
+class ImageSpace::BootImageLayout {
+ public:
+ // Description of a "chunk" of the boot image, i.e. either primary boot image
+ // or a boot image extension, used in conjunction with the boot class path to
+ // load boot image components.
+ struct ImageChunk {
+ std::string base_location;
+ std::string base_filename;
+ size_t start_index;
+ size_t component_count;
+ size_t reservation_size;
+ uint32_t checksum;
+ uint32_t boot_image_component_count;
+ uint32_t boot_image_checksum;
+ };
+
+ BootImageLayout(const std::string& image_location, ArrayRef<const std::string> boot_class_path)
+ : image_location_(image_location),
+ boot_class_path_(boot_class_path) {}
+
+ std::string GetPrimaryImageLocation();
+
+ bool LoadFromSystem(InstructionSet image_isa, /*out*/std::string* error_msg) {
+ return LoadOrValidateFromSystem(image_isa, /*oat_checksums=*/ nullptr, error_msg);
+ }
+
+ bool ValidateFromSystem(InstructionSet image_isa,
+ /*inout*/std::string_view* oat_checksums,
+ /*out*/std::string* error_msg) {
+ DCHECK(oat_checksums != nullptr);
+ return LoadOrValidateFromSystem(image_isa, oat_checksums, error_msg);
+ }
+
+ bool LoadFromDalvikCache(const std::string& dalvik_cache, /*out*/std::string* error_msg) {
+ return LoadOrValidateFromDalvikCache(dalvik_cache, /*oat_checksums=*/ nullptr, error_msg);
+ }
+
+ bool ValidateFromDalvikCache(const std::string& dalvik_cache,
+ /*inout*/std::string_view* oat_checksums,
+ /*out*/std::string* error_msg) {
+ DCHECK(oat_checksums != nullptr);
+ return LoadOrValidateFromDalvikCache(dalvik_cache, oat_checksums, error_msg);
+ }
+
+ ArrayRef<const ImageChunk> GetChunks() const {
+ return ArrayRef<const ImageChunk>(chunks_);
+ }
+
+ uint32_t GetBaseAddress() const {
+ return base_address_;
+ }
+
+ size_t GetNextBcpIndex() const {
+ return next_bcp_index_;
+ }
+
+ size_t GetTotalComponentCount() const {
+ return total_component_count_;
+ }
+
+ size_t GetTotalReservationSize() const {
+ return total_reservation_size_;
+ }
+
+ private:
+ std::string ExpandLocationImpl(const std::string& location,
+ size_t bcp_index,
+ bool boot_image_extension) {
+ std::vector<std::string> expanded = ExpandMultiImageLocations(
+ ArrayRef<const std::string>(boot_class_path_).SubArray(bcp_index, 1u),
+ location,
+ boot_image_extension);
+ DCHECK_EQ(expanded.size(), 1u);
+ return expanded[0];
+ }
+
+ std::string ExpandLocation(const std::string& location, size_t bcp_index) {
+ if (bcp_index == 0u) {
+ DCHECK_EQ(location, ExpandLocationImpl(location, bcp_index, /*boot_image_extension=*/ false));
+ return location;
+ } else {
+ return ExpandLocationImpl(location, bcp_index, /*boot_image_extension=*/ true);
+ }
+ }
+
+ bool VerifyImageLocation(const std::vector<std::string>& components,
+ /*out*/size_t* named_components_count,
+ /*out*/std::string* error_msg);
+
+ bool MatchNamedComponents(
+ ArrayRef<const std::string> named_components,
+ /*out*/std::vector<std::pair<std::string, size_t>>* base_locations_and_bcp_indexes,
+ /*out*/std::string* error_msg);
+
+ bool ValidateBootImageChecksum(const std::string& actual_filename,
+ const ImageHeader& header,
+ /*out*/std::string* error_msg);
+
+ bool ReadHeader(const std::string& base_location,
+ const std::string& base_filename,
+ size_t bcp_index,
+ size_t bcp_component_count,
+ /*out*/std::string* error_msg);
+
+ bool CheckAndRemoveLastChunkChecksum(/*inout*/std::string_view* oat_checksums,
+ /*out*/std::string* error_msg);
+
+ template <typename FilenameFn>
+ bool LoadOrValidate(FilenameFn&& filename_fn,
+ /*inout*/std::string_view* oat_checksums,
+ /*out*/std::string* error_msg);
+
+ bool LoadOrValidateFromSystem(InstructionSet image_isa,
+ /*inout*/std::string_view* oat_checksums,
+ /*out*/std::string* error_msg);
+
+ bool LoadOrValidateFromDalvikCache(const std::string& dalvik_cache,
+ /*inout*/std::string_view* oat_checksums,
+ /*out*/std::string* error_msg);
+
+ const std::string& image_location_;
+ ArrayRef<const std::string> boot_class_path_;
+
+ std::vector<ImageChunk> chunks_;
+ uint32_t base_address_ = 0u;
+ size_t next_bcp_index_ = 0u;
+ size_t total_component_count_ = 0u;
+ size_t total_reservation_size_ = 0u;
+};
+
+std::string ImageSpace::BootImageLayout::GetPrimaryImageLocation() {
+ size_t location_start = 0u;
+ size_t location_end = image_location_.find(':');
+ while (location_end == location_start) {
+ ++location_start;
+ location_end = image_location_.find(location_start, ':');
+ }
+ std::string location = (location_end == std::string::npos)
+ ? image_location_.substr(location_start)
+ : image_location_.substr(location_start, location_end - location_start);
+ if (image_location_.find('/') == std::string::npos) {
+ // No path, so use the path from the first boot class path component.
+ size_t slash_pos = boot_class_path_.empty()
+ ? std::string::npos
+ : boot_class_path_[0].rfind('/');
+ if (slash_pos == std::string::npos) {
+ return std::string();
+ }
+ location.insert(0u, boot_class_path_[0].substr(0u, slash_pos + 1u));
+ }
+ return location;
+}
+
+bool ImageSpace::BootImageLayout::VerifyImageLocation(
+ const std::vector<std::string>& components,
+ /*out*/size_t* named_components_count,
+ /*out*/std::string* error_msg) {
+ DCHECK(named_components_count != nullptr);
+
+ // Validate boot class path. Require a path and non-empty name in each component.
+ for (const std::string& bcp_component : boot_class_path_) {
+ size_t bcp_slash_pos = bcp_component.rfind('/');
+ if (bcp_slash_pos == std::string::npos || bcp_slash_pos == bcp_component.size() - 1u) {
+ *error_msg = StringPrintf("Invalid boot class path component: %s", bcp_component.c_str());
+ return false;
+ }
+ }
+
+ // Validate the format of image location components.
+ size_t components_size = components.size();
+ if (components_size == 0u) {
+ *error_msg = "Empty image location.";
+ return false;
+ }
+ size_t wildcards_start = components_size; // No wildcards.
+ for (size_t i = 0; i != components_size; ++i) {
+ const std::string& component = components[i];
+ DCHECK(!component.empty()); // Guaranteed by Split().
+ size_t wildcard_pos = component.find('*');
+ if (wildcard_pos == std::string::npos) {
+ if (wildcards_start != components.size()) {
+ *error_msg =
+ StringPrintf("Image component without wildcard after component with wildcard: %s",
+ component.c_str());
+ return false;
+ }
+ if (component.back() == '/') {
+ *error_msg = StringPrintf("Image component ends with path separator: %s",
+ component.c_str());
+ return false;
+ }
+ } else {
+ if (wildcards_start == components_size) {
+ wildcards_start = i;
+ }
+ // Wildcard must be the last character.
+ if (wildcard_pos != component.size() - 1u) {
+ *error_msg = StringPrintf("Unsupported wildcard (*) position in %s", component.c_str());
+ return false;
+ }
+ // And it must be either plain wildcard or preceded by a path separator.
+ if (component.size() != 1u && component[wildcard_pos - 1u] != '/') {
+ *error_msg = StringPrintf("Non-plain wildcard (*) not preceded by path separator '/': %s",
+ component.c_str());
+ return false;
+ }
+ if (i == 0) {
+ *error_msg = StringPrintf("Primary component contains wildcard (*): %s", component.c_str());
+ return false;
+ }
+ }
+ }
+
+ *named_components_count = wildcards_start;
+ return true;
+}
+
+bool ImageSpace::BootImageLayout::MatchNamedComponents(
+ ArrayRef<const std::string> named_components,
+ /*out*/std::vector<std::pair<std::string, size_t>>* base_locations_and_bcp_indexes,
+ /*out*/std::string* error_msg) {
+ DCHECK(!named_components.empty());
+ DCHECK(base_locations_and_bcp_indexes->empty());
+ base_locations_and_bcp_indexes->reserve(named_components.size());
+ size_t bcp_component_count = boot_class_path_.size();
+ size_t bcp_pos = 0;
+ std::string base_name;
+ for (size_t i = 0, size = named_components.size(); i != size; ++i) {
+ const std::string& component = named_components[i];
+ size_t slash_pos = component.rfind('/');
+ std::string base_location;
+ if (i == 0u) {
+ // The primary boot image name is taken as provided. It forms the base
+ // for expanding the extension filenames.
+ if (slash_pos != std::string::npos) {
+ base_name = component.substr(slash_pos + 1u);
+ base_location = component;
+ } else {
+ base_name = component;
+ size_t bcp_slash_pos = boot_class_path_[0u].rfind('/');
+ DCHECK_NE(bcp_slash_pos, std::string::npos);
+ base_location = boot_class_path_[0u].substr(0u, bcp_slash_pos + 1u) + component;
+ }
+ } else {
+ std::string to_match;
+ if (slash_pos != std::string::npos) {
+ // If we have the full path, we just need to match the filename to the BCP component.
+ base_location = component.substr(0u, slash_pos + 1u) + base_name;
+ to_match = component;
+ }
+ while (true) {
+ if (slash_pos == std::string::npos) {
+ // If we do not have a full path, we need to update the path based on the BCP location.
+ size_t bcp_slash_pos = boot_class_path_[bcp_pos].rfind('/');
+ DCHECK_NE(bcp_slash_pos, std::string::npos);
+ std::string path = boot_class_path_[bcp_pos].substr(0u, bcp_slash_pos + 1u);
+ to_match = path + component;
+ base_location = path + base_name;
+ }
+ if (ExpandLocation(base_location, bcp_pos) == to_match) {
+ break;
+ }
+ ++bcp_pos;
+ if (bcp_pos == bcp_component_count) {
+ *error_msg = StringPrintf("Image component %s does not match a boot class path component",
+ component.c_str());
+ return false;
+ }
+ }
+ }
+ base_locations_and_bcp_indexes->emplace_back(base_location, bcp_pos);
+ ++bcp_pos;
+ }
+ return true;
+}
+
+bool ImageSpace::BootImageLayout::ValidateBootImageChecksum(const std::string& actual_filename,
+ const ImageHeader& header,
+ /*out*/std::string* error_msg) {
+ uint32_t boot_image_component_count = header.GetBootImageComponentCount();
+ if (chunks_.empty() != (boot_image_component_count == 0u)) {
+ *error_msg = StringPrintf("Unexpected boot image component count in %s: %u, %s",
+ actual_filename.c_str(),
+ header.GetImageReservationSize(),
+ chunks_.empty() ? "should be 0" : "should not be 0");
+ return false;
+ }
+ uint32_t component_count = 0u;
+ uint32_t composite_checksum = 0u;
+ for (const ImageChunk& chunk : chunks_) {
+ if (component_count == boot_image_component_count) {
+ break; // Hit the component count.
+ }
+ if (chunk.start_index != component_count) {
+ break; // End of contiguous chunks, fail below; same as reaching end of `chunks_`.
+ }
+ if (chunk.component_count > boot_image_component_count - component_count) {
+ *error_msg = StringPrintf("Boot image component count in %s ends in the middle of a chunk, "
+ "%u is between %u and %zu",
+ actual_filename.c_str(),
+ boot_image_component_count,
+ component_count,
+ component_count + chunk.component_count);
+ return false;
+ }
+ component_count += chunk.component_count;
+ composite_checksum ^= chunk.checksum;
+ }
+ DCHECK_LE(component_count, boot_image_component_count);
+ if (component_count != boot_image_component_count) {
+ *error_msg = StringPrintf("Missing boot image components for checksum in %s: %u > %u",
+ actual_filename.c_str(),
+ boot_image_component_count,
+ component_count);
+ return false;
+ }
+ if (composite_checksum != header.GetBootImageChecksum()) {
+ *error_msg = StringPrintf("Boot image checksum mismatch in %s: 0x%08x != 0x%08x",
+ actual_filename.c_str(),
+ header.GetBootImageChecksum(),
+ composite_checksum);
+ return false;
+ }
+ return true;
+}
+
+bool ImageSpace::BootImageLayout::ReadHeader(const std::string& base_location,
+ const std::string& base_filename,
+ size_t bcp_index,
+ size_t bcp_component_count,
+ /*out*/std::string* error_msg) {
+ DCHECK_LE(next_bcp_index_, bcp_index);
+ DCHECK_LT(bcp_index, bcp_component_count);
+ size_t allowed_component_count = bcp_component_count - bcp_index;
+ DCHECK_LE(total_reservation_size_, kMaxTotalImageReservationSize);
+ size_t allowed_reservation_size = kMaxTotalImageReservationSize - total_reservation_size_;
+
+ std::string actual_filename = ExpandLocation(base_filename, bcp_index);
+ ImageHeader header;
+ if (!ReadSpecificImageHeader(actual_filename.c_str(), &header, error_msg)) {
+ return false;
+ }
+ if (header.GetComponentCount() == 0u ||
+ header.GetComponentCount() > allowed_component_count) {
+ *error_msg = StringPrintf("Unexpected component count in %s, received %u, "
+ "expected non-zero and <= %zu",
+ actual_filename.c_str(),
+ header.GetComponentCount(),
+ allowed_component_count);
+ return false;
+ }
+ if (header.GetImageReservationSize() > allowed_reservation_size) {
+ *error_msg = StringPrintf("Reservation size too big in %s: %u > %zu",
+ actual_filename.c_str(),
+ header.GetImageReservationSize(),
+ allowed_reservation_size);
+ return false;
+ }
+ if (!ValidateBootImageChecksum(actual_filename, header, error_msg)) {
+ return false;
+ }
+
+ if (chunks_.empty()) {
+ base_address_ = reinterpret_cast32<uint32_t>(header.GetImageBegin());
+ }
+ ImageChunk chunk;
+ chunk.base_location = base_location;
+ chunk.base_filename = base_filename;
+ chunk.start_index = bcp_index;
+ chunk.component_count = header.GetComponentCount();
+ chunk.reservation_size = header.GetImageReservationSize();
+ chunk.checksum = header.GetImageChecksum();
+ chunk.boot_image_component_count = header.GetBootImageComponentCount();
+ chunk.boot_image_checksum = header.GetBootImageChecksum();
+ chunks_.push_back(std::move(chunk));
+ next_bcp_index_ = bcp_index + header.GetComponentCount();
+ total_component_count_ += header.GetComponentCount();
+ total_reservation_size_ += header.GetImageReservationSize();
+ return true;
+}
+
+bool ImageSpace::BootImageLayout::CheckAndRemoveLastChunkChecksum(
+ /*inout*/std::string_view* oat_checksums,
+ /*out*/std::string* error_msg) {
+ DCHECK(oat_checksums != nullptr);
+ DCHECK(!chunks_.empty());
+ const ImageChunk& chunk = chunks_.back();
+ size_t component_count = chunk.component_count;
+ size_t checksum = chunk.checksum;
+ if (!CheckAndRemoveImageChecksum(component_count, checksum, oat_checksums, error_msg)) {
+ DCHECK(!error_msg->empty());
+ return false;
+ }
+ if (oat_checksums->empty()) {
+ if (next_bcp_index_ != boot_class_path_.size()) {
+ *error_msg = StringPrintf("Checksum too short, missing %zu components.",
+ boot_class_path_.size() - next_bcp_index_);
+ return false;
+ }
+ return true;
+ }
+ if (!StartsWith(*oat_checksums, ":")) {
+ *error_msg = StringPrintf("Missing ':' separator at start of %s",
+ std::string(*oat_checksums).c_str());
+ return false;
+ }
+ oat_checksums->remove_prefix(1u);
+ if (oat_checksums->empty()) {
+ *error_msg = "Missing checksums after the ':' separator.";
+ return false;
+ }
+ return true;
+}
+
+template <typename FilenameFn>
+bool ImageSpace::BootImageLayout::LoadOrValidate(FilenameFn&& filename_fn,
+ /*inout*/std::string_view* oat_checksums,
+ /*out*/std::string* error_msg) {
+ DCHECK(GetChunks().empty());
+ DCHECK_EQ(GetBaseAddress(), 0u);
+ bool validate = (oat_checksums != nullptr);
+ static_assert(ImageSpace::kImageChecksumPrefix == 'i', "Format prefix check.");
+ DCHECK(!validate || StartsWith(*oat_checksums, "i"));
+
+ std::vector<std::string> components;
+ Split(image_location_, ':', &components);
+ size_t named_components_count = 0u;
+ if (!VerifyImageLocation(components, &named_components_count, error_msg)) {
+ return false;
+ }
+
+ ArrayRef<const std::string> named_components =
+ ArrayRef<const std::string>(components).SubArray(/*pos=*/ 0u, named_components_count);
+
+ std::vector<std::pair<std::string, size_t>> base_locations_and_bcp_indexes;
+ if (!MatchNamedComponents(named_components, &base_locations_and_bcp_indexes, error_msg)) {
+ return false;
+ }
+
+ // Load the image headers of named components.
+ DCHECK_EQ(base_locations_and_bcp_indexes.size(), named_components.size());
+ const size_t bcp_component_count = boot_class_path_.size();
+ size_t bcp_pos = 0u;
+ for (size_t i = 0, size = named_components.size(); i != size; ++i) {
+ const std::string& base_location = base_locations_and_bcp_indexes[i].first;
+ size_t bcp_index = base_locations_and_bcp_indexes[i].second;
+ if (bcp_index < bcp_pos) {
+ DCHECK_NE(i, 0u);
+ LOG(ERROR) << "Named image component already covered by previous image: " << base_location;
+ continue;
+ }
+ if (validate && bcp_index > bcp_pos) {
+ *error_msg = StringPrintf("End of contiguous boot class path images, remaining checksum: %s",
+ std::string(*oat_checksums).c_str());
+ return false;
+ }
+ std::string local_error_msg;
+ std::string* err_msg = (i == 0 || validate) ? error_msg : &local_error_msg;
+ std::string base_filename;
+ if (!filename_fn(base_location, &base_filename, err_msg) ||
+ !ReadHeader(base_location, base_filename, bcp_index, bcp_component_count, err_msg)) {
+ if (i == 0u || validate) {
+ return false;
+ }
+ VLOG(image) << "Error reading named image component header for " << base_location
+ << ", error: " << local_error_msg;
+ bcp_pos = bcp_index + 1u; // Skip at least this component.
+ DCHECK_GT(bcp_pos, GetNextBcpIndex());
+ continue;
+ }
+ if (validate) {
+ if (!CheckAndRemoveLastChunkChecksum(oat_checksums, error_msg)) {
+ return false;
+ }
+ if (oat_checksums->empty() || !StartsWith(*oat_checksums, "i")) {
+ return true; // Let the caller deal with the dex file checksums if any.
+ }
+ }
+ bcp_pos = GetNextBcpIndex();
+ }
+
+ // Look for remaining components if there are any wildcard specifications.
+ ArrayRef<const std::string> search_paths =
+ ArrayRef<const std::string>(components).SubArray(/*pos=*/ named_components_count);
+ if (!search_paths.empty()) {
+ const std::string& primary_base_location = base_locations_and_bcp_indexes[0].first;
+ size_t base_slash_pos = primary_base_location.rfind('/');
+ DCHECK_NE(base_slash_pos, std::string::npos);
+ std::string base_name = primary_base_location.substr(base_slash_pos + 1u);
+ DCHECK(!base_name.empty());
+ while (bcp_pos != bcp_component_count) {
+ const std::string& bcp_component = boot_class_path_[bcp_pos];
+ bool found = false;
+ for (const std::string& path : search_paths) {
+ std::string base_location;
+ if (path.size() == 1u) {
+ DCHECK_EQ(path, "*");
+ size_t slash_pos = bcp_component.rfind('/');
+ DCHECK_NE(slash_pos, std::string::npos);
+ base_location = bcp_component.substr(0u, slash_pos + 1u) + base_name;
+ } else {
+ DCHECK(EndsWith(path, "/*"));
+ base_location = path.substr(0u, path.size() - 1u) + base_name;
+ }
+ std::string err_msg; // Ignored.
+ std::string base_filename;
+ if (filename_fn(base_location, &base_filename, &err_msg) &&
+ ReadHeader(base_location, base_filename, bcp_pos, bcp_component_count, &err_msg)) {
+ VLOG(image) << "Found image extension for " << ExpandLocation(base_location, bcp_pos);
+ bcp_pos = GetNextBcpIndex();
+ found = true;
+ if (validate) {
+ if (!CheckAndRemoveLastChunkChecksum(oat_checksums, error_msg)) {
+ return false;
+ }
+ if (oat_checksums->empty() || !StartsWith(*oat_checksums, "i")) {
+ return true; // Let the caller deal with the dex file checksums if any.
+ }
+ }
+ break;
+ }
+ }
+ if (!found) {
+ if (validate) {
+ *error_msg = StringPrintf("Missing extension for %s, remaining checksum: %s",
+ bcp_component.c_str(),
+ std::string(*oat_checksums).c_str());
+ return false;
+ }
+ ++bcp_pos;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool ImageSpace::BootImageLayout::LoadOrValidateFromSystem(InstructionSet image_isa,
+ /*inout*/std::string_view* oat_checksums,
+ /*out*/std::string* error_msg) {
+ auto filename_fn = [image_isa](const std::string& location,
+ /*out*/std::string* filename,
+ /*out*/std::string* err_msg ATTRIBUTE_UNUSED) {
+ *filename = GetSystemImageFilename(location.c_str(), image_isa);
+ return true;
+ };
+ return LoadOrValidate(filename_fn, oat_checksums, error_msg);
+}
+
+bool ImageSpace::BootImageLayout::LoadOrValidateFromDalvikCache(
+ const std::string& dalvik_cache,
+ /*inout*/std::string_view* oat_checksums,
+ /*out*/std::string* error_msg) {
+ auto filename_fn = [&dalvik_cache](const std::string& location,
+ /*out*/std::string* filename,
+ /*out*/std::string* err_msg) {
+ return GetDalvikCacheFilename(location.c_str(), dalvik_cache.c_str(), filename, err_msg);
+ };
+ return LoadOrValidate(filename_fn, oat_checksums, error_msg);
+}
+
class ImageSpace::BootImageLoader {
public:
BootImageLoader(const std::vector<std::string>& boot_class_path,
@@ -1397,8 +2059,10 @@ class ImageSpace::BootImageLoader {
bool IsZygote() const { return is_zygote_; }
void FindImageFiles() {
+ BootImageLayout layout(image_location_, boot_class_path_);
+ std::string image_location = layout.GetPrimaryImageLocation();
std::string system_filename;
- bool found_image = FindImageFilenameImpl(image_location_.c_str(),
+ bool found_image = FindImageFilenameImpl(image_location.c_str(),
image_isa_,
&has_system_,
&system_filename,
@@ -1427,126 +2091,115 @@ class ImageSpace::BootImageLoader {
bool LoadFromSystem(bool validate_oat_file,
size_t extra_reservation_size,
- /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
+ /*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
/*out*/MemMap* extra_reservation,
- /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
- TimingLogger logger(__PRETTY_FUNCTION__, /*precise=*/ true, VLOG_IS_ON(image));
- std::string filename = GetSystemImageFilename(image_location_.c_str(), image_isa_);
-
- if (!LoadFromFile(filename,
- validate_oat_file,
- extra_reservation_size,
- &logger,
- boot_image_spaces,
- extra_reservation,
- error_msg)) {
- return false;
- }
-
- if (VLOG_IS_ON(image)) {
- LOG(INFO) << "ImageSpace::BootImageLoader::LoadFromSystem exiting "
- << boot_image_spaces->front();
- logger.Dump(LOG_STREAM(INFO));
- }
- return true;
- }
+ /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_);
bool LoadFromDalvikCache(
bool validate_oat_file,
size_t extra_reservation_size,
- /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
+ /*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
/*out*/MemMap* extra_reservation,
- /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
- TimingLogger logger(__PRETTY_FUNCTION__, /*precise=*/ true, VLOG_IS_ON(image));
- DCHECK(DalvikCacheExists());
-
- if (!LoadFromFile(cache_filename_,
- validate_oat_file,
- extra_reservation_size,
- &logger,
- boot_image_spaces,
- extra_reservation,
- error_msg)) {
- return false;
- }
-
- if (VLOG_IS_ON(image)) {
- LOG(INFO) << "ImageSpace::BootImageLoader::LoadFromDalvikCache exiting "
- << boot_image_spaces->front();
- logger.Dump(LOG_STREAM(INFO));
- }
- return true;
- }
+ /*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_);
private:
- bool LoadFromFile(
- const std::string& filename,
+ bool LoadImage(
+ const BootImageLayout& layout,
bool validate_oat_file,
size_t extra_reservation_size,
TimingLogger* logger,
- /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
+ /*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
/*out*/MemMap* extra_reservation,
/*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
- ImageHeader system_hdr;
- if (!ReadSpecificImageHeader(filename.c_str(), &system_hdr, error_msg)) {
+ ArrayRef<const BootImageLayout::ImageChunk> chunks = layout.GetChunks();
+ DCHECK(!chunks.empty());
+ const uint32_t base_address = layout.GetBaseAddress();
+ const size_t image_component_count = layout.GetTotalComponentCount();
+ const size_t image_reservation_size = layout.GetTotalReservationSize();
+
+ DCHECK_LE(image_reservation_size, kMaxTotalImageReservationSize);
+ static_assert(kMaxTotalImageReservationSize < std::numeric_limits<uint32_t>::max());
+ if (extra_reservation_size > std::numeric_limits<uint32_t>::max() - image_reservation_size) {
+ // Since the `image_reservation_size` is limited to kMaxTotalImageReservationSize,
+ // the `extra_reservation_size` would have to be really excessive to fail this check.
+ *error_msg = StringPrintf("Excessive extra reservation size: %zu", extra_reservation_size);
return false;
}
- if (system_hdr.GetComponentCount() == 0u ||
- system_hdr.GetComponentCount() > boot_class_path_.size()) {
- *error_msg = StringPrintf("Unexpected component count in %s, received %u, "
- "expected non-zero and <= %zu",
- filename.c_str(),
- system_hdr.GetComponentCount(),
- boot_class_path_.size());
- return false;
- }
- MemMap image_reservation;
- MemMap local_extra_reservation;
- if (!ReserveBootImageMemory(system_hdr.GetImageReservationSize(),
- reinterpret_cast32<uint32_t>(system_hdr.GetImageBegin()),
- extra_reservation_size,
- &image_reservation,
- &local_extra_reservation,
- error_msg)) {
+
+ // Reserve address space. If relocating, choose a random address for ALSR.
+ uint8_t* addr = reinterpret_cast<uint8_t*>(
+ relocate_ ? ART_BASE_ADDRESS + ChooseRelocationOffsetDelta() : base_address);
+ MemMap image_reservation =
+ ReserveBootImageMemory(addr, image_reservation_size + extra_reservation_size, error_msg);
+ if (!image_reservation.IsValid()) {
return false;
}
- ArrayRef<const std::string> provided_locations(boot_class_path_locations_.data(),
- system_hdr.GetComponentCount());
- std::vector<std::string> locations =
- ExpandMultiImageLocations(provided_locations, image_location_);
- std::vector<std::string> filenames =
- ExpandMultiImageLocations(provided_locations, filename);
- DCHECK_EQ(locations.size(), filenames.size());
+ // Load components.
std::vector<std::unique_ptr<ImageSpace>> spaces;
- spaces.reserve(locations.size());
- for (std::size_t i = 0u, size = locations.size(); i != size; ++i) {
- spaces.push_back(Load(locations[i], filenames[i], logger, &image_reservation, error_msg));
- const ImageSpace* space = spaces.back().get();
- if (space == nullptr) {
- return false;
- }
- uint32_t expected_component_count = (i == 0u) ? system_hdr.GetComponentCount() : 0u;
- uint32_t expected_reservation_size = (i == 0u) ? system_hdr.GetImageReservationSize() : 0u;
- if (!Loader::CheckImageReservationSize(*space, expected_reservation_size, error_msg) ||
- !Loader::CheckImageComponentCount(*space, expected_component_count, error_msg)) {
- return false;
+ spaces.reserve(image_component_count);
+ size_t max_image_space_dependencies = 0u;
+ for (size_t i = 0, num_chunks = chunks.size(); i != num_chunks; ++i) {
+ const BootImageLayout::ImageChunk& chunk = chunks[i];
+ std::string extension_error_msg;
+ uint8_t* old_reservation_begin = image_reservation.Begin();
+ size_t old_reservation_size = image_reservation.Size();
+ DCHECK_LE(chunk.reservation_size, old_reservation_size);
+ if (!LoadComponents(chunk,
+ validate_oat_file,
+ max_image_space_dependencies,
+ logger,
+ &spaces,
+ &image_reservation,
+ (i == 0) ? error_msg : &extension_error_msg)) {
+ // Failed to load the chunk. If this is the primary boot image, report the error.
+ if (i == 0) {
+ return false;
+ }
+ // For extension, shrink the reservation (and remap if needed, see below).
+ size_t new_reservation_size = old_reservation_size - chunk.reservation_size;
+ if (new_reservation_size == 0u) {
+ DCHECK_EQ(extra_reservation_size, 0u);
+ DCHECK_EQ(i + 1u, num_chunks);
+ image_reservation.Reset();
+ } else if (old_reservation_begin != image_reservation.Begin()) {
+ // Part of the image reservation has been used and then unmapped when
+ // rollling back the partial boot image extension load. Try to remap
+ // the image reservation. As this should be running single-threaded,
+ // the address range should still be available to mmap().
+ image_reservation.Reset();
+ std::string remap_error_msg;
+ image_reservation = ReserveBootImageMemory(old_reservation_begin,
+ new_reservation_size,
+ &remap_error_msg);
+ if (!image_reservation.IsValid()) {
+ *error_msg = StringPrintf("Failed to remap boot image reservation after failing "
+ "to load boot image extension (%s: %s): %s",
+ boot_class_path_locations_[chunk.start_index].c_str(),
+ extension_error_msg.c_str(),
+ remap_error_msg.c_str());
+ return false;
+ }
+ } else {
+ DCHECK_EQ(old_reservation_size, image_reservation.Size());
+ image_reservation.SetSize(new_reservation_size);
+ }
+ LOG(ERROR) << "Failed to load boot image extension "
+ << boot_class_path_locations_[chunk.start_index] << ": " << extension_error_msg;
}
- }
- for (size_t i = 0u, size = spaces.size(); i != size; ++i) {
- std::string expected_boot_class_path =
- (i == 0u) ? android::base::Join(provided_locations, ':') : std::string();
- if (!OpenOatFile(spaces[i].get(),
- boot_class_path_[i],
- expected_boot_class_path,
- validate_oat_file,
- logger,
- &image_reservation,
- error_msg)) {
- return false;
+ // Update `max_image_space_dependencies` if all previous BCP components
+ // were covered and loading the current chunk succeeded.
+ if (max_image_space_dependencies == chunk.start_index &&
+ spaces.size() == chunk.start_index + chunk.component_count) {
+ max_image_space_dependencies = chunk.start_index + chunk.component_count;
}
}
- if (!CheckReservationExhausted(image_reservation, error_msg)) {
+
+ MemMap local_extra_reservation;
+ if (!RemapExtraReservation(extra_reservation_size,
+ &image_reservation,
+ &local_extra_reservation,
+ error_msg)) {
return false;
}
@@ -1706,6 +2359,7 @@ class ImageSpace::BootImageLoader {
simple_relocate_visitor);
// Retrieve the Class.class, Method.class and Constructor.class needed in the loops below.
+ ObjPtr<mirror::ObjectArray<mirror::Class>> class_roots;
ObjPtr<mirror::Class> class_class;
ObjPtr<mirror::Class> method_class;
ObjPtr<mirror::Class> constructor_class;
@@ -1720,9 +2374,8 @@ class ImageSpace::BootImageLoader {
kExtension ? source_size - image_size : image_size);
int32_t class_roots_index = enum_cast<int32_t>(ImageHeader::kClassRoots);
DCHECK_LT(class_roots_index, image_roots->GetLength<kVerifyNone>());
- ObjPtr<mirror::ObjectArray<mirror::Class>> class_roots =
- ObjPtr<mirror::ObjectArray<mirror::Class>>::DownCast(base_relocate_visitor(
- image_roots->GetWithoutChecks<kVerifyNone>(class_roots_index).Ptr()));
+ class_roots = ObjPtr<mirror::ObjectArray<mirror::Class>>::DownCast(base_relocate_visitor(
+ image_roots->GetWithoutChecks<kVerifyNone>(class_roots_index).Ptr()));
if (kExtension) {
DCHECK(patched_objects->Test(class_roots.Ptr()));
class_class = GetClassRoot<mirror::Class, kWithoutReadBarrier>(class_roots);
@@ -1854,6 +2507,12 @@ class ImageSpace::BootImageLoader {
pos += RoundUp(object->SizeOf<kVerifyNone>(), kObjectAlignment);
}
}
+ if (kIsDebugBuild && !kExtension) {
+ // We used just Test() instead of Set() above but we need to use Set()
+ // for class roots to satisfy a DCHECK() for extensions.
+ DCHECK(!patched_objects->Test(class_roots.Ptr()));
+ patched_objects->Set(class_roots.Ptr());
+ }
}
void MaybeRelocateSpaces(const std::vector<std::unique_ptr<ImageSpace>>& spaces,
@@ -1919,8 +2578,8 @@ class ImageSpace::BootImageLoader {
bool OpenOatFile(ImageSpace* space,
const std::string& dex_filename,
- const std::string& expected_boot_class_path,
bool validate_oat_file,
+ ArrayRef<const std::unique_ptr<ImageSpace>> dependencies,
TimingLogger* logger,
/*inout*/MemMap* image_reservation,
/*out*/std::string* error_msg) {
@@ -1967,13 +2626,47 @@ class ImageSpace::BootImageLoader {
const char* oat_boot_class_path =
oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathKey);
oat_boot_class_path = (oat_boot_class_path != nullptr) ? oat_boot_class_path : "";
- if (expected_boot_class_path != oat_boot_class_path) {
- *error_msg = StringPrintf("Failed to match oat boot class path %s to expected "
- "boot class path %s in image %s",
- oat_boot_class_path,
- expected_boot_class_path.c_str(),
- space->GetName());
- return false;
+ const char* oat_boot_class_path_checksums =
+ oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey);
+ oat_boot_class_path_checksums =
+ (oat_boot_class_path_checksums != nullptr) ? oat_boot_class_path_checksums : "";
+ size_t component_count = image_header.GetComponentCount();
+ if (component_count == 0u) {
+ if (oat_boot_class_path[0] != 0 || oat_boot_class_path_checksums[0] != 0) {
+ *error_msg = StringPrintf("Unexpected non-empty boot class path %s and/or checksums %s"
+ " in image %s",
+ oat_boot_class_path,
+ oat_boot_class_path_checksums,
+ space->GetName());
+ return false;
+ }
+ } else if (dependencies.empty()) {
+ std::string expected_boot_class_path = android::base::Join(ArrayRef<const std::string>(
+ boot_class_path_locations_).SubArray(0u, component_count), ':');
+ if (expected_boot_class_path != oat_boot_class_path) {
+ *error_msg = StringPrintf("Failed to match oat boot class path %s to expected "
+ "boot class path %s in image %s",
+ oat_boot_class_path,
+ expected_boot_class_path.c_str(),
+ space->GetName());
+ return false;
+ }
+ } else {
+ std::string local_error_msg;
+ if (!VerifyBootClassPathChecksums(
+ oat_boot_class_path_checksums,
+ oat_boot_class_path,
+ dependencies,
+ ArrayRef<const std::string>(boot_class_path_locations_),
+ ArrayRef<const std::string>(boot_class_path_),
+ &local_error_msg)) {
+ *error_msg = StringPrintf("Failed to verify BCP %s with checksums %s in image %s: %s",
+ oat_boot_class_path,
+ oat_boot_class_path_checksums,
+ space->GetName(),
+ local_error_msg.c_str());
+ return false;
+ }
}
ptrdiff_t relocation_diff = space->Begin() - image_header.GetImageBegin();
CHECK(image_header.GetOatDataBegin() != nullptr);
@@ -2000,65 +2693,153 @@ class ImageSpace::BootImageLoader {
return true;
}
- bool ReserveBootImageMemory(uint32_t reservation_size,
- uint32_t image_start,
- size_t extra_reservation_size,
- /*out*/MemMap* image_reservation,
- /*out*/MemMap* extra_reservation,
- /*out*/std::string* error_msg) {
- DCHECK_ALIGNED(reservation_size, kPageSize);
- DCHECK_ALIGNED(image_start, kPageSize);
- DCHECK(!image_reservation->IsValid());
- DCHECK_LT(extra_reservation_size, std::numeric_limits<uint32_t>::max() - reservation_size);
- size_t total_size = reservation_size + extra_reservation_size;
- // If relocating, choose a random address for ALSR.
- uint32_t addr = relocate_ ? ART_BASE_ADDRESS + ChooseRelocationOffsetDelta() : image_start;
- *image_reservation =
- MemMap::MapAnonymous("Boot image reservation",
- reinterpret_cast32<uint8_t*>(addr),
- total_size,
- PROT_NONE,
- /*low_4gb=*/ true,
- /*reuse=*/ false,
- /*reservation=*/ nullptr,
- error_msg);
- if (!image_reservation->IsValid()) {
+ bool LoadComponents(const BootImageLayout::ImageChunk& chunk,
+ bool validate_oat_file,
+ size_t max_image_space_dependencies,
+ TimingLogger* logger,
+ /*inout*/std::vector<std::unique_ptr<ImageSpace>>* spaces,
+ /*inout*/MemMap* image_reservation,
+ /*out*/std::string* error_msg)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Make sure we destroy the spaces we created if we're returning an error.
+ // Note that this can unmap part of the original `image_reservation`.
+ class Guard {
+ public:
+ explicit Guard(std::vector<std::unique_ptr<ImageSpace>>* spaces_in)
+ : spaces_(spaces_in), committed_(spaces_->size()) {}
+ void Commit() {
+ DCHECK_LT(committed_, spaces_->size());
+ committed_ = spaces_->size();
+ }
+ ~Guard() {
+ DCHECK_LE(committed_, spaces_->size());
+ spaces_->resize(committed_);
+ }
+ private:
+ std::vector<std::unique_ptr<ImageSpace>>* const spaces_;
+ size_t committed_;
+ };
+ Guard guard(spaces);
+
+ bool is_extension = (chunk.start_index != 0u);
+ DCHECK_NE(spaces->empty(), is_extension);
+ if (max_image_space_dependencies < chunk.boot_image_component_count) {
+ DCHECK(is_extension);
+ *error_msg = StringPrintf("Missing dependencies for extension component %s, %zu < %u",
+ boot_class_path_locations_[chunk.start_index].c_str(),
+ max_image_space_dependencies,
+ chunk.boot_image_component_count);
return false;
}
- DCHECK(!extra_reservation->IsValid());
- if (extra_reservation_size != 0u) {
- DCHECK_ALIGNED(extra_reservation_size, kPageSize);
- DCHECK_LT(extra_reservation_size, image_reservation->Size());
- uint8_t* split = image_reservation->End() - extra_reservation_size;
- *extra_reservation = image_reservation->RemapAtEnd(split,
- "Boot image extra reservation",
- PROT_NONE,
- error_msg);
- if (!extra_reservation->IsValid()) {
+ ArrayRef<const std::string> requested_bcp_locations =
+ ArrayRef<const std::string>(boot_class_path_locations_).SubArray(
+ chunk.start_index, chunk.component_count);
+ std::vector<std::string> locations =
+ ExpandMultiImageLocations(requested_bcp_locations, chunk.base_location, is_extension);
+ std::vector<std::string> filenames =
+ ExpandMultiImageLocations(requested_bcp_locations, chunk.base_filename, is_extension);
+ DCHECK_EQ(locations.size(), filenames.size());
+ for (std::size_t i = 0u, size = locations.size(); i != size; ++i) {
+ spaces->push_back(Load(locations[i], filenames[i], logger, image_reservation, error_msg));
+ const ImageSpace* space = spaces->back().get();
+ if (space == nullptr) {
+ return false;
+ }
+ uint32_t expected_component_count = (i == 0u) ? chunk.component_count : 0u;
+ uint32_t expected_reservation_size = (i == 0u) ? chunk.reservation_size : 0u;
+ if (!Loader::CheckImageReservationSize(*space, expected_reservation_size, error_msg) ||
+ !Loader::CheckImageComponentCount(*space, expected_component_count, error_msg)) {
+ return false;
+ }
+ const ImageHeader& header = space->GetImageHeader();
+ if (i == 0 && (chunk.checksum != header.GetImageChecksum() ||
+ chunk.boot_image_component_count != header.GetBootImageComponentCount() ||
+ chunk.boot_image_checksum != header.GetBootImageChecksum())) {
+ *error_msg = StringPrintf("Image header modified since previously read from %s; "
+ "checksum: 0x%08x -> 0x%08x,"
+ "boot_image_component_count: %u -> %u, "
+ "boot_image_checksum: 0x%08x -> 0x%08x",
+ space->GetImageFilename().c_str(),
+ chunk.checksum,
+ header.GetImageChecksum(),
+ chunk.boot_image_component_count,
+ header.GetBootImageComponentCount(),
+ chunk.boot_image_checksum,
+ header.GetBootImageChecksum());
+ return false;
+ }
+ }
+ DCHECK_GE(max_image_space_dependencies, chunk.boot_image_component_count);
+ ArrayRef<const std::unique_ptr<ImageSpace>> dependencies =
+ ArrayRef<const std::unique_ptr<ImageSpace>>(*spaces).SubArray(
+ /*pos=*/ 0u, chunk.boot_image_component_count);
+ for (std::size_t i = 0u, size = locations.size(); i != size; ++i) {
+ ImageSpace* space = (*spaces)[spaces->size() - chunk.component_count + i].get();
+ if (!OpenOatFile(space,
+ boot_class_path_[chunk.start_index + i],
+ validate_oat_file,
+ dependencies,
+ logger,
+ image_reservation,
+ error_msg)) {
return false;
}
}
+ guard.Commit();
return true;
}
- bool CheckReservationExhausted(const MemMap& image_reservation, /*out*/std::string* error_msg) {
- if (image_reservation.IsValid()) {
- *error_msg = StringPrintf("Excessive image reservation after loading boot image: %p-%p",
- image_reservation.Begin(),
- image_reservation.End());
+ MemMap ReserveBootImageMemory(uint8_t* addr,
+ uint32_t reservation_size,
+ /*out*/std::string* error_msg) {
+ DCHECK_ALIGNED(reservation_size, kPageSize);
+ DCHECK_ALIGNED(addr, kPageSize);
+ return MemMap::MapAnonymous("Boot image reservation",
+ addr,
+ reservation_size,
+ PROT_NONE,
+ /*low_4gb=*/ true,
+ /*reuse=*/ false,
+ /*reservation=*/ nullptr,
+ error_msg);
+ }
+
+ bool RemapExtraReservation(size_t extra_reservation_size,
+ /*inout*/MemMap* image_reservation,
+ /*out*/MemMap* extra_reservation,
+ /*out*/std::string* error_msg) {
+ DCHECK_ALIGNED(extra_reservation_size, kPageSize);
+ DCHECK(!extra_reservation->IsValid());
+ size_t expected_size = image_reservation->IsValid() ? image_reservation->Size() : 0u;
+ if (extra_reservation_size != expected_size) {
+ *error_msg = StringPrintf("Image reservation mismatch after loading boot image: %zu != %zu",
+ extra_reservation_size,
+ expected_size);
return false;
}
+ if (extra_reservation_size != 0u) {
+ DCHECK(image_reservation->IsValid());
+ DCHECK_EQ(extra_reservation_size, image_reservation->Size());
+ *extra_reservation = image_reservation->RemapAtEnd(image_reservation->Begin(),
+ "Boot image extra reservation",
+ PROT_NONE,
+ error_msg);
+ if (!extra_reservation->IsValid()) {
+ return false;
+ }
+ }
+ DCHECK(!image_reservation->IsValid());
return true;
}
- const std::vector<std::string>& boot_class_path_;
- const std::vector<std::string>& boot_class_path_locations_;
- const std::string& image_location_;
- InstructionSet image_isa_;
- bool relocate_;
- bool executable_;
- bool is_zygote_;
+ const ArrayRef<const std::string> boot_class_path_;
+ const ArrayRef<const std::string> boot_class_path_locations_;
+ const std::string image_location_;
+ const InstructionSet image_isa_;
+ const bool relocate_;
+ const bool executable_;
+ const bool is_zygote_;
bool has_system_;
bool has_cache_;
bool is_global_cache_;
@@ -2067,6 +2848,68 @@ class ImageSpace::BootImageLoader {
std::string cache_filename_;
};
+bool ImageSpace::BootImageLoader::LoadFromSystem(
+ bool validate_oat_file,
+ size_t extra_reservation_size,
+ /*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
+ /*out*/MemMap* extra_reservation,
+ /*out*/std::string* error_msg) {
+ TimingLogger logger(__PRETTY_FUNCTION__, /*precise=*/ true, VLOG_IS_ON(image));
+
+ BootImageLayout layout(image_location_, boot_class_path_);
+ if (!layout.LoadFromSystem(image_isa_, error_msg)) {
+ return false;
+ }
+
+ if (!LoadImage(layout,
+ validate_oat_file,
+ extra_reservation_size,
+ &logger,
+ boot_image_spaces,
+ extra_reservation,
+ error_msg)) {
+ return false;
+ }
+
+ if (VLOG_IS_ON(image)) {
+ LOG(INFO) << "ImageSpace::BootImageLoader::LoadFromSystem exiting "
+ << boot_image_spaces->front();
+ logger.Dump(LOG_STREAM(INFO));
+ }
+ return true;
+}
+
+bool ImageSpace::BootImageLoader::LoadFromDalvikCache(
+ bool validate_oat_file,
+ size_t extra_reservation_size,
+ /*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
+ /*out*/MemMap* extra_reservation,
+ /*out*/std::string* error_msg) {
+ TimingLogger logger(__PRETTY_FUNCTION__, /*precise=*/ true, VLOG_IS_ON(image));
+ DCHECK(DalvikCacheExists());
+
+ BootImageLayout layout(image_location_, boot_class_path_);
+ if (!layout.LoadFromDalvikCache(dalvik_cache_, error_msg)) {
+ return false;
+ }
+ if (!LoadImage(layout,
+ validate_oat_file,
+ extra_reservation_size,
+ &logger,
+ boot_image_spaces,
+ extra_reservation,
+ error_msg)) {
+ return false;
+ }
+
+ if (VLOG_IS_ON(image)) {
+ LOG(INFO) << "ImageSpace::BootImageLoader::LoadFromDalvikCache exiting "
+ << boot_image_spaces->front();
+ logger.Dump(LOG_STREAM(INFO));
+ }
+ return true;
+}
+
static constexpr uint64_t kLowSpaceValue = 50 * MB;
static constexpr uint64_t kTmpFsSentinelValue = 384 * MB;
@@ -2112,7 +2955,7 @@ bool ImageSpace::LoadBootImage(
bool executable,
bool is_zygote,
size_t extra_reservation_size,
- /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
+ /*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
/*out*/MemMap* extra_reservation) {
ScopedTrace trace(__FUNCTION__);
@@ -2355,53 +3198,159 @@ bool ImageSpace::ValidateOatFile(const OatFile& oat_file, std::string* error_msg
return true;
}
-std::string ImageSpace::GetBootClassPathChecksums(ArrayRef<const std::string> boot_class_path,
- const std::string& image_location,
- InstructionSet image_isa,
- ImageSpaceLoadingOrder order,
- /*out*/std::string* error_msg) {
- std::string system_filename;
- bool has_system = false;
- std::string cache_filename;
- bool has_cache = false;
- bool dalvik_cache_exists = false;
- bool is_global_cache = false;
- if (!FindImageFilename(image_location.c_str(),
- image_isa,
- &system_filename,
- &has_system,
- &cache_filename,
- &dalvik_cache_exists,
- &has_cache,
- &is_global_cache)) {
- *error_msg = StringPrintf("Unable to find image file for %s and %s",
- image_location.c_str(),
- GetInstructionSetString(image_isa));
- return std::string();
- }
-
- DCHECK(has_system || has_cache);
- const std::string& filename = (order == ImageSpaceLoadingOrder::kSystemFirst)
- ? (has_system ? system_filename : cache_filename)
- : (has_cache ? cache_filename : system_filename);
- ImageHeader header;
- if (!ReadSpecificImageHeader(filename.c_str(), &header, error_msg)) {
- return std::string();
+std::string ImageSpace::GetBootClassPathChecksums(
+ ArrayRef<ImageSpace* const> image_spaces,
+ ArrayRef<const DexFile* const> boot_class_path) {
+ DCHECK(!boot_class_path.empty());
+ size_t bcp_pos = 0u;
+ std::string boot_image_checksum;
+
+ for (size_t image_pos = 0u, size = image_spaces.size(); image_pos != size; ) {
+ const ImageSpace* main_space = image_spaces[image_pos];
+ // Caller must make sure that the image spaces correspond to the head of the BCP.
+ DCHECK_NE(main_space->oat_file_non_owned_->GetOatDexFiles().size(), 0u);
+ DCHECK_EQ(main_space->oat_file_non_owned_->GetOatDexFiles()[0]->GetDexFileLocation(),
+ boot_class_path[bcp_pos]->GetLocation());
+ const ImageHeader& current_header = main_space->GetImageHeader();
+ uint32_t component_count = current_header.GetComponentCount();
+ DCHECK_NE(component_count, 0u);
+ DCHECK_LE(component_count, image_spaces.size() - image_pos);
+ if (image_pos != 0u) {
+ boot_image_checksum += ':';
+ }
+ AppendImageChecksum(component_count, current_header.GetImageChecksum(), &boot_image_checksum);
+ for (size_t component_index = 0; component_index != component_count; ++component_index) {
+ const ImageSpace* space = image_spaces[image_pos + component_index];
+ const OatFile* oat_file = space->oat_file_non_owned_;
+ size_t num_dex_files = oat_file->GetOatDexFiles().size();
+ if (kIsDebugBuild) {
+ CHECK_NE(num_dex_files, 0u);
+ CHECK_LE(oat_file->GetOatDexFiles().size(), boot_class_path.size() - bcp_pos);
+ for (size_t i = 0; i != num_dex_files; ++i) {
+ CHECK_EQ(oat_file->GetOatDexFiles()[i]->GetDexFileLocation(),
+ boot_class_path[bcp_pos + i]->GetLocation());
+ }
+ }
+ bcp_pos += num_dex_files;
+ }
+ image_pos += component_count;
}
- if (header.GetComponentCount() == 0u || header.GetComponentCount() > boot_class_path.size()) {
- *error_msg = StringPrintf("Unexpected component count in %s, received %u, "
- "expected non-zero and <= %zu",
- filename.c_str(),
- header.GetComponentCount(),
- boot_class_path.size());
- return std::string();
+
+ ArrayRef<const DexFile* const> boot_class_path_tail =
+ ArrayRef<const DexFile* const>(boot_class_path).SubArray(bcp_pos);
+ DCHECK(boot_class_path_tail.empty() ||
+ !DexFileLoader::IsMultiDexLocation(boot_class_path_tail.front()->GetLocation().c_str()));
+ for (const DexFile* dex_file : boot_class_path_tail) {
+ if (!DexFileLoader::IsMultiDexLocation(dex_file->GetLocation().c_str())) {
+ if (!boot_image_checksum.empty()) {
+ boot_image_checksum += ':';
+ }
+ boot_image_checksum += kDexFileChecksumPrefix;
+ }
+ StringAppendF(&boot_image_checksum, "/%08x", dex_file->GetLocationChecksum());
+ }
+ return boot_image_checksum;
+}
+
+static size_t CheckAndCountBCPComponents(std::string_view oat_boot_class_path,
+ ArrayRef<const std::string> boot_class_path,
+ /*out*/std::string* error_msg) {
+ // Check that the oat BCP is a prefix of current BCP locations and count components.
+ size_t component_count = 0u;
+ std::string_view remaining_bcp(oat_boot_class_path);
+ bool bcp_ok = false;
+ for (const std::string& location : boot_class_path) {
+ if (!StartsWith(remaining_bcp, location)) {
+ break;
+ }
+ remaining_bcp.remove_prefix(location.size());
+ ++component_count;
+ if (remaining_bcp.empty()) {
+ bcp_ok = true;
+ break;
+ }
+ if (!StartsWith(remaining_bcp, ":")) {
+ break;
+ }
+ remaining_bcp.remove_prefix(1u);
+ }
+ if (!bcp_ok) {
+ *error_msg = StringPrintf("Oat boot class path (%s) is not a prefix of"
+ " runtime boot class path (%s)",
+ std::string(oat_boot_class_path).c_str(),
+ android::base::Join(boot_class_path, ':').c_str());
+ return static_cast<size_t>(-1);
+ }
+ return component_count;
+}
+
+bool ImageSpace::VerifyBootClassPathChecksums(std::string_view oat_checksums,
+ std::string_view oat_boot_class_path,
+ const std::string& image_location,
+ ArrayRef<const std::string> boot_class_path_locations,
+ ArrayRef<const std::string> boot_class_path,
+ InstructionSet image_isa,
+ ImageSpaceLoadingOrder order,
+ /*out*/std::string* error_msg) {
+ if (oat_checksums.empty() || oat_boot_class_path.empty()) {
+ *error_msg = oat_checksums.empty() ? "Empty checksums." : "Empty boot class path.";
+ return false;
+ }
+
+ DCHECK_EQ(boot_class_path_locations.size(), boot_class_path.size());
+ size_t bcp_size =
+ CheckAndCountBCPComponents(oat_boot_class_path, boot_class_path_locations, error_msg);
+ if (bcp_size == static_cast<size_t>(-1)) {
+ DCHECK(!error_msg->empty());
+ return false;
+ }
+
+ size_t bcp_pos = 0u;
+ if (StartsWith(oat_checksums, "i")) {
+ // Use only the matching part of the BCP for validation.
+ BootImageLayout layout(image_location, boot_class_path.SubArray(/*pos=*/ 0u, bcp_size));
+ std::string primary_image_location = layout.GetPrimaryImageLocation();
+ std::string system_filename;
+ bool has_system = false;
+ std::string cache_filename;
+ bool has_cache = false;
+ bool dalvik_cache_exists = false;
+ bool is_global_cache = false;
+ if (!FindImageFilename(primary_image_location.c_str(),
+ image_isa,
+ &system_filename,
+ &has_system,
+ &cache_filename,
+ &dalvik_cache_exists,
+ &has_cache,
+ &is_global_cache)) {
+ *error_msg = StringPrintf("Unable to find image file for %s and %s",
+ image_location.c_str(),
+ GetInstructionSetString(image_isa));
+ return false;
+ }
+
+ DCHECK(has_system || has_cache);
+ bool use_system = (order == ImageSpaceLoadingOrder::kSystemFirst) ? has_system : !has_cache;
+ bool image_checksums_ok = use_system
+ ? layout.ValidateFromSystem(image_isa, &oat_checksums, error_msg)
+ : layout.ValidateFromDalvikCache(cache_filename, &oat_checksums, error_msg);
+ if (!image_checksums_ok) {
+ return false;
+ }
+ bcp_pos = layout.GetNextBcpIndex();
}
- std::string boot_image_checksum =
- StringPrintf("i;%d/%08x", header.GetComponentCount(), header.GetImageChecksum());
- ArrayRef<const std::string> boot_class_path_tail =
- ArrayRef<const std::string>(boot_class_path).SubArray(header.GetComponentCount());
- for (const std::string& bcp_filename : boot_class_path_tail) {
+ for ( ; bcp_pos != bcp_size; ++bcp_pos) {
+ static_assert(ImageSpace::kDexFileChecksumPrefix == 'd', "Format prefix check.");
+ if (!StartsWith(oat_checksums, "d")) {
+ *error_msg = StringPrintf("Missing dex checksums, expected %s to start with 'd'",
+ std::string(oat_checksums).c_str());
+ return false;
+ }
+ oat_checksums.remove_prefix(1u);
+
+ const std::string& bcp_filename = boot_class_path[bcp_pos];
std::vector<std::unique_ptr<const DexFile>> dex_files;
const ArtDexFileLoader dex_file_loader;
if (!dex_file_loader.Open(bcp_filename.c_str(),
@@ -2410,65 +3359,123 @@ std::string ImageSpace::GetBootClassPathChecksums(ArrayRef<const std::string> bo
/*verify_checksum=*/ false,
error_msg,
&dex_files)) {
- return std::string();
+ return false;
}
DCHECK(!dex_files.empty());
- StringAppendF(&boot_image_checksum, ":d");
for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
- StringAppendF(&boot_image_checksum, "/%08x", dex_file->GetLocationChecksum());
+ std::string dex_file_checksum = StringPrintf("/%08x", dex_file->GetLocationChecksum());
+ if (!StartsWith(oat_checksums, dex_file_checksum)) {
+ *error_msg = StringPrintf("Dex checksum mismatch, expected %s to start with %s",
+ std::string(oat_checksums).c_str(),
+ dex_file_checksum.c_str());
+ return false;
+ }
+ oat_checksums.remove_prefix(dex_file_checksum.size());
+ }
+ if (bcp_pos + 1u != bcp_size) {
+ if (!StartsWith(oat_checksums, ":")) {
+ *error_msg = StringPrintf("Missing ':' separator at start of %s",
+ std::string(oat_checksums).c_str());
+ return false;
+ }
}
}
- return boot_image_checksum;
+ if (!oat_checksums.empty()) {
+ *error_msg = StringPrintf("Checksum too long, unexpected tail %s",
+ std::string(oat_checksums).c_str());
+ return false;
+ }
+ return true;
}
-std::string ImageSpace::GetBootClassPathChecksums(
- const std::vector<ImageSpace*>& image_spaces,
- const std::vector<const DexFile*>& boot_class_path) {
- size_t pos = 0u;
- std::string boot_image_checksum;
+bool ImageSpace::VerifyBootClassPathChecksums(
+ std::string_view oat_checksums,
+ std::string_view oat_boot_class_path,
+ ArrayRef<const std::unique_ptr<ImageSpace>> image_spaces,
+ ArrayRef<const std::string> boot_class_path_locations,
+ ArrayRef<const std::string> boot_class_path,
+ /*out*/std::string* error_msg) {
+ DCHECK_EQ(boot_class_path.size(), boot_class_path_locations.size());
+ DCHECK_GE(boot_class_path_locations.size(), image_spaces.size());
+ if (oat_checksums.empty() || oat_boot_class_path.empty()) {
+ *error_msg = oat_checksums.empty() ? "Empty checksums." : "Empty boot class path.";
+ return false;
+ }
- if (!image_spaces.empty()) {
- const ImageHeader& primary_header = image_spaces.front()->GetImageHeader();
- uint32_t component_count = primary_header.GetComponentCount();
- DCHECK_EQ(component_count, image_spaces.size());
- boot_image_checksum =
- StringPrintf("i;%d/%08x", component_count, primary_header.GetImageChecksum());
- for (const ImageSpace* space : image_spaces) {
- size_t num_dex_files = space->oat_file_non_owned_->GetOatDexFiles().size();
- if (kIsDebugBuild) {
+ size_t oat_bcp_size =
+ CheckAndCountBCPComponents(oat_boot_class_path, boot_class_path_locations, error_msg);
+ if (oat_bcp_size == static_cast<size_t>(-1)) {
+ DCHECK(!error_msg->empty());
+ return false;
+ }
+ const size_t num_image_spaces = image_spaces.size();
+ if (num_image_spaces != oat_bcp_size) {
+ *error_msg = StringPrintf("Image header records more dependencies (%zu) than BCP (%zu)",
+ num_image_spaces,
+ oat_bcp_size);
+ return false;
+ }
+
+ // Verify image checksums.
+ size_t image_pos = 0u;
+ while (image_pos != num_image_spaces && StartsWith(oat_checksums, "i")) {
+ // Verify the current image checksum.
+ const ImageHeader& current_header = image_spaces[image_pos]->GetImageHeader();
+ uint32_t component_count = current_header.GetComponentCount();
+ DCHECK_NE(component_count, 0u);
+ DCHECK_LE(component_count, image_spaces.size() - image_pos);
+ uint32_t checksum = current_header.GetImageChecksum();
+ if (!CheckAndRemoveImageChecksum(component_count, checksum, &oat_checksums, error_msg)) {
+ DCHECK(!error_msg->empty());
+ return false;
+ }
+
+ if (kIsDebugBuild) {
+ for (size_t component_index = 0; component_index != component_count; ++component_index) {
+ const OatFile* oat_file = image_spaces[image_pos + component_index]->oat_file_non_owned_;
+ size_t num_dex_files = oat_file->GetOatDexFiles().size();
CHECK_NE(num_dex_files, 0u);
- CHECK_LE(space->oat_file_non_owned_->GetOatDexFiles().size(), boot_class_path.size() - pos);
- for (size_t i = 0; i != num_dex_files; ++i) {
- CHECK_EQ(space->oat_file_non_owned_->GetOatDexFiles()[i]->GetDexFileLocation(),
- boot_class_path[pos + i]->GetLocation());
+ const std::string main_location = oat_file->GetOatDexFiles()[0]->GetDexFileLocation();
+ // TODO: Get rid of the weird ResolveRelativeEncodedDexLocation() stuff from oat_file.cc
+ // and enable this check:
+ // CHECK_EQ(main_location, boot_class_path_locations[image_pos + component_index]);
+ CHECK(!DexFileLoader::IsMultiDexLocation(main_location.c_str()));
+ for (size_t i = 1u; i != num_dex_files; ++i) {
+ CHECK(DexFileLoader::IsMultiDexLocation(
+ oat_file->GetOatDexFiles()[i]->GetDexFileLocation().c_str()));
}
}
- pos += num_dex_files;
}
- }
- ArrayRef<const DexFile* const> boot_class_path_tail =
- ArrayRef<const DexFile* const>(boot_class_path).SubArray(pos);
- DCHECK(boot_class_path_tail.empty() ||
- !DexFileLoader::IsMultiDexLocation(boot_class_path_tail.front()->GetLocation().c_str()));
- for (const DexFile* dex_file : boot_class_path_tail) {
- if (!DexFileLoader::IsMultiDexLocation(dex_file->GetLocation().c_str())) {
- StringAppendF(&boot_image_checksum, boot_image_checksum.empty() ? "d" : ":d");
+ image_pos += component_count;
+
+ if (!StartsWith(oat_checksums, ":")) {
+ // Check that we've reached the end of checksums and BCP.
+ if (!oat_checksums.empty()) {
+ *error_msg = StringPrintf("Expected ':' separator or end of checksums, remaining %s.",
+ std::string(oat_checksums).c_str());
+ return false;
+ }
+ if (image_pos != oat_bcp_size) {
+ *error_msg = StringPrintf("Component count mismatch between checksums (%zu) and BCP (%zu)",
+ image_pos,
+ oat_bcp_size);
+ return false;
+ }
+ return true;
}
- StringAppendF(&boot_image_checksum, "/%08x", dex_file->GetLocationChecksum());
+ oat_checksums.remove_prefix(1u);
}
- return boot_image_checksum;
-}
-std::vector<std::string> ImageSpace::ExpandMultiImageLocations(
- const std::vector<std::string>& dex_locations,
- const std::string& image_location) {
- return ExpandMultiImageLocations(ArrayRef<const std::string>(dex_locations), image_location);
+ // We do not allow dependencies of extensions on dex files. That would require
+ // interleaving the loading of the images with opening the other BCP dex files.
+ return false;
}
std::vector<std::string> ImageSpace::ExpandMultiImageLocations(
ArrayRef<const std::string> dex_locations,
- const std::string& image_location) {
+ const std::string& image_location,
+ bool boot_image_extension) {
DCHECK(!dex_locations.empty());
// Find the path.
@@ -2497,10 +3504,14 @@ std::vector<std::string> ImageSpace::ExpandMultiImageLocations(
std::vector<std::string> locations;
locations.reserve(dex_locations.size());
- locations.push_back(image_location);
+ size_t start_index = 0u;
+ if (!boot_image_extension) {
+ start_index = 1u;
+ locations.push_back(image_location);
+ }
- // Now create the other names. Use a counted loop to skip the first one.
- for (size_t i = 1u; i < dex_locations.size(); ++i) {
+ // Now create the other names. Use a counted loop to skip the first one if needed.
+ for (size_t i = start_index; i < dex_locations.size(); ++i) {
// Replace path with `base` (i.e. image path and prefix) and replace the original
// extension (if any) with `extension`.
std::string name = dex_locations[i];
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index c1b5a81980..f56b42bca3 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -39,11 +39,61 @@ class ImageSpace : public MemMapSpace {
return kSpaceTypeImageSpace;
}
- // Load boot image spaces from a primary image file for a specified instruction set.
+ // Load boot image spaces for specified boot class path, image location, instruction set, etc.
//
// On successful return, the loaded spaces are added to boot_image_spaces (which must be
// empty on entry) and `extra_reservation` is set to the requested reservation located
// after the end of the last loaded oat file.
+ //
+ // IMAGE LOCATION
+ //
+ // The "image location" is a colon-separated list that specifies one or more
+ // components by name and may also specify search paths for extensions
+ // corresponding to the remaining boot class path (BCP) extensions.
+ //
+ // The primary boot image can be specified as one of
+ // <path>/<base-name>
+ // <base-name>
+ // and the path of the first BCP component is used for the second form.
+ //
+ // Named extension specifications must correspond to an expansion of the
+ // <base-name> with a BCP component (for example boot.art with the BCP
+ // component name <jar-path>/framework.jar expands to boot-framework.art).
+ // They can be similarly specified as one of
+ // <ext-path>/<ext-name>
+ // <ext-name>
+ // and must be listed in the order of their corresponding BCP components.
+ //
+ // Search paths for remaining extensions can be specified after named
+ // components as one of
+ // <search-path>/*
+ // *
+ // where the second form means that the path of a particular BCP component
+ // should be used to search for that component's boot image extension. These
+ // paths will be searched in the specifed order.
+ //
+ // The actual filename shall be derived from the specified locations using
+ // `GetSystemImageFilename()` or `GetDalvikCacheFilename()`.
+ //
+ // Example image locations:
+ // /system/framework/boot.art
+ // - only primary boot image with full path.
+ // boot.art:boot-framework.art
+ // - primary and one extension, use BCP component paths.
+ // /apex/com.android.art/boot.art:*
+ // - primary with exact location, search for the rest based on BCP
+ // component paths.
+ // boot.art:/system/framework/*
+ // - primary based on BCP component path, search for extensions in
+ // /system/framework.
+ // /apex/com.android.art/boot.art:/system/framework/*:*
+ // - primary with exact location, search for extensions first in
+ // /system/framework, then in the corresponding BCP component path.
+ // /apex/com.android.art/boot.art:*:/system/framework/*
+ // - primary with exact location, search for extensions first in the
+ // corresponding BCP component path and then in /system/framework.
+ // /apex/com.android.art/boot.art:*:boot-framework.jar
+ // - invalid, named components may not follow search paths.
static bool LoadBootImage(
const std::vector<std::string>& boot_class_path,
const std::vector<std::string>& boot_class_path_locations,
@@ -54,7 +104,7 @@ class ImageSpace : public MemMapSpace {
bool executable,
bool is_zygote,
size_t extra_reservation_size,
- /*out*/std::vector<std::unique_ptr<space::ImageSpace>>* boot_image_spaces,
+ /*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
/*out*/MemMap* extra_reservation) REQUIRES_SHARED(Locks::mutator_lock_);
// Try to open an existing app image space.
@@ -137,24 +187,39 @@ class ImageSpace : public MemMapSpace {
// The leading character in a dex file checksum part of boot class path checkums.
static constexpr char kDexFileChecksumPrefix = 'd';
- // Returns the checksums for the boot image and extra boot class path dex files,
- // based on the boot class path, image location and ISA (may differ from the ISA of an
- // initialized Runtime). The boot image and dex files do not need to be loaded in memory.
- static std::string GetBootClassPathChecksums(ArrayRef<const std::string> boot_class_path,
- const std::string& image_location,
- InstructionSet image_isa,
- ImageSpaceLoadingOrder order,
- /*out*/std::string* error_msg);
-
- // Returns the checksums for the boot image and extra boot class path dex files,
- // based on the boot image and boot class path dex files loaded in memory.
- static std::string GetBootClassPathChecksums(const std::vector<ImageSpace*>& image_spaces,
- const std::vector<const DexFile*>& boot_class_path);
+ // Returns the checksums for the boot image, extensions and extra boot class path dex files,
+ // based on the image spaces and boot class path dex files loaded in memory.
+ // The `image_spaces` must correspond to the head of the `boot_class_path`.
+ static std::string GetBootClassPathChecksums(ArrayRef<ImageSpace* const> image_spaces,
+ ArrayRef<const DexFile* const> boot_class_path);
+
+ // Returns whether the checksums are valid for the given boot class path,
+ // image location and ISA (may differ from the ISA of an initialized Runtime).
+ // The boot image and dex files do not need to be loaded in memory.
+ static bool VerifyBootClassPathChecksums(std::string_view oat_checksums,
+ std::string_view oat_boot_class_path,
+ const std::string& image_location,
+ ArrayRef<const std::string> boot_class_path_locations,
+ ArrayRef<const std::string> boot_class_path,
+ InstructionSet image_isa,
+ ImageSpaceLoadingOrder order,
+ /*out*/std::string* error_msg);
+
+ // Returns whether the oat checksums and boot class path description are valid
+ // for the given boot image spaces and boot class path. Used for boot image extensions.
+ static bool VerifyBootClassPathChecksums(
+ std::string_view oat_checksums,
+ std::string_view oat_boot_class_path,
+ ArrayRef<const std::unique_ptr<ImageSpace>> image_spaces,
+ ArrayRef<const std::string> boot_class_path_locations,
+ ArrayRef<const std::string> boot_class_path,
+ /*out*/std::string* error_msg);
// Expand a single image location to multi-image locations based on the dex locations.
static std::vector<std::string> ExpandMultiImageLocations(
- const std::vector<std::string>& dex_locations,
- const std::string& image_location);
+ ArrayRef<const std::string> dex_locations,
+ const std::string& image_location,
+ bool boot_image_extension = false);
// Returns true if the dex checksums in the given oat file match the
// checksums of the original dex files on disk. This is intended to be used
@@ -218,11 +283,7 @@ class ImageSpace : public MemMapSpace {
friend class Space;
private:
- // Internal overload that takes ArrayRef<> instead of vector<>.
- static std::vector<std::string> ExpandMultiImageLocations(
- ArrayRef<const std::string> dex_locations,
- const std::string& image_location);
-
+ class BootImageLayout;
class BootImageLoader;
template <typename ReferenceVisitor>
class ClassTableVisitor;
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
index 98774bdf2d..6a9bdf6c79 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -435,15 +435,10 @@ bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, AccessMethod acce
DCHECK(member != nullptr);
Runtime* runtime = Runtime::Current();
- EnforcementPolicy policy = runtime->GetHiddenApiEnforcementPolicy();
- DCHECK(policy != EnforcementPolicy::kDisabled)
+ EnforcementPolicy hiddenApiPolicy = runtime->GetHiddenApiEnforcementPolicy();
+ DCHECK(hiddenApiPolicy != EnforcementPolicy::kDisabled)
<< "Should never enter this function when access checks are completely disabled";
- const bool deny_access =
- (policy == EnforcementPolicy::kEnabled) &&
- IsSdkVersionSetAndMoreThan(runtime->GetTargetSdkVersion(),
- api_list.GetMaxAllowedSdkVersion());
-
MemberSignature member_signature(member);
// Check for an exemption first. Exempted APIs are treated as white list.
@@ -455,6 +450,18 @@ bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, AccessMethod acce
return false;
}
+ EnforcementPolicy testApiPolicy = runtime->GetTestApiEnforcementPolicy();
+
+ bool deny_access = false;
+ if (hiddenApiPolicy == EnforcementPolicy::kEnabled) {
+ if (testApiPolicy == EnforcementPolicy::kDisabled && api_list.IsTestApi()) {
+ deny_access = false;
+ } else {
+ deny_access = IsSdkVersionSetAndMoreThan(runtime->GetTargetSdkVersion(),
+ api_list.GetMaxAllowedSdkVersion());
+ }
+ }
+
if (access_method != AccessMethod::kNone) {
// Print a log message with information about this class member access.
// We do this if we're about to deny access, or the app is debuggable.
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index a21225b376..2ef3522eee 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -358,6 +358,7 @@ ALWAYS_INLINE inline uint32_t GetRuntimeFlags(ArtMethod* method)
return 0u;
case Intrinsics::kUnsafeGetLong:
case Intrinsics::kFP16ToFloat:
+ case Intrinsics::kFP16ToHalf:
return kAccCorePlatformApi;
default:
// Remaining intrinsics are public API. We DCHECK that in SetIntrinsic().
diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc
index 70fafe6587..145bb07676 100644
--- a/runtime/hidden_api_test.cc
+++ b/runtime/hidden_api_test.cc
@@ -109,6 +109,7 @@ TEST_F(HiddenApiTest, CheckGetActionFromRuntimeFlags) {
runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kJustWarn);
ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Whitelist()), false);
ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Greylist()), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxQ()), false);
ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxP()), false);
ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxO()), false);
ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Blacklist()), false);
@@ -118,6 +119,7 @@ TEST_F(HiddenApiTest, CheckGetActionFromRuntimeFlags) {
static_cast<uint32_t>(hiddenapi::ApiList::GreylistMaxO().GetMaxAllowedSdkVersion()));
ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Whitelist()), false);
ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Greylist()), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxQ()), false);
ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxP()), false);
ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxO()), false);
ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Blacklist()), true);
@@ -127,6 +129,7 @@ TEST_F(HiddenApiTest, CheckGetActionFromRuntimeFlags) {
static_cast<uint32_t>(hiddenapi::ApiList::GreylistMaxO().GetMaxAllowedSdkVersion()) + 1);
ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Whitelist()), false);
ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Greylist()), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxQ()), false);
ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxP()), false);
ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxO()), true);
ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Blacklist()), true);
@@ -136,9 +139,58 @@ TEST_F(HiddenApiTest, CheckGetActionFromRuntimeFlags) {
static_cast<uint32_t>(hiddenapi::ApiList::GreylistMaxP().GetMaxAllowedSdkVersion()) + 1);
ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Whitelist()), false);
ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Greylist()), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxQ()), false);
ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxP()), true);
ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxO()), true);
ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Blacklist()), true);
+
+ runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
+ runtime_->SetTargetSdkVersion(
+ static_cast<uint32_t>(hiddenapi::ApiList::GreylistMaxQ().GetMaxAllowedSdkVersion()) + 1);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Whitelist()), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Greylist()), false);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxQ()), true);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxP()), true);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxO()), true);
+ ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Blacklist()), true);
+}
+
+TEST_F(HiddenApiTest, CheckTestApiEnforcement) {
+ ScopedObjectAccess soa(self_);
+
+ runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
+ runtime_->SetTargetSdkVersion(
+ static_cast<uint32_t>(hiddenapi::ApiList::GreylistMaxQ().GetMaxAllowedSdkVersion()) + 1);
+
+ // Default case where all TestApis are treated like non-TestApi.
+ runtime_->SetTestApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
+ ASSERT_EQ(
+ ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::Whitelist()), false);
+ ASSERT_EQ(
+ ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::Greylist()), false);
+ ASSERT_EQ(
+ ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::GreylistMaxQ()), true);
+ ASSERT_EQ(
+ ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::GreylistMaxP()), true);
+ ASSERT_EQ(
+ ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::GreylistMaxO()), true);
+ ASSERT_EQ(
+ ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::Blacklist()), true);
+
+ // A case where we want to allow access to TestApis.
+ runtime_->SetTestApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kDisabled);
+ ASSERT_EQ(
+ ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::Whitelist()), false);
+ ASSERT_EQ(
+ ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::Greylist()), false);
+ ASSERT_EQ(
+ ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::GreylistMaxQ()), false);
+ ASSERT_EQ(
+ ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::GreylistMaxP()), false);
+ ASSERT_EQ(
+ ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::GreylistMaxO()), false);
+ ASSERT_EQ(
+ ShouldDenyAccess(hiddenapi::ApiList::TestApi() | hiddenapi::ApiList::Blacklist()), false);
}
TEST_F(HiddenApiTest, CheckMembersRead) {
diff --git a/runtime/image.cc b/runtime/image.cc
index 11fac590b0..06ba946549 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -29,7 +29,7 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '7', '8', '\0' }; // FP16ToFloat intrinsic
+const uint8_t ImageHeader::kImageVersion[] = { '0', '8', '0', '\0' }; // Chained checksums.
ImageHeader::ImageHeader(uint32_t image_reservation_size,
uint32_t component_count,
@@ -44,6 +44,8 @@ ImageHeader::ImageHeader(uint32_t image_reservation_size,
uint32_t oat_file_end,
uint32_t boot_image_begin,
uint32_t boot_image_size,
+ uint32_t boot_image_component_count,
+ uint32_t boot_image_checksum,
uint32_t pointer_size)
: image_reservation_size_(image_reservation_size),
component_count_(component_count),
@@ -57,6 +59,8 @@ ImageHeader::ImageHeader(uint32_t image_reservation_size,
oat_file_end_(oat_file_end),
boot_image_begin_(boot_image_begin),
boot_image_size_(boot_image_size),
+ boot_image_component_count_(boot_image_component_count),
+ boot_image_checksum_(boot_image_checksum),
image_roots_(image_roots),
pointer_size_(pointer_size) {
CHECK_EQ(image_begin, RoundUp(image_begin, kPageSize));
@@ -93,6 +97,13 @@ void ImageHeader::RelocateBootImageReferences(int64_t delta) {
}
}
+bool ImageHeader::IsAppImage() const {
+ // Unlike boot image and boot image extensions which include address space for
+ // oat files in their reservation size, app images are loaded separately from oat
+ // files and their reservation size is the image size rounded up to full page.
+ return image_reservation_size_ == RoundUp(image_size_, kPageSize);
+}
+
bool ImageHeader::IsValid() const {
if (memcmp(magic_, kImageMagic, sizeof(kImageMagic)) != 0) {
return false;
diff --git a/runtime/image.h b/runtime/image.h
index 13bf112c99..12950a3591 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -137,6 +137,8 @@ class PACKED(8) ImageHeader {
uint32_t oat_file_end,
uint32_t boot_image_begin,
uint32_t boot_image_size,
+ uint32_t boot_image_component_count,
+ uint32_t boot_image_checksum,
uint32_t pointer_size);
bool IsValid() const;
@@ -350,15 +352,19 @@ class PACKED(8) ImageHeader {
return boot_image_size_;
}
+ uint32_t GetBootImageComponentCount() const {
+ return boot_image_component_count_;
+ }
+
+ uint32_t GetBootImageChecksum() const {
+ return boot_image_checksum_;
+ }
+
uint64_t GetDataSize() const {
return data_size_;
}
- bool IsAppImage() const {
- // App images currently require a boot image, if the size is non zero then it is an app image
- // header.
- return boot_image_size_ != 0u;
- }
+ bool IsAppImage() const;
// Visit mirror::Objects in the section starting at base.
// TODO: Delete base parameter if it is always equal to GetImageBegin.
@@ -465,10 +471,15 @@ class PACKED(8) ImageHeader {
// .so files. Used for positioning a following alloc spaces.
uint32_t oat_file_end_ = 0u;
- // Boot image begin and end (app image headers only).
+ // Boot image begin and end (only applies to boot image extension and app image headers).
uint32_t boot_image_begin_ = 0u;
uint32_t boot_image_size_ = 0u; // Includes heap (*.art) and code (.oat).
+ // Number of boot image components that this image depends on and their composite checksum
+ // (only applies to boot image extension and app image headers).
+ uint32_t boot_image_component_count_ = 0u;
+ uint32_t boot_image_checksum_ = 0u;
+
// Absolute address of an Object[] of objects needed to reinitialize from an image.
uint32_t image_roots_ = 0u;
diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc
index 6b2d989cd3..3759225b91 100644
--- a/runtime/interpreter/interpreter_intrinsics.cc
+++ b/runtime/interpreter/interpreter_intrinsics.cc
@@ -574,6 +574,7 @@ bool MterpHandleIntrinsic(ShadowFrame* shadow_frame,
UNIMPLEMENTED_CASE(CRC32UpdateBytes /* (I[BII)I */)
UNIMPLEMENTED_CASE(CRC32UpdateByteBuffer /* (IJII)I */)
UNIMPLEMENTED_CASE(FP16ToFloat /* (S)F */)
+ UNIMPLEMENTED_CASE(FP16ToHalf /* (F)S */)
INTRINSIC_CASE(VarHandleFullFence)
INTRINSIC_CASE(VarHandleAcquireFence)
INTRINSIC_CASE(VarHandleReleaseFence)
diff --git a/runtime/intrinsics_list.h b/runtime/intrinsics_list.h
index 15ae309624..bb41ca732d 100644
--- a/runtime/intrinsics_list.h
+++ b/runtime/intrinsics_list.h
@@ -166,6 +166,7 @@
V(MemoryPokeLongNative, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kCanThrow, "Llibcore/io/Memory;", "pokeLongNative", "(JJ)V") \
V(MemoryPokeShortNative, kStatic, kNeedsEnvironmentOrCache, kWriteSideEffects, kCanThrow, "Llibcore/io/Memory;", "pokeShortNative", "(JS)V") \
V(FP16ToFloat, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Llibcore/util/FP16;", "toFloat", "(S)F") \
+ V(FP16ToHalf, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow, "Llibcore/util/FP16;", "toHalf", "(F)S") \
V(StringCharAt, kVirtual, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow, "Ljava/lang/String;", "charAt", "(I)C") \
V(StringCompareTo, kVirtual, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow, "Ljava/lang/String;", "compareTo", "(Ljava/lang/String;)I") \
V(StringEquals, kVirtual, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow, "Ljava/lang/String;", "equals", "(Ljava/lang/Object;)Z") \
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 11619c455f..f69d786299 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -215,7 +215,10 @@ Jit::Jit(JitCodeCache* code_cache, JitOptions* options)
boot_completed_lock_("Jit::boot_completed_lock_"),
cumulative_timings_("JIT timings"),
memory_use_("Memory used for compilation", 16),
- lock_("JIT memory use lock") {}
+ lock_("JIT memory use lock"),
+ zygote_mapping_methods_(),
+ fd_methods_(-1),
+ fd_methods_size_(0) {}
Jit* Jit::Create(JitCodeCache* code_cache, JitOptions* options) {
if (jit_load_ == nullptr) {
@@ -589,6 +592,135 @@ void Jit::AddMemoryUsage(ArtMethod* method, size_t bytes) {
memory_use_.AddValue(bytes);
}
+void Jit::NotifyZygoteCompilationDone() {
+ if (fd_methods_ == -1) {
+ return;
+ }
+
+ size_t offset = 0;
+ for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) {
+ const ImageHeader& header = space->GetImageHeader();
+ const ImageSection& section = header.GetMethodsSection();
+ // Because mremap works at page boundaries, we can only handle methods
+ // within a page range. For methods that falls above or below the range,
+ // the child processes will copy their contents to their private mapping
+ // in `child_mapping_methods`. See `MapBootImageMethods`.
+ uint8_t* page_start = AlignUp(header.GetImageBegin() + section.Offset(), kPageSize);
+ uint8_t* page_end =
+ AlignDown(header.GetImageBegin() + section.Offset() + section.Size(), kPageSize);
+ if (page_end > page_start) {
+ uint64_t capacity = page_end - page_start;
+ memcpy(zygote_mapping_methods_.Begin() + offset, page_start, capacity);
+ offset += capacity;
+ }
+ }
+
+ // Do an msync to ensure we are not affected by writes still being in caches.
+ if (msync(zygote_mapping_methods_.Begin(), fd_methods_size_, MS_SYNC) != 0) {
+ PLOG(WARNING) << "Failed to sync boot image methods memory";
+ code_cache_->GetZygoteMap()->SetCompilationState(ZygoteCompilationState::kNotifiedFailure);
+ return;
+ }
+
+ // We don't need the shared mapping anymore, and we need to drop it in case
+ // the file hasn't been sealed writable.
+ zygote_mapping_methods_ = MemMap::Invalid();
+
+ // Seal writes now. Zygote and children will map the memory private in order
+ // to write to it.
+ if (fcntl(fd_methods_, F_ADD_SEALS, F_SEAL_SEAL | F_SEAL_WRITE) == -1) {
+ PLOG(WARNING) << "Failed to seal boot image methods file descriptor";
+ code_cache_->GetZygoteMap()->SetCompilationState(ZygoteCompilationState::kNotifiedFailure);
+ return;
+ }
+
+ std::string error_str;
+ MemMap child_mapping_methods = MemMap::MapFile(
+ fd_methods_size_,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE,
+ fd_methods_,
+ /* start= */ 0,
+ /* low_4gb= */ false,
+ "boot-image-methods",
+ &error_str);
+
+ if (!child_mapping_methods.IsValid()) {
+ LOG(WARNING) << "Failed to create child mapping of boot image methods: " << error_str;
+ code_cache_->GetZygoteMap()->SetCompilationState(ZygoteCompilationState::kNotifiedFailure);
+ return;
+ }
+
+ // Ensure the contents are the same as before: there was a window between
+ // the memcpy and the sealing where other processes could have changed the
+ // contents.
+ // Note this would not be needed if we could have used F_SEAL_FUTURE_WRITE,
+ // see b/143833776.
+ offset = 0;
+ for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) {
+ const ImageHeader& header = space->GetImageHeader();
+ const ImageSection& section = header.GetMethodsSection();
+ // Because mremap works at page boundaries, we can only handle methods
+ // within a page range. For methods that falls above or below the range,
+ // the child processes will copy their contents to their private mapping
+ // in `child_mapping_methods`. See `MapBootImageMethods`.
+ uint8_t* page_start = AlignUp(header.GetImageBegin() + section.Offset(), kPageSize);
+ uint8_t* page_end =
+ AlignDown(header.GetImageBegin() + section.Offset() + section.Size(), kPageSize);
+ if (page_end > page_start) {
+ uint64_t capacity = page_end - page_start;
+ if (memcmp(child_mapping_methods.Begin() + offset, page_start, capacity) != 0) {
+ LOG(WARNING) << "Contents differ in boot image methods data";
+ code_cache_->GetZygoteMap()->SetCompilationState(
+ ZygoteCompilationState::kNotifiedFailure);
+ return;
+ }
+ offset += capacity;
+ }
+ }
+
+ // Future spawned processes don't need the fd anymore.
+ fd_methods_.reset();
+
+ // In order to have the zygote and children share the memory, we also remap
+ // the memory into the zygote process.
+ offset = 0;
+ for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) {
+ const ImageHeader& header = space->GetImageHeader();
+ const ImageSection& section = header.GetMethodsSection();
+ // Because mremap works at page boundaries, we can only handle methods
+ // within a page range. For methods that falls above or below the range,
+ // the child processes will copy their contents to their private mapping
+ // in `child_mapping_methods`. See `MapBootImageMethods`.
+ uint8_t* page_start = AlignUp(header.GetImageBegin() + section.Offset(), kPageSize);
+ uint8_t* page_end =
+ AlignDown(header.GetImageBegin() + section.Offset() + section.Size(), kPageSize);
+ if (page_end > page_start) {
+ uint64_t capacity = page_end - page_start;
+ if (mremap(child_mapping_methods.Begin() + offset,
+ capacity,
+ capacity,
+ MREMAP_FIXED | MREMAP_MAYMOVE,
+ page_start) == MAP_FAILED) {
+ // Failing to remap is safe as the process will just use the old
+ // contents.
+ PLOG(WARNING) << "Failed mremap of boot image methods of " << space->GetImageFilename();
+ }
+ offset += capacity;
+ }
+ }
+
+ LOG(INFO) << "Successfully notified child processes on sharing boot image methods";
+
+ // Mark that compilation of boot classpath is done, and memory can now be
+ // shared. Other processes will pick up this information.
+ code_cache_->GetZygoteMap()->SetCompilationState(ZygoteCompilationState::kNotifiedOk);
+
+ // The private mapping created for this process has been mremaped. We can
+ // reset it.
+ child_mapping_methods.Reset();
+}
+
class JitCompileTask final : public Task {
public:
enum class TaskKind {
@@ -691,38 +823,9 @@ class JitDoneCompilingProfileTask final : public SelfDeletingTask {
}
if (Runtime::Current()->IsZygote()) {
- // Copy the boot image methods data to the mappings we created to share
- // with the children.
- Jit* jit = Runtime::Current()->GetJit();
- size_t offset = 0;
- for (gc::space::ImageSpace* space : Runtime::Current()->GetHeap()->GetBootImageSpaces()) {
- const ImageHeader& header = space->GetImageHeader();
- const ImageSection& section = header.GetMethodsSection();
- // Because mremap works at page boundaries, we can only handle methods
- // within a page range. For methods that falls above or below the range,
- // the child processes will copy their contents to their private mapping
- // in `child_mapping_methods_`. See `MapBootImageMethods`.
- uint8_t* page_start = AlignUp(header.GetImageBegin() + section.Offset(), kPageSize);
- uint8_t* page_end =
- AlignDown(header.GetImageBegin() + section.Offset() + section.Size(), kPageSize);
- if (page_end > page_start) {
- uint64_t capacity = page_end - page_start;
- memcpy(jit->GetZygoteMappingMethods().Begin() + offset, page_start, capacity);
- // So the memory is shared, also map the memory into the zygote
- // process.
- if (mremap(jit->GetChildMappingMethods().Begin() + offset,
- capacity,
- capacity,
- MREMAP_FIXED | MREMAP_MAYMOVE,
- page_start) == MAP_FAILED) {
- PLOG(WARNING) << "Failed mremap of boot image methods of " << space->GetImageFilename();
- }
- offset += capacity;
- }
- }
- // Mark that compilation of boot classpath is done. Other processes will
- // pick up this boolean.
- jit->GetCodeCache()->GetZygoteMap()->SetCompilationDone();
+ // Record that we are done compiling the profile.
+ Runtime::Current()->GetJit()->GetCodeCache()->GetZygoteMap()->SetCompilationState(
+ ZygoteCompilationState::kDone);
}
}
@@ -842,7 +945,28 @@ static void CopyIfDifferent(void* s1, const void* s2, size_t n) {
}
void Jit::MapBootImageMethods() {
- if (!GetChildMappingMethods().IsValid()) {
+ CHECK_NE(fd_methods_.get(), -1);
+ if (!code_cache_->GetZygoteMap()->CanMapBootImageMethods()) {
+ LOG(WARNING) << "Not mapping boot image methods due to error from zygote";
+ return;
+ }
+
+ std::string error_str;
+ MemMap child_mapping_methods = MemMap::MapFile(
+ fd_methods_size_,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE,
+ fd_methods_,
+ /* start= */ 0,
+ /* low_4gb= */ false,
+ "boot-image-methods",
+ &error_str);
+
+ // We don't need the fd anymore.
+ fd_methods_.reset();
+
+ if (!child_mapping_methods.IsValid()) {
+ LOG(WARNING) << "Failed to create child mapping of boot image methods: " << error_str;
return;
}
size_t offset = 0;
@@ -863,6 +987,10 @@ void Jit::MapBootImageMethods() {
// such methods, we need their entrypoints to be stubs that do the
// initialization check.
header.VisitPackedArtMethods([&](ArtMethod& method) NO_THREAD_SAFETY_ANALYSIS {
+ // Methods in the boot image should never have their single
+ // implementation flag set (and therefore never have a `data_` pointing
+ // to an ArtMethod for single implementation).
+ CHECK(method.IsIntrinsic() || !method.HasSingleImplementationFlag());
if (method.IsRuntimeMethod()) {
return;
}
@@ -898,7 +1026,7 @@ void Jit::MapBootImageMethods() {
// For all the methods in the mapping, put the entrypoint to the
// resolution stub.
ArtMethod* new_method = reinterpret_cast<ArtMethod*>(
- GetChildMappingMethods().Begin() + offset + (pointer - page_start));
+ child_mapping_methods.Begin() + offset + (pointer - page_start));
const void* code = new_method->GetEntryPointFromQuickCompiledCode();
if (!class_linker->IsQuickGenericJniStub(code) &&
!class_linker->IsQuickToInterpreterBridge(code) &&
@@ -918,7 +1046,7 @@ void Jit::MapBootImageMethods() {
// |/////////| -> copy -> |/////////|
// | | | |
//
- CopyIfDifferent(GetChildMappingMethods().Begin() + offset,
+ CopyIfDifferent(child_mapping_methods.Begin() + offset,
page_start,
pointer + sizeof(ArtMethod) - page_start);
} else if (pointer < page_end && (pointer + sizeof(ArtMethod)) > page_end) {
@@ -933,14 +1061,14 @@ void Jit::MapBootImageMethods() {
// section end --> -----------
//
size_t bytes_to_copy = (page_end - pointer);
- CopyIfDifferent(GetChildMappingMethods().Begin() + offset + capacity - bytes_to_copy,
+ CopyIfDifferent(child_mapping_methods.Begin() + offset + capacity - bytes_to_copy,
page_end - bytes_to_copy,
bytes_to_copy);
}
}, space->Begin(), kRuntimePointerSize);
// Map the memory in the boot image range.
- if (mremap(GetChildMappingMethods().Begin() + offset,
+ if (mremap(child_mapping_methods.Begin() + offset,
capacity,
capacity,
MREMAP_FIXED | MREMAP_MAYMOVE,
@@ -949,6 +1077,11 @@ void Jit::MapBootImageMethods() {
}
offset += capacity;
}
+
+ // The private mapping created for this process has been mremaped. We can
+ // reset it.
+ child_mapping_methods.Reset();
+ LOG(INFO) << "Successfully mapped boot image methods";
}
void Jit::CreateThreadPool() {
@@ -990,7 +1123,7 @@ void Jit::CreateThreadPool() {
// Start with '/boot' and end with '.art' to match the pattern recognized
// by android_os_Debug.cpp for boot images.
const char* name = "/boot-image-methods.art";
- unique_fd mem_fd = unique_fd(art::memfd_create(name, /* flags= */ 0));
+ unique_fd mem_fd = unique_fd(art::memfd_create(name, /* flags= */ MFD_ALLOW_SEALING));
if (mem_fd.get() == -1) {
PLOG(WARNING) << "Could not create boot image methods file descriptor";
return;
@@ -1000,6 +1133,9 @@ void Jit::CreateThreadPool() {
return;
}
std::string error_str;
+
+ // Create the shared mapping eagerly, as this prevents other processes
+ // from adding the writable seal.
zygote_mapping_methods_ = MemMap::MapFile(
total_capacity,
PROT_READ | PROT_WRITE,
@@ -1020,21 +1156,17 @@ void Jit::CreateThreadPool() {
return;
}
- child_mapping_methods_ = MemMap::MapFile(
- total_capacity,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE,
- mem_fd,
- /* start= */ 0,
- /* low_4gb= */ true,
- "boot-image-methods",
- &error_str);
-
- if (!child_mapping_methods_.IsValid()) {
- LOG(WARNING) << "Failed to create child mapping of boot image methods: " << error_str;
+ // We should use the F_SEAL_FUTURE_WRITE flag, but this has unexpected
+ // behavior on private mappings after fork (the mapping becomes shared between
+ // parent and children), see b/143833776.
+ // We will seal the write once we are done writing to the shared mapping.
+ if (fcntl(mem_fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW) == -1) {
+ PLOG(WARNING) << "Failed to seal boot image methods file descriptor";
zygote_mapping_methods_ = MemMap();
return;
}
+ fd_methods_ = unique_fd(mem_fd.release());
+ fd_methods_size_ = total_capacity;
}
}
}
@@ -1425,8 +1557,27 @@ static void* RunPollingThread(void* arg) {
Jit* jit = reinterpret_cast<Jit*>(arg);
do {
sleep(10);
- } while (!jit->GetCodeCache()->GetZygoteMap()->IsCompilationDone());
- jit->MapBootImageMethods();
+ } while (!jit->GetCodeCache()->GetZygoteMap()->IsCompilationNotified());
+
+ // We will suspend other threads: we can only do that if we're attached to the
+ // runtime.
+ Runtime* runtime = Runtime::Current();
+ bool thread_attached = runtime->AttachCurrentThread(
+ "BootImagePollingThread",
+ /* as_daemon= */ true,
+ /* thread_group= */ nullptr,
+ /* create_peer= */ false);
+ CHECK(thread_attached);
+
+ {
+ // Prevent other threads from running while we are remapping the boot image
+ // ArtMethod's. Native threads might still be running, but they cannot
+ // change the contents of ArtMethod's.
+ ScopedSuspendAll ssa(__FUNCTION__);
+ runtime->GetJit()->MapBootImageMethods();
+ }
+
+ Runtime::Current()->DetachCurrentThread();
return nullptr;
}
@@ -1437,8 +1588,7 @@ void Jit::PostForkChildAction(bool is_system_server, bool is_zygote) {
tasks_after_boot_.clear();
}
- if (Runtime::Current()->IsUsingApexBootImageLocation() &&
- !GetCodeCache()->GetZygoteMap()->IsCompilationDone()) {
+ if (Runtime::Current()->IsUsingApexBootImageLocation() && fd_methods_ != -1) {
// Create a thread that will poll the status of zygote compilation, and map
// the private mapping of boot image methods.
zygote_mapping_methods_.ResetInForkedProcess();
@@ -1491,6 +1641,15 @@ void Jit::PostZygoteFork() {
if (thread_pool_ == nullptr) {
return;
}
+ if (Runtime::Current()->IsZygote() &&
+ code_cache_->GetZygoteMap()->IsCompilationDoneButNotNotified()) {
+ // Copy the boot image methods data to the mappings we created to share
+ // with the children. We do this here as we are the only thread running and
+ // we don't risk other threads concurrently updating the ArtMethod's.
+ CHECK_EQ(GetTaskCount(), 1);
+ NotifyZygoteCompilationDone();
+ CHECK(code_cache_->GetZygoteMap()->IsCompilationNotified());
+ }
thread_pool_->CreateThreads();
}
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index 68aa1dc35b..e5b77c2c7e 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -17,6 +17,8 @@
#ifndef ART_RUNTIME_JIT_JIT_H_
#define ART_RUNTIME_JIT_JIT_H_
+#include <android-base/unique_fd.h>
+
#include "base/histogram-inl.h"
#include "base/macros.h"
#include "base/mutex.h"
@@ -374,16 +376,12 @@ class Jit {
bool CanAssumeInitialized(ObjPtr<mirror::Class> cls, bool is_for_shared_region) const
REQUIRES_SHARED(Locks::mutator_lock_);
- const MemMap& GetZygoteMappingMethods() const {
- return zygote_mapping_methods_;
- }
-
- const MemMap& GetChildMappingMethods() const {
- return child_mapping_methods_;
- }
-
// Map boot image methods after all compilation in zygote has been done.
- void MapBootImageMethods();
+ void MapBootImageMethods() REQUIRES(Locks::mutator_lock_);
+
+ // Notify to other processes that the zygote is done profile compiling boot
+ // class path methods.
+ void NotifyZygoteCompilationDone();
private:
Jit(JitCodeCache* code_cache, JitOptions* options);
@@ -434,16 +432,23 @@ class Jit {
// In the JIT zygote configuration, after all compilation is done, the zygote
// will copy its contents of the boot image to the zygote_mapping_methods_,
- // which will be picked up by processes that will map child_mapping_methods_
+ // which will be picked up by processes that will map the memory
// in-place within the boot image mapping.
//
- // zygote_mapping_methods_ and child_mapping_methods_ point to the same memory
- // (backed by a memfd). The difference between the two is that
// zygote_mapping_methods_ is shared memory only usable by the zygote and not
- // inherited by child processes. child_mapping_methods_ is a private mapping
- // that all processes will map.
+ // inherited by child processes. We create it eagerly to ensure other
+ // processes cannot seal writable the file.
MemMap zygote_mapping_methods_;
- MemMap child_mapping_methods_;
+
+ // The file descriptor created through memfd_create pointing to memory holding
+ // boot image methods. Created by the zygote, and inherited by child
+ // processes. The descriptor will be closed in each process (including the
+ // zygote) once they don't need it.
+ android::base::unique_fd fd_methods_;
+
+ // The size of the memory pointed by `fd_methods_`. Cached here to avoid
+ // recomputing it.
+ size_t fd_methods_size_;
DISALLOW_COPY_AND_ASSIGN(Jit);
};
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 6a13d59363..82ca44c2cd 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -1838,15 +1838,18 @@ void ZygoteMap::Initialize(uint32_t number_of_methods) {
// Allocate for 40-80% capacity. This will offer OK lookup times, and termination
// cases.
size_t capacity = RoundUpToPowerOfTwo(number_of_methods * 100 / 80);
- const Entry* data =
- reinterpret_cast<const Entry*>(region_->AllocateData(capacity * sizeof(Entry)));
- if (data != nullptr) {
- region_->FillData(data, capacity, Entry { nullptr, nullptr });
- map_ = ArrayRef(data, capacity);
+ const uint8_t* memory = region_->AllocateData(
+ capacity * sizeof(Entry) + sizeof(ZygoteCompilationState));
+ if (memory == nullptr) {
+ LOG(WARNING) << "Could not allocate data for the zygote map";
+ return;
}
- done_ = reinterpret_cast<const bool*>(region_->AllocateData(sizeof(bool)));
- CHECK(done_ != nullptr) << "Could not allocate a single boolean in the JIT region";
- region_->WriteData(done_, false);
+ const Entry* data = reinterpret_cast<const Entry*>(memory);
+ region_->FillData(data, capacity, Entry { nullptr, nullptr });
+ map_ = ArrayRef(data, capacity);
+ compilation_state_ = reinterpret_cast<const ZygoteCompilationState*>(
+ memory + capacity * sizeof(Entry));
+ region_->WriteData(compilation_state_, ZygoteCompilationState::kInProgress);
}
const void* ZygoteMap::GetCodeFor(ArtMethod* method, uintptr_t pc) const {
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 12425cf467..58cf0e36a7 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -79,6 +79,22 @@ class MarkCodeClosure;
// of garbage collecting code.
using CodeCacheBitmap = gc::accounting::MemoryRangeBitmap<kJitCodeAccountingBytes>;
+// The state of profile-based compilation in the zygote.
+// - kInProgress: JIT compilation is happening
+// - kDone: JIT compilation is finished, and the zygote is preparing notifying
+// the other processes.
+// - kNotifiedOk: the zygote has notified the other processes, which can start
+// sharing the boot image method mappings.
+// - kNotifiedFailure: the zygote has notified the other processes, but they
+// cannot share the boot image method mappings due to
+// unexpected errors
+enum class ZygoteCompilationState : uint8_t {
+ kInProgress = 0,
+ kDone = 1,
+ kNotifiedOk = 2,
+ kNotifiedFailure = 3,
+};
+
// Class abstraction over a map of ArtMethod -> compiled code, where the
// ArtMethod are compiled by the zygote, and the map acts as a communication
// channel between the zygote and the other processes.
@@ -88,7 +104,8 @@ using CodeCacheBitmap = gc::accounting::MemoryRangeBitmap<kJitCodeAccountingByte
// This map is writable only by the zygote, and readable by all children.
class ZygoteMap {
public:
- explicit ZygoteMap(JitMemoryRegion* region) : map_(), region_(region), done_(nullptr) {}
+ explicit ZygoteMap(JitMemoryRegion* region)
+ : map_(), region_(region), compilation_state_(nullptr) {}
// Initialize the data structure so it can hold `number_of_methods` mappings.
// Note that the map is fixed size and never grows.
@@ -106,12 +123,21 @@ class ZygoteMap {
return GetCodeFor(method) != nullptr;
}
- void SetCompilationDone() {
- region_->WriteData(done_, true);
+ void SetCompilationState(ZygoteCompilationState state) {
+ region_->WriteData(compilation_state_, state);
+ }
+
+ bool IsCompilationDoneButNotNotified() const {
+ return compilation_state_ != nullptr && *compilation_state_ == ZygoteCompilationState::kDone;
+ }
+
+ bool IsCompilationNotified() const {
+ return compilation_state_ != nullptr && *compilation_state_ > ZygoteCompilationState::kDone;
}
- bool IsCompilationDone() const {
- return *done_;
+ bool CanMapBootImageMethods() const {
+ return compilation_state_ != nullptr &&
+ *compilation_state_ == ZygoteCompilationState::kNotifiedOk;
}
private:
@@ -129,7 +155,9 @@ class ZygoteMap {
// The region in which the map is allocated.
JitMemoryRegion* const region_;
- const bool* done_;
+ // The current state of compilation in the zygote. Starts with kInProgress,
+ // and should end with kNotifiedOk or kNotifiedFailure.
+ const ZygoteCompilationState* compilation_state_;
DISALLOW_COPY_AND_ASSIGN(ZygoteMap);
};
diff --git a/runtime/jit/jit_memory_region.cc b/runtime/jit/jit_memory_region.cc
index 09980c8d36..7c7496b9e3 100644
--- a/runtime/jit/jit_memory_region.cc
+++ b/runtime/jit/jit_memory_region.cc
@@ -114,14 +114,12 @@ bool JitMemoryRegion::Initialize(size_t initial_capacity,
// +---------------+
// | non exec code |\
// +---------------+ \
- // | writable data |\ \
- // +---------------+ \ \
- // : :\ \ \
- // +---------------+.\.\.+---------------+
- // | exec code | \ \| code |
- // +---------------+...\.+---------------+
- // | readonly data | \| data |
- // +---------------+.....+---------------+
+ // : :\ \
+ // +---------------+.\.+---------------+
+ // | exec code | \| code |
+ // +---------------+...+---------------+
+ // | data | | data |
+ // +---------------+...+---------------+
//
// In this configuration code updates are written to the non-executable view of the code
// cache, and the executable view of the code cache has fixed RX memory protections.
@@ -134,7 +132,7 @@ bool JitMemoryRegion::Initialize(size_t initial_capacity,
base_flags = MAP_SHARED;
data_pages = MemMap::MapFile(
data_capacity + exec_capacity,
- kProtR,
+ is_zygote ? kProtR : kProtRW,
base_flags,
mem_fd,
/* start= */ 0,
@@ -216,34 +214,36 @@ bool JitMemoryRegion::Initialize(size_t initial_capacity,
return false;
}
}
- // Create a dual view of the data cache.
- name = data_cache_name + "-rw";
- writable_data_pages = MemMap::MapFile(data_capacity,
- kProtRW,
- base_flags,
- mem_fd,
- /* start= */ 0,
- /* low_4GB= */ false,
- name.c_str(),
- &error_str);
- if (!writable_data_pages.IsValid()) {
- std::ostringstream oss;
- oss << "Failed to create dual data view: " << error_str;
- *error_msg = oss.str();
- return false;
- }
- if (writable_data_pages.MadviseDontFork() != 0) {
- *error_msg = "Failed to madvise dont fork the writable data view";
- return false;
- }
- if (non_exec_pages.MadviseDontFork() != 0) {
- *error_msg = "Failed to madvise dont fork the writable code view";
- return false;
- }
- // Now that we have created the writable and executable mappings, prevent creating any new
- // ones.
- if (is_zygote && !ProtectZygoteMemory(mem_fd.get(), error_msg)) {
- return false;
+ // For the zygote, create a dual view of the data cache.
+ if (is_zygote) {
+ name = data_cache_name + "-rw";
+ writable_data_pages = MemMap::MapFile(data_capacity,
+ kProtRW,
+ base_flags,
+ mem_fd,
+ /* start= */ 0,
+ /* low_4GB= */ false,
+ name.c_str(),
+ &error_str);
+ if (!writable_data_pages.IsValid()) {
+ std::ostringstream oss;
+ oss << "Failed to create dual data view for zygote: " << error_str;
+ *error_msg = oss.str();
+ return false;
+ }
+ if (writable_data_pages.MadviseDontFork() != 0) {
+ *error_msg = "Failed to madvise dont fork the writable data view";
+ return false;
+ }
+ if (non_exec_pages.MadviseDontFork() != 0) {
+ *error_msg = "Failed to madvise dont fork the writable code view";
+ return false;
+ }
+ // Now that we have created the writable and executable mappings, prevent creating any new
+ // ones.
+ if (!ProtectZygoteMemory(mem_fd.get(), error_msg)) {
+ return false;
+ }
}
}
} else {
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 38bd611c38..2dc9f672dd 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -419,6 +419,7 @@ static inline bool IsCallerTransformer(Handle<mirror::MethodType> callsite_type)
static inline bool MethodHandleInvokeMethod(ArtMethod* called_method,
Handle<mirror::MethodType> callsite_type,
Handle<mirror::MethodType> target_type,
+ Handle<mirror::MethodType> nominal_type,
Thread* self,
ShadowFrame& shadow_frame,
const InstructionOperands* const operands,
@@ -543,6 +544,11 @@ static inline bool MethodHandleInvokeMethod(ArtMethod* called_method,
return false;
}
+ if (nominal_type != nullptr) {
+ return ConvertReturnValue(nominal_type, target_type, result) &&
+ ConvertReturnValue(callsite_type, nominal_type, result);
+ }
+
return ConvertReturnValue(callsite_type, target_type, result);
}
@@ -714,8 +720,9 @@ bool DoInvokePolymorphicMethod(Thread* self,
const InstructionOperands* const operands,
JValue* result)
REQUIRES_SHARED(Locks::mutator_lock_) {
- StackHandleScope<1> hs(self);
+ StackHandleScope<2> hs(self);
Handle<mirror::MethodType> handle_type(hs.NewHandle(method_handle->GetMethodType()));
+ Handle<mirror::MethodType> nominal_handle_type(hs.NewHandle(method_handle->GetNominalType()));
const mirror::MethodHandle::Kind handle_kind = method_handle->GetHandleKind();
DCHECK(IsInvoke(handle_kind));
@@ -761,6 +768,7 @@ bool DoInvokePolymorphicMethod(Thread* self,
return MethodHandleInvokeMethod(called_method,
callsite_type,
handle_type,
+ nominal_handle_type,
self,
shadow_frame,
operands,
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index a0e8a237c5..e1dd54f6f7 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -1040,6 +1040,24 @@ void Class::ClearSkipAccessChecksFlagOnAllMethods(PointerSize pointer_size) {
}
}
+void Class::ClearMustCountLocksFlagOnAllMethods(PointerSize pointer_size) {
+ DCHECK(IsVerified());
+ for (auto& m : GetMethods(pointer_size)) {
+ if (!m.IsNative() && m.IsInvokable()) {
+ m.ClearMustCountLocks();
+ }
+ }
+}
+
+void Class::ClearDontCompileFlagOnAllMethods(PointerSize pointer_size) {
+ DCHECK(IsVerified());
+ for (auto& m : GetMethods(pointer_size)) {
+ if (!m.IsNative() && m.IsInvokable()) {
+ m.ClearDontCompile();
+ }
+ }
+}
+
void Class::SetSkipAccessChecksFlagOnAllMethods(PointerSize pointer_size) {
DCHECK(IsVerified());
for (auto& m : GetMethods(pointer_size)) {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index d925a96d9c..15f7dc6177 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -1134,6 +1134,13 @@ class MANAGED Class final : public Object {
static ObjPtr<mirror::Class> GetPrimitiveClass(ObjPtr<mirror::String> name)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Clear the kAccMustCountLocks flag on each method, for class redefinition.
+ void ClearMustCountLocksFlagOnAllMethods(PointerSize pointer_size)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // Clear the kAccCompileDontBother flag on each method, for class redefinition.
+ void ClearDontCompileFlagOnAllMethods(PointerSize pointer_size)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// Clear the kAccSkipAccessChecks flag on each method, for class redefinition.
void ClearSkipAccessChecksFlagOnAllMethods(PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index c2c47a5187..96fc403690 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -176,19 +176,33 @@ void DexCache::InitializeDexCache(Thread* self,
void DexCache::VisitReflectiveTargets(ReflectiveValueVisitor* visitor) {
for (size_t i = 0; i < NumResolvedFields(); i++) {
auto pair(GetNativePairPtrSize(GetResolvedFields(), i, kRuntimePointerSize));
+ if (pair.index == FieldDexCachePair::InvalidIndexForSlot(i)) {
+ continue;
+ }
ArtField* new_val = visitor->VisitField(
pair.object, DexCacheSourceInfo(kSourceDexCacheResolvedField, pair.index, this));
if (UNLIKELY(new_val != pair.object)) {
- pair.object = new_val;
+ if (new_val == nullptr) {
+ pair = FieldDexCachePair(nullptr, FieldDexCachePair::InvalidIndexForSlot(i));
+ } else {
+ pair.object = new_val;
+ }
SetNativePairPtrSize(GetResolvedFields(), i, pair, kRuntimePointerSize);
}
}
for (size_t i = 0; i < NumResolvedMethods(); i++) {
auto pair(GetNativePairPtrSize(GetResolvedMethods(), i, kRuntimePointerSize));
+ if (pair.index == MethodDexCachePair::InvalidIndexForSlot(i)) {
+ continue;
+ }
ArtMethod* new_val = visitor->VisitMethod(
pair.object, DexCacheSourceInfo(kSourceDexCacheResolvedMethod, pair.index, this));
if (UNLIKELY(new_val != pair.object)) {
- pair.object = new_val;
+ if (new_val == nullptr) {
+ pair = MethodDexCachePair(nullptr, MethodDexCachePair::InvalidIndexForSlot(i));
+ } else {
+ pair.object = new_val;
+ }
SetNativePairPtrSize(GetResolvedMethods(), i, pair, kRuntimePointerSize);
}
}
diff --git a/runtime/mirror/method_handle_impl-inl.h b/runtime/mirror/method_handle_impl-inl.h
index 932b4343f3..27ccc53e83 100644
--- a/runtime/mirror/method_handle_impl-inl.h
+++ b/runtime/mirror/method_handle_impl-inl.h
@@ -33,12 +33,6 @@ inline ObjPtr<mirror::MethodType> MethodHandle::GetNominalType() {
return GetFieldObject<mirror::MethodType>(OFFSET_OF_OBJECT_MEMBER(MethodHandle, nominal_type_));
}
-inline ObjPtr<mirror::Class> MethodHandle::GetTargetClass() {
- Kind kind = GetHandleKind();
- return (kind < kFirstAccessorKind) ?
- GetTargetMethod()->GetDeclaringClass() : GetTargetField()->GetDeclaringClass();
-}
-
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/method_handle_impl.h b/runtime/mirror/method_handle_impl.h
index 54aa0c9bc2..a0f02f68c9 100644
--- a/runtime/mirror/method_handle_impl.h
+++ b/runtime/mirror/method_handle_impl.h
@@ -82,8 +82,6 @@ class MANAGED MethodHandle : public Object {
GetField64(OFFSET_OF_OBJECT_MEMBER(MethodHandle, art_field_or_method_)));
}
- ALWAYS_INLINE ObjPtr<mirror::Class> GetTargetClass() REQUIRES_SHARED(Locks::mutator_lock_);
-
// Gets the return type for a named invoke method, or nullptr if the invoke method is not
// supported.
static const char* GetReturnTypeDescriptor(const char* invoke_method_name);
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index 156895d03b..ce942c846d 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -134,24 +134,25 @@ static void CollectNonDebuggableClasses() REQUIRES(!Locks::mutator_lock_) {
// Must match values in com.android.internal.os.Zygote.
enum {
- DEBUG_ENABLE_JDWP = 1,
- DEBUG_ENABLE_CHECKJNI = 1 << 1,
- DEBUG_ENABLE_ASSERT = 1 << 2,
- DEBUG_ENABLE_SAFEMODE = 1 << 3,
- DEBUG_ENABLE_JNI_LOGGING = 1 << 4,
- DEBUG_GENERATE_DEBUG_INFO = 1 << 5,
- DEBUG_ALWAYS_JIT = 1 << 6,
- DEBUG_NATIVE_DEBUGGABLE = 1 << 7,
- DEBUG_JAVA_DEBUGGABLE = 1 << 8,
- DISABLE_VERIFIER = 1 << 9,
- ONLY_USE_SYSTEM_OAT_FILES = 1 << 10,
- DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 11,
- HIDDEN_API_ENFORCEMENT_POLICY_MASK = (1 << 12)
- | (1 << 13),
- PROFILE_SYSTEM_SERVER = 1 << 14,
- PROFILE_FROM_SHELL = 1 << 15,
- USE_APP_IMAGE_STARTUP_CACHE = 1 << 16,
- DEBUG_IGNORE_APP_SIGNAL_HANDLER = 1 << 17,
+ DEBUG_ENABLE_JDWP = 1,
+ DEBUG_ENABLE_CHECKJNI = 1 << 1,
+ DEBUG_ENABLE_ASSERT = 1 << 2,
+ DEBUG_ENABLE_SAFEMODE = 1 << 3,
+ DEBUG_ENABLE_JNI_LOGGING = 1 << 4,
+ DEBUG_GENERATE_DEBUG_INFO = 1 << 5,
+ DEBUG_ALWAYS_JIT = 1 << 6,
+ DEBUG_NATIVE_DEBUGGABLE = 1 << 7,
+ DEBUG_JAVA_DEBUGGABLE = 1 << 8,
+ DISABLE_VERIFIER = 1 << 9,
+ ONLY_USE_SYSTEM_OAT_FILES = 1 << 10,
+ DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 11,
+ HIDDEN_API_ENFORCEMENT_POLICY_MASK = (1 << 12)
+ | (1 << 13),
+ PROFILE_SYSTEM_SERVER = 1 << 14,
+ PROFILE_FROM_SHELL = 1 << 15,
+ USE_APP_IMAGE_STARTUP_CACHE = 1 << 16,
+ DEBUG_IGNORE_APP_SIGNAL_HANDLER = 1 << 17,
+ DISABLE_TEST_API_ENFORCEMENT_POLICY = 1 << 18,
// bits to shift (flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) by to get a value
// corresponding to hiddenapi::EnforcementPolicy
@@ -319,6 +320,13 @@ static void ZygoteHooks_nativePostForkChild(JNIEnv* env,
(runtime_flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) >> API_ENFORCEMENT_POLICY_SHIFT);
runtime_flags &= ~HIDDEN_API_ENFORCEMENT_POLICY_MASK;
+ if ((runtime_flags & DISABLE_TEST_API_ENFORCEMENT_POLICY) != 0u) {
+ runtime->SetTestApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kDisabled);
+ } else {
+ runtime->SetTestApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
+ }
+ runtime_flags &= ~DISABLE_TEST_API_ENFORCEMENT_POLICY;
+
bool profile_system_server = (runtime_flags & PROFILE_SYSTEM_SERVER) == PROFILE_SYSTEM_SERVER;
runtime_flags &= ~PROFILE_SYSTEM_SERVER;
diff --git a/runtime/oat.cc b/runtime/oat.cc
index db6cda5027..3fceec9364 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -411,9 +411,4 @@ void OatHeader::Flatten(const SafeMap<std::string, std::string>* key_value_store
key_value_store_size_ = data_ptr - reinterpret_cast<char*>(&key_value_store_);
}
-OatMethodOffsets::OatMethodOffsets(uint32_t code_offset) : code_offset_(code_offset) {
-}
-
-OatMethodOffsets::~OatMethodOffsets() {}
-
} // namespace art
diff --git a/runtime/oat.h b/runtime/oat.h
index 54d111cc31..352b9e892e 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@ class InstructionSetFeatures;
class PACKED(4) OatHeader {
public:
static constexpr std::array<uint8_t, 4> kOatMagic { { 'o', 'a', 't', '\n' } };
- // Last oat version changed reason: Optimize stack map decoding - interleave varints.
- static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '7', '3', '\0' } };
+ // Last oat version changed reason: Revert^4 Boot image extension.
+ static constexpr std::array<uint8_t, 4> kOatVersion { { '1', '7', '6', '\0' } };
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
static constexpr const char* kDebuggableKey = "debuggable";
@@ -134,31 +134,6 @@ class PACKED(4) OatHeader {
DISALLOW_COPY_AND_ASSIGN(OatHeader);
};
-// OatMethodOffsets are currently 5x32-bits=160-bits long, so if we can
-// save even one OatMethodOffsets struct, the more complicated encoding
-// using a bitmap pays for itself since few classes will have 160
-// methods.
-enum OatClassType {
- kOatClassAllCompiled = 0, // OatClass is followed by an OatMethodOffsets for each method.
- kOatClassSomeCompiled = 1, // A bitmap of which OatMethodOffsets are present follows the OatClass.
- kOatClassNoneCompiled = 2, // All methods are interpreted so no OatMethodOffsets are necessary.
- kOatClassMax = 3,
-};
-
-std::ostream& operator<<(std::ostream& os, const OatClassType& rhs);
-
-class PACKED(4) OatMethodOffsets {
- public:
- explicit OatMethodOffsets(uint32_t code_offset = 0);
-
- ~OatMethodOffsets();
-
- OatMethodOffsets(const OatMethodOffsets&) = default;
- OatMethodOffsets& operator=(const OatMethodOffsets&) = default;
-
- uint32_t code_offset_;
-};
-
} // namespace art
#endif // ART_RUNTIME_OAT_H_
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 4b5d5c328b..9ef5fbbbd3 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -855,14 +855,18 @@ bool OatFileBase::Setup(int zip_fd, const char* abs_dex_location, std::string* e
CheckedCall(mprotect, "protect relocations", reloc_begin, DataBimgRelRoSize(), PROT_READ);
// Make sure the file lists a boot image dependency, otherwise the .data.bimg.rel.ro
// section is bogus. The full dependency is checked before the code is executed.
- const char* boot_class_path_checksum =
- GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey);
- if (boot_class_path_checksum == nullptr ||
- boot_class_path_checksum[0] != gc::space::ImageSpace::kImageChecksumPrefix) {
- *error_msg = StringPrintf("Oat file '%s' contains .data.bimg.rel.ro section "
- "without boot image dependency.",
- GetLocation().c_str());
- return false;
+ // We cannot do this check if we do not have a key-value store, i.e. for secondary
+ // oat files for boot image extensions.
+ if (GetOatHeader().GetKeyValueStoreSize() != 0u) {
+ const char* boot_class_path_checksum =
+ GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey);
+ if (boot_class_path_checksum == nullptr ||
+ boot_class_path_checksum[0] != gc::space::ImageSpace::kImageChecksumPrefix) {
+ *error_msg = StringPrintf("Oat file '%s' contains .data.bimg.rel.ro section "
+ "without boot image dependency.",
+ GetLocation().c_str());
+ return false;
+ }
}
}
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 69d5efd4f4..70a9534824 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -34,7 +34,6 @@
#include "dex/utf.h"
#include "index_bss_mapping.h"
#include "mirror/object.h"
-#include "oat.h"
#include "runtime.h"
namespace art {
@@ -61,6 +60,31 @@ class DummyOatFile;
} // namespace collector
} // namespace gc
+// OatMethodOffsets are currently 5x32-bits=160-bits long, so if we can
+// save even one OatMethodOffsets struct, the more complicated encoding
+// using a bitmap pays for itself since few classes will have 160
+// methods.
+enum OatClassType {
+ kOatClassAllCompiled = 0, // OatClass is followed by an OatMethodOffsets for each method.
+ kOatClassSomeCompiled = 1, // A bitmap of OatMethodOffsets that are present follows the OatClass.
+ kOatClassNoneCompiled = 2, // All methods are interpreted so no OatMethodOffsets are necessary.
+ kOatClassMax = 3,
+};
+
+std::ostream& operator<<(std::ostream& os, const OatClassType& rhs);
+
+class PACKED(4) OatMethodOffsets {
+ public:
+ explicit OatMethodOffsets(uint32_t code_offset = 0) : code_offset_(code_offset) {}
+
+ ~OatMethodOffsets() {}
+
+ OatMethodOffsets(const OatMethodOffsets&) = default;
+ OatMethodOffsets& operator=(const OatMethodOffsets&) = default;
+
+ uint32_t code_offset_;
+};
+
// Runtime representation of the OAT file format which holds compiler output.
// The class opens an OAT file from storage and maps it to memory, typically with
// dlopen and provides access to its internal data structures (see OatWriter for
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index ab1389af56..48f17f01f0 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -604,78 +604,55 @@ const std::vector<uint32_t>* OatFileAssistant::GetRequiredDexChecksums() {
}
bool OatFileAssistant::ValidateBootClassPathChecksums(const OatFile& oat_file) {
- // Get the BCP from the oat file.
+ // Get the checksums and the BCP from the oat file.
+ const char* oat_boot_class_path_checksums =
+ oat_file.GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey);
const char* oat_boot_class_path =
oat_file.GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathKey);
- if (oat_boot_class_path == nullptr) {
+ if (oat_boot_class_path_checksums == nullptr || oat_boot_class_path == nullptr) {
return false;
}
-
- // Check that the oat BCP is a prefix of current BCP locations and count components.
- Runtime* runtime = Runtime::Current();
- size_t component_count = 0u;
- std::string_view remaining_bcp(oat_boot_class_path);
- bool bcp_ok = false;
- for (const std::string& location : runtime->GetBootClassPathLocations()) {
- if (!StartsWith(remaining_bcp, location)) {
- break;
- }
- remaining_bcp.remove_prefix(location.size());
- ++component_count;
- if (remaining_bcp.empty()) {
- bcp_ok = true;
- break;
- }
- if (!StartsWith(remaining_bcp, ":")) {
- break;
- }
- remaining_bcp.remove_prefix(1u);
- }
- if (!bcp_ok) {
- return false;
- }
-
- // Get the checksums.
- const char* oat_boot_class_path_checksums =
- oat_file.GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey);
- if (oat_boot_class_path_checksums == nullptr) {
- return false;
+ std::string_view oat_boot_class_path_checksums_view(oat_boot_class_path_checksums);
+ std::string_view oat_boot_class_path_view(oat_boot_class_path);
+ if (oat_boot_class_path_view == cached_boot_class_path_ &&
+ oat_boot_class_path_checksums_view == cached_boot_class_path_checksums_) {
+ return true;
}
- // Retrieve checksums for this portion of the BCP if we do not have them cached.
- if (cached_boot_class_path_checksum_component_count_ != component_count) {
- ArrayRef<const std::string> boot_class_path(runtime->GetBootClassPath());
- std::string error_msg;
- std::string boot_class_path_checksums = gc::space::ImageSpace::GetBootClassPathChecksums(
- boot_class_path.SubArray(/* pos= */ 0u, component_count),
- runtime->GetImageLocation(),
- isa_,
- runtime->GetImageSpaceLoadingOrder(),
- &error_msg);
- if (boot_class_path_checksums.empty()) {
- VLOG(oat) << "No image for oat image checksum to match against.";
-
- if (HasOriginalDexFiles()) {
- return false;
- }
-
- // If there is no original dex file to fall back to, grudgingly accept
- // the oat file. This could technically lead to crashes, but there's no
- // way we could find a better oat file to use for this dex location,
- // and it's better than being stuck in a boot loop with no way out.
- // The problem will hopefully resolve itself the next time the runtime
- // starts up.
- LOG(WARNING) << "Dex location " << dex_location_ << " does not seem to include dex file. "
- << "Allow oat file use. This is potentially dangerous.";
+ Runtime* runtime = Runtime::Current();
+ std::string error_msg;
+ bool result = gc::space::ImageSpace::VerifyBootClassPathChecksums(
+ oat_boot_class_path_checksums_view,
+ oat_boot_class_path_view,
+ runtime->GetImageLocation(),
+ ArrayRef<const std::string>(runtime->GetBootClassPathLocations()),
+ ArrayRef<const std::string>(runtime->GetBootClassPath()),
+ isa_,
+ runtime->GetImageSpaceLoadingOrder(),
+ &error_msg);
+ if (!result) {
+ VLOG(oat) << "Failed to verify checksums of oat file " << oat_file.GetLocation()
+ << " error: " << error_msg;
- return true;
+ if (HasOriginalDexFiles()) {
+ return false;
}
- cached_boot_class_path_checksum_component_count_ = component_count;
- cached_boot_class_path_checksums_ = boot_class_path_checksums;
+
+ // If there is no original dex file to fall back to, grudgingly accept
+ // the oat file. This could technically lead to crashes, but there's no
+ // way we could find a better oat file to use for this dex location,
+ // and it's better than being stuck in a boot loop with no way out.
+ // The problem will hopefully resolve itself the next time the runtime
+ // starts up.
+ LOG(WARNING) << "Dex location " << dex_location_ << " does not seem to include dex file. "
+ << "Allow oat file use. This is potentially dangerous.";
+ return true;
}
- // Compare the checksums.
- return cached_boot_class_path_checksums_ == oat_boot_class_path_checksums;
+ // This checksum has been validated, so save it.
+ cached_boot_class_path_ = oat_boot_class_path_view;
+ cached_boot_class_path_checksums_ = oat_boot_class_path_checksums_view;
+ return true;
}
OatFileAssistant::OatFileInfo& OatFileAssistant::GetBestInfo() {
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 1f3f74f611..b5812f9e25 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -439,7 +439,7 @@ class OatFileAssistant {
// File descriptor corresponding to apk, dex file, or zip.
int zip_fd_;
- size_t cached_boot_class_path_checksum_component_count_ = 0u;
+ std::string cached_boot_class_path_;
std::string cached_boot_class_path_checksums_;
friend class OatFileAssistantTest;
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 9a5409f46d..0412ab5909 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -34,6 +34,7 @@
#include "common_runtime_test.h"
#include "dexopt_test.h"
#include "hidden_api.h"
+#include "oat.h"
#include "oat_file.h"
#include "oat_file_manager.h"
#include "scoped_thread_state_change-inl.h"
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index e4c7dc3081..687ec544e3 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -473,7 +473,7 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
OatFileAssistant oat_file_assistant(dex_location,
kRuntimeISA,
- !runtime->IsAotCompiler(),
+ runtime->GetOatFilesExecutable(),
only_use_system_oat_files_);
// Get the oat file on disk.
diff --git a/runtime/reflective_value_visitor.h b/runtime/reflective_value_visitor.h
index 0b09a0bf8a..3a72760345 100644
--- a/runtime/reflective_value_visitor.h
+++ b/runtime/reflective_value_visitor.h
@@ -109,6 +109,10 @@ class ReflectionSourceInfo : public ValueObject {
os << "Type=" << type_;
}
+ ReflectionSourceType GetType() const {
+ return type_;
+ }
+
private:
const ReflectionSourceType type_;
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 597cd6bd2a..6d17204ef5 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -146,6 +146,7 @@
#include "native_bridge_art_interface.h"
#include "native_stack_dump.h"
#include "nativehelper/scoped_local_ref.h"
+#include "oat.h"
#include "oat_file.h"
#include "oat_file_manager.h"
#include "object_callbacks.h"
@@ -288,6 +289,7 @@ Runtime::Runtime()
safe_mode_(false),
hidden_api_policy_(hiddenapi::EnforcementPolicy::kDisabled),
core_platform_api_policy_(hiddenapi::EnforcementPolicy::kDisabled),
+ test_api_policy_(hiddenapi::EnforcementPolicy::kDisabled),
dedupe_hidden_api_warnings_(true),
hidden_api_access_event_log_rate_(0),
dump_native_stack_on_sig_quit_(true),
@@ -986,7 +988,6 @@ void Runtime::InitNonZygoteOrPostFork(
UnloadNativeBridge();
is_native_bridge_loaded_ = false;
break;
-
case NativeBridgeAction::kInitialize:
InitializeNativeBridge(env, isa);
break;
@@ -1861,6 +1862,16 @@ void Runtime::InitNativeMethods() {
// a regular JNI libraries with a regular JNI_OnLoad. Most JNI libraries can
// just use System.loadLibrary, but libcore can't because it's the library
// that implements System.loadLibrary!
+
+ // libicu_jni has to be initialized before libopenjdk{d} due to runtime dependency from
+ // libopenjdk{d} to Icu4cMetadata native methods in libicu_jni. See http://b/143888405
+ {
+ std::string error_msg;
+ if (!java_vm_->LoadNativeLibrary(
+ env, "libicu_jni.so", nullptr, WellKnownClasses::java_lang_Object, &error_msg)) {
+ LOG(FATAL) << "LoadNativeLibrary failed for \"libicu_jni.so\": " << error_msg;
+ }
+ }
{
std::string error_msg;
if (!java_vm_->LoadNativeLibrary(
@@ -1878,13 +1889,6 @@ void Runtime::InitNativeMethods() {
LOG(FATAL) << "LoadNativeLibrary failed for \"" << kOpenJdkLibrary << "\": " << error_msg;
}
}
- {
- std::string error_msg;
- if (!java_vm_->LoadNativeLibrary(
- env, "libicu_jni.so", nullptr, WellKnownClasses::java_lang_Object, &error_msg)) {
- LOG(FATAL) << "LoadNativeLibrary failed for \"libicu_jni.so\": " << error_msg;
- }
- }
// Initialize well known classes that may invoke runtime native methods.
WellKnownClasses::LateInit(env);
@@ -2231,7 +2235,8 @@ void Runtime::VisitImageRoots(RootVisitor* visitor) {
}
}
-static ArtMethod* CreateRuntimeMethod(ClassLinker* class_linker, LinearAlloc* linear_alloc) {
+static ArtMethod* CreateRuntimeMethod(ClassLinker* class_linker, LinearAlloc* linear_alloc)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
const PointerSize image_pointer_size = class_linker->GetImagePointerSize();
const size_t method_alignment = ArtMethod::Alignment(image_pointer_size);
const size_t method_size = ArtMethod::Size(image_pointer_size);
@@ -2964,4 +2969,8 @@ void Runtime::SetJniIdType(JniIdType t) {
WellKnownClasses::HandleJniIdTypeChange(Thread::Current()->GetJniEnv());
}
+bool Runtime::GetOatFilesExecutable() const {
+ return !IsAotCompiler() && !(IsSystemServer() && jit_options_->GetSaveProfilingInfo());
+}
+
} // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 4975e6588b..cfa67a81de 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -435,7 +435,7 @@ class Runtime {
imt_conflict_method_ = nullptr;
}
- void FixupConflictTables();
+ void FixupConflictTables() REQUIRES_SHARED(Locks::mutator_lock_);
void SetImtConflictMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
void SetImtUnimplementedMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -601,6 +601,14 @@ class Runtime {
return core_platform_api_policy_;
}
+ void SetTestApiEnforcementPolicy(hiddenapi::EnforcementPolicy policy) {
+ test_api_policy_ = policy;
+ }
+
+ hiddenapi::EnforcementPolicy GetTestApiEnforcementPolicy() const {
+ return test_api_policy_;
+ }
+
void SetHiddenApiExemptions(const std::vector<std::string>& exemptions) {
hidden_api_exemptions_ = exemptions;
}
@@ -942,6 +950,9 @@ class Runtime {
return verifier_missing_kthrow_fatal_;
}
+ // Return true if we should load oat files as executable or not.
+ bool GetOatFilesExecutable() const;
+
private:
static void InitPlatformSignalHandlers();
@@ -1215,6 +1226,9 @@ class Runtime {
// Whether access checks on core platform API should be performed.
hiddenapi::EnforcementPolicy core_platform_api_policy_;
+ // Whether access checks on test API should be performed.
+ hiddenapi::EnforcementPolicy test_api_policy_;
+
// List of signature prefixes of methods that have been removed from the blacklist, and treated
// as if whitelisted.
std::vector<std::string> hidden_api_exemptions_;
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 59a38e161d..36c35f85ba 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -2439,13 +2439,19 @@ void Thread::Destroy() {
{
ScopedObjectAccess soa(self);
Runtime::Current()->GetHeap()->RevokeThreadLocalBuffers(this);
- if (kUseReadBarrier) {
- Runtime::Current()->GetHeap()->ConcurrentCopyingCollector()->RevokeThreadLocalMarkStack(this);
- }
}
}
Thread::~Thread() {
+ if (kUseReadBarrier) {
+ // It's a cheap operation so can be done in the destructor (instead of
+ // Destroy()).
+ // Doing it without mutator_lock mutual exclusion is also necessary as there
+ // is a checkpoint in ConcurrentCopying which scans the threads' stacks,
+ // thereby assigning a thread-local mark-stack to self, breaking some
+ // assumptions about how the GC works (see b/140119552).
+ Runtime::Current()->GetHeap()->ConcurrentCopyingCollector()->RevokeThreadLocalMarkStack(this);
+ }
CHECK(tlsPtr_.class_loader_override == nullptr);
CHECK(tlsPtr_.jpeer == nullptr);
CHECK(tlsPtr_.opeer == nullptr);
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index e49092e0ec..e01d21ebe1 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -331,9 +331,16 @@ ArrayRef<const uint8_t> VdexFile::GetQuickenedInfoOf(const DexFile& dex_file,
static std::string ComputeBootClassPathChecksumString() {
Runtime* const runtime = Runtime::Current();
+ // Do not include boot image extension checksums, use their dex file checksums instead. Unlike
+ // oat files, vdex files do not reference anything in image spaces, so there is no reason why
+ // loading or not loading a boot image extension would affect the validity of the vdex file.
+ // Note: Update of a boot class path module such as conscrypt invalidates the vdex file anyway.
+ ArrayRef<gc::space::ImageSpace* const> image_spaces(runtime->GetHeap()->GetBootImageSpaces());
+ size_t boot_image_components =
+ image_spaces.empty() ? 0u : image_spaces[0]->GetImageHeader().GetComponentCount();
return gc::space::ImageSpace::GetBootClassPathChecksums(
- runtime->GetHeap()->GetBootImageSpaces(),
- runtime->GetClassLinker()->GetBootClassPath());
+ image_spaces.SubArray(/*pos=*/ 0u, boot_image_components),
+ ArrayRef<const DexFile* const>(runtime->GetClassLinker()->GetBootClassPath()));
}
static bool CreateDirectories(const std::string& child_path, /* out */ std::string* error_msg) {
diff --git a/runtime/verifier/class_verifier.cc b/runtime/verifier/class_verifier.cc
index fca9e16450..ed83652c8f 100644
--- a/runtime/verifier/class_verifier.cc
+++ b/runtime/verifier/class_verifier.cc
@@ -21,6 +21,8 @@
#include "art_method-inl.h"
#include "base/enums.h"
+#include "base/locks.h"
+#include "base/logging.h"
#include "base/systrace.h"
#include "base/utils.h"
#include "class_linker.h"
@@ -36,6 +38,8 @@
#include "mirror/dex_cache.h"
#include "runtime.h"
#include "thread.h"
+#include "verifier/method_verifier.h"
+#include "verifier/reg_type_cache.h"
namespace art {
namespace verifier {
@@ -46,6 +50,20 @@ using android::base::StringPrintf;
// sure we only print this once.
static bool gPrintedDxMonitorText = false;
+class StandardVerifyCallback : public VerifierCallback {
+ public:
+ void SetDontCompile(ArtMethod* m, bool value) override REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (value) {
+ m->SetDontCompile();
+ }
+ }
+ void SetMustCountLocks(ArtMethod* m, bool value) override REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (value) {
+ m->SetMustCountLocks();
+ }
+ }
+};
+
FailureKind ClassVerifier::ReverifyClass(Thread* self,
ObjPtr<mirror::Class> klass,
HardFailLogMode log_level,
@@ -54,19 +72,58 @@ FailureKind ClassVerifier::ReverifyClass(Thread* self,
DCHECK(!Runtime::Current()->IsAotCompiler());
StackHandleScope<1> hs(self);
Handle<mirror::Class> h_klass(hs.NewHandle(klass));
- ScopedAssertNoThreadSuspension sants(__FUNCTION__);
+ // We don't want to mess with these while other mutators are possibly looking at them. Instead we
+ // will wait until we can update them while everything is suspended.
+ class DelayedVerifyCallback : public VerifierCallback {
+ public:
+ void SetDontCompile(ArtMethod* m, bool value) override REQUIRES_SHARED(Locks::mutator_lock_) {
+ dont_compiles_.push_back({ m, value });
+ }
+ void SetMustCountLocks(ArtMethod* m, bool value) override
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ count_locks_.push_back({ m, value });
+ }
+ void UpdateFlags(bool skip_access_checks) REQUIRES(Locks::mutator_lock_) {
+ for (auto it : count_locks_) {
+ VLOG(verifier_debug) << "Setting " << it.first->PrettyMethod() << " count locks to "
+ << it.second;
+ if (it.second) {
+ it.first->SetMustCountLocks();
+ } else {
+ it.first->ClearMustCountLocks();
+ }
+ if (skip_access_checks && it.first->IsInvokable() && !it.first->IsNative()) {
+ it.first->SetSkipAccessChecks();
+ }
+ }
+ for (auto it : dont_compiles_) {
+ VLOG(verifier_debug) << "Setting " << it.first->PrettyMethod() << " dont-compile to "
+ << it.second;
+ if (it.second) {
+ it.first->SetDontCompile();
+ } else {
+ it.first->ClearDontCompile();
+ }
+ }
+ }
+
+ private:
+ std::vector<std::pair<ArtMethod*, bool>> dont_compiles_;
+ std::vector<std::pair<ArtMethod*, bool>> count_locks_;
+ };
+ DelayedVerifyCallback dvc;
FailureKind res = CommonVerifyClass(self,
h_klass.Get(),
/*callbacks=*/nullptr,
+ &dvc,
/*allow_soft_failures=*/false,
log_level,
api_level,
- /*can_allocate=*/ false,
error);
- if (res == FailureKind::kSoftFailure) {
- // We cannot skip access checks since there was a soft failure.
- h_klass->ClearSkipAccessChecksFlagOnAllMethods(kRuntimePointerSize);
- }
+ DCHECK_NE(res, FailureKind::kHardFailure);
+ ScopedThreadSuspension sts(Thread::Current(), ThreadState::kSuspended);
+ ScopedSuspendAll ssa("Update method flags for reverify");
+ dvc.UpdateFlags(res == FailureKind::kNoFailure);
return res;
}
@@ -80,22 +137,24 @@ FailureKind ClassVerifier::VerifyClass(Thread* self,
if (klass->IsVerified()) {
return FailureKind::kNoFailure;
}
+ StandardVerifyCallback svc;
return CommonVerifyClass(self,
klass,
callbacks,
+ &svc,
allow_soft_failures,
log_level,
api_level,
- /*can_allocate=*/ true,
error);
}
+
FailureKind ClassVerifier::CommonVerifyClass(Thread* self,
ObjPtr<mirror::Class> klass,
CompilerCallbacks* callbacks,
+ VerifierCallback* verifier_callback,
bool allow_soft_failures,
HardFailLogMode log_level,
uint32_t api_level,
- bool can_allocate,
std::string* error) {
bool early_failure = false;
std::string failure_message;
@@ -130,13 +189,14 @@ FailureKind ClassVerifier::CommonVerifyClass(Thread* self,
class_loader,
*class_def,
callbacks,
+ verifier_callback,
allow_soft_failures,
log_level,
api_level,
- can_allocate,
error);
}
+
FailureKind ClassVerifier::VerifyClass(Thread* self,
const DexFile* dex_file,
Handle<mirror::DexCache> dex_cache,
@@ -147,16 +207,17 @@ FailureKind ClassVerifier::VerifyClass(Thread* self,
HardFailLogMode log_level,
uint32_t api_level,
std::string* error) {
+ StandardVerifyCallback svc;
return VerifyClass(self,
dex_file,
dex_cache,
class_loader,
class_def,
callbacks,
+ &svc,
allow_soft_failures,
log_level,
api_level,
- /*can_allocate=*/ true,
error);
}
@@ -166,10 +227,10 @@ FailureKind ClassVerifier::VerifyClass(Thread* self,
Handle<mirror::ClassLoader> class_loader,
const dex::ClassDef& class_def,
CompilerCallbacks* callbacks,
+ VerifierCallback* verifier_callback,
bool allow_soft_failures,
HardFailLogMode log_level,
uint32_t api_level,
- bool can_allocate,
std::string* error) {
// A class must not be abstract and final.
if ((class_def.access_flags_ & (kAccAbstract | kAccFinal)) == (kAccAbstract | kAccFinal)) {
@@ -220,12 +281,12 @@ FailureKind ClassVerifier::VerifyClass(Thread* self,
resolved_method,
method.GetAccessFlags(),
callbacks,
+ verifier_callback,
allow_soft_failures,
log_level,
/*need_precise_constants=*/ false,
api_level,
Runtime::Current()->IsAotCompiler(),
- can_allocate,
&hard_failure_msg);
if (result.kind == FailureKind::kHardFailure) {
if (failure_data.kind == FailureKind::kHardFailure) {
diff --git a/runtime/verifier/class_verifier.h b/runtime/verifier/class_verifier.h
index c97ea24799..0b229662e8 100644
--- a/runtime/verifier/class_verifier.h
+++ b/runtime/verifier/class_verifier.h
@@ -25,6 +25,8 @@
#include "base/locks.h"
#include "handle.h"
#include "obj_ptr.h"
+#include "verifier/method_verifier.h"
+#include "verifier/reg_type_cache.h"
#include "verifier_enums.h"
namespace art {
@@ -50,14 +52,16 @@ namespace verifier {
// Verifier that ensures the complete class is OK.
class ClassVerifier {
public:
- // Redo verification on a loaded class. This is for use by class redefinition. Since the class is
- // already loaded and in use this can only be performed with the mutator lock held.
+ // Redo verification on a loaded class. This is for use by class redefinition. This must be called
+ // with all methods already having all of kAccDontCompile and kAccCountLocks and not having
+ // kAccSkipAccessChecks. This will remove some of these flags from the method. The caller must
+ // ensure this cannot race with other changes to the verification class flags.
static FailureKind ReverifyClass(Thread* self,
ObjPtr<mirror::Class> klass,
HardFailLogMode log_level,
uint32_t api_level,
std::string* error)
- REQUIRES(Locks::mutator_lock_);
+ REQUIRES_SHARED(Locks::mutator_lock_);
// Verify a class. Returns "kNoFailure" on success.
static FailureKind VerifyClass(Thread* self,
ObjPtr<mirror::Class> klass,
@@ -78,18 +82,6 @@ class ClassVerifier {
uint32_t api_level,
std::string* error)
REQUIRES_SHARED(Locks::mutator_lock_);
- static FailureKind VerifyClass(Thread* self,
- const DexFile* dex_file,
- Handle<mirror::DexCache> dex_cache,
- Handle<mirror::ClassLoader> class_loader,
- const dex::ClassDef& class_def,
- CompilerCallbacks* callbacks,
- bool allow_soft_failures,
- HardFailLogMode log_level,
- uint32_t api_level,
- bool can_allocate,
- std::string* error)
- REQUIRES_SHARED(Locks::mutator_lock_);
static void Init(ClassLinker* class_linker) REQUIRES_SHARED(Locks::mutator_lock_);
static void Shutdown();
@@ -101,13 +93,25 @@ class ClassVerifier {
static FailureKind CommonVerifyClass(Thread* self,
ObjPtr<mirror::Class> klass,
CompilerCallbacks* callbacks,
+ VerifierCallback* verifier_callback,
bool allow_soft_failures,
HardFailLogMode log_level,
uint32_t api_level,
- bool can_allocate,
std::string* error)
REQUIRES_SHARED(Locks::mutator_lock_);
+ static FailureKind VerifyClass(Thread* self,
+ const DexFile* dex_file,
+ Handle<mirror::DexCache> dex_cache,
+ Handle<mirror::ClassLoader> class_loader,
+ const dex::ClassDef& class_def,
+ CompilerCallbacks* callbacks,
+ VerifierCallback* verifier_callback,
+ bool allow_soft_failures,
+ HardFailLogMode log_level,
+ uint32_t api_level,
+ std::string* error)
+ REQUIRES_SHARED(Locks::mutator_lock_);
DISALLOW_COPY_AND_ASSIGN(ClassVerifier);
};
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 29bc40c30c..742f50dec8 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -5107,12 +5107,12 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self,
ArtMethod* method,
uint32_t method_access_flags,
CompilerCallbacks* callbacks,
+ VerifierCallback* verifier_callback,
bool allow_soft_failures,
HardFailLogMode log_level,
bool need_precise_constants,
uint32_t api_level,
bool aot_mode,
- bool allow_suspension,
std::string* hard_failure_msg) {
if (VLOG_IS_ON(verifier_debug)) {
return VerifyMethod<true>(self,
@@ -5127,12 +5127,12 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self,
method,
method_access_flags,
callbacks,
+ verifier_callback,
allow_soft_failures,
log_level,
need_precise_constants,
api_level,
aot_mode,
- allow_suspension,
hard_failure_msg);
} else {
return VerifyMethod<false>(self,
@@ -5147,12 +5147,12 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self,
method,
method_access_flags,
callbacks,
+ verifier_callback,
allow_soft_failures,
log_level,
need_precise_constants,
api_level,
aot_mode,
- allow_suspension,
hard_failure_msg);
}
}
@@ -5170,12 +5170,12 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self,
ArtMethod* method,
uint32_t method_access_flags,
CompilerCallbacks* callbacks,
+ VerifierCallback* verifier_callback,
bool allow_soft_failures,
HardFailLogMode log_level,
bool need_precise_constants,
uint32_t api_level,
bool aot_mode,
- bool allow_suspension,
std::string* hard_failure_msg) {
MethodVerifier::FailureData result;
uint64_t start_ns = kTimeVerifyMethod ? NanoTime() : 0;
@@ -5186,8 +5186,8 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self,
dex_file,
code_item,
method_idx,
- /* can_load_classes= */ allow_suspension,
- /* allow_thread_suspension= */ allow_suspension,
+ /* can_load_classes= */ true,
+ /* allow_thread_suspension= */ true,
allow_soft_failures,
aot_mode,
dex_cache,
@@ -5209,6 +5209,7 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self,
callbacks->MethodVerified(&verifier);
}
+ bool set_dont_compile = false;
if (verifier.failures_.size() != 0) {
if (VLOG_IS_ON(verifier)) {
verifier.DumpFailures(VLOG_STREAM(verifier) << "Soft verification failures in "
@@ -5221,12 +5222,12 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self,
result.kind = FailureKind::kSoftFailure;
if (method != nullptr &&
!CanCompilerHandleVerificationFailure(verifier.encountered_failure_types_)) {
- method->SetDontCompile();
+ set_dont_compile = true;
}
}
if (method != nullptr) {
if (verifier.HasInstructionThatWillThrow()) {
- method->SetDontCompile();
+ set_dont_compile = true;
if (aot_mode && (callbacks != nullptr) && !callbacks->IsBootImage()) {
// When compiling apps, make HasInstructionThatWillThrow a soft error to trigger
// re-verification at runtime.
@@ -5240,9 +5241,12 @@ MethodVerifier::FailureData MethodVerifier::VerifyMethod(Thread* self,
result.kind = FailureKind::kSoftFailure;
}
}
+ bool must_count_locks = false;
if ((verifier.encountered_failure_types_ & VerifyError::VERIFY_ERROR_LOCKING) != 0) {
- method->SetMustCountLocks();
+ must_count_locks = true;
}
+ verifier_callback->SetDontCompile(method, set_dont_compile);
+ verifier_callback->SetMustCountLocks(method, must_count_locks);
}
} else {
// Bad method data.
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 09d384a069..83dafd3975 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -72,6 +72,16 @@ enum RegisterTrackingMode {
kTrackRegsAll,
};
+// A class used by the verifier to tell users about what options need to be set for given methods.
+class VerifierCallback {
+ public:
+ virtual ~VerifierCallback() {}
+ virtual void SetDontCompile(ArtMethod* method, bool value)
+ REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+ virtual void SetMustCountLocks(ArtMethod* method, bool value)
+ REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+};
+
// A mapping from a dex pc to the register line statuses as they are immediately prior to the
// execution of that instruction.
class PcToRegisterLineTable {
@@ -248,12 +258,12 @@ class MethodVerifier {
ArtMethod* method,
uint32_t method_access_flags,
CompilerCallbacks* callbacks,
+ VerifierCallback* verifier_callback,
bool allow_soft_failures,
HardFailLogMode log_level,
bool need_precise_constants,
uint32_t api_level,
bool aot_mode,
- bool allow_suspension,
std::string* hard_failure_msg)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -270,12 +280,12 @@ class MethodVerifier {
ArtMethod* method,
uint32_t method_access_flags,
CompilerCallbacks* callbacks,
+ VerifierCallback* verifier_callback,
bool allow_soft_failures,
HardFailLogMode log_level,
bool need_precise_constants,
uint32_t api_level,
bool aot_mode,
- bool allow_suspension,
std::string* hard_failure_msg)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/test/1948-obsolete-const-method-handle/util-src/build-classes b/test/1948-obsolete-const-method-handle/util-src/build-classes
index 1b2d79a060..af22dd4df3 100755
--- a/test/1948-obsolete-const-method-handle/util-src/build-classes
+++ b/test/1948-obsolete-const-method-handle/util-src/build-classes
@@ -38,7 +38,7 @@ if [[ ! -d "${BUILD_PATH}" ]]; then
fi
# Build the initial class files.
-(cd "${SRC_PATH}" && javac -cp "${ASM_CLASSPATH}" -d "${BUILD_PATH}" Main.java art/*.java art/constmethodhandle/*.java) || fail "javac error"
+(cd "${SRC_PATH}" && javac -source 8 -target 8 -cp "${ASM_CLASSPATH}" -d "${BUILD_PATH}" Main.java art/*.java art/constmethodhandle/*.java) || fail "javac error"
# Modify the class files using ASM
(cd "${SCRIPT_PATH}" && java -cp "${ASM_CLASSPATH}:${BUILD_PATH}" art.constmethodhandle.TestGenerator "${BUILD_PATH}" "$D8") || fail "generator failure"
# Remove the modification classes. We don't need nor want them for the actual test.
diff --git a/test/1988-multi-structural-redefine/expected.txt b/test/1988-multi-structural-redefine/expected.txt
new file mode 100644
index 0000000000..00aea886c4
--- /dev/null
+++ b/test/1988-multi-structural-redefine/expected.txt
@@ -0,0 +1,5 @@
+hello - Transform 1
+hello - Transform 2
+Redefining both class art.Test1988$Transform1 and class art.Test1988$Transform2 to use each other.
+Transform1 says hi and Transform2 says bye!
+Transform2 says hi and Transform1 says bye!
diff --git a/test/1988-multi-structural-redefine/info.txt b/test/1988-multi-structural-redefine/info.txt
new file mode 100644
index 0000000000..875a5f6ec1
--- /dev/null
+++ b/test/1988-multi-structural-redefine/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/1988-multi-structural-redefine/run b/test/1988-multi-structural-redefine/run
new file mode 100755
index 0000000000..a36de16ea6
--- /dev/null
+++ b/test/1988-multi-structural-redefine/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2017 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.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti --android-runtime-option -Xopaque-jni-ids:true
diff --git a/test/1988-multi-structural-redefine/src/Main.java b/test/1988-multi-structural-redefine/src/Main.java
new file mode 100644
index 0000000000..7e95671cab
--- /dev/null
+++ b/test/1988-multi-structural-redefine/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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 Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1988.run();
+ }
+}
diff --git a/test/1988-multi-structural-redefine/src/art/Redefinition.java b/test/1988-multi-structural-redefine/src/art/Redefinition.java
new file mode 120000
index 0000000000..81eaf31bbb
--- /dev/null
+++ b/test/1988-multi-structural-redefine/src/art/Redefinition.java
@@ -0,0 +1 @@
+../../../jvmti-common/Redefinition.java \ No newline at end of file
diff --git a/test/1988-multi-structural-redefine/src/art/Test1988.java b/test/1988-multi-structural-redefine/src/art/Test1988.java
new file mode 100644
index 0000000000..6dab4daa20
--- /dev/null
+++ b/test/1988-multi-structural-redefine/src/art/Test1988.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2016 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 art;
+
+import java.util.Base64;
+
+public class Test1988 {
+ static class Transform1 {
+ public static void sayHi() {
+ System.out.println("hello - Transform 1");
+ }
+ }
+ static class Transform2 {
+ public static void sayHi() {
+ System.out.println("hello - Transform 2");
+ }
+ }
+
+ /** Base64 encoded dex file for
+ *
+ * static class Trasnform1 {
+ * public static void sayHi() {
+ * System.out.println("Transform1 says hi and " + Transform2.getBye());
+ * }
+ * public static String getBye() {
+ * return "Transform1 says bye!";
+ * }
+ * }
+ */
+ public static final byte[] T1_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQAU4pPI4BKgrMtz7s1Ogc8in1PQhazaRWBcBQAAcAAAAHhWNBIAAAAAAAAAAJgEAAAd" +
+ "AAAAcAAAAAsAAADkAAAABAAAABABAAABAAAAQAEAAAkAAABIAQAAAQAAAJABAACsAwAAsAEAAD4C" +
+ "AABGAgAASQIAAE0CAABoAgAAgwIAAJMCAAC3AgAA1wIAAO4CAAACAwAAFgMAADEDAABFAwAAVAMA" +
+ "AGADAAB2AwAAjwMAAJIDAACWAwAAowMAAKsDAACzAwAAuQMAAL4DAADHAwAAzgMAANgDAADfAwAA" +
+ "AwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACQAAAAoAAAALAAAADAAAABEAAAABAAAABwAAAAAAAAAC" +
+ "AAAACAAAADgCAAARAAAACgAAAAAAAAASAAAACgAAADgCAAAJAAUAFwAAAAAAAgAAAAAAAAAAABUA" +
+ "AAAAAAIAGQAAAAEAAAAVAAAABQADABgAAAAGAAIAAAAAAAgAAgAAAAAACAABABQAAAAIAAAAGgAA" +
+ "AAAAAAAAAAAABgAAAAAAAAANAAAAiAQAAGUEAAAAAAAAAQAAAAAAAAAqAgAAAwAAABoADwARAAAA" +
+ "AQABAAEAAAAmAgAABAAAAHAQBQAAAA4ABAAAAAIAAAAuAgAAGwAAAGIAAABxAAMAAAAMASICCABw" +
+ "EAYAAgAaAxAAbiAHADIAbiAHABIAbhAIAAIADAFuIAQAEAAOAAYADgALAA4ACAAOARoPAAAAAAEA" +
+ "AAAHAAY8aW5pdD4AAUwAAkxMABlMYXJ0L1Rlc3QxOTg4JFRyYW5zZm9ybTE7ABlMYXJ0L1Rlc3Qx" +
+ "OTg4JFRyYW5zZm9ybTI7AA5MYXJ0L1Rlc3QxOTg4OwAiTGRhbHZpay9hbm5vdGF0aW9uL0VuY2xv" +
+ "c2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABVMamF2YS9pby9Qcmlu" +
+ "dFN0cmVhbTsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABlMamF2YS9s" +
+ "YW5nL1N0cmluZ0J1aWxkZXI7ABJMamF2YS9sYW5nL1N5c3RlbTsADVRlc3QxOTg4LmphdmEAClRy" +
+ "YW5zZm9ybTEAFFRyYW5zZm9ybTEgc2F5cyBieWUhABdUcmFuc2Zvcm0xIHNheXMgaGkgYW5kIAAB" +
+ "VgACVkwAC2FjY2Vzc0ZsYWdzAAZhcHBlbmQABmdldEJ5ZQAEbmFtZQADb3V0AAdwcmludGxuAAVz" +
+ "YXlIaQAIdG9TdHJpbmcABXZhbHVlAHV+fkQ4eyJjb21waWxhdGlvbi1tb2RlIjoiZGVidWciLCJt" +
+ "aW4tYXBpIjoxLCJzaGEtMSI6IjY4NjQ4NTU3NTM0MDJiYmFjODk2Nzc2YjAzN2RlYmJjOTM4YzQ5" +
+ "NTMiLCJ2ZXJzaW9uIjoiMS43LjYtZGV2In0AAgMBGxgCAgQCEwQIFhcOAAADAACAgATIAwEJsAMB" +
+ "CeADAAAAAAACAAAAVgQAAFwEAAB8BAAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAd" +
+ "AAAAcAAAAAIAAAALAAAA5AAAAAMAAAAEAAAAEAEAAAQAAAABAAAAQAEAAAUAAAAJAAAASAEAAAYA" +
+ "AAABAAAAkAEAAAEgAAADAAAAsAEAAAMgAAADAAAAJgIAAAEQAAABAAAAOAIAAAIgAAAdAAAAPgIA" +
+ "AAQgAAACAAAAVgQAAAAgAAABAAAAZQQAAAMQAAACAAAAeAQAAAYgAAABAAAAiAQAAAAQAAABAAAA" +
+ "mAQAAA==");
+
+
+ /** Base64 encoded dex file for
+ *
+ * static class Trasnform2 {
+ * public static void sayHi() {
+ * System.out.println("Transform2 says hi and " + Transform1.getBye());
+ * }
+ * public static String getBye() {
+ * return "Transform2 says bye!";
+ * }
+ * }
+ */
+ public static final byte[] T2_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQD94cwR+R7Yw7VMom5CwuQd5mZlsV2xrVFcBQAAcAAAAHhWNBIAAAAAAAAAAJgEAAAd" +
+ "AAAAcAAAAAsAAADkAAAABAAAABABAAABAAAAQAEAAAkAAABIAQAAAQAAAJABAACsAwAAsAEAAD4C" +
+ "AABGAgAASQIAAE0CAABoAgAAgwIAAJMCAAC3AgAA1wIAAO4CAAACAwAAFgMAADEDAABFAwAAVAMA" +
+ "AGADAAB2AwAAjwMAAJIDAACWAwAAowMAAKsDAACzAwAAuQMAAL4DAADHAwAAzgMAANgDAADfAwAA" +
+ "AwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACQAAAAoAAAALAAAADAAAABEAAAABAAAABwAAAAAAAAAC" +
+ "AAAACAAAADgCAAARAAAACgAAAAAAAAASAAAACgAAADgCAAAJAAUAFwAAAAAAAAAVAAAAAQACAAAA" +
+ "AAABAAAAFQAAAAEAAgAZAAAABQADABgAAAAGAAIAAAAAAAgAAgAAAAAACAABABQAAAAIAAAAGgAA" +
+ "AAEAAAAAAAAABgAAAAAAAAANAAAAiAQAAGUEAAAAAAAAAQAAAAAAAAAqAgAAAwAAABoADwARAAAA" +
+ "AQABAAEAAAAmAgAABAAAAHAQBQAAAA4ABAAAAAIAAAAuAgAAGwAAAGIAAABxAAAAAAAMASICCABw" +
+ "EAYAAgAaAxAAbiAHADIAbiAHABIAbhAIAAIADAFuIAQAEAAOAA4ADgATAA4AEAAOARoPAAAAAAEA" +
+ "AAAHAAY8aW5pdD4AAUwAAkxMABlMYXJ0L1Rlc3QxOTg4JFRyYW5zZm9ybTE7ABlMYXJ0L1Rlc3Qx" +
+ "OTg4JFRyYW5zZm9ybTI7AA5MYXJ0L1Rlc3QxOTg4OwAiTGRhbHZpay9hbm5vdGF0aW9uL0VuY2xv" +
+ "c2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABVMamF2YS9pby9Qcmlu" +
+ "dFN0cmVhbTsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABlMamF2YS9s" +
+ "YW5nL1N0cmluZ0J1aWxkZXI7ABJMamF2YS9sYW5nL1N5c3RlbTsADVRlc3QxOTg4LmphdmEAClRy" +
+ "YW5zZm9ybTIAFFRyYW5zZm9ybTIgc2F5cyBieWUhABdUcmFuc2Zvcm0yIHNheXMgaGkgYW5kIAAB" +
+ "VgACVkwAC2FjY2Vzc0ZsYWdzAAZhcHBlbmQABmdldEJ5ZQAEbmFtZQADb3V0AAdwcmludGxuAAVz" +
+ "YXlIaQAIdG9TdHJpbmcABXZhbHVlAHV+fkQ4eyJjb21waWxhdGlvbi1tb2RlIjoiZGVidWciLCJt" +
+ "aW4tYXBpIjoxLCJzaGEtMSI6IjY4NjQ4NTU3NTM0MDJiYmFjODk2Nzc2YjAzN2RlYmJjOTM4YzQ5" +
+ "NTMiLCJ2ZXJzaW9uIjoiMS43LjYtZGV2In0AAgMBGxgCAgQCEwQIFhcOAAADAAGAgATIAwEJsAMB" +
+ "CeADAAAAAAACAAAAVgQAAFwEAAB8BAAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAd" +
+ "AAAAcAAAAAIAAAALAAAA5AAAAAMAAAAEAAAAEAEAAAQAAAABAAAAQAEAAAUAAAAJAAAASAEAAAYA" +
+ "AAABAAAAkAEAAAEgAAADAAAAsAEAAAMgAAADAAAAJgIAAAEQAAABAAAAOAIAAAIgAAAdAAAAPgIA" +
+ "AAQgAAACAAAAVgQAAAAgAAABAAAAZQQAAAMQAAACAAAAeAQAAAYgAAABAAAAiAQAAAAQAAABAAAA" +
+ "mAQAAA==");
+
+
+ public static void run() {
+ doTest();
+ }
+
+ public static void doTest() {
+ Transform1.sayHi();
+ Transform2.sayHi();
+ System.out.println(
+ "Redefining both " + Transform1.class + " and " + Transform2.class + " to use each other.");
+ Redefinition.doMultiStructuralClassRedefinition(
+ new Redefinition.DexOnlyClassDefinition(Transform1.class, T1_BYTES),
+ new Redefinition.DexOnlyClassDefinition(Transform2.class, T2_BYTES));
+ Transform1.sayHi();
+ Transform2.sayHi();
+ }
+}
diff --git a/test/1990-structural-bad-verify/expected.txt b/test/1990-structural-bad-verify/expected.txt
new file mode 100644
index 0000000000..7478dda33a
--- /dev/null
+++ b/test/1990-structural-bad-verify/expected.txt
@@ -0,0 +1,2 @@
+hello
+I say hello and you say goodbye!
diff --git a/test/1990-structural-bad-verify/info.txt b/test/1990-structural-bad-verify/info.txt
new file mode 100644
index 0000000000..f2ecd68701
--- /dev/null
+++ b/test/1990-structural-bad-verify/info.txt
@@ -0,0 +1,6 @@
+Tests basic functions in the jvmti plugin.
+
+b/142876078
+
+This tests a crash that could occur which was caused by the dex-cache being in an unexpected
+state.
diff --git a/test/1990-structural-bad-verify/run b/test/1990-structural-bad-verify/run
new file mode 100755
index 0000000000..03e41a58e7
--- /dev/null
+++ b/test/1990-structural-bad-verify/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1990-structural-bad-verify/src/Main.java b/test/1990-structural-bad-verify/src/Main.java
new file mode 100644
index 0000000000..5622925df1
--- /dev/null
+++ b/test/1990-structural-bad-verify/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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 Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1990.run();
+ }
+}
diff --git a/test/1990-structural-bad-verify/src/art/Redefinition.java b/test/1990-structural-bad-verify/src/art/Redefinition.java
new file mode 120000
index 0000000000..81eaf31bbb
--- /dev/null
+++ b/test/1990-structural-bad-verify/src/art/Redefinition.java
@@ -0,0 +1 @@
+../../../jvmti-common/Redefinition.java \ No newline at end of file
diff --git a/test/1990-structural-bad-verify/src/art/Test1990.java b/test/1990-structural-bad-verify/src/art/Test1990.java
new file mode 100644
index 0000000000..90034fca55
--- /dev/null
+++ b/test/1990-structural-bad-verify/src/art/Test1990.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2016 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 art;
+
+import java.util.Base64;
+public class Test1990 {
+
+ static class Transform {
+ public static void saySomething() {
+ System.out.println("hello");
+ }
+ }
+
+ /**
+ * base64 encoded class/dex file for
+ * static class Transform {
+ * public static void saySomething() {
+ * System.out.println("I say hello and " + sayGoodbye());
+ * }
+ * public static String sayGoodbye() {
+ * return "you say goodbye!";
+ * }
+ * }
+ */
+ // NB The actual dex codes are as follows. This is an explanation of the error this test checks.
+ //
+ // The exact order of instructions is important. Notice the 'invoke-static sayGoodbye'
+ // (instruction 0002) dominates the rest of the block. During the first (runnable) verification
+ // step the verifier will first check and verify there are no hard-failures in this class. Next it
+ // will realize it cannot find the sayGoodbye method on the loaded & resolved Transform class.
+ // This is (correctly) recognized as a soft-verification failure but then the verifier decides the
+ // rest of the method is dead-code. This means the verifier will not perform any of the
+ // soft-failure checks on the rest of the method (since control would never reach there).
+ //
+ // Later after performing the redefinition we do a reverify. At this time we held an exclusive
+ // mutator-lock though so it cannot resolve classes and will not add anything to the dex-cache.
+ // Here we can get past instruction 0002 and successfully determine the rest of the function is
+ // fine. In the process we filled in the methods into the dex-cache but not the classes. This
+ // caused this test to crash when run through the interpreter.
+ //
+ // #2 : (in Lart/Test1990$Transform;)
+ // name : 'saySomething'
+ // type : '()V'
+ // access : 0x0009 (PUBLIC STATIC)
+ // code -
+ // registers : 4
+ // ins : 0
+ // outs : 2
+ // insns size : 27 16-bit code units
+ // 0001d0: |[0001d0] art.Test1990$Transform.saySomething:()V
+ // 0001e0: 6200 0000 |0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream; // field@0000
+ // 0001e4: 7100 0100 0000 |0002: invoke-static {}, Lart/Test1990$Transform;.sayGoodbye:()Ljava/lang/String; // method@0001
+ // 0001ea: 0c01 |0005: move-result-object v1
+ // 0001ec: 2202 0700 |0006: new-instance v2, Ljava/lang/StringBuilder; // type@0007
+ // 0001f0: 7010 0500 0200 |0008: invoke-direct {v2}, Ljava/lang/StringBuilder;.<init>:()V // method@0005
+ // 0001f6: 1a03 0100 |000b: const-string v3, "I say hello and " // string@0001
+ // 0001fa: 6e20 0600 3200 |000d: invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0006
+ // 000200: 6e20 0600 1200 |0010: invoke-virtual {v2, v1}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // method@0006
+ // 000206: 6e10 0700 0200 |0013: invoke-virtual {v2}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String; // method@0007
+ // 00020c: 0c01 |0016: move-result-object v1
+ // 00020e: 6e20 0300 1000 |0017: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V // method@0003
+ // 000214: 0e00 |001a: return-void
+ // catches : (none)
+ // positions :
+ // 0x0000 line=5
+ // 0x001a line=6
+ // locals :
+
+ // Virtual methods -
+ // source_file_idx : 13 (Test1990.java)
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+"ZGV4CjAzNQCV0LekDslEGFglxYgCw7HSyxVegIDjERswBQAAcAAAAHhWNBIAAAAAAAAAAGwEAAAc" +
+"AAAAcAAAAAoAAADgAAAABAAAAAgBAAABAAAAOAEAAAgAAABAAQAAAQAAAIABAACQAwAAoAEAAC4C" +
+"AAA2AgAASAIAAEsCAABPAgAAaQIAAHkCAACdAgAAvQIAANQCAADoAgAA/AIAABcDAAArAwAAOgMA" +
+"AEUDAABIAwAATAMAAFkDAABhAwAAZwMAAGwDAAB1AwAAgQMAAI8DAACZAwAAoAMAALIDAAAEAAAA" +
+"BQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAAPAAAAAgAAAAYAAAAAAAAAAwAAAAcAAAAo" +
+"AgAADwAAAAkAAAAAAAAAEAAAAAkAAAAoAgAACAAEABQAAAAAAAIAAAAAAAAAAAAWAAAAAAACABcA" +
+"AAAEAAMAFQAAAAUAAgAAAAAABwACAAAAAAAHAAEAEgAAAAcAAAAYAAAAAAAAAAAAAAAFAAAAAAAA" +
+"AA0AAABcBAAAOQQAAAAAAAABAAAAAAAAABoCAAADAAAAGgAaABEAAAABAAEAAQAAABYCAAAEAAAA" +
+"cBAEAAAADgAEAAAAAgAAAB4CAAAbAAAAYgAAAHEAAQAAAAwBIgIHAHAQBQACABoDAQBuIAYAMgBu" +
+"IAYAEgBuEAcAAgAMAW4gAwAQAA4AAwAOAAgADgAFAA4BGg8AAAAAAQAAAAYABjxpbml0PgAQSSBz" +
+"YXkgaGVsbG8gYW5kIAABTAACTEwAGExhcnQvVGVzdDE5OTAkVHJhbnNmb3JtOwAOTGFydC9UZXN0" +
+"MTk5MDsAIkxkYWx2aWsvYW5ub3RhdGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3Rh" +
+"dGlvbi9Jbm5lckNsYXNzOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVj" +
+"dDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAZTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwASTGphdmEv" +
+"bGFuZy9TeXN0ZW07AA1UZXN0MTk5MC5qYXZhAAlUcmFuc2Zvcm0AAVYAAlZMAAthY2Nlc3NGbGFn" +
+"cwAGYXBwZW5kAARuYW1lAANvdXQAB3ByaW50bG4ACnNheUdvb2RieWUADHNheVNvbWV0aGluZwAI" +
+"dG9TdHJpbmcABXZhbHVlABB5b3Ugc2F5IGdvb2RieWUhAHZ+fkQ4eyJjb21waWxhdGlvbi1tb2Rl" +
+"IjoiZGVidWciLCJtaW4tYXBpIjoxLCJzaGEtMSI6IjYwZGE0ZDY3YjM4MWM0MjQ2Nzc1N2M0OWZi" +
+"NmU1NTc1NmQ4OGEyZjMiLCJ2ZXJzaW9uIjoiMS43LjEyLWRldiJ9AAICARkYAQIDAhEECBMXDgAA" +
+"AwAAgIAEuAMBCaADAQnQAwAAAAAAAgAAACoEAAAwBAAAUAQAAAAAAAAAAAAAAAAAABAAAAAAAAAA" +
+"AQAAAAAAAAABAAAAHAAAAHAAAAACAAAACgAAAOAAAAADAAAABAAAAAgBAAAEAAAAAQAAADgBAAAF" +
+"AAAACAAAAEABAAAGAAAAAQAAAIABAAABIAAAAwAAAKABAAADIAAAAwAAABYCAAABEAAAAQAAACgC" +
+"AAACIAAAHAAAAC4CAAAEIAAAAgAAACoEAAAAIAAAAQAAADkEAAADEAAAAgAAAEwEAAAGIAAAAQAA" +
+"AFwEAAAAEAAAAQAAAGwEAAA=");
+
+
+
+ public static void run() throws Exception {
+ Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+ doTest(new Transform());
+ }
+
+ public static void doTest(Transform t) throws Exception {
+ Transform.saySomething();
+ Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
+ Transform.saySomething();
+ }
+}
diff --git a/test/1991-hello-structural-retransform/expected.txt b/test/1991-hello-structural-retransform/expected.txt
new file mode 100644
index 0000000000..7478dda33a
--- /dev/null
+++ b/test/1991-hello-structural-retransform/expected.txt
@@ -0,0 +1,2 @@
+hello
+I say hello and you say goodbye!
diff --git a/test/1991-hello-structural-retransform/info.txt b/test/1991-hello-structural-retransform/info.txt
new file mode 100644
index 0000000000..875a5f6ec1
--- /dev/null
+++ b/test/1991-hello-structural-retransform/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/1991-hello-structural-retransform/run b/test/1991-hello-structural-retransform/run
new file mode 100755
index 0000000000..03e41a58e7
--- /dev/null
+++ b/test/1991-hello-structural-retransform/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1991-hello-structural-retransform/src/Main.java b/test/1991-hello-structural-retransform/src/Main.java
new file mode 100644
index 0000000000..531ca4afaf
--- /dev/null
+++ b/test/1991-hello-structural-retransform/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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 Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1991.run();
+ }
+}
diff --git a/test/1991-hello-structural-retransform/src/art/Redefinition.java b/test/1991-hello-structural-retransform/src/art/Redefinition.java
new file mode 120000
index 0000000000..81eaf31bbb
--- /dev/null
+++ b/test/1991-hello-structural-retransform/src/art/Redefinition.java
@@ -0,0 +1 @@
+../../../jvmti-common/Redefinition.java \ No newline at end of file
diff --git a/test/1991-hello-structural-retransform/src/art/Test1991.java b/test/1991-hello-structural-retransform/src/art/Test1991.java
new file mode 100644
index 0000000000..6060c202cd
--- /dev/null
+++ b/test/1991-hello-structural-retransform/src/art/Test1991.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 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 art;
+
+import java.util.Base64;
+public class Test1991 {
+
+ static class Transform {
+ public static void sayHi() {
+ System.out.println("hello");
+ }
+ }
+
+
+ /**
+ * base64 encoded class/dex file for
+ * static class Transform {
+ * public static void sayHi() {
+ * System.out.println("I say hello and " + sayGoodbye());
+ * }
+ * public static String sayGoodbye() {
+ * return "you say goodbye!";
+ * }
+ * }
+ */
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQCi0OGZvVpTRbHGfNbo3bfcu60kPpJayMgoBQAAcAAAAHhWNBIAAAAAAAAAAGQEAAAc" +
+ "AAAAcAAAAAoAAADgAAAABAAAAAgBAAABAAAAOAEAAAgAAABAAQAAAQAAAIABAACIAwAAoAEAAC4C" +
+ "AAA2AgAASAIAAEsCAABPAgAAaQIAAHkCAACdAgAAvQIAANQCAADoAgAA/AIAABcDAAArAwAAOgMA" +
+ "AEUDAABIAwAATAMAAFkDAABhAwAAZwMAAGwDAAB1AwAAgQMAAIgDAACSAwAAmQMAAKsDAAAEAAAA" +
+ "BQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAAPAAAAAgAAAAYAAAAAAAAAAwAAAAcAAAAo" +
+ "AgAADwAAAAkAAAAAAAAAEAAAAAkAAAAoAgAACAAEABQAAAAAAAIAAAAAAAAAAAAWAAAAAAACABcA" +
+ "AAAEAAMAFQAAAAUAAgAAAAAABwACAAAAAAAHAAEAEgAAAAcAAAAYAAAAAAAAAAAAAAAFAAAAAAAA" +
+ "AA0AAABUBAAAMgQAAAAAAAABAAAAAAAAABoCAAADAAAAGgAaABEAAAABAAEAAQAAABYCAAAEAAAA" +
+ "cBAEAAAADgAEAAAAAgAAAB4CAAAbAAAAYgAAAHEAAQAAAAwBIgIHAHAQBQACABoDAQBuIAYAMgBu" +
+ "IAYAEgBuEAcAAgAMAW4gAwAQAA4ABgAOAAsADgAIAA4BGg8AAAAAAQAAAAYABjxpbml0PgAQSSBz" +
+ "YXkgaGVsbG8gYW5kIAABTAACTEwAGExhcnQvVGVzdDE5OTEkVHJhbnNmb3JtOwAOTGFydC9UZXN0" +
+ "MTk5MTsAIkxkYWx2aWsvYW5ub3RhdGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3Rh" +
+ "dGlvbi9Jbm5lckNsYXNzOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVj" +
+ "dDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAZTGphdmEvbGFuZy9TdHJpbmdCdWlsZGVyOwASTGphdmEv" +
+ "bGFuZy9TeXN0ZW07AA1UZXN0MTk5MS5qYXZhAAlUcmFuc2Zvcm0AAVYAAlZMAAthY2Nlc3NGbGFn" +
+ "cwAGYXBwZW5kAARuYW1lAANvdXQAB3ByaW50bG4ACnNheUdvb2RieWUABXNheUhpAAh0b1N0cmlu" +
+ "ZwAFdmFsdWUAEHlvdSBzYXkgZ29vZGJ5ZSEAdn5+RDh7ImNvbXBpbGF0aW9uLW1vZGUiOiJkZWJ1" +
+ "ZyIsIm1pbi1hcGkiOjEsInNoYS0xIjoiNjBkYTRkNjdiMzgxYzQyNDY3NzU3YzQ5ZmI2ZTU1NzU2" +
+ "ZDg4YTJmMyIsInZlcnNpb24iOiIxLjcuMTItZGV2In0AAgIBGRgBAgMCEQQIExcOAAADAACAgAS4" +
+ "AwEJoAMBCdADAAAAAAIAAAAjBAAAKQQAAEgEAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAEAAAAAAAAA" +
+ "AQAAABwAAABwAAAAAgAAAAoAAADgAAAAAwAAAAQAAAAIAQAABAAAAAEAAAA4AQAABQAAAAgAAABA" +
+ "AQAABgAAAAEAAACAAQAAASAAAAMAAACgAQAAAyAAAAMAAAAWAgAAARAAAAEAAAAoAgAAAiAAABwA" +
+ "AAAuAgAABCAAAAIAAAAjBAAAACAAAAEAAAAyBAAAAxAAAAIAAABEBAAABiAAAAEAAABUBAAAABAA" +
+ "AAEAAABkBAAA");
+
+
+ public static void run() {
+ Redefinition.setTestConfiguration(Redefinition.Config.STRUCTURAL_TRANSFORM);
+ doTest();
+ }
+
+ public static void doTest() {
+ Transform.sayHi();
+ Redefinition.addCommonTransformationResult("art/Test1991$Transform", new byte[0], DEX_BYTES);
+ Redefinition.enableCommonRetransformation(true);
+ Redefinition.doCommonClassRetransformation(Transform.class);
+ Transform.sayHi();
+ }
+}
diff --git a/test/1992-retransform-no-such-field/expected.txt b/test/1992-retransform-no-such-field/expected.txt
new file mode 100644
index 0000000000..53de32a31a
--- /dev/null
+++ b/test/1992-retransform-no-such-field/expected.txt
@@ -0,0 +1,2 @@
+This file was written in the year 2019!
+This new class was written in 2019
diff --git a/test/1992-retransform-no-such-field/info.txt b/test/1992-retransform-no-such-field/info.txt
new file mode 100644
index 0000000000..875a5f6ec1
--- /dev/null
+++ b/test/1992-retransform-no-such-field/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/1992-retransform-no-such-field/run b/test/1992-retransform-no-such-field/run
new file mode 100755
index 0000000000..c6e62ae6cd
--- /dev/null
+++ b/test/1992-retransform-no-such-field/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-run "$@" --jvmti
diff --git a/test/1992-retransform-no-such-field/src/Main.java b/test/1992-retransform-no-such-field/src/Main.java
new file mode 100644
index 0000000000..ccffb88e7a
--- /dev/null
+++ b/test/1992-retransform-no-such-field/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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 Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1992.run();
+ }
+}
diff --git a/test/1992-retransform-no-such-field/src/art/Redefinition.java b/test/1992-retransform-no-such-field/src/art/Redefinition.java
new file mode 120000
index 0000000000..81eaf31bbb
--- /dev/null
+++ b/test/1992-retransform-no-such-field/src/art/Redefinition.java
@@ -0,0 +1 @@
+../../../jvmti-common/Redefinition.java \ No newline at end of file
diff --git a/test/1992-retransform-no-such-field/src/art/Test1992.java b/test/1992-retransform-no-such-field/src/art/Test1992.java
new file mode 100644
index 0000000000..5e1d5bb349
--- /dev/null
+++ b/test/1992-retransform-no-such-field/src/art/Test1992.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2019 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 art;
+
+import java.util.Base64;
+public class Test1992 {
+ public static boolean FAIL_IT = false;
+
+ static class Transform {
+ public void saySomething() {
+ System.out.println("This file was written in the year 2019!");
+ }
+ }
+
+ /**
+ * base64 encoded class/dex file for
+ * class Transform {
+ * public void saySomething() {
+ * if (Test1992.FAIL_IT) {
+ * // Force verification soft-fail.
+ * Test1992.NOT_THERE = 1;
+ * }
+ * System.out.println("This new class was written in " + year);
+ * }
+ * }
+ */
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+"yv66vgAAADUAKQoACAARCQASABMJABIAFAkAFQAWCAAXCgAYABkHABoHAB0BAAY8aW5pdD4BAAMo" +
+"KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAMc2F5U29tZXRoaW5nAQANU3RhY2tNYXBUYWJs" +
+"ZQEAClNvdXJjZUZpbGUBAA1UZXN0MTk5Mi5qYXZhDAAJAAoHAB4MAB8AIAwAIQAiBwAjDAAkACUB" +
+"ACJUaGlzIG5ldyBjbGFzcyB3YXMgd3JpdHRlbiBpbiAyMDE5BwAmDAAnACgBABZhcnQvVGVzdDE5" +
+"OTIkVHJhbnNmb3JtAQAJVHJhbnNmb3JtAQAMSW5uZXJDbGFzc2VzAQAQamF2YS9sYW5nL09iamVj" +
+"dAEADGFydC9UZXN0MTk5MgEAB0ZBSUxfSVQBAAFaAQAJTk9UX1RIRVJFAQABSQEAEGphdmEvbGFu" +
+"Zy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3Ry" +
+"ZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAgAAcACAAAAAAAAgAAAAkACgAB" +
+"AAsAAAAdAAEAAQAAAAUqtwABsQAAAAEADAAAAAYAAQAAAAUAAQANAAoAAQALAAAAQAACAAEAAAAT" +
+"sgACmQAHBLMAA7IABBIFtgAGsQAAAAIADAAAABIABAAAAAkABgAKAAoADAASAA0ADgAAAAMAAQoA" +
+"AgAPAAAAAgAQABwAAAAKAAEABwASABsACA==");
+
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+"ZGV4CjAzNQAWyivK2j0yR4u2YH/R8Bs3KIFv7O/Hs0WkBAAAcAAAAHhWNBIAAAAAAAAAAOADAAAZ" +
+"AAAAcAAAAAsAAADUAAAAAgAAAAABAAADAAAAGAEAAAQAAAAwAQAAAQAAAFABAAA0AwAAcAEAAMoB" +
+"AADSAQAA2wEAAN4BAAD4AQAACAIAACwCAABMAgAAYwIAAHcCAACLAgAAnwIAAKoCAAC5AgAA3QIA" +
+"AOgCAADrAgAA7wIAAPICAAD/AgAABQMAAAoDAAATAwAAIQMAACgDAAACAAAAAwAAAAQAAAAFAAAA" +
+"BgAAAAcAAAAIAAAACQAAAAoAAAAPAAAAEQAAAA8AAAAJAAAAAAAAABAAAAAJAAAAxAEAAAIACgAB" +
+"AAAAAgAAAAsAAAAIAAUAFAAAAAEAAAAAAAAAAQAAABYAAAAFAAEAFQAAAAYAAAAAAAAAAQAAAAAA" +
+"AAAGAAAAAAAAAAwAAADQAwAArwMAAAAAAAABAAEAAQAAALYBAAAEAAAAcBADAAAADgADAAEAAgAA" +
+"ALoBAAAPAAAAYwAAADgABQASEGcAAQBiAAIAGgENAG4gAgAQAA4ABQAOAAkADks9eAAAAAABAAAA" +
+"BwAGPGluaXQ+AAdGQUlMX0lUAAFJABhMYXJ0L1Rlc3QxOTkyJFRyYW5zZm9ybTsADkxhcnQvVGVz" +
+"dDE5OTI7ACJMZGFsdmlrL2Fubm90YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90" +
+"YXRpb24vSW5uZXJDbGFzczsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEvbGFuZy9PYmpl" +
+"Y3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVtOwAJTk9UX1RIRVJFAA1U" +
+"ZXN0MTk5Mi5qYXZhACJUaGlzIG5ldyBjbGFzcyB3YXMgd3JpdHRlbiBpbiAyMDE5AAlUcmFuc2Zv" +
+"cm0AAVYAAlZMAAFaAAthY2Nlc3NGbGFncwAEbmFtZQADb3V0AAdwcmludGxuAAxzYXlTb21ldGhp" +
+"bmcABXZhbHVlAHZ+fkQ4eyJjb21waWxhdGlvbi1tb2RlIjoiZGVidWciLCJtaW4tYXBpIjoxLCJz" +
+"aGEtMSI6IjYwZGE0ZDY3YjM4MWM0MjQ2Nzc1N2M0OWZiNmU1NTc1NmQ4OGEyZjMiLCJ2ZXJzaW9u" +
+"IjoiMS43LjEyLWRldiJ9AAIDARcYAgIEAhIECBMXDgAAAQEAgIAE8AIBAYgDAAAAAAAAAAIAAACg" +
+"AwAApgMAAMQDAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAEAAAAAAAAAAQAAABkAAABwAAAAAgAAAAsA" +
+"AADUAAAAAwAAAAIAAAAAAQAABAAAAAMAAAAYAQAABQAAAAQAAAAwAQAABgAAAAEAAABQAQAAASAA" +
+"AAIAAABwAQAAAyAAAAIAAAC2AQAAARAAAAEAAADEAQAAAiAAABkAAADKAQAABCAAAAIAAACgAwAA" +
+"ACAAAAEAAACvAwAAAxAAAAIAAADAAwAABiAAAAEAAADQAwAAABAAAAEAAADgAwAA");
+
+
+
+ public static void run() {
+ Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+ doTest(new Transform());
+ }
+
+ public static void doTest(Transform t) {
+ t.saySomething();
+ Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ t.saySomething();
+ }
+}
diff --git a/test/1993-fallback-non-structural/expected.txt b/test/1993-fallback-non-structural/expected.txt
new file mode 100644
index 0000000000..7e2cdf4ee5
--- /dev/null
+++ b/test/1993-fallback-non-structural/expected.txt
@@ -0,0 +1,3 @@
+Can structurally Redefine: false
+hello
+Goodbye
diff --git a/test/1993-fallback-non-structural/info.txt b/test/1993-fallback-non-structural/info.txt
new file mode 100644
index 0000000000..3b558e1cd1
--- /dev/null
+++ b/test/1993-fallback-non-structural/info.txt
@@ -0,0 +1,4 @@
+Tests basic functions in the jvmti plugin.
+
+Tests that using the structural redefinition functions will fall back to non-structural
+redefinition when possible.
diff --git a/test/1993-fallback-non-structural/run b/test/1993-fallback-non-structural/run
new file mode 100755
index 0000000000..03e41a58e7
--- /dev/null
+++ b/test/1993-fallback-non-structural/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1993-fallback-non-structural/src/Main.java b/test/1993-fallback-non-structural/src/Main.java
new file mode 100644
index 0000000000..61e060c773
--- /dev/null
+++ b/test/1993-fallback-non-structural/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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 Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1993.run();
+ }
+}
diff --git a/test/1993-fallback-non-structural/src/art/Redefinition.java b/test/1993-fallback-non-structural/src/art/Redefinition.java
new file mode 120000
index 0000000000..81eaf31bbb
--- /dev/null
+++ b/test/1993-fallback-non-structural/src/art/Redefinition.java
@@ -0,0 +1 @@
+../../../jvmti-common/Redefinition.java \ No newline at end of file
diff --git a/test/1993-fallback-non-structural/src/art/Test1993.java b/test/1993-fallback-non-structural/src/art/Test1993.java
new file mode 100644
index 0000000000..f4420993f7
--- /dev/null
+++ b/test/1993-fallback-non-structural/src/art/Test1993.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 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 art;
+
+import java.util.Base64;
+public class Test1993 {
+
+ static class Transform {
+ public void sayHi() {
+ // Use lower 'h' to make sure the string will have a different string id
+ // than the transformation (the transformation code is the same except
+ // the actual printed String, which was making the test inacurately passing
+ // in JIT mode when loading the string from the dex cache, as the string ids
+ // of the two different strings were the same).
+ // We know the string ids will be different because lexicographically:
+ // "Goodbye" < "LTransform;" < "hello".
+ System.out.println("hello");
+ }
+ }
+
+ /**
+ * base64 encoded class/dex file for
+ * class Transform {
+ * public void sayHi() {
+ * System.out.println("Goodbye");
+ * }
+ * }
+ */
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQDxrdbiBcsn0r58mdtcdyDxVUxwbWfShNQwBAAAcAAAAHhWNBIAAAAAAAAAAGwDAAAV" +
+ "AAAAcAAAAAkAAADEAAAAAgAAAOgAAAABAAAAAAEAAAQAAAAIAQAAAQAAACgBAADoAgAASAEAAJIB" +
+ "AACaAQAAowEAAL0BAADNAQAA8QEAABECAAAoAgAAPAIAAFACAABkAgAAcwIAAH4CAACBAgAAhQIA" +
+ "AJICAACYAgAAnQIAAKYCAACtAgAAtAIAAAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAgAAAAJAAAA" +
+ "DAAAAAwAAAAIAAAAAAAAAA0AAAAIAAAAjAEAAAcABAAQAAAAAAAAAAAAAAAAAAAAEgAAAAQAAQAR" +
+ "AAAABQAAAAAAAAAAAAAAAAAAAAUAAAAAAAAACgAAAFwDAAA7AwAAAAAAAAEAAQABAAAAgAEAAAQA" +
+ "AABwEAMAAAAOAAMAAQACAAAAhAEAAAgAAABiAAAAGgEBAG4gAgAQAA4AAwAOAAUADngAAAAAAQAA" +
+ "AAYABjxpbml0PgAHR29vZGJ5ZQAYTGFydC9UZXN0MTk5MyRUcmFuc2Zvcm07AA5MYXJ0L1Rlc3Qx" +
+ "OTkzOwAiTGRhbHZpay9hbm5vdGF0aW9uL0VuY2xvc2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0" +
+ "aW9uL0lubmVyQ2xhc3M7ABVMamF2YS9pby9QcmludFN0cmVhbTsAEkxqYXZhL2xhbmcvT2JqZWN0" +
+ "OwASTGphdmEvbGFuZy9TdHJpbmc7ABJMamF2YS9sYW5nL1N5c3RlbTsADVRlc3QxOTkzLmphdmEA" +
+ "CVRyYW5zZm9ybQABVgACVkwAC2FjY2Vzc0ZsYWdzAARuYW1lAANvdXQAB3ByaW50bG4ABXNheUhp" +
+ "AAV2YWx1ZQB2fn5EOHsiY29tcGlsYXRpb24tbW9kZSI6ImRlYnVnIiwibWluLWFwaSI6MSwic2hh" +
+ "LTEiOiJjZDkwMDIzOTMwZDk3M2Y1NzcxMWYxZDRmZGFhZDdhM2U0NzE0NjM3IiwidmVyc2lvbiI6" +
+ "IjEuNy4xNC1kZXYifQACAgETGAECAwIOBAgPFwsAAAEBAICABMgCAQHgAgAAAAAAAAACAAAALAMA" +
+ "ADIDAABQAwAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAVAAAAcAAAAAIAAAAJAAAA" +
+ "xAAAAAMAAAACAAAA6AAAAAQAAAABAAAAAAEAAAUAAAAEAAAACAEAAAYAAAABAAAAKAEAAAEgAAAC" +
+ "AAAASAEAAAMgAAACAAAAgAEAAAEQAAABAAAAjAEAAAIgAAAVAAAAkgEAAAQgAAACAAAALAMAAAAg" +
+ "AAABAAAAOwMAAAMQAAACAAAATAMAAAYgAAABAAAAXAMAAAAQAAABAAAAbAMAAA==");
+
+ public static void run() {
+ Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+ doTest(new Transform());
+ }
+
+ public static void doTest(Transform t) {
+ // TODO Remove this once the class is structurally modifiable.
+ System.out.println("Can structurally Redefine: " +
+ Redefinition.isStructurallyModifiable(Transform.class));
+ t.sayHi();
+ Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
+ t.sayHi();
+ }
+}
diff --git a/test/1997-structural-shadow-method/expected.txt b/test/1997-structural-shadow-method/expected.txt
new file mode 100644
index 0000000000..3a8b8de39b
--- /dev/null
+++ b/test/1997-structural-shadow-method/expected.txt
@@ -0,0 +1,6 @@
+Hello!
+Hello!
+Hello!
+Hello World!
+Hello World!
+Hello World!
diff --git a/test/1997-structural-shadow-method/info.txt b/test/1997-structural-shadow-method/info.txt
new file mode 100644
index 0000000000..71e3bfcd2e
--- /dev/null
+++ b/test/1997-structural-shadow-method/info.txt
@@ -0,0 +1 @@
+Test structural redefinition when the method being added was resolvable previously.
diff --git a/test/1997-structural-shadow-method/run b/test/1997-structural-shadow-method/run
new file mode 100755
index 0000000000..03e41a58e7
--- /dev/null
+++ b/test/1997-structural-shadow-method/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1997-structural-shadow-method/src/Main.java b/test/1997-structural-shadow-method/src/Main.java
new file mode 100644
index 0000000000..3c9bc85fb3
--- /dev/null
+++ b/test/1997-structural-shadow-method/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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 Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1997.run();
+ }
+}
diff --git a/test/1997-structural-shadow-method/src/art/Redefinition.java b/test/1997-structural-shadow-method/src/art/Redefinition.java
new file mode 120000
index 0000000000..81eaf31bbb
--- /dev/null
+++ b/test/1997-structural-shadow-method/src/art/Redefinition.java
@@ -0,0 +1 @@
+../../../jvmti-common/Redefinition.java \ No newline at end of file
diff --git a/test/1997-structural-shadow-method/src/art/Test1997.java b/test/1997-structural-shadow-method/src/art/Test1997.java
new file mode 100644
index 0000000000..7309a31bd3
--- /dev/null
+++ b/test/1997-structural-shadow-method/src/art/Test1997.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 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 art;
+
+import java.util.Base64;
+
+public class Test1997 {
+
+ public static class SuperTransform {
+ // We will be shadowing this function.
+ public static void sayHi() {
+ System.out.println("Hello!");
+ }
+ }
+
+ // The class we will be transforming.
+ public static class Transform extends SuperTransform {
+ public static void sayHiTwice() {
+ Transform.sayHi();
+ Transform.sayHi();
+ }
+ }
+
+ // public static class Transform extends SuperTransform {
+ // public static void sayHiTwice() {
+ // Transform.sayHi();
+ // Transform.sayHi();
+ // }
+ // public static void sayHi() {
+ // System.out.println("Hello World!");
+ // }
+ // }
+ private static final byte[] DEX_BYTES =
+ Base64.getDecoder()
+ .decode(
+ "ZGV4CjAzNQA9wdy7Lgbrv+sD+wixborREr0maZCK5yqABAAAcAAAAHhWNBIAAAAAAAAAALwDAAAW"
+ + "AAAAcAAAAAkAAADIAAAAAgAAAOwAAAABAAAABAEAAAUAAAAMAQAAAQAAADQBAAAsAwAAVAEAAMIB"
+ + "AADKAQAA2AEAAPcBAAARAgAAIQIAAEUCAABlAgAAfAIAAJACAACkAgAAswIAAL4CAADBAgAAxQIA"
+ + "ANICAADYAgAA3QIAAOYCAADtAgAA+QIAAAADAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAA"
+ + "CQAAAAwAAAAMAAAACAAAAAAAAAANAAAACAAAALwBAAAHAAUAEAAAAAAAAAAAAAAAAQAAAAAAAAAB"
+ + "AAAAEgAAAAEAAAATAAAABQABABEAAAABAAAAAQAAAAAAAAAAAAAACgAAAKwDAACHAwAAAAAAAAEA"
+ + "AQABAAAAqgEAAAQAAABwEAAAAAAOAAIAAAACAAAArgEAAAgAAABiAAAAGgEBAG4gBAAQAA4AAAAA"
+ + "AAAAAACzAQAABwAAAHEAAgAAAHEAAgAAAA4ADwAOABUADngAEQAOPDwAAAAAAQAAAAYABjxpbml0"
+ + "PgAMSGVsbG8gV29ybGQhAB1MYXJ0L1Rlc3QxOTk3JFN1cGVyVHJhbnNmb3JtOwAYTGFydC9UZXN0"
+ + "MTk5NyRUcmFuc2Zvcm07AA5MYXJ0L1Rlc3QxOTk3OwAiTGRhbHZpay9hbm5vdGF0aW9uL0VuY2xv"
+ + "c2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABVMamF2YS9pby9Qcmlu"
+ + "dFN0cmVhbTsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AA1UZXN0MTk5"
+ + "Ny5qYXZhAAlUcmFuc2Zvcm0AAVYAAlZMAAthY2Nlc3NGbGFncwAEbmFtZQADb3V0AAdwcmludGxu"
+ + "AAVzYXlIaQAKc2F5SGlUd2ljZQAFdmFsdWUAdn5+RDh7ImNvbXBpbGF0aW9uLW1vZGUiOiJkZWJ1"
+ + "ZyIsIm1pbi1hcGkiOjEsInNoYS0xIjoiNjBkYTRkNjdiMzgxYzQyNDY3NzU3YzQ5ZmI2ZTU1NzU2"
+ + "ZDg4YTJmMyIsInZlcnNpb24iOiIxLjcuMTItZGV2In0AAgMBFBgCAgQCDgQJDxcLAAADAAGBgATU"
+ + "AgEJ7AIBCYwDAAAAAAAAAAIAAAB4AwAAfgMAAKADAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAEAAAAA"
+ + "AAAAAQAAABYAAABwAAAAAgAAAAkAAADIAAAAAwAAAAIAAADsAAAABAAAAAEAAAAEAQAABQAAAAUA"
+ + "AAAMAQAABgAAAAEAAAA0AQAAASAAAAMAAABUAQAAAyAAAAMAAACqAQAAARAAAAEAAAC8AQAAAiAA"
+ + "ABYAAADCAQAABCAAAAIAAAB4AwAAACAAAAEAAACHAwAAAxAAAAIAAACcAwAABiAAAAEAAACsAwAA"
+ + "ABAAAAEAAAC8AwAA");
+
+ public static void run() throws Exception {
+ Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+ doTest();
+ }
+
+ public static void doTest() throws Exception {
+ Transform.sayHiTwice();
+ Transform.sayHi();
+ Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
+ Transform.sayHiTwice();
+ Transform.sayHi();
+ }
+}
diff --git a/test/1998-structural-shadow-field/expected.txt b/test/1998-structural-shadow-field/expected.txt
new file mode 100644
index 0000000000..9ae530e728
--- /dev/null
+++ b/test/1998-structural-shadow-field/expected.txt
@@ -0,0 +1,4 @@
+Hello
+Hello
+null
+Hello
diff --git a/test/1998-structural-shadow-field/info.txt b/test/1998-structural-shadow-field/info.txt
new file mode 100644
index 0000000000..71e3bfcd2e
--- /dev/null
+++ b/test/1998-structural-shadow-field/info.txt
@@ -0,0 +1 @@
+Test structural redefinition when the method being added was resolvable previously.
diff --git a/test/1998-structural-shadow-field/run b/test/1998-structural-shadow-field/run
new file mode 100755
index 0000000000..03e41a58e7
--- /dev/null
+++ b/test/1998-structural-shadow-field/run
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 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.
+
+./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1998-structural-shadow-field/src/Main.java b/test/1998-structural-shadow-field/src/Main.java
new file mode 100644
index 0000000000..f6aeca5b85
--- /dev/null
+++ b/test/1998-structural-shadow-field/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 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 Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1998.run();
+ }
+}
diff --git a/test/1998-structural-shadow-field/src/art/Redefinition.java b/test/1998-structural-shadow-field/src/art/Redefinition.java
new file mode 120000
index 0000000000..81eaf31bbb
--- /dev/null
+++ b/test/1998-structural-shadow-field/src/art/Redefinition.java
@@ -0,0 +1 @@
+../../../jvmti-common/Redefinition.java \ No newline at end of file
diff --git a/test/1998-structural-shadow-field/src/art/Test1998.java b/test/1998-structural-shadow-field/src/art/Test1998.java
new file mode 100644
index 0000000000..3fda936f84
--- /dev/null
+++ b/test/1998-structural-shadow-field/src/art/Test1998.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 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 art;
+
+import java.util.Base64;
+
+public class Test1998 {
+
+ public static class SuperTransform {
+ public static String greeting = "Hello";
+ }
+
+ // The class we will be transforming.
+ public static class Transform extends SuperTransform { }
+
+ // public static class Transform extends SuperTransform {
+ // public static String greeting;
+ // }
+ private static final byte[] DEX_BYTES =
+ Base64.getDecoder()
+ .decode(
+"ZGV4CjAzNQCYmnoWz4BqygrZQM4zf/mJ/25+dM86MHKAAwAAcAAAAHhWNBIAAAAAAAAAAMgCAAAP" +
+"AAAAcAAAAAcAAACsAAAAAQAAAMgAAAABAAAA1AAAAAIAAADcAAAAAQAAAOwAAAB0AgAADAEAACgB" +
+"AAAwAQAATwEAAGkBAAB5AQAAnQEAAL0BAADRAQAA4AEAAOsBAADuAQAA+wEAAAUCAAALAgAAEgIA" +
+"AAEAAAACAAAAAwAAAAQAAAAFAAAABgAAAAkAAAAJAAAABgAAAAAAAAABAAUACwAAAAAAAAAAAAAA" +
+"AQAAAAAAAAABAAAAAQAAAAAAAAAAAAAABwAAALgCAACZAgAAAAAAAAEAAQABAAAAJAEAAAQAAABw" +
+"EAAAAAAOAAUADgAGPGluaXQ+AB1MYXJ0L1Rlc3QxOTk4JFN1cGVyVHJhbnNmb3JtOwAYTGFydC9U" +
+"ZXN0MTk5OCRUcmFuc2Zvcm07AA5MYXJ0L1Rlc3QxOTk4OwAiTGRhbHZpay9hbm5vdGF0aW9uL0Vu" +
+"Y2xvc2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABJMamF2YS9sYW5n" +
+"L1N0cmluZzsADVRlc3QxOTk4LmphdmEACVRyYW5zZm9ybQABVgALYWNjZXNzRmxhZ3MACGdyZWV0" +
+"aW5nAARuYW1lAAV2YWx1ZQB2fn5EOHsiY29tcGlsYXRpb24tbW9kZSI6ImRlYnVnIiwibWluLWFw" +
+"aSI6MSwic2hhLTEiOiI2MGRhNGQ2N2IzODFjNDI0Njc3NTdjNDlmYjZlNTU3NTZkODhhMmYzIiwi" +
+"dmVyc2lvbiI6IjEuNy4xMi1kZXYifQACAwENGAICBAIKBAkMFwgBAAEAAAkBgYAEjAIAAAAAAAAA" +
+"AgAAAIoCAACQAgAArAIAAAAAAAAAAAAAAAAAAA8AAAAAAAAAAQAAAAAAAAABAAAADwAAAHAAAAAC" +
+"AAAABwAAAKwAAAADAAAAAQAAAMgAAAAEAAAAAQAAANQAAAAFAAAAAgAAANwAAAAGAAAAAQAAAOwA" +
+"AAABIAAAAQAAAAwBAAADIAAAAQAAACQBAAACIAAADwAAACgBAAAEIAAAAgAAAIoCAAAAIAAAAQAA" +
+"AJkCAAADEAAAAgAAAKgCAAAGIAAAAQAAALgCAAAAEAAAAQAAAMgCAAA=");
+
+ public static void run() throws Exception {
+ Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+ doTest();
+ }
+
+ public static void doTest() throws Exception {
+ System.out.println(Transform.greeting);
+ System.out.println(SuperTransform.greeting);
+ Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
+ System.out.println(Transform.greeting);
+ System.out.println(SuperTransform.greeting);
+ }
+}
diff --git a/test/580-fp16/src-art/Main.java b/test/580-fp16/src-art/Main.java
index 798b52dd34..4aa8d55faa 100644
--- a/test/580-fp16/src-art/Main.java
+++ b/test/580-fp16/src-art/Main.java
@@ -28,27 +28,71 @@ public class Main {
return Float.floatToRawIntBits(f);
}
- public static void assertEquals(int expected, int actual) {
- if (expected != actual) {
- throw new Error("Expected: " + expected + ", found: " + actual);
+ public static void assertEquals(short expected, short calculated) {
+ if (expected != calculated) {
+ throw new Error("Expected: " + expected + ", Calculated: " + calculated);
}
}
-
- public static void assertEquals(float expected, float actual) {
- if (expected != actual) {
- throw new Error("Expected: " + expected + ", found: " + actual);
+ public static void assertEquals(float expected, float calculated) {
+ if (expected != calculated) {
+ throw new Error("Expected: " + expected + ", Calculated: " + calculated);
}
}
- public static void main(String args[]) {
- // Test FP16 to float
+ public static void testHalfToFloatToHalfConversions(){
+ // Test FP16 to float and back to Half for all possible Short values
for (short h = Short.MIN_VALUE; h < Short.MAX_VALUE; h++) {
if (FP16.isNaN(h)) {
// NaN inputs are tested below.
continue;
}
- assertEquals(FP16.toHalf(FP16.toFloat(h)), h);
+ assertEquals(h, FP16.toHalf(FP16.toFloat(h)));
}
+ }
+
+ public static void testToHalf(){
+ // These asserts check some known values and edge cases for FP16.toHalf
+ // and have been inspired by the cts HalfTest.
+ // Zeroes, NaN and infinities
+ assertEquals(FP16.POSITIVE_ZERO, FP16.toHalf(0.0f));
+ assertEquals(FP16.NEGATIVE_ZERO, FP16.toHalf(-0.0f));
+ assertEquals(FP16.NaN, FP16.toHalf(Float.NaN));
+ assertEquals(FP16.POSITIVE_INFINITY, FP16.toHalf(Float.POSITIVE_INFINITY));
+ assertEquals(FP16.NEGATIVE_INFINITY, FP16.toHalf(Float.NEGATIVE_INFINITY));
+ // Known values
+ assertEquals((short) 0x3c01, FP16.toHalf(1.0009765625f));
+ assertEquals((short) 0xc000, FP16.toHalf(-2.0f));
+ assertEquals((short) 0x0400, FP16.toHalf(6.10352e-5f));
+ assertEquals((short) 0x7bff, FP16.toHalf(65504.0f));
+ assertEquals((short) 0x3555, FP16.toHalf(1.0f / 3.0f));
+ // Subnormals
+ assertEquals((short) 0x03ff, FP16.toHalf(6.09756e-5f));
+ assertEquals(FP16.MIN_VALUE, FP16.toHalf(5.96046e-8f));
+ assertEquals((short) 0x83ff, FP16.toHalf(-6.09756e-5f));
+ assertEquals((short) 0x8001, FP16.toHalf(-5.96046e-8f));
+ // Subnormals (flushed to +/-0)
+ assertEquals(FP16.POSITIVE_ZERO, FP16.toHalf(5.96046e-9f));
+ assertEquals(FP16.NEGATIVE_ZERO, FP16.toHalf(-5.96046e-9f));
+ // Test for values that overflow the mantissa bits into exp bits
+ assertEquals(0x1000, FP16.toHalf(Float.intBitsToFloat(0x39fff000)));
+ assertEquals(0x0400, FP16.toHalf(Float.intBitsToFloat(0x387fe000)));
+ // Floats with absolute value above +/-65519 are rounded to +/-inf
+ // when using round-to-even
+ assertEquals(0x7bff, FP16.toHalf(65519.0f));
+ assertEquals(0x7bff, FP16.toHalf(65519.9f));
+ assertEquals(FP16.POSITIVE_INFINITY, FP16.toHalf(65520.0f));
+ assertEquals(FP16.NEGATIVE_INFINITY, FP16.toHalf(-65520.0f));
+ // Check if numbers are rounded to nearest even when they
+ // cannot be accurately represented by Half
+ assertEquals(0x6800, FP16.toHalf(2049.0f));
+ assertEquals(0x6c00, FP16.toHalf(4098.0f));
+ assertEquals(0x7000, FP16.toHalf(8196.0f));
+ assertEquals(0x7400, FP16.toHalf(16392.0f));
+ assertEquals(0x7800, FP16.toHalf(32784.0f));
+
+ }
+
+ public static void testToFloat(){
// FP16 SNaN/QNaN inputs to float
// The most significant bit of mantissa:
// V
@@ -67,4 +111,10 @@ public class Main {
assertEquals(0xffc00000, TestFP16ToFloatRawIntBits((short)(0xfe00))); // QNaN->QNaN
assertEquals(0xffffe000, TestFP16ToFloatRawIntBits((short)(0xffff))); // QNaN->QNaN
}
+
+ public static void main(String args[]) {
+ testHalfToFloatToHalfConversions();
+ testToHalf();
+ testToFloat();
+ }
}
diff --git a/test/684-checker-simd-dotprod/src/Main.java b/test/684-checker-simd-dotprod/src/Main.java
index e0c87161dd..aa03d1e4a5 100644
--- a/test/684-checker-simd-dotprod/src/Main.java
+++ b/test/684-checker-simd-dotprod/src/Main.java
@@ -17,6 +17,7 @@
import other.TestByte;
import other.TestCharShort;
import other.TestVarious;
+import other.TestFloatDouble;
/**
* Tests for dot product idiom vectorization.
@@ -26,6 +27,7 @@ public class Main {
TestByte.run();
TestCharShort.run();
TestVarious.run();
+ TestFloatDouble.run();
System.out.println("passed");
}
}
diff --git a/test/684-checker-simd-dotprod/src/other/TestFloatDouble.java b/test/684-checker-simd-dotprod/src/other/TestFloatDouble.java
new file mode 100644
index 0000000000..b155ae1555
--- /dev/null
+++ b/test/684-checker-simd-dotprod/src/other/TestFloatDouble.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2019 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 other;
+
+/**
+ * Tests for dot product idiom vectorization: char and short case.
+ */
+public class TestFloatDouble {
+
+ public static final int ARRAY_SIZE = 1024;
+
+
+ /// CHECK-START-{X86_64}: float other.TestFloatDouble.testDotProdSimpleFloat(float[], float[]) loop_optimization (after)
+ /// CHECK-NOT: VecDotProd
+ public static final float testDotProdSimpleFloat(float[] a, float[] b) {
+ float sum = 0;
+ for (int i = 0; i < b.length; i++) {
+ sum += a[i] * b[i];
+ }
+ return sum;
+ }
+
+
+ /// CHECK-START-{X86_64}: double other.TestFloatDouble.testDotProdSimpleDouble(double[], double[]) loop_optimization (after)
+ /// CHECK-NOT: VecDotProd
+
+ public static final double testDotProdSimpleDouble(double[] a, double[] b) {
+ double sum = 0;
+ for (int i = 0; i < b.length; i++) {
+ sum += a[i] * b[i];
+ }
+ return sum;
+ }
+
+ private static void expectEquals(float expected, float result) {
+ if (Float.compare(expected, result) != 0) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEquals(double expected, double result) {
+ if (Double.compare(expected, result) != 0) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ public static void run() {
+ final float MAX_F = Float.MAX_VALUE;
+ final float MIN_F = Float.MIN_VALUE;
+ final double MAX_D = Double.MAX_VALUE;
+ final double MIN_D = Double.MIN_VALUE;
+
+ double[] a = new double[1024];
+ for (int i = 0; i != 1024; ++i) a[i] = MAX_D;
+ double[] b = new double[1024];
+ for (int i = 0; i != 1024; ++i) b[i] = ((i & 1) == 0) ? 1.0 : -1.0;
+ expectEquals(0.0, testDotProdSimpleDouble(a,b));
+
+ float[] f1_1 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3.33f, 0.125f, 3.0f, 0.25f};
+ float[] f2_1 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6.125f, 2.25f, 1.213f, 0.5f};
+ expectEquals(24.4415f, testDotProdSimpleFloat(f1_1, f2_1));
+
+ float [] f1_2 = { 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0.63671875f, 0.76953125f, 0.22265625f, 1.0f};
+ float [] f2_2 = { 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, MIN_F, MAX_F, MAX_F, MIN_F };
+ expectEquals(3.376239E38f, testDotProdSimpleFloat(f1_2, f2_2));
+
+ float[] f1_3 = { 0xc0000000, 0xc015c28f, 0x411dd42c, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, MIN_F, MIN_F };
+ float[] f2_3 = { 0x3f4c779a, 0x408820c5, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0x00000000, 0, MAX_F, MAX_F };
+ expectEquals(-2.30124471E18f, testDotProdSimpleFloat(f1_3, f2_3));
+ }
+
+ public static void main(String[] args) {
+ run();
+ }
+}
diff --git a/test/706-checker-scheduler/src/Main.java b/test/706-checker-scheduler/src/Main.java
index af18193388..5a66fbbebc 100644
--- a/test/706-checker-scheduler/src/Main.java
+++ b/test/706-checker-scheduler/src/Main.java
@@ -322,7 +322,7 @@ public class Main {
// but has more complex chains of transforming the original references:
// ParameterValue --> BoundType --> NullCheck --> ArrayGet.
// ParameterValue --> BoundType --> NullCheck --> IntermediateAddress --> ArraySet.
- // After using LSA to analyze the orginal references, the scheduler should be able
+ // After using LSA to analyze the original references, the scheduler should be able
// to find out that 'a' and 'b' may alias, hence unable to schedule these ArraGet/Set.
/// CHECK-START-ARM64: void Main.CrossOverLoop2(java.lang.Object, java.lang.Object) scheduler (before)
@@ -584,9 +584,126 @@ public class Main {
}
}
+ // Check that instructions having cross iteration dependencies are not
+ // reordered.
+ //
+ /// CHECK-START-{ARM,ARM64}: void Main.testCrossItersDependencies() scheduler (before)
+ /// CHECK: <<ID1:i\d+>> Phi [{{i\d+}},<<ID3:i\d+>>]
+ /// CHECK: <<ID2:i\d+>> Phi [{{i\d+}},<<ID4:i\d+>>]
+ //
+ /// CHECK: <<ID3>> Sub [<<ID1>>,<<ID2>>]
+ /// CHECK: <<ID4>> Add [<<ID2>>,{{i\d+}}]
+
+ /// CHECK-START-{ARM,ARM64}: void Main.testCrossItersDependencies() scheduler (after)
+ /// CHECK: <<ID1:i\d+>> Phi [{{i\d+}},<<ID3:i\d+>>]
+ /// CHECK: <<ID2:i\d+>> Phi [{{i\d+}},<<ID4:i\d+>>]
+ //
+ /// CHECK: <<ID3>> Sub [<<ID1>>,<<ID2>>]
+ /// CHECK: <<ID4>> Add [<<ID2>>,{{i\d+}}]
+
+ /// CHECK-START-ARM: void Main.testCrossItersDependencies() disassembly (after)
+ /// CHECK: subs
+ /// CHECK: add
+ /// CHECK: adds
+ /// CHECK: ldrh
+ /// CHECK: cmp
+ /// CHECK: beq
+
+ /// CHECK-START-ARM64: void Main.testCrossItersDependencies() disassembly (after)
+ /// CHECK: sub
+ /// CHECK: add
+ /// CHECK: add
+ /// CHECK: ldrh
+ /// CHECK: cbz
+ private static void testCrossItersDependencies() {
+ int[] data = {1, 2, 3, 0};
+ int sub = 0;
+ int sum = data[0];
+ for (int i = 1; data[i] != 0; ++i) {
+ sub -= sum;
+ sum += data[i];
+ }
+ expectEquals(sub, -4);
+ expectEquals(sum, 6);
+ }
+
+ // Check instructions defining values for the next iteration don't become
+ // self-dependent in a scheduling graph which prevents valid reordering.
+ //
+ /// CHECK-START-{ARM,ARM64}: void Main.testNoSelfDependantSchedNode(int) scheduler (before)
+ /// CHECK: IntermediateAddress
+ /// CHECK: ArrayGet
+ /// CHECK: LessThanOrEqual
+ /// CHECK: Select
+ /// CHECK: IntermediateAddress
+ /// CHECK: ArraySet
+ /// CHECK: Add
+
+ /// CHECK-START-{ARM,ARM64}: void Main.testNoSelfDependantSchedNode(int) scheduler (after)
+ /// CHECK: IntermediateAddress
+ /// CHECK: ArrayGet
+ /// CHECK: IntermediateAddress
+ /// CHECK: LessThanOrEqual
+ /// CHECK: Select
+ /// CHECK: ArraySet
+ /// CHECK: Add
+ //
+ // Parameter n is to prevent unrolling of the main loop.
+ private static void testNoSelfDependantSchedNode(int n) {
+ final int MAX = 2;
+ int[] a = {1, 2, 3};
+ int[] b = new int[a.length];
+ n = Math.min(n, a.length);
+ for (int i = 0; i < n; ++i) {
+ int j = a[i];
+ b[i] = (j > MAX ? MAX : 0);
+ }
+ expectEquals(b[0], 0);
+ expectEquals(b[1], 0);
+ expectEquals(b[2], 2);
+ }
+
+ // In case of cross iteration dependencies when a value for the next iteration is also used on
+ // the current iteration a MOV instruction is generated anyway. In such cases setting dependency
+ // between scheduling nodes will not eliminate MOV.
+ // In the test 'i+1' is such an example.
+ // The test checks that a dependency between scheduling nodes (first ArrayGet and Add) is not
+ // setup and Add is scheduled before ArrayGet.
+ //
+ /// CHECK-START-{ARM,ARM64}: void Main.testNonPreventingSchedulingCrossItersDeps(int) scheduler (before)
+ /// CHECK: IntermediateAddress
+ /// CHECK-NEXT: ArrayGet
+ /// CHECK-NEXT: Add
+ /// CHECK-NEXT: ArrayGet
+
+ /// CHECK-START-{ARM,ARM64}: void Main.testNonPreventingSchedulingCrossItersDeps(int) scheduler (after)
+ /// CHECK: IntermediateAddress
+ /// CHECK-NEXT: Add
+ /// CHECK-NEXT: ArrayGet
+ /// CHECK-NEXT: ArrayGet
+ //
+ // Parameter n is to prevent unrolling of the main loop.
+ private static void testNonPreventingSchedulingCrossItersDeps(int n) {
+ int[] a = {1, 2, 3};
+ n = Math.min(n, a.length);
+ for (int i = 0; i < n - 1; ++i) {
+ if (a[i] < a[i + 1]) {
+ int tmp = a[i];
+ a[i] = a[i + 1];
+ a[i + 1] = tmp;
+ }
+ }
+ expectEquals(a[0], 2);
+ expectEquals(a[1], 3);
+ expectEquals(a[2], 1);
+ }
+
public static void main(String[] args) {
testVecSetScalars();
testVecReplicateScalar();
+ testCrossItersDependencies();
+ testNoSelfDependantSchedNode(3);
+ testNonPreventingSchedulingCrossItersDeps(3);
if ((arrayAccess() + intDiv(10)) != -35) {
System.out.println("FAIL");
}
diff --git a/test/906-iterate-heap/src/art/Test906.java b/test/906-iterate-heap/src/art/Test906.java
index 190f36f5d4..b782c9bd70 100644
--- a/test/906-iterate-heap/src/art/Test906.java
+++ b/test/906-iterate-heap/src/art/Test906.java
@@ -91,17 +91,26 @@ public class Test906 {
public static class Baz extends Bar {}
public static class Alpha extends Bar {}
public static class MISSING extends Baz {}
+ public interface Iface {}
+ public static class Beta implements Iface {}
+ public static class Gamma implements Iface {}
+ public static class Delta extends Beta {}
private static void testIterateOverInstances() throws Exception {
Object[] foos = GenTs(Foo.class);
Object[] bars = GenTs(Bar.class);
Object[] bazs = GenTs(Baz.class);
Object[] alphas = GenTs(Alpha.class);
+ Object[] betas = GenTs(Beta.class);
+ Object[] gammas = GenTs(Gamma.class);
+ Object[] deltas = GenTs(Delta.class);
checkEq(0, iterateOverInstancesCount(MISSING.class));
checkEq(alphas.length, iterateOverInstancesCount(Alpha.class));
checkEq(bazs.length, iterateOverInstancesCount(Baz.class));
checkEq(bazs.length + alphas.length + bars.length, iterateOverInstancesCount(Bar.class));
checkEq(bazs.length + alphas.length + bars.length + foos.length,
iterateOverInstancesCount(Foo.class));
+ checkEq(betas.length + gammas.length + deltas.length,
+ iterateOverInstancesCount(Iface.class));
}
public static void doTest() throws Exception {
diff --git a/test/956-methodhandles/src/Main.java b/test/956-methodhandles/src/Main.java
index 78f6db732f..e70c83b060 100644
--- a/test/956-methodhandles/src/Main.java
+++ b/test/956-methodhandles/src/Main.java
@@ -27,6 +27,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
+import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -94,6 +95,11 @@ public class Main {
}
}
+ public static class I {
+ public static void someVoidMethod() {
+ }
+ }
+
public static void main(String[] args) throws Throwable {
testfindSpecial_invokeSuperBehaviour();
testfindSpecial_invokeDirectBehaviour();
@@ -634,6 +640,30 @@ public class Main {
fail();
} catch (WrongMethodTypeException expected) {
}
+
+ // Zero / null introduction
+ MethodHandle voidMH = MethodHandles.lookup().findStatic(I.class, "someVoidMethod",
+ MethodType.methodType(void.class));
+ {
+ MethodHandle booleanMH = voidMH.asType(MethodType.methodType(boolean.class));
+ assertEquals(boolean.class, booleanMH.type().returnType());
+ assertEquals(false, booleanMH.invoke());
+ }
+ {
+ MethodHandle intMH = voidMH.asType(MethodType.methodType(int.class));
+ assertEquals(int.class, intMH.type().returnType());
+ assertEquals(0, intMH.invoke());
+ }
+ {
+ MethodHandle longMH = voidMH.asType(MethodType.methodType(long.class));
+ assertEquals(long.class, longMH.type().returnType());
+ assertEquals(0L, longMH.invoke());
+ }
+ {
+ MethodHandle objMH = voidMH.asType(MethodType.methodType(Object.class));
+ assertEquals(Object.class, objMH.type().returnType());
+ assertEquals(null, objMH.invoke());
+ }
}
public static void assertTrue(boolean value) {
@@ -755,6 +785,14 @@ public class Main {
Object[].class, MethodType.methodType(void.class));
fail("Unexpected success for array class type for findConstructor");
} catch (NoSuchMethodException e) {}
+
+ // Child class constructor (b/143343351)
+ {
+ MethodHandle handle = MethodHandles.lookup().findConstructor(
+ ArrayList.class, MethodType.methodType(void.class));
+ AbstractList list = (AbstractList) handle.asType(MethodType.methodType(AbstractList.class))
+ .invokeExact();
+ }
}
public static void testStringConstructors() throws Throwable {
@@ -874,6 +912,16 @@ public class Main {
fail("Unexpected string constructor result: '" + s + "'");
}
+ // Child class constructor (b/143343351)
+ {
+ MethodHandle handle = MethodHandles.lookup().findConstructor(
+ String.class, MethodType.methodType(void.class));
+ CharSequence o = (CharSequence) handle.asType(MethodType.methodType(CharSequence.class))
+ .invokeExact();
+ if (!o.equals("")) {
+ fail("Unexpected child class constructor result: '" + o + "'");
+ }
+ }
System.out.println("String constructors done.");
}
diff --git a/test/Android.bp b/test/Android.bp
index ac83a5dc15..f10d3788bf 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -14,6 +14,8 @@
// limitations under the License.
//
+// ART gtests.
+
art_cc_defaults {
name: "art_test_defaults",
host_supported: true,
@@ -168,6 +170,62 @@ art_cc_library {
},
}
+// ART run-tests.
+
+art_cc_test_library {
+ name: "libarttest",
+ defaults: ["libarttest-defaults"],
+ shared_libs: [
+ "libart",
+ "libdexfile",
+ "libprofile",
+ "libartbase",
+ ],
+}
+
+art_cc_test_library {
+ name: "libarttestd",
+ defaults: [
+ "art_debug_defaults",
+ "libarttest-defaults",
+ ],
+ shared_libs: [
+ "libartd",
+ "libdexfiled",
+ "libprofiled",
+ "libartbased",
+ ],
+}
+
+art_cc_defaults {
+ name: "libnativebridgetest-defaults",
+ defaults: [
+ "art_test_defaults",
+ "art_defaults",
+ ],
+ header_libs: ["libnativebridge-headers"],
+ srcs: ["115-native-bridge/nativebridge.cc"],
+}
+
+art_cc_test_library {
+ name: "libnativebridgetest",
+ shared_libs: ["libart"],
+ defaults: [
+ "libnativebridgetest-defaults",
+ ],
+}
+
+art_cc_test_library {
+ name: "libnativebridgetestd",
+ shared_libs: ["libartd"],
+ defaults: [
+ "libnativebridgetest-defaults",
+ "art_debug_defaults",
+ ],
+}
+
+// ART JVMTI run-tests.
+
cc_defaults {
name: "libartagent-defaults",
defaults: [
@@ -529,58 +587,6 @@ cc_defaults {
],
}
-art_cc_test_library {
- name: "libarttest",
- defaults: ["libarttest-defaults"],
- shared_libs: [
- "libart",
- "libdexfile",
- "libprofile",
- "libartbase",
- ],
-}
-
-art_cc_test_library {
- name: "libarttestd",
- defaults: [
- "art_debug_defaults",
- "libarttest-defaults",
- ],
- shared_libs: [
- "libartd",
- "libdexfiled",
- "libprofiled",
- "libartbased",
- ],
-}
-
-art_cc_defaults {
- name: "libnativebridgetest-defaults",
- defaults: [
- "art_test_defaults",
- "art_defaults",
- ],
- header_libs: ["libnativebridge-headers"],
- srcs: ["115-native-bridge/nativebridge.cc"],
-}
-
-art_cc_test_library {
- name: "libnativebridgetest",
- shared_libs: ["libart"],
- defaults: [
- "libnativebridgetest-defaults",
- ],
-}
-
-art_cc_test_library {
- name: "libnativebridgetestd",
- shared_libs: ["libartd"],
- defaults: [
- "libnativebridgetest-defaults",
- "art_debug_defaults",
- ],
-}
-
filegroup {
name: "art_cts_jvmti_test_library",
visibility: [
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index 5fa7e19eda..4ca5fe8333 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -32,6 +32,7 @@
#include "mirror/class-inl.h"
#include "mirror/class.h"
#include "nativehelper/ScopedUtfChars.h"
+#include "oat.h"
#include "oat_file.h"
#include "oat_quick_method_header.h"
#include "profile/profile_compilation_info.h"
diff --git a/test/jvmti-common/Redefinition.java b/test/jvmti-common/Redefinition.java
index 2ebce17686..3402fa12b5 100644
--- a/test/jvmti-common/Redefinition.java
+++ b/test/jvmti-common/Redefinition.java
@@ -19,7 +19,7 @@ package art;
import java.util.ArrayList;
// Common Redefinition functions. Placed here for use by CTS
public class Redefinition {
- public static final class CommonClassDefinition {
+ public static class CommonClassDefinition {
public final Class<?> target;
public final byte[] class_file_bytes;
public final byte[] dex_file_bytes;
@@ -31,12 +31,19 @@ public class Redefinition {
}
}
+ public static class DexOnlyClassDefinition extends CommonClassDefinition {
+ public DexOnlyClassDefinition(Class<?> target, byte[] dex_file_bytes) {
+ super(target, new byte[0], dex_file_bytes);
+ }
+ }
+
// A set of possible test configurations. Test should set this if they need to.
// This must be kept in sync with the defines in ti-agent/common_helper.cc
public static enum Config {
COMMON_REDEFINE(0),
COMMON_RETRANSFORM(1),
- COMMON_TRANSFORM(2);
+ COMMON_TRANSFORM(2),
+ STRUCTURAL_TRANSFORM(3);
private final int val;
private Config(int val) {
@@ -90,5 +97,18 @@ public class Redefinition {
byte[] dex_bytes);
public static native void doCommonStructuralClassRedefinition(Class<?> target, byte[] dex_file);
+ public static void doMultiStructuralClassRedefinition(CommonClassDefinition... defs) {
+ ArrayList<Class<?>> classes = new ArrayList<>();
+ ArrayList<byte[]> dex_files = new ArrayList<>();
+
+ for (CommonClassDefinition d : defs) {
+ classes.add(d.target);
+ dex_files.add(d.dex_file_bytes);
+ }
+ doCommonMultiStructuralClassRedefinition(classes.toArray(new Class<?>[0]),
+ dex_files.toArray(new byte[0][]));
+ }
+ public static native void doCommonMultiStructuralClassRedefinition(Class<?>[] targets,
+ byte[][] dexfiles);
public static native boolean isStructurallyModifiable(Class<?> target);
}
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 1e00c3ff6a..cd66472aa8 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1139,7 +1139,15 @@
"1984-structural-redefine-field-trace",
"1985-structural-redefine-stack-scope",
"1986-structural-redefine-multi-thread-stack-scope",
- "1987-structural-redefine-recursive-stack-scope"
+ "1987-structural-redefine-recursive-stack-scope",
+ "1988-multi-structural-redefine",
+ "1989-transform-bad-monitor",
+ "1990-structural-bad-verify",
+ "1991-hello-structural-retransform",
+ "1992-retransform-no-such-field",
+ "1993-fallback-non-structural",
+ "1997-structural-shadow-method",
+ "1998-structural-shadow-field"
],
"variant": "jvm",
"description": ["Doesn't run on RI."]
@@ -1261,5 +1269,11 @@
"env_vars": {"ART_READ_BARRIER_TYPE": "TABLELOOKUP"},
"bug": "b/140507091",
"description": ["Test containing Checker assertions expecting Baker read barriers."]
+ },
+ {
+ "tests": ["689-zygote-jit-deopt"],
+ "variant": "gcstress",
+ "bug": "b/137887811",
+ "description": ["Occasional timeouts."]
}
]
diff --git a/test/run-test b/test/run-test
index eeeefbb4c1..72e7562380 100755
--- a/test/run-test
+++ b/test/run-test
@@ -672,12 +672,12 @@ if [ "$runtime" = "dalvik" ]; then
elif [ "$runtime" = "art" ]; then
if [ "$target_mode" = "no" ]; then
guess_host_arch_name
- run_args+=(--boot "${ANDROID_HOST_OUT}/framework/core${image_suffix}.art")
+ run_args+=(--boot "${ANDROID_HOST_OUT}/framework/core${image_suffix}.art:*")
run_args+=(--runtime-option "-Djava.library.path=${host_lib_root}/lib${suffix64}:${host_lib_root}/nativetest${suffix64}")
else
guess_target_arch_name
run_args+=(--runtime-option "-Djava.library.path=/data/nativetest${suffix64}/art/${target_arch_name}")
- run_args+=(--boot "/data/art-test/core${image_suffix}.art")
+ run_args+=(--boot "/data/art-test/core${image_suffix}.art:/data/art-test/*")
fi
if [ "$relocate" = "yes" ]; then
run_args+=(--relocate)
diff --git a/test/ti-agent/jvmti_helper.cc b/test/ti-agent/jvmti_helper.cc
index ae1a8d34f3..22bc64ac01 100644
--- a/test/ti-agent/jvmti_helper.cc
+++ b/test/ti-agent/jvmti_helper.cc
@@ -238,6 +238,26 @@ void DeallocParams(jvmtiEnv* env, jvmtiParamInfo* params, jint n_params) {
}
}
+jint GetExtensionEventId(jvmtiEnv* jvmti, const std::string_view& name) {
+ jint n_ext = 0;
+ jint res = -1;
+ bool found_res = false;
+ jvmtiExtensionEventInfo* infos = nullptr;
+ CHECK_EQ(jvmti->GetExtensionEvents(&n_ext, &infos), JVMTI_ERROR_NONE);
+ for (jint i = 0; i < n_ext; i++) {
+ const jvmtiExtensionEventInfo& info = infos[i];
+ if (name == info.id) {
+ res = info.extension_event_index;
+ found_res = true;
+ }
+ DeallocParams(jvmti, info.params, info.param_count);
+ Dealloc(jvmti, info.short_description, info.id, info.params);
+ }
+ Dealloc(jvmti, infos);
+ CHECK(found_res);
+ return res;
+}
+
void* GetExtensionFunctionVoid(JNIEnv* env, jvmtiEnv* jvmti, const std::string_view& name) {
jint n_ext = 0;
void* res = nullptr;
diff --git a/test/ti-agent/jvmti_helper.h b/test/ti-agent/jvmti_helper.h
index a3b95353c7..74d594ff04 100644
--- a/test/ti-agent/jvmti_helper.h
+++ b/test/ti-agent/jvmti_helper.h
@@ -94,6 +94,8 @@ template<typename T> T GetExtensionFunction(JNIEnv* env, jvmtiEnv* jvmti, const
return reinterpret_cast<T>(GetExtensionFunctionVoid(env, jvmti, name));
}
+jint GetExtensionEventId(jvmtiEnv* jvmti, const std::string_view& name);
+
} // namespace art
#endif // ART_TEST_TI_AGENT_JVMTI_HELPER_H_
diff --git a/test/ti-agent/redefinition_helper.cc b/test/ti-agent/redefinition_helper.cc
index 9d9f13fa39..0baa9fe547 100644
--- a/test/ti-agent/redefinition_helper.cc
+++ b/test/ti-agent/redefinition_helper.cc
@@ -31,8 +31,13 @@
namespace art {
+enum class RedefineType {
+ kNormal,
+ kStructural,
+};
+
static void SetupCommonRedefine();
-static void SetupCommonRetransform();
+static void SetupCommonRetransform(RedefineType type);
static void SetupCommonTransform();
template <bool is_redefine>
static void throwCommonRedefinitionError(jvmtiEnv* jvmti,
@@ -68,6 +73,7 @@ static void throwCommonRedefinitionError(jvmtiEnv* jvmti,
#define CONFIGURATION_COMMON_REDEFINE 0
#define CONFIGURATION_COMMON_RETRANSFORM 1
#define CONFIGURATION_COMMON_TRANSFORM 2
+#define CONFIGURATION_STRUCTURAL_TRANSFORM 3
extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_nativeSetTestConfiguration(JNIEnv*,
jclass,
@@ -78,21 +84,54 @@ extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_nativeSetTestConfigurati
return;
}
case CONFIGURATION_COMMON_RETRANSFORM: {
- SetupCommonRetransform();
+ SetupCommonRetransform(RedefineType::kNormal);
return;
}
case CONFIGURATION_COMMON_TRANSFORM: {
SetupCommonTransform();
return;
}
+ case CONFIGURATION_STRUCTURAL_TRANSFORM: {
+ SetupCommonRetransform(RedefineType::kStructural);
+ return;
+ }
default: {
LOG(FATAL) << "Unknown test configuration: " << type;
}
}
}
+template<RedefineType kType>
+static bool SupportsAndIsJVM() {
+ if constexpr (kType == RedefineType::kStructural) {
+ return false;
+ } else {
+ return IsJVM();
+ }
+}
+
+
namespace common_redefine {
+template <RedefineType kType>
+static jvmtiError CallRedefineEntrypoint(JNIEnv* env,
+ jvmtiEnv* jvmti,
+ jint num_defs,
+ const jvmtiClassDefinition* defs) {
+ decltype(jvmti->functions->RedefineClasses) entrypoint = nullptr;
+ if constexpr (kType == RedefineType::kNormal) {
+ entrypoint = jvmti->functions->RedefineClasses;
+ } else {
+ entrypoint = GetExtensionFunction<decltype(entrypoint)>(
+ env, jvmti_env, "com.android.art.class.structurally_redefine_classes");
+ }
+ if (entrypoint == nullptr) {
+ LOG(INFO) << "Could not find entrypoint!";
+ return JVMTI_ERROR_NOT_AVAILABLE;
+ }
+ return entrypoint(jvmti, num_defs, defs);
+}
+
static void throwRedefinitionError(jvmtiEnv* jvmti,
JNIEnv* env,
jint num_targets,
@@ -101,6 +140,7 @@ static void throwRedefinitionError(jvmtiEnv* jvmti,
return throwCommonRedefinitionError<true>(jvmti, env, num_targets, target, res);
}
+template<RedefineType kType>
static void DoMultiClassRedefine(jvmtiEnv* jvmti_env,
JNIEnv* env,
jint num_redefines,
@@ -109,24 +149,25 @@ static void DoMultiClassRedefine(jvmtiEnv* jvmti_env,
jbyteArray* dex_file_bytes) {
std::vector<jvmtiClassDefinition> defs;
for (jint i = 0; i < num_redefines; i++) {
- jbyteArray desired_array = IsJVM() ? class_file_bytes[i] : dex_file_bytes[i];
+ jbyteArray desired_array = SupportsAndIsJVM<kType>() ? class_file_bytes[i] : dex_file_bytes[i];
jint len = static_cast<jint>(env->GetArrayLength(desired_array));
const unsigned char* redef_bytes = reinterpret_cast<const unsigned char*>(
env->GetByteArrayElements(desired_array, nullptr));
defs.push_back({targets[i], static_cast<jint>(len), redef_bytes});
}
- jvmtiError res = jvmti_env->RedefineClasses(num_redefines, defs.data());
+ jvmtiError res = CallRedefineEntrypoint<kType>(env, jvmti_env, num_redefines, defs.data());
if (res != JVMTI_ERROR_NONE) {
throwRedefinitionError(jvmti_env, env, num_redefines, targets, res);
}
}
+template<RedefineType kType>
static void DoClassRedefine(jvmtiEnv* jvmti_env,
JNIEnv* env,
jclass target,
jbyteArray class_file_bytes,
jbyteArray dex_file_bytes) {
- return DoMultiClassRedefine(jvmti_env, env, 1, &target, &class_file_bytes, &dex_file_bytes);
+ return DoMultiClassRedefine<kType>(jvmti_env, env, 1, &target, &class_file_bytes, &dex_file_bytes);
}
extern "C" JNIEXPORT jboolean JNICALL
@@ -145,25 +186,44 @@ Java_art_Redefinition_isStructurallyModifiable(JNIEnv* env, jclass, jclass targe
extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonStructuralClassRedefinition(
JNIEnv* env, jclass, jclass target, jbyteArray dex_file_bytes) {
- using ArtStructurallyRedefineClassDirect =
- jvmtiError (*)(jvmtiEnv * env, jclass k, jbyte* data, jint len);
- ArtStructurallyRedefineClassDirect redef =
- GetExtensionFunction<ArtStructurallyRedefineClassDirect>(
- env, jvmti_env, "com.android.art.UNSAFE.class.structurally_redefine_class_direct");
- if (redef == nullptr || env->ExceptionCheck()) {
- return;
- }
- jint len = env->GetArrayLength(dex_file_bytes);
- std::vector<jbyte> v(len, 0);
- env->GetByteArrayRegion(dex_file_bytes, 0, len, v.data());
- JvmtiErrorToException(env, jvmti_env, redef(jvmti_env, target, v.data(), len));
+ DoClassRedefine<RedefineType::kStructural>(jvmti_env, env, target, nullptr, dex_file_bytes);
}
// Magic JNI export that classes can use for redefining classes.
// To use classes should declare this as a native function with signature (Ljava/lang/Class;[B[B)V
extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonClassRedefinition(
JNIEnv* env, jclass, jclass target, jbyteArray class_file_bytes, jbyteArray dex_file_bytes) {
- DoClassRedefine(jvmti_env, env, target, class_file_bytes, dex_file_bytes);
+ DoClassRedefine<RedefineType::kNormal>(jvmti_env, env, target, class_file_bytes, dex_file_bytes);
+}
+
+// Magic JNI export that classes can use for redefining classes.
+// To use classes should declare this as a native function with signature
+// ([Ljava/lang/Class;[[B[[B)V
+extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonMultiStructuralClassRedefinition(
+ JNIEnv* env,
+ jclass,
+ jobjectArray targets,
+ jobjectArray dex_file_bytes) {
+ std::vector<jclass> classes;
+ std::vector<jbyteArray> class_files;
+ std::vector<jbyteArray> dex_files;
+ jint len = env->GetArrayLength(targets);
+ if (len != env->GetArrayLength(dex_file_bytes)) {
+ env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"),
+ "the three array arguments passed to this function have different lengths!");
+ return;
+ }
+ for (jint i = 0; i < len; i++) {
+ classes.push_back(static_cast<jclass>(env->GetObjectArrayElement(targets, i)));
+ dex_files.push_back(static_cast<jbyteArray>(env->GetObjectArrayElement(dex_file_bytes, i)));
+ class_files.push_back(nullptr);
+ }
+ return DoMultiClassRedefine<RedefineType::kStructural>(jvmti_env,
+ env,
+ len,
+ classes.data(),
+ class_files.data(),
+ dex_files.data());
}
// Magic JNI export that classes can use for redefining classes.
@@ -189,12 +249,12 @@ extern "C" JNIEXPORT void JNICALL Java_art_Redefinition_doCommonMultiClassRedefi
dex_files.push_back(static_cast<jbyteArray>(env->GetObjectArrayElement(dex_file_bytes, i)));
class_files.push_back(static_cast<jbyteArray>(env->GetObjectArrayElement(class_file_bytes, i)));
}
- return DoMultiClassRedefine(jvmti_env,
- env,
- len,
- classes.data(),
- class_files.data(),
- dex_files.data());
+ return DoMultiClassRedefine<RedefineType::kNormal>(jvmti_env,
+ env,
+ len,
+ classes.data(),
+ class_files.data(),
+ dex_files.data());
}
// Get all capabilities except those related to retransformation.
@@ -380,7 +440,7 @@ jint OnLoad(JavaVM* vm,
printf("Unable to get jvmti env!\n");
return 1;
}
- SetupCommonRetransform();
+ SetupCommonRetransform(RedefineType::kNormal);
return 0;
}
@@ -409,11 +469,20 @@ static void SetupCommonRedefine() {
jvmti_env->AddCapabilities(&caps);
}
-static void SetupCommonRetransform() {
+static void SetupCommonRetransform(RedefineType type) {
SetStandardCapabilities(jvmti_env);
- current_callbacks.ClassFileLoadHook = common_retransform::CommonClassFileLoadHookRetransformable;
- jvmtiError res = jvmti_env->SetEventCallbacks(&current_callbacks, sizeof(current_callbacks));
- CHECK_EQ(res, JVMTI_ERROR_NONE);
+ if (type == RedefineType::kNormal) {
+ current_callbacks.ClassFileLoadHook =
+ common_retransform::CommonClassFileLoadHookRetransformable;
+ jvmtiError res = jvmti_env->SetEventCallbacks(&current_callbacks, sizeof(current_callbacks));
+ CHECK_EQ(res, JVMTI_ERROR_NONE);
+ } else {
+ jvmtiError res = jvmti_env->SetExtensionEventCallback(
+ GetExtensionEventId(jvmti_env, "com.android.art.class.structural_dex_file_load_hook"),
+ reinterpret_cast<jvmtiExtensionEvent>(
+ common_retransform::CommonClassFileLoadHookRetransformable));
+ CHECK_EQ(res, JVMTI_ERROR_NONE);
+ }
common_retransform::gTransformations.clear();
}
diff --git a/tools/generate_cmake_lists.py b/tools/generate_cmake_lists.py
index 5639617e2e..b19c2920f0 100755
--- a/tools/generate_cmake_lists.py
+++ b/tools/generate_cmake_lists.py
@@ -68,7 +68,7 @@ def main():
ANDROID_BUILD_TOP = get_android_build_top()
- subprocess.check_output('m -j64', shell=True, cwd=ANDROID_BUILD_TOP)
+ subprocess.check_output('build/soong/soong_ui.bash --make-mode', shell=True, cwd=ANDROID_BUILD_TOP)
out_art_cmakelists_dir = os.path.join(ANDROID_BUILD_TOP,
'out/development/ide/clion/art')
diff --git a/tools/jvmti-agents/ti-alloc-sample/Android.bp b/tools/jvmti-agents/ti-alloc-sample/Android.bp
new file mode 100644
index 0000000000..0dc2dd8fb0
--- /dev/null
+++ b/tools/jvmti-agents/ti-alloc-sample/Android.bp
@@ -0,0 +1,73 @@
+//
+// Copyright (C) 2018 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.
+//
+
+// Build variants {target,host} x {debug,ndebug} x {32,64}
+cc_defaults {
+ name: "ti-alloc-sample-base-defaults",
+ srcs: ["ti_alloc_sample.cc"],
+ defaults: ["art_defaults"],
+
+ // Note that this tool needs to be built for both 32-bit and 64-bit since it requires
+ // to be same ISA as what it is attached to.
+ compile_multilib: "both",
+ header_libs: [
+ "libopenjdkjvmti_headers",
+ "libnativehelper_header_only",
+ "jni_headers",
+ ],
+}
+
+cc_defaults {
+ name: "ti-alloc-sample-defaults",
+ host_supported: true,
+ shared_libs: [
+ "libbase",
+ ],
+ defaults: ["ti-alloc-sample-base-defaults"],
+}
+
+cc_defaults {
+ name: "ti-alloc-sample-static-defaults",
+ host_supported: false,
+ defaults: ["ti-alloc-sample-base-defaults"],
+
+ shared_libs: [
+ "liblog",
+ ],
+ static_libs: [
+ "libbase_ndk",
+ ],
+ sdk_version: "current",
+ stl: "c++_static",
+}
+
+art_cc_library {
+ name: "libtiallocsamples",
+ defaults: ["ti-alloc-sample-static-defaults"],
+}
+
+art_cc_library {
+ name: "libtiallocsample",
+ defaults: ["ti-alloc-sample-defaults"],
+}
+
+art_cc_library {
+ name: "libtiallocsampled",
+ defaults: [
+ "art_debug_defaults",
+ "ti-alloc-sample-defaults",
+ ],
+}
diff --git a/tools/jvmti-agents/ti-alloc-sample/README.md b/tools/jvmti-agents/ti-alloc-sample/README.md
new file mode 100644
index 0000000000..0da090af12
--- /dev/null
+++ b/tools/jvmti-agents/ti-alloc-sample/README.md
@@ -0,0 +1,79 @@
+# tiallocsample
+
+tiallocsample is a JVMTI agent designed to track the call stacks of allocations
+in the heap.
+
+# Usage
+### Build
+> `m libtiallocsample`
+
+The libraries will be built for 32-bit, 64-bit, host and target. Below examples
+assume you want to use the 64-bit version.
+
+Use `libtiallocsamples` if you wish to build a version without non-NDK dynamic dependencies.
+
+### Command Line
+
+The agent is loaded using -agentpath like normal. It takes arguments in the
+following format:
+> `sample_rate,stack_depth_limit,log_path`
+
+* sample_rate is an integer specifying how frequently an event is reported.
+ E.g., 10 means every tenth call to new will be logged.
+* stack_depth_limit is an integer that determines the number of frames the deepest stack trace
+ can contain. It returns just the top portion if the limit is exceeded.
+* log_path is an absolute file path specifying where the log is to be written.
+
+#### Output Format
+
+The resulting file is a sequence of object allocations, with a limited form of
+text compression. For example a single stack frame might look like:
+
+```
++0,jthread[main], jclass[[I file: <UNKNOWN_FILE>], size[24, hex: 0x18]
++1,main([Ljava/lang/String;)V
++2,run()V
++3,invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;
++4,loop()V
++5,dispatchMessage(Landroid/os/Message;)V
++6,handleMessage(Landroid/os/Message;)V
++7,onDisplayChanged(I)V
++8,getState()I
++9,updateDisplayInfoLocked()V
++10,getDisplayInfo(I)Landroid/view/DisplayInfo;
++11,createFromParcel(Landroid/os/Parcel;)Ljava/lang/Object;
++12,createFromParcel(Landroid/os/Parcel;)Landroid/view/DisplayInfo;
++13,<init>(Landroid/os/Parcel;Landroid/view/DisplayInfo$1;)V
++14,<init>(Landroid/os/Parcel;)V
++15,readFromParcel(Landroid/os/Parcel;)V
+=16,0;1;2;3;1;4;5;6;7;8;9;10;10;11;12;13;14;15
+16
+```
+
+Lines starting with a + are key, value pairs. So, for instance, key 2 stands for
+```
+run()V
+```
+.
+
+The line starting with 0 is the thread, type, and size (TTS) of an allocation. The
+remaining lines starting with + are stack frames (SFs), containing function signatures.
+Lines starting with = are stack traces (STs), and are again key, value pairs. In the
+example above, an ST called 16 is the TTS plus sequence of SFs. Any line not starting
+with + or = is a sample. It is a reference to an ST. Hence repeated samples are
+represented as just numbers.
+
+#### ART
+> `art -Xplugin:$ANDROID_HOST_OUT/lib64/libopenjdkjvmti.so '-agentpath:libtiallocsample.so=100' -cp tmp/java/helloworld.dex -Xint helloworld`
+
+* `-Xplugin` and `-agentpath` need to be used, otherwise the agent will fail during init.
+* If using `libartd.so`, make sure to use the debug version of jvmti.
+
+> `adb shell setenforce 0`
+>
+> `adb push $ANDROID_PRODUCT_OUT/system/lib64/libtiallocsample.so /data/local/tmp/`
+>
+> `adb shell am start-activity --attach-agent /data/local/tmp/libtiallocsample.so=100 some.debuggable.apps/.the.app.MainActivity`
+
+#### RI
+> `java '-agentpath:libtiallocsample.so=MethodEntry' -cp tmp/helloworld/classes helloworld`
diff --git a/tools/jvmti-agents/ti-alloc-sample/mkflame.py b/tools/jvmti-agents/ti-alloc-sample/mkflame.py
new file mode 100755
index 0000000000..f37aa4aafb
--- /dev/null
+++ b/tools/jvmti-agents/ti-alloc-sample/mkflame.py
@@ -0,0 +1,213 @@
+#!/usr/bin/python3
+#
+# Copyright 2019, 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.
+
+"""
+Usage: mkflame.py <jvmti_trace_file>
+"""
+
+import argparse
+import sys
+
+class TraceCollection:
+ def __init__(self, args):
+ self.args = args
+ # A table indexed by number and containing the definition for that number.
+ self.definitions = {}
+ # The "weight" of a stack trace, either 1 for counting or the size of the allocation.
+ self.weights = {}
+ # The count for each individual allocation.
+ self.allocation_count = {}
+
+ def definition(self, index):
+ """
+ Returns the definition for "index".
+ """
+ return self.definitions[index]
+
+ def set_definition(self, index, definition):
+ """
+ Sets the definition for "index".
+ """
+ self.definitions[index] = definition
+
+ def weight(self, index):
+ """
+ Returns the weight for "index".
+ """
+ return self.weights[index]
+
+ def set_weight(self, index, weight):
+ """
+ Sets the weight for "index".
+ """
+ self.weights[index] = weight
+
+ def read_file(self, filename):
+ """
+ Reads a file into a DefinitionTable.
+ """
+ def process_definition(line):
+ """
+ Adds line to the list of definitions in table.
+ """
+ def expand_stack_trace(definition):
+ """
+ Converts a semicolon-separated list of numbers into the text stack trace.
+ """
+ def get_allocation_thread(thread_type_size):
+ """
+ Returns the thread of an allocation from the thread/type/size record.
+ """
+ THREAD_STRING = "thread["
+ THREAD_STRING_LEN = len(THREAD_STRING)
+ thread_string = thread_type_size[thread_type_size.find(THREAD_STRING) +
+ THREAD_STRING_LEN:]
+ return thread_string[:thread_string.find("]")]
+
+ def get_allocation_type(thread_type_size):
+ """
+ Returns the type of an allocation from the thread/type/size record.
+ """
+ TYPE_STRING = "jclass["
+ TYPE_STRING_LEN = len(TYPE_STRING)
+ type_string = thread_type_size[thread_type_size.find(TYPE_STRING) + TYPE_STRING_LEN:]
+ return type_string[:type_string.find(" ")]
+
+ def get_allocation_size(thread_type_size):
+ """
+ Returns the size of an allocation from the thread/type/size record.
+ """
+ SIZE_STRING = "size["
+ SIZE_STRING_LEN = len(SIZE_STRING)
+ size_string = thread_type_size[thread_type_size.find(SIZE_STRING) + SIZE_STRING_LEN:]
+ size_string = size_string[:size_string.find(",")]
+ return int(size_string)
+
+ def get_top_and_weight(index):
+ thread_type_size = self.definition(int(tokens[0]))
+ size = get_allocation_size(thread_type_size)
+ if self.args.type_only:
+ thread_type_size = get_allocation_type(thread_type_size)
+ elif self.args.thread_only:
+ thread_type_size = get_allocation_thread(thread_type_size)
+ return (thread_type_size, size)
+
+ tokens = definition.split(";")
+ # The first element (base) of the stack trace is the thread/type/size.
+ # Get the weight (either 1 or the number of bytes allocated).
+ (thread_type_size, weight) = get_top_and_weight(int(tokens[0]))
+ self.set_weight(index, weight)
+ # Remove the thread/type/size from the base of the stack trace.
+ del tokens[0]
+ # Build the stack trace list.
+ expanded_definition = ""
+ for i in range(len(tokens)):
+ if self.args.depth_limit > 0 and i >= self.args.depth_limit:
+ break
+ token = tokens[i]
+ # Replace semicolons by colons in the method entry signatures.
+ method = self.definition(int(token)).replace(";", ":")
+ if len(expanded_definition) > 0:
+ expanded_definition += ";"
+ expanded_definition += method
+ if not self.args.ignore_type:
+ # Add the thread/type/size as the top-most stack frame.
+ if len(expanded_definition) > 0:
+ expanded_definition += ";"
+ expanded_definition += thread_type_size.replace(";", ":")
+ if self.args.reverse_stack:
+ def_list = expanded_definition.split(";")
+ expanded_definition = ";".join(def_list[::-1])
+ return expanded_definition
+
+ # If the line contains a comma, it is of the form [+=]index,definition,
+ # where index is a string containing an integer, and definition is the
+ # value represented by the integer whenever it is used later.
+ # * Lines starting with + are either a thread/type/size record or a single
+ # stack frame. These are simply interned in the table.
+ # * Those starting with = are stack traces, and contain a sequence of
+ # numbers separated by semicolon. These are "expanded" and then interned.
+ comma_pos = line.find(",")
+ index = int(line[1:comma_pos])
+ definition = line[comma_pos+1:]
+ if line[0:1] == "=":
+ definition = expand_stack_trace(definition)
+ # Intern the definition in the table.
+ #if len(definition) == 0:
+ # Zero length samples are errors and are discarded.
+ #print("ERROR: definition for " + str(index) + " is empty")
+ #return
+ self.set_definition(index, definition)
+
+ def process_trace(index):
+ """
+ Remembers one stack trace in the list of stack traces we have seen.
+ Remembering a stack trace increments a count associated with the trace.
+ """
+ trace = self.definition(index)
+ if self.args.use_size:
+ weight = self.weight(index)
+ else:
+ weight = 1
+ if trace in self.allocation_count:
+ self.allocation_count[trace] = self.allocation_count[trace] + weight
+ else:
+ self.allocation_count[trace] = weight
+
+ # Read the file, processing each line as a definition or stack trace.
+ tracefile = open(filename, "r")
+ current_allocation_trace = ""
+ for line in tracefile:
+ line = line.rstrip("\n")
+ if line[0:1] == "=" or line[0:1] == "+":
+ # definition.
+ process_definition(line)
+ else:
+ # stack trace.
+ process_trace(int(line))
+
+ def dump_flame_graph(self):
+ """
+ Prints out a stack trace format compatible with flame graph creation utilities.
+ """
+ for definition, weight in self.allocation_count.items():
+ print(definition + " " + str(weight))
+
+def parse_options():
+ parser = argparse.ArgumentParser(description="Convert a trace to a form usable for flame graphs.")
+ parser.add_argument("filename", help="The trace file as input", type=str)
+ parser.add_argument("--use_size", help="Count by allocation size", action="store_true",
+ default=False)
+ parser.add_argument("--ignore_type", help="Ignore type of allocation", action="store_true",
+ default=False)
+ parser.add_argument("--reverse_stack", help="Reverse root and top of stacks", action="store_true",
+ default=False)
+ parser.add_argument("--type_only", help="Only consider allocation type", action="store_true",
+ default=False)
+ parser.add_argument("--thread_only", help="Only consider allocation thread", action="store_true",
+ default=False)
+ parser.add_argument("--depth_limit", help="Limit the length of a trace", type=int, default=0)
+ args = parser.parse_args()
+ return args
+
+def main(argv):
+ args = parse_options()
+ trace_collection = TraceCollection(args)
+ trace_collection.read_file(args.filename)
+ trace_collection.dump_flame_graph()
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/tools/jvmti-agents/ti-alloc-sample/ti_alloc_sample.cc b/tools/jvmti-agents/ti-alloc-sample/ti_alloc_sample.cc
new file mode 100644
index 0000000000..d719db5af8
--- /dev/null
+++ b/tools/jvmti-agents/ti-alloc-sample/ti_alloc_sample.cc
@@ -0,0 +1,461 @@
+// Copyright (C) 2018 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 <android-base/logging.h>
+
+#include <atomic>
+#include <fstream>
+#include <iostream>
+#include <istream>
+#include <iomanip>
+#include <jni.h>
+#include <jvmti.h>
+#include <limits>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <sstream>
+#include <vector>
+
+namespace tifast {
+
+namespace {
+
+// Special art ti-version number. We will use this as a fallback if we cannot get a regular JVMTI
+// env.
+static constexpr jint kArtTiVersion = JVMTI_VERSION_1_2 | 0x40000000;
+
+// jthread is a typedef of jobject so we use this to allow the templates to distinguish them.
+struct jthreadContainer { jthread thread; };
+// jlocation is a typedef of jlong so use this to distinguish the less common jlong.
+struct jlongContainer { jlong val; };
+
+static void DeleteLocalRef(JNIEnv* env, jobject obj) {
+ if (obj != nullptr && env != nullptr) {
+ env->DeleteLocalRef(obj);
+ }
+}
+
+class ScopedThreadInfo {
+ public:
+ ScopedThreadInfo(jvmtiEnv* jvmtienv, JNIEnv* env, jthread thread)
+ : jvmtienv_(jvmtienv), env_(env), free_name_(false) {
+ if (thread == nullptr) {
+ info_.name = const_cast<char*>("<NULLPTR>");
+ } else if (jvmtienv->GetThreadInfo(thread, &info_) != JVMTI_ERROR_NONE) {
+ info_.name = const_cast<char*>("<UNKNOWN THREAD>");
+ } else {
+ free_name_ = true;
+ }
+ }
+
+ ~ScopedThreadInfo() {
+ if (free_name_) {
+ jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(info_.name));
+ }
+ DeleteLocalRef(env_, info_.thread_group);
+ DeleteLocalRef(env_, info_.context_class_loader);
+ }
+
+ const char* GetName() const {
+ return info_.name;
+ }
+
+ private:
+ jvmtiEnv* jvmtienv_;
+ JNIEnv* env_;
+ bool free_name_;
+ jvmtiThreadInfo info_{};
+};
+
+class ScopedClassInfo {
+ public:
+ ScopedClassInfo(jvmtiEnv* jvmtienv, jclass c) : jvmtienv_(jvmtienv), class_(c) {}
+
+ ~ScopedClassInfo() {
+ if (class_ != nullptr) {
+ jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(name_));
+ jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(generic_));
+ jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(file_));
+ jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(debug_ext_));
+ }
+ }
+
+ bool Init(bool get_generic = true) {
+ if (class_ == nullptr) {
+ name_ = const_cast<char*>("<NONE>");
+ generic_ = const_cast<char*>("<NONE>");
+ return true;
+ } else {
+ jvmtiError ret1 = jvmtienv_->GetSourceFileName(class_, &file_);
+ jvmtiError ret2 = jvmtienv_->GetSourceDebugExtension(class_, &debug_ext_);
+ char** gen_ptr = &generic_;
+ if (!get_generic) {
+ generic_ = nullptr;
+ gen_ptr = nullptr;
+ }
+ return jvmtienv_->GetClassSignature(class_, &name_, gen_ptr) == JVMTI_ERROR_NONE &&
+ ret1 != JVMTI_ERROR_MUST_POSSESS_CAPABILITY &&
+ ret1 != JVMTI_ERROR_INVALID_CLASS &&
+ ret2 != JVMTI_ERROR_MUST_POSSESS_CAPABILITY &&
+ ret2 != JVMTI_ERROR_INVALID_CLASS;
+ }
+ }
+
+ jclass GetClass() const {
+ return class_;
+ }
+
+ const char* GetName() const {
+ return name_;
+ }
+
+ const char* GetGeneric() const {
+ return generic_;
+ }
+
+ const char* GetSourceDebugExtension() const {
+ if (debug_ext_ == nullptr) {
+ return "<UNKNOWN_SOURCE_DEBUG_EXTENSION>";
+ } else {
+ return debug_ext_;
+ }
+ }
+ const char* GetSourceFileName() const {
+ if (file_ == nullptr) {
+ return "<UNKNOWN_FILE>";
+ } else {
+ return file_;
+ }
+ }
+
+ private:
+ jvmtiEnv* jvmtienv_;
+ jclass class_;
+ char* name_ = nullptr;
+ char* generic_ = nullptr;
+ char* file_ = nullptr;
+ char* debug_ext_ = nullptr;
+
+ friend std::ostream& operator<<(std::ostream &os, ScopedClassInfo const& m);
+};
+
+class ScopedMethodInfo {
+ public:
+ ScopedMethodInfo(jvmtiEnv* jvmtienv, JNIEnv* env, jmethodID m)
+ : jvmtienv_(jvmtienv), env_(env), method_(m) {}
+
+ ~ScopedMethodInfo() {
+ DeleteLocalRef(env_, declaring_class_);
+ jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(name_));
+ jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(signature_));
+ jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(generic_));
+ }
+
+ bool Init(bool get_generic = true) {
+ if (jvmtienv_->GetMethodDeclaringClass(method_, &declaring_class_) != JVMTI_ERROR_NONE) {
+ return false;
+ }
+ class_info_.reset(new ScopedClassInfo(jvmtienv_, declaring_class_));
+ jint nlines;
+ jvmtiLineNumberEntry* lines;
+ jvmtiError err = jvmtienv_->GetLineNumberTable(method_, &nlines, &lines);
+ if (err == JVMTI_ERROR_NONE) {
+ if (nlines > 0) {
+ first_line_ = lines[0].line_number;
+ }
+ jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(lines));
+ } else if (err != JVMTI_ERROR_ABSENT_INFORMATION &&
+ err != JVMTI_ERROR_NATIVE_METHOD) {
+ return false;
+ }
+ return class_info_->Init(get_generic) &&
+ (jvmtienv_->GetMethodName(method_, &name_, &signature_, &generic_) == JVMTI_ERROR_NONE);
+ }
+
+ const ScopedClassInfo& GetDeclaringClassInfo() const {
+ return *class_info_;
+ }
+
+ jclass GetDeclaringClass() const {
+ return declaring_class_;
+ }
+
+ const char* GetName() const {
+ return name_;
+ }
+
+ const char* GetSignature() const {
+ return signature_;
+ }
+
+ const char* GetGeneric() const {
+ return generic_;
+ }
+
+ jint GetFirstLine() const {
+ return first_line_;
+ }
+
+ private:
+ jvmtiEnv* jvmtienv_;
+ JNIEnv* env_;
+ jmethodID method_;
+ jclass declaring_class_ = nullptr;
+ std::unique_ptr<ScopedClassInfo> class_info_;
+ char* name_ = nullptr;
+ char* signature_ = nullptr;
+ char* generic_ = nullptr;
+ jint first_line_ = -1;
+};
+
+std::ostream& operator<<(std::ostream &os, ScopedClassInfo const& c) {
+ const char* generic = c.GetGeneric();
+ if (generic != nullptr) {
+ return os << c.GetName() << "<" << generic << ">" << " file: " << c.GetSourceFileName();
+ } else {
+ return os << c.GetName() << " file: " << c.GetSourceFileName();
+ }
+}
+
+class LockedStream {
+ public:
+ explicit LockedStream(const std::string& filepath) {
+ stream_.open(filepath, std::ofstream::out);
+ if (!stream_.is_open()) {
+ LOG(ERROR) << "====== JVMTI FAILED TO OPEN LOG FILE";
+ }
+ }
+ ~LockedStream() {
+ stream_.close();
+ }
+ void Write(const std::string& str) {
+ stream_ << str;
+ stream_.flush();
+ }
+ private:
+ std::ofstream stream_;
+};
+
+static LockedStream* stream = nullptr;
+
+class UniqueStringTable {
+ public:
+ UniqueStringTable() = default;
+ ~UniqueStringTable() = default;
+ std::string Intern(const std::string& header, const std::string& key) {
+ if (map_.find(key) == map_.end()) {
+ map_[key] = next_index_;
+ // Emit definition line. E.g., =123,string
+ stream->Write(header + std::to_string(next_index_) + "," + key + "\n");
+ ++next_index_;
+ }
+ return std::to_string(map_[key]);
+ }
+ private:
+ int32_t next_index_;
+ std::map<std::string, int32_t> map_;
+};
+
+static UniqueStringTable* string_table = nullptr;
+
+// Formatter for the thread, type, and size of an allocation.
+static std::string formatAllocation(jvmtiEnv* jvmti,
+ JNIEnv* jni,
+ jthreadContainer thr,
+ jclass klass,
+ jlongContainer size) {
+ ScopedThreadInfo sti(jvmti, jni, thr.thread);
+ std::ostringstream allocation;
+ allocation << "jthread[" << sti.GetName() << "]";
+ ScopedClassInfo sci(jvmti, klass);
+ if (sci.Init(/*get_generic=*/false)) {
+ allocation << ", jclass[" << sci << "]";
+ } else {
+ allocation << ", jclass[TYPE UNKNOWN]";
+ }
+ allocation << ", size[" << size.val << ", hex: 0x" << std::hex << size.val << "]";
+ return string_table->Intern("+", allocation.str());
+}
+
+// Formatter for a method entry on a call stack.
+static std::string formatMethod(jvmtiEnv* jvmti, JNIEnv* jni, jmethodID method_id) {
+ ScopedMethodInfo smi(jvmti, jni, method_id);
+ std::string method;
+ if (smi.Init(/*get_generic=*/false)) {
+ method = std::string(smi.GetDeclaringClassInfo().GetName()) +
+ "::" + smi.GetName() + smi.GetSignature();
+ } else {
+ method = "ERROR";
+ }
+ return string_table->Intern("+", method);
+}
+
+static int sampling_rate;
+static int stack_depth_limit;
+
+static void JNICALL logVMObjectAlloc(jvmtiEnv* jvmti,
+ JNIEnv* jni,
+ jthread thread,
+ jobject obj ATTRIBUTE_UNUSED,
+ jclass klass,
+ jlong size) {
+ // Sample only once out of sampling_rate tries, and prevent recursive allocation tracking,
+ static thread_local int sample_countdown = sampling_rate;
+ --sample_countdown;
+ if (sample_countdown != 0) {
+ return;
+ }
+
+ // Guard accesses to string table and emission.
+ static std::mutex mutex;
+ std::lock_guard<std::mutex> lg(mutex);
+
+ std::string record =
+ formatAllocation(jvmti,
+ jni,
+ jthreadContainer{.thread = thread},
+ klass,
+ jlongContainer{.val = size});
+
+ std::unique_ptr<jvmtiFrameInfo[]> stack_frames(new jvmtiFrameInfo[stack_depth_limit]);
+ jint stack_depth;
+ jvmtiError err = jvmti->GetStackTrace(thread,
+ 0,
+ stack_depth_limit,
+ stack_frames.get(),
+ &stack_depth);
+ if (err == JVMTI_ERROR_NONE) {
+ // Emit stack frames in order from deepest in the stack to most recent.
+ // This simplifies post-collection processing.
+ for (int i = stack_depth - 1; i >= 0; --i) {
+ record += ";" + formatMethod(jvmti, jni, stack_frames[i].method);
+ }
+ }
+ stream->Write(string_table->Intern("=", record) + "\n");
+
+ sample_countdown = sampling_rate;
+}
+
+static jvmtiEventCallbacks kLogCallbacks {
+ .VMObjectAlloc = logVMObjectAlloc,
+};
+
+static jint SetupJvmtiEnv(JavaVM* vm, jvmtiEnv** jvmti) {
+ jint res = vm->GetEnv(reinterpret_cast<void**>(jvmti), JVMTI_VERSION_1_1);
+ if (res != JNI_OK || *jvmti == nullptr) {
+ LOG(ERROR) << "Unable to access JVMTI, error code " << res;
+ return vm->GetEnv(reinterpret_cast<void**>(jvmti), kArtTiVersion);
+ }
+ return res;
+}
+
+} // namespace
+
+static jvmtiError SetupCapabilities(jvmtiEnv* jvmti) {
+ jvmtiCapabilities caps{};
+ caps.can_generate_vm_object_alloc_events = 1;
+ caps.can_get_line_numbers = 1;
+ caps.can_get_source_file_name = 1;
+ caps.can_get_source_debug_extension = 1;
+ return jvmti->AddCapabilities(&caps);
+}
+
+static bool ProcessOptions(std::string options) {
+ std::string output_file_path;
+ if (options.empty()) {
+ static constexpr int kDefaultSamplingRate = 10;
+ static constexpr int kDefaultStackDepthLimit = 50;
+ static constexpr const char* kDefaultOutputFilePath = "/data/local/tmp/logstream.txt";
+
+ sampling_rate = kDefaultSamplingRate;
+ stack_depth_limit = kDefaultStackDepthLimit;
+ output_file_path = kDefaultOutputFilePath;
+ } else {
+ // options string should contain "sampling_rate,stack_depth_limit,output_file_path".
+ size_t comma_pos = options.find(',');
+ if (comma_pos == std::string::npos) {
+ return false;
+ }
+ sampling_rate = std::stoi(options.substr(0, comma_pos));
+ options = options.substr(comma_pos + 1);
+ comma_pos = options.find(',');
+ if (comma_pos == std::string::npos) {
+ return false;
+ }
+ stack_depth_limit = std::stoi(options.substr(0, comma_pos));
+ output_file_path = options.substr(comma_pos + 1);
+ }
+ LOG(INFO) << "Starting allocation tracing: sampling_rate=" << sampling_rate
+ << ", stack_depth_limit=" << stack_depth_limit
+ << ", output_file_path=" << output_file_path;
+ stream = new LockedStream(output_file_path);
+
+ return true;
+}
+
+static jint AgentStart(JavaVM* vm,
+ char* options,
+ void* reserved ATTRIBUTE_UNUSED) {
+ // Handle the sampling rate, depth limit, and output path, if set.
+ if (!ProcessOptions(options)) {
+ return JNI_ERR;
+ }
+
+ // Create the environment.
+ jvmtiEnv* jvmti = nullptr;
+ if (SetupJvmtiEnv(vm, &jvmti) != JNI_OK) {
+ LOG(ERROR) << "Could not get JVMTI env or ArtTiEnv!";
+ return JNI_ERR;
+ }
+
+ jvmtiError error = SetupCapabilities(jvmti);
+ if (error != JVMTI_ERROR_NONE) {
+ LOG(ERROR) << "Unable to set caps";
+ return JNI_ERR;
+ }
+
+ // Add callbacks and notification.
+ error = jvmti->SetEventCallbacks(&kLogCallbacks, static_cast<jint>(sizeof(kLogCallbacks)));
+ if (error != JVMTI_ERROR_NONE) {
+ LOG(ERROR) << "Unable to set event callbacks.";
+ return JNI_ERR;
+ }
+ error = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_VM_OBJECT_ALLOC,
+ nullptr /* all threads */);
+ if (error != JVMTI_ERROR_NONE) {
+ LOG(ERROR) << "Unable to enable event " << JVMTI_EVENT_VM_OBJECT_ALLOC;
+ return JNI_ERR;
+ }
+
+ string_table = new UniqueStringTable();
+
+ return JNI_OK;
+}
+
+// Late attachment (e.g. 'am attach-agent').
+extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *vm, char* options, void* reserved) {
+ return AgentStart(vm, options, reserved);
+}
+
+// Early attachment
+extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm, char* options, void* reserved) {
+ return AgentStart(jvm, options, reserved);
+}
+
+} // namespace tifast
+
diff --git a/tools/libcore_no_getrandom_failures.txt b/tools/libcore_no_getrandom_failures.txt
index f2926e452c..05af9d7e19 100644
--- a/tools/libcore_no_getrandom_failures.txt
+++ b/tools/libcore_no_getrandom_failures.txt
@@ -15,6 +15,21 @@
"libcore.java.math.BigIntegerTest#test_probablePrime",
"libcore.javax.crypto.CipherInputStreamTest#testDecryptCorruptGCM",
"libcore.javax.crypto.CipherOutputStreamTest#testDecryptCorruptGCM",
+ "libcore.libcore.timezone.TelephonyLookupTest#createInstanceWithFallback",
+ "libcore.libcore.timezone.TelephonyLookupTest#getTelephonyNetworkFinder",
+ "libcore.libcore.timezone.TelephonyLookupTest#validateCountryCodeLowerCase",
+ "libcore.libcore.timezone.TelephonyLookupTest#validateDuplicateMccMnc",
+ "libcore.libcore.timezone.TelephonyLookupTest#xmlParsing_emptyFile",
+ "libcore.libcore.timezone.TelephonyLookupTest#xmlParsing_emptyNetworksOk",
+ "libcore.libcore.timezone.TelephonyLookupTest#xmlParsing_missingCountryCodeAttribute",
+ "libcore.libcore.timezone.TelephonyLookupTest#xmlParsing_missingMccAttribute",
+ "libcore.libcore.timezone.TelephonyLookupTest#xmlParsing_missingMncAttribute",
+ "libcore.libcore.timezone.TelephonyLookupTest#xmlParsing_missingNetworks",
+ "libcore.libcore.timezone.TelephonyLookupTest#xmlParsing_truncatedInput",
+ "libcore.libcore.timezone.TelephonyLookupTest#xmlParsing_unexpectedComments",
+ "libcore.libcore.timezone.TelephonyLookupTest#xmlParsing_unexpectedElementsIgnored",
+ "libcore.libcore.timezone.TelephonyLookupTest#xmlParsing_unexpectedRootElement",
+ "libcore.libcore.timezone.TelephonyLookupTest#xmlParsing_unexpectedTextIgnored",
"libcore.libcore.timezone.TimeZoneFinderTest#createInstanceWithFallback",
"libcore.libcore.timezone.TimeZoneFinderTest#getCountryZonesFinder",
"libcore.libcore.timezone.TimeZoneFinderTest#getCountryZonesFinder_empty",