[automerger skipped] Snap tm-dev to android13-tests-dev am: 474f318add am: 99887e2870 am: 1900909bf8 -s ours
am skip reason: Merged-In I347582df87e2bce0142d0210519f013d060a0180 with SHA-1 daa9be44dc is already in history
Original change: https://googleplex-android-review.googlesource.com/c/platform/art/+/20514366
Change-Id: I20513d43e271035824bbe3cb7c0bca0429ad053d
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/.clang-format b/.clang-format
index 0d6a160..ba0bff3 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,3 +1,4 @@
+# Please keep in sync with .clang-format-java-2 where applicable.
---
BasedOnStyle: Google
---
@@ -22,3 +23,24 @@
FixNamespaceComments: true
PointerAlignment: Left
TabWidth: 2
+
+---
+
+Language: Java
+
+ColumnLimit: 100
+CommentPragmas: (NOLINT|CHECK[^ ]*):.*
+ContinuationIndentWidth: 8
+IndentWidth: 4
+JavaImportGroups:
+- android
+- androidx
+- com.android
+- dalvik
+- libcore
+- com
+- junit
+- net
+- org
+- java
+- javax
diff --git a/.clang-format-java-2 b/.clang-format-java-2
new file mode 100644
index 0000000..956bdcd
--- /dev/null
+++ b/.clang-format-java-2
@@ -0,0 +1,48 @@
+# Variant of .clang-format but with 2 space indent for Java files, for use in
+# subdirectories with that style.
+# Please keep in sync with .clang-format where applicable.
+---
+BasedOnStyle: Google
+---
+
+Language: Cpp
+
+AlignConsecutiveMacros: AcrossComments
+AllowShortBlocksOnASingleLine: Empty
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortIfStatementsOnASingleLine: Never
+AllowShortLoopsOnASingleLine: false
+AttributeMacros: ['__', 'NO_RETURN']
+BinPackArguments: false
+BinPackParameters: false
+BreakConstructorInitializers: BeforeColon
+BreakBeforeTernaryOperators: false
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+Cpp11BracedListStyle: true
+DerivePointerAlignment: false
+FixNamespaceComments: true
+PointerAlignment: Left
+TabWidth: 2
+
+---
+
+Language: Java
+
+ColumnLimit: 100
+CommentPragmas: (NOLINT|CHECK[^ ]*):.*
+ContinuationIndentWidth: 4
+IndentWidth: 2
+JavaImportGroups:
+- android
+- androidx
+- com.android
+- dalvik
+- libcore
+- com
+- junit
+- net
+- org
+- java
+- javax
diff --git a/Android.mk b/Android.mk
index 9257c22..d560276 100644
--- a/Android.mk
+++ b/Android.mk
@@ -380,76 +380,6 @@
art_apex_manifest_file :=
-#######################
-# Fake packages for ART
-
-# The art-runtime package depends on the core ART libraries and binaries. It exists so we can
-# manipulate the set of things shipped, e.g., add debug versions and so on.
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := art-runtime
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
-
-# Reference the libraries and binaries in the appropriate APEX module, because
-# they don't have platform variants. However if
-# ART_MODULE_BUILD_FROM_SOURCE isn't true then the APEX
-# modules are disabled, so Soong won't apply the APEX mutators to them, and
-# then they are available with their plain names.
-ifeq (true,$(ART_MODULE_BUILD_FROM_SOURCE))
- art_module_lib = $(1).com.android.art
- art_module_debug_lib = $(1).com.android.art.debug
-else
- art_module_lib = $(1)
- art_module_debug_lib = $(1)
-endif
-
-# Base requirements.
-LOCAL_REQUIRED_MODULES := \
- $(call art_module_lib,dalvikvm) \
- $(call art_module_lib,dex2oat) \
- $(call art_module_lib,dexoptanalyzer) \
- $(call art_module_lib,libart) \
- $(call art_module_lib,libart-compiler) \
- $(call art_module_lib,libopenjdkjvm) \
- $(call art_module_lib,libopenjdkjvmti) \
- $(call art_module_lib,odrefresh) \
- $(call art_module_lib,profman) \
- $(call art_module_lib,libadbconnection) \
- $(call art_module_lib,libperfetto_hprof) \
-
-# Potentially add in debug variants:
-#
-# * We will never add them if PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD = false.
-# * We will always add them if PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD = true.
-# * Otherwise, we will add them by default to eng builds.
-art_target_include_debug_build := $(PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD)
-ifneq (false,$(art_target_include_debug_build))
-ifneq (,$(filter eng,$(TARGET_BUILD_VARIANT)))
- art_target_include_debug_build := true
-endif
-ifeq (true,$(art_target_include_debug_build))
-LOCAL_REQUIRED_MODULES += \
- $(call art_module_debug_lib,dex2oatd) \
- $(call art_module_debug_lib,dexoptanalyzerd) \
- $(call art_module_debug_lib,libartd) \
- $(call art_module_debug_lib,libartd-compiler) \
- $(call art_module_debug_lib,libopenjdkd) \
- $(call art_module_debug_lib,libopenjdkjvmd) \
- $(call art_module_debug_lib,libopenjdkjvmtid) \
- $(call art_module_debug_lib,profmand) \
- $(call art_module_debug_lib,libadbconnectiond) \
- $(call art_module_debug_lib,libperfetto_hprofd) \
-
-endif
-endif
-
-art_module_lib :=
-art_module_debug_lib :=
-
-include $(BUILD_PHONY_PACKAGE)
-
####################################################################################################
# Fake packages to ensure generation of libopenjdkd when one builds with mm/mmm/mmma.
#
@@ -521,7 +451,6 @@
lib/libart-disassembler.so \
lib/libartpalette.so \
lib/libart.so \
- lib/libbacktrace.so \
lib/libdexfile.so \
lib/libdt_fd_forward.so \
lib/libdt_socket.so \
@@ -542,7 +471,6 @@
lib/libprofile.so \
lib/libsigchain.so \
lib/libunwindstack.so \
- lib/libziparchive.so \
lib64/libadbconnection.so \
lib64/libandroidio.so \
lib64/libartbase.so \
@@ -551,7 +479,6 @@
lib64/libart-disassembler.so \
lib64/libartpalette.so \
lib64/libart.so \
- lib64/libbacktrace.so \
lib64/libdexfile.so \
lib64/libdt_fd_forward.so \
lib64/libdt_socket.so \
@@ -572,7 +499,6 @@
lib64/libprofile.so \
lib64/libsigchain.so \
lib64/libunwindstack.so \
- lib64/libziparchive.so \
PRIVATE_RUNTIME_APEX_DEPENDENCY_FILES := \
bin/linker \
@@ -625,7 +551,10 @@
rm -rf $$apex_dir && \
mkdir -p $$apex_dir && \
debugfs=$(HOST_OUT)/bin/debugfs_static && \
- $(HOST_OUT)/bin/deapexer --debugfs_path $$debugfs extract $$apex_file $$apex_dir; \
+ blkid=$(HOST_OUT)/bin/blkid_static && \
+ fsckerofs=$(HOST_OUT)/bin/fsck.erofs && \
+ $(HOST_OUT)/bin/deapexer --debugfs_path $$debugfs --blkid_path $$blkid \
+ --fsckerofs_path $$fsckerofs extract $$apex_file $$apex_dir; \
fi && \
for f in $(2); do \
sf=$$apex_dir/$$f && \
@@ -649,6 +578,13 @@
#
# TODO(b/129332183): Remove this when Golem has full support for the
# ART APEX.
+#
+# TODO(b/129332183): This approach is flawed: We mix DSOs from prebuilt APEXes
+# and prebuilts/runtime/mainline/platform/impl with source built ones, and both
+# may depend on the same DSOs, and some of them don't have stable ABIs.
+# libbase.so in particular is such a problematic dependency. When those
+# dependencies eventually don't work anymore we don't have much choice but to
+# update all prebuilts.
.PHONY: standalone-apex-files
standalone-apex-files: deapexer \
$(RELEASE_ART_APEX) \
@@ -664,16 +600,11 @@
# Also, platform libraries are installed in prebuilts, so copy them over.
$(call extract-from-apex,$(RUNTIME_APEX),\
$(PRIVATE_RUNTIME_APEX_DEPENDENCY_FILES)) && \
- for libdir in $(TARGET_OUT)/lib $(TARGET_OUT)/lib64; do \
- if [ -d $$libdir/bionic ]; then \
- mv -f $$libdir/bionic/*.so $$libdir; \
- fi || exit 1; \
- done && \
- for libdir in $(TARGET_OUT)/lib $(TARGET_OUT)/lib64; do \
- if [ -d $$libdir ]; then \
- cp prebuilts/runtime/mainline/platform/impl/$(TARGET_ARCH)/*.so $$libdir; \
- fi || exit 1; \
- done
+ libdir=$(TARGET_OUT)/lib$$(expr $(TARGET_ARCH) : '.*\(64\)' || :) && \
+ if [ -d $$libdir/bionic ]; then \
+ mv -f $$libdir/bionic/*.so $$libdir; \
+ fi && \
+ cp prebuilts/runtime/mainline/platform/impl/$(TARGET_ARCH)/*.so $$libdir
$(call extract-from-apex,$(CONSCRYPT_APEX),\
$(PRIVATE_CONSCRYPT_APEX_DEPENDENCY_LIBS))
$(call extract-from-apex,$(I18N_APEX),\
@@ -706,6 +637,7 @@
$(TARGET_OUT_EXECUTABLES)/dex2oat_wrapper \
$(ART_TARGET_PLATFORM_DEPENDENCIES) \
$(ART_TARGET_SHARED_LIBRARY_BENCHMARK) \
+ $(TARGET_OUT_SHARED_LIBRARIES)/libgolemtiagent.so \
$(PRODUCT_OUT)/apex/art_boot_images/javalib/$(TARGET_ARCH)/boot.art \
standalone-apex-files
# remove debug libraries from public.libraries.txt because golem builds
@@ -723,22 +655,21 @@
ART_HOST_SHARED_LIBRARY_BENCHMARK := $(ART_HOST_OUT_SHARED_LIBRARIES)/libartbenchmark.so
build-art-host-golem: build-art-host \
$(ART_HOST_SHARED_LIBRARY_BENCHMARK) \
+ $(ART_HOST_OUT_SHARED_LIBRARIES)/libgolemtiagent.so \
$(HOST_OUT_EXECUTABLES)/dex2oat_wrapper
########################################################################
-# Phony target for building what go/lem requires for syncing /system to target.
-.PHONY: build-art-unbundled-golem
-art_apex_jars := $(foreach pair,$(ART_APEX_JARS), $(call word-colon,2,$(pair)))
-build-art-unbundled-golem: art-runtime linker oatdump $(art_apex_jars) conscrypt crash_dump
-
-########################################################################
# Rules for building all dependencies for tests.
.PHONY: build-art-host-gtests build-art-host-run-tests build-art-host-tests
build-art-host-gtests: build-art-host $(ART_TEST_HOST_GTEST_DEPENDENCIES)
-build-art-host-run-tests: build-art-host $(TEST_ART_RUN_TEST_DEPENDENCIES) $(ART_TEST_HOST_RUN_TEST_DEPENDENCIES)
+build-art-host-run-tests: build-art-host \
+ $(TEST_ART_RUN_TEST_DEPENDENCIES) \
+ $(ART_TEST_HOST_RUN_TEST_DEPENDENCIES) \
+ art-run-test-host-data \
+ art-run-test-jvm-data
build-art-host-tests: build-art-host-gtests build-art-host-run-tests
@@ -746,7 +677,10 @@
build-art-target-gtests: build-art-target $(ART_TEST_TARGET_GTEST_DEPENDENCIES)
-build-art-target-run-tests: build-art-target $(TEST_ART_RUN_TEST_DEPENDENCIES) $(ART_TEST_TARGET_RUN_TEST_DEPENDENCIES)
+build-art-target-run-tests: build-art-target \
+ $(TEST_ART_RUN_TEST_DEPENDENCIES) \
+ $(ART_TEST_TARGET_RUN_TEST_DEPENDENCIES) \
+ art-run-test-target-data
build-art-target-tests: build-art-target-gtests build-art-target-run-tests
@@ -882,7 +816,7 @@
define create_public_sdk_dex
public_sdk_$(1)_stub := $$(call get_public_sdk_stub_dex,$(1))
$$(public_sdk_$(1)_stub): PRIVATE_MIN_SDK_VERSION := $(1)
-$$(public_sdk_$(1)_stub): $$(call resolve-prebuilt-sdk-jar-path,$(1)) $$(DX) $$(ZIP2ZIP)
+$$(public_sdk_$(1)_stub): $$(call resolve-prebuilt-sdk-jar-path,$(1)) $$(D8) $$(ZIP2ZIP)
$$(transform-classes.jar-to-dex)
$$(call declare-1p-target,$$(public_sdk_$(1)_stub),art)
diff --git a/OWNERS b/OWNERS
index 06c7d08..cfaa94b 100644
--- a/OWNERS
+++ b/OWNERS
@@ -4,16 +4,18 @@
jiakaiz@google.com
lokeshgidra@google.com
mast@google.com
-miguelaranda@google.com
-mingaleev@google.com
mythria@google.com
ngeoffray@google.com
+rpl@google.com
+solanes@google.com
+vmarko@google.com
+
+# Core Libraries.
+miguelaranda@google.com
+mingaleev@google.com
nikitai@google.com
oth@google.com
prb@google.com
-rpl@google.com
skvadrik@google.com
-solanes@google.com
sorinbasca@google.com
vichang@google.com
-vmarko@google.com
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index b8ee3c5..c5b3c9c 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -7,11 +7,53 @@
# so we don't need the custom runtests script and this check.
check_libnativebridge_test_field = libnativebridge/tests/preupload_check_test_tag.sh ${PREUPLOAD_COMMIT_MESSAGE} ${PREUPLOAD_FILES}
+check_expectation_jsons = tools/check_presubmit_json_expectations.sh ${REPO_ROOT} ${PREUPLOAD_FILES}
+
[Builtin Hooks]
-cpplint = true
bpfmt = true
+clang_format = true
+cpplint = true
gofmt = true
[Builtin Hooks Options]
+# Enable clang-format in all directories except test/, because there are many
+# test Java files with 2 space indent which won't be handled well if they
+# change. Unfortunately there is no way to exclude a directory for a builtin
+# hook.
+clang_format = --commit ${PREUPLOAD_COMMIT} --style file
+ adbconnection/
+ artd/
+ benchmark/
+ build/
+ cmdline/
+ compiler/
+ dalvikvm/
+ dex2oat/
+ dexdump/
+ dexlayout/
+ dexlist/
+ dexoptanalyzer/
+ disassembler/
+ dt_fd_forward/
+ imgdiag/
+ libartbase/
+ libartpalette/
+ libartservice/
+ libarttools/
+ libdexfile/
+ libelffile/
+ libnativebridge/
+ libnativeloader/
+ libprofile/
+ oatdump/
+ odrefresh/
+ openjdkjvm/
+ openjdkjvmti/
+ perfetto_hprof/
+ profman/
+ runtime/
+ sigchainlib/
+ simulator/
+ tools/
# Cpplint prints nothing unless there were errors.
cpplint = --quiet ${PREUPLOAD_FILES}
diff --git a/TEST_MAPPING b/TEST_MAPPING
index e799c0a..86a8888 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -71,6 +71,9 @@
"name": "art-run-test-022-interface[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-024-illegal-access[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-025-access-controller[com.google.android.art.apex]"
},
{
@@ -86,6 +89,9 @@
"name": "art-run-test-029-assert[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-032-concrete-sub[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-033-class-init-deadlock[com.google.android.art.apex]"
},
{
@@ -107,6 +113,9 @@
"name": "art-run-test-041-narrowing[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-042-new-instance[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-043-privates[com.google.android.art.apex]"
},
{
@@ -155,6 +164,9 @@
"name": "art-run-test-067-preemptive-unpark[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-069-field-type[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-070-nio-buffer[com.google.android.art.apex]"
},
{
@@ -164,12 +176,21 @@
"name": "art-run-test-072-reachability-fence[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-073-mismatched-field[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-074-gc-thrash[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-075-verification-error[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-076-boolean-put[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-077-method-override[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-078-polymorphic-virtual[com.google.android.art.apex]"
},
{
@@ -305,15 +326,9 @@
"name": "art-run-test-143-string-value[com.google.android.art.apex]"
},
{
- "name": "art-run-test-144-static-field-sigquit[com.google.android.art.apex]"
- },
- {
"name": "art-run-test-145-alloc-tracking-stress[com.google.android.art.apex]"
},
{
- "name": "art-run-test-151-OpenFileLimit[com.google.android.art.apex]"
- },
- {
"name": "art-run-test-152-dead-large-object[com.google.android.art.apex]"
},
{
@@ -350,6 +365,9 @@
"name": "art-run-test-176-app-image-string[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-182-method-linking[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-1960-checker-bounds-codegen[com.google.android.art.apex]"
},
{
@@ -401,10 +419,19 @@
"name": "art-run-test-2042-checker-dce-always-throw[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-2042-reference-processing[com.google.android.art.apex]"
+ },
+ {
+ "name": "art-run-test-2043-reference-pauses[com.google.android.art.apex]"
+ },
+ {
+ "name": "art-run-test-2044-get-stack-traces[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-2231-checker-heap-poisoning[com.google.android.art.apex]"
},
{
- "name": "art-run-test-2232-write-metrics-to-log[com.google.android.art.apex]"
+ "name": "art-run-test-2233-checker-remove-loop-suspend-check[com.google.android.art.apex]"
},
{
"name": "art-run-test-2234-checker-remove-entry-suspendcheck[com.google.android.art.apex]"
@@ -413,6 +440,18 @@
"name": "art-run-test-2236-JdkUnsafeGetLong-regression[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-2241-checker-inline-try-catch[com.google.android.art.apex]"
+ },
+ {
+ "name": "art-run-test-2242-checker-lse-acquire-release-operations[com.google.android.art.apex]"
+ },
+ {
+ "name": "art-run-test-2243-checker-not-inline-into-throw[com.google.android.art.apex]"
+ },
+ {
+ "name": "art-run-test-2244-checker-remove-try-boundary[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-300-package-override[com.google.android.art.apex]"
},
{
@@ -800,9 +839,15 @@
"name": "art-run-test-536-checker-intrinsic-optimization[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-536-checker-needs-access-check[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-537-checker-arraycopy[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-537-checker-inline-and-unverified[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-537-checker-jump-over-jump[com.google.android.art.apex]"
},
{
@@ -1100,6 +1145,9 @@
"name": "art-run-test-662-regression-alias[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-663-checker-select-generator[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-665-checker-simd-zero[com.google.android.art.apex]"
},
{
@@ -1223,16 +1271,42 @@
"name": "art-run-test-835-b216762268[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-838-override[com.google.android.art.apex]"
+ },
+ {
+ "name": "art-run-test-839-clinit-throw[com.google.android.art.apex]"
+ },
+ {
+ "name": "art-run-test-841-defaults[com.google.android.art.apex]"
+ },
+ {
+ "name": "art-run-test-843-default-interface[com.google.android.art.apex]"
+ },
+ {
"name": "art-run-test-963-default-range-smali[com.google.android.art.apex]"
},
{
+ "name": "art-run-test-965-default-verify[com.google.android.art.apex]"
+ },
+ {
+ "name": "art-run-test-967-default-ame[com.google.android.art.apex]"
+ },
+ {
"name": "art_libnativebridge_cts_tests[com.google.android.art.apex]"
},
{
+ "name": "art_standalone_artd_tests[com.google.android.art.apex]"
+ },
+ {
"name": "art_standalone_cmdline_tests[com.google.android.art.apex]"
},
{
- "name": "art_standalone_compiler_tests[com.google.android.art.apex]"
+ "name": "art_standalone_compiler_tests[com.google.android.art.apex]",
+ "options": [
+ {
+ "exclude-filter": "JniCompilerTest*"
+ }
+ ]
},
{
"name": "art_standalone_dexdump_tests[com.google.android.art.apex]"
@@ -1265,10 +1339,10 @@
"name": "art_standalone_oatdump_tests[com.google.android.art.apex]"
},
{
- "name": "art_standalone_profman_tests[com.google.android.art.apex]"
+ "name": "art_standalone_odrefresh_tests[com.google.android.art.apex]"
},
{
- "name": "art_standalone_runtime_compiler_tests[com.google.android.art.apex]"
+ "name": "art_standalone_profman_tests[com.google.android.art.apex]"
},
{
"name": "art_standalone_runtime_tests[com.google.android.art.apex]"
@@ -1277,21 +1351,27 @@
"name": "art_standalone_sigchain_tests[com.google.android.art.apex]"
},
{
+ "name": "libnativeloader_e2e_tests[com.google.android.art.apex]"
+ },
+ {
"name": "libnativeloader_test[com.google.android.art.apex]"
}
],
"presubmit": [
{
- "name": "CtsJdwpTestCases"
+ "name": "ArtServiceTests"
},
{
"name": "BootImageProfileTest"
},
{
- "name": "ArtServiceTests"
+ "name": "ComposHostTestCases"
},
{
- "name": "ComposHostTestCases"
+ "name": "CtsJdwpTestCases"
+ },
+ {
+ "name": "art-apex-update-rollback"
},
{
"name": "art_standalone_dexpreopt_tests"
@@ -1363,6 +1443,9 @@
"name": "art-run-test-022-interface"
},
{
+ "name": "art-run-test-024-illegal-access"
+ },
+ {
"name": "art-run-test-025-access-controller"
},
{
@@ -1378,6 +1461,9 @@
"name": "art-run-test-029-assert"
},
{
+ "name": "art-run-test-032-concrete-sub"
+ },
+ {
"name": "art-run-test-033-class-init-deadlock"
},
{
@@ -1399,6 +1485,9 @@
"name": "art-run-test-041-narrowing"
},
{
+ "name": "art-run-test-042-new-instance"
+ },
+ {
"name": "art-run-test-043-privates"
},
{
@@ -1447,6 +1536,9 @@
"name": "art-run-test-067-preemptive-unpark"
},
{
+ "name": "art-run-test-069-field-type"
+ },
+ {
"name": "art-run-test-070-nio-buffer"
},
{
@@ -1456,12 +1548,21 @@
"name": "art-run-test-072-reachability-fence"
},
{
+ "name": "art-run-test-073-mismatched-field"
+ },
+ {
"name": "art-run-test-074-gc-thrash"
},
{
+ "name": "art-run-test-075-verification-error"
+ },
+ {
"name": "art-run-test-076-boolean-put"
},
{
+ "name": "art-run-test-077-method-override"
+ },
+ {
"name": "art-run-test-078-polymorphic-virtual"
},
{
@@ -1597,15 +1698,9 @@
"name": "art-run-test-143-string-value"
},
{
- "name": "art-run-test-144-static-field-sigquit"
- },
- {
"name": "art-run-test-145-alloc-tracking-stress"
},
{
- "name": "art-run-test-151-OpenFileLimit"
- },
- {
"name": "art-run-test-152-dead-large-object"
},
{
@@ -1642,6 +1737,9 @@
"name": "art-run-test-176-app-image-string"
},
{
+ "name": "art-run-test-182-method-linking"
+ },
+ {
"name": "art-run-test-1960-checker-bounds-codegen"
},
{
@@ -1693,10 +1791,19 @@
"name": "art-run-test-2042-checker-dce-always-throw"
},
{
+ "name": "art-run-test-2042-reference-processing"
+ },
+ {
+ "name": "art-run-test-2043-reference-pauses"
+ },
+ {
+ "name": "art-run-test-2044-get-stack-traces"
+ },
+ {
"name": "art-run-test-2231-checker-heap-poisoning"
},
{
- "name": "art-run-test-2232-write-metrics-to-log"
+ "name": "art-run-test-2233-checker-remove-loop-suspend-check"
},
{
"name": "art-run-test-2234-checker-remove-entry-suspendcheck"
@@ -1705,6 +1812,18 @@
"name": "art-run-test-2236-JdkUnsafeGetLong-regression"
},
{
+ "name": "art-run-test-2241-checker-inline-try-catch"
+ },
+ {
+ "name": "art-run-test-2242-checker-lse-acquire-release-operations"
+ },
+ {
+ "name": "art-run-test-2243-checker-not-inline-into-throw"
+ },
+ {
+ "name": "art-run-test-2244-checker-remove-try-boundary"
+ },
+ {
"name": "art-run-test-300-package-override"
},
{
@@ -2092,9 +2211,15 @@
"name": "art-run-test-536-checker-intrinsic-optimization"
},
{
+ "name": "art-run-test-536-checker-needs-access-check"
+ },
+ {
"name": "art-run-test-537-checker-arraycopy"
},
{
+ "name": "art-run-test-537-checker-inline-and-unverified"
+ },
+ {
"name": "art-run-test-537-checker-jump-over-jump"
},
{
@@ -2392,6 +2517,9 @@
"name": "art-run-test-662-regression-alias"
},
{
+ "name": "art-run-test-663-checker-select-generator"
+ },
+ {
"name": "art-run-test-665-checker-simd-zero"
},
{
@@ -2515,12 +2643,33 @@
"name": "art-run-test-835-b216762268"
},
{
+ "name": "art-run-test-838-override"
+ },
+ {
+ "name": "art-run-test-839-clinit-throw"
+ },
+ {
+ "name": "art-run-test-841-defaults"
+ },
+ {
+ "name": "art-run-test-843-default-interface"
+ },
+ {
"name": "art-run-test-963-default-range-smali"
},
{
+ "name": "art-run-test-965-default-verify"
+ },
+ {
+ "name": "art-run-test-967-default-ame"
+ },
+ {
"name": "art_libnativebridge_cts_tests"
},
{
+ "name": "art_standalone_artd_tests"
+ },
+ {
"name": "art_standalone_cmdline_tests"
},
{
@@ -2563,7 +2712,1371 @@
"name": "art_standalone_profman_tests"
},
{
- "name": "art_standalone_runtime_compiler_tests"
+ "name": "art_standalone_runtime_tests"
+ },
+ {
+ "name": "art_standalone_sigchain_tests"
+ },
+ {
+ "name": "libnativeloader_e2e_tests"
+ },
+ {
+ "name": "libnativeloader_test"
+ }
+ ],
+ "hwasan-presubmit": [
+ {
+ "name": "ArtServiceTests"
+ },
+ {
+ "name": "ComposHostTestCases"
+ },
+ {
+ "name": "art-apex-update-rollback"
+ },
+ {
+ "name": "art_standalone_dexpreopt_tests"
+ },
+ {
+ "name": "art-run-test-001-HelloWorld"
+ },
+ {
+ "name": "art-run-test-001-Main"
+ },
+ {
+ "name": "art-run-test-002-sleep"
+ },
+ {
+ "name": "art-run-test-004-InterfaceTest"
+ },
+ {
+ "name": "art-run-test-004-NativeAllocations"
+ },
+ {
+ "name": "art-run-test-004-checker-UnsafeTest18"
+ },
+ {
+ "name": "art-run-test-006-args"
+ },
+ {
+ "name": "art-run-test-007-count10"
+ },
+ {
+ "name": "art-run-test-009-instanceof"
+ },
+ {
+ "name": "art-run-test-010-instance"
+ },
+ {
+ "name": "art-run-test-011-array-copy"
+ },
+ {
+ "name": "art-run-test-012-math"
+ },
+ {
+ "name": "art-run-test-013-math2"
+ },
+ {
+ "name": "art-run-test-014-math3"
+ },
+ {
+ "name": "art-run-test-015-switch"
+ },
+ {
+ "name": "art-run-test-016-intern"
+ },
+ {
+ "name": "art-run-test-017-float"
+ },
+ {
+ "name": "art-run-test-018-stack-overflow"
+ },
+ {
+ "name": "art-run-test-019-wrong-array-type"
+ },
+ {
+ "name": "art-run-test-020-string"
+ },
+ {
+ "name": "art-run-test-021-string2"
+ },
+ {
+ "name": "art-run-test-022-interface"
+ },
+ {
+ "name": "art-run-test-024-illegal-access"
+ },
+ {
+ "name": "art-run-test-025-access-controller"
+ },
+ {
+ "name": "art-run-test-026-access"
+ },
+ {
+ "name": "art-run-test-027-arithmetic"
+ },
+ {
+ "name": "art-run-test-028-array-write"
+ },
+ {
+ "name": "art-run-test-029-assert"
+ },
+ {
+ "name": "art-run-test-032-concrete-sub"
+ },
+ {
+ "name": "art-run-test-033-class-init-deadlock"
+ },
+ {
+ "name": "art-run-test-035-enum"
+ },
+ {
+ "name": "art-run-test-036-finalizer"
+ },
+ {
+ "name": "art-run-test-037-inherit"
+ },
+ {
+ "name": "art-run-test-039-join-main"
+ },
+ {
+ "name": "art-run-test-040-miranda"
+ },
+ {
+ "name": "art-run-test-041-narrowing"
+ },
+ {
+ "name": "art-run-test-042-new-instance"
+ },
+ {
+ "name": "art-run-test-043-privates"
+ },
+ {
+ "name": "art-run-test-045-reflect-array"
+ },
+ {
+ "name": "art-run-test-046-reflect"
+ },
+ {
+ "name": "art-run-test-047-returns"
+ },
+ {
+ "name": "art-run-test-048-reflect-v8"
+ },
+ {
+ "name": "art-run-test-049-show-object"
+ },
+ {
+ "name": "art-run-test-050-sync-test"
+ },
+ {
+ "name": "art-run-test-052-verifier-fun"
+ },
+ {
+ "name": "art-run-test-053-wait-some"
+ },
+ {
+ "name": "art-run-test-055-enum-performance"
+ },
+ {
+ "name": "art-run-test-058-enum-order"
+ },
+ {
+ "name": "art-run-test-059-finalizer-throw"
+ },
+ {
+ "name": "art-run-test-061-out-of-memory"
+ },
+ {
+ "name": "art-run-test-062-character-encodings"
+ },
+ {
+ "name": "art-run-test-063-process-manager"
+ },
+ {
+ "name": "art-run-test-067-preemptive-unpark"
+ },
+ {
+ "name": "art-run-test-069-field-type"
+ },
+ {
+ "name": "art-run-test-070-nio-buffer"
+ },
+ {
+ "name": "art-run-test-072-precise-gc"
+ },
+ {
+ "name": "art-run-test-072-reachability-fence"
+ },
+ {
+ "name": "art-run-test-073-mismatched-field"
+ },
+ {
+ "name": "art-run-test-074-gc-thrash"
+ },
+ {
+ "name": "art-run-test-075-verification-error"
+ },
+ {
+ "name": "art-run-test-076-boolean-put"
+ },
+ {
+ "name": "art-run-test-077-method-override"
+ },
+ {
+ "name": "art-run-test-078-polymorphic-virtual"
+ },
+ {
+ "name": "art-run-test-079-phantom"
+ },
+ {
+ "name": "art-run-test-080-oom-fragmentation"
+ },
+ {
+ "name": "art-run-test-080-oom-throw"
+ },
+ {
+ "name": "art-run-test-080-oom-throw-with-finalizer"
+ },
+ {
+ "name": "art-run-test-081-hot-exceptions"
+ },
+ {
+ "name": "art-run-test-082-inline-execute"
+ },
+ {
+ "name": "art-run-test-083-compiler-regressions"
+ },
+ {
+ "name": "art-run-test-084-class-init"
+ },
+ {
+ "name": "art-run-test-090-loop-formation"
+ },
+ {
+ "name": "art-run-test-092-locale"
+ },
+ {
+ "name": "art-run-test-093-serialization"
+ },
+ {
+ "name": "art-run-test-094-pattern"
+ },
+ {
+ "name": "art-run-test-095-switch-MAX_INT"
+ },
+ {
+ "name": "art-run-test-096-array-copy-concurrent-gc"
+ },
+ {
+ "name": "art-run-test-099-vmdebug"
+ },
+ {
+ "name": "art-run-test-100-reflect2"
+ },
+ {
+ "name": "art-run-test-1000-non-moving-space-stress"
+ },
+ {
+ "name": "art-run-test-1004-checker-volatile-ref-load"
+ },
+ {
+ "name": "art-run-test-101-fibonacci"
+ },
+ {
+ "name": "art-run-test-102-concurrent-gc"
+ },
+ {
+ "name": "art-run-test-103-string-append"
+ },
+ {
+ "name": "art-run-test-104-growth-limit"
+ },
+ {
+ "name": "art-run-test-105-invoke"
+ },
+ {
+ "name": "art-run-test-106-exceptions2"
+ },
+ {
+ "name": "art-run-test-107-int-math2"
+ },
+ {
+ "name": "art-run-test-108-check-cast"
+ },
+ {
+ "name": "art-run-test-109-suspend-check"
+ },
+ {
+ "name": "art-run-test-110-field-access"
+ },
+ {
+ "name": "art-run-test-112-double-math"
+ },
+ {
+ "name": "art-run-test-114-ParallelGC"
+ },
+ {
+ "name": "art-run-test-120-hashcode"
+ },
+ {
+ "name": "art-run-test-121-simple-suspend-check"
+ },
+ {
+ "name": "art-run-test-122-npe"
+ },
+ {
+ "name": "art-run-test-123-compiler-regressions-mt"
+ },
+ {
+ "name": "art-run-test-123-inline-execute2"
+ },
+ {
+ "name": "art-run-test-125-gc-and-classloading"
+ },
+ {
+ "name": "art-run-test-128-reg-spill-on-implicit-nullcheck"
+ },
+ {
+ "name": "art-run-test-129-ThreadGetId"
+ },
+ {
+ "name": "art-run-test-132-daemon-locks-shutdown"
+ },
+ {
+ "name": "art-run-test-133-static-invoke-super"
+ },
+ {
+ "name": "art-run-test-1338-gc-no-los"
+ },
+ {
+ "name": "art-run-test-140-dce-regression"
+ },
+ {
+ "name": "art-run-test-140-field-packing"
+ },
+ {
+ "name": "art-run-test-143-string-value"
+ },
+ {
+ "name": "art-run-test-145-alloc-tracking-stress"
+ },
+ {
+ "name": "art-run-test-152-dead-large-object"
+ },
+ {
+ "name": "art-run-test-153-reference-stress"
+ },
+ {
+ "name": "art-run-test-156-register-dex-file-multi-loader"
+ },
+ {
+ "name": "art-run-test-159-app-image-fields"
+ },
+ {
+ "name": "art-run-test-160-read-barrier-stress"
+ },
+ {
+ "name": "art-run-test-163-app-image-methods"
+ },
+ {
+ "name": "art-run-test-165-lock-owner-proxy"
+ },
+ {
+ "name": "art-run-test-168-vmstack-annotated"
+ },
+ {
+ "name": "art-run-test-170-interface-init"
+ },
+ {
+ "name": "art-run-test-174-escaping-instance-of-bad-class"
+ },
+ {
+ "name": "art-run-test-175-alloc-big-bignums"
+ },
+ {
+ "name": "art-run-test-176-app-image-string"
+ },
+ {
+ "name": "art-run-test-182-method-linking"
+ },
+ {
+ "name": "art-run-test-1960-checker-bounds-codegen"
+ },
+ {
+ "name": "art-run-test-1961-checker-loop-vectorizer"
+ },
+ {
+ "name": "art-run-test-201-built-in-except-detail-messages"
+ },
+ {
+ "name": "art-run-test-2019-constantcalculationsinking"
+ },
+ {
+ "name": "art-run-test-202-thread-oome"
+ },
+ {
+ "name": "art-run-test-2020-InvokeVirtual-Inlining"
+ },
+ {
+ "name": "art-run-test-2021-InvokeStatic-Inlining"
+ },
+ {
+ "name": "art-run-test-2022-Invariantloops"
+ },
+ {
+ "name": "art-run-test-2023-InvariantLoops_typecast"
+ },
+ {
+ "name": "art-run-test-2024-InvariantNegativeLoop"
+ },
+ {
+ "name": "art-run-test-2025-ChangedArrayValue"
+ },
+ {
+ "name": "art-run-test-2026-DifferentMemoryLSCouples"
+ },
+ {
+ "name": "art-run-test-2027-TwiceTheSameMemoryCouple"
+ },
+ {
+ "name": "art-run-test-2028-MultiBackward"
+ },
+ {
+ "name": "art-run-test-2029-contended-monitors"
+ },
+ {
+ "name": "art-run-test-2030-long-running-child"
+ },
+ {
+ "name": "art-run-test-2042-checker-dce-always-throw"
+ },
+ {
+ "name": "art-run-test-2042-reference-processing"
+ },
+ {
+ "name": "art-run-test-2043-reference-pauses"
+ },
+ {
+ "name": "art-run-test-2044-get-stack-traces"
+ },
+ {
+ "name": "art-run-test-2231-checker-heap-poisoning"
+ },
+ {
+ "name": "art-run-test-2233-checker-remove-loop-suspend-check"
+ },
+ {
+ "name": "art-run-test-2234-checker-remove-entry-suspendcheck"
+ },
+ {
+ "name": "art-run-test-2236-JdkUnsafeGetLong-regression"
+ },
+ {
+ "name": "art-run-test-2241-checker-inline-try-catch"
+ },
+ {
+ "name": "art-run-test-2242-checker-lse-acquire-release-operations"
+ },
+ {
+ "name": "art-run-test-2243-checker-not-inline-into-throw"
+ },
+ {
+ "name": "art-run-test-2244-checker-remove-try-boundary"
+ },
+ {
+ "name": "art-run-test-300-package-override"
+ },
+ {
+ "name": "art-run-test-301-abstract-protected"
+ },
+ {
+ "name": "art-run-test-302-float-conversion"
+ },
+ {
+ "name": "art-run-test-304-method-tracing"
+ },
+ {
+ "name": "art-run-test-401-optimizing-compiler"
+ },
+ {
+ "name": "art-run-test-402-optimizing-control-flow"
+ },
+ {
+ "name": "art-run-test-403-optimizing-long"
+ },
+ {
+ "name": "art-run-test-404-optimizing-allocator"
+ },
+ {
+ "name": "art-run-test-405-optimizing-long-allocator"
+ },
+ {
+ "name": "art-run-test-406-fields"
+ },
+ {
+ "name": "art-run-test-407-arrays"
+ },
+ {
+ "name": "art-run-test-408-move-bug"
+ },
+ {
+ "name": "art-run-test-409-materialized-condition"
+ },
+ {
+ "name": "art-run-test-410-floats"
+ },
+ {
+ "name": "art-run-test-411-checker-hdiv-hrem-const"
+ },
+ {
+ "name": "art-run-test-411-checker-hdiv-hrem-pow2"
+ },
+ {
+ "name": "art-run-test-411-checker-instruct-simplifier-hrem"
+ },
+ {
+ "name": "art-run-test-411-optimizing-arith"
+ },
+ {
+ "name": "art-run-test-413-regalloc-regression"
+ },
+ {
+ "name": "art-run-test-414-static-fields"
+ },
+ {
+ "name": "art-run-test-418-const-string"
+ },
+ {
+ "name": "art-run-test-419-long-parameter"
+ },
+ {
+ "name": "art-run-test-420-const-class"
+ },
+ {
+ "name": "art-run-test-421-exceptions"
+ },
+ {
+ "name": "art-run-test-421-large-frame"
+ },
+ {
+ "name": "art-run-test-422-instanceof"
+ },
+ {
+ "name": "art-run-test-422-type-conversion"
+ },
+ {
+ "name": "art-run-test-423-invoke-interface"
+ },
+ {
+ "name": "art-run-test-424-checkcast"
+ },
+ {
+ "name": "art-run-test-426-monitor"
+ },
+ {
+ "name": "art-run-test-427-bitwise"
+ },
+ {
+ "name": "art-run-test-427-bounds"
+ },
+ {
+ "name": "art-run-test-429-ssa-builder"
+ },
+ {
+ "name": "art-run-test-430-live-register-slow-path"
+ },
+ {
+ "name": "art-run-test-433-gvn"
+ },
+ {
+ "name": "art-run-test-434-shifter-operand"
+ },
+ {
+ "name": "art-run-test-435-try-finally-without-catch"
+ },
+ {
+ "name": "art-run-test-436-rem-float"
+ },
+ {
+ "name": "art-run-test-436-shift-constant"
+ },
+ {
+ "name": "art-run-test-437-inline"
+ },
+ {
+ "name": "art-run-test-438-volatile"
+ },
+ {
+ "name": "art-run-test-439-npe"
+ },
+ {
+ "name": "art-run-test-439-swap-double"
+ },
+ {
+ "name": "art-run-test-440-stmp"
+ },
+ {
+ "name": "art-run-test-441-checker-inliner"
+ },
+ {
+ "name": "art-run-test-443-not-bool-inline"
+ },
+ {
+ "name": "art-run-test-444-checker-nce"
+ },
+ {
+ "name": "art-run-test-445-checker-licm"
+ },
+ {
+ "name": "art-run-test-446-checker-inliner2"
+ },
+ {
+ "name": "art-run-test-447-checker-inliner3"
+ },
+ {
+ "name": "art-run-test-449-checker-bce-rem"
+ },
+ {
+ "name": "art-run-test-450-checker-types"
+ },
+ {
+ "name": "art-run-test-451-regression-add-float"
+ },
+ {
+ "name": "art-run-test-451-spill-splot"
+ },
+ {
+ "name": "art-run-test-455-checker-gvn"
+ },
+ {
+ "name": "art-run-test-456-baseline-array-set"
+ },
+ {
+ "name": "art-run-test-458-long-to-fpu"
+ },
+ {
+ "name": "art-run-test-464-checker-inline-sharpen-calls"
+ },
+ {
+ "name": "art-run-test-465-checker-clinit-gvn"
+ },
+ {
+ "name": "art-run-test-469-condition-materialization"
+ },
+ {
+ "name": "art-run-test-470-huge-method"
+ },
+ {
+ "name": "art-run-test-471-deopt-environment"
+ },
+ {
+ "name": "art-run-test-472-type-propagation"
+ },
+ {
+ "name": "art-run-test-473-checker-inliner-constants"
+ },
+ {
+ "name": "art-run-test-473-remove-dead-block"
+ },
+ {
+ "name": "art-run-test-474-checker-boolean-input"
+ },
+ {
+ "name": "art-run-test-474-fp-sub-neg"
+ },
+ {
+ "name": "art-run-test-475-simplify-mul-zero"
+ },
+ {
+ "name": "art-run-test-476-checker-ctor-fence-redun-elim"
+ },
+ {
+ "name": "art-run-test-476-checker-ctor-memory-barrier"
+ },
+ {
+ "name": "art-run-test-476-clinit-inline-static-invoke"
+ },
+ {
+ "name": "art-run-test-477-checker-bound-type"
+ },
+ {
+ "name": "art-run-test-477-long-2-float-convers-precision"
+ },
+ {
+ "name": "art-run-test-478-checker-clinit-check-pruning"
+ },
+ {
+ "name": "art-run-test-478-checker-inline-noreturn"
+ },
+ {
+ "name": "art-run-test-478-checker-inliner-nested-loop"
+ },
+ {
+ "name": "art-run-test-479-regression-implicit-null-check"
+ },
+ {
+ "name": "art-run-test-480-checker-dead-blocks"
+ },
+ {
+ "name": "art-run-test-481-regression-phi-cond"
+ },
+ {
+ "name": "art-run-test-482-checker-loop-back-edge-use"
+ },
+ {
+ "name": "art-run-test-483-dce-block"
+ },
+ {
+ "name": "art-run-test-485-checker-dce-switch"
+ },
+ {
+ "name": "art-run-test-486-checker-must-do-null-check"
+ },
+ {
+ "name": "art-run-test-487-checker-inline-calls"
+ },
+ {
+ "name": "art-run-test-488-checker-inline-recursive-calls"
+ },
+ {
+ "name": "art-run-test-489-current-method-regression"
+ },
+ {
+ "name": "art-run-test-490-checker-inline"
+ },
+ {
+ "name": "art-run-test-491-current-method"
+ },
+ {
+ "name": "art-run-test-492-checker-inline-invoke-interface"
+ },
+ {
+ "name": "art-run-test-493-checker-inline-invoke-interface"
+ },
+ {
+ "name": "art-run-test-494-checker-instanceof-tests"
+ },
+ {
+ "name": "art-run-test-495-checker-checkcast-tests"
+ },
+ {
+ "name": "art-run-test-496-checker-inlining-class-loader"
+ },
+ {
+ "name": "art-run-test-499-bce-phi-array-length"
+ },
+ {
+ "name": "art-run-test-500-instanceof"
+ },
+ {
+ "name": "art-run-test-505-simplifier-type-propagation"
+ },
+ {
+ "name": "art-run-test-507-boolean-test"
+ },
+ {
+ "name": "art-run-test-507-referrer"
+ },
+ {
+ "name": "art-run-test-508-checker-disassembly"
+ },
+ {
+ "name": "art-run-test-508-referrer-method"
+ },
+ {
+ "name": "art-run-test-513-array-deopt"
+ },
+ {
+ "name": "art-run-test-514-shifts"
+ },
+ {
+ "name": "art-run-test-519-bound-load-class"
+ },
+ {
+ "name": "art-run-test-521-checker-array-set-null"
+ },
+ {
+ "name": "art-run-test-521-regression-integer-field-set"
+ },
+ {
+ "name": "art-run-test-524-boolean-simplifier-regression"
+ },
+ {
+ "name": "art-run-test-525-checker-arrays-fields1"
+ },
+ {
+ "name": "art-run-test-525-checker-arrays-fields2"
+ },
+ {
+ "name": "art-run-test-526-checker-caller-callee-regs"
+ },
+ {
+ "name": "art-run-test-526-long-regalloc"
+ },
+ {
+ "name": "art-run-test-527-checker-array-access-simd"
+ },
+ {
+ "name": "art-run-test-527-checker-array-access-split"
+ },
+ {
+ "name": "art-run-test-528-long-hint"
+ },
+ {
+ "name": "art-run-test-529-long-split"
+ },
+ {
+ "name": "art-run-test-530-checker-loops-try-catch"
+ },
+ {
+ "name": "art-run-test-530-checker-loops1"
+ },
+ {
+ "name": "art-run-test-530-checker-loops2"
+ },
+ {
+ "name": "art-run-test-530-checker-loops3"
+ },
+ {
+ "name": "art-run-test-530-checker-loops4"
+ },
+ {
+ "name": "art-run-test-530-checker-loops5"
+ },
+ {
+ "name": "art-run-test-530-checker-lse"
+ },
+ {
+ "name": "art-run-test-530-checker-lse-ctor-fences"
+ },
+ {
+ "name": "art-run-test-530-checker-lse-simd"
+ },
+ {
+ "name": "art-run-test-530-checker-lse-try-catch"
+ },
+ {
+ "name": "art-run-test-530-instanceof-checkcast"
+ },
+ {
+ "name": "art-run-test-532-checker-nonnull-arrayset"
+ },
+ {
+ "name": "art-run-test-534-checker-bce-deoptimization"
+ },
+ {
+ "name": "art-run-test-535-deopt-and-inlining"
+ },
+ {
+ "name": "art-run-test-536-checker-intrinsic-optimization"
+ },
+ {
+ "name": "art-run-test-536-checker-needs-access-check"
+ },
+ {
+ "name": "art-run-test-537-checker-arraycopy"
+ },
+ {
+ "name": "art-run-test-537-checker-inline-and-unverified"
+ },
+ {
+ "name": "art-run-test-537-checker-jump-over-jump"
+ },
+ {
+ "name": "art-run-test-538-checker-embed-constants"
+ },
+ {
+ "name": "art-run-test-540-checker-rtp-bug"
+ },
+ {
+ "name": "art-run-test-542-bitfield-rotates"
+ },
+ {
+ "name": "art-run-test-542-inline-trycatch"
+ },
+ {
+ "name": "art-run-test-542-unresolved-access-check"
+ },
+ {
+ "name": "art-run-test-545-tracing-and-jit"
+ },
+ {
+ "name": "art-run-test-548-checker-inlining-and-dce"
+ },
+ {
+ "name": "art-run-test-549-checker-types-merge"
+ },
+ {
+ "name": "art-run-test-550-checker-multiply-accumulate"
+ },
+ {
+ "name": "art-run-test-550-new-instance-clinit"
+ },
+ {
+ "name": "art-run-test-551-checker-clinit"
+ },
+ {
+ "name": "art-run-test-551-checker-shifter-operand"
+ },
+ {
+ "name": "art-run-test-551-implicit-null-checks"
+ },
+ {
+ "name": "art-run-test-552-checker-x86-avx2-bit-manipulation"
+ },
+ {
+ "name": "art-run-test-554-checker-rtp-checkcast"
+ },
+ {
+ "name": "art-run-test-557-checker-instruct-simplifier-ror"
+ },
+ {
+ "name": "art-run-test-558-switch"
+ },
+ {
+ "name": "art-run-test-559-bce-ssa"
+ },
+ {
+ "name": "art-run-test-559-checker-rtp-ifnotnull"
+ },
+ {
+ "name": "art-run-test-560-packed-switch"
+ },
+ {
+ "name": "art-run-test-561-divrem"
+ },
+ {
+ "name": "art-run-test-561-shared-slowpaths"
+ },
+ {
+ "name": "art-run-test-562-bce-preheader"
+ },
+ {
+ "name": "art-run-test-562-checker-no-intermediate"
+ },
+ {
+ "name": "art-run-test-563-checker-invoke-super"
+ },
+ {
+ "name": "art-run-test-564-checker-bitcount"
+ },
+ {
+ "name": "art-run-test-564-checker-inline-loop"
+ },
+ {
+ "name": "art-run-test-564-checker-negbitwise"
+ },
+ {
+ "name": "art-run-test-565-checker-condition-liveness"
+ },
+ {
+ "name": "art-run-test-566-checker-codegen-select"
+ },
+ {
+ "name": "art-run-test-567-checker-builder-intrinsics"
+ },
+ {
+ "name": "art-run-test-568-checker-onebit"
+ },
+ {
+ "name": "art-run-test-570-checker-select"
+ },
+ {
+ "name": "art-run-test-572-checker-array-get-regression"
+ },
+ {
+ "name": "art-run-test-573-checker-checkcast-regression"
+ },
+ {
+ "name": "art-run-test-576-polymorphic-inlining"
+ },
+ {
+ "name": "art-run-test-577-checker-fp2int"
+ },
+ {
+ "name": "art-run-test-578-bce-visit"
+ },
+ {
+ "name": "art-run-test-578-polymorphic-inlining"
+ },
+ {
+ "name": "art-run-test-579-inline-infinite"
+ },
+ {
+ "name": "art-run-test-580-checker-fp16"
+ },
+ {
+ "name": "art-run-test-580-checker-round"
+ },
+ {
+ "name": "art-run-test-580-checker-string-fact-intrinsics"
+ },
+ {
+ "name": "art-run-test-580-crc32"
+ },
+ {
+ "name": "art-run-test-581-checker-rtp"
+ },
+ {
+ "name": "art-run-test-582-checker-bce-length"
+ },
+ {
+ "name": "art-run-test-583-checker-zero"
+ },
+ {
+ "name": "art-run-test-584-checker-div-bool"
+ },
+ {
+ "name": "art-run-test-589-super-imt"
+ },
+ {
+ "name": "art-run-test-590-checker-arr-set-null-regression"
+ },
+ {
+ "name": "art-run-test-591-checker-regression-dead-loop"
+ },
+ {
+ "name": "art-run-test-593-checker-long-2-float-regression"
+ },
+ {
+ "name": "art-run-test-594-checker-array-alias"
+ },
+ {
+ "name": "art-run-test-594-load-string-regression"
+ },
+ {
+ "name": "art-run-test-603-checker-instanceof"
+ },
+ {
+ "name": "art-run-test-605-new-string-from-bytes"
+ },
+ {
+ "name": "art-run-test-607-daemon-stress"
+ },
+ {
+ "name": "art-run-test-609-checker-inline-interface"
+ },
+ {
+ "name": "art-run-test-609-checker-x86-bounds-check"
+ },
+ {
+ "name": "art-run-test-610-arraycopy"
+ },
+ {
+ "name": "art-run-test-611-checker-simplify-if"
+ },
+ {
+ "name": "art-run-test-614-checker-dump-constant-location"
+ },
+ {
+ "name": "art-run-test-615-checker-arm64-store-zero"
+ },
+ {
+ "name": "art-run-test-617-clinit-oome"
+ },
+ {
+ "name": "art-run-test-618-checker-induction"
+ },
+ {
+ "name": "art-run-test-619-checker-current-method"
+ },
+ {
+ "name": "art-run-test-620-checker-bce-intrinsics"
+ },
+ {
+ "name": "art-run-test-622-checker-bce-regressions"
+ },
+ {
+ "name": "art-run-test-625-checker-licm-regressions"
+ },
+ {
+ "name": "art-run-test-627-checker-unroll"
+ },
+ {
+ "name": "art-run-test-628-vdex"
+ },
+ {
+ "name": "art-run-test-631-checker-get-class"
+ },
+ {
+ "name": "art-run-test-632-checker-char-at-bounds"
+ },
+ {
+ "name": "art-run-test-635-checker-arm64-volatile-load-cc"
+ },
+ {
+ "name": "art-run-test-636-arm64-veneer-pool"
+ },
+ {
+ "name": "art-run-test-637-checker-throw-inline"
+ },
+ {
+ "name": "art-run-test-639-checker-code-sinking"
+ },
+ {
+ "name": "art-run-test-640-checker-boolean-simd"
+ },
+ {
+ "name": "art-run-test-640-checker-integer-valueof"
+ },
+ {
+ "name": "art-run-test-640-checker-simd"
+ },
+ {
+ "name": "art-run-test-641-checker-arraycopy"
+ },
+ {
+ "name": "art-run-test-641-iterations"
+ },
+ {
+ "name": "art-run-test-643-checker-bogus-ic"
+ },
+ {
+ "name": "art-run-test-645-checker-abs-simd"
+ },
+ {
+ "name": "art-run-test-646-checker-arraycopy-large-cst-pos"
+ },
+ {
+ "name": "art-run-test-646-checker-long-const-to-int"
+ },
+ {
+ "name": "art-run-test-646-checker-simd-hadd"
+ },
+ {
+ "name": "art-run-test-650-checker-inline-access-thunks"
+ },
+ {
+ "name": "art-run-test-654-checker-periodic"
+ },
+ {
+ "name": "art-run-test-655-checker-simd-arm-opt"
+ },
+ {
+ "name": "art-run-test-656-checker-simd-opt"
+ },
+ {
+ "name": "art-run-test-657-branches"
+ },
+ {
+ "name": "art-run-test-658-fp-read-barrier"
+ },
+ {
+ "name": "art-run-test-659-unpadded-array"
+ },
+ {
+ "name": "art-run-test-660-checker-sad"
+ },
+ {
+ "name": "art-run-test-660-checker-simd-sad"
+ },
+ {
+ "name": "art-run-test-661-checker-simd-reduc"
+ },
+ {
+ "name": "art-run-test-662-regression-alias"
+ },
+ {
+ "name": "art-run-test-663-checker-select-generator"
+ },
+ {
+ "name": "art-run-test-665-checker-simd-zero"
+ },
+ {
+ "name": "art-run-test-666-dex-cache-itf"
+ },
+ {
+ "name": "art-run-test-667-checker-simd-alignment"
+ },
+ {
+ "name": "art-run-test-667-out-of-bounds"
+ },
+ {
+ "name": "art-run-test-669-checker-break"
+ },
+ {
+ "name": "art-run-test-671-npe-field-opts"
+ },
+ {
+ "name": "art-run-test-672-checker-throw-method"
+ },
+ {
+ "name": "art-run-test-673-checker-throw-vmethod"
+ },
+ {
+ "name": "art-run-test-676-proxy-jit-at-first-use"
+ },
+ {
+ "name": "art-run-test-677-fsi2"
+ },
+ {
+ "name": "art-run-test-678-quickening"
+ },
+ {
+ "name": "art-run-test-684-checker-simd-dotprod"
+ },
+ {
+ "name": "art-run-test-684-select-condition"
+ },
+ {
+ "name": "art-run-test-689-multi-catch"
+ },
+ {
+ "name": "art-run-test-694-clinit-jit"
+ },
+ {
+ "name": "art-run-test-695-simplify-throws"
+ },
+ {
+ "name": "art-run-test-696-loop"
+ },
+ {
+ "name": "art-run-test-697-checker-string-append"
+ },
+ {
+ "name": "art-run-test-698-selects"
+ },
+ {
+ "name": "art-run-test-700-LoadArgRegs"
+ },
+ {
+ "name": "art-run-test-703-floating-point-div"
+ },
+ {
+ "name": "art-run-test-704-multiply-accumulate"
+ },
+ {
+ "name": "art-run-test-705-register-conflict"
+ },
+ {
+ "name": "art-run-test-711-checker-type-conversion"
+ },
+ {
+ "name": "art-run-test-713-varhandle-invokers"
+ },
+ {
+ "name": "art-run-test-718-zipfile-finalizer"
+ },
+ {
+ "name": "art-run-test-719-varhandle-concurrency"
+ },
+ {
+ "name": "art-run-test-721-osr"
+ },
+ {
+ "name": "art-run-test-726-array-store"
+ },
+ {
+ "name": "art-run-test-730-checker-inlining-super"
+ },
+ {
+ "name": "art-run-test-731-bounds-check-slow-path"
+ },
+ {
+ "name": "art-run-test-805-TooDeepClassInstanceOf"
+ },
+ {
+ "name": "art-run-test-806-TooWideClassInstanceOf"
+ },
+ {
+ "name": "art-run-test-812-recursive-default"
+ },
+ {
+ "name": "art-run-test-814-large-field-offsets"
+ },
+ {
+ "name": "art-run-test-815-invokeinterface-default"
+ },
+ {
+ "name": "art-run-test-818-clinit-nterp"
+ },
+ {
+ "name": "art-run-test-821-madvise-willneed"
+ },
+ {
+ "name": "art-run-test-828-partial-lse"
+ },
+ {
+ "name": "art-run-test-834-lse"
+ },
+ {
+ "name": "art-run-test-835-b216762268"
+ },
+ {
+ "name": "art-run-test-838-override"
+ },
+ {
+ "name": "art-run-test-839-clinit-throw"
+ },
+ {
+ "name": "art-run-test-841-defaults"
+ },
+ {
+ "name": "art-run-test-843-default-interface"
+ },
+ {
+ "name": "art-run-test-963-default-range-smali"
+ },
+ {
+ "name": "art-run-test-965-default-verify"
+ },
+ {
+ "name": "art-run-test-967-default-ame"
+ },
+ {
+ "name": "art_libnativebridge_cts_tests"
+ },
+ {
+ "name": "art_standalone_artd_tests"
+ },
+ {
+ "name": "art_standalone_cmdline_tests"
+ },
+ {
+ "name": "art_standalone_compiler_tests"
+ },
+ {
+ "name": "art_standalone_dex2oat_tests"
+ },
+ {
+ "name": "art_standalone_dexdump_tests"
+ },
+ {
+ "name": "art_standalone_dexlist_tests"
+ },
+ {
+ "name": "art_standalone_dexoptanalyzer_tests"
+ },
+ {
+ "name": "art_standalone_libartbase_tests"
+ },
+ {
+ "name": "art_standalone_libartpalette_tests"
+ },
+ {
+ "name": "art_standalone_libartservice_tests"
+ },
+ {
+ "name": "art_standalone_libarttools_tests"
+ },
+ {
+ "name": "art_standalone_libdexfile_support_tests"
+ },
+ {
+ "name": "art_standalone_libdexfile_tests"
+ },
+ {
+ "name": "art_standalone_libprofile_tests"
+ },
+ {
+ "name": "art_standalone_oatdump_tests"
+ },
+ {
+ "name": "art_standalone_odrefresh_tests"
+ },
+ {
+ "name": "art_standalone_profman_tests"
},
{
"name": "art_standalone_runtime_tests"
@@ -2572,6 +4085,9 @@
"name": "art_standalone_sigchain_tests"
},
{
+ "name": "libnativeloader_e2e_tests"
+ },
+ {
"name": "libnativeloader_test"
}
]
diff --git a/adbconnection/adbconnection.cc b/adbconnection/adbconnection.cc
index f9ebe40..239fe31 100644
--- a/adbconnection/adbconnection.cc
+++ b/adbconnection/adbconnection.cc
@@ -23,6 +23,8 @@
#include "adbconnection/client.h"
#include "android-base/endian.h"
#include "android-base/stringprintf.h"
+#include "art_field-inl.h"
+#include "art_method-alloc-inl.h"
#include "base/file_utils.h"
#include "base/globals.h"
#include "base/logging.h"
@@ -32,6 +34,7 @@
#include "debugger.h"
#include "jni/java_vm_ext.h"
#include "jni/jni_env_ext.h"
+#include "mirror/class-alloc-inl.h"
#include "mirror/throwable.h"
#include "nativehelper/scoped_local_ref.h"
#include "runtime-inl.h"
@@ -180,21 +183,25 @@
}
}
-static jobject CreateAdbConnectionThread(art::Thread* thr) {
- JNIEnv* env = thr->GetJniEnv();
- // Move to native state to talk with the jnienv api.
- art::ScopedThreadStateChange stsc(thr, art::ThreadState::kNative);
- ScopedLocalRef<jstring> thr_name(env, env->NewStringUTF(kAdbConnectionThreadName));
- ScopedLocalRef<jobject> thr_group(
- env,
- env->GetStaticObjectField(art::WellKnownClasses::java_lang_ThreadGroup,
- art::WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup));
- return env->NewObject(art::WellKnownClasses::java_lang_Thread,
- art::WellKnownClasses::java_lang_Thread_init,
- thr_group.get(),
- thr_name.get(),
- /*Priority=*/ 0,
- /*Daemon=*/ true);
+static art::ObjPtr<art::mirror::Object> CreateAdbConnectionThread(art::Thread* self)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ art::StackHandleScope<3u> hs(self);
+ art::Handle<art::mirror::String> thr_name =
+ hs.NewHandle(art::mirror::String::AllocFromModifiedUtf8(self, kAdbConnectionThreadName));
+ if (thr_name == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ art::ArtField* system_thread_group_field =
+ art::WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup;
+ DCHECK(system_thread_group_field->GetDeclaringClass()->IsInitialized());
+ // Avoid using `ArtField::GetObject` as it requires linking against `libdexfile` for
+ // `operator<<(std::ostream&, Primitive::Type)`.
+ art::Handle<art::mirror::Object> system_thread_group = hs.NewHandle(
+ system_thread_group_field->GetDeclaringClass()->GetFieldObject<art::mirror::Object>(
+ system_thread_group_field->GetOffset()));
+ return art::WellKnownClasses::java_lang_Thread_init->NewObject<'L', 'L', 'I', 'Z'>(
+ hs, self, system_thread_group, thr_name, /*priority=*/ 0, /*daemon=*/ true).Get();
}
struct CallbackData {
@@ -274,10 +281,14 @@
}
runtime->StartThreadBirth();
}
- ScopedLocalRef<jobject> thr(soa.Env(), CreateAdbConnectionThread(soa.Self()));
+ jobject thr = soa.Env()->GetVm()->AddGlobalRef(self, CreateAdbConnectionThread(soa.Self()));
+ if (thr == nullptr) {
+ LOG(ERROR) << "Failed to create debugger thread!";
+ return;
+ }
// Note: Using pthreads instead of std::thread to not abort when the thread cannot be
// created (exception support required).
- std::unique_ptr<CallbackData> data(new CallbackData { this, soa.Env()->NewGlobalRef(thr.get()) });
+ std::unique_ptr<CallbackData> data(new CallbackData { this, thr });
started_debugger_threads_ = true;
gPthread.emplace();
int pthread_create_result = pthread_create(&gPthread.value(),
@@ -289,7 +300,7 @@
started_debugger_threads_ = false;
// If the create succeeded the other thread will call EndThreadBirth.
art::Runtime* runtime = art::Runtime::Current();
- soa.Env()->DeleteGlobalRef(data->thr_);
+ soa.Env()->DeleteGlobalRef(thr);
LOG(ERROR) << "Failed to create thread for adb-jdwp connection manager!";
art::MutexLock mu(art::Thread::Current(), *art::Locks::runtime_shutdown_lock_);
runtime->EndThreadBirth();
@@ -810,8 +821,12 @@
void AdbConnectionState::AttachJdwpAgent(art::Thread* self) {
art::Runtime* runtime = art::Runtime::Current();
self->AssertNoPendingException();
+
+ std::string args = MakeAgentArg();
+ VLOG(jdwp) << "Attaching JDWP agent with args '" << args << "'";
+
runtime->AttachAgent(/* env= */ nullptr,
- MakeAgentArg(),
+ args,
/* class_loader= */ nullptr);
if (self->IsExceptionPending()) {
LOG(ERROR) << "Failed to load agent " << agent_name_;
diff --git a/artd/Android.bp b/artd/Android.bp
index 6db1287..c0ffd0a 100644
--- a/artd/Android.bp
+++ b/artd/Android.bp
@@ -22,22 +22,31 @@
default_applicable_licenses: ["art_license"],
}
-art_cc_binary {
- name: "artd",
+cc_defaults {
+ name: "artd_defaults",
defaults: ["art_defaults"],
-
srcs: [
"artd.cc",
],
-
shared_libs: [
- "artd-aidl-ndk",
- "libartbase",
"libarttools",
"libbase",
"libbinder_ndk",
],
+ static_libs: [
+ "artd-aidl-ndk",
+ ],
+}
+art_cc_binary {
+ name: "artd",
+ defaults: ["artd_defaults"],
+ srcs: [
+ "artd_main.cc",
+ ],
+ shared_libs: [
+ "libartbase",
+ ],
apex_available: [
"com.android.art",
"com.android.art.debug",
@@ -50,3 +59,35 @@
filename: "init.rc",
installable: false,
}
+
+art_cc_defaults {
+ name: "art_artd_tests_defaults",
+ defaults: ["artd_defaults"],
+ srcs: [
+ "artd_test.cc",
+ ],
+}
+
+// Version of ART gtest `art_artd_tests` bundled with the ART APEX on target.
+//
+// This test requires the full libbinder_ndk implementation on host, which is
+// not available as a prebuilt on the thin master-art branch. Hence it won't
+// work there, and there's a conditional in Android.gtest.mk to exclude it from
+// test-art-host-gtest.
+art_cc_test {
+ name: "art_artd_tests",
+ defaults: [
+ "art_gtest_defaults",
+ "art_artd_tests_defaults",
+ ],
+}
+
+// Standalone version of ART gtest `art_artd_tests`, not bundled with the ART
+// APEX on target.
+art_cc_test {
+ name: "art_standalone_artd_tests",
+ defaults: [
+ "art_standalone_gtest_defaults",
+ "art_artd_tests_defaults",
+ ],
+}
diff --git a/artd/artd.cc b/artd/artd.cc
index 1dcd2e8..27a609d 100644
--- a/artd/artd.cc
+++ b/artd/artd.cc
@@ -1,87 +1,62 @@
/*
-** Copyright 2021, 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.
-*/
+ * Copyright (C) 2021 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 "artd.h"
+
+#include <unistd.h>
#include <string>
-#define LOG_TAG "artd"
-#include <android/binder_manager.h>
-#include <android/binder_process.h>
-#include <unistd.h>
-#include <utils/Errors.h>
-
-#include "aidl/android/os/BnArtd.h"
-#include "base/logging.h"
-#include "base/macros.h"
+#include "aidl/com/android/server/art/BnArtd.h"
+#include "android-base/logging.h"
+#include "android-base/result.h"
+#include "android/binder_auto_utils.h"
+#include "android/binder_manager.h"
+#include "android/binder_process.h"
#include "tools/tools.h"
-using ::ndk::ScopedAStatus;
-
-namespace android {
+namespace art {
namespace artd {
-class Artd : public aidl::android::os::BnArtd {
- constexpr static const char* const SERVICE_NAME = "artd";
+namespace {
- public:
- Artd() {}
+using ::android::base::Error;
+using ::android::base::Result;
+using ::ndk::ScopedAStatus;
- /*
- * Binder API
- */
+constexpr const char* kServiceName = "artd";
- ScopedAStatus isAlive(bool* _aidl_return) {
- *_aidl_return = true;
- return ScopedAStatus::ok();
+} // namespace
+
+ScopedAStatus Artd::isAlive(bool* _aidl_return) {
+ *_aidl_return = true;
+ return ScopedAStatus::ok();
+}
+
+Result<void> Artd::Start() {
+ ScopedAStatus status = ScopedAStatus::fromStatus(
+ AServiceManager_registerLazyService(this->asBinder().get(), kServiceName));
+ if (!status.isOk()) {
+ return Error() << status.getDescription();
}
- /*
- * Server API
- */
+ ABinderProcess_startThreadPool();
- ScopedAStatus Start() {
- LOG(INFO) << "Starting artd";
-
- status_t ret = AServiceManager_addService(this->asBinder().get(), SERVICE_NAME);
- if (ret != android::OK) {
- return ScopedAStatus::fromStatus(ret);
- }
-
- ABinderProcess_startThreadPool();
-
- return ScopedAStatus::ok();
- }
-};
+ return {};
+}
} // namespace artd
-} // namespace android
-
-int main(const int argc __attribute__((unused)), char* argv[]) {
- setenv("ANDROID_LOG_TAGS", "*:v", 1);
- android::base::InitLogging(argv);
-
- android::artd::Artd artd;
-
- if (auto ret = artd.Start(); !ret.isOk()) {
- LOG(ERROR) << "Unable to start artd: " << ret.getMessage();
- exit(1);
- }
-
- ABinderProcess_joinThreadPool();
-
- LOG(INFO) << "artd shutting down";
-
- return 0;
-}
+} // namespace art
diff --git a/artd/artd.h b/artd/artd.h
new file mode 100644
index 0000000..f01d9a8
--- /dev/null
+++ b/artd/artd.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_ARTD_ARTD_H_
+#define ART_ARTD_ARTD_H_
+
+#include "aidl/com/android/server/art/BnArtd.h"
+#include "android-base/result.h"
+#include "android/binder_auto_utils.h"
+
+namespace art {
+namespace artd {
+
+class Artd : public aidl::com::android::server::art::BnArtd {
+ public:
+ ndk::ScopedAStatus isAlive(bool* _aidl_return) override;
+
+ android::base::Result<void> Start();
+};
+
+} // namespace artd
+} // namespace art
+
+#endif // ART_ARTD_ARTD_H_
diff --git a/artd/artd.rc b/artd/artd.rc
index eebec2d..5ddfcdc 100644
--- a/artd/artd.rc
+++ b/artd/artd.rc
@@ -12,8 +12,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# This service is disabled until b/192042812 is resolved.
+# A lazy service that is started and stopped dynamically as needed.
service artd /apex/com.android.art/bin/artd
- disabled
+ interface aidl artd
+ disabled # Prevents the service from automatically starting at boot.
+ oneshot # Prevents the service from automatically restarting each time it is stopped.
class core
- user artd
\ No newline at end of file
+ user artd
+ group artd
+ capabilities DAC_OVERRIDE DAC_READ_SEARCH FOWNER CHOWN
diff --git a/artd/artd_main.cc b/artd/artd_main.cc
new file mode 100644
index 0000000..3644eba
--- /dev/null
+++ b/artd/artd_main.cc
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 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 <stdlib.h>
+
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+#include "android/binder_interface_utils.h"
+#include "android/binder_process.h"
+#include "artd.h"
+
+int main(int argc ATTRIBUTE_UNUSED, char* argv[]) {
+ android::base::InitLogging(argv);
+
+ auto artd = ndk::SharedRefBase::make<art::artd::Artd>();
+
+ LOG(INFO) << "Starting artd";
+
+ if (auto ret = artd->Start(); !ret.ok()) {
+ LOG(ERROR) << "Unable to start artd: " << ret.error();
+ exit(1);
+ }
+
+ ABinderProcess_joinThreadPool();
+
+ LOG(INFO) << "artd shutting down";
+
+ return 0;
+}
diff --git a/artd/artd_test.cc b/artd/artd_test.cc
new file mode 100644
index 0000000..14bccc2
--- /dev/null
+++ b/artd/artd_test.cc
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 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 "artd.h"
+
+#include <memory>
+
+#include "android/binder_interface_utils.h"
+#include "base/common_art_test.h"
+#include "gtest/gtest.h"
+
+namespace art {
+namespace artd {
+namespace {
+
+class ArtdTest : public CommonArtTest {
+ protected:
+ void SetUp() override {
+ CommonArtTest::SetUp();
+ artd_ = ndk::SharedRefBase::make<Artd>();
+ }
+
+ void TearDown() override { CommonArtTest::TearDown(); }
+
+ std::shared_ptr<Artd> artd_;
+};
+
+TEST_F(ArtdTest, isAlive) {
+ bool result = false;
+ artd_->isAlive(&result);
+ EXPECT_TRUE(result);
+}
+
+} // namespace
+} // namespace artd
+} // namespace art
diff --git a/artd/binder/Android.bp b/artd/binder/Android.bp
index 6acfe4e..ad8474f 100644
--- a/artd/binder/Android.bp
+++ b/artd/binder/Android.bp
@@ -25,7 +25,7 @@
aidl_interface {
name: "artd-aidl",
srcs: [
- "android/os/IArtd.aidl",
+ "com/android/server/art/*.aidl",
],
host_supported: true,
backend: {
diff --git a/artd/binder/android/os/IArtd.aidl b/artd/binder/android/os/IArtd.aidl
deleted file mode 100644
index a16764b..0000000
--- a/artd/binder/android/os/IArtd.aidl
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2021 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 android.os;
-
-/** {@hide} */
-interface IArtd {
- // Test to see if the artd service is available.
- boolean isAlive();
-}
diff --git a/artd/binder/com/android/server/art/IArtd.aidl b/artd/binder/com/android/server/art/IArtd.aidl
new file mode 100644
index 0000000..58b2aae
--- /dev/null
+++ b/artd/binder/com/android/server/art/IArtd.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.art;
+
+/** {@hide} */
+interface IArtd {
+ // Test to see if the artd service is available.
+ boolean isAlive();
+}
diff --git a/artd/tests/src/com/android/art/ArtdIntegrationTests.java b/artd/tests/src/com/android/art/ArtdIntegrationTests.java
index 7d40adb..2a32972 100644
--- a/artd/tests/src/com/android/art/ArtdIntegrationTests.java
+++ b/artd/tests/src/com/android/art/ArtdIntegrationTests.java
@@ -16,11 +16,12 @@
package com.android.art;
-import android.os.IArtd;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import com.android.server.art.IArtd;
+
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
diff --git a/benchmark/Android.bp b/benchmark/Android.bp
index d781f84..c7f9f6b 100644
--- a/benchmark/Android.bp
+++ b/benchmark/Android.bp
@@ -51,6 +51,7 @@
// TODO(ngeoffray): find a way to link against the libraries in the apex.
shared_libs: [
"libart",
+ "libartbase",
"libbase",
],
}
@@ -82,3 +83,30 @@
},
},
}
+
+art_cc_library {
+ name: "libgolemtiagent",
+ host_supported: true,
+ defaults: ["art_defaults"],
+ srcs: [
+ "golem-tiagent/golem-tiagent.cc",
+ ],
+ target: {
+ // This has to be duplicated for android and host to make sure it
+ // comes after the -Wframe-larger-than warnings inserted by art.go
+ // target-specific properties
+ android: {
+ cflags: ["-Wno-frame-larger-than="],
+ },
+ host: {
+ cflags: ["-Wno-frame-larger-than="],
+ },
+ },
+ header_libs: [
+ "libnativehelper_header_only",
+ "libopenjdkjvmti_headers"
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+}
diff --git a/benchmark/golem-tiagent/golem-tiagent.cc b/benchmark/golem-tiagent/golem-tiagent.cc
new file mode 100644
index 0000000..be2c727
--- /dev/null
+++ b/benchmark/golem-tiagent/golem-tiagent.cc
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2022 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/macros.h"
+#include "android-base/logging.h"
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace art {
+
+jvmtiEnv* jvmti_env = nullptr;
+
+void CheckJvmtiError(jvmtiEnv* env, jvmtiError error) {
+ if (error != JVMTI_ERROR_NONE) {
+ char* error_name;
+ jvmtiError name_error = env->GetErrorName(error, &error_name);
+ if (name_error != JVMTI_ERROR_NONE) {
+ LOG(FATAL) << "Unable to get error name for " << error;
+ }
+ LOG(FATAL) << "Unexpected error: " << error_name;
+ }
+}
+
+static void JNICALL VMInitCallback(jvmtiEnv *jenv ATTRIBUTE_UNUSED,
+ JNIEnv* jni_env,
+ jthread thread ATTRIBUTE_UNUSED) {
+ // Set a breakpoint on a rare method that we won't expect to be hit.
+ // java.lang.Thread.stop is deprecated and not expected to be used.
+ jclass cl = jni_env->FindClass("java/lang/Thread");
+ if (cl == nullptr) {
+ LOG(FATAL) << "Cannot find class java/lang/Thread to set a breakpoint";
+ }
+
+ jmethodID method = jni_env->GetMethodID(cl, "stop", "()V");
+ if (method == nullptr) {
+ LOG(FATAL) << "Cannot find method to set a breapoint";
+ }
+
+ jlong start = 0;
+ jlong end;
+ CheckJvmtiError(jvmti_env, jvmti_env->GetMethodLocation(method, &start, &end));
+ CheckJvmtiError(jvmti_env, jvmti_env->SetBreakpoint(method, start));
+}
+
+extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm,
+ char* options ATTRIBUTE_UNUSED,
+ void* reserved ATTRIBUTE_UNUSED) {
+ // Setup jvmti_env
+ if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0) != 0) {
+ LOG(ERROR) << "Unable to get jvmti env!";
+ return 1;
+ }
+
+ // Enable breakpoint capability
+ jvmtiCapabilities capabilities;
+ memset(&capabilities, 0, sizeof(capabilities));
+ capabilities.can_generate_breakpoint_events = 1;
+ CheckJvmtiError(jvmti_env, jvmti_env->AddCapabilities(&capabilities));
+
+ // Set a callback for VM_INIT phase so we can set a breakpoint. We cannot just
+ // set a breakpoint here since vm isn't fully initialized here.
+ jvmtiEventCallbacks callbacks;
+ memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
+ callbacks.VMInit = VMInitCallback;
+ CheckJvmtiError(jvmti_env, jvmti_env->SetEventCallbacks(&callbacks, sizeof(callbacks)));
+ CheckJvmtiError(jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, nullptr));
+
+ return 0;
+}
+
+} // namespace art
diff --git a/benchmark/stringbuilder-append/src/StringBuilderAppendBenchmark.java b/benchmark/stringbuilder-append/src/StringBuilderAppendBenchmark.java
index 1550e81..d710e34 100644
--- a/benchmark/stringbuilder-append/src/StringBuilderAppendBenchmark.java
+++ b/benchmark/stringbuilder-append/src/StringBuilderAppendBenchmark.java
@@ -20,6 +20,10 @@
public static String longString1 = "This is a long string 1";
public static String longString2 = "This is a long string 2";
public static int int1 = 42;
+ public static double double1 = 42.0;
+ public static double double2 = 1.0E308;
+ public static float float1 = 42.0f;
+ public static float float2 = 1.0E38f;
public void timeAppendStrings(int count) {
String s1 = string1;
@@ -59,4 +63,74 @@
throw new AssertionError();
}
}
+
+ public void timeAppendStringAndDouble(int count) {
+ String s1 = string1;
+ double d1 = double1;
+ int sum = 0;
+ for (int i = 0; i < count; ++i) {
+ String result = s1 + d1;
+ sum += result.length(); // Make sure the append is not optimized away.
+ }
+ if (sum != count * (s1.length() + Double.toString(d1).length())) {
+ throw new AssertionError();
+ }
+ }
+
+ public void timeAppendStringAndHugeDouble(int count) {
+ String s1 = string1;
+ double d2 = double2;
+ int sum = 0;
+ for (int i = 0; i < count; ++i) {
+ String result = s1 + d2;
+ sum += result.length(); // Make sure the append is not optimized away.
+ }
+ if (sum != count * (s1.length() + Double.toString(d2).length())) {
+ throw new AssertionError();
+ }
+ }
+
+ public void timeAppendStringAndFloat(int count) {
+ String s1 = string1;
+ float f1 = float1;
+ int sum = 0;
+ for (int i = 0; i < count; ++i) {
+ String result = s1 + f1;
+ sum += result.length(); // Make sure the append is not optimized away.
+ }
+ if (sum != count * (s1.length() + Float.toString(f1).length())) {
+ throw new AssertionError();
+ }
+ }
+
+ public void timeAppendStringAndHugeFloat(int count) {
+ String s1 = string1;
+ float f2 = float2;
+ int sum = 0;
+ for (int i = 0; i < count; ++i) {
+ String result = s1 + f2;
+ sum += result.length(); // Make sure the append is not optimized away.
+ }
+ if (sum != count * (s1.length() + Float.toString(f2).length())) {
+ throw new AssertionError();
+ }
+ }
+
+ public void timeAppendStringDoubleStringAndFloat(int count) {
+ String s1 = string1;
+ String s2 = string2;
+ double d1 = double1;
+ float f1 = float1;
+ int sum = 0;
+ for (int i = 0; i < count; ++i) {
+ String result = s1 + d1 + s2 + f1;
+ sum += result.length(); // Make sure the append is not optimized away.
+ }
+ if (sum != count * (s1.length() +
+ Double.toString(d1).length() +
+ s2.length() +
+ Float.toString(f1).length())) {
+ throw new AssertionError();
+ }
+ }
}
diff --git a/build/Android.bp b/build/Android.bp
index d2545a4..504ed5b 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -28,27 +28,23 @@
}
art_clang_tidy_errors = [
- "android-cloexec-open",
- "bugprone-lambda-function-name",
- "bugprone-unused-raii", // Protect scoped things like MutexLock.
- "bugprone-virtual-near-miss",
- "modernize-use-bool-literals",
- "performance-implicit-conversion-in-loop",
- "performance-unnecessary-copy-initialization",
-]
-
-art_clang_tidy_allowed = [
- // Many files have these warnings. Move them to art_clang_tidy_errors
- // when all files are free of these warnings.
"android-cloexec-dup",
+ "android-cloexec-open",
"bugprone-argument-comment",
+ "bugprone-lambda-function-name",
+ "bugprone-macro-parentheses",
+ "bugprone-unused-raii", // Protect scoped things like MutexLock.
"bugprone-unused-return-value",
+ "bugprone-virtual-near-miss",
"misc-unused-using-decls",
+ "modernize-use-bool-literals",
"modernize-use-nullptr",
- "modernize-use-using",
"performance-faster-string-find",
"performance-for-range-copy",
+ "performance-implicit-conversion-in-loop",
+ "performance-no-automatic-move",
"performance-noexcept-move-constructor",
+ "performance-unnecessary-copy-initialization",
"performance-unnecessary-value-param",
]
@@ -66,6 +62,9 @@
"-modernize-return-braced-init-list",
"-modernize-use-default-member-init",
"-modernize-pass-by-value",
+ // The only two remaining offenders are art/openjdkjvmti/include/jvmti.h and
+ // libcore/ojluni/src/main/native/jvm.h, which are both external files by Oracle
+ "-modernize-use-using",
]
soong_config_module_type_import {
@@ -246,7 +245,7 @@
},
},
- tidy_checks: art_clang_tidy_errors + art_clang_tidy_allowed + art_clang_tidy_disabled,
+ tidy_checks: art_clang_tidy_errors + art_clang_tidy_disabled,
tidy_checks_as_errors: art_clang_tidy_errors,
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index def3190..11bdf4c 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -27,7 +27,7 @@
# Manually add system libraries that we need to run the host ART tools.
my_files += \
- $(foreach lib, libbacktrace libbase libc++ libicu libicu_jni liblog libsigchain libunwindstack \
+ $(foreach lib, libbase libc++ libicu libicu_jni liblog libsigchain libunwindstack \
libziparchive libjavacore libandroidio libopenjdkd liblz4 liblzma, \
$(call intermediates-dir-for,SHARED_LIBRARIES,$(lib),HOST)/$(lib).so:lib64/$(lib).so \
$(call intermediates-dir-for,SHARED_LIBRARIES,$(lib),HOST,,2ND)/$(lib).so:lib/$(lib).so) \
@@ -107,13 +107,13 @@
art_cmdline_tests \
art_compiler_host_tests \
art_compiler_tests \
- art_dex2oat_tests \
art_dexanalyze_tests \
art_dexdiag_tests \
art_dexdump_tests \
art_dexlayout_tests \
art_dexlist_tests \
art_dexoptanalyzer_tests \
+ art_disassembler_tests \
art_hiddenapi_tests \
art_imgdiag_tests \
art_libartbase_tests \
@@ -127,13 +127,28 @@
art_libprofile_tests \
art_oatdump_tests \
art_profman_tests \
- art_runtime_compiler_tests \
art_runtime_tests \
- art_sigchain_tests \
-ART_TEST_MODULES_TARGET := $(ART_TEST_MODULES_COMMON) art_odrefresh_tests
+# b/258770641 Temporarily disable sigchain and dex2oat tests on ASAN configuration while we
+# investigate the failures.
+ifeq (,$(SANITIZE_HOST))
+ ART_TEST_MODULES_COMMON += art_sigchain_tests
+ ART_TEST_MODULES_COMMON += art_dex2oat_tests
+endif
+
+ART_TEST_MODULES_TARGET := $(ART_TEST_MODULES_COMMON) \
+ art_artd_tests \
+ art_odrefresh_tests \
+
ART_TEST_MODULES_HOST := $(ART_TEST_MODULES_COMMON)
+ifneq (,$(wildcard frameworks/native/libs/binder))
+ # Only include the artd host tests if we have the binder sources available and
+ # can build the libbinder_ndk dependency. It is not available as a prebuilt on
+ # master-art.
+ ART_TEST_MODULES_HOST += art_artd_tests
+endif
+
ART_TARGET_GTEST_NAMES := $(foreach tm,$(ART_TEST_MODULES_TARGET),\
$(foreach path,$(ART_TEST_LIST_device_$(TARGET_ARCH)_$(tm)),\
$(notdir $(path))\
diff --git a/build/README.md b/build/README.md
index 22726ee..3e577bb 100644
--- a/build/README.md
+++ b/build/README.md
@@ -31,7 +31,7 @@
See the [Android source access
instructions](https://source.android.com/setup/build/downloading) for
- further details.
+ further details. Google internal users please see [go/sync](http://go/sync).
2. Set up the development environment:
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index fb071f1..504ce92 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -59,7 +59,6 @@
// TODO(b/124476339): Clean up the following libraries once "required"
// dependencies work with APEX libraries.
"libart-compiler",
- "libartservice",
"libdt_fd_forward",
"libdt_socket",
"libjdwp",
@@ -167,6 +166,8 @@
]
// Core Java libraries.
+// This list must be the same as art-bootclasspath-fragment because it's that which is pulled in
+// through bootclasspath_fragments below. (com.android.art-device-defaults-minus-odrefresh)
libcore_java_libs = [
"core-oj",
"core-libart",
@@ -263,10 +264,12 @@
bootclasspath_fragments: ["art-bootclasspath-fragment"],
systemserverclasspath_fragments: ["art-systemserverclasspath-fragment"],
compat_configs: ["libcore-platform-compat-config"],
- java_libs: libcore_java_libs,
native_shared_libs: art_runtime_base_native_shared_libs +
art_runtime_base_native_device_only_shared_libs +
libcore_native_shared_libs,
+ jni_libs: [
+ "libartservice",
+ ],
binaries: [
"artd",
],
@@ -298,9 +301,6 @@
enabled: false,
},
},
- // Indicates that pre-installed version of this apex can be compressed.
- // Whether it actually will be compressed is controlled on per-device basis.
- compressible: true,
}
apex_defaults {
@@ -321,6 +321,9 @@
art_runtime_run_test_libs +
art_runtime_debug_native_shared_libs +
libcore_debug_native_shared_libs,
+ jni_libs: [
+ "libartserviced",
+ ],
multilib: {
both: {
binaries: art_tools_debug_binaries_both +
@@ -358,6 +361,7 @@
file_contexts: ":com.android.art-file_contexts",
certificate: ":com.android.art.certificate",
installable: false,
+ compressible: false,
}
apex_test {
@@ -395,6 +399,7 @@
// ART gtests with dependencies on internal ART APEX libraries.
art_gtests = [
+ "art_artd_tests",
"art_cmdline_tests",
"art_compiler_tests",
"art_dex2oat_tests",
@@ -403,6 +408,7 @@
"art_dexdump_tests",
"art_dexlayout_tests",
"art_dexlist_tests",
+ "art_disassembler_tests",
"art_dexoptanalyzer_tests",
"art_imgdiag_tests",
"art_libartbase_tests",
@@ -415,7 +421,6 @@
"art_oatdump_tests",
"art_odrefresh_tests",
"art_profman_tests",
- "art_runtime_compiler_tests",
"art_runtime_tests",
"art_sigchain_tests",
]
@@ -430,7 +435,21 @@
certificate: ":com.android.art.certificate",
tests: art_gtests,
binaries: ["signal_dumper"], // Need signal_dumper for run-tests.
+ // Mark this test APEX as non-updatable, as its contains
+ // additional files (used only for testing) that would not pass
+ // dependency checks performed on updatable APEXes (see
+ // go/apex-allowed-deps-error).
updatable: false,
+ // Because this APEX is non-updatable, some of its native shared
+ // libraries (implicitly added as dependencies) are eligible to
+ // the symlink optimization. As we want this APEX to be
+ // self-contained (for testing purposes), we want to package
+ // these dependencies in this APEX, instead of symbolic links to
+ // their counterparts on the `system` partition, which may not
+ // even exist, as in the case of `libbacktrace` (see b/232790938
+ // and b/233357459). Marking this APEX as "future updatable"
+ // disables all symlink optimizations for it.
+ future_updatable: true,
}
// TODO: Do this better. art_apex_test_host will disable host builds when
@@ -505,6 +524,8 @@
art_check_apex_gen_stem = "$(location art-apex-tester)" +
" --deapexer $(location deapexer)" +
" --debugfs $(location debugfs_static)" +
+ " --fsckerofs $(location fsck.erofs)" +
+ " --blkid $(location blkid_static)" +
" --tmpdir $(genDir)"
// The non-flattened APEXes are always checked, as they are always generated
@@ -514,8 +535,10 @@
defaults: ["art_module_source_build_genrule_defaults"],
tools: [
"art-apex-tester",
+ "blkid_static",
"deapexer",
"debugfs_static",
+ "fsck.erofs",
],
}
diff --git a/build/apex/art_apex_test.py b/build/apex/art_apex_test.py
index 428baf4..6e58cf6 100755
--- a/build/apex/art_apex_test.py
+++ b/build/apex/art_apex_test.py
@@ -65,12 +65,14 @@
return var in os.environ and os.environ[var] == 'true'
-def extract_apex(apex_path, deapexer_path, debugfs_path, tmpdir):
+def extract_apex(apex_path, deapexer_path, debugfs_path, fsckerofs_path,
+ blkid_path, tmpdir):
_, apex_name = os.path.split(apex_path)
extract_path = os.path.join(tmpdir, apex_name)
if os.path.exists(extract_path):
shutil.rmtree(extract_path)
subprocess.check_call([deapexer_path, '--debugfs', debugfs_path,
+ '--fsckerofs', fsckerofs_path, '--blkid', blkid_path,
'extract', apex_path, extract_path],
stdout=subprocess.DEVNULL)
return extract_path
@@ -217,6 +219,8 @@
return False, 'Could not find %s'
if fs_object.is_dir:
return False, '%s is a directory'
+ if fs_object.is_symlink:
+ return False, '%s is a symlink'
return True, ''
def is_dir(self, path):
@@ -272,17 +276,22 @@
def check_art_test_executable(self, filename, multilib=None):
dirs = self.arch_dirs_for_path(ART_TEST_DIR, multilib)
if not dirs:
- self.fail('ART test binary missing: %s', filename)
+ self.fail('Directories for ART test binary missing: %s', filename)
+ return
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:
+ file_obj = self._provider.get(test_path)
+ if not file_obj:
+ self.fail('ART test binary missing: %s', test_path)
+ elif not file_obj.is_exec:
self.fail('%s is not executable', test_path)
def check_art_test_data(self, filename):
dirs = self.arch_dirs_for_path(ART_TEST_DIR)
if not dirs:
- self.fail('ART test data missing: %s', filename)
+ self.fail('Directories for ART test data missing: %s', filename)
+ return
for dir in dirs:
if not self.check_file('%s/%s' % (dir, filename)):
return
@@ -471,7 +480,6 @@
self._checker.check_native_library('libart-disassembler')
self._checker.check_native_library('libartbase')
self._checker.check_native_library('libartpalette')
- self._checker.check_native_library('libartservice')
self._checker.check_native_library('libarttools')
self._checker.check_native_library('libdt_fd_forward')
self._checker.check_native_library('libopenjdkjvm')
@@ -504,7 +512,6 @@
# catch invalid dependencies on /system or other APEXes that should go
# through an exported library with stubs (b/128708192 tracks implementing a
# better approach for that).
- self._checker.check_native_library('libbacktrace')
self._checker.check_native_library('libbase')
self._checker.check_native_library('libc++')
self._checker.check_native_library('libdt_socket')
@@ -513,7 +520,6 @@
self._checker.check_native_library('liblzma')
self._checker.check_native_library('libnpt')
self._checker.check_native_library('libunwindstack')
- self._checker.check_native_library('libziparchive')
# Allow extra dependencies that appear in ASAN builds.
self._checker.check_optional_native_library('libclang_rt.asan*')
@@ -550,8 +556,8 @@
self._checker.check_symlinked_multilib_executable('dex2oat')
# Check internal libraries for ART.
+ self._checker.check_native_library('libartservice')
self._checker.check_native_library('libperfetto_hprof')
- self._checker.check_prefer64_library('artd-aidl-ndk')
# Check internal Java libraries
self._checker.check_java_library("service-art")
@@ -637,6 +643,7 @@
self._checker.check_symlinked_multilib_executable('dex2oatd')
# Check ART internal libraries.
+ self._checker.check_native_library('libartserviced')
self._checker.check_native_library('libperfetto_hprofd')
# Check internal native library dependencies.
@@ -664,6 +671,7 @@
def run(self):
# Check ART test binaries.
+ self._checker.check_art_test_executable('art_artd_tests')
self._checker.check_art_test_executable('art_cmdline_tests')
self._checker.check_art_test_executable('art_compiler_tests')
self._checker.check_art_test_executable('art_dex2oat_tests')
@@ -673,6 +681,7 @@
self._checker.check_art_test_executable('art_dexlayout_tests')
self._checker.check_art_test_executable('art_dexlist_tests')
self._checker.check_art_test_executable('art_dexoptanalyzer_tests')
+ self._checker.check_art_test_executable('art_disassembler_tests')
self._checker.check_art_test_executable('art_imgdiag_tests')
self._checker.check_art_test_executable('art_libartbase_tests')
self._checker.check_art_test_executable('art_libartpalette_tests')
@@ -684,7 +693,6 @@
self._checker.check_art_test_executable('art_oatdump_tests')
self._checker.check_art_test_executable('art_odrefresh_tests')
self._checker.check_art_test_executable('art_profman_tests')
- self._checker.check_art_test_executable('art_runtime_compiler_tests')
self._checker.check_art_test_executable('art_runtime_tests')
self._checker.check_art_test_executable('art_sigchain_tests')
@@ -698,6 +706,7 @@
# Check ART jar files which are needed for gtests.
self._checker.check_art_test_data('art-gtest-jars-AbstractMethod.jar')
+ self._checker.check_art_test_data('art-gtest-jars-ArrayClassWithUnresolvedComponent.dex')
self._checker.check_art_test_data('art-gtest-jars-MyClassNatives.jar')
self._checker.check_art_test_data('art-gtest-jars-Main.jar')
self._checker.check_art_test_data('art-gtest-jars-ProtoCompare.jar')
@@ -749,6 +758,7 @@
self._checker.check_art_test_data('art-gtest-jars-MainEmptyUncompressed.jar')
self._checker.check_art_test_data('art-gtest-jars-Dex2oatVdexTestDex.jar')
self._checker.check_art_test_data('art-gtest-jars-Dex2oatVdexPublicSdkDex.dex')
+ self._checker.check_art_test_data('art-gtest-jars-SuperWithAccessChecks.dex')
class NoSuperfluousBinariesChecker:
@@ -889,6 +899,12 @@
if not test_args.debugfs:
logging.error("Need debugfs.")
return 1
+ if not test_args.fsckerofs:
+ logging.error("Need fsck.erofs.")
+ return 1
+ if not test_args.blkid:
+ logging.error("Need blkid.")
+ return 1
if test_args.host:
# Host APEX.
@@ -928,7 +944,7 @@
# Extract the apex. It would be nice to use the output from "deapexer list"
# to avoid this work, but it doesn't provide info about executable bits.
apex_dir = extract_apex(test_args.apex, test_args.deapexer, test_args.debugfs,
- test_args.tmpdir)
+ test_args.fsckerofs, test_args.blkid, test_args.tmpdir)
apex_provider = TargetApexProvider(apex_dir)
except (zipfile.BadZipFile, zipfile.LargeZipFile) as e:
logging.error('Failed to create provider: %s', e)
@@ -1014,6 +1030,8 @@
test_args = test_parser.parse_args(['unused']) # For consistency.
test_args.debugfs = '%s/bin/debugfs' % host_out
+ test_args.fsckerofs = '%s/bin/fsck.erofs' % host_out
+ test_args.blkid = '%s/bin/blkid_static' % host_out
test_args.tmpdir = '.'
test_args.tree = False
test_args.list = False
@@ -1069,6 +1087,8 @@
parser.add_argument('--tmpdir', help='Directory for temp files')
parser.add_argument('--deapexer', help='Path to deapexer')
parser.add_argument('--debugfs', help='Path to debugfs')
+ parser.add_argument('--fsckerofs', help='Path to fsck.erofs')
+ parser.add_argument('--blkid', help='Path to blkid')
parser.add_argument('--bitness', help='Bitness to check', choices=BITNESS_ALL,
default=BITNESS_AUTO)
diff --git a/build/apex/manifest-art.json b/build/apex/manifest-art.json
index 9f2962e..4f20be6 100644
--- a/build/apex/manifest-art.json
+++ b/build/apex/manifest-art.json
@@ -1,6 +1,10 @@
{
"name": "com.android.art",
- "version": 330400000,
+
+ // Placeholder module version to be replaced during build.
+ // Do not change!
+ "version": 0,
+
"provideNativeLibs": [
"libjdwp.so"
],
diff --git a/build/apex/runtests.sh b/build/apex/runtests.sh
index a452fe6..cdbb7c0 100755
--- a/build/apex/runtests.sh
+++ b/build/apex/runtests.sh
@@ -59,14 +59,27 @@
export TARGET_BUILD_UNBUNDLED=true
fi
-have_deapexer_p=false
+deapex_binaries=(
+ blkid_static
+ deapexer
+ debugfs_static
+ fsck.erofs
+)
+
+have_deapex_binaries=false
if [[ "$TARGET_FLATTEN_APEX" != true ]]; then
- if [ ! -e "$HOST_OUT/bin/deapexer" -o ! -e "$HOST_OUT/bin/debugfs_static" ] ; then
- say "Could not find deapexer and/or debugfs_static, building now."
- build/soong/soong_ui.bash --make-mode deapexer debugfs_static-host || \
- die "Cannot build deapexer and debugfs_static"
+ have_deapex_binaries=true
+ for f in ${deapex_binaries[@]}; do
+ if [ ! -e "$HOST_OUT/bin/$f" ]; then
+ have_deapex_binaries=false
+ fi
+ done
+ if $have_deapex_binaries; then :; else
+ deapex_targets=( ${deapex_binaries[@]/%/-host} )
+ say "Building host binaries for deapexer: ${deapex_targets[*]}"
+ build/soong/soong_ui.bash --make-mode ${deapex_targets[@]} || \
+ die "Failed to build: ${deapex_targets[*]}"
fi
- have_deapexer_p=true
fi
# Fail early.
@@ -203,9 +216,11 @@
apex_path="$PRODUCT_OUT/system/apex/${apex_module}.apex"
fi
fi
- if $have_deapexer_p; then
+ if $have_deapex_binaries; then
art_apex_test_args="$art_apex_test_args --deapexer $HOST_OUT/bin/deapexer"
art_apex_test_args="$art_apex_test_args --debugfs $HOST_OUT/bin/debugfs_static"
+ art_apex_test_args="$art_apex_test_args --fsckerofs $HOST_OUT/bin/fsck.erofs"
+ art_apex_test_args="$art_apex_test_args --blkid $HOST_OUT/bin/blkid_static"
fi
case $apex_module in
(*.debug) test_only_args="--flavor debug";;
diff --git a/build/art.go b/build/art.go
index 7914950..cc53719 100644
--- a/build/art.go
+++ b/build/art.go
@@ -39,8 +39,7 @@
cflags = append(cflags, opt)
tlab := false
-
- gcType := ctx.Config().GetenvWithDefault("ART_DEFAULT_GC_TYPE", "CMS")
+ gcType := ctx.Config().GetenvWithDefault("ART_DEFAULT_GC_TYPE", "CMC")
if ctx.Config().IsEnvTrue("ART_TEST_DEBUG_GC") {
gcType = "SS"
@@ -48,9 +47,6 @@
}
cflags = append(cflags, "-DART_DEFAULT_GC_TYPE_IS_"+gcType)
- if tlab {
- cflags = append(cflags, "-DART_USE_TLAB=1")
- }
if ctx.Config().IsEnvTrue("ART_HEAP_POISONING") {
cflags = append(cflags, "-DART_HEAP_POISONING=1")
@@ -70,10 +66,22 @@
asflags = append(asflags,
"-DART_USE_READ_BARRIER=1",
"-DART_READ_BARRIER_TYPE_IS_"+barrierType+"=1")
+
+ if !ctx.Config().IsEnvFalse("ART_USE_GENERATIONAL_CC") {
+ cflags = append(cflags, "-DART_USE_GENERATIONAL_CC=1")
+ }
+ // Force CC only if ART_USE_READ_BARRIER was set to true explicitly during
+ // build time.
+ if ctx.Config().IsEnvTrue("ART_USE_READ_BARRIER") {
+ cflags = append(cflags, "-DART_FORCE_USE_READ_BARRIER=1")
+ }
+ tlab = true
+ } else if gcType == "CMC" {
+ tlab = true
}
- if !ctx.Config().IsEnvFalse("ART_USE_GENERATIONAL_CC") {
- cflags = append(cflags, "-DART_USE_GENERATIONAL_CC=1")
+ if tlab {
+ cflags = append(cflags, "-DART_USE_TLAB=1")
}
cdexLevel := ctx.Config().GetenvWithDefault("ART_DEFAULT_COMPACT_DEX_LEVEL", "fast")
@@ -86,14 +94,16 @@
// the debug version. So make the gap consistent (and adjust for the worst).
if len(ctx.Config().SanitizeDevice()) > 0 || len(ctx.Config().SanitizeHost()) > 0 {
cflags = append(cflags,
- "-DART_STACK_OVERFLOW_GAP_arm=8192",
+ "-DART_STACK_OVERFLOW_GAP_arm=16384",
"-DART_STACK_OVERFLOW_GAP_arm64=16384",
+ "-DART_STACK_OVERFLOW_GAP_riscv64=16384",
"-DART_STACK_OVERFLOW_GAP_x86=16384",
"-DART_STACK_OVERFLOW_GAP_x86_64=20480")
} else {
cflags = append(cflags,
"-DART_STACK_OVERFLOW_GAP_arm=8192",
"-DART_STACK_OVERFLOW_GAP_arm64=8192",
+ "-DART_STACK_OVERFLOW_GAP_riscv64=8192",
"-DART_STACK_OVERFLOW_GAP_x86=8192",
"-DART_STACK_OVERFLOW_GAP_x86_64=8192")
}
@@ -132,7 +142,7 @@
)
cflags = append(cflags, "-DART_BASE_ADDRESS="+ctx.Config().LibartImgDeviceBaseAddress())
- minDelta := ctx.Config().GetenvWithDefault("LIBART_IMG_TARGET_MIN_BASE_ADDRESS_DELTA", "-0x1000000")
+ minDelta := ctx.Config().GetenvWithDefault("LIBART_IMG_TARGET_MIN_BASE_ADDRESS_DELTA", "(-0x1000000)")
maxDelta := ctx.Config().GetenvWithDefault("LIBART_IMG_TARGET_MAX_BASE_ADDRESS_DELTA", "0x1000000")
cflags = append(cflags, "-DART_BASE_ADDRESS_MIN_DELTA="+minDelta)
cflags = append(cflags, "-DART_BASE_ADDRESS_MAX_DELTA="+maxDelta)
@@ -146,7 +156,11 @@
if len(ctx.Config().SanitizeHost()) > 0 {
// art/test/137-cfi/cfi.cc
// error: stack frame size of 1944 bytes in function 'Java_Main_unwindInProcess'
- hostFrameSizeLimit = 6400
+ // b/249586057, need larger stack frame for newer clang compilers
+ hostFrameSizeLimit = 10000
+ // cannot add "-fsanitize-address-use-after-return=never" everywhere,
+ // or some file like compiler_driver.o can have stack frame of 30072 bytes.
+ // cflags = append(cflags, "-fsanitize-address-use-after-return=never")
}
cflags = append(cflags,
fmt.Sprintf("-Wframe-larger-than=%d", hostFrameSizeLimit),
@@ -154,7 +168,7 @@
)
cflags = append(cflags, "-DART_BASE_ADDRESS="+ctx.Config().LibartImgHostBaseAddress())
- minDelta := ctx.Config().GetenvWithDefault("LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA", "-0x1000000")
+ minDelta := ctx.Config().GetenvWithDefault("LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA", "(-0x1000000)")
maxDelta := ctx.Config().GetenvWithDefault("LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA", "0x1000000")
cflags = append(cflags, "-DART_BASE_ADDRESS_MIN_DELTA="+minDelta)
cflags = append(cflags, "-DART_BASE_ADDRESS_MAX_DELTA="+maxDelta)
@@ -165,7 +179,7 @@
}
clang_path := filepath.Join(config.ClangDefaultBase, ctx.Config().PrebuiltOS(), config.ClangDefaultVersion)
- cflags = append(cflags, "-DART_CLANG_PATH=\""+clang_path+"\"")
+ cflags = append(cflags, fmt.Sprintf("-DART_CLANG_PATH=\"%s\"", clang_path))
return cflags
}
@@ -305,7 +319,7 @@
// The 'key' is the file in testcases and 'value' is the path to copy it from.
// The actual copy will be done in make since soong does not do installations.
func addTestcasesFile(ctx android.InstallHookContext) {
- if ctx.Os() != ctx.Config().BuildOS || ctx.Module().IsSkipInstall() {
+ if ctx.Os() != ctx.Config().BuildOS || ctx.Target().HostCross || ctx.Module().IsSkipInstall() {
return
}
@@ -456,7 +470,8 @@
}
func artTest() android.Module {
- module := cc.TestFactory()
+ // Disable bp2build.
+ module := cc.NewTest(android.HostAndDeviceSupported, false /* bazelable */).Init()
installCodegenCustomizer(module, binary)
diff --git a/build/boot/boot-image-profile.txt b/build/boot/boot-image-profile.txt
index 5121ddf..ca2f8f2 100644
--- a/build/boot/boot-image-profile.txt
+++ b/build/boot/boot-image-profile.txt
@@ -10434,6 +10434,7 @@
Ldalvik/annotation/optimization/CriticalNative;
Ldalvik/annotation/optimization/FastNative;
Ldalvik/annotation/optimization/NeverCompile;
+Ldalvik/annotation/optimization/NeverInline;
Ldalvik/system/AppSpecializationHooks;
Ldalvik/system/BaseDexClassLoader$Reporter;
Ldalvik/system/BaseDexClassLoader;
diff --git a/build/boot/preloaded-classes b/build/boot/preloaded-classes
index 84928ea..4d22e22 100644
--- a/build/boot/preloaded-classes
+++ b/build/boot/preloaded-classes
@@ -466,6 +466,7 @@
dalvik.annotation.optimization.CriticalNative
dalvik.annotation.optimization.FastNative
dalvik.annotation.optimization.NeverCompile
+dalvik.annotation.optimization.NeverInline
dalvik.system.AppSpecializationHooks
dalvik.system.BaseDexClassLoader$Reporter
dalvik.system.BaseDexClassLoader
@@ -705,6 +706,7 @@
java.lang.Object
java.lang.OutOfMemoryError
java.lang.Package
+java.lang.Process$PipeInputStream
java.lang.Process
java.lang.ProcessBuilder$NullInputStream
java.lang.ProcessBuilder$NullOutputStream
@@ -715,6 +717,13 @@
java.lang.ProcessEnvironment$Value
java.lang.ProcessEnvironment$Variable
java.lang.ProcessEnvironment
+java.lang.ProcessHandleImpl$ExitCompletion
+java.lang.ProcessHandleImpl$Info
+java.lang.ProcessHandleImpl
+java.lang.ProcessImpl$DeferredCloseInputStream
+java.lang.ProcessImpl$DeferredCloseProcessPipeInputStream
+java.lang.ProcessImpl$ProcessPipeInputStream
+java.lang.ProcessImpl$ProcessPipeOutputStream
java.lang.ProcessImpl
java.lang.Readable
java.lang.ReflectiveOperationException
@@ -2413,6 +2422,7 @@
jdk.internal.math.FormattedFloatingDecimal
jdk.internal.misc.JavaObjectInputStreamAccess
jdk.internal.misc.SharedSecrets
+jdk.internal.misc.TerminatingThreadLocal
jdk.internal.misc.Unsafe
jdk.internal.misc.VM
jdk.internal.reflect.Reflection
diff --git a/build/build-art-module.sh b/build/build-art-module.sh
deleted file mode 100755
index c6726f1..0000000
--- a/build/build-art-module.sh
+++ /dev/null
@@ -1,130 +0,0 @@
-#!/bin/bash -e
-
-# This script builds the APEX modules, SDKs and module exports that the ART
-# Module provides.
-
-if [ ! -e build/make/core/Makefile ]; then
- echo "$0 must be run from the top of the tree"
- exit 1
-fi
-
-skip_apex=
-skip_module_sdk=
-build_args=()
-for arg; do
- case "$arg" in
- --skip-apex) skip_apex=true ;;
- --skip-module-sdk) skip_module_sdk=true ;;
- *) build_args+=("$arg") ;;
- esac
- shift
-done
-
-if [ -z "$skip_apex" ]; then
- # Take the list of modules from MAINLINE_MODULES.
- if [ -n "${MAINLINE_MODULES}" ]; then
- read -r -a MAINLINE_MODULES <<< "${MAINLINE_MODULES}"
- else
- MAINLINE_MODULES=(
- com.android.art
- com.android.art.debug
- )
- fi
-else
- MAINLINE_MODULES=()
-fi
-
-# Take the list of products to build the modules for from
-# MAINLINE_MODULE_PRODUCTS.
-if [ -n "${MAINLINE_MODULE_PRODUCTS}" ]; then
- read -r -a MAINLINE_MODULE_PRODUCTS <<< "${MAINLINE_MODULE_PRODUCTS}"
-else
- # The default products are the same as in
- # build/soong/scripts/build-mainline-modules.sh.
- MAINLINE_MODULE_PRODUCTS=(
- art_module_arm
- art_module_arm64
- art_module_x86
- art_module_x86_64
- )
-fi
-
-MODULE_SDKS_AND_EXPORTS=()
-if [ -z "$skip_module_sdk" ]; then
- MODULE_SDKS_AND_EXPORTS=(
- art-module-sdk
- art-module-host-exports
- art-module-test-exports
- )
-fi
-
-echo_and_run() {
- echo "$*"
- "$@"
-}
-
-export OUT_DIR=${OUT_DIR:-out}
-export DIST_DIR=${DIST_DIR:-${OUT_DIR}/dist}
-
-# Use same build settings as build_unbundled_mainline_module.sh, for build
-# consistency.
-# TODO(mast): Call out to a common script for building APEXes.
-export UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true
-export TARGET_BUILD_VARIANT=${TARGET_BUILD_VARIANT:-"user"}
-export TARGET_BUILD_DENSITY=alldpi
-export TARGET_BUILD_TYPE=release
-
-if [ ! -d frameworks/base ]; then
- # Configure the build system for the reduced manifest branch.
- export SOONG_ALLOW_MISSING_DEPENDENCIES=true
-fi
-
-if [ ${#MAINLINE_MODULES[*]} -gt 0 ]; then
- (
- export TARGET_BUILD_APPS="${MAINLINE_MODULES[*]}"
-
- # We require .apex files here, so ensure we get them regardless of product
- # settings.
- export OVERRIDE_TARGET_FLATTEN_APEX=false
-
- for product in ${MAINLINE_MODULE_PRODUCTS[*]}; do
- echo_and_run build/soong/soong_ui.bash --make-mode \
- TARGET_PRODUCT=${product} "${build_args[@]}" ${MAINLINE_MODULES[*]}
-
- vars="$(TARGET_PRODUCT=${product} build/soong/soong_ui.bash \
- --dumpvars-mode --vars="PRODUCT_OUT TARGET_ARCH")"
- # Assign to a variable and eval that, since bash ignores any error status
- # from the command substitution if it's directly on the eval line.
- eval $vars
-
- mkdir -p ${DIST_DIR}/${TARGET_ARCH}
- for module in ${MAINLINE_MODULES[*]}; do
- echo_and_run cp ${PRODUCT_OUT}/system/apex/${module}.apex \
- ${DIST_DIR}/${TARGET_ARCH}/
- done
- done
- )
-fi
-
-if [ ${#MODULE_SDKS_AND_EXPORTS[*]} -gt 0 ]; then
- # Create multi-arch SDKs in a different out directory. The multi-arch script
- # uses Soong in --soong-only mode which cannot use the same directory as
- # normal mode with make.
- export OUT_DIR=${OUT_DIR}/aml
-
- # Put the build system in apps building mode so we don't trip on platform
- # dependencies, but there are no actual apps to build here.
- export TARGET_BUILD_APPS=none
-
- # We use force building LLVM components flag (even though we actually don't
- # compile them) because we don't have bionic host prebuilts
- # for them.
- export FORCE_BUILD_LLVM_COMPONENTS=true
-
- echo_and_run build/soong/scripts/build-aml-prebuilts.sh \
- TARGET_PRODUCT=mainline_sdk "${build_args[@]}" ${MODULE_SDKS_AND_EXPORTS[*]}
-
- rm -rf ${DIST_DIR}/mainline-sdks
- mkdir -p ${DIST_DIR}
- echo_and_run cp -r ${OUT_DIR}/soong/mainline-sdks ${DIST_DIR}/
-fi
diff --git a/build/codegen.go b/build/codegen.go
index 96dd223..66569be 100644
--- a/build/codegen.go
+++ b/build/codegen.go
@@ -62,6 +62,8 @@
arch = &c.Codegen.Arm
case "arm64":
arch = &c.Codegen.Arm64
+ case "riscv64":
+ arch = &c.Codegen.Riscv64
case "x86":
arch = &c.Codegen.X86
case "x86_64":
@@ -205,7 +207,7 @@
type codegenProperties struct {
Codegen struct {
- Arm, Arm64, X86, X86_64 codegenArchProperties
+ Arm, Arm64, Riscv64, X86, X86_64 codegenArchProperties
}
}
diff --git a/build/makevars.go b/build/makevars.go
index 1965b5d..0606bab 100644
--- a/build/makevars.go
+++ b/build/makevars.go
@@ -34,6 +34,7 @@
"bin/llvm-addr2line",
"bin/llvm-dwarfdump",
"bin/llvm-objdump",
+ "lib/libc++.so.1",
"lib64/libc++.so.1",
}
)
diff --git a/build/sdk/Android.bp b/build/sdk/Android.bp
index a9ee675..b47bdc2 100644
--- a/build/sdk/Android.bp
+++ b/build/sdk/Android.bp
@@ -93,17 +93,10 @@
},
android: {
- bootclasspath_fragments: [
- // Adds the fragment and its contents to the sdk.
- "art-bootclasspath-fragment",
- ],
-
- systemserverclasspath_fragments: [
- "art-systemserverclasspath-fragment",
- ],
-
- compat_configs: [
- "libcore-platform-compat-config",
+ apexes: [
+ // Adds exportable dependencies of the API to the sdk,
+ // e.g. *classpath_fragments.
+ "com.android.art",
],
java_header_libs: [
diff --git a/cmdline/cmdline_parser_test.cc b/cmdline/cmdline_parser_test.cc
index 38c37ec..effbee9 100644
--- a/cmdline/cmdline_parser_test.cc
+++ b/cmdline/cmdline_parser_test.cc
@@ -528,10 +528,12 @@
TEST_F(CmdlineParserTest, TestIgnoreUnrecognized) {
RuntimeParser::Builder parserBuilder;
+ // clang-format off
parserBuilder
.Define("-help")
.IntoKey(M::Help)
.IgnoreUnrecognized(true);
+ // clang-format on
parser_.reset(new RuntimeParser(parserBuilder.Build()));
diff --git a/cmdline/cmdline_types.h b/cmdline/cmdline_types.h
index dc2f8b7..b16f069 100644
--- a/cmdline/cmdline_types.h
+++ b/cmdline/cmdline_types.h
@@ -527,6 +527,8 @@
return gc::kCollectorTypeSS;
} else if (option == "CC") {
return gc::kCollectorTypeCC;
+ } else if (option == "CMC") {
+ return gc::kCollectorTypeCMC;
} else {
return gc::kCollectorTypeNone;
}
@@ -539,7 +541,7 @@
bool verify_pre_gc_heap_ = false;
bool verify_pre_sweeping_heap_ = kIsDebugBuild;
bool generational_cc = kEnableGenerationalCCByDefault;
- bool verify_post_gc_heap_ = false;
+ bool verify_post_gc_heap_ = kIsDebugBuild;
bool verify_pre_gc_rosalloc_ = kIsDebugBuild;
bool verify_pre_sweeping_rosalloc_ = false;
bool verify_post_gc_rosalloc_ = false;
diff --git a/cmdline/detail/cmdline_parse_argument_detail.h b/cmdline/detail/cmdline_parse_argument_detail.h
index de0a588..936d290 100644
--- a/cmdline/detail/cmdline_parse_argument_detail.h
+++ b/cmdline/detail/cmdline_parse_argument_detail.h
@@ -150,7 +150,7 @@
for (auto cname : names_) {
std::string_view name = cname;
if (using_blanks_) {
- name = name.substr(0, name.find("_"));
+ name = name.substr(0, name.find('_'));
}
auto& os = vios.Stream();
auto print_once = [&]() {
diff --git a/cmdline/token_range.h b/cmdline/token_range.h
index e28ead9..e917e1d 100644
--- a/cmdline/token_range.h
+++ b/cmdline/token_range.h
@@ -81,7 +81,7 @@
{}
// Non-copying constructor. Retain reference to existing list of tokens.
- TokenRange(std::shared_ptr<TokenList> token_list,
+ TokenRange(const std::shared_ptr<TokenList>& token_list,
TokenList::const_iterator it_begin,
TokenList::const_iterator it_end)
: token_list_(token_list),
@@ -98,7 +98,7 @@
TokenRange(TokenRange&&) = default;
// Non-copying constructor. Retains reference to an existing list of tokens, with offset.
- explicit TokenRange(std::shared_ptr<TokenList> token_list)
+ explicit TokenRange(const std::shared_ptr<TokenList>& token_list)
: token_list_(token_list),
begin_(token_list_->begin()),
end_(token_list_->end())
diff --git a/cmdline/unit.h b/cmdline/unit.h
index f73981f..0b5c344 100644
--- a/cmdline/unit.h
+++ b/cmdline/unit.h
@@ -21,13 +21,7 @@
// Used for arguments that simply indicate presence (e.g. "-help") without any values.
struct Unit {
- // Historical note: We specified a user-defined constructor to avoid
- // 'Conditional jump or move depends on uninitialised value(s)' errors
- // when running Valgrind.
- Unit() {}
- Unit(const Unit&) = default;
- ~Unit() {}
- bool operator==(Unit) const {
+ bool operator==(const Unit&) const {
return true;
}
};
diff --git a/compiler/Android.bp b/compiler/Android.bp
index de98fdb..cab7fbe 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -33,11 +33,8 @@
defaults: ["art_defaults"],
host_supported: true,
srcs: [
- "compiled_method.cc",
"debug/elf_debug_writer.cc",
"dex/inline_method_analyser.cc",
- "dex/verification_results.cc",
- "driver/compiled_method_storage.cc",
"driver/compiler_options.cc",
"driver/dex_compilation_unit.cc",
"jit/jit_compiler.cc",
@@ -94,10 +91,10 @@
"optimizing/ssa_phi_elimination.cc",
"optimizing/stack_map_stream.cc",
"optimizing/superblock_cloner.cc",
+ "optimizing/write_barrier_elimination.cc",
"trampolines/trampoline_compiler.cc",
"utils/assembler.cc",
"utils/jni_macro_assembler.cc",
- "utils/swap_space.cc",
"compiler.cc",
],
@@ -176,6 +173,8 @@
],
export_include_dirs: ["."],
+ // Not using .map.txt because this is an internal API
+ version_script: "libart-compiler.map",
}
cc_defaults {
@@ -228,7 +227,7 @@
"libprofile",
"libdexfile",
],
- whole_static_libs: ["libelffile"],
+ static_libs: ["libelffile"],
runtime_libs: [
// `art::HGraphVisualizerDisassembler::HGraphVisualizerDisassembler` may dynamically load
// `libart-disassembler.so`.
@@ -296,7 +295,7 @@
"libprofiled",
"libdexfiled",
],
- whole_static_libs: ["libelffiled"],
+ static_libs: ["libelffiled"],
runtime_libs: [
// `art::HGraphVisualizerDisassembler::HGraphVisualizerDisassembler` may dynamically load
// `libartd-disassembler.so`.
@@ -369,6 +368,7 @@
data: [
":art-gtest-jars-ExceptionHandle",
":art-gtest-jars-Interfaces",
+ ":art-gtest-jars-Main",
":art-gtest-jars-MyClassNatives",
],
tidy_timeout_srcs: [
@@ -381,9 +381,9 @@
"optimizing/ssa_test.cc",
],
srcs: [
+ "compiler_reflection_test.cc",
"debug/dwarf/dwarf_test.cc",
"debug/src_map_elem_test.cc",
- "driver/compiled_method_storage_test.cc",
"exception_test.cc",
"jni/jni_compiler_test.cc",
"linker/linker_patch_test.cc",
@@ -419,7 +419,6 @@
"optimizing/suspend_check_test.cc",
"utils/atomic_dex_ref_map_test.cc",
"utils/dedupe_set_test.cc",
- "utils/swap_space_test.cc",
"jni/jni_cfi_test.cc",
"optimizing/codegen_test.cc",
@@ -465,8 +464,8 @@
],
shared_libs: [
- "libbacktrace",
"libnativeloader",
+ "libunwindstack",
],
target: {
@@ -488,10 +487,12 @@
],
shared_libs: [
"libprofiled",
- "libartd-compiler",
"libartd-simulator-container",
+ "liblzma",
],
static_libs: [
+ "libartd-compiler",
+ "libelffiled",
"libvixld",
],
}
@@ -506,7 +507,8 @@
data: [":generate-boot-image"],
shared_libs: [
"libprofile",
- "libart-compiler",
+ "liblzma",
+ "libartpalette",
],
static_libs: [
// For now, link `libart-simulator-container` statically for simplicity,
@@ -515,6 +517,8 @@
// TODO(b/192070541): Consider linking `libart-simulator-container`
// dynamically.
"libart-simulator-container",
+ "libart-compiler",
+ "libelffile",
"libvixl",
],
test_config: "art_standalone_compiler_tests.xml",
@@ -548,9 +552,11 @@
},
},
shared_libs: [
- "libartd-compiler",
+ "liblzma",
],
static_libs: [
+ "libartd-compiler",
+ "libelffiled",
"libvixld",
],
}
diff --git a/compiler/art_standalone_compiler_tests.xml b/compiler/art_standalone_compiler_tests.xml
index f723971..394ac8d 100644
--- a/compiler/art_standalone_compiler_tests.xml
+++ b/compiler/art_standalone_compiler_tests.xml
@@ -14,6 +14,8 @@
limitations under the License.
-->
<configuration description="Runs art_standalone_compiler_tests.">
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.art.apex" />
+
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="art_standalone_compiler_tests->/data/local/tmp/art_standalone_compiler_tests/art_standalone_compiler_tests" />
@@ -24,6 +26,7 @@
<option name="cleanup" value="true" />
<option name="push" value="art-gtest-jars-ExceptionHandle.jar->/data/local/tmp/art_standalone_compiler_tests/art-gtest-jars-ExceptionHandle.jar" />
<option name="push" value="art-gtest-jars-Interfaces.jar->/data/local/tmp/art_standalone_compiler_tests/art-gtest-jars-Interfaces.jar" />
+ <option name="push" value="art-gtest-jars-Main.jar->/data/local/tmp/art_standalone_compiler_tests/art-gtest-jars-Main.jar" />
<option name="push" value="art-gtest-jars-MyClassNatives.jar->/data/local/tmp/art_standalone_compiler_tests/art-gtest-jars-MyClassNatives.jar" />
</target_preparer>
diff --git a/compiler/cfi_test.h b/compiler/cfi_test.h
index 9755ef1..e65bee8 100644
--- a/compiler/cfi_test.h
+++ b/compiler/cfi_test.h
@@ -23,6 +23,7 @@
#include "arch/instruction_set.h"
#include "base/enums.h"
+#include "base/macros.h"
#include "debug/dwarf/dwarf_test.h"
#include "disassembler.h"
#include "dwarf/dwarf_constants.h"
@@ -30,7 +31,7 @@
#include "gtest/gtest.h"
#include "thread.h"
-namespace art {
+namespace art HIDDEN {
class CFITest : public dwarf::DwarfTest {
public:
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc
index bbb2016..442b96e 100644
--- a/compiler/common_compiler_test.cc
+++ b/compiler/common_compiler_test.cc
@@ -28,10 +28,8 @@
#include "base/memfd.h"
#include "base/utils.h"
#include "class_linker.h"
-#include "compiled_method-inl.h"
#include "dex/descriptors_names.h"
-#include "dex/verification_results.h"
-#include "driver/compiled_method_storage.h"
+#include "driver/compiled_code_storage.h"
#include "driver/compiler_options.h"
#include "jni/java_vm_ext.h"
#include "interpreter/interpreter.h"
@@ -44,7 +42,7 @@
#include "thread-current-inl.h"
#include "utils/atomic_dex_ref_map-inl.h"
-namespace art {
+namespace art HIDDEN {
class CommonCompilerTestImpl::CodeAndMetadata {
public:
@@ -58,10 +56,10 @@
const uint32_t vmap_table_offset = vmap_table.empty() ? 0u
: sizeof(OatQuickMethodHeader) + vmap_table.size();
OatQuickMethodHeader method_header(vmap_table_offset);
- const size_t code_alignment = GetInstructionSetAlignment(instruction_set);
+ const size_t code_alignment = GetInstructionSetCodeAlignment(instruction_set);
DCHECK_ALIGNED_PARAM(kPageSize, code_alignment);
- code_offset_ = RoundUp(vmap_table.size() + sizeof(method_header), code_alignment);
- const uint32_t capacity = RoundUp(code_offset_ + code_size, kPageSize);
+ const uint32_t code_offset = RoundUp(vmap_table.size() + sizeof(method_header), code_alignment);
+ const uint32_t capacity = RoundUp(code_offset + code_size, kPageSize);
// Create a memfd handle with sufficient capacity.
android::base::unique_fd mem_fd(art::memfd_create_compat("test code", /*flags=*/ 0));
@@ -82,12 +80,12 @@
CHECK(rw_map_.IsValid()) << error_msg;
// Store data.
- uint8_t* code_addr = rw_map_.Begin() + code_offset_;
+ uint8_t* code_addr = rw_map_.Begin() + code_offset;
CHECK_ALIGNED_PARAM(code_addr, code_alignment);
- CHECK_LE(vmap_table_offset, code_offset_);
+ CHECK_LE(vmap_table_offset, code_offset);
memcpy(code_addr - vmap_table_offset, vmap_table.data(), vmap_table.size());
static_assert(std::is_trivially_copyable<OatQuickMethodHeader>::value, "Cannot use memcpy");
- CHECK_LE(sizeof(method_header), code_offset_);
+ CHECK_LE(sizeof(method_header), code_offset);
memcpy(code_addr - sizeof(method_header), &method_header, sizeof(method_header));
CHECK_LE(code_size, static_cast<size_t>(rw_map_.End() - code_addr));
memcpy(code_addr, code.data(), code_size);
@@ -108,22 +106,84 @@
/*filename=*/ "test code",
&error_msg);
CHECK(rx_map_.IsValid()) << error_msg;
+
+ DCHECK_LT(code_offset, rx_map_.Size());
+ size_t adjustment = GetInstructionSetEntryPointAdjustment(instruction_set);
+ entry_point_ = rx_map_.Begin() + code_offset + adjustment;
}
- const void* GetCodePointer() const {
+ const void* GetEntryPoint() const {
DCHECK(rx_map_.IsValid());
- DCHECK_LE(code_offset_, rx_map_.Size());
- return rx_map_.Begin() + code_offset_;
+ return entry_point_;
}
private:
MemMap rw_map_;
MemMap rx_map_;
- uint32_t code_offset_;
+ const void* entry_point_;
DISALLOW_COPY_AND_ASSIGN(CodeAndMetadata);
};
+class CommonCompilerTestImpl::OneCompiledMethodStorage final : public CompiledCodeStorage {
+ public:
+ OneCompiledMethodStorage() {}
+ ~OneCompiledMethodStorage() {}
+
+ CompiledMethod* CreateCompiledMethod(InstructionSet instruction_set,
+ ArrayRef<const uint8_t> code,
+ ArrayRef<const uint8_t> stack_map,
+ ArrayRef<const uint8_t> cfi ATTRIBUTE_UNUSED,
+ ArrayRef<const linker::LinkerPatch> patches,
+ bool is_intrinsic ATTRIBUTE_UNUSED) override {
+ // Supports only one method at a time.
+ CHECK_EQ(instruction_set_, InstructionSet::kNone);
+ CHECK_NE(instruction_set, InstructionSet::kNone);
+ instruction_set_ = instruction_set;
+ CHECK(code_.empty());
+ CHECK(!code.empty());
+ code_.assign(code.begin(), code.end());
+ CHECK(stack_map_.empty());
+ CHECK(!stack_map.empty());
+ stack_map_.assign(stack_map.begin(), stack_map.end());
+ CHECK(patches.empty()) << "Linker patches are unsupported for compiler gtests.";
+ return reinterpret_cast<CompiledMethod*>(this);
+ }
+
+ ArrayRef<const uint8_t> GetThunkCode(const linker::LinkerPatch& patch ATTRIBUTE_UNUSED,
+ /*out*/ std::string* debug_name ATTRIBUTE_UNUSED) override {
+ LOG(FATAL) << "Unsupported.";
+ UNREACHABLE();
+ }
+
+ void SetThunkCode(const linker::LinkerPatch& patch ATTRIBUTE_UNUSED,
+ ArrayRef<const uint8_t> code ATTRIBUTE_UNUSED,
+ const std::string& debug_name ATTRIBUTE_UNUSED) override {
+ LOG(FATAL) << "Unsupported.";
+ UNREACHABLE();
+ }
+
+ InstructionSet GetInstructionSet() const {
+ CHECK_NE(instruction_set_, InstructionSet::kNone);
+ return instruction_set_;
+ }
+
+ ArrayRef<const uint8_t> GetCode() const {
+ CHECK(!code_.empty());
+ return ArrayRef<const uint8_t>(code_);
+ }
+
+ ArrayRef<const uint8_t> GetStackMap() const {
+ CHECK(!stack_map_.empty());
+ return ArrayRef<const uint8_t>(stack_map_);
+ }
+
+ private:
+ InstructionSet instruction_set_ = InstructionSet::kNone;
+ std::vector<uint8_t> code_;
+ std::vector<uint8_t> stack_map_;
+};
+
std::unique_ptr<CompilerOptions> CommonCompilerTestImpl::CreateCompilerOptions(
InstructionSet instruction_set, const std::string& variant) {
std::unique_ptr<CompilerOptions> compiler_options = std::make_unique<CompilerOptions>();
@@ -143,24 +203,7 @@
InstructionSet instruction_set) {
CHECK_NE(code.size(), 0u);
code_and_metadata_.emplace_back(code, vmap_table, instruction_set);
- return code_and_metadata_.back().GetCodePointer();
-}
-
-void CommonCompilerTestImpl::MakeExecutable(ArtMethod* method,
- const CompiledMethod* compiled_method) {
- CHECK(method != nullptr);
- const void* method_code = nullptr;
- // If the code size is 0 it means the method was skipped due to profile guided compilation.
- if (compiled_method != nullptr && compiled_method->GetQuickCode().size() != 0u) {
- const void* code_ptr = MakeExecutable(compiled_method->GetQuickCode(),
- compiled_method->GetVmapTable(),
- compiled_method->GetInstructionSet());
- method_code =
- CompiledMethod::CodePointer(code_ptr, compiled_method->GetInstructionSet());
- LOG(INFO) << "MakeExecutable " << method->PrettyMethod() << " code=" << method_code;
- }
- Runtime::Current()->GetInstrumentation()->InitializeMethodsCode(
- method, /*aot_code=*/ method_code);
+ return code_and_metadata_.back().GetEntryPoint();
}
void CommonCompilerTestImpl::SetUp() {
@@ -207,7 +250,6 @@
void CommonCompilerTestImpl::SetUpRuntimeOptionsImpl() {
compiler_options_.reset(new CompilerOptions);
- verification_results_.reset(new VerificationResults());
ApplyInstructionSet();
}
@@ -221,7 +263,6 @@
void CommonCompilerTestImpl::TearDown() {
code_and_metadata_.clear();
- verification_results_.reset();
compiler_options_.reset();
}
@@ -229,7 +270,7 @@
CHECK(method != nullptr);
TimingLogger timings("CommonCompilerTestImpl::CompileMethod", false, false);
TimingLogger::ScopedTiming t(__FUNCTION__, &timings);
- CompiledMethodStorage storage(/*swap_fd=*/ -1);
+ OneCompiledMethodStorage storage;
CompiledMethod* compiled_method = nullptr;
{
DCHECK(!Runtime::Current()->IsStarted());
@@ -241,7 +282,6 @@
Handle<mirror::DexCache> dex_cache =
hs.NewHandle(GetClassLinker()->FindDexCache(self, dex_file));
Handle<mirror::ClassLoader> class_loader = hs.NewHandle(method->GetClassLoader());
- compiler_options_->verification_results_ = verification_results_.get();
if (method->IsNative()) {
compiled_method = compiler->JniCompile(method->GetAccessFlags(),
method->GetDexMethodIndex(),
@@ -257,48 +297,17 @@
dex_file,
dex_cache);
}
- compiler_options_->verification_results_ = nullptr;
+ CHECK(compiled_method != nullptr) << "Failed to compile " << method->PrettyMethod();
+ CHECK_EQ(reinterpret_cast<OneCompiledMethodStorage*>(compiled_method), &storage);
}
- CHECK(method != nullptr);
{
TimingLogger::ScopedTiming t2("MakeExecutable", &timings);
- MakeExecutable(method, compiled_method);
+ const void* method_code = MakeExecutable(storage.GetCode(),
+ storage.GetStackMap(),
+ storage.GetInstructionSet());
+ LOG(INFO) << "MakeExecutable " << method->PrettyMethod() << " code=" << method_code;
+ GetRuntime()->GetInstrumentation()->InitializeMethodsCode(method, /*aot_code=*/ method_code);
}
- CompiledMethod::ReleaseSwapAllocatedCompiledMethod(&storage, compiled_method);
-}
-
-void CommonCompilerTestImpl::CompileDirectMethod(Handle<mirror::ClassLoader> class_loader,
- const char* class_name,
- const char* method_name,
- const char* signature) {
- std::string class_descriptor(DotToDescriptor(class_name));
- Thread* self = Thread::Current();
- ClassLinker* class_linker = GetClassLinker();
- ObjPtr<mirror::Class> klass =
- class_linker->FindClass(self, class_descriptor.c_str(), class_loader);
- CHECK(klass != nullptr) << "Class not found " << class_name;
- auto pointer_size = class_linker->GetImagePointerSize();
- ArtMethod* method = klass->FindClassMethod(method_name, signature, pointer_size);
- CHECK(method != nullptr && method->IsDirect()) << "Direct method not found: "
- << class_name << "." << method_name << signature;
- CompileMethod(method);
-}
-
-void CommonCompilerTestImpl::CompileVirtualMethod(Handle<mirror::ClassLoader> class_loader,
- const char* class_name,
- const char* method_name,
- const char* signature) {
- std::string class_descriptor(DotToDescriptor(class_name));
- Thread* self = Thread::Current();
- ClassLinker* class_linker = GetClassLinker();
- ObjPtr<mirror::Class> klass =
- class_linker->FindClass(self, class_descriptor.c_str(), class_loader);
- CHECK(klass != nullptr) << "Class not found " << class_name;
- auto pointer_size = class_linker->GetImagePointerSize();
- ArtMethod* method = klass->FindClassMethod(method_name, signature, pointer_size);
- CHECK(method != nullptr && !method->IsDirect()) << "Virtual method not found: "
- << class_name << "." << method_name << signature;
- CompileMethod(method);
}
void CommonCompilerTestImpl::ClearBootImageOption() {
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index 89cc1fa..f3cd132 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -24,25 +24,25 @@
#include "arch/instruction_set.h"
#include "arch/instruction_set_features.h"
+#include "base/macros.h"
#include "common_runtime_test.h"
#include "compiler.h"
#include "oat_file.h"
-namespace art {
+namespace art HIDDEN {
namespace mirror {
class ClassLoader;
} // namespace mirror
-class CompiledMethod;
class CompilerOptions;
class CumulativeLogger;
class DexFile;
class TimingLogger;
-class VerificationResults;
template<class T> class Handle;
-class CommonCompilerTestImpl {
+// Export all symbols in `CommonCompilerTestImpl` for dex2oat tests.
+class EXPORT CommonCompilerTestImpl {
public:
static std::unique_ptr<CompilerOptions> CreateCompilerOptions(InstructionSet instruction_set,
const std::string& variant);
@@ -55,9 +55,6 @@
ArrayRef<const uint8_t> vmap_table,
InstructionSet instruction_set);
- void MakeExecutable(ArtMethod* method, const CompiledMethod* compiled_method)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
protected:
void SetUp();
@@ -74,14 +71,6 @@
void CompileMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
- void CompileDirectMethod(Handle<mirror::ClassLoader> class_loader, const char* class_name,
- const char* method_name, const char* signature)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
- void CompileVirtualMethod(Handle<mirror::ClassLoader> class_loader, const char* class_name,
- const char* method_name, const char* signature)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
void ApplyInstructionSet();
void OverrideInstructionSetFeatures(InstructionSet instruction_set, const std::string& variant);
@@ -96,7 +85,6 @@
= InstructionSetFeatures::FromCppDefines();
std::unique_ptr<CompilerOptions> compiler_options_;
- std::unique_ptr<VerificationResults> verification_results_;
protected:
virtual ClassLinker* GetClassLinker() = 0;
@@ -104,6 +92,8 @@
private:
class CodeAndMetadata;
+ class OneCompiledMethodStorage;
+
std::vector<CodeAndMetadata> code_and_metadata_;
};
diff --git a/compiler/compiled_method-inl.h b/compiler/compiled_method-inl.h
deleted file mode 100644
index e60b30f..0000000
--- a/compiler/compiled_method-inl.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ART_COMPILER_COMPILED_METHOD_INL_H_
-#define ART_COMPILER_COMPILED_METHOD_INL_H_
-
-#include "compiled_method.h"
-
-#include "base/array_ref.h"
-#include "base/length_prefixed_array.h"
-#include "linker/linker_patch.h"
-
-namespace art {
-
-inline ArrayRef<const uint8_t> CompiledCode::GetQuickCode() const {
- return GetArray(quick_code_);
-}
-
-template <typename T>
-inline ArrayRef<const T> CompiledCode::GetArray(const LengthPrefixedArray<T>* array) {
- if (array == nullptr) {
- return ArrayRef<const T>();
- }
- DCHECK_NE(array->size(), 0u);
- return ArrayRef<const T>(&array->At(0), array->size());
-}
-
-inline ArrayRef<const uint8_t> CompiledMethod::GetVmapTable() const {
- return GetArray(vmap_table_);
-}
-
-inline ArrayRef<const uint8_t> CompiledMethod::GetCFIInfo() const {
- return GetArray(cfi_info_);
-}
-
-inline ArrayRef<const linker::LinkerPatch> CompiledMethod::GetPatches() const {
- return GetArray(patches_);
-}
-
-} // namespace art
-
-#endif // ART_COMPILER_COMPILED_METHOD_INL_H_
diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc
deleted file mode 100644
index 03b87ef..0000000
--- a/compiler/compiled_method.cc
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2011 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 "compiled_method.h"
-
-#include "driver/compiled_method_storage.h"
-#include "utils/swap_space.h"
-
-namespace art {
-
-CompiledCode::CompiledCode(CompiledMethodStorage* storage,
- InstructionSet instruction_set,
- const ArrayRef<const uint8_t>& quick_code)
- : storage_(storage),
- quick_code_(storage->DeduplicateCode(quick_code)),
- packed_fields_(InstructionSetField::Encode(instruction_set)) {
-}
-
-CompiledCode::~CompiledCode() {
- GetStorage()->ReleaseCode(quick_code_);
-}
-
-bool CompiledCode::operator==(const CompiledCode& rhs) const {
- if (quick_code_ != nullptr) {
- if (rhs.quick_code_ == nullptr) {
- return false;
- } else if (quick_code_->size() != rhs.quick_code_->size()) {
- return false;
- } else {
- return std::equal(quick_code_->begin(), quick_code_->end(), rhs.quick_code_->begin());
- }
- }
- return (rhs.quick_code_ == nullptr);
-}
-
-size_t CompiledCode::AlignCode(size_t offset) const {
- return AlignCode(offset, GetInstructionSet());
-}
-
-size_t CompiledCode::AlignCode(size_t offset, InstructionSet instruction_set) {
- return RoundUp(offset, GetInstructionSetAlignment(instruction_set));
-}
-
-size_t CompiledCode::CodeDelta() const {
- return CodeDelta(GetInstructionSet());
-}
-
-size_t CompiledCode::CodeDelta(InstructionSet instruction_set) {
- switch (instruction_set) {
- case InstructionSet::kArm:
- case InstructionSet::kArm64:
- case InstructionSet::kX86:
- case InstructionSet::kX86_64:
- return 0;
- case InstructionSet::kThumb2: {
- // +1 to set the low-order bit so a BLX will switch to Thumb mode
- return 1;
- }
- default:
- LOG(FATAL) << "Unknown InstructionSet: " << instruction_set;
- UNREACHABLE();
- }
-}
-
-const void* CompiledCode::CodePointer(const void* code_pointer, InstructionSet instruction_set) {
- switch (instruction_set) {
- case InstructionSet::kArm:
- case InstructionSet::kArm64:
- case InstructionSet::kX86:
- case InstructionSet::kX86_64:
- return code_pointer;
- case InstructionSet::kThumb2: {
- uintptr_t address = reinterpret_cast<uintptr_t>(code_pointer);
- // Set the low-order bit so a BLX will switch to Thumb mode
- address |= 0x1;
- return reinterpret_cast<const void*>(address);
- }
- default:
- LOG(FATAL) << "Unknown InstructionSet: " << instruction_set;
- UNREACHABLE();
- }
-}
-
-CompiledMethod::CompiledMethod(CompiledMethodStorage* storage,
- InstructionSet instruction_set,
- const ArrayRef<const uint8_t>& quick_code,
- const ArrayRef<const uint8_t>& vmap_table,
- const ArrayRef<const uint8_t>& cfi_info,
- const ArrayRef<const linker::LinkerPatch>& patches)
- : CompiledCode(storage, instruction_set, quick_code),
- vmap_table_(storage->DeduplicateVMapTable(vmap_table)),
- cfi_info_(storage->DeduplicateCFIInfo(cfi_info)),
- patches_(storage->DeduplicateLinkerPatches(patches)) {
-}
-
-CompiledMethod* CompiledMethod::SwapAllocCompiledMethod(
- CompiledMethodStorage* storage,
- InstructionSet instruction_set,
- const ArrayRef<const uint8_t>& quick_code,
- const ArrayRef<const uint8_t>& vmap_table,
- const ArrayRef<const uint8_t>& cfi_info,
- const ArrayRef<const linker::LinkerPatch>& patches) {
- SwapAllocator<CompiledMethod> alloc(storage->GetSwapSpaceAllocator());
- CompiledMethod* ret = alloc.allocate(1);
- alloc.construct(ret,
- storage,
- instruction_set,
- quick_code,
- vmap_table,
- cfi_info, patches);
- return ret;
-}
-
-void CompiledMethod::ReleaseSwapAllocatedCompiledMethod(CompiledMethodStorage* storage,
- CompiledMethod* m) {
- SwapAllocator<CompiledMethod> alloc(storage->GetSwapSpaceAllocator());
- alloc.destroy(m);
- alloc.deallocate(m, 1);
-}
-
-CompiledMethod::~CompiledMethod() {
- CompiledMethodStorage* storage = GetStorage();
- storage->ReleaseLinkerPatches(patches_);
- storage->ReleaseCFIInfo(cfi_info_);
- storage->ReleaseVMapTable(vmap_table_);
-}
-
-} // namespace art
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
deleted file mode 100644
index e92777f..0000000
--- a/compiler/compiled_method.h
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_COMPILED_METHOD_H_
-#define ART_COMPILER_COMPILED_METHOD_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "arch/instruction_set.h"
-#include "base/bit_field.h"
-#include "base/bit_utils.h"
-
-namespace art {
-
-template <typename T> class ArrayRef;
-class CompiledMethodStorage;
-template<typename T> class LengthPrefixedArray;
-
-namespace linker {
-class LinkerPatch;
-} // namespace linker
-
-class CompiledCode {
- public:
- // For Quick to supply an code blob
- CompiledCode(CompiledMethodStorage* storage,
- InstructionSet instruction_set,
- const ArrayRef<const uint8_t>& quick_code);
-
- virtual ~CompiledCode();
-
- InstructionSet GetInstructionSet() const {
- return GetPackedField<InstructionSetField>();
- }
-
- ArrayRef<const uint8_t> GetQuickCode() const;
-
- bool operator==(const CompiledCode& rhs) const;
-
- // To align an offset from a page-aligned value to make it suitable
- // for code storage. For example on ARM, to ensure that PC relative
- // valu computations work out as expected.
- size_t AlignCode(size_t offset) const;
- static size_t AlignCode(size_t offset, InstructionSet instruction_set);
-
- // returns the difference between the code address and a usable PC.
- // mainly to cope with kThumb2 where the lower bit must be set.
- size_t CodeDelta() const;
- static size_t CodeDelta(InstructionSet instruction_set);
-
- // Returns a pointer suitable for invoking the code at the argument
- // code_pointer address. Mainly to cope with kThumb2 where the
- // lower bit must be set to indicate Thumb mode.
- static const void* CodePointer(const void* code_pointer, InstructionSet instruction_set);
-
- protected:
- static constexpr size_t kInstructionSetFieldSize =
- MinimumBitsToStore(static_cast<size_t>(InstructionSet::kLast));
- static constexpr size_t kNumberOfCompiledCodePackedBits = kInstructionSetFieldSize;
- static constexpr size_t kMaxNumberOfPackedBits = sizeof(uint32_t) * kBitsPerByte;
-
- template <typename T>
- static ArrayRef<const T> GetArray(const LengthPrefixedArray<T>* array);
-
- CompiledMethodStorage* GetStorage() {
- return storage_;
- }
-
- template <typename BitFieldType>
- typename BitFieldType::value_type GetPackedField() const {
- return BitFieldType::Decode(packed_fields_);
- }
-
- template <typename BitFieldType>
- void SetPackedField(typename BitFieldType::value_type value) {
- DCHECK(IsUint<BitFieldType::size>(static_cast<uintptr_t>(value)));
- packed_fields_ = BitFieldType::Update(value, packed_fields_);
- }
-
- private:
- using InstructionSetField = BitField<InstructionSet, 0u, kInstructionSetFieldSize>;
-
- CompiledMethodStorage* const storage_;
-
- // Used to store the compiled code.
- const LengthPrefixedArray<uint8_t>* const quick_code_;
-
- uint32_t packed_fields_;
-};
-
-class CompiledMethod final : public CompiledCode {
- public:
- // Constructs a CompiledMethod.
- // Note: Consider using the static allocation methods below that will allocate the CompiledMethod
- // in the swap space.
- CompiledMethod(CompiledMethodStorage* storage,
- InstructionSet instruction_set,
- const ArrayRef<const uint8_t>& quick_code,
- const ArrayRef<const uint8_t>& vmap_table,
- const ArrayRef<const uint8_t>& cfi_info,
- const ArrayRef<const linker::LinkerPatch>& patches);
-
- virtual ~CompiledMethod();
-
- static CompiledMethod* SwapAllocCompiledMethod(
- CompiledMethodStorage* storage,
- InstructionSet instruction_set,
- const ArrayRef<const uint8_t>& quick_code,
- const ArrayRef<const uint8_t>& vmap_table,
- const ArrayRef<const uint8_t>& cfi_info,
- const ArrayRef<const linker::LinkerPatch>& patches);
-
- static void ReleaseSwapAllocatedCompiledMethod(CompiledMethodStorage* storage, CompiledMethod* m);
-
- bool IsIntrinsic() const {
- return GetPackedField<IsIntrinsicField>();
- }
-
- // Marks the compiled method as being generated using an intrinsic codegen.
- // Such methods have no relationships to their code items.
- // This affects debug information generated at link time.
- void MarkAsIntrinsic() {
- DCHECK(!IsIntrinsic());
- SetPackedField<IsIntrinsicField>(/* value= */ true);
- }
-
- ArrayRef<const uint8_t> GetVmapTable() const;
-
- ArrayRef<const uint8_t> GetCFIInfo() const;
-
- ArrayRef<const linker::LinkerPatch> GetPatches() const;
-
- private:
- static constexpr size_t kIsIntrinsicLsb = kNumberOfCompiledCodePackedBits;
- static constexpr size_t kIsIntrinsicSize = 1u;
- static constexpr size_t kNumberOfCompiledMethodPackedBits = kIsIntrinsicLsb + kIsIntrinsicSize;
- static_assert(kNumberOfCompiledMethodPackedBits <= CompiledCode::kMaxNumberOfPackedBits,
- "Too many packed fields.");
-
- using IsIntrinsicField = BitField<bool, kIsIntrinsicLsb, kIsIntrinsicSize>;
-
- // For quick code, holds code infos which contain stack maps, inline information, and etc.
- const LengthPrefixedArray<uint8_t>* const vmap_table_;
- // For quick code, a FDE entry for the debug_frame section.
- const LengthPrefixedArray<uint8_t>* const cfi_info_;
- // For quick code, linker patches needed by the method.
- const LengthPrefixedArray<linker::LinkerPatch>* const patches_;
-};
-
-} // namespace art
-
-#endif // ART_COMPILER_COMPILED_METHOD_H_
diff --git a/compiler/compiler.cc b/compiler/compiler.cc
index 98d7339..e2587c1 100644
--- a/compiler/compiler.cc
+++ b/compiler/compiler.cc
@@ -25,10 +25,10 @@
#include "oat.h"
#include "optimizing/optimizing_compiler.h"
-namespace art {
+namespace art HIDDEN {
Compiler* Compiler::Create(const CompilerOptions& compiler_options,
- CompiledMethodStorage* storage,
+ CompiledCodeStorage* storage,
Compiler::Kind kind) {
// Check that oat version when runtime was compiled matches the oat version of the compiler.
constexpr std::array<uint8_t, 4> compiler_oat_version = OatHeader::kOatVersion;
diff --git a/compiler/compiler.h b/compiler/compiler.h
index afa0dba..ce785bb 100644
--- a/compiler/compiler.h
+++ b/compiler/compiler.h
@@ -17,12 +17,13 @@
#ifndef ART_COMPILER_COMPILER_H_
#define ART_COMPILER_COMPILER_H_
+#include "base/macros.h"
#include "base/mutex.h"
#include "base/os.h"
#include "compilation_kind.h"
#include "dex/invoke_type.h"
-namespace art {
+namespace art HIDDEN {
namespace dex {
struct CodeItem;
@@ -38,8 +39,8 @@
} // namespace mirror
class ArtMethod;
+class CompiledCodeStorage;
class CompiledMethod;
-class CompiledMethodStorage;
class CompilerOptions;
class DexFile;
template<class T> class Handle;
@@ -52,9 +53,9 @@
kOptimizing
};
- static Compiler* Create(const CompilerOptions& compiler_options,
- CompiledMethodStorage* storage,
- Kind kind);
+ EXPORT static Compiler* Create(const CompilerOptions& compiler_options,
+ CompiledCodeStorage* storage,
+ Kind kind);
virtual bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file) const = 0;
@@ -99,7 +100,7 @@
protected:
Compiler(const CompilerOptions& compiler_options,
- CompiledMethodStorage* storage,
+ CompiledCodeStorage* storage,
uint64_t warning) :
compiler_options_(compiler_options),
storage_(storage),
@@ -110,13 +111,13 @@
return compiler_options_;
}
- CompiledMethodStorage* GetCompiledMethodStorage() const {
+ CompiledCodeStorage* GetCompiledCodeStorage() const {
return storage_;
}
private:
const CompilerOptions& compiler_options_;
- CompiledMethodStorage* const storage_;
+ CompiledCodeStorage* const storage_;
const uint64_t maximum_compilation_time_before_warning_;
DISALLOW_COPY_AND_ASSIGN(Compiler);
diff --git a/compiler/compiler_reflection_test.cc b/compiler/compiler_reflection_test.cc
new file mode 100644
index 0000000..f3c07db
--- /dev/null
+++ b/compiler/compiler_reflection_test.cc
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 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 "reflection.h"
+
+#include "base/macros.h"
+#include "class_linker.h"
+#include "common_compiler_test.h"
+#include "handle_scope-inl.h"
+#include "jni/jni_internal.h"
+#include "mirror/class.h"
+#include "mirror/class_loader.h"
+
+namespace art HIDDEN {
+
+class CompilerReflectionTest : public CommonCompilerTest {};
+
+TEST_F(CompilerReflectionTest, StaticMainMethod) {
+ ScopedObjectAccess soa(Thread::Current());
+ jobject jclass_loader = LoadDex("Main");
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
+
+ ObjPtr<mirror::Class> klass = class_linker_->FindClass(soa.Self(), "LMain;", class_loader);
+ ASSERT_TRUE(klass != nullptr);
+
+ ArtMethod* method = klass->FindClassMethod("main",
+ "([Ljava/lang/String;)V",
+ kRuntimePointerSize);
+ ASSERT_TRUE(method != nullptr);
+ ASSERT_TRUE(method->IsStatic());
+
+ CompileMethod(method);
+
+ // Start runtime.
+ bool started = runtime_->Start();
+ CHECK(started);
+ soa.Self()->TransitionFromSuspendedToRunnable();
+
+ jvalue args[1];
+ args[0].l = nullptr;
+ InvokeWithJValues(soa, nullptr, jni::EncodeArtMethod(method), args);
+}
+
+} // namespace art
diff --git a/compiler/debug/debug_info.h b/compiler/debug/debug_info.h
index 04c6991..4027f11 100644
--- a/compiler/debug/debug_info.h
+++ b/compiler/debug/debug_info.h
@@ -20,9 +20,10 @@
#include <map>
#include "base/array_ref.h"
+#include "base/macros.h"
#include "method_debug_info.h"
-namespace art {
+namespace art HIDDEN {
class DexFile;
namespace debug {
diff --git a/compiler/debug/dwarf/dwarf_test.cc b/compiler/debug/dwarf/dwarf_test.cc
index 8897e45..14c92b2 100644
--- a/compiler/debug/dwarf/dwarf_test.cc
+++ b/compiler/debug/dwarf/dwarf_test.cc
@@ -23,7 +23,7 @@
#include "dwarf/headers.h"
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
namespace dwarf {
// Run the tests only on host since we need objdump.
diff --git a/compiler/debug/dwarf/dwarf_test.h b/compiler/debug/dwarf/dwarf_test.h
index bad986a..1a0a798 100644
--- a/compiler/debug/dwarf/dwarf_test.h
+++ b/compiler/debug/dwarf/dwarf_test.h
@@ -26,6 +26,7 @@
#include <set>
#include <string>
+#include "base/macros.h"
#include "base/os.h"
#include "base/unix_file/fd_file.h"
#include "common_compiler_test.h"
@@ -33,7 +34,7 @@
#include "gtest/gtest.h"
#include "stream/file_output_stream.h"
-namespace art {
+namespace art HIDDEN {
namespace dwarf {
#define DW_CHECK(substring) Check(substring, false, __FILE__, __LINE__)
diff --git a/compiler/debug/elf_compilation_unit.h b/compiler/debug/elf_compilation_unit.h
index b1d89eb..1d7523c 100644
--- a/compiler/debug/elf_compilation_unit.h
+++ b/compiler/debug/elf_compilation_unit.h
@@ -19,9 +19,10 @@
#include <vector>
+#include "base/macros.h"
#include "debug/method_debug_info.h"
-namespace art {
+namespace art HIDDEN {
namespace debug {
struct ElfCompilationUnit {
diff --git a/compiler/debug/elf_debug_frame_writer.h b/compiler/debug/elf_debug_frame_writer.h
index 094e887..6b72262 100644
--- a/compiler/debug/elf_debug_frame_writer.h
+++ b/compiler/debug/elf_debug_frame_writer.h
@@ -20,13 +20,14 @@
#include <vector>
#include "arch/instruction_set.h"
+#include "base/macros.h"
#include "debug/method_debug_info.h"
#include "dwarf/debug_frame_opcode_writer.h"
#include "dwarf/dwarf_constants.h"
#include "dwarf/headers.h"
#include "elf/elf_builder.h"
-namespace art {
+namespace art HIDDEN {
namespace debug {
static constexpr bool kWriteDebugFrameHdr = false;
@@ -88,6 +89,10 @@
WriteCIE(is64bit, return_reg, opcodes, buffer);
return;
}
+ case InstructionSet::kRiscv64: {
+ UNIMPLEMENTED(FATAL);
+ return;
+ }
case InstructionSet::kX86: {
// FIXME: Add fp registers once libunwind adds support for them. Bug: 20491296
constexpr bool generate_opcodes_for_x86_fp = false;
diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h
index 986c7e8..9915a24 100644
--- a/compiler/debug/elf_debug_info_writer.h
+++ b/compiler/debug/elf_debug_info_writer.h
@@ -22,6 +22,7 @@
#include <vector>
#include "art_field-inl.h"
+#include "base/macros.h"
#include "debug/elf_compilation_unit.h"
#include "debug/elf_debug_loc_writer.h"
#include "debug/method_debug_info.h"
@@ -32,14 +33,14 @@
#include "dwarf/debug_info_entry_writer.h"
#include "elf/elf_builder.h"
#include "heap_poisoning.h"
-#include "linear_alloc.h"
+#include "linear_alloc-inl.h"
#include "mirror/array.h"
#include "mirror/class-inl.h"
#include "mirror/class.h"
#include "oat_file.h"
#include "obj_ptr-inl.h"
-namespace art {
+namespace art HIDDEN {
namespace debug {
static std::vector<const char*> GetParamNames(const MethodDebugInfo* mi) {
@@ -478,7 +479,9 @@
if (methods_ptr == nullptr) {
// Some types might have no methods. Allocate empty array instead.
LinearAlloc* allocator = Runtime::Current()->GetLinearAlloc();
- void* storage = allocator->Alloc(Thread::Current(), sizeof(LengthPrefixedArray<ArtMethod>));
+ void* storage = allocator->Alloc(Thread::Current(),
+ sizeof(LengthPrefixedArray<ArtMethod>),
+ LinearAllocKind::kNoGCRoots);
methods_ptr = new (storage) LengthPrefixedArray<ArtMethod>(0);
type->SetMethodsPtr(methods_ptr, 0, 0);
DCHECK(type->GetMethodsPtr() != nullptr);
diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h
index 8d62747..4896bc1 100644
--- a/compiler/debug/elf_debug_line_writer.h
+++ b/compiler/debug/elf_debug_line_writer.h
@@ -20,6 +20,7 @@
#include <unordered_set>
#include <vector>
+#include "base/macros.h"
#include "debug/elf_compilation_unit.h"
#include "debug/src_map_elem.h"
#include "dex/dex_file-inl.h"
@@ -29,7 +30,7 @@
#include "oat_file.h"
#include "stack_map.h"
-namespace art {
+namespace art HIDDEN {
namespace debug {
using PositionInfos = std::vector<DexFile::PositionInfo>;
@@ -73,6 +74,7 @@
code_factor_bits_ = 2; // 32-bit instructions
break;
case InstructionSet::kNone:
+ case InstructionSet::kRiscv64:
case InstructionSet::kX86:
case InstructionSet::kX86_64:
break;
diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h
index 37ab948..8cf476e 100644
--- a/compiler/debug/elf_debug_loc_writer.h
+++ b/compiler/debug/elf_debug_loc_writer.h
@@ -21,13 +21,13 @@
#include <map>
#include "arch/instruction_set.h"
-#include "compiled_method.h"
+#include "base/macros.h"
#include "debug/method_debug_info.h"
#include "dwarf/debug_info_entry_writer.h"
#include "dwarf/register.h"
#include "stack_map.h"
-namespace art {
+namespace art HIDDEN {
namespace debug {
using Reg = dwarf::Reg;
@@ -38,6 +38,8 @@
return Reg::ArmCore(machine_reg);
case InstructionSet::kArm64:
return Reg::Arm64Core(machine_reg);
+ case InstructionSet::kRiscv64:
+ return Reg::Riscv64Core(machine_reg);
case InstructionSet::kX86:
return Reg::X86Core(machine_reg);
case InstructionSet::kX86_64:
@@ -55,6 +57,8 @@
return Reg::ArmFp(machine_reg);
case InstructionSet::kArm64:
return Reg::Arm64Fp(machine_reg);
+ case InstructionSet::kRiscv64:
+ return Reg::Riscv64Fp(machine_reg);
case InstructionSet::kX86:
return Reg::X86Fp(machine_reg);
case InstructionSet::kX86_64:
diff --git a/compiler/debug/elf_debug_writer.cc b/compiler/debug/elf_debug_writer.cc
index 765a81d..8f64d73 100644
--- a/compiler/debug/elf_debug_writer.cc
+++ b/compiler/debug/elf_debug_writer.cc
@@ -38,7 +38,7 @@
#include "oat.h"
#include "stream/vector_output_stream.h"
-namespace art {
+namespace art HIDDEN {
namespace debug {
using ElfRuntimeTypes = std::conditional<sizeof(void*) == 4, ElfTypes32, ElfTypes64>::type;
@@ -208,7 +208,8 @@
using Reader = ElfDebugReader<ElfTypes>;
Reader reader(buffer);
reader.VisitFunctionSymbols([&](Elf_Sym sym, const char*) {
- DCHECK_EQ(sym.st_value, method_info.code_address + CompiledMethod::CodeDelta(isa));
+ DCHECK_EQ(sym.st_value,
+ method_info.code_address + GetInstructionSetEntryPointAdjustment(isa));
DCHECK_EQ(sym.st_size, method_info.code_size);
num_syms++;
});
diff --git a/compiler/debug/elf_debug_writer.h b/compiler/debug/elf_debug_writer.h
index 1ce3c6f..72b028c 100644
--- a/compiler/debug/elf_debug_writer.h
+++ b/compiler/debug/elf_debug_writer.h
@@ -27,7 +27,7 @@
#include "dwarf/dwarf_constants.h"
#include "elf/elf_builder.h"
-namespace art {
+namespace art HIDDEN {
class OatHeader;
struct JITCodeEntry;
namespace mirror {
@@ -37,11 +37,11 @@
struct MethodDebugInfo;
template <typename ElfTypes>
-void WriteDebugInfo(
+EXPORT void WriteDebugInfo(
ElfBuilder<ElfTypes>* builder,
const DebugInfo& debug_info);
-std::vector<uint8_t> MakeMiniDebugInfo(
+EXPORT std::vector<uint8_t> MakeMiniDebugInfo(
InstructionSet isa,
const InstructionSetFeatures* features,
uint64_t text_section_address,
diff --git a/compiler/debug/elf_symtab_writer.h b/compiler/debug/elf_symtab_writer.h
index 410f704..fcd6696 100644
--- a/compiler/debug/elf_symtab_writer.h
+++ b/compiler/debug/elf_symtab_writer.h
@@ -21,6 +21,7 @@
#include <unordered_set>
#include <unordered_map>
+#include "base/macros.h"
#include "base/utils.h"
#include "debug/debug_info.h"
#include "debug/method_debug_info.h"
@@ -29,7 +30,7 @@
#include "dex/dex_file-inl.h"
#include "elf/elf_builder.h"
-namespace art {
+namespace art HIDDEN {
namespace debug {
// The ARM specification defines three special mapping symbols
@@ -153,7 +154,7 @@
uint64_t address = info.code_address;
address += info.is_code_address_text_relative ? text->GetAddress() : 0;
// Add in code delta, e.g., thumb bit 0 for Thumb2 code.
- address += CompiledMethod::CodeDelta(info.isa);
+ address += GetInstructionSetEntryPointAdjustment(info.isa);
symtab->Add(name_offset, text, address, info.code_size, STB_GLOBAL, STT_FUNC);
}
// Add symbols for dex files.
diff --git a/compiler/debug/method_debug_info.h b/compiler/debug/method_debug_info.h
index 152db6e..b83c6e2 100644
--- a/compiler/debug/method_debug_info.h
+++ b/compiler/debug/method_debug_info.h
@@ -21,9 +21,10 @@
#include "arch/instruction_set.h"
#include "base/array_ref.h"
+#include "base/macros.h"
#include "dex/dex_file.h"
-namespace art {
+namespace art HIDDEN {
namespace debug {
struct MethodDebugInfo {
diff --git a/compiler/debug/src_map_elem.h b/compiler/debug/src_map_elem.h
index 5286b8c..646a1f0 100644
--- a/compiler/debug/src_map_elem.h
+++ b/compiler/debug/src_map_elem.h
@@ -19,7 +19,9 @@
#include <stdint.h>
-namespace art {
+#include "base/macros.h"
+
+namespace art HIDDEN {
class SrcMapElem {
public:
diff --git a/compiler/debug/src_map_elem_test.cc b/compiler/debug/src_map_elem_test.cc
index ceaa53f..bdbafd5 100644
--- a/compiler/debug/src_map_elem_test.cc
+++ b/compiler/debug/src_map_elem_test.cc
@@ -20,7 +20,7 @@
#include "base/macros.h"
-namespace art {
+namespace art HIDDEN {
namespace debug {
TEST(SrcMapElem, Operators) {
diff --git a/compiler/dex/inline_method_analyser.cc b/compiler/dex/inline_method_analyser.cc
index 3201965..381db3d 100644
--- a/compiler/dex/inline_method_analyser.cc
+++ b/compiler/dex/inline_method_analyser.cc
@@ -33,7 +33,7 @@
* only to allow the debugger to check whether a method has been inlined.
*/
-namespace art {
+namespace art HIDDEN {
namespace { // anonymous namespace
diff --git a/compiler/dex/inline_method_analyser.h b/compiler/dex/inline_method_analyser.h
index e1d652a..99d07c6 100644
--- a/compiler/dex/inline_method_analyser.h
+++ b/compiler/dex/inline_method_analyser.h
@@ -28,7 +28,7 @@
* only to allow the debugger to check whether a method has been inlined.
*/
-namespace art {
+namespace art HIDDEN {
class CodeItemDataAccessor;
diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc
deleted file mode 100644
index b819d0e..0000000
--- a/compiler/dex/verification_results.cc
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2013 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 "verification_results.h"
-
-#include <android-base/logging.h>
-
-#include "base/mutex-inl.h"
-#include "base/stl_util.h"
-#include "runtime.h"
-#include "thread-current-inl.h"
-#include "thread.h"
-
-namespace art {
-
-VerificationResults::VerificationResults()
- : uncompilable_methods_lock_("compiler uncompilable methods lock"),
- rejected_classes_lock_("compiler rejected classes lock") {}
-
-// Non-inline version of the destructor, as it does some implicit work not worth
-// inlining.
-VerificationResults::~VerificationResults() {}
-
-void VerificationResults::AddRejectedClass(ClassReference ref) {
- {
- WriterMutexLock mu(Thread::Current(), rejected_classes_lock_);
- rejected_classes_.insert(ref);
- }
- DCHECK(IsClassRejected(ref));
-}
-
-bool VerificationResults::IsClassRejected(ClassReference ref) const {
- ReaderMutexLock mu(Thread::Current(), rejected_classes_lock_);
- return rejected_classes_.find(ref) != rejected_classes_.end();
-}
-
-void VerificationResults::AddUncompilableMethod(MethodReference ref) {
- {
- WriterMutexLock mu(Thread::Current(), uncompilable_methods_lock_);
- uncompilable_methods_.insert(ref);
- }
- DCHECK(IsUncompilableMethod(ref));
-}
-
-bool VerificationResults::IsUncompilableMethod(MethodReference ref) const {
- ReaderMutexLock mu(Thread::Current(), uncompilable_methods_lock_);
- return uncompilable_methods_.find(ref) != uncompilable_methods_.end();
-}
-
-
-} // namespace art
diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h
deleted file mode 100644
index b294ed3..0000000
--- a/compiler/dex/verification_results.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_DEX_VERIFICATION_RESULTS_H_
-#define ART_COMPILER_DEX_VERIFICATION_RESULTS_H_
-
-#include <set>
-
-#include "base/macros.h"
-#include "base/mutex.h"
-#include "dex/class_reference.h"
-#include "dex/method_reference.h"
-
-namespace art {
-
-namespace verifier {
-class VerifierDepsTest;
-} // namespace verifier
-
-// Used by CompilerCallbacks to track verification information from the Runtime.
-class VerificationResults {
- public:
- VerificationResults();
- ~VerificationResults();
-
- void AddRejectedClass(ClassReference ref) REQUIRES(!rejected_classes_lock_);
- bool IsClassRejected(ClassReference ref) const REQUIRES(!rejected_classes_lock_);
-
- void AddUncompilableMethod(MethodReference ref) REQUIRES(!uncompilable_methods_lock_);
- bool IsUncompilableMethod(MethodReference ref) const REQUIRES(!uncompilable_methods_lock_);
-
- private:
- // TODO: External locking during CompilerDriver::PreCompile(), no locking during compilation.
- mutable ReaderWriterMutex uncompilable_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- std::set<MethodReference> uncompilable_methods_ GUARDED_BY(uncompilable_methods_lock_);
-
- // Rejected classes.
- // TODO: External locking during CompilerDriver::PreCompile(), no locking during compilation.
- mutable ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- std::set<ClassReference> rejected_classes_ GUARDED_BY(rejected_classes_lock_);
-
- friend class verifier::VerifierDepsTest;
-};
-
-} // namespace art
-
-#endif // ART_COMPILER_DEX_VERIFICATION_RESULTS_H_
diff --git a/compiler/driver/compiled_code_storage.h b/compiler/driver/compiled_code_storage.h
new file mode 100644
index 0000000..cef7398
--- /dev/null
+++ b/compiler/driver/compiled_code_storage.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_DRIVER_COMPILED_CODE_STORAGE_H_
+#define ART_COMPILER_DRIVER_COMPILED_CODE_STORAGE_H_
+
+#include <string>
+
+#include "base/array_ref.h"
+#include "base/macros.h"
+
+namespace art HIDDEN {
+
+namespace linker {
+class LinkerPatch;
+} // namespace linker
+
+class CompiledMethod;
+enum class InstructionSet;
+
+// Interface for storing AOT-compiled artifacts.
+// These artifacts include compiled method code and related stack maps and
+// linker patches as well as the compiled thunk code required for some kinds
+// of linker patches.
+//
+// This interface is used for passing AOT-compiled code and metadata produced
+// by the `libart-compiler` to `dex2oat`. The `CompiledMethod` created by
+// `dex2oat` is completely opaque to the `libart-compiler`.
+class CompiledCodeStorage {
+ public:
+ virtual CompiledMethod* CreateCompiledMethod(InstructionSet instruction_set,
+ ArrayRef<const uint8_t> code,
+ ArrayRef<const uint8_t> stack_map,
+ ArrayRef<const uint8_t> cfi,
+ ArrayRef<const linker::LinkerPatch> patches,
+ bool is_intrinsic) = 0;
+
+ // TODO: Rewrite the interface for passing thunks to the `dex2oat` to reduce
+ // locking. The `OptimizingCompiler` is currently calling `GetThunkCode()`
+ // and locking a mutex there for every `LinkerPatch` that needs a thunk to
+ // check whether we need to compile it. Using a thunk compiler interface,
+ // we could drive this from the `dex2oat` side and lock the mutex at most
+ // once per `CreateCompiledMethod()` for any number of patches.
+ virtual ArrayRef<const uint8_t> GetThunkCode(const linker::LinkerPatch& patch,
+ /*out*/ std::string* debug_name = nullptr) = 0;
+ virtual void SetThunkCode(const linker::LinkerPatch& patch,
+ ArrayRef<const uint8_t> code,
+ const std::string& debug_name) = 0;
+
+ protected:
+ CompiledCodeStorage() {}
+ ~CompiledCodeStorage() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CompiledCodeStorage);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_DRIVER_COMPILED_CODE_STORAGE_H_
diff --git a/compiler/driver/compiled_method_storage.cc b/compiler/driver/compiled_method_storage.cc
deleted file mode 100644
index 4857ec0..0000000
--- a/compiler/driver/compiled_method_storage.cc
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright (C) 2015 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 <algorithm>
-#include <ostream>
-
-#include "compiled_method_storage.h"
-
-#include <android-base/logging.h>
-
-#include "base/data_hash.h"
-#include "base/utils.h"
-#include "compiled_method.h"
-#include "linker/linker_patch.h"
-#include "thread-current-inl.h"
-#include "utils/dedupe_set-inl.h"
-#include "utils/swap_space.h"
-
-namespace art {
-
-namespace { // anonymous namespace
-
-template <typename T>
-const LengthPrefixedArray<T>* CopyArray(SwapSpace* swap_space, const ArrayRef<const T>& array) {
- DCHECK(!array.empty());
- SwapAllocator<uint8_t> allocator(swap_space);
- void* storage = allocator.allocate(LengthPrefixedArray<T>::ComputeSize(array.size()));
- LengthPrefixedArray<T>* array_copy = new(storage) LengthPrefixedArray<T>(array.size());
- std::copy(array.begin(), array.end(), array_copy->begin());
- return array_copy;
-}
-
-template <typename T>
-void ReleaseArray(SwapSpace* swap_space, const LengthPrefixedArray<T>* array) {
- SwapAllocator<uint8_t> allocator(swap_space);
- size_t size = LengthPrefixedArray<T>::ComputeSize(array->size());
- array->~LengthPrefixedArray<T>();
- allocator.deallocate(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(array)), size);
-}
-
-} // anonymous namespace
-
-template <typename T, typename DedupeSetType>
-inline const LengthPrefixedArray<T>* CompiledMethodStorage::AllocateOrDeduplicateArray(
- const ArrayRef<const T>& data,
- DedupeSetType* dedupe_set) {
- if (data.empty()) {
- return nullptr;
- } else if (!DedupeEnabled()) {
- return CopyArray(swap_space_.get(), data);
- } else {
- return dedupe_set->Add(Thread::Current(), data);
- }
-}
-
-template <typename T>
-inline void CompiledMethodStorage::ReleaseArrayIfNotDeduplicated(
- const LengthPrefixedArray<T>* array) {
- if (array != nullptr && !DedupeEnabled()) {
- ReleaseArray(swap_space_.get(), array);
- }
-}
-
-template <typename ContentType>
-class CompiledMethodStorage::DedupeHashFunc {
- private:
- static constexpr bool kUseMurmur3Hash = true;
-
- public:
- size_t operator()(const ArrayRef<ContentType>& array) const {
- return DataHash()(array);
- }
-};
-
-template <typename T>
-class CompiledMethodStorage::LengthPrefixedArrayAlloc {
- public:
- explicit LengthPrefixedArrayAlloc(SwapSpace* swap_space)
- : swap_space_(swap_space) {
- }
-
- const LengthPrefixedArray<T>* Copy(const ArrayRef<const T>& array) {
- return CopyArray(swap_space_, array);
- }
-
- void Destroy(const LengthPrefixedArray<T>* array) {
- ReleaseArray(swap_space_, array);
- }
-
- private:
- SwapSpace* const swap_space_;
-};
-
-class CompiledMethodStorage::ThunkMapKey {
- public:
- ThunkMapKey(linker::LinkerPatch::Type type, uint32_t custom_value1, uint32_t custom_value2)
- : type_(type), custom_value1_(custom_value1), custom_value2_(custom_value2) {}
-
- bool operator<(const ThunkMapKey& other) const {
- if (custom_value1_ != other.custom_value1_) {
- return custom_value1_ < other.custom_value1_;
- }
- if (custom_value2_ != other.custom_value2_) {
- return custom_value2_ < other.custom_value2_;
- }
- return type_ < other.type_;
- }
-
- private:
- linker::LinkerPatch::Type type_;
- uint32_t custom_value1_;
- uint32_t custom_value2_;
-};
-
-class CompiledMethodStorage::ThunkMapValue {
- public:
- ThunkMapValue(std::vector<uint8_t, SwapAllocator<uint8_t>>&& code,
- const std::string& debug_name)
- : code_(std::move(code)), debug_name_(debug_name) {}
-
- ArrayRef<const uint8_t> GetCode() const {
- return ArrayRef<const uint8_t>(code_);
- }
-
- const std::string& GetDebugName() const {
- return debug_name_;
- }
-
- private:
- std::vector<uint8_t, SwapAllocator<uint8_t>> code_;
- std::string debug_name_;
-};
-
-CompiledMethodStorage::CompiledMethodStorage(int swap_fd)
- : swap_space_(swap_fd == -1 ? nullptr : new SwapSpace(swap_fd, 10 * MB)),
- dedupe_enabled_(true),
- dedupe_code_("dedupe code", LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
- dedupe_vmap_table_("dedupe vmap table",
- LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
- dedupe_cfi_info_("dedupe cfi info", LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
- dedupe_linker_patches_("dedupe cfi info",
- LengthPrefixedArrayAlloc<linker::LinkerPatch>(swap_space_.get())),
- thunk_map_lock_("thunk_map_lock"),
- thunk_map_(std::less<ThunkMapKey>(), SwapAllocator<ThunkMapValueType>(swap_space_.get())) {
-}
-
-CompiledMethodStorage::~CompiledMethodStorage() {
- // All done by member destructors.
-}
-
-void CompiledMethodStorage::DumpMemoryUsage(std::ostream& os, bool extended) const {
- if (swap_space_.get() != nullptr) {
- const size_t swap_size = swap_space_->GetSize();
- os << " swap=" << PrettySize(swap_size) << " (" << swap_size << "B)";
- }
- if (extended) {
- Thread* self = Thread::Current();
- os << "\nCode dedupe: " << dedupe_code_.DumpStats(self);
- os << "\nVmap table dedupe: " << dedupe_vmap_table_.DumpStats(self);
- os << "\nCFI info dedupe: " << dedupe_cfi_info_.DumpStats(self);
- }
-}
-
-const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateCode(
- const ArrayRef<const uint8_t>& code) {
- return AllocateOrDeduplicateArray(code, &dedupe_code_);
-}
-
-void CompiledMethodStorage::ReleaseCode(const LengthPrefixedArray<uint8_t>* code) {
- ReleaseArrayIfNotDeduplicated(code);
-}
-
-size_t CompiledMethodStorage::UniqueCodeEntries() const {
- DCHECK(DedupeEnabled());
- return dedupe_code_.Size(Thread::Current());
-}
-
-const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateVMapTable(
- const ArrayRef<const uint8_t>& table) {
- return AllocateOrDeduplicateArray(table, &dedupe_vmap_table_);
-}
-
-void CompiledMethodStorage::ReleaseVMapTable(const LengthPrefixedArray<uint8_t>* table) {
- ReleaseArrayIfNotDeduplicated(table);
-}
-
-size_t CompiledMethodStorage::UniqueVMapTableEntries() const {
- DCHECK(DedupeEnabled());
- return dedupe_vmap_table_.Size(Thread::Current());
-}
-
-const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateCFIInfo(
- const ArrayRef<const uint8_t>& cfi_info) {
- return AllocateOrDeduplicateArray(cfi_info, &dedupe_cfi_info_);
-}
-
-void CompiledMethodStorage::ReleaseCFIInfo(const LengthPrefixedArray<uint8_t>* cfi_info) {
- ReleaseArrayIfNotDeduplicated(cfi_info);
-}
-
-size_t CompiledMethodStorage::UniqueCFIInfoEntries() const {
- DCHECK(DedupeEnabled());
- return dedupe_cfi_info_.Size(Thread::Current());
-}
-
-const LengthPrefixedArray<linker::LinkerPatch>* CompiledMethodStorage::DeduplicateLinkerPatches(
- const ArrayRef<const linker::LinkerPatch>& linker_patches) {
- return AllocateOrDeduplicateArray(linker_patches, &dedupe_linker_patches_);
-}
-
-void CompiledMethodStorage::ReleaseLinkerPatches(
- const LengthPrefixedArray<linker::LinkerPatch>* linker_patches) {
- ReleaseArrayIfNotDeduplicated(linker_patches);
-}
-
-size_t CompiledMethodStorage::UniqueLinkerPatchesEntries() const {
- DCHECK(DedupeEnabled());
- return dedupe_linker_patches_.Size(Thread::Current());
-}
-
-CompiledMethodStorage::ThunkMapKey CompiledMethodStorage::GetThunkMapKey(
- const linker::LinkerPatch& linker_patch) {
- uint32_t custom_value1 = 0u;
- uint32_t custom_value2 = 0u;
- switch (linker_patch.GetType()) {
- case linker::LinkerPatch::Type::kCallEntrypoint:
- custom_value1 = linker_patch.EntrypointOffset();
- break;
- case linker::LinkerPatch::Type::kBakerReadBarrierBranch:
- custom_value1 = linker_patch.GetBakerCustomValue1();
- custom_value2 = linker_patch.GetBakerCustomValue2();
- break;
- case linker::LinkerPatch::Type::kCallRelative:
- // No custom values.
- break;
- default:
- LOG(FATAL) << "Unexpected patch type: " << linker_patch.GetType();
- UNREACHABLE();
- }
- return ThunkMapKey(linker_patch.GetType(), custom_value1, custom_value2);
-}
-
-ArrayRef<const uint8_t> CompiledMethodStorage::GetThunkCode(const linker::LinkerPatch& linker_patch,
- /*out*/ std::string* debug_name) {
- ThunkMapKey key = GetThunkMapKey(linker_patch);
- MutexLock lock(Thread::Current(), thunk_map_lock_);
- auto it = thunk_map_.find(key);
- if (it != thunk_map_.end()) {
- const ThunkMapValue& value = it->second;
- if (debug_name != nullptr) {
- *debug_name = value.GetDebugName();
- }
- return value.GetCode();
- } else {
- if (debug_name != nullptr) {
- *debug_name = std::string();
- }
- return ArrayRef<const uint8_t>();
- }
-}
-
-void CompiledMethodStorage::SetThunkCode(const linker::LinkerPatch& linker_patch,
- ArrayRef<const uint8_t> code,
- const std::string& debug_name) {
- DCHECK(!code.empty());
- ThunkMapKey key = GetThunkMapKey(linker_patch);
- std::vector<uint8_t, SwapAllocator<uint8_t>> code_copy(
- code.begin(), code.end(), SwapAllocator<uint8_t>(swap_space_.get()));
- ThunkMapValue value(std::move(code_copy), debug_name);
- MutexLock lock(Thread::Current(), thunk_map_lock_);
- // Note: Multiple threads can try and compile the same thunk, so this may not create a new entry.
- thunk_map_.emplace(key, std::move(value));
-}
-
-} // namespace art
diff --git a/compiler/driver/compiled_method_storage.h b/compiler/driver/compiled_method_storage.h
deleted file mode 100644
index f9f3401..0000000
--- a/compiler/driver/compiled_method_storage.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_DRIVER_COMPILED_METHOD_STORAGE_H_
-#define ART_COMPILER_DRIVER_COMPILED_METHOD_STORAGE_H_
-
-#include <iosfwd>
-#include <map>
-#include <memory>
-
-#include "base/array_ref.h"
-#include "base/length_prefixed_array.h"
-#include "base/macros.h"
-#include "utils/dedupe_set.h"
-#include "utils/swap_space.h"
-
-namespace art {
-
-namespace linker {
-class LinkerPatch;
-} // namespace linker
-
-class CompiledMethodStorage {
- public:
- explicit CompiledMethodStorage(int swap_fd);
- ~CompiledMethodStorage();
-
- void DumpMemoryUsage(std::ostream& os, bool extended) const;
-
- void SetDedupeEnabled(bool dedupe_enabled) {
- dedupe_enabled_ = dedupe_enabled;
- }
- bool DedupeEnabled() const {
- return dedupe_enabled_;
- }
-
- SwapAllocator<void> GetSwapSpaceAllocator() {
- return SwapAllocator<void>(swap_space_.get());
- }
-
- const LengthPrefixedArray<uint8_t>* DeduplicateCode(const ArrayRef<const uint8_t>& code);
- void ReleaseCode(const LengthPrefixedArray<uint8_t>* code);
- size_t UniqueCodeEntries() const;
-
- const LengthPrefixedArray<uint8_t>* DeduplicateVMapTable(const ArrayRef<const uint8_t>& table);
- void ReleaseVMapTable(const LengthPrefixedArray<uint8_t>* table);
- size_t UniqueVMapTableEntries() const;
-
- const LengthPrefixedArray<uint8_t>* DeduplicateCFIInfo(const ArrayRef<const uint8_t>& cfi_info);
- void ReleaseCFIInfo(const LengthPrefixedArray<uint8_t>* cfi_info);
- size_t UniqueCFIInfoEntries() const;
-
- const LengthPrefixedArray<linker::LinkerPatch>* DeduplicateLinkerPatches(
- const ArrayRef<const linker::LinkerPatch>& linker_patches);
- void ReleaseLinkerPatches(const LengthPrefixedArray<linker::LinkerPatch>* linker_patches);
- size_t UniqueLinkerPatchesEntries() const;
-
- // Returns the code associated with the given patch.
- // If the code has not been set, returns empty data.
- // If `debug_name` is not null, stores the associated debug name in `*debug_name`.
- ArrayRef<const uint8_t> GetThunkCode(const linker::LinkerPatch& linker_patch,
- /*out*/ std::string* debug_name = nullptr);
-
- // Sets the code and debug name associated with the given patch.
- void SetThunkCode(const linker::LinkerPatch& linker_patch,
- ArrayRef<const uint8_t> code,
- const std::string& debug_name);
-
- private:
- class ThunkMapKey;
- class ThunkMapValue;
- using ThunkMapValueType = std::pair<const ThunkMapKey, ThunkMapValue>;
- using ThunkMap = std::map<ThunkMapKey,
- ThunkMapValue,
- std::less<ThunkMapKey>,
- SwapAllocator<ThunkMapValueType>>;
- static_assert(std::is_same<ThunkMapValueType, ThunkMap::value_type>::value, "Value type check.");
-
- static ThunkMapKey GetThunkMapKey(const linker::LinkerPatch& linker_patch);
-
- template <typename T, typename DedupeSetType>
- const LengthPrefixedArray<T>* AllocateOrDeduplicateArray(const ArrayRef<const T>& data,
- DedupeSetType* dedupe_set);
-
- template <typename T>
- void ReleaseArrayIfNotDeduplicated(const LengthPrefixedArray<T>* array);
-
- // DeDuplication data structures.
- template <typename ContentType>
- class DedupeHashFunc;
-
- template <typename T>
- class LengthPrefixedArrayAlloc;
-
- template <typename T>
- using ArrayDedupeSet = DedupeSet<ArrayRef<const T>,
- LengthPrefixedArray<T>,
- LengthPrefixedArrayAlloc<T>,
- size_t,
- DedupeHashFunc<const T>,
- 4>;
-
- // Swap pool and allocator used for native allocations. May be file-backed. Needs to be first
- // as other fields rely on this.
- std::unique_ptr<SwapSpace> swap_space_;
-
- bool dedupe_enabled_;
-
- ArrayDedupeSet<uint8_t> dedupe_code_;
- ArrayDedupeSet<uint8_t> dedupe_vmap_table_;
- ArrayDedupeSet<uint8_t> dedupe_cfi_info_;
- ArrayDedupeSet<linker::LinkerPatch> dedupe_linker_patches_;
-
- Mutex thunk_map_lock_;
- ThunkMap thunk_map_ GUARDED_BY(thunk_map_lock_);
-
- DISALLOW_COPY_AND_ASSIGN(CompiledMethodStorage);
-};
-
-} // namespace art
-
-#endif // ART_COMPILER_DRIVER_COMPILED_METHOD_STORAGE_H_
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 51cd999..603596f 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -23,6 +23,7 @@
#include "arch/instruction_set.h"
#include "arch/instruction_set_features.h"
+#include "art_method-inl.h"
#include "base/runtime_debug.h"
#include "base/string_view_cpp20.h"
#include "base/variant_map.h"
@@ -30,12 +31,11 @@
#include "cmdline_parser.h"
#include "compiler_options_map-inl.h"
#include "dex/dex_file-inl.h"
-#include "dex/verification_results.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "simple_compiler_options_map.h"
-namespace art {
+namespace art HIDDEN {
CompilerOptions::CompilerOptions()
: compiler_filter_(CompilerFilter::kDefaultCompilerFilter),
@@ -48,7 +48,6 @@
no_inline_from_(),
dex_files_for_oat_file_(),
image_classes_(),
- verification_results_(nullptr),
compiler_type_(CompilerType::kAotCompiler),
image_type_(ImageType::kNone),
multi_image_(false),
@@ -146,14 +145,34 @@
bool CompilerOptions::IsImageClass(const char* descriptor) const {
// Historical note: We used to hold the set indirectly and there was a distinction between an
- // empty set and a null, null meaning to include all classes. However, the distiction has been
+ // empty set and a null, null meaning to include all classes. However, the distinction has been
// removed; if we don't have a profile, we treat it as an empty set of classes. b/77340429
return image_classes_.find(std::string_view(descriptor)) != image_classes_.end();
}
-const VerificationResults* CompilerOptions::GetVerificationResults() const {
- DCHECK(Runtime::Current()->IsAotCompiler());
- return verification_results_;
+bool CompilerOptions::IsPreloadedClass(const char* pretty_descriptor) const {
+ return preloaded_classes_.find(std::string_view(pretty_descriptor)) != preloaded_classes_.end();
+}
+
+bool CompilerOptions::ShouldCompileWithClinitCheck(ArtMethod* method) const {
+ if (method != nullptr &&
+ Runtime::Current()->IsAotCompiler() &&
+ method->IsStatic() &&
+ !method->IsConstructor() &&
+ // Compiled code for native methods never do a clinit check, so we may put the resolution
+ // trampoline for native methods. This means that it's possible post zygote fork for the
+ // entry to be dirtied. We could resolve this by either:
+ // - Make these methods use the generic JNI entrypoint, but that's not
+ // desirable for a method that is in the profile.
+ // - Ensure the declaring class of such native methods are always in the
+ // preloaded-classes list.
+ // - Emit the clinit check in the compiled code of native methods.
+ !method->IsNative()) {
+ ScopedObjectAccess soa(Thread::Current());
+ ObjPtr<mirror::Class> cls = method->GetDeclaringClass<kWithoutReadBarrier>();
+ return cls->IsInBootImageAndNotInPreloadedClasses();
+ }
+ return false;
}
} // namespace art
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 1bffdb1..1cc9f2d 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -30,7 +30,7 @@
#include "base/utils.h"
#include "optimizing/register_allocator.h"
-namespace art {
+namespace art HIDDEN {
namespace jit {
class JitCompiler;
@@ -44,11 +44,11 @@
class Arm64RelativePatcherTest;
} // namespace linker
+class ArtMethod;
class DexFile;
enum class InstructionSet;
class InstructionSetFeatures;
class ProfileCompilationInfo;
-class VerificationResults;
// Enum for CheckProfileMethodsCompiled. Outside CompilerOptions so it can be forward-declared.
enum class ProfileMethodsCheck : uint8_t {
@@ -83,8 +83,8 @@
kAppImage, // Creating app image.
};
- CompilerOptions();
- ~CompilerOptions();
+ EXPORT CompilerOptions();
+ EXPORT ~CompilerOptions();
CompilerFilter::Filter GetCompilerFilter() const {
return compiler_filter_;
@@ -298,9 +298,11 @@
return image_classes_;
}
- bool IsImageClass(const char* descriptor) const;
+ EXPORT bool IsImageClass(const char* descriptor) const;
- const VerificationResults* GetVerificationResults() const;
+ // Returns whether the given `pretty_descriptor` is in the list of preloaded
+ // classes. `pretty_descriptor` should be the result of calling `PrettyDescriptor`.
+ EXPORT bool IsPreloadedClass(const char* pretty_descriptor) const;
bool ParseCompilerOptions(const std::vector<std::string>& options,
bool ignore_unrecognized,
@@ -383,9 +385,15 @@
return ContainsElement(GetDexFilesForOatFile(), dex_file);
}
+ // If this is a static non-constructor method in the boot classpath, and its class isn't
+ // initialized at compile-time, or won't be initialized by the zygote, add
+ // initialization checks at entry. This will avoid the need of trampolines
+ // which at runtime we will need to dirty after initialization.
+ EXPORT bool ShouldCompileWithClinitCheck(ArtMethod* method) const;
+
private:
- bool ParseDumpInitFailures(const std::string& option, std::string* error_msg);
- bool ParseRegisterAllocationStrategy(const std::string& option, std::string* error_msg);
+ EXPORT bool ParseDumpInitFailures(const std::string& option, std::string* error_msg);
+ EXPORT bool ParseRegisterAllocationStrategy(const std::string& option, std::string* error_msg);
CompilerFilter::Filter compiler_filter_;
size_t huge_method_threshold_;
@@ -408,8 +416,9 @@
// Must not be empty for real boot image, only for tests pretending to compile boot image.
HashSet<std::string> image_classes_;
- // Results of AOT verification.
- const VerificationResults* verification_results_;
+ // Classes listed in the preloaded-classes file, used for boot image and
+ // boot image extension compilation.
+ HashSet<std::string> preloaded_classes_;
CompilerType compiler_type_;
ImageType image_type_;
diff --git a/compiler/driver/compiler_options_map-inl.h b/compiler/driver/compiler_options_map-inl.h
index fcbc0f2..79a5962 100644
--- a/compiler/driver/compiler_options_map-inl.h
+++ b/compiler/driver/compiler_options_map-inl.h
@@ -29,7 +29,7 @@
#include "cmdline_parser.h"
#include "compiler_options.h"
-namespace art {
+namespace art HIDDEN {
template <>
struct CmdlineType<CompilerFilter::Filter> : CmdlineTypeParser<CompilerFilter::Filter> {
@@ -118,6 +118,7 @@
template <typename Map, typename Builder>
inline void AddCompilerOptionsArgumentParserOptions(Builder& b) {
+ // clang-format off
b.
Define("--compiler-filter=_")
.template WithType<CompilerFilter::Filter>()
@@ -256,6 +257,7 @@
.template WithType<unsigned int>()
.WithHelp("Maximum solid block size for compressed images.")
.IntoKey(Map::MaxImageBlockSize);
+ // clang-format on
}
#pragma GCC diagnostic pop
diff --git a/compiler/driver/compiler_options_map.h b/compiler/driver/compiler_options_map.h
index 7e2f846..b2dd57d 100644
--- a/compiler/driver/compiler_options_map.h
+++ b/compiler/driver/compiler_options_map.h
@@ -21,10 +21,11 @@
#include <vector>
#include "base/compiler_filter.h"
+#include "base/macros.h"
#include "base/variant_map.h"
#include "cmdline_types.h"
-namespace art {
+namespace art HIDDEN {
enum class ProfileMethodsCheck : uint8_t;
diff --git a/compiler/driver/dex_compilation_unit.cc b/compiler/driver/dex_compilation_unit.cc
index 0d0f074..ccebfa9 100644
--- a/compiler/driver/dex_compilation_unit.cc
+++ b/compiler/driver/dex_compilation_unit.cc
@@ -25,7 +25,7 @@
#include "mirror/dex_cache.h"
#include "scoped_thread_state_change-inl.h"
-namespace art {
+namespace art HIDDEN {
DexCompilationUnit::DexCompilationUnit(Handle<mirror::ClassLoader> class_loader,
ClassLinker* class_linker,
diff --git a/compiler/driver/dex_compilation_unit.h b/compiler/driver/dex_compilation_unit.h
index def90fa..d595c0a 100644
--- a/compiler/driver/dex_compilation_unit.h
+++ b/compiler/driver/dex_compilation_unit.h
@@ -20,11 +20,12 @@
#include <stdint.h>
#include "base/arena_object.h"
+#include "base/macros.h"
#include "dex/code_item_accessors.h"
#include "dex/dex_file.h"
#include "handle.h"
-namespace art {
+namespace art HIDDEN {
namespace mirror {
class Class;
class ClassLoader;
diff --git a/compiler/driver/simple_compiler_options_map.h b/compiler/driver/simple_compiler_options_map.h
index e7a51a4..6663c0c 100644
--- a/compiler/driver/simple_compiler_options_map.h
+++ b/compiler/driver/simple_compiler_options_map.h
@@ -23,9 +23,10 @@
#include <memory>
#include "compiler_options_map-inl.h"
+#include "base/macros.h"
#include "base/variant_map.h"
-namespace art {
+namespace art HIDDEN {
template <typename TValue>
struct SimpleParseArgumentMapKey : VariantMapKey<TValue> {
diff --git a/compiler/exception_test.cc b/compiler/exception_test.cc
index 495398b..82c4998 100644
--- a/compiler/exception_test.cc
+++ b/compiler/exception_test.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <android-base/test_utils.h>
+
#include <memory>
#include <type_traits>
@@ -22,6 +24,7 @@
#include "base/callee_save_type.h"
#include "base/enums.h"
#include "base/leb128.h"
+#include "base/macros.h"
#include "base/malloc_arena_pool.h"
#include "class_linker.h"
#include "common_runtime_test.h"
@@ -42,7 +45,7 @@
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
-namespace art {
+namespace art HIDDEN {
class ExceptionTest : public CommonRuntimeTest {
protected:
@@ -78,7 +81,12 @@
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stack_maps(&allocator, kRuntimeISA);
- stack_maps.BeginMethod(4 * sizeof(void*), 0u, 0u, 0u);
+ stack_maps.BeginMethod(/* frame_size_in_bytes= */ 4 * sizeof(void*),
+ /* core_spill_mask= */ 0u,
+ /* fp_spill_mask= */ 0u,
+ /* num_dex_registers= */ 0u,
+ /* baseline= */ false,
+ /* debuggable= */ false);
stack_maps.BeginStackMapEntry(kDexPc, native_pc_offset);
stack_maps.EndStackMapEntry();
stack_maps.EndMethod(code_size);
@@ -86,7 +94,7 @@
const size_t stack_maps_size = stack_map.size();
const size_t header_size = sizeof(OatQuickMethodHeader);
- const size_t code_alignment = GetInstructionSetAlignment(kRuntimeISA);
+ const size_t code_alignment = GetInstructionSetCodeAlignment(kRuntimeISA);
fake_header_code_and_maps_.resize(stack_maps_size + header_size + code_size + code_alignment);
// NB: The start of the vector might not have been allocated the desired alignment.
@@ -187,15 +195,24 @@
fake_stack.push_back(0);
}
- fake_stack.push_back(method_g_->GetOatQuickMethodHeader(0)->ToNativeQuickPc(
- method_g_, kDexPc, /* is_for_catch_handler= */ false)); // return pc
+ OatQuickMethodHeader* header = OatQuickMethodHeader::FromEntryPoint(
+ method_g_->GetEntryPointFromQuickCompiledCode());
+ // Untag native pc when running with hwasan since the pcs on the stack aren't tagged and we use
+ // this to create a fake stack. See OatQuickMethodHeader::Contains where we untag code pointers
+ // before comparing it with the PC from the stack.
+ uintptr_t native_pc = header->ToNativeQuickPc(method_g_, kDexPc);
+ if (running_with_hwasan()) {
+ // TODO(228989263): Use HWASanUntag once we have a hwasan target for tests too. HWASanUntag
+ // uses static checks which won't work if we don't have a dedicated target.
+ native_pc = (native_pc & ((1ULL << 56) - 1));
+ }
+ fake_stack.push_back(native_pc); // return pc
// Create/push fake 16byte stack frame for method g
fake_stack.push_back(reinterpret_cast<uintptr_t>(method_g_));
fake_stack.push_back(0);
fake_stack.push_back(0);
- fake_stack.push_back(method_g_->GetOatQuickMethodHeader(0)->ToNativeQuickPc(
- method_g_, kDexPc, /* is_for_catch_handler= */ false)); // return pc
+ fake_stack.push_back(native_pc); // return pc.
// Create/push fake 16byte stack frame for method f
fake_stack.push_back(reinterpret_cast<uintptr_t>(method_f_));
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 7002636..e672367 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -34,13 +34,17 @@
#include "jit/jit_code_cache.h"
#include "jit/jit_logger.h"
-namespace art {
+namespace art HIDDEN {
namespace jit {
JitCompiler* JitCompiler::Create() {
return new JitCompiler();
}
+void JitCompiler::SetDebuggableCompilerOption(bool value) {
+ compiler_options_->SetDebuggable(value);
+}
+
void JitCompiler::ParseCompilerOptions() {
// Special case max code units for inlining, whose default is "unset" (implictly
// meaning no limit). Do this before parsing the actual passed options.
@@ -85,7 +89,7 @@
if (StartsWith(option, "--instruction-set-variant=")) {
const char* str = option.c_str() + strlen("--instruction-set-variant=");
VLOG(compiler) << "JIT instruction set variant " << str;
- instruction_set_features = InstructionSetFeatures::FromVariant(
+ instruction_set_features = InstructionSetFeatures::FromVariantAndHwcap(
instruction_set, str, &error_msg);
if (instruction_set_features == nullptr) {
LOG(WARNING) << "Error parsing " << option << " message=" << error_msg;
@@ -121,7 +125,7 @@
}
}
-extern "C" JitCompilerInterface* jit_load() {
+EXPORT extern "C" JitCompilerInterface* jit_load() {
VLOG(jit) << "Create jit compiler";
auto* const jit_compiler = JitCompiler::Create();
CHECK(jit_compiler != nullptr);
@@ -199,6 +203,8 @@
VLOG(jit) << "Compilation of " << method->PrettyMethod() << " took "
<< PrettyDuration(UsToNs(duration_us));
runtime->GetMetrics()->JitMethodCompileCount()->AddOne();
+ runtime->GetMetrics()->JitMethodCompileTotalTimeDelta()->Add(duration_us);
+ runtime->GetMetrics()->JitMethodCompileCountDelta()->AddOne();
}
// Trim maps to reduce memory usage.
diff --git a/compiler/jit/jit_compiler.h b/compiler/jit/jit_compiler.h
index 8e9966d..5a919fb 100644
--- a/compiler/jit/jit_compiler.h
+++ b/compiler/jit/jit_compiler.h
@@ -17,12 +17,13 @@
#ifndef ART_COMPILER_JIT_JIT_COMPILER_H_
#define ART_COMPILER_JIT_JIT_COMPILER_H_
+#include "base/macros.h"
#include "base/mutex.h"
#include "compilation_kind.h"
#include "jit/jit.h"
-namespace art {
+namespace art HIDDEN {
class ArtMethod;
class Compiler;
@@ -50,6 +51,8 @@
bool IsBaselineCompiler() const override;
+ void SetDebuggableCompilerOption(bool val) override;
+
bool GenerateDebugInfo() override;
void ParseCompilerOptions() override;
diff --git a/compiler/jit/jit_logger.cc b/compiler/jit/jit_logger.cc
index 6b9453f..3284526 100644
--- a/compiler/jit/jit_logger.cc
+++ b/compiler/jit/jit_logger.cc
@@ -24,7 +24,7 @@
#include "jit/jit_code_cache.h"
#include "oat_file-inl.h"
-namespace art {
+namespace art HIDDEN {
namespace jit {
#ifdef ART_TARGET_ANDROID
diff --git a/compiler/jit/jit_logger.h b/compiler/jit/jit_logger.h
index f4ef75a..9d1f307 100644
--- a/compiler/jit/jit_logger.h
+++ b/compiler/jit/jit_logger.h
@@ -19,11 +19,11 @@
#include <memory>
+#include "base/macros.h"
#include "base/mutex.h"
#include "base/os.h"
-#include "compiled_method.h"
-namespace art {
+namespace art HIDDEN {
class ArtMethod;
diff --git a/compiler/jni/jni_cfi_test.cc b/compiler/jni/jni_cfi_test.cc
index 9e3bb86..70cf2d4 100644
--- a/compiler/jni/jni_cfi_test.cc
+++ b/compiler/jni/jni_cfi_test.cc
@@ -20,6 +20,7 @@
#include "arch/instruction_set.h"
#include "base/arena_allocator.h"
#include "base/enums.h"
+#include "base/macros.h"
#include "base/malloc_arena_pool.h"
#include "cfi_test.h"
#include "gtest/gtest.h"
@@ -30,7 +31,7 @@
#include "jni/jni_cfi_test_expected.inc"
-namespace art {
+namespace art HIDDEN {
// Run the tests only on host.
#ifndef ART_TARGET_ANDROID
@@ -124,22 +125,31 @@
TestImpl(InstructionSet::isa, #isa, expected_asm, expected_cfi); \
}
+// We can't use compile-time macros for read-barrier as the introduction
+// of userfaultfd-GC has made it a runtime choice.
+#define TEST_ISA_ONLY_CC(isa) \
+ TEST_F(JNICFITest, isa) { \
+ if (kUseBakerReadBarrier && gUseReadBarrier) { \
+ std::vector<uint8_t> expected_asm(expected_asm_##isa, \
+ expected_asm_##isa + arraysize(expected_asm_##isa)); \
+ std::vector<uint8_t> expected_cfi(expected_cfi_##isa, \
+ expected_cfi_##isa + arraysize(expected_cfi_##isa)); \
+ TestImpl(InstructionSet::isa, #isa, expected_asm, expected_cfi); \
+ } \
+ }
+
#ifdef ART_ENABLE_CODEGEN_arm
// Run the tests for ARM only with Baker read barriers, as the
// expected generated code contains a Marking Register refresh
// instruction.
-#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
-TEST_ISA(kThumb2)
-#endif
+TEST_ISA_ONLY_CC(kThumb2)
#endif
#ifdef ART_ENABLE_CODEGEN_arm64
// Run the tests for ARM64 only with Baker read barriers, as the
// expected generated code contains a Marking Register refresh
// instruction.
-#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
-TEST_ISA(kArm64)
-#endif
+TEST_ISA_ONLY_CC(kArm64)
#endif
#ifdef ART_ENABLE_CODEGEN_x86
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index 0a1f017..d1d3190 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -22,6 +22,7 @@
#include "art_method-inl.h"
#include "base/bit_utils.h"
#include "base/casts.h"
+#include "base/macros.h"
#include "base/mem_map.h"
#include "class_linker.h"
#include "common_compiler_test.h"
@@ -43,7 +44,7 @@
#include "oat_quick_method_header.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
-#include "thread.h"
+#include "thread-inl.h"
extern "C" JNIEXPORT jint JNICALL Java_MyClassNatives_bar(JNIEnv*, jobject, jint count) {
return count + 1;
@@ -71,7 +72,7 @@
// TODO: In the Baker read barrier configuration, add checks to ensure
// the Marking Register's value is correct.
-namespace art {
+namespace art HIDDEN {
enum class JniKind {
kNormal, // Regular kind of un-annotated natives.
@@ -236,13 +237,14 @@
bool direct,
const char* method_name,
const char* method_sig) {
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<2> hs(soa.Self());
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ StackHandleScope<2> hs(self);
Handle<mirror::ClassLoader> loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
// Compile the native method before starting the runtime
Handle<mirror::Class> c =
- hs.NewHandle(class_linker_->FindClass(soa.Self(), "LMyClassNatives;", loader));
+ hs.NewHandle(class_linker_->FindClass(self, "LMyClassNatives;", loader));
const auto pointer_size = class_linker_->GetImagePointerSize();
ArtMethod* method = c->FindClassMethod(method_name, method_sig, pointer_size);
ASSERT_TRUE(method != nullptr) << method_name << " " << method_sig;
@@ -251,8 +253,11 @@
// Class initialization could replace the entrypoint, so force
// the initialization before we set up the entrypoint below.
class_linker_->EnsureInitialized(
- soa.Self(), c, /*can_init_fields=*/ true, /*can_init_parents=*/ true);
- class_linker_->MakeInitializedClassesVisiblyInitialized(soa.Self(), /*wait=*/ true);
+ self, c, /*can_init_fields=*/ true, /*can_init_parents=*/ true);
+ {
+ ScopedThreadSuspension sts(self, ThreadState::kNative);
+ class_linker_->MakeInitializedClassesVisiblyInitialized(self, /*wait=*/ true);
+ }
}
if (check_generic_jni_) {
method->SetEntryPointFromQuickCompiledCode(class_linker_->GetRuntimeQuickGenericJniStub());
@@ -402,7 +407,7 @@
jobject JniCompilerTest::class_loader_;
void JniCompilerTest::AssertCallerObjectLocked(JNIEnv* env) {
- Thread* self = down_cast<JNIEnvExt*>(env)->GetSelf();
+ Thread* self = Thread::ForEnv(env);
CHECK_EQ(self, Thread::Current());
ScopedObjectAccess soa(self);
ArtMethod** caller_frame = self->GetManagedStack()->GetTopQuickFrame();
@@ -414,7 +419,7 @@
CHECK(!caller->IsCriticalNative());
CHECK(caller->IsSynchronized());
ObjPtr<mirror::Object> lock;
- if (self->GetManagedStack()->GetTopQuickFrameTag()) {
+ if (self->GetManagedStack()->GetTopQuickFrameGenericJniTag()) {
// Generic JNI.
lock = GetGenericJniSynchronizationObject(self, caller);
} else if (caller->IsStatic()) {
@@ -845,6 +850,7 @@
return x | y;
}
+EXPORT // Defined in `libart.so`.
void InitEntryPoints(JniEntryPoints* jpoints,
QuickEntryPoints* qpoints,
bool monitor_jni_entry_exit);
@@ -1307,7 +1313,7 @@
CompileForTestWithCurrentJni(class_loader_, false, "synchronizedThrowException", "()V");
}
}
- // Start runtime to avoid re-initialization in SetupForTest.
+ // Start runtime to avoid re-initialization in SetUpForTest.
Thread::Current()->TransitionFromSuspendedToRunnable();
bool started = runtime_->Start();
CHECK(started);
diff --git a/compiler/jni/quick/arm/calling_convention_arm.cc b/compiler/jni/quick/arm/calling_convention_arm.cc
index c1afdb8..d81ca77 100644
--- a/compiler/jni/quick/arm/calling_convention_arm.cc
+++ b/compiler/jni/quick/arm/calling_convention_arm.cc
@@ -23,7 +23,7 @@
#include "base/macros.h"
#include "utils/arm/managed_register_arm.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
//
@@ -199,6 +199,10 @@
return ArmManagedRegister::FromCoreRegister(R0);
}
+ManagedRegister ArmManagedRuntimeCallingConvention::ArgumentRegisterForMethodExitHook() {
+ return ArmManagedRegister::FromCoreRegister(R2);
+}
+
void ArmManagedRuntimeCallingConvention::ResetIterator(FrameOffset displacement) {
ManagedRuntimeCallingConvention::ResetIterator(displacement);
gpr_index_ = 1u; // Skip r0 for ArtMethod*
diff --git a/compiler/jni/quick/arm/calling_convention_arm.h b/compiler/jni/quick/arm/calling_convention_arm.h
index 4526d9e..3a09d4e 100644
--- a/compiler/jni/quick/arm/calling_convention_arm.h
+++ b/compiler/jni/quick/arm/calling_convention_arm.h
@@ -18,9 +18,10 @@
#define ART_COMPILER_JNI_QUICK_ARM_CALLING_CONVENTION_ARM_H_
#include "base/enums.h"
+#include "base/macros.h"
#include "jni/quick/calling_convention.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
class ArmManagedRuntimeCallingConvention final : public ManagedRuntimeCallingConvention {
@@ -39,6 +40,7 @@
void ResetIterator(FrameOffset displacement) override;
// Managed runtime calling convention
ManagedRegister MethodRegister() override;
+ ManagedRegister ArgumentRegisterForMethodExitHook() override;
void Next() override;
bool IsCurrentParamInRegister() override;
bool IsCurrentParamOnStack() override;
diff --git a/compiler/jni/quick/arm64/calling_convention_arm64.cc b/compiler/jni/quick/arm64/calling_convention_arm64.cc
index ec77db3..e716502 100644
--- a/compiler/jni/quick/arm64/calling_convention_arm64.cc
+++ b/compiler/jni/quick/arm64/calling_convention_arm64.cc
@@ -22,7 +22,7 @@
#include "arch/instruction_set.h"
#include "utils/arm64/managed_register_arm64.h"
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
static constexpr ManagedRegister kXArgumentRegisters[] = {
@@ -174,6 +174,10 @@
return Arm64ManagedRegister::FromXRegister(X0);
}
+ManagedRegister Arm64ManagedRuntimeCallingConvention::ArgumentRegisterForMethodExitHook() {
+ return Arm64ManagedRegister::FromXRegister(X4);
+}
+
bool Arm64ManagedRuntimeCallingConvention::IsCurrentParamInRegister() {
if (IsCurrentParamAFloatOrDouble()) {
return itr_float_and_doubles_ < kMaxFloatOrDoubleRegisterArguments;
diff --git a/compiler/jni/quick/arm64/calling_convention_arm64.h b/compiler/jni/quick/arm64/calling_convention_arm64.h
index 176271e..f29eb15 100644
--- a/compiler/jni/quick/arm64/calling_convention_arm64.h
+++ b/compiler/jni/quick/arm64/calling_convention_arm64.h
@@ -18,9 +18,10 @@
#define ART_COMPILER_JNI_QUICK_ARM64_CALLING_CONVENTION_ARM64_H_
#include "base/enums.h"
+#include "base/macros.h"
#include "jni/quick/calling_convention.h"
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
class Arm64ManagedRuntimeCallingConvention final : public ManagedRuntimeCallingConvention {
@@ -35,6 +36,7 @@
ManagedRegister ReturnRegister() const override;
// Managed runtime calling convention
ManagedRegister MethodRegister() override;
+ ManagedRegister ArgumentRegisterForMethodExitHook() override;
bool IsCurrentParamInRegister() override;
bool IsCurrentParamOnStack() override;
ManagedRegister CurrentParamRegister() override;
diff --git a/compiler/jni/quick/calling_convention.cc b/compiler/jni/quick/calling_convention.cc
index eb4d372..ce9bee6 100644
--- a/compiler/jni/quick/calling_convention.cc
+++ b/compiler/jni/quick/calling_convention.cc
@@ -37,7 +37,7 @@
#include "jni/quick/x86_64/calling_convention_x86_64.h"
#endif
-namespace art {
+namespace art HIDDEN {
// Managed runtime calling convention
diff --git a/compiler/jni/quick/calling_convention.h b/compiler/jni/quick/calling_convention.h
index e2f3bfb..0187b14 100644
--- a/compiler/jni/quick/calling_convention.h
+++ b/compiler/jni/quick/calling_convention.h
@@ -20,11 +20,12 @@
#include "base/arena_object.h"
#include "base/array_ref.h"
#include "base/enums.h"
+#include "base/macros.h"
#include "dex/primitive.h"
#include "thread.h"
#include "utils/managed_register.h"
-namespace art {
+namespace art HIDDEN {
enum class InstructionSet;
@@ -244,6 +245,11 @@
// Register that holds the incoming method argument
virtual ManagedRegister MethodRegister() = 0;
+ // Register that is used to pass frame size for method exit hook call. This
+ // shouldn't be the same as the return register since method exit hook also expects
+ // return values in the return register.
+ virtual ManagedRegister ArgumentRegisterForMethodExitHook() = 0;
+
// Iterator interface
bool HasNext();
virtual void Next();
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index 6cb5021..c485a00 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -36,7 +36,9 @@
#include "dex/dex_file-inl.h"
#include "driver/compiler_options.h"
#include "entrypoints/quick/quick_entrypoints.h"
+#include "instrumentation.h"
#include "jni/jni_env_ext.h"
+#include "runtime.h"
#include "thread.h"
#include "utils/arm/managed_register_arm.h"
#include "utils/arm64/managed_register_arm64.h"
@@ -47,7 +49,7 @@
#define __ jni_asm->
-namespace art {
+namespace art HIDDEN {
constexpr size_t kIRTCookieSize = JniCallingConvention::SavedLocalReferenceCookieSize();
@@ -101,6 +103,20 @@
// i.e. if the method was annotated with @CriticalNative
const bool is_critical_native = (access_flags & kAccCriticalNative) != 0u;
+ bool needs_entry_exit_hooks =
+ compiler_options.GetDebuggable() && compiler_options.IsJitCompiler();
+ // We don't support JITing stubs for critical native methods in debuggable runtimes yet.
+ // TODO(mythria): Add support required for calling method entry / exit hooks from critical native
+ // methods.
+ DCHECK_IMPLIES(needs_entry_exit_hooks, !is_critical_native);
+
+ // When walking the stack the top frame doesn't have a pc associated with it. We then depend on
+ // the invariant that we don't have JITed code when AOT code is available. In debuggable runtimes
+ // this invariant doesn't hold. So we tag the SP for JITed code to indentify if we are executing
+ // JITed code or AOT code. Since tagging involves additional instructions we tag only in
+ // debuggable runtimes.
+ bool should_tag_sp = needs_entry_exit_hooks;
+
VLOG(jni) << "JniCompile: Method :: "
<< dex_file.PrettyMethod(method_idx, /* with signature */ true)
<< " :: access_flags = " << std::hex << access_flags << std::dec;
@@ -182,7 +198,7 @@
// Skip this for @CriticalNative because we're not passing a `jclass` to the native method.
std::unique_ptr<JNIMacroLabel> jclass_read_barrier_slow_path;
std::unique_ptr<JNIMacroLabel> jclass_read_barrier_return;
- if (kUseReadBarrier && is_static && LIKELY(!is_critical_native)) {
+ if (gUseReadBarrier && is_static && LIKELY(!is_critical_native)) {
jclass_read_barrier_slow_path = __ CreateLabel();
jclass_read_barrier_return = __ CreateLabel();
@@ -219,7 +235,22 @@
// because garbage collections are disabled within the execution of a
// @CriticalNative method.
if (LIKELY(!is_critical_native)) {
- __ StoreStackPointerToThread(Thread::TopOfManagedStackOffset<kPointerSize>());
+ __ StoreStackPointerToThread(Thread::TopOfManagedStackOffset<kPointerSize>(), should_tag_sp);
+ }
+
+ // 1.5. Call any method entry hooks if required.
+ // For critical native methods, we don't JIT stubs in debuggable runtimes (see
+ // OptimizingCompiler::JitCompile).
+ // TODO(mythria): Add support to call method entry / exit hooks for critical native methods too.
+ std::unique_ptr<JNIMacroLabel> method_entry_hook_slow_path;
+ std::unique_ptr<JNIMacroLabel> method_entry_hook_return;
+ if (UNLIKELY(needs_entry_exit_hooks)) {
+ uint64_t address = reinterpret_cast64<uint64_t>(Runtime::Current()->GetInstrumentation());
+ int offset = instrumentation::Instrumentation::HaveMethodEntryListenersOffset().Int32Value();
+ method_entry_hook_slow_path = __ CreateLabel();
+ method_entry_hook_return = __ CreateLabel();
+ __ TestByteAndJumpIfNotZero(address + offset, method_entry_hook_slow_path.get());
+ __ Bind(method_entry_hook_return.get());
}
// 2. Lock the object (if synchronized) and transition out of Runnable (if normal native).
@@ -532,7 +563,21 @@
__ Bind(suspend_check_resume.get());
}
- // 7.5. Remove activation - need to restore callee save registers since the GC
+ // 7.5. Check if method exit hooks needs to be called
+ // For critical native methods, we don't JIT stubs in debuggable runtimes.
+ // TODO(mythria): Add support to call method entry / exit hooks for critical native methods too.
+ std::unique_ptr<JNIMacroLabel> method_exit_hook_slow_path;
+ std::unique_ptr<JNIMacroLabel> method_exit_hook_return;
+ if (UNLIKELY(needs_entry_exit_hooks)) {
+ uint64_t address = reinterpret_cast64<uint64_t>(Runtime::Current()->GetInstrumentation());
+ int offset = instrumentation::Instrumentation::NeedsExitHooksOffset().Int32Value();
+ method_exit_hook_slow_path = __ CreateLabel();
+ method_exit_hook_return = __ CreateLabel();
+ __ TestByteAndJumpIfNotZero(address + offset, method_exit_hook_slow_path.get());
+ __ Bind(method_exit_hook_return.get());
+ }
+
+ // 7.6. Remove activation - need to restore callee save registers since the GC
// may have changed them.
DCHECK_EQ(jni_asm->cfi().GetCurrentCFAOffset(), static_cast<int>(current_frame_size));
if (LIKELY(!is_critical_native) || !main_jni_conv->UseTailCall()) {
@@ -547,7 +592,7 @@
// 8.1. Read barrier slow path for the declaring class in the method for a static call.
// Skip this for @CriticalNative because we're not passing a `jclass` to the native method.
- if (kUseReadBarrier && is_static && !is_critical_native) {
+ if (gUseReadBarrier && is_static && !is_critical_native) {
__ Bind(jclass_read_barrier_slow_path.get());
// Construct slow path for read barrier:
@@ -605,7 +650,7 @@
if (reference_return) {
// Suspend check entry point overwrites top of managed stack and leaves it clobbered.
// We need to restore the top for subsequent runtime call to `JniDecodeReferenceResult()`.
- __ StoreStackPointerToThread(Thread::TopOfManagedStackOffset<kPointerSize>());
+ __ StoreStackPointerToThread(Thread::TopOfManagedStackOffset<kPointerSize>(), should_tag_sp);
}
if (reference_return && main_out_arg_size != 0) {
__ IncreaseFrameSize(main_out_arg_size);
@@ -630,6 +675,25 @@
__ DeliverPendingException();
}
+ // 8.6. Method entry / exit hooks slow paths.
+ if (UNLIKELY(needs_entry_exit_hooks)) {
+ __ Bind(method_entry_hook_slow_path.get());
+ // Use Jni specific method entry hook that saves all the arguments. We have only saved the
+ // callee save registers at this point. So go through Jni specific stub that saves the rest
+ // of the live registers.
+ __ CallFromThread(QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodEntryHook));
+ __ ExceptionPoll(exception_slow_path.get());
+ __ Jump(method_entry_hook_return.get());
+
+ __ Bind(method_exit_hook_slow_path.get());
+ // Method exit hooks is called just before tearing down the frame. So there are no live
+ // registers and we can directly call the method exit hook and don't need a Jni specific
+ // entrypoint.
+ __ Move(mr_conv->ArgumentRegisterForMethodExitHook(), managed_frame_size);
+ __ CallFromThread(QUICK_ENTRYPOINT_OFFSET(kPointerSize, pMethodExitHook));
+ __ Jump(method_exit_hook_return.get());
+ }
+
// 9. Finalize code generation.
__ FinalizeCode();
size_t cs = __ CodeSize();
diff --git a/compiler/jni/quick/jni_compiler.h b/compiler/jni/quick/jni_compiler.h
index 52a6f3c..d43b2a9 100644
--- a/compiler/jni/quick/jni_compiler.h
+++ b/compiler/jni/quick/jni_compiler.h
@@ -21,8 +21,9 @@
#include "arch/instruction_set.h"
#include "base/array_ref.h"
+#include "base/macros.h"
-namespace art {
+namespace art HIDDEN {
class ArenaAllocator;
class ArtMethod;
diff --git a/compiler/jni/quick/x86/calling_convention_x86.cc b/compiler/jni/quick/x86/calling_convention_x86.cc
index 65be92c..598e8e7 100644
--- a/compiler/jni/quick/x86/calling_convention_x86.cc
+++ b/compiler/jni/quick/x86/calling_convention_x86.cc
@@ -22,7 +22,7 @@
#include "arch/x86/jni_frame_x86.h"
#include "utils/x86/managed_register_x86.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
static constexpr ManagedRegister kManagedCoreArgumentRegisters[] = {
@@ -143,6 +143,10 @@
return X86ManagedRegister::FromCpuRegister(EAX);
}
+ManagedRegister X86ManagedRuntimeCallingConvention::ArgumentRegisterForMethodExitHook() {
+ return X86ManagedRegister::FromCpuRegister(EBX);
+}
+
void X86ManagedRuntimeCallingConvention::ResetIterator(FrameOffset displacement) {
ManagedRuntimeCallingConvention::ResetIterator(displacement);
gpr_arg_count_ = 1u; // Skip EAX for ArtMethod*
diff --git a/compiler/jni/quick/x86/calling_convention_x86.h b/compiler/jni/quick/x86/calling_convention_x86.h
index cd7ef5b..f0d663d 100644
--- a/compiler/jni/quick/x86/calling_convention_x86.h
+++ b/compiler/jni/quick/x86/calling_convention_x86.h
@@ -18,9 +18,10 @@
#define ART_COMPILER_JNI_QUICK_X86_CALLING_CONVENTION_X86_H_
#include "base/enums.h"
+#include "base/macros.h"
#include "jni/quick/calling_convention.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
class X86ManagedRuntimeCallingConvention final : public ManagedRuntimeCallingConvention {
@@ -37,6 +38,7 @@
void ResetIterator(FrameOffset displacement) override;
// Managed runtime calling convention
ManagedRegister MethodRegister() override;
+ ManagedRegister ArgumentRegisterForMethodExitHook() override;
void Next() override;
bool IsCurrentParamInRegister() override;
bool IsCurrentParamOnStack() override;
diff --git a/compiler/jni/quick/x86_64/calling_convention_x86_64.cc b/compiler/jni/quick/x86_64/calling_convention_x86_64.cc
index 862ee5e..9d0761d 100644
--- a/compiler/jni/quick/x86_64/calling_convention_x86_64.cc
+++ b/compiler/jni/quick/x86_64/calling_convention_x86_64.cc
@@ -23,7 +23,7 @@
#include "base/bit_utils.h"
#include "utils/x86_64/managed_register_x86_64.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
static constexpr ManagedRegister kCoreArgumentRegisters[] = {
@@ -147,6 +147,10 @@
return X86_64ManagedRegister::FromCpuRegister(RDI);
}
+ManagedRegister X86_64ManagedRuntimeCallingConvention::ArgumentRegisterForMethodExitHook() {
+ return X86_64ManagedRegister::FromCpuRegister(R8);
+}
+
bool X86_64ManagedRuntimeCallingConvention::IsCurrentParamInRegister() {
if (IsCurrentParamAFloatOrDouble()) {
return itr_float_and_doubles_ < kMaxFloatOrDoubleRegisterArguments;
diff --git a/compiler/jni/quick/x86_64/calling_convention_x86_64.h b/compiler/jni/quick/x86_64/calling_convention_x86_64.h
index 483f1f5..859a277 100644
--- a/compiler/jni/quick/x86_64/calling_convention_x86_64.h
+++ b/compiler/jni/quick/x86_64/calling_convention_x86_64.h
@@ -18,9 +18,10 @@
#define ART_COMPILER_JNI_QUICK_X86_64_CALLING_CONVENTION_X86_64_H_
#include "base/enums.h"
+#include "base/macros.h"
#include "jni/quick/calling_convention.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
class X86_64ManagedRuntimeCallingConvention final : public ManagedRuntimeCallingConvention {
@@ -35,6 +36,7 @@
ManagedRegister ReturnRegister() const override;
// Managed runtime calling convention
ManagedRegister MethodRegister() override;
+ ManagedRegister ArgumentRegisterForMethodExitHook() override;
bool IsCurrentParamInRegister() override;
bool IsCurrentParamOnStack() override;
ManagedRegister CurrentParamRegister() override;
diff --git a/compiler/libart-compiler.map b/compiler/libart-compiler.map
new file mode 100644
index 0000000..f66052a
--- /dev/null
+++ b/compiler/libart-compiler.map
@@ -0,0 +1,34 @@
+#
+# Copyright (C) 2022 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.
+#
+
+ART_COMPILER {
+ global:
+ extern "C++" {
+ art::debug::MakeMiniDebugInfo*;
+ *art::debug::WriteDebugInfo*;
+ art::Compiler::Create*;
+ art::CompilerOptions::*;
+ art::CreateTrampoline*;
+ art::IntrinsicObjects::*;
+ art::linker::operator*art::linker::LinkerPatch::Type*;
+ art::operator*art::Whence*;
+ };
+
+ jit_load;
+
+ local:
+ *;
+};
diff --git a/compiler/linker/linker_patch.h b/compiler/linker/linker_patch.h
index 7da1e82..8ed7fce 100644
--- a/compiler/linker/linker_patch.h
+++ b/compiler/linker/linker_patch.h
@@ -23,9 +23,10 @@
#include <android-base/logging.h>
#include "base/bit_utils.h"
+#include "base/macros.h"
#include "dex/method_reference.h"
-namespace art {
+namespace art HIDDEN {
class DexFile;
@@ -328,7 +329,7 @@
friend bool operator==(const LinkerPatch& lhs, const LinkerPatch& rhs);
friend bool operator<(const LinkerPatch& lhs, const LinkerPatch& rhs);
};
-std::ostream& operator<<(std::ostream& os, LinkerPatch::Type type);
+EXPORT std::ostream& operator<<(std::ostream& os, LinkerPatch::Type type);
inline bool operator==(const LinkerPatch& lhs, const LinkerPatch& rhs) {
return lhs.literal_offset_ == rhs.literal_offset_ &&
diff --git a/compiler/linker/linker_patch_test.cc b/compiler/linker/linker_patch_test.cc
index 997418c..1c46da1 100644
--- a/compiler/linker/linker_patch_test.cc
+++ b/compiler/linker/linker_patch_test.cc
@@ -16,9 +16,10 @@
#include <gtest/gtest.h>
+#include "base/macros.h"
#include "linker_patch.h"
-namespace art {
+namespace art HIDDEN {
namespace linker {
TEST(LinkerPatch, LinkerPatchOperators) {
diff --git a/compiler/linker/output_stream_test.cc b/compiler/linker/output_stream_test.cc
index f1af4cb..22b174f 100644
--- a/compiler/linker/output_stream_test.cc
+++ b/compiler/linker/output_stream_test.cc
@@ -16,17 +16,17 @@
#include <android-base/logging.h>
+#include "base/common_art_test.h"
#include "base/macros.h"
#include "base/unix_file/fd_file.h"
-#include "common_runtime_test.h"
#include "stream/buffered_output_stream.h"
#include "stream/file_output_stream.h"
#include "stream/vector_output_stream.h"
-namespace art {
+namespace art HIDDEN {
namespace linker {
-class OutputStreamTest : public CommonRuntimeTest {
+class OutputStreamTest : public CommonArtTest {
protected:
void CheckOffset(off_t expected) {
off_t actual = output_stream_->Seek(0, kSeekCurrent);
diff --git a/compiler/optimizing/block_builder.cc b/compiler/optimizing/block_builder.cc
index e1f061a..703584c 100644
--- a/compiler/optimizing/block_builder.cc
+++ b/compiler/optimizing/block_builder.cc
@@ -22,7 +22,7 @@
#include "dex/dex_file_exception_helpers.h"
#include "quicken_info.h"
-namespace art {
+namespace art HIDDEN {
HBasicBlockBuilder::HBasicBlockBuilder(HGraph* graph,
const DexFile* const dex_file,
diff --git a/compiler/optimizing/block_builder.h b/compiler/optimizing/block_builder.h
index 42a3f32..8668ef8 100644
--- a/compiler/optimizing/block_builder.h
+++ b/compiler/optimizing/block_builder.h
@@ -17,13 +17,14 @@
#ifndef ART_COMPILER_OPTIMIZING_BLOCK_BUILDER_H_
#define ART_COMPILER_OPTIMIZING_BLOCK_BUILDER_H_
+#include "base/macros.h"
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
#include "dex/code_item_accessors.h"
#include "dex/dex_file.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
class HBasicBlockBuilder : public ValueObject {
public:
diff --git a/compiler/optimizing/block_namer.cc b/compiler/optimizing/block_namer.cc
index d30448c..029e26b 100644
--- a/compiler/optimizing/block_namer.cc
+++ b/compiler/optimizing/block_namer.cc
@@ -18,7 +18,7 @@
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
std::ostream& BlockNamer::PrintName(std::ostream& os, HBasicBlock* blk) const {
os << "B";
diff --git a/compiler/optimizing/block_namer.h b/compiler/optimizing/block_namer.h
index ed396b9..39c5973 100644
--- a/compiler/optimizing/block_namer.h
+++ b/compiler/optimizing/block_namer.h
@@ -19,7 +19,9 @@
#include <ostream>
-namespace art {
+#include "base/macros.h"
+
+namespace art HIDDEN {
class HBasicBlock;
struct BlockNamer {
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index dad3c81..52f1e9a 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -24,7 +24,7 @@
#include "nodes.h"
#include "side_effects_analysis.h"
-namespace art {
+namespace art HIDDEN {
class MonotonicValueRange;
@@ -564,6 +564,19 @@
early_exit_loop_.clear();
taken_test_loop_.clear();
finite_loop_.clear();
+
+ // We may have eliminated all bounds checks so we should update the flag.
+ // TODO(solanes): Do this without a linear pass of the graph?
+ GetGraph()->SetHasBoundsChecks(false);
+ for (HBasicBlock* block : GetGraph()->GetReversePostOrder()) {
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ HInstruction* instruction = it.Current();
+ if (instruction->IsBoundsCheck()) {
+ GetGraph()->SetHasBoundsChecks(true);
+ return;
+ }
+ }
+ }
}
private:
@@ -1818,6 +1831,7 @@
HInstruction* condition,
bool is_null_check = false) {
HInstruction* suspend = loop->GetSuspendCheck();
+ DCHECK(suspend != nullptr);
block->InsertInstructionBefore(condition, block->GetLastInstruction());
DeoptimizationKind kind =
is_null_check ? DeoptimizationKind::kLoopNullBCE : DeoptimizationKind::kLoopBoundsBCE;
diff --git a/compiler/optimizing/bounds_check_elimination.h b/compiler/optimizing/bounds_check_elimination.h
index ef08877..f210fa9 100644
--- a/compiler/optimizing/bounds_check_elimination.h
+++ b/compiler/optimizing/bounds_check_elimination.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_BOUNDS_CHECK_ELIMINATION_H_
#define ART_COMPILER_OPTIMIZING_BOUNDS_CHECK_ELIMINATION_H_
+#include "base/macros.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
class SideEffectsAnalysis;
class HInductionVarAnalysis;
diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc
index 5927d68..929a9e7 100644
--- a/compiler/optimizing/bounds_check_elimination_test.cc
+++ b/compiler/optimizing/bounds_check_elimination_test.cc
@@ -17,6 +17,7 @@
#include "bounds_check_elimination.h"
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "gvn.h"
#include "induction_var_analysis.h"
@@ -27,7 +28,7 @@
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
/**
* Fixture class for the BoundsCheckElimination tests.
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index e7826bb..48d1a9d 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -33,7 +33,7 @@
#include "ssa_builder.h"
#include "thread.h"
-namespace art {
+namespace art HIDDEN {
HGraphBuilder::HGraphBuilder(HGraph* graph,
const CodeItemDebugInfoAccessor& accessor,
@@ -103,7 +103,6 @@
graph_->SetNumberOfVRegs(code_item_accessor_.RegistersSize());
graph_->SetNumberOfInVRegs(code_item_accessor_.InsSize());
graph_->SetMaximumNumberOfOutVRegs(code_item_accessor_.OutsSize());
- graph_->SetHasTryCatch(code_item_accessor_.TriesSize() != 0);
// Use ScopedArenaAllocator for all local allocations.
ScopedArenaAllocator local_allocator(graph_->GetArenaStack());
@@ -168,7 +167,6 @@
graph_->SetNumberOfVRegs(return_vregs + num_arg_vregs);
graph_->SetNumberOfInVRegs(num_arg_vregs);
graph_->SetMaximumNumberOfOutVRegs(num_arg_vregs);
- graph_->SetHasTryCatch(false);
// Use ScopedArenaAllocator for all local allocations.
ScopedArenaAllocator local_allocator(graph_->GetArenaStack());
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 580769e..ef225d9 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -19,12 +19,13 @@
#include "base/arena_object.h"
#include "base/array_ref.h"
+#include "base/macros.h"
#include "dex/code_item_accessors.h"
#include "dex/dex_file-inl.h"
#include "dex/dex_file.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
class ArtMethod;
class CodeGenerator;
diff --git a/compiler/optimizing/cha_guard_optimization.cc b/compiler/optimizing/cha_guard_optimization.cc
index c6232ef..20a763c 100644
--- a/compiler/optimizing/cha_guard_optimization.cc
+++ b/compiler/optimizing/cha_guard_optimization.cc
@@ -16,7 +16,7 @@
#include "cha_guard_optimization.h"
-namespace art {
+namespace art HIDDEN {
// Note we can only do CHA guard elimination/motion in a single pass, since
// if a guard is not removed, another guard might be removed due to
@@ -200,6 +200,7 @@
block->RemoveInstruction(deopt);
HInstruction* suspend = loop_info->GetSuspendCheck();
+ DCHECK(suspend != nullptr);
// Need a new deoptimize instruction that copies the environment
// of the suspend instruction for the loop.
HDeoptimize* deoptimize = new (GetGraph()->GetAllocator()) HDeoptimize(
diff --git a/compiler/optimizing/cha_guard_optimization.h b/compiler/optimizing/cha_guard_optimization.h
index 440d51a..5c1fdd9 100644
--- a/compiler/optimizing/cha_guard_optimization.h
+++ b/compiler/optimizing/cha_guard_optimization.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_CHA_GUARD_OPTIMIZATION_H_
#define ART_COMPILER_OPTIMIZING_CHA_GUARD_OPTIMIZATION_H_
+#include "base/macros.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
/**
* Optimize CHA guards by removing/moving them.
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 27eabaf..4d177c2 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -15,6 +15,7 @@
*/
#include "code_generator.h"
+#include "base/globals.h"
#ifdef ART_ENABLE_CODEGEN_arm
#include "code_generator_arm_vixl.h"
@@ -39,7 +40,6 @@
#include "base/leb128.h"
#include "class_linker.h"
#include "class_root-inl.h"
-#include "compiled_method.h"
#include "dex/bytecode_utils.h"
#include "dex/code_item_accessors-inl.h"
#include "graph_visualizer.h"
@@ -61,7 +61,7 @@
#include "thread-current-inl.h"
#include "utils/assembler.h"
-namespace art {
+namespace art HIDDEN {
// Return whether a location is consistent with a type.
static bool CheckType(DataType::Type type, Location location) {
@@ -389,7 +389,8 @@
core_spill_mask_,
fpu_spill_mask_,
GetGraph()->GetNumberOfVRegs(),
- GetGraph()->IsCompilingBaseline());
+ GetGraph()->IsCompilingBaseline(),
+ GetGraph()->IsDebuggable());
size_t frame_start = GetAssembler()->CodeSize();
GenerateFrameEntry();
@@ -412,7 +413,13 @@
for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
HInstruction* current = it.Current();
if (current->HasEnvironment()) {
- // Create stackmap for HNativeDebugInfo or any instruction which calls native code.
+ // Catch StackMaps are dealt with later on in `RecordCatchBlockInfo`.
+ if (block->IsCatchBlock() && block->GetFirstInstruction() == current) {
+ DCHECK(current->IsNop());
+ continue;
+ }
+
+ // Create stackmap for HNop or any instruction which calls native code.
// Note that we need correct mapping for the native PC of the call instruction,
// so the runtime's stackmap is not sufficient since it is at PC after the call.
MaybeRecordNativeDebugInfo(current, block->GetDexPc());
@@ -1123,7 +1130,7 @@
for (HBasicBlock* block : graph.GetReversePostOrder()) {
if (block->IsLoopHeader()) {
HSuspendCheck* suspend_check = block->GetLoopInformation()->GetSuspendCheck();
- if (!suspend_check->GetEnvironment()->IsFromInlinedInvoke()) {
+ if (suspend_check != nullptr && !suspend_check->GetEnvironment()->IsFromInlinedInvoke()) {
loop_headers.push_back(suspend_check);
}
}
@@ -1333,53 +1340,43 @@
continue;
}
- uint32_t dex_pc = block->GetDexPc();
- uint32_t num_vregs = graph_->GetNumberOfVRegs();
- uint32_t native_pc = GetAddressOf(block);
+ // Get the outer dex_pc. We save the full environment list for DCHECK purposes in kIsDebugBuild.
+ std::vector<uint32_t> dex_pc_list_for_verification;
+ if (kIsDebugBuild) {
+ dex_pc_list_for_verification.push_back(block->GetDexPc());
+ }
+ DCHECK(block->GetFirstInstruction()->IsNop());
+ DCHECK(block->GetFirstInstruction()->AsNop()->NeedsEnvironment());
+ HEnvironment* const environment = block->GetFirstInstruction()->GetEnvironment();
+ DCHECK(environment != nullptr);
+ HEnvironment* outer_environment = environment;
+ while (outer_environment->GetParent() != nullptr) {
+ outer_environment = outer_environment->GetParent();
+ if (kIsDebugBuild) {
+ dex_pc_list_for_verification.push_back(outer_environment->GetDexPc());
+ }
+ }
- stack_map_stream->BeginStackMapEntry(dex_pc,
+ if (kIsDebugBuild) {
+ // dex_pc_list_for_verification is set from innnermost to outermost. Let's reverse it
+ // since we are expected to pass from outermost to innermost.
+ std::reverse(dex_pc_list_for_verification.begin(), dex_pc_list_for_verification.end());
+ DCHECK_EQ(dex_pc_list_for_verification.front(), outer_environment->GetDexPc());
+ }
+
+ uint32_t native_pc = GetAddressOf(block);
+ stack_map_stream->BeginStackMapEntry(outer_environment->GetDexPc(),
native_pc,
/* register_mask= */ 0,
/* sp_mask= */ nullptr,
- StackMap::Kind::Catch);
+ StackMap::Kind::Catch,
+ /* needs_vreg_info= */ true,
+ dex_pc_list_for_verification);
- HInstruction* current_phi = block->GetFirstPhi();
- for (size_t vreg = 0; vreg < num_vregs; ++vreg) {
- while (current_phi != nullptr && current_phi->AsPhi()->GetRegNumber() < vreg) {
- HInstruction* next_phi = current_phi->GetNext();
- DCHECK(next_phi == nullptr ||
- current_phi->AsPhi()->GetRegNumber() <= next_phi->AsPhi()->GetRegNumber())
- << "Phis need to be sorted by vreg number to keep this a linear-time loop.";
- current_phi = next_phi;
- }
-
- if (current_phi == nullptr || current_phi->AsPhi()->GetRegNumber() != vreg) {
- stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kNone, 0);
- } else {
- Location location = current_phi->GetLocations()->Out();
- switch (location.GetKind()) {
- case Location::kStackSlot: {
- stack_map_stream->AddDexRegisterEntry(
- DexRegisterLocation::Kind::kInStack, location.GetStackIndex());
- break;
- }
- case Location::kDoubleStackSlot: {
- stack_map_stream->AddDexRegisterEntry(
- DexRegisterLocation::Kind::kInStack, location.GetStackIndex());
- stack_map_stream->AddDexRegisterEntry(
- DexRegisterLocation::Kind::kInStack, location.GetHighStackIndex(kVRegSize));
- ++vreg;
- DCHECK_LT(vreg, num_vregs);
- break;
- }
- default: {
- // All catch phis must be allocated to a stack slot.
- LOG(FATAL) << "Unexpected kind " << location.GetKind();
- UNREACHABLE();
- }
- }
- }
- }
+ EmitEnvironment(environment,
+ /* slow_path= */ nullptr,
+ /* needs_vreg_info= */ true,
+ /* is_for_catch_handler= */ true);
stack_map_stream->EndStackMapEntry();
}
@@ -1390,7 +1387,9 @@
code_generation_data_->AddSlowPath(slow_path);
}
-void CodeGenerator::EmitVRegInfo(HEnvironment* environment, SlowPathCode* slow_path) {
+void CodeGenerator::EmitVRegInfo(HEnvironment* environment,
+ SlowPathCode* slow_path,
+ bool is_for_catch_handler) {
StackMapStream* stack_map_stream = GetStackMapStream();
// Walk over the environment, and record the location of dex registers.
for (size_t i = 0, environment_size = environment->Size(); i < environment_size; ++i) {
@@ -1445,6 +1444,7 @@
}
case Location::kRegister : {
+ DCHECK(!is_for_catch_handler);
int id = location.reg();
if (slow_path != nullptr && slow_path->IsCoreRegisterSaved(id)) {
uint32_t offset = slow_path->GetStackOffsetOfCoreRegister(id);
@@ -1466,6 +1466,7 @@
}
case Location::kFpuRegister : {
+ DCHECK(!is_for_catch_handler);
int id = location.reg();
if (slow_path != nullptr && slow_path->IsFpuRegisterSaved(id)) {
uint32_t offset = slow_path->GetStackOffsetOfFpuRegister(id);
@@ -1487,6 +1488,7 @@
}
case Location::kFpuRegisterPair : {
+ DCHECK(!is_for_catch_handler);
int low = location.low();
int high = location.high();
if (slow_path != nullptr && slow_path->IsFpuRegisterSaved(low)) {
@@ -1508,6 +1510,7 @@
}
case Location::kRegisterPair : {
+ DCHECK(!is_for_catch_handler);
int low = location.low();
int high = location.high();
if (slow_path != nullptr && slow_path->IsCoreRegisterSaved(low)) {
@@ -1538,9 +1541,54 @@
}
}
+void CodeGenerator::EmitVRegInfoOnlyCatchPhis(HEnvironment* environment) {
+ StackMapStream* stack_map_stream = GetStackMapStream();
+ DCHECK(environment->GetHolder()->GetBlock()->IsCatchBlock());
+ DCHECK_EQ(environment->GetHolder()->GetBlock()->GetFirstInstruction(), environment->GetHolder());
+ HInstruction* current_phi = environment->GetHolder()->GetBlock()->GetFirstPhi();
+ for (size_t vreg = 0; vreg < environment->Size(); ++vreg) {
+ while (current_phi != nullptr && current_phi->AsPhi()->GetRegNumber() < vreg) {
+ HInstruction* next_phi = current_phi->GetNext();
+ DCHECK(next_phi == nullptr ||
+ current_phi->AsPhi()->GetRegNumber() <= next_phi->AsPhi()->GetRegNumber())
+ << "Phis need to be sorted by vreg number to keep this a linear-time loop.";
+ current_phi = next_phi;
+ }
+
+ if (current_phi == nullptr || current_phi->AsPhi()->GetRegNumber() != vreg) {
+ stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kNone, 0);
+ } else {
+ Location location = current_phi->GetLocations()->Out();
+ switch (location.GetKind()) {
+ case Location::kStackSlot: {
+ stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kInStack,
+ location.GetStackIndex());
+ break;
+ }
+ case Location::kDoubleStackSlot: {
+ stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kInStack,
+ location.GetStackIndex());
+ stack_map_stream->AddDexRegisterEntry(DexRegisterLocation::Kind::kInStack,
+ location.GetHighStackIndex(kVRegSize));
+ ++vreg;
+ DCHECK_LT(vreg, environment->Size());
+ break;
+ }
+ default: {
+ LOG(FATAL) << "All catch phis must be allocated to a stack slot. Unexpected kind "
+ << location.GetKind();
+ UNREACHABLE();
+ }
+ }
+ }
+ }
+}
+
void CodeGenerator::EmitEnvironment(HEnvironment* environment,
SlowPathCode* slow_path,
- bool needs_vreg_info) {
+ bool needs_vreg_info,
+ bool is_for_catch_handler,
+ bool innermost_environment) {
if (environment == nullptr) return;
StackMapStream* stack_map_stream = GetStackMapStream();
@@ -1548,7 +1596,11 @@
if (emit_inline_info) {
// We emit the parent environment first.
- EmitEnvironment(environment->GetParent(), slow_path, needs_vreg_info);
+ EmitEnvironment(environment->GetParent(),
+ slow_path,
+ needs_vreg_info,
+ is_for_catch_handler,
+ /* innermost_environment= */ false);
stack_map_stream->BeginInlineInfoEntry(environment->GetMethod(),
environment->GetDexPc(),
needs_vreg_info ? environment->Size() : 0,
@@ -1556,9 +1608,13 @@
this);
}
+ // If a dex register map is not required we just won't emit it.
if (needs_vreg_info) {
- // If a dex register map is not required we just won't emit it.
- EmitVRegInfo(environment, slow_path);
+ if (innermost_environment && is_for_catch_handler) {
+ EmitVRegInfoOnlyCatchPhis(environment);
+ } else {
+ EmitVRegInfo(environment, slow_path, is_for_catch_handler);
+ }
}
if (emit_inline_info) {
@@ -1671,7 +1727,7 @@
// When (non-Baker) read barriers are enabled, some instructions
// use a slow path to emit a read barrier, which does not trigger
// GC.
- (kEmitCompilerReadBarrier &&
+ (gUseReadBarrier &&
!kUseBakerReadBarrier &&
(instruction->IsInstanceFieldGet() ||
instruction->IsPredicatedInstanceFieldGet() ||
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index d81a7b5..ee80357 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -26,6 +26,7 @@
#include "base/bit_utils.h"
#include "base/enums.h"
#include "base/globals.h"
+#include "base/macros.h"
#include "base/memory_region.h"
#include "class_root.h"
#include "dex/string_reference.h"
@@ -33,13 +34,15 @@
#include "graph_visualizer.h"
#include "locations.h"
#include "nodes.h"
+#include "oat_quick_method_header.h"
#include "optimizing_compiler_stats.h"
#include "read_barrier_option.h"
#include "stack.h"
+#include "subtype_check.h"
#include "utils/assembler.h"
#include "utils/label.h"
-namespace art {
+namespace art HIDDEN {
// Binary encoding of 2^32 for type double.
static int64_t constexpr k2Pow32EncodingForDouble = INT64_C(0x41F0000000000000);
@@ -56,8 +59,18 @@
// Maximum value for a primitive long.
static int64_t constexpr kPrimLongMax = INT64_C(0x7fffffffffffffff);
-static constexpr ReadBarrierOption kCompilerReadBarrierOption =
- kEmitCompilerReadBarrier ? kWithReadBarrier : kWithoutReadBarrier;
+static const ReadBarrierOption gCompilerReadBarrierOption =
+ gUseReadBarrier ? kWithReadBarrier : kWithoutReadBarrier;
+
+constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf();
+constexpr size_t status_byte_offset =
+ mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte);
+constexpr uint32_t shifted_visibly_initialized_value =
+ enum_cast<uint32_t>(ClassStatus::kVisiblyInitialized) << (status_lsb_position % kBitsPerByte);
+constexpr uint32_t shifted_initializing_value =
+ enum_cast<uint32_t>(ClassStatus::kInitializing) << (status_lsb_position % kBitsPerByte);
+constexpr uint32_t shifted_initialized_value =
+ enum_cast<uint32_t>(ClassStatus::kInitialized) << (status_lsb_position % kBitsPerByte);
class Assembler;
class CodeGenerator;
@@ -460,7 +473,7 @@
// If the target class is in the boot image, it's non-moveable and it doesn't matter
// if we compare it with a from-space or to-space reference, the result is the same.
// It's OK to traverse a class hierarchy jumping between from-space and to-space.
- return kEmitCompilerReadBarrier && !instance_of->GetTargetClass()->IsInBootImage();
+ return gUseReadBarrier && !instance_of->GetTargetClass()->IsInBootImage();
}
static ReadBarrierOption ReadBarrierOptionForInstanceOf(HInstanceOf* instance_of) {
@@ -475,7 +488,7 @@
case TypeCheckKind::kArrayObjectCheck:
case TypeCheckKind::kInterfaceCheck: {
bool needs_read_barrier =
- kEmitCompilerReadBarrier && !check_cast->GetTargetClass()->IsInBootImage();
+ gUseReadBarrier && !check_cast->GetTargetClass()->IsInBootImage();
// We do not emit read barriers for HCheckCast, so we can get false negatives
// and the slow path shall re-check and simply return if the cast is actually OK.
return !needs_read_barrier;
@@ -678,7 +691,7 @@
return LocationSummary::kCallOnMainOnly;
case HLoadString::LoadKind::kJitTableAddress:
DCHECK(!load->NeedsEnvironment());
- return kEmitCompilerReadBarrier
+ return gUseReadBarrier
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall;
break;
@@ -836,8 +849,11 @@
void BlockIfInRegister(Location location, bool is_out = false) const;
void EmitEnvironment(HEnvironment* environment,
SlowPathCode* slow_path,
- bool needs_vreg_info = true);
- void EmitVRegInfo(HEnvironment* environment, SlowPathCode* slow_path);
+ bool needs_vreg_info = true,
+ bool is_for_catch_handler = false,
+ bool innermost_environment = true);
+ void EmitVRegInfo(HEnvironment* environment, SlowPathCode* slow_path, bool is_for_catch_handler);
+ void EmitVRegInfoOnlyCatchPhis(HEnvironment* environment);
static void PrepareCriticalNativeArgumentMoves(
HInvokeStaticOrDirect* invoke,
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 2a0b481..5a5d36d 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -27,7 +27,6 @@
#include "class_root-inl.h"
#include "class_table.h"
#include "code_generator_utils.h"
-#include "compiled_method.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "gc/accounting/card_table.h"
@@ -44,6 +43,7 @@
#include "mirror/var_handle.h"
#include "offsets.h"
#include "optimizing/common_arm64.h"
+#include "optimizing/nodes.h"
#include "thread.h"
#include "utils/arm64/assembler_arm64.h"
#include "utils/assembler.h"
@@ -58,7 +58,7 @@
#error "ARM64 Codegen VIXL macro-assembler macro already defined."
#endif
-namespace art {
+namespace art HIDDEN {
template<class MirrorType>
class GcRoot;
@@ -583,7 +583,7 @@
obj_(obj),
offset_(offset),
index_(index) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
// If `obj` is equal to `out` or `ref`, it means the initial object
// has been overwritten by (or after) the heap object reference load
// to be instrumented, e.g.:
@@ -762,7 +762,7 @@
public:
ReadBarrierForRootSlowPathARM64(HInstruction* instruction, Location out, Location root)
: SlowPathCodeARM64(instruction), out_(out), root_(root) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
}
void EmitNativeCode(CodeGenerator* codegen) override {
@@ -825,6 +825,9 @@
CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
__ Bind(GetEntryLabel());
SaveLiveRegisters(codegen, locations);
+ if (instruction_->IsMethodExitHook()) {
+ __ Mov(vixl::aarch64::x4, arm64_codegen->GetFrameSize());
+ }
arm64_codegen->InvokeRuntime(entry_point, instruction_, instruction_->GetDexPc(), this);
RestoreLiveRegisters(codegen, locations);
__ B(GetExitLabel());
@@ -1169,9 +1172,21 @@
new (codegen_->GetScopedAllocator()) MethodEntryExitHooksSlowPathARM64(instruction);
codegen_->AddSlowPath(slow_path);
+ if (instruction->IsMethodExitHook()) {
+ // Check if we are required to check if the caller needs a deoptimization. Strictly speaking it
+ // would be sufficient to check if CheckCallerForDeopt bit is set. Though it is faster to check
+ // if it is just non-zero. kCHA bit isn't used in debuggable runtimes as cha optimization is
+ // disabled in debuggable runtime. The other bit is used when this method itself requires a
+ // deoptimization due to redefinition. So it is safe to just check for non-zero value here.
+ __ Ldr(value, MemOperand(sp, codegen_->GetStackOffsetOfShouldDeoptimizeFlag()));
+ __ Cbnz(value, slow_path->GetEntryLabel());
+ }
+
uint64_t address = reinterpret_cast64<uint64_t>(Runtime::Current()->GetInstrumentation());
- int offset = instrumentation::Instrumentation::NeedsEntryExitHooksOffset().Int32Value();
- __ Mov(temp, address + offset);
+ MemberOffset offset = instruction->IsMethodExitHook() ?
+ instrumentation::Instrumentation::HaveMethodExitListenersOffset() :
+ instrumentation::Instrumentation::HaveMethodEntryListenersOffset();
+ __ Mov(temp, address + offset.Int32Value());
__ Ldrb(value, MemOperand(temp, 0));
__ Cbnz(value, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
@@ -1233,6 +1248,54 @@
void CodeGeneratorARM64::GenerateFrameEntry() {
MacroAssembler* masm = GetVIXLAssembler();
+
+ // Check if we need to generate the clinit check. We will jump to the
+ // resolution stub if the class is not initialized and the executing thread is
+ // not the thread initializing it.
+ // We do this before constructing the frame to get the correct stack trace if
+ // an exception is thrown.
+ if (GetCompilerOptions().ShouldCompileWithClinitCheck(GetGraph()->GetArtMethod())) {
+ UseScratchRegisterScope temps(masm);
+ vixl::aarch64::Label resolution;
+ vixl::aarch64::Label memory_barrier;
+
+ Register temp1 = temps.AcquireW();
+ Register temp2 = temps.AcquireW();
+
+ // Check if we're visibly initialized.
+
+ // We don't emit a read barrier here to save on code size. We rely on the
+ // resolution trampoline to do a suspend check before re-entering this code.
+ __ Ldr(temp1, MemOperand(kArtMethodRegister, ArtMethod::DeclaringClassOffset().Int32Value()));
+ __ Ldrb(temp2, HeapOperand(temp1, status_byte_offset));
+ __ Cmp(temp2, shifted_visibly_initialized_value);
+ __ B(hs, &frame_entry_label_);
+
+ // Check if we're initialized and jump to code that does a memory barrier if
+ // so.
+ __ Cmp(temp2, shifted_initialized_value);
+ __ B(hs, &memory_barrier);
+
+ // Check if we're initializing and the thread initializing is the one
+ // executing the code.
+ __ Cmp(temp2, shifted_initializing_value);
+ __ B(lo, &resolution);
+
+ __ Ldr(temp1, HeapOperand(temp1, mirror::Class::ClinitThreadIdOffset().Int32Value()));
+ __ Ldr(temp2, MemOperand(tr, Thread::TidOffset<kArm64PointerSize>().Int32Value()));
+ __ Cmp(temp1, temp2);
+ __ B(eq, &frame_entry_label_);
+ __ Bind(&resolution);
+
+ // Jump to the resolution stub.
+ ThreadOffset64 entrypoint_offset =
+ GetThreadOffset<kArm64PointerSize>(kQuickQuickResolutionTrampoline);
+ __ Ldr(temp1.X(), MemOperand(tr, entrypoint_offset.Int32Value()));
+ __ Br(temp1.X());
+
+ __ Bind(&memory_barrier);
+ GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
+ }
__ Bind(&frame_entry_label_);
bool do_overflow_check =
@@ -1364,12 +1427,12 @@
}
}
-void CodeGeneratorARM64::MarkGCCard(Register object, Register value, bool value_can_be_null) {
+void CodeGeneratorARM64::MarkGCCard(Register object, Register value, bool emit_null_check) {
UseScratchRegisterScope temps(GetVIXLAssembler());
Register card = temps.AcquireX();
Register temp = temps.AcquireW(); // Index within the CardTable - 32bit.
vixl::aarch64::Label done;
- if (value_can_be_null) {
+ if (emit_null_check) {
__ Cbz(value, &done);
}
// Load the address of the card table into `card`.
@@ -1391,7 +1454,7 @@
// of the card to mark; and 2. to load the `kCardDirty` value) saves a load
// (no need to explicitly load `kCardDirty` as an immediate value).
__ Strb(card, MemOperand(card, temp.X()));
- if (value_can_be_null) {
+ if (emit_null_check) {
__ Bind(&done);
}
}
@@ -1904,11 +1967,6 @@
Register class_reg) {
UseScratchRegisterScope temps(GetVIXLAssembler());
Register temp = temps.AcquireW();
- constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf();
- const size_t status_byte_offset =
- mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte);
- constexpr uint32_t shifted_visibly_initialized_value =
- enum_cast<uint32_t>(ClassStatus::kVisiblyInitialized) << (status_lsb_position % kBitsPerByte);
// CMP (immediate) is limited to imm12 or imm12<<12, so we would need to materialize
// the constant 0xf0000000 for comparison with the full 32-bit field. To reduce the code
@@ -1974,6 +2032,13 @@
void InstructionCodeGeneratorARM64::GenerateSuspendCheck(HSuspendCheck* instruction,
HBasicBlock* successor) {
+ if (instruction->IsNoOp()) {
+ if (successor != nullptr) {
+ __ B(codegen_->GetLabelOf(successor));
+ }
+ return;
+ }
+
if (codegen_->CanUseImplicitSuspendCheck()) {
__ Ldr(kImplicitSuspendCheckRegister, MemOperand(kImplicitSuspendCheckRegister));
codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
@@ -2051,7 +2116,7 @@
bool is_predicated = instruction->IsPredicatedInstanceFieldGet();
bool object_field_get_with_read_barrier =
- kEmitCompilerReadBarrier && (instruction->GetType() == DataType::Type::kReference);
+ gUseReadBarrier && (instruction->GetType() == DataType::Type::kReference);
LocationSummary* locations =
new (GetGraph()->GetAllocator()) LocationSummary(instruction,
object_field_get_with_read_barrier
@@ -2107,7 +2172,7 @@
MemOperand field =
HeapOperand(InputRegisterAt(instruction, receiver_input), field_info.GetFieldOffset());
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier &&
+ if (gUseReadBarrier && kUseBakerReadBarrier &&
load_type == DataType::Type::kReference) {
// Object FieldGet with Baker's read barrier case.
// /* HeapReference<Object> */ out = *(base + offset)
@@ -2165,7 +2230,8 @@
void InstructionCodeGeneratorARM64::HandleFieldSet(HInstruction* instruction,
const FieldInfo& field_info,
- bool value_can_be_null) {
+ bool value_can_be_null,
+ WriteBarrierKind write_barrier_kind) {
DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
bool is_predicated =
instruction->IsInstanceFieldSet() && instruction->AsInstanceFieldSet()->GetIsPredicatedSet();
@@ -2205,8 +2271,12 @@
}
}
- if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
- codegen_->MarkGCCard(obj, Register(value), value_can_be_null);
+ if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1)) &&
+ write_barrier_kind != WriteBarrierKind::kDontEmit) {
+ codegen_->MarkGCCard(
+ obj,
+ Register(value),
+ value_can_be_null && write_barrier_kind == WriteBarrierKind::kEmitWithNullCheck);
}
if (is_predicated) {
@@ -2549,7 +2619,7 @@
void LocationsBuilderARM64::VisitArrayGet(HArrayGet* instruction) {
bool object_array_get_with_read_barrier =
- kEmitCompilerReadBarrier && (instruction->GetType() == DataType::Type::kReference);
+ gUseReadBarrier && (instruction->GetType() == DataType::Type::kReference);
LocationSummary* locations =
new (GetGraph()->GetAllocator()) LocationSummary(instruction,
object_array_get_with_read_barrier
@@ -2605,10 +2675,10 @@
// does not support the HIntermediateAddress instruction.
DCHECK(!((type == DataType::Type::kReference) &&
instruction->GetArray()->IsIntermediateAddress() &&
- kEmitCompilerReadBarrier &&
+ gUseReadBarrier &&
!kUseBakerReadBarrier));
- if (type == DataType::Type::kReference && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (type == DataType::Type::kReference && gUseReadBarrier && kUseBakerReadBarrier) {
// Object ArrayGet with Baker's read barrier case.
// Note that a potential implicit null check is handled in the
// CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier call.
@@ -2871,7 +2941,11 @@
}
}
- codegen_->MarkGCCard(array, value.W(), /* value_can_be_null= */ false);
+ if (instruction->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit) {
+ DCHECK_EQ(instruction->GetWriteBarrierKind(), WriteBarrierKind::kEmitNoNullCheck)
+ << " Already null checked so we shouldn't do it again.";
+ codegen_->MarkGCCard(array, value.W(), /* emit_null_check= */ false);
+ }
if (can_value_be_null) {
DCHECK(do_store.IsLinked());
@@ -3845,12 +3919,12 @@
}
}
-void LocationsBuilderARM64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
- new (GetGraph()->GetAllocator()) LocationSummary(info);
+void LocationsBuilderARM64::VisitNop(HNop* nop) {
+ new (GetGraph()->GetAllocator()) LocationSummary(nop);
}
-void InstructionCodeGeneratorARM64::VisitNativeDebugInfo(HNativeDebugInfo*) {
- // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
+void InstructionCodeGeneratorARM64::VisitNop(HNop*) {
+ // The environment recording already happened in CodeGenerator::Compile.
}
void CodeGeneratorARM64::IncreaseFrame(size_t adjustment) {
@@ -3893,12 +3967,15 @@
}
void InstructionCodeGeneratorARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
+ HandleFieldSet(instruction,
+ instruction->GetFieldInfo(),
+ instruction->GetValueCanBeNull(),
+ instruction->GetWriteBarrierKind());
}
// Temp is used for read barrier.
static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) {
- if (kEmitCompilerReadBarrier &&
+ if (gUseReadBarrier &&
(kUseBakerReadBarrier ||
type_check_kind == TypeCheckKind::kAbstractClassCheck ||
type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
@@ -5313,7 +5390,7 @@
load_kind == HLoadClass::LoadKind::kBssEntryPublic ||
load_kind == HLoadClass::LoadKind::kBssEntryPackage);
- const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
+ const bool requires_read_barrier = gUseReadBarrier && !cls->IsInBootImage();
LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall;
@@ -5327,7 +5404,7 @@
}
locations->SetOut(Location::RequiresRegister());
if (cls->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) {
- if (!kUseReadBarrier || kUseBakerReadBarrier) {
+ if (!gUseReadBarrier || kUseBakerReadBarrier) {
// Rely on the type resolution or initialization and marking to save everything we need.
locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
} else {
@@ -5354,7 +5431,7 @@
const ReadBarrierOption read_barrier_option = cls->IsInBootImage()
? kWithoutReadBarrier
- : kCompilerReadBarrierOption;
+ : gCompilerReadBarrierOption;
bool generate_null_check = false;
switch (load_kind) {
case HLoadClass::LoadKind::kReferrersClass: {
@@ -5523,7 +5600,7 @@
} else {
locations->SetOut(Location::RequiresRegister());
if (load->GetLoadKind() == HLoadString::LoadKind::kBssEntry) {
- if (!kUseReadBarrier || kUseBakerReadBarrier) {
+ if (!gUseReadBarrier || kUseBakerReadBarrier) {
// Rely on the pResolveString and marking to save everything we need.
locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
} else {
@@ -5577,7 +5654,7 @@
temp,
/* offset placeholder */ 0u,
ldr_label,
- kCompilerReadBarrierOption);
+ gCompilerReadBarrierOption);
SlowPathCodeARM64* slow_path =
new (codegen_->GetScopedAllocator()) LoadStringSlowPathARM64(load);
codegen_->AddSlowPath(slow_path);
@@ -5601,7 +5678,7 @@
out.X(),
/* offset= */ 0,
/* fixup_label= */ nullptr,
- kCompilerReadBarrierOption);
+ gCompilerReadBarrierOption);
return;
}
default:
@@ -6156,7 +6233,10 @@
}
void InstructionCodeGeneratorARM64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
+ HandleFieldSet(instruction,
+ instruction->GetFieldInfo(),
+ instruction->GetValueCanBeNull(),
+ instruction->GetWriteBarrierKind());
}
void LocationsBuilderARM64::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
@@ -6462,7 +6542,7 @@
DataType::Type type = DataType::Type::kReference;
Register out_reg = RegisterFrom(out, type);
if (read_barrier_option == kWithReadBarrier) {
- CHECK(kEmitCompilerReadBarrier);
+ CHECK(gUseReadBarrier);
if (kUseBakerReadBarrier) {
// Load with fast path based Baker's read barrier.
// /* HeapReference<Object> */ out = *(out + offset)
@@ -6503,7 +6583,7 @@
Register out_reg = RegisterFrom(out, type);
Register obj_reg = RegisterFrom(obj, type);
if (read_barrier_option == kWithReadBarrier) {
- CHECK(kEmitCompilerReadBarrier);
+ CHECK(gUseReadBarrier);
if (kUseBakerReadBarrier) {
// Load with fast path based Baker's read barrier.
// /* HeapReference<Object> */ out = *(obj + offset)
@@ -6538,7 +6618,7 @@
DCHECK(fixup_label == nullptr || offset == 0u);
Register root_reg = RegisterFrom(root, DataType::Type::kReference);
if (read_barrier_option == kWithReadBarrier) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
if (kUseBakerReadBarrier) {
// Fast path implementation of art::ReadBarrier::BarrierForRoot when
// Baker's read barrier are used.
@@ -6604,7 +6684,7 @@
void CodeGeneratorARM64::GenerateIntrinsicCasMoveWithBakerReadBarrier(
vixl::aarch64::Register marked_old_value,
vixl::aarch64::Register old_value) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
DCHECK(kUseBakerReadBarrier);
// Similar to the Baker RB path in GenerateGcRootFieldLoad(), with a MOV instead of LDR.
@@ -6626,7 +6706,7 @@
const vixl::aarch64::MemOperand& src,
bool needs_null_check,
bool use_load_acquire) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
DCHECK(kUseBakerReadBarrier);
// Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
@@ -6722,7 +6802,7 @@
uint32_t data_offset,
Location index,
bool needs_null_check) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
DCHECK(kUseBakerReadBarrier);
static_assert(
@@ -6800,7 +6880,7 @@
void CodeGeneratorARM64::MaybeGenerateMarkingRegisterCheck(int code, Location temp_loc) {
// The following condition is a compile-time one, so it does not have a run-time cost.
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier && kIsDebugBuild) {
+ if (kIsDebugBuild && gUseReadBarrier && kUseBakerReadBarrier) {
// The following condition is a run-time one; it is executed after the
// previous compile-time test, to avoid penalizing non-debug builds.
if (GetCompilerOptions().EmitRunTimeChecksInDebugMode()) {
@@ -6829,7 +6909,7 @@
Location obj,
uint32_t offset,
Location index) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
// Insert a slow path based read barrier *after* the reference load.
//
@@ -6854,7 +6934,7 @@
Location obj,
uint32_t offset,
Location index) {
- if (kEmitCompilerReadBarrier) {
+ if (gUseReadBarrier) {
// Baker's read barriers shall be handled by the fast path
// (CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier).
DCHECK(!kUseBakerReadBarrier);
@@ -6869,7 +6949,7 @@
void CodeGeneratorARM64::GenerateReadBarrierForRootSlow(HInstruction* instruction,
Location out,
Location root) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
// Insert a slow path based read barrier *after* the GC root load.
//
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index f4d652c..deba88b 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -18,6 +18,7 @@
#define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM64_H_
#include "base/bit_field.h"
+#include "base/macros.h"
#include "class_root.h"
#include "code_generator.h"
#include "common_arm64.h"
@@ -36,7 +37,7 @@
#include "aarch64/macro-assembler-aarch64.h"
#pragma GCC diagnostic pop
-namespace art {
+namespace art HIDDEN {
namespace linker {
class Arm64RelativePatcherTest;
@@ -92,7 +93,10 @@
vixl::aarch64::CPURegList(
tr,
// Reserve X20 as Marking Register when emitting Baker read barriers.
- ((kEmitCompilerReadBarrier && kUseBakerReadBarrier) ? mr : vixl::aarch64::NoCPUReg),
+ // TODO: We don't need to reserve marking-register for userfaultfd GC. But
+ // that would require some work in the assembler code as the right GC is
+ // chosen at load-time and not compile time.
+ (kReserveMarkingRegister ? mr : vixl::aarch64::NoCPUReg),
kImplicitSuspendCheckRegister,
vixl::aarch64::lr);
@@ -111,9 +115,7 @@
const vixl::aarch64::CPURegList callee_saved_core_registers(
vixl::aarch64::CPURegister::kRegister,
vixl::aarch64::kXRegSize,
- ((kEmitCompilerReadBarrier && kUseBakerReadBarrier)
- ? vixl::aarch64::x21.GetCode()
- : vixl::aarch64::x20.GetCode()),
+ (kReserveMarkingRegister ? vixl::aarch64::x21.GetCode() : vixl::aarch64::x20.GetCode()),
vixl::aarch64::x30.GetCode());
const vixl::aarch64::CPURegList callee_saved_fp_registers(vixl::aarch64::CPURegister::kVRegister,
vixl::aarch64::kDRegSize,
@@ -327,7 +329,8 @@
void HandleFieldSet(HInstruction* instruction,
const FieldInfo& field_info,
- bool value_can_be_null);
+ bool value_can_be_null,
+ WriteBarrierKind write_barrier_kind);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
void HandleCondition(HCondition* instruction);
@@ -615,7 +618,7 @@
// Emit a write barrier.
void MarkGCCard(vixl::aarch64::Register object,
vixl::aarch64::Register value,
- bool value_can_be_null);
+ bool emit_null_check);
void GenerateMemoryBarrier(MemBarrierKind kind);
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 09fa598..51d6a46 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -26,7 +26,6 @@
#include "class_table.h"
#include "code_generator_utils.h"
#include "common_arm.h"
-#include "compiled_method.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "gc/accounting/card_table.h"
#include "gc/space/image_space.h"
@@ -46,7 +45,7 @@
#include "utils/assembler.h"
#include "utils/stack_checks.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
namespace vixl32 = vixl::aarch32;
@@ -744,7 +743,7 @@
obj_(obj),
offset_(offset),
index_(index) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
// If `obj` is equal to `out` or `ref`, it means the initial object
// has been overwritten by (or after) the heap object reference load
// to be instrumented, e.g.:
@@ -922,7 +921,7 @@
public:
ReadBarrierForRootSlowPathARMVIXL(HInstruction* instruction, Location out, Location root)
: SlowPathCodeARMVIXL(instruction), out_(out), root_(root) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
}
void EmitNativeCode(CodeGenerator* codegen) override {
@@ -974,6 +973,10 @@
(instruction_->IsMethodEntryHook()) ? kQuickMethodEntryHook : kQuickMethodExitHook;
__ Bind(GetEntryLabel());
SaveLiveRegisters(codegen, locations);
+ if (instruction_->IsMethodExitHook()) {
+ // Load frame size to pass to the exit hooks
+ __ Mov(vixl::aarch32::Register(R2), arm_codegen->GetFrameSize());
+ }
arm_codegen->InvokeRuntime(entry_point, instruction_, instruction_->GetDexPc(), this);
RestoreLiveRegisters(codegen, locations);
__ B(GetExitLabel());
@@ -2101,7 +2104,10 @@
blocked_core_registers_[LR] = true;
blocked_core_registers_[PC] = true;
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ // TODO: We don't need to reserve marking-register for userfaultfd GC. But
+ // that would require some work in the assembler code as the right GC is
+ // chosen at load-time and not compile time.
+ if (kReserveMarkingRegister) {
// Reserve marking register.
blocked_core_registers_[MR] = true;
}
@@ -2164,9 +2170,24 @@
new (codegen_->GetScopedAllocator()) MethodEntryExitHooksSlowPathARMVIXL(instruction);
codegen_->AddSlowPath(slow_path);
- int offset = instrumentation::Instrumentation::NeedsEntryExitHooksOffset().Int32Value();
+ if (instruction->IsMethodExitHook()) {
+ // Check if we are required to check if the caller needs a deoptimization. Strictly speaking it
+ // would be sufficient to check if CheckCallerForDeopt bit is set. Though it is faster to check
+ // if it is just non-zero. kCHA bit isn't used in debuggable runtimes as cha optimization is
+ // disabled in debuggable runtime. The other bit is used when this method itself requires a
+ // deoptimization due to redefinition. So it is safe to just check for non-zero value here.
+ GetAssembler()->LoadFromOffset(kLoadWord,
+ temp,
+ sp,
+ codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
+ __ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel());
+ }
+
+ MemberOffset offset = instruction->IsMethodExitHook() ?
+ instrumentation::Instrumentation::HaveMethodExitListenersOffset() :
+ instrumentation::Instrumentation::HaveMethodEntryListenersOffset();
uint32_t address = reinterpret_cast32<uint32_t>(Runtime::Current()->GetInstrumentation());
- __ Mov(temp, address + offset);
+ __ Mov(temp, address + offset.Int32Value());
__ Ldrb(temp, MemOperand(temp, 0));
__ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
@@ -2234,6 +2255,61 @@
bool skip_overflow_check =
IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm);
DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
+
+ // Check if we need to generate the clinit check. We will jump to the
+ // resolution stub if the class is not initialized and the executing thread is
+ // not the thread initializing it.
+ // We do this before constructing the frame to get the correct stack trace if
+ // an exception is thrown.
+ if (GetCompilerOptions().ShouldCompileWithClinitCheck(GetGraph()->GetArtMethod())) {
+ UseScratchRegisterScope temps(GetVIXLAssembler());
+ vixl32::Label resolution;
+ vixl32::Label memory_barrier;
+
+ // Check if we're visibly initialized.
+
+ vixl32::Register temp1 = temps.Acquire();
+ // Use r4 as other temporary register.
+ DCHECK(!blocked_core_registers_[R4]);
+ DCHECK(!kCoreCalleeSaves.Includes(r4));
+ vixl32::Register temp2 = r4;
+ for (vixl32::Register reg : kParameterCoreRegistersVIXL) {
+ DCHECK(!reg.Is(r4));
+ }
+
+ // We don't emit a read barrier here to save on code size. We rely on the
+ // resolution trampoline to do a suspend check before re-entering this code.
+ __ Ldr(temp1, MemOperand(kMethodRegister, ArtMethod::DeclaringClassOffset().Int32Value()));
+ __ Ldrb(temp2, MemOperand(temp1, status_byte_offset));
+ __ Cmp(temp2, shifted_visibly_initialized_value);
+ __ B(cs, &frame_entry_label_);
+
+ // Check if we're initialized and jump to code that does a memory barrier if
+ // so.
+ __ Cmp(temp2, shifted_initialized_value);
+ __ B(cs, &memory_barrier);
+
+ // Check if we're initializing and the thread initializing is the one
+ // executing the code.
+ __ Cmp(temp2, shifted_initializing_value);
+ __ B(lo, &resolution);
+
+ __ Ldr(temp1, MemOperand(temp1, mirror::Class::ClinitThreadIdOffset().Int32Value()));
+ __ Ldr(temp2, MemOperand(tr, Thread::TidOffset<kArmPointerSize>().Int32Value()));
+ __ Cmp(temp1, temp2);
+ __ B(eq, &frame_entry_label_);
+ __ Bind(&resolution);
+
+ // Jump to the resolution stub.
+ ThreadOffset32 entrypoint_offset =
+ GetThreadOffset<kArmPointerSize>(kQuickQuickResolutionTrampoline);
+ __ Ldr(temp1, MemOperand(tr, entrypoint_offset.Int32Value()));
+ __ Bx(temp1);
+
+ __ Bind(&memory_barrier);
+ GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
+ }
+
__ Bind(&frame_entry_label_);
if (HasEmptyFrame()) {
@@ -3069,12 +3145,12 @@
}
}
-void LocationsBuilderARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo* info) {
- new (GetGraph()->GetAllocator()) LocationSummary(info);
+void LocationsBuilderARMVIXL::VisitNop(HNop* nop) {
+ new (GetGraph()->GetAllocator()) LocationSummary(nop);
}
-void InstructionCodeGeneratorARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo*) {
- // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
+void InstructionCodeGeneratorARMVIXL::VisitNop(HNop*) {
+ // The environment recording already happened in CodeGenerator::Compile.
}
void CodeGeneratorARMVIXL::IncreaseFrame(size_t adjustment) {
@@ -5727,8 +5803,9 @@
__ CompareAndBranchIfNonZero(temp1, &fail);
}
-void LocationsBuilderARMVIXL::HandleFieldSet(
- HInstruction* instruction, const FieldInfo& field_info) {
+void LocationsBuilderARMVIXL::HandleFieldSet(HInstruction* instruction,
+ const FieldInfo& field_info,
+ WriteBarrierKind write_barrier_kind) {
DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
LocationSummary* locations =
@@ -5751,8 +5828,12 @@
// Temporary registers for the write barrier.
// TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark.
if (needs_write_barrier) {
- locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too.
- locations->AddTemp(Location::RequiresRegister());
+ if (write_barrier_kind != WriteBarrierKind::kDontEmit) {
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ } else if (kPoisonHeapReferences) {
+ locations->AddTemp(Location::RequiresRegister());
+ }
} else if (generate_volatile) {
// ARM encoding have some additional constraints for ldrexd/strexd:
// - registers need to be consecutive
@@ -5773,7 +5854,8 @@
void InstructionCodeGeneratorARMVIXL::HandleFieldSet(HInstruction* instruction,
const FieldInfo& field_info,
- bool value_can_be_null) {
+ bool value_can_be_null,
+ WriteBarrierKind write_barrier_kind) {
DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
LocationSummary* locations = instruction->GetLocations();
@@ -5889,10 +5971,16 @@
UNREACHABLE();
}
- if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
+ if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1)) &&
+ write_barrier_kind != WriteBarrierKind::kDontEmit) {
vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
vixl32::Register card = RegisterFrom(locations->GetTemp(1));
- codegen_->MarkGCCard(temp, card, base, RegisterFrom(value), value_can_be_null);
+ codegen_->MarkGCCard(
+ temp,
+ card,
+ base,
+ RegisterFrom(value),
+ value_can_be_null && write_barrier_kind == WriteBarrierKind::kEmitWithNullCheck);
}
if (is_volatile) {
@@ -5911,7 +5999,7 @@
instruction->IsPredicatedInstanceFieldGet());
bool object_field_get_with_read_barrier =
- kEmitCompilerReadBarrier && (field_info.GetFieldType() == DataType::Type::kReference);
+ gUseReadBarrier && (field_info.GetFieldType() == DataType::Type::kReference);
bool is_predicated = instruction->IsPredicatedInstanceFieldGet();
LocationSummary* locations =
new (GetGraph()->GetAllocator()) LocationSummary(instruction,
@@ -6082,7 +6170,7 @@
case DataType::Type::kReference: {
// /* HeapReference<Object> */ out = *(base + offset)
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
Location maybe_temp = (locations->GetTempCount() != 0) ? locations->GetTemp(0) : Location();
// Note that a potential implicit null check is handled in this
// CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier call.
@@ -6165,11 +6253,14 @@
}
void LocationsBuilderARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo());
+ HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetWriteBarrierKind());
}
void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
+ HandleFieldSet(instruction,
+ instruction->GetFieldInfo(),
+ instruction->GetValueCanBeNull(),
+ instruction->GetWriteBarrierKind());
}
void LocationsBuilderARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
@@ -6202,11 +6293,14 @@
}
void LocationsBuilderARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo());
+ HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetWriteBarrierKind());
}
void InstructionCodeGeneratorARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
+ HandleFieldSet(instruction,
+ instruction->GetFieldInfo(),
+ instruction->GetValueCanBeNull(),
+ instruction->GetWriteBarrierKind());
}
void LocationsBuilderARMVIXL::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
@@ -6386,7 +6480,7 @@
void LocationsBuilderARMVIXL::VisitArrayGet(HArrayGet* instruction) {
bool object_array_get_with_read_barrier =
- kEmitCompilerReadBarrier && (instruction->GetType() == DataType::Type::kReference);
+ gUseReadBarrier && (instruction->GetType() == DataType::Type::kReference);
LocationSummary* locations =
new (GetGraph()->GetAllocator()) LocationSummary(instruction,
object_array_get_with_read_barrier
@@ -6534,14 +6628,14 @@
// The read barrier instrumentation of object ArrayGet
// instructions does not support the HIntermediateAddress
// instruction.
- DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier));
+ DCHECK(!(has_intermediate_address && gUseReadBarrier));
static_assert(
sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
"art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
// /* HeapReference<Object> */ out =
// *(obj + data_offset + index * sizeof(HeapReference<Object>))
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// Note that a potential implicit null check is handled in this
// CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier call.
DCHECK(!instruction->CanDoImplicitNullCheckOn(instruction->InputAt(0)));
@@ -6688,8 +6782,10 @@
locations->SetInAt(2, Location::RequiresRegister());
}
if (needs_write_barrier) {
- // Temporary registers for the write barrier.
- locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
+ // Temporary registers for the write barrier or register poisoning.
+ // TODO(solanes): We could reduce the temp usage but it requires some non-trivial refactoring of
+ // InstructionCodeGeneratorARMVIXL::VisitArraySet.
+ locations->AddTemp(Location::RequiresRegister());
locations->AddTemp(Location::RequiresRegister());
}
}
@@ -6841,7 +6937,11 @@
}
}
- codegen_->MarkGCCard(temp1, temp2, array, value, /* value_can_be_null= */ false);
+ if (instruction->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit) {
+ DCHECK_EQ(instruction->GetWriteBarrierKind(), WriteBarrierKind::kEmitNoNullCheck)
+ << " Already null checked so we shouldn't do it again.";
+ codegen_->MarkGCCard(temp1, temp2, array, value, /* emit_null_check= */ false);
+ }
if (can_value_be_null) {
DCHECK(do_store.IsReferenced());
@@ -7072,9 +7172,9 @@
vixl32::Register card,
vixl32::Register object,
vixl32::Register value,
- bool value_can_be_null) {
+ bool emit_null_check) {
vixl32::Label is_null;
- if (value_can_be_null) {
+ if (emit_null_check) {
__ CompareAndBranchIfZero(value, &is_null, /* is_far_target=*/ false);
}
// Load the address of the card table into `card`.
@@ -7097,7 +7197,7 @@
// of the card to mark; and 2. to load the `kCardDirty` value) saves a load
// (no need to explicitly load `kCardDirty` as an immediate value).
__ Strb(card, MemOperand(card, temp));
- if (value_can_be_null) {
+ if (emit_null_check) {
__ Bind(&is_null);
}
}
@@ -7459,7 +7559,7 @@
load_kind == HLoadClass::LoadKind::kBssEntryPublic ||
load_kind == HLoadClass::LoadKind::kBssEntryPackage);
- const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
+ const bool requires_read_barrier = gUseReadBarrier && !cls->IsInBootImage();
LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall;
@@ -7473,7 +7573,7 @@
}
locations->SetOut(Location::RequiresRegister());
if (load_kind == HLoadClass::LoadKind::kBssEntry) {
- if (!kUseReadBarrier || kUseBakerReadBarrier) {
+ if (!gUseReadBarrier || kUseBakerReadBarrier) {
// Rely on the type resolution or initialization and marking to save everything we need.
locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
} else {
@@ -7501,7 +7601,7 @@
const ReadBarrierOption read_barrier_option = cls->IsInBootImage()
? kWithoutReadBarrier
- : kCompilerReadBarrierOption;
+ : gCompilerReadBarrierOption;
bool generate_null_check = false;
switch (load_kind) {
case HLoadClass::LoadKind::kReferrersClass: {
@@ -7622,12 +7722,7 @@
LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg) {
UseScratchRegisterScope temps(GetVIXLAssembler());
vixl32::Register temp = temps.Acquire();
- constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf();
- constexpr uint32_t shifted_visibly_initialized_value =
- enum_cast<uint32_t>(ClassStatus::kVisiblyInitialized) << status_lsb_position;
-
- const size_t status_offset = mirror::Class::StatusOffset().SizeValue();
- GetAssembler()->LoadFromOffset(kLoadWord, temp, class_reg, status_offset);
+ __ Ldrb(temp, MemOperand(class_reg, status_byte_offset));
__ Cmp(temp, shifted_visibly_initialized_value);
__ B(lo, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
@@ -7721,7 +7816,7 @@
} else {
locations->SetOut(Location::RequiresRegister());
if (load_kind == HLoadString::LoadKind::kBssEntry) {
- if (!kUseReadBarrier || kUseBakerReadBarrier) {
+ if (!gUseReadBarrier || kUseBakerReadBarrier) {
// Rely on the pResolveString and marking to save everything we need, including temps.
locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
} else {
@@ -7760,7 +7855,7 @@
codegen_->EmitMovwMovtPlaceholder(labels, out);
// All aligned loads are implicitly atomic consume operations on ARM.
codegen_->GenerateGcRootFieldLoad(
- load, out_loc, out, /*offset=*/ 0, kCompilerReadBarrierOption);
+ load, out_loc, out, /*offset=*/ 0, gCompilerReadBarrierOption);
LoadStringSlowPathARMVIXL* slow_path =
new (codegen_->GetScopedAllocator()) LoadStringSlowPathARMVIXL(load);
codegen_->AddSlowPath(slow_path);
@@ -7781,7 +7876,7 @@
load->GetString()));
// /* GcRoot<mirror::String> */ out = *out
codegen_->GenerateGcRootFieldLoad(
- load, out_loc, out, /*offset=*/ 0, kCompilerReadBarrierOption);
+ load, out_loc, out, /*offset=*/ 0, gCompilerReadBarrierOption);
return;
}
default:
@@ -7838,7 +7933,7 @@
// Temp is used for read barrier.
static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) {
- if (kEmitCompilerReadBarrier &&
+ if (gUseReadBarrier &&
(kUseBakerReadBarrier ||
type_check_kind == TypeCheckKind::kAbstractClassCheck ||
type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
@@ -8773,7 +8868,7 @@
ReadBarrierOption read_barrier_option) {
vixl32::Register out_reg = RegisterFrom(out);
if (read_barrier_option == kWithReadBarrier) {
- CHECK(kEmitCompilerReadBarrier);
+ CHECK(gUseReadBarrier);
DCHECK(maybe_temp.IsRegister()) << maybe_temp;
if (kUseBakerReadBarrier) {
// Load with fast path based Baker's read barrier.
@@ -8808,7 +8903,7 @@
vixl32::Register out_reg = RegisterFrom(out);
vixl32::Register obj_reg = RegisterFrom(obj);
if (read_barrier_option == kWithReadBarrier) {
- CHECK(kEmitCompilerReadBarrier);
+ CHECK(gUseReadBarrier);
if (kUseBakerReadBarrier) {
DCHECK(maybe_temp.IsRegister()) << maybe_temp;
// Load with fast path based Baker's read barrier.
@@ -8837,7 +8932,7 @@
ReadBarrierOption read_barrier_option) {
vixl32::Register root_reg = RegisterFrom(root);
if (read_barrier_option == kWithReadBarrier) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
if (kUseBakerReadBarrier) {
// Fast path implementation of art::ReadBarrier::BarrierForRoot when
// Baker's read barrier are used.
@@ -8901,7 +8996,7 @@
void CodeGeneratorARMVIXL::GenerateIntrinsicCasMoveWithBakerReadBarrier(
vixl::aarch32::Register marked_old_value,
vixl::aarch32::Register old_value) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
DCHECK(kUseBakerReadBarrier);
// Similar to the Baker RB path in GenerateGcRootFieldLoad(), with a MOV instead of LDR.
@@ -8935,7 +9030,7 @@
vixl32::Register obj,
const vixl32::MemOperand& src,
bool needs_null_check) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
DCHECK(kUseBakerReadBarrier);
// Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
@@ -9028,7 +9123,7 @@
Location index,
Location temp,
bool needs_null_check) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
DCHECK(kUseBakerReadBarrier);
static_assert(
@@ -9094,7 +9189,7 @@
void CodeGeneratorARMVIXL::MaybeGenerateMarkingRegisterCheck(int code, Location temp_loc) {
// The following condition is a compile-time one, so it does not have a run-time cost.
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier && kIsDebugBuild) {
+ if (kIsDebugBuild && gUseReadBarrier && kUseBakerReadBarrier) {
// The following condition is a run-time one; it is executed after the
// previous compile-time test, to avoid penalizing non-debug builds.
if (GetCompilerOptions().EmitRunTimeChecksInDebugMode()) {
@@ -9124,7 +9219,7 @@
Location obj,
uint32_t offset,
Location index) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
// Insert a slow path based read barrier *after* the reference load.
//
@@ -9150,7 +9245,7 @@
Location obj,
uint32_t offset,
Location index) {
- if (kEmitCompilerReadBarrier) {
+ if (gUseReadBarrier) {
// Baker's read barriers shall be handled by the fast path
// (CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier).
DCHECK(!kUseBakerReadBarrier);
@@ -9165,7 +9260,7 @@
void CodeGeneratorARMVIXL::GenerateReadBarrierForRootSlow(HInstruction* instruction,
Location out,
Location root) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
// Insert a slow path based read barrier *after* the GC root load.
//
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index 790ad0f..872a17b 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -18,6 +18,7 @@
#define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_ARM_VIXL_H_
#include "base/enums.h"
+#include "base/macros.h"
#include "class_root.h"
#include "code_generator.h"
#include "common_arm.h"
@@ -36,7 +37,7 @@
#include "aarch32/macro-assembler-aarch32.h"
#pragma GCC diagnostic pop
-namespace art {
+namespace art HIDDEN {
namespace linker {
class Thumb2RelativePatcherTest;
@@ -84,7 +85,7 @@
vixl::aarch32::r6,
vixl::aarch32::r7),
// Do not consider r8 as a callee-save register with Baker read barriers.
- ((kEmitCompilerReadBarrier && kUseBakerReadBarrier)
+ (kReserveMarkingRegister
? vixl::aarch32::RegisterList()
: vixl::aarch32::RegisterList(vixl::aarch32::r8)),
vixl::aarch32::RegisterList(vixl::aarch32::r10,
@@ -309,7 +310,9 @@
void HandleIntegerRotate(LocationSummary* locations);
void HandleLongRotate(LocationSummary* locations);
void HandleShift(HBinaryOperation* operation);
- void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
+ void HandleFieldSet(HInstruction* instruction,
+ const FieldInfo& field_info,
+ WriteBarrierKind write_barrier_kind);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
Location ArithmeticZeroOrFpuRegister(HInstruction* input);
@@ -378,7 +381,8 @@
void HandleFieldSet(HInstruction* instruction,
const FieldInfo& field_info,
- bool value_can_be_null);
+ bool value_can_be_null,
+ WriteBarrierKind write_barrier_kind);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
void GenerateMinMaxInt(LocationSummary* locations, bool is_min);
@@ -542,7 +546,7 @@
vixl::aarch32::Register card,
vixl::aarch32::Register object,
vixl::aarch32::Register value,
- bool value_can_be_null);
+ bool emit_null_check);
void GenerateMemoryBarrier(MemBarrierKind kind);
@@ -602,7 +606,6 @@
struct PcRelativePatchInfo {
PcRelativePatchInfo(const DexFile* dex_file, uint32_t off_or_idx)
: target_dex_file(dex_file), offset_or_index(off_or_idx) { }
- PcRelativePatchInfo(PcRelativePatchInfo&& other) = default;
// Target dex file or null for .data.bmig.rel.ro patches.
const DexFile* target_dex_file;
diff --git a/compiler/optimizing/code_generator_utils.cc b/compiler/optimizing/code_generator_utils.cc
index abec264..9980592 100644
--- a/compiler/optimizing/code_generator_utils.cc
+++ b/compiler/optimizing/code_generator_utils.cc
@@ -20,7 +20,7 @@
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
void CalculateMagicAndShiftForDivRem(int64_t divisor, bool is_long,
int64_t* magic, int* shift) {
diff --git a/compiler/optimizing/code_generator_utils.h b/compiler/optimizing/code_generator_utils.h
index 64665ad..9d9ab2b 100644
--- a/compiler/optimizing/code_generator_utils.h
+++ b/compiler/optimizing/code_generator_utils.h
@@ -21,7 +21,9 @@
#include <cstdlib>
#include <limits>
-namespace art {
+#include "base/macros.h"
+
+namespace art HIDDEN {
class HInstruction;
diff --git a/compiler/optimizing/code_generator_vector_arm64_neon.cc b/compiler/optimizing/code_generator_vector_arm64_neon.cc
index 0fe9898..c70c8f5 100644
--- a/compiler/optimizing/code_generator_vector_arm64_neon.cc
+++ b/compiler/optimizing/code_generator_vector_arm64_neon.cc
@@ -23,7 +23,7 @@
using namespace vixl::aarch64; // NOLINT(build/namespaces)
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
using helpers::DRegisterFrom;
diff --git a/compiler/optimizing/code_generator_vector_arm64_sve.cc b/compiler/optimizing/code_generator_vector_arm64_sve.cc
index 824b6c9..87b6d57 100644
--- a/compiler/optimizing/code_generator_vector_arm64_sve.cc
+++ b/compiler/optimizing/code_generator_vector_arm64_sve.cc
@@ -23,17 +23,14 @@
using namespace vixl::aarch64; // NOLINT(build/namespaces)
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
using helpers::DRegisterFrom;
-using helpers::HeapOperand;
using helpers::InputRegisterAt;
using helpers::Int64FromLocation;
using helpers::LocationFrom;
using helpers::OutputRegister;
-using helpers::QRegisterFrom;
-using helpers::StackOperandFrom;
using helpers::SveStackOperandFrom;
using helpers::VRegisterFrom;
using helpers::ZRegisterFrom;
diff --git a/compiler/optimizing/code_generator_vector_arm_vixl.cc b/compiler/optimizing/code_generator_vector_arm_vixl.cc
index c46f9b7..1359d89 100644
--- a/compiler/optimizing/code_generator_vector_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_vector_arm_vixl.cc
@@ -20,7 +20,7 @@
namespace vixl32 = vixl::aarch32;
using namespace vixl32; // NOLINT(build/namespaces)
-namespace art {
+namespace art HIDDEN {
namespace arm {
using helpers::DRegisterFrom;
diff --git a/compiler/optimizing/code_generator_vector_x86.cc b/compiler/optimizing/code_generator_vector_x86.cc
index 9c837dd..0e57604 100644
--- a/compiler/optimizing/code_generator_vector_x86.cc
+++ b/compiler/optimizing/code_generator_vector_x86.cc
@@ -19,7 +19,7 @@
#include "mirror/array-inl.h"
#include "mirror/string.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
diff --git a/compiler/optimizing/code_generator_vector_x86_64.cc b/compiler/optimizing/code_generator_vector_x86_64.cc
index 330bf76..10b84d6 100644
--- a/compiler/optimizing/code_generator_vector_x86_64.cc
+++ b/compiler/optimizing/code_generator_vector_x86_64.cc
@@ -19,7 +19,7 @@
#include "mirror/array-inl.h"
#include "mirror/string.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 8c6b802..58cb56d 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -20,7 +20,6 @@
#include "art_method-inl.h"
#include "class_table.h"
#include "code_generator_utils.h"
-#include "compiled_method.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "gc/accounting/card_table.h"
@@ -36,6 +35,7 @@
#include "mirror/array-inl.h"
#include "mirror/class-inl.h"
#include "mirror/var_handle.h"
+#include "optimizing/nodes.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
#include "utils/assembler.h"
@@ -43,7 +43,7 @@
#include "utils/x86/assembler_x86.h"
#include "utils/x86/managed_register_x86.h"
-namespace art {
+namespace art HIDDEN {
template<class MirrorType>
class GcRoot;
@@ -503,7 +503,7 @@
: SlowPathCode(instruction),
ref_(ref),
unpoison_ref_before_marking_(unpoison_ref_before_marking) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
}
const char* GetDescription() const override { return "ReadBarrierMarkSlowPathX86"; }
@@ -590,7 +590,7 @@
field_addr_(field_addr),
unpoison_ref_before_marking_(unpoison_ref_before_marking),
temp_(temp) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
}
const char* GetDescription() const override { return "ReadBarrierMarkAndUpdateFieldSlowPathX86"; }
@@ -744,7 +744,7 @@
obj_(obj),
offset_(offset),
index_(index) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
// If `obj` is equal to `out` or `ref`, it means the initial object
// has been overwritten by (or after) the heap object reference load
// to be instrumented, e.g.:
@@ -918,7 +918,7 @@
public:
ReadBarrierForRootSlowPathX86(HInstruction* instruction, Location out, Location root)
: SlowPathCode(instruction), out_(out), root_(root) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
}
void EmitNativeCode(CodeGenerator* codegen) override {
@@ -967,6 +967,9 @@
(instruction_->IsMethodEntryHook()) ? kQuickMethodEntryHook : kQuickMethodExitHook;
__ Bind(GetEntryLabel());
SaveLiveRegisters(codegen, locations);
+ if (instruction_->IsMethodExitHook()) {
+ __ movl(EBX, Immediate(codegen->GetFrameSize()));
+ }
x86_codegen->InvokeRuntime(entry_point, instruction_, instruction_->GetDexPc(), this);
RestoreLiveRegisters(codegen, locations);
__ jmp(GetExitLabel());
@@ -1197,9 +1200,21 @@
new (codegen_->GetScopedAllocator()) MethodEntryExitHooksSlowPathX86(instruction);
codegen_->AddSlowPath(slow_path);
+ if (instruction->IsMethodExitHook()) {
+ // Check if we are required to check if the caller needs a deoptimization. Strictly speaking it
+ // would be sufficient to check if CheckCallerForDeopt bit is set. Though it is faster to check
+ // if it is just non-zero. kCHA bit isn't used in debuggable runtimes as cha optimization is
+ // disabled in debuggable runtime. The other bit is used when this method itself requires a
+ // deoptimization due to redefinition. So it is safe to just check for non-zero value here.
+ __ cmpl(Address(ESP, codegen_->GetStackOffsetOfShouldDeoptimizeFlag()), Immediate(0));
+ __ j(kNotEqual, slow_path->GetEntryLabel());
+ }
+
uint64_t address = reinterpret_cast64<uint64_t>(Runtime::Current()->GetInstrumentation());
- int offset = instrumentation::Instrumentation::NeedsEntryExitHooksOffset().Int32Value();
- __ cmpb(Address::Absolute(address + offset), Immediate(0));
+ MemberOffset offset = instruction->IsMethodExitHook() ?
+ instrumentation::Instrumentation::HaveMethodExitListenersOffset() :
+ instrumentation::Instrumentation::HaveMethodEntryListenersOffset();
+ __ cmpb(Address::Absolute(address + offset.Int32Value()), Immediate(0));
__ j(kNotEqual, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
}
@@ -1261,6 +1276,44 @@
void CodeGeneratorX86::GenerateFrameEntry() {
__ cfi().SetCurrentCFAOffset(kX86WordSize); // return address
+
+ // Check if we need to generate the clinit check. We will jump to the
+ // resolution stub if the class is not initialized and the executing thread is
+ // not the thread initializing it.
+ // We do this before constructing the frame to get the correct stack trace if
+ // an exception is thrown.
+ if (GetCompilerOptions().ShouldCompileWithClinitCheck(GetGraph()->GetArtMethod())) {
+ NearLabel continue_execution, resolution;
+ // We'll use EBP as temporary.
+ __ pushl(EBP);
+ // Check if we're visibly initialized.
+
+ // We don't emit a read barrier here to save on code size. We rely on the
+ // resolution trampoline to do a suspend check before re-entering this code.
+ __ movl(EBP, Address(kMethodRegisterArgument, ArtMethod::DeclaringClassOffset().Int32Value()));
+ __ cmpb(Address(EBP, status_byte_offset), Immediate(shifted_visibly_initialized_value));
+ __ j(kAboveEqual, &continue_execution);
+
+ // Check if we're initializing and the thread initializing is the one
+ // executing the code.
+ __ cmpb(Address(EBP, status_byte_offset), Immediate(shifted_initializing_value));
+ __ j(kBelow, &resolution);
+
+ __ movl(EBP, Address(EBP, mirror::Class::ClinitThreadIdOffset().Int32Value()));
+ __ fs()->cmpl(EBP, Address::Absolute(Thread::TidOffset<kX86PointerSize>().Int32Value()));
+ __ j(kEqual, &continue_execution);
+ __ Bind(&resolution);
+
+ __ popl(EBP);
+ // Jump to the resolution stub.
+ ThreadOffset32 entrypoint_offset =
+ GetThreadOffset<kX86PointerSize>(kQuickQuickResolutionTrampoline);
+ __ fs()->jmp(Address::Absolute(entrypoint_offset));
+
+ __ Bind(&continue_execution);
+ __ popl(EBP);
+ }
+
__ Bind(&frame_entry_label_);
bool skip_overflow_check =
IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86);
@@ -1619,7 +1672,7 @@
__ movsd(dst.AsFpuRegister<XmmRegister>(), src);
break;
case DataType::Type::kReference:
- DCHECK(!kEmitCompilerReadBarrier);
+ DCHECK(!gUseReadBarrier);
__ movl(dst.AsRegister<Register>(), src);
__ MaybeUnpoisonHeapReference(dst.AsRegister<Register>());
break;
@@ -2230,12 +2283,12 @@
}
}
-void LocationsBuilderX86::VisitNativeDebugInfo(HNativeDebugInfo* info) {
- new (GetGraph()->GetAllocator()) LocationSummary(info);
+void LocationsBuilderX86::VisitNop(HNop* nop) {
+ new (GetGraph()->GetAllocator()) LocationSummary(nop);
}
-void InstructionCodeGeneratorX86::VisitNativeDebugInfo(HNativeDebugInfo*) {
- // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
+void InstructionCodeGeneratorX86::VisitNop(HNop*) {
+ // The environment recording already happened in CodeGenerator::Compile.
}
void CodeGeneratorX86::IncreaseFrame(size_t adjustment) {
@@ -5689,13 +5742,10 @@
DCHECK_EQ(size, linker_patches->size());
}
-void CodeGeneratorX86::MarkGCCard(Register temp,
- Register card,
- Register object,
- Register value,
- bool value_can_be_null) {
+void CodeGeneratorX86::MarkGCCard(
+ Register temp, Register card, Register object, Register value, bool emit_null_check) {
NearLabel is_null;
- if (value_can_be_null) {
+ if (emit_null_check) {
__ testl(value, value);
__ j(kEqual, &is_null);
}
@@ -5720,7 +5770,7 @@
// (no need to explicitly load `kCardDirty` as an immediate value).
__ movb(Address(temp, card, TIMES_1, 0),
X86ManagedRegister::FromCpuRegister(card).AsByteRegister());
- if (value_can_be_null) {
+ if (emit_null_check) {
__ Bind(&is_null);
}
}
@@ -5731,11 +5781,11 @@
instruction->IsPredicatedInstanceFieldGet());
bool object_field_get_with_read_barrier =
- kEmitCompilerReadBarrier && (instruction->GetType() == DataType::Type::kReference);
+ gUseReadBarrier && (instruction->GetType() == DataType::Type::kReference);
bool is_predicated = instruction->IsPredicatedInstanceFieldGet();
LocationSummary* locations =
new (GetGraph()->GetAllocator()) LocationSummary(instruction,
- kEmitCompilerReadBarrier
+ gUseReadBarrier
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall);
if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
@@ -5793,7 +5843,7 @@
if (load_type == DataType::Type::kReference) {
// /* HeapReference<Object> */ out = *(base + offset)
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// Note that a potential implicit null check is handled in this
// CodeGeneratorX86::GenerateFieldLoadWithBakerReadBarrier call.
codegen_->GenerateFieldLoadWithBakerReadBarrier(
@@ -5824,7 +5874,9 @@
}
}
-void LocationsBuilderX86::HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info) {
+void LocationsBuilderX86::HandleFieldSet(HInstruction* instruction,
+ const FieldInfo& field_info,
+ WriteBarrierKind write_barrier_kind) {
DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
LocationSummary* locations =
@@ -5861,10 +5913,13 @@
locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
- // Temporary registers for the write barrier.
- locations->AddTemp(Location::RequiresRegister()); // May be used for reference poisoning too.
- // Ensure the card is in a byte register.
- locations->AddTemp(Location::RegisterLocation(ECX));
+ if (write_barrier_kind != WriteBarrierKind::kDontEmit) {
+ locations->AddTemp(Location::RequiresRegister());
+ // Ensure the card is in a byte register.
+ locations->AddTemp(Location::RegisterLocation(ECX));
+ } else if (kPoisonHeapReferences) {
+ locations->AddTemp(Location::RequiresRegister());
+ }
}
}
}
@@ -5875,7 +5930,8 @@
Address field_addr,
Register base,
bool is_volatile,
- bool value_can_be_null) {
+ bool value_can_be_null,
+ WriteBarrierKind write_barrier_kind) {
LocationSummary* locations = instruction->GetLocations();
Location value = locations->InAt(value_index);
bool needs_write_barrier =
@@ -5988,10 +6044,15 @@
codegen_->MaybeRecordImplicitNullCheck(instruction);
}
- if (needs_write_barrier) {
+ if (needs_write_barrier && write_barrier_kind != WriteBarrierKind::kDontEmit) {
Register temp = locations->GetTemp(0).AsRegister<Register>();
Register card = locations->GetTemp(1).AsRegister<Register>();
- codegen_->MarkGCCard(temp, card, base, value.AsRegister<Register>(), value_can_be_null);
+ codegen_->MarkGCCard(
+ temp,
+ card,
+ base,
+ value.AsRegister<Register>(),
+ value_can_be_null && write_barrier_kind == WriteBarrierKind::kEmitWithNullCheck);
}
if (is_volatile) {
@@ -6001,7 +6062,8 @@
void InstructionCodeGeneratorX86::HandleFieldSet(HInstruction* instruction,
const FieldInfo& field_info,
- bool value_can_be_null) {
+ bool value_can_be_null,
+ WriteBarrierKind write_barrier_kind) {
DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
LocationSummary* locations = instruction->GetLocations();
@@ -6026,7 +6088,8 @@
field_addr,
base,
is_volatile,
- value_can_be_null);
+ value_can_be_null,
+ write_barrier_kind);
if (is_predicated) {
__ Bind(&pred_is_null);
@@ -6042,19 +6105,25 @@
}
void LocationsBuilderX86::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo());
+ HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetWriteBarrierKind());
}
void InstructionCodeGeneratorX86::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
+ HandleFieldSet(instruction,
+ instruction->GetFieldInfo(),
+ instruction->GetValueCanBeNull(),
+ instruction->GetWriteBarrierKind());
}
void LocationsBuilderX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo());
+ HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetWriteBarrierKind());
}
void InstructionCodeGeneratorX86::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
+ HandleFieldSet(instruction,
+ instruction->GetFieldInfo(),
+ instruction->GetValueCanBeNull(),
+ instruction->GetWriteBarrierKind());
}
void LocationsBuilderX86::VisitPredicatedInstanceFieldGet(
@@ -6202,7 +6271,7 @@
void LocationsBuilderX86::VisitArrayGet(HArrayGet* instruction) {
bool object_array_get_with_read_barrier =
- kEmitCompilerReadBarrier && (instruction->GetType() == DataType::Type::kReference);
+ gUseReadBarrier && (instruction->GetType() == DataType::Type::kReference);
LocationSummary* locations =
new (GetGraph()->GetAllocator()) LocationSummary(instruction,
object_array_get_with_read_barrier
@@ -6244,7 +6313,7 @@
"art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
// /* HeapReference<Object> */ out =
// *(obj + data_offset + index * sizeof(HeapReference<Object>))
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// Note that a potential implicit null check is handled in this
// CodeGeneratorX86::GenerateArrayLoadWithBakerReadBarrier call.
codegen_->GenerateArrayLoadWithBakerReadBarrier(
@@ -6315,10 +6384,12 @@
locations->SetInAt(2, Location::RegisterOrConstant(instruction->InputAt(2)));
}
if (needs_write_barrier) {
- // Temporary registers for the write barrier.
- locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
- // Ensure the card is in a byte register.
- locations->AddTemp(Location::RegisterLocation(ECX));
+ // Used by reference poisoning or emitting write barrier.
+ locations->AddTemp(Location::RequiresRegister());
+ if (instruction->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit) {
+ // Only used when emitting a write barrier. Ensure the card is in a byte register.
+ locations->AddTemp(Location::RegisterLocation(ECX));
+ }
}
}
@@ -6435,9 +6506,16 @@
}
}
- Register card = locations->GetTemp(1).AsRegister<Register>();
- codegen_->MarkGCCard(
- temp, card, array, value.AsRegister<Register>(), /* value_can_be_null= */ false);
+ if (instruction->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit) {
+ DCHECK_EQ(instruction->GetWriteBarrierKind(), WriteBarrierKind::kEmitNoNullCheck)
+ << " Already null checked so we shouldn't do it again.";
+ Register card = locations->GetTemp(1).AsRegister<Register>();
+ codegen_->MarkGCCard(temp,
+ card,
+ array,
+ value.AsRegister<Register>(),
+ /* emit_null_check= */ false);
+ }
if (can_value_be_null) {
DCHECK(do_store.IsLinked());
@@ -7057,7 +7135,7 @@
load_kind == HLoadClass::LoadKind::kBssEntryPublic ||
load_kind == HLoadClass::LoadKind::kBssEntryPackage);
- const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
+ const bool requires_read_barrier = gUseReadBarrier && !cls->IsInBootImage();
LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall;
@@ -7071,7 +7149,7 @@
}
locations->SetOut(Location::RequiresRegister());
if (call_kind == LocationSummary::kCallOnSlowPath && cls->HasPcRelativeLoadKind()) {
- if (!kUseReadBarrier || kUseBakerReadBarrier) {
+ if (!gUseReadBarrier || kUseBakerReadBarrier) {
// Rely on the type resolution and/or initialization to save everything.
locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
} else {
@@ -7109,7 +7187,7 @@
bool generate_null_check = false;
const ReadBarrierOption read_barrier_option = cls->IsInBootImage()
? kWithoutReadBarrier
- : kCompilerReadBarrierOption;
+ : gCompilerReadBarrierOption;
switch (load_kind) {
case HLoadClass::LoadKind::kReferrersClass: {
DCHECK(!cls->CanCallRuntime());
@@ -7233,12 +7311,6 @@
void InstructionCodeGeneratorX86::GenerateClassInitializationCheck(
SlowPathCode* slow_path, Register class_reg) {
- constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf();
- const size_t status_byte_offset =
- mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte);
- constexpr uint32_t shifted_visibly_initialized_value =
- enum_cast<uint32_t>(ClassStatus::kVisiblyInitialized) << (status_lsb_position % kBitsPerByte);
-
__ cmpb(Address(class_reg, status_byte_offset), Immediate(shifted_visibly_initialized_value));
__ j(kBelow, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
@@ -7296,7 +7368,7 @@
} else {
locations->SetOut(Location::RequiresRegister());
if (load_kind == HLoadString::LoadKind::kBssEntry) {
- if (!kUseReadBarrier || kUseBakerReadBarrier) {
+ if (!gUseReadBarrier || kUseBakerReadBarrier) {
// Rely on the pResolveString to save everything.
locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
} else {
@@ -7345,7 +7417,7 @@
Address address = Address(method_address, CodeGeneratorX86::kPlaceholder32BitOffset);
Label* fixup_label = codegen_->NewStringBssEntryPatch(load);
// /* GcRoot<mirror::String> */ out = *address /* PC-relative */
- GenerateGcRootFieldLoad(load, out_loc, address, fixup_label, kCompilerReadBarrierOption);
+ GenerateGcRootFieldLoad(load, out_loc, address, fixup_label, gCompilerReadBarrierOption);
// No need for memory fence, thanks to the x86 memory model.
SlowPathCode* slow_path = new (codegen_->GetScopedAllocator()) LoadStringSlowPathX86(load);
codegen_->AddSlowPath(slow_path);
@@ -7365,7 +7437,7 @@
Label* fixup_label = codegen_->NewJitRootStringPatch(
load->GetDexFile(), load->GetStringIndex(), load->GetString());
// /* GcRoot<mirror::String> */ out = *address
- GenerateGcRootFieldLoad(load, out_loc, address, fixup_label, kCompilerReadBarrierOption);
+ GenerateGcRootFieldLoad(load, out_loc, address, fixup_label, gCompilerReadBarrierOption);
return;
}
default:
@@ -7416,7 +7488,7 @@
// Temp is used for read barrier.
static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) {
- if (kEmitCompilerReadBarrier &&
+ if (gUseReadBarrier &&
!kUseBakerReadBarrier &&
(type_check_kind == TypeCheckKind::kAbstractClassCheck ||
type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
@@ -8188,7 +8260,7 @@
ReadBarrierOption read_barrier_option) {
Register out_reg = out.AsRegister<Register>();
if (read_barrier_option == kWithReadBarrier) {
- CHECK(kEmitCompilerReadBarrier);
+ CHECK(gUseReadBarrier);
if (kUseBakerReadBarrier) {
// Load with fast path based Baker's read barrier.
// /* HeapReference<Object> */ out = *(out + offset)
@@ -8222,7 +8294,7 @@
Register out_reg = out.AsRegister<Register>();
Register obj_reg = obj.AsRegister<Register>();
if (read_barrier_option == kWithReadBarrier) {
- CHECK(kEmitCompilerReadBarrier);
+ CHECK(gUseReadBarrier);
if (kUseBakerReadBarrier) {
// Load with fast path based Baker's read barrier.
// /* HeapReference<Object> */ out = *(obj + offset)
@@ -8250,7 +8322,7 @@
ReadBarrierOption read_barrier_option) {
Register root_reg = root.AsRegister<Register>();
if (read_barrier_option == kWithReadBarrier) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
if (kUseBakerReadBarrier) {
// Fast path implementation of art::ReadBarrier::BarrierForRoot when
// Baker's read barrier are used:
@@ -8314,7 +8386,7 @@
Register obj,
uint32_t offset,
bool needs_null_check) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
DCHECK(kUseBakerReadBarrier);
// /* HeapReference<Object> */ ref = *(obj + offset)
@@ -8328,7 +8400,7 @@
uint32_t data_offset,
Location index,
bool needs_null_check) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
DCHECK(kUseBakerReadBarrier);
static_assert(
@@ -8347,7 +8419,7 @@
bool needs_null_check,
bool always_update_field,
Register* temp) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
DCHECK(kUseBakerReadBarrier);
// In slow path based read barriers, the read barrier call is
@@ -8428,7 +8500,7 @@
Location obj,
uint32_t offset,
Location index) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
// Insert a slow path based read barrier *after* the reference load.
//
@@ -8455,7 +8527,7 @@
Location obj,
uint32_t offset,
Location index) {
- if (kEmitCompilerReadBarrier) {
+ if (gUseReadBarrier) {
// Baker's read barriers shall be handled by the fast path
// (CodeGeneratorX86::GenerateReferenceLoadWithBakerReadBarrier).
DCHECK(!kUseBakerReadBarrier);
@@ -8470,7 +8542,7 @@
void CodeGeneratorX86::GenerateReadBarrierForRootSlow(HInstruction* instruction,
Location out,
Location root) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
// Insert a slow path based read barrier *after* the GC root load.
//
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 75c5ceb..9f09e17 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -19,6 +19,7 @@
#include "arch/x86/instruction_set_features_x86.h"
#include "base/enums.h"
+#include "base/macros.h"
#include "code_generator.h"
#include "dex/dex_file_types.h"
#include "driver/compiler_options.h"
@@ -26,7 +27,7 @@
#include "parallel_move_resolver.h"
#include "utils/x86/assembler_x86.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
// Use a local definition to prevent copying mistakes.
@@ -196,7 +197,9 @@
void HandleInvoke(HInvoke* invoke);
void HandleCondition(HCondition* condition);
void HandleShift(HBinaryOperation* instruction);
- void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info);
+ void HandleFieldSet(HInstruction* instruction,
+ const FieldInfo& field_info,
+ WriteBarrierKind write_barrier_kind);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
bool CpuHasAvxFeatureFlag();
bool CpuHasAvx2FeatureFlag();
@@ -249,7 +252,8 @@
Address field_addr,
Register base,
bool is_volatile,
- bool value_can_be_null);
+ bool value_can_be_null,
+ WriteBarrierKind write_barrier_kind);
private:
// Generate code for the given suspend check. If not null, `successor`
@@ -279,7 +283,8 @@
void HandleFieldSet(HInstruction* instruction,
const FieldInfo& field_info,
- bool value_can_be_null);
+ bool value_can_be_null,
+ WriteBarrierKind write_barrier_kind);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
// Generate a heap reference load using one register `out`:
@@ -519,11 +524,8 @@
void EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) override;
// Emit a write barrier.
- void MarkGCCard(Register temp,
- Register card,
- Register object,
- Register value,
- bool value_can_be_null);
+ void MarkGCCard(
+ Register temp, Register card, Register object, Register value, bool emit_null_check);
void GenerateMemoryBarrier(MemBarrierKind kind);
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 511917a..2d7dc44 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -21,7 +21,6 @@
#include "class_root-inl.h"
#include "class_table.h"
#include "code_generator_utils.h"
-#include "compiled_method.h"
#include "entrypoints/quick/quick_entrypoints.h"
#include "gc/accounting/card_table.h"
#include "gc/space/image_space.h"
@@ -37,6 +36,7 @@
#include "mirror/class-inl.h"
#include "mirror/object_reference.h"
#include "mirror/var_handle.h"
+#include "optimizing/nodes.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
#include "utils/assembler.h"
@@ -45,7 +45,7 @@
#include "utils/x86_64/constants_x86_64.h"
#include "utils/x86_64/managed_register_x86_64.h"
-namespace art {
+namespace art HIDDEN {
template<class MirrorType>
class GcRoot;
@@ -510,7 +510,7 @@
: SlowPathCode(instruction),
ref_(ref),
unpoison_ref_before_marking_(unpoison_ref_before_marking) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
}
const char* GetDescription() const override { return "ReadBarrierMarkSlowPathX86_64"; }
@@ -601,7 +601,7 @@
unpoison_ref_before_marking_(unpoison_ref_before_marking),
temp1_(temp1),
temp2_(temp2) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
}
const char* GetDescription() const override {
@@ -761,7 +761,7 @@
obj_(obj),
offset_(offset),
index_(index) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
// If `obj` is equal to `out` or `ref`, it means the initial
// object has been overwritten by (or after) the heap object
// reference load to be instrumented, e.g.:
@@ -937,7 +937,7 @@
public:
ReadBarrierForRootSlowPathX86_64(HInstruction* instruction, Location out, Location root)
: SlowPathCode(instruction), out_(out), root_(root) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
}
void EmitNativeCode(CodeGenerator* codegen) override {
@@ -986,6 +986,10 @@
(instruction_->IsMethodEntryHook()) ? kQuickMethodEntryHook : kQuickMethodExitHook;
__ Bind(GetEntryLabel());
SaveLiveRegisters(codegen, locations);
+ if (instruction_->IsMethodExitHook()) {
+ // Load FrameSize to pass to the exit hook.
+ __ movq(CpuRegister(R8), Immediate(codegen->GetFrameSize()));
+ }
x86_64_codegen->InvokeRuntime(entry_point, instruction_, instruction_->GetDexPc(), this);
RestoreLiveRegisters(codegen, locations);
__ jmp(GetExitLabel());
@@ -1561,9 +1565,22 @@
new (codegen_->GetScopedAllocator()) MethodEntryExitHooksSlowPathX86_64(instruction);
codegen_->AddSlowPath(slow_path);
+ if (instruction->IsMethodExitHook()) {
+ // Check if we are required to check if the caller needs a deoptimization. Strictly speaking it
+ // would be sufficient to check if CheckCallerForDeopt bit is set. Though it is faster to check
+ // if it is just non-zero. kCHA bit isn't used in debuggable runtimes as cha optimization is
+ // disabled in debuggable runtime. The other bit is used when this method itself requires a
+ // deoptimization due to redefinition. So it is safe to just check for non-zero value here.
+ __ cmpl(Address(CpuRegister(RSP), codegen_->GetStackOffsetOfShouldDeoptimizeFlag()),
+ Immediate(0));
+ __ j(kNotEqual, slow_path->GetEntryLabel());
+ }
+
uint64_t address = reinterpret_cast64<uint64_t>(Runtime::Current()->GetInstrumentation());
- int offset = instrumentation::Instrumentation::NeedsEntryExitHooksOffset().Int32Value();
- __ movq(CpuRegister(TMP), Immediate(address + offset));
+ MemberOffset offset = instruction->IsMethodExitHook() ?
+ instrumentation::Instrumentation::HaveMethodExitListenersOffset()
+ : instrumentation::Instrumentation::HaveMethodEntryListenersOffset();
+ __ movq(CpuRegister(TMP), Immediate(address + offset.Int32Value()));
__ cmpb(Address(CpuRegister(TMP), 0), Immediate(0));
__ j(kNotEqual, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
@@ -1653,6 +1670,44 @@
void CodeGeneratorX86_64::GenerateFrameEntry() {
__ cfi().SetCurrentCFAOffset(kX86_64WordSize); // return address
+
+ // Check if we need to generate the clinit check. We will jump to the
+ // resolution stub if the class is not initialized and the executing thread is
+ // not the thread initializing it.
+ // We do this before constructing the frame to get the correct stack trace if
+ // an exception is thrown.
+ if (GetCompilerOptions().ShouldCompileWithClinitCheck(GetGraph()->GetArtMethod())) {
+ NearLabel resolution;
+ // Check if we're visibly initialized.
+
+ // We don't emit a read barrier here to save on code size. We rely on the
+ // resolution trampoline to do a suspend check before re-entering this code.
+ __ movl(CpuRegister(TMP),
+ Address(CpuRegister(kMethodRegisterArgument),
+ ArtMethod::DeclaringClassOffset().Int32Value()));
+ __ cmpb(Address(CpuRegister(TMP), status_byte_offset),
+ Immediate(shifted_visibly_initialized_value));
+ __ j(kAboveEqual, &frame_entry_label_);
+
+ // Check if we're initializing and the thread initializing is the one
+ // executing the code.
+ __ cmpb(Address(CpuRegister(TMP), status_byte_offset), Immediate(shifted_initializing_value));
+ __ j(kBelow, &resolution);
+
+ __ movl(CpuRegister(TMP),
+ Address(CpuRegister(TMP), mirror::Class::ClinitThreadIdOffset().Int32Value()));
+ __ gs()->cmpl(
+ CpuRegister(TMP),
+ Address::Absolute(Thread::TidOffset<kX86_64PointerSize>().Int32Value(), /*no_rip=*/ true));
+ __ j(kEqual, &frame_entry_label_);
+ __ Bind(&resolution);
+
+ // Jump to the resolution stub.
+ ThreadOffset64 entrypoint_offset =
+ GetThreadOffset<kX86_64PointerSize>(kQuickQuickResolutionTrampoline);
+ __ gs()->jmp(Address::Absolute(entrypoint_offset, /*no_rip=*/ true));
+ }
+
__ Bind(&frame_entry_label_);
bool skip_overflow_check = IsLeafMethod()
&& !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86_64);
@@ -2274,12 +2329,12 @@
}
}
-void LocationsBuilderX86_64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
- new (GetGraph()->GetAllocator()) LocationSummary(info);
+void LocationsBuilderX86_64::VisitNop(HNop* nop) {
+ new (GetGraph()->GetAllocator()) LocationSummary(nop);
}
-void InstructionCodeGeneratorX86_64::VisitNativeDebugInfo(HNativeDebugInfo*) {
- // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
+void InstructionCodeGeneratorX86_64::VisitNop(HNop*) {
+ // The environment recording already happened in CodeGenerator::Compile.
}
void CodeGeneratorX86_64::IncreaseFrame(size_t adjustment) {
@@ -5013,7 +5068,7 @@
instruction->IsPredicatedInstanceFieldGet());
bool object_field_get_with_read_barrier =
- kEmitCompilerReadBarrier && (instruction->GetType() == DataType::Type::kReference);
+ gUseReadBarrier && (instruction->GetType() == DataType::Type::kReference);
bool is_predicated = instruction->IsPredicatedInstanceFieldGet();
LocationSummary* locations =
new (GetGraph()->GetAllocator()) LocationSummary(instruction,
@@ -5064,7 +5119,7 @@
if (load_type == DataType::Type::kReference) {
// /* HeapReference<Object> */ out = *(base + offset)
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// Note that a potential implicit null check is handled in this
// CodeGeneratorX86_64::GenerateFieldLoadWithBakerReadBarrier call.
codegen_->GenerateFieldLoadWithBakerReadBarrier(
@@ -5119,6 +5174,9 @@
locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
}
}
+
+ // TODO(solanes): We could reduce the temp usage but it requires some non-trivial refactoring of
+ // InstructionCodeGeneratorX86_64::HandleFieldSet.
if (needs_write_barrier) {
// Temporary registers for the write barrier.
locations->AddTemp(Location::RequiresRegister());
@@ -5180,7 +5238,8 @@
bool is_volatile,
bool is_atomic,
bool value_can_be_null,
- bool byte_swap) {
+ bool byte_swap,
+ WriteBarrierKind write_barrier_kind) {
LocationSummary* locations = instruction->GetLocations();
Location value = locations->InAt(value_index);
@@ -5298,10 +5357,16 @@
codegen_->MaybeRecordImplicitNullCheck(instruction);
}
- if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(value_index))) {
+ if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(value_index)) &&
+ write_barrier_kind != WriteBarrierKind::kDontEmit) {
CpuRegister temp = locations->GetTemp(0).AsRegister<CpuRegister>();
CpuRegister card = locations->GetTemp(extra_temp_index).AsRegister<CpuRegister>();
- codegen_->MarkGCCard(temp, card, base, value.AsRegister<CpuRegister>(), value_can_be_null);
+ codegen_->MarkGCCard(
+ temp,
+ card,
+ base,
+ value.AsRegister<CpuRegister>(),
+ value_can_be_null && write_barrier_kind == WriteBarrierKind::kEmitWithNullCheck);
}
if (is_volatile) {
@@ -5311,7 +5376,8 @@
void InstructionCodeGeneratorX86_64::HandleFieldSet(HInstruction* instruction,
const FieldInfo& field_info,
- bool value_can_be_null) {
+ bool value_can_be_null,
+ WriteBarrierKind write_barrier_kind) {
DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
LocationSummary* locations = instruction->GetLocations();
@@ -5336,7 +5402,9 @@
base,
is_volatile,
/*is_atomic=*/ false,
- value_can_be_null);
+ value_can_be_null,
+ /*byte_swap=*/ false,
+ write_barrier_kind);
if (is_predicated) {
__ Bind(&pred_is_null);
@@ -5348,7 +5416,10 @@
}
void InstructionCodeGeneratorX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
+ HandleFieldSet(instruction,
+ instruction->GetFieldInfo(),
+ instruction->GetValueCanBeNull(),
+ instruction->GetWriteBarrierKind());
}
void LocationsBuilderX86_64::VisitPredicatedInstanceFieldGet(
@@ -5388,7 +5459,10 @@
}
void InstructionCodeGeneratorX86_64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
- HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
+ HandleFieldSet(instruction,
+ instruction->GetFieldInfo(),
+ instruction->GetValueCanBeNull(),
+ instruction->GetWriteBarrierKind());
}
void LocationsBuilderX86_64::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
@@ -5513,7 +5587,7 @@
void LocationsBuilderX86_64::VisitArrayGet(HArrayGet* instruction) {
bool object_array_get_with_read_barrier =
- kEmitCompilerReadBarrier && (instruction->GetType() == DataType::Type::kReference);
+ gUseReadBarrier && (instruction->GetType() == DataType::Type::kReference);
LocationSummary* locations =
new (GetGraph()->GetAllocator()) LocationSummary(instruction,
object_array_get_with_read_barrier
@@ -5551,7 +5625,7 @@
"art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
// /* HeapReference<Object> */ out =
// *(obj + data_offset + index * sizeof(HeapReference<Object>))
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// Note that a potential implicit null check is handled in this
// CodeGeneratorX86_64::GenerateArrayLoadWithBakerReadBarrier call.
codegen_->GenerateArrayLoadWithBakerReadBarrier(
@@ -5619,9 +5693,12 @@
}
if (needs_write_barrier) {
- // Temporary registers for the write barrier.
- locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
+ // Used by reference poisoning or emitting write barrier.
locations->AddTemp(Location::RequiresRegister());
+ if (instruction->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit) {
+ // Only used when emitting a write barrier.
+ locations->AddTemp(Location::RequiresRegister());
+ }
}
}
@@ -5739,9 +5816,16 @@
}
}
- CpuRegister card = locations->GetTemp(1).AsRegister<CpuRegister>();
- codegen_->MarkGCCard(
- temp, card, array, value.AsRegister<CpuRegister>(), /* value_can_be_null= */ false);
+ if (instruction->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit) {
+ DCHECK_EQ(instruction->GetWriteBarrierKind(), WriteBarrierKind::kEmitNoNullCheck)
+ << " Already null checked so we shouldn't do it again.";
+ CpuRegister card = locations->GetTemp(1).AsRegister<CpuRegister>();
+ codegen_->MarkGCCard(temp,
+ card,
+ array,
+ value.AsRegister<CpuRegister>(),
+ /* emit_null_check= */ false);
+ }
if (can_value_be_null) {
DCHECK(do_store.IsLinked());
@@ -5940,9 +6024,9 @@
CpuRegister card,
CpuRegister object,
CpuRegister value,
- bool value_can_be_null) {
+ bool emit_null_check) {
NearLabel is_null;
- if (value_can_be_null) {
+ if (emit_null_check) {
__ testl(value, value);
__ j(kEqual, &is_null);
}
@@ -5967,7 +6051,7 @@
// of the card to mark; and 2. to load the `kCardDirty` value) saves a load
// (no need to explicitly load `kCardDirty` as an immediate value).
__ movb(Address(temp, card, TIMES_1, 0), card);
- if (value_can_be_null) {
+ if (emit_null_check) {
__ Bind(&is_null);
}
}
@@ -6282,12 +6366,6 @@
void InstructionCodeGeneratorX86_64::GenerateClassInitializationCheck(
SlowPathCode* slow_path, CpuRegister class_reg) {
- constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf();
- const size_t status_byte_offset =
- mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte);
- constexpr uint32_t shifted_visibly_initialized_value =
- enum_cast<uint32_t>(ClassStatus::kVisiblyInitialized) << (status_lsb_position % kBitsPerByte);
-
__ cmpb(Address(class_reg, status_byte_offset), Immediate(shifted_visibly_initialized_value));
__ j(kBelow, slow_path->GetEntryLabel());
__ Bind(slow_path->GetExitLabel());
@@ -6352,7 +6430,7 @@
load_kind == HLoadClass::LoadKind::kBssEntryPublic ||
load_kind == HLoadClass::LoadKind::kBssEntryPackage);
- const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
+ const bool requires_read_barrier = gUseReadBarrier && !cls->IsInBootImage();
LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
? LocationSummary::kCallOnSlowPath
: LocationSummary::kNoCall;
@@ -6366,7 +6444,7 @@
}
locations->SetOut(Location::RequiresRegister());
if (load_kind == HLoadClass::LoadKind::kBssEntry) {
- if (!kUseReadBarrier || kUseBakerReadBarrier) {
+ if (!gUseReadBarrier || kUseBakerReadBarrier) {
// Rely on the type resolution and/or initialization to save everything.
locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
} else {
@@ -6403,7 +6481,7 @@
const ReadBarrierOption read_barrier_option = cls->IsInBootImage()
? kWithoutReadBarrier
- : kCompilerReadBarrierOption;
+ : gCompilerReadBarrierOption;
bool generate_null_check = false;
switch (load_kind) {
case HLoadClass::LoadKind::kReferrersClass: {
@@ -6550,7 +6628,7 @@
} else {
locations->SetOut(Location::RequiresRegister());
if (load->GetLoadKind() == HLoadString::LoadKind::kBssEntry) {
- if (!kUseReadBarrier || kUseBakerReadBarrier) {
+ if (!gUseReadBarrier || kUseBakerReadBarrier) {
// Rely on the pResolveString to save everything.
locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
} else {
@@ -6598,7 +6676,7 @@
/* no_rip= */ false);
Label* fixup_label = codegen_->NewStringBssEntryPatch(load);
// /* GcRoot<mirror::Class> */ out = *address /* PC-relative */
- GenerateGcRootFieldLoad(load, out_loc, address, fixup_label, kCompilerReadBarrierOption);
+ GenerateGcRootFieldLoad(load, out_loc, address, fixup_label, gCompilerReadBarrierOption);
// No need for memory fence, thanks to the x86-64 memory model.
SlowPathCode* slow_path = new (codegen_->GetScopedAllocator()) LoadStringSlowPathX86_64(load);
codegen_->AddSlowPath(slow_path);
@@ -6619,7 +6697,7 @@
Label* fixup_label = codegen_->NewJitRootStringPatch(
load->GetDexFile(), load->GetStringIndex(), load->GetString());
// /* GcRoot<mirror::String> */ out = *address
- GenerateGcRootFieldLoad(load, out_loc, address, fixup_label, kCompilerReadBarrierOption);
+ GenerateGcRootFieldLoad(load, out_loc, address, fixup_label, gCompilerReadBarrierOption);
return;
}
default:
@@ -6672,7 +6750,7 @@
// Temp is used for read barrier.
static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) {
- if (kEmitCompilerReadBarrier &&
+ if (gUseReadBarrier &&
!kUseBakerReadBarrier &&
(type_check_kind == TypeCheckKind::kAbstractClassCheck ||
type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
@@ -7426,7 +7504,7 @@
ReadBarrierOption read_barrier_option) {
CpuRegister out_reg = out.AsRegister<CpuRegister>();
if (read_barrier_option == kWithReadBarrier) {
- CHECK(kEmitCompilerReadBarrier);
+ CHECK(gUseReadBarrier);
if (kUseBakerReadBarrier) {
// Load with fast path based Baker's read barrier.
// /* HeapReference<Object> */ out = *(out + offset)
@@ -7460,7 +7538,7 @@
CpuRegister out_reg = out.AsRegister<CpuRegister>();
CpuRegister obj_reg = obj.AsRegister<CpuRegister>();
if (read_barrier_option == kWithReadBarrier) {
- CHECK(kEmitCompilerReadBarrier);
+ CHECK(gUseReadBarrier);
if (kUseBakerReadBarrier) {
// Load with fast path based Baker's read barrier.
// /* HeapReference<Object> */ out = *(obj + offset)
@@ -7488,7 +7566,7 @@
ReadBarrierOption read_barrier_option) {
CpuRegister root_reg = root.AsRegister<CpuRegister>();
if (read_barrier_option == kWithReadBarrier) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
if (kUseBakerReadBarrier) {
// Fast path implementation of art::ReadBarrier::BarrierForRoot when
// Baker's read barrier are used:
@@ -7552,7 +7630,7 @@
CpuRegister obj,
uint32_t offset,
bool needs_null_check) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
DCHECK(kUseBakerReadBarrier);
// /* HeapReference<Object> */ ref = *(obj + offset)
@@ -7566,7 +7644,7 @@
uint32_t data_offset,
Location index,
bool needs_null_check) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
DCHECK(kUseBakerReadBarrier);
static_assert(
@@ -7586,7 +7664,7 @@
bool always_update_field,
CpuRegister* temp1,
CpuRegister* temp2) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
DCHECK(kUseBakerReadBarrier);
// In slow path based read barriers, the read barrier call is
@@ -7668,7 +7746,7 @@
Location obj,
uint32_t offset,
Location index) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
// Insert a slow path based read barrier *after* the reference load.
//
@@ -7695,7 +7773,7 @@
Location obj,
uint32_t offset,
Location index) {
- if (kEmitCompilerReadBarrier) {
+ if (gUseReadBarrier) {
// Baker's read barriers shall be handled by the fast path
// (CodeGeneratorX86_64::GenerateReferenceLoadWithBakerReadBarrier).
DCHECK(!kUseBakerReadBarrier);
@@ -7710,7 +7788,7 @@
void CodeGeneratorX86_64::GenerateReadBarrierForRootSlow(HInstruction* instruction,
Location out,
Location root) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
// Insert a slow path based read barrier *after* the GC root load.
//
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index 39a72d8..1fac62f 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -18,13 +18,14 @@
#define ART_COMPILER_OPTIMIZING_CODE_GENERATOR_X86_64_H_
#include "arch/x86_64/instruction_set_features_x86_64.h"
+#include "base/macros.h"
#include "code_generator.h"
#include "driver/compiler_options.h"
#include "nodes.h"
#include "parallel_move_resolver.h"
#include "utils/x86_64/assembler_x86_64.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
// Use a local definition to prevent copying mistakes.
@@ -250,7 +251,8 @@
bool is_volatile,
bool is_atomic,
bool value_can_be_null,
- bool byte_swap = false);
+ bool byte_swap,
+ WriteBarrierKind write_barrier_kind);
void Bswap(Location value, DataType::Type type, CpuRegister* temp = nullptr);
@@ -273,7 +275,8 @@
void HandleFieldSet(HInstruction* instruction,
const FieldInfo& field_info,
- bool value_can_be_null);
+ bool value_can_be_null,
+ WriteBarrierKind write_barrier_kind);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
void GenerateMinMaxInt(LocationSummary* locations, bool is_min, DataType::Type type);
@@ -435,7 +438,7 @@
CpuRegister card,
CpuRegister object,
CpuRegister value,
- bool value_can_be_null);
+ bool emit_null_check);
void GenerateMemoryBarrier(MemBarrierKind kind);
diff --git a/compiler/optimizing/code_sinking.cc b/compiler/optimizing/code_sinking.cc
index 766bb01..4267933 100644
--- a/compiler/optimizing/code_sinking.cc
+++ b/compiler/optimizing/code_sinking.cc
@@ -25,7 +25,7 @@
#include "common_dominator.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
bool CodeSinking::Run() {
HBasicBlock* exit = graph_->GetExitBlock();
@@ -271,10 +271,21 @@
}
}
for (const HUseListNode<HEnvironment*>& use : instruction->GetEnvUses()) {
- HInstruction* user = use.GetUser()->GetHolder();
+ HEnvironment* env = use.GetUser();
+ HInstruction* user = env->GetHolder();
if (user->GetBlock() == target_block &&
(insert_pos == nullptr || user->StrictlyDominates(insert_pos))) {
- insert_pos = user;
+ if (target_block->IsCatchBlock() && target_block->GetFirstInstruction() == user) {
+ // We can sink the instructions past the environment setting Nop. If we do that, we have to
+ // remove said instruction from the environment. Since we know that we will be sinking the
+ // instruction to this block and there are no more instructions to consider, we can safely
+ // remove it from the environment now.
+ DCHECK(target_block->GetFirstInstruction()->IsNop());
+ env->RemoveAsUserOfInput(use.GetIndex());
+ env->SetRawEnvAt(use.GetIndex(), /*instruction=*/ nullptr);
+ } else {
+ insert_pos = user;
+ }
}
}
if (insert_pos == nullptr) {
diff --git a/compiler/optimizing/code_sinking.h b/compiler/optimizing/code_sinking.h
index 8eb3a52..14f62f2 100644
--- a/compiler/optimizing/code_sinking.h
+++ b/compiler/optimizing/code_sinking.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_OPTIMIZING_CODE_SINKING_H_
#define ART_COMPILER_OPTIMIZING_CODE_SINKING_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
/**
* Optimization pass to move instructions into uncommon branches,
diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc
index c0441b0..9f1f62b 100644
--- a/compiler/optimizing/codegen_test.cc
+++ b/compiler/optimizing/codegen_test.cc
@@ -33,7 +33,7 @@
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
// Return all combinations of ISA and code generator that are executable on
// hardware, or on simulator, and that we'd like to test.
diff --git a/compiler/optimizing/codegen_test_utils.h b/compiler/optimizing/codegen_test_utils.h
index 397e601..88551d8 100644
--- a/compiler/optimizing/codegen_test_utils.h
+++ b/compiler/optimizing/codegen_test_utils.h
@@ -20,6 +20,7 @@
#include "arch/arm/registers_arm.h"
#include "arch/instruction_set.h"
#include "arch/x86/registers_x86.h"
+#include "base/macros.h"
#include "code_simulator.h"
#include "code_simulator_container.h"
#include "common_compiler_test.h"
@@ -43,9 +44,9 @@
#include "code_generator_x86_64.h"
#endif
-namespace art {
+namespace art HIDDEN {
-typedef CodeGenerator* (*CreateCodegenFn)(HGraph*, const CompilerOptions&);
+using CreateCodegenFn = CodeGenerator* (*)(HGraph*, const CompilerOptions&);
class CodegenTargetConfig {
public:
@@ -254,15 +255,11 @@
Runtime* GetRuntime() override { return nullptr; }
};
CodeHolder code_holder;
- const void* code_ptr =
+ const void* method_code =
code_holder.MakeExecutable(allocator.GetMemory(), ArrayRef<const uint8_t>(), target_isa);
- typedef Expected (*fptr)();
- fptr f = reinterpret_cast<fptr>(reinterpret_cast<uintptr_t>(code_ptr));
- if (target_isa == InstructionSet::kThumb2) {
- // For thumb we need the bottom bit set.
- f = reinterpret_cast<fptr>(reinterpret_cast<uintptr_t>(f) + 1);
- }
+ using fptr = Expected (*)();
+ fptr f = reinterpret_cast<fptr>(reinterpret_cast<uintptr_t>(method_code));
VerifyGeneratedCode(target_isa, f, has_result, expected);
}
diff --git a/compiler/optimizing/common_arm.h b/compiler/optimizing/common_arm.h
index 320915e..5f71cb9 100644
--- a/compiler/optimizing/common_arm.h
+++ b/compiler/optimizing/common_arm.h
@@ -17,6 +17,7 @@
#ifndef ART_COMPILER_OPTIMIZING_COMMON_ARM_H_
#define ART_COMPILER_OPTIMIZING_COMMON_ARM_H_
+#include "base/macros.h"
#include "instruction_simplifier_shared.h"
#include "locations.h"
#include "nodes.h"
@@ -28,7 +29,7 @@
#include "aarch32/macro-assembler-aarch32.h"
#pragma GCC diagnostic pop
-namespace art {
+namespace art HIDDEN {
using helpers::HasShifterOperand;
diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h
index 81c6561..18751c4 100644
--- a/compiler/optimizing/common_arm64.h
+++ b/compiler/optimizing/common_arm64.h
@@ -17,6 +17,7 @@
#ifndef ART_COMPILER_OPTIMIZING_COMMON_ARM64_H_
#define ART_COMPILER_OPTIMIZING_COMMON_ARM64_H_
+#include "base/macros.h"
#include "code_generator.h"
#include "instruction_simplifier_shared.h"
#include "locations.h"
@@ -31,7 +32,7 @@
#include "aarch64/simulator-aarch64.h"
#pragma GCC diagnostic pop
-namespace art {
+namespace art HIDDEN {
using helpers::CanFitInShifterOperand;
using helpers::HasShifterOperand;
diff --git a/compiler/optimizing/common_dominator.h b/compiler/optimizing/common_dominator.h
index 9f012cf..f01270e 100644
--- a/compiler/optimizing/common_dominator.h
+++ b/compiler/optimizing/common_dominator.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_COMMON_DOMINATOR_H_
#define ART_COMPILER_OPTIMIZING_COMMON_DOMINATOR_H_
+#include "base/macros.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
// Helper class for finding common dominators of two or more blocks in a graph.
// The domination information of a graph must not be modified while there is
diff --git a/compiler/optimizing/constant_folding.cc b/compiler/optimizing/constant_folding.cc
index 2031707..489b62d 100644
--- a/compiler/optimizing/constant_folding.cc
+++ b/compiler/optimizing/constant_folding.cc
@@ -16,14 +16,19 @@
#include "constant_folding.h"
-namespace art {
+#include <algorithm>
+
+#include "optimizing/data_type.h"
+#include "optimizing/nodes.h"
+
+namespace art HIDDEN {
// This visitor tries to simplify instructions that can be evaluated
// as constants.
class HConstantFoldingVisitor : public HGraphDelegateVisitor {
public:
- explicit HConstantFoldingVisitor(HGraph* graph)
- : HGraphDelegateVisitor(graph) {}
+ explicit HConstantFoldingVisitor(HGraph* graph, OptimizingCompilerStats* stats)
+ : HGraphDelegateVisitor(graph, stats) {}
private:
void VisitBasicBlock(HBasicBlock* block) override;
@@ -33,6 +38,9 @@
void VisitTypeConversion(HTypeConversion* inst) override;
void VisitDivZeroCheck(HDivZeroCheck* inst) override;
+ void VisitIf(HIf* inst) override;
+
+ void PropagateValue(HBasicBlock* starting_block, HInstruction* variable, HConstant* constant);
DISALLOW_COPY_AND_ASSIGN(HConstantFoldingVisitor);
};
@@ -69,7 +77,7 @@
bool HConstantFolding::Run() {
- HConstantFoldingVisitor visitor(graph_);
+ HConstantFoldingVisitor visitor(graph_, stats_);
// Process basic blocks in reverse post-order in the dominator tree,
// so that an instruction turned into a constant, used as input of
// another instruction, may possibly be used to turn that second
@@ -130,6 +138,137 @@
}
}
+void HConstantFoldingVisitor::PropagateValue(HBasicBlock* starting_block,
+ HInstruction* variable,
+ HConstant* constant) {
+ const bool recording_stats = stats_ != nullptr;
+ size_t uses_before = 0;
+ size_t uses_after = 0;
+ if (recording_stats) {
+ uses_before = variable->GetUses().SizeSlow();
+ }
+
+ variable->ReplaceUsesDominatedBy(
+ starting_block->GetFirstInstruction(), constant, /* strictly_dominated= */ false);
+
+ if (recording_stats) {
+ uses_after = variable->GetUses().SizeSlow();
+ DCHECK_GE(uses_after, 1u) << "we must at least have the use in the if clause.";
+ DCHECK_GE(uses_before, uses_after);
+ MaybeRecordStat(stats_, MethodCompilationStat::kPropagatedIfValue, uses_before - uses_after);
+ }
+}
+
+void HConstantFoldingVisitor::VisitIf(HIf* inst) {
+ // Consistency check: the true and false successors do not dominate each other.
+ DCHECK(!inst->IfTrueSuccessor()->Dominates(inst->IfFalseSuccessor()) &&
+ !inst->IfFalseSuccessor()->Dominates(inst->IfTrueSuccessor()));
+
+ HInstruction* if_input = inst->InputAt(0);
+
+ // Already a constant.
+ if (if_input->IsConstant()) {
+ return;
+ }
+
+ // if (variable) {
+ // SSA `variable` guaranteed to be true
+ // } else {
+ // and here false
+ // }
+ PropagateValue(inst->IfTrueSuccessor(), if_input, GetGraph()->GetIntConstant(1));
+ PropagateValue(inst->IfFalseSuccessor(), if_input, GetGraph()->GetIntConstant(0));
+
+ // If the input is a condition, we can propagate the information of the condition itself.
+ if (!if_input->IsCondition()) {
+ return;
+ }
+ HCondition* condition = if_input->AsCondition();
+
+ // We want either `==` or `!=`, since we cannot make assumptions for other conditions e.g. `>`
+ if (!condition->IsEqual() && !condition->IsNotEqual()) {
+ return;
+ }
+
+ HInstruction* left = condition->GetLeft();
+ HInstruction* right = condition->GetRight();
+
+ // We want one of them to be a constant and not the other.
+ if (left->IsConstant() == right->IsConstant()) {
+ return;
+ }
+
+ // At this point we have something like:
+ // if (variable == constant) {
+ // SSA `variable` guaranteed to be equal to constant here
+ // } else {
+ // No guarantees can be made here (usually, see boolean case below).
+ // }
+ // Similarly with variable != constant, except that we can make guarantees in the else case.
+
+ HConstant* constant = left->IsConstant() ? left->AsConstant() : right->AsConstant();
+ HInstruction* variable = left->IsConstant() ? right : left;
+
+ // Don't deal with floats/doubles since they bring a lot of edge cases e.g.
+ // if (f == 0.0f) {
+ // // f is not really guaranteed to be 0.0f. It could be -0.0f, for example
+ // }
+ if (DataType::IsFloatingPointType(variable->GetType())) {
+ return;
+ }
+ DCHECK(!DataType::IsFloatingPointType(constant->GetType()));
+
+ // Sometimes we have an HCompare flowing into an Equals/NonEquals, which can act as a proxy. For
+ // example: `Equals(Compare(var, constant), 0)`. This is common for long, float, and double.
+ if (variable->IsCompare()) {
+ // We only care about equality comparisons so we skip if it is a less or greater comparison.
+ if (!constant->IsArithmeticZero()) {
+ return;
+ }
+
+ // Update left and right to be the ones from the HCompare.
+ left = variable->AsCompare()->GetLeft();
+ right = variable->AsCompare()->GetRight();
+
+ // Re-check that one of them to be a constant and not the other.
+ if (left->IsConstant() == right->IsConstant()) {
+ return;
+ }
+
+ constant = left->IsConstant() ? left->AsConstant() : right->AsConstant();
+ variable = left->IsConstant() ? right : left;
+
+ // Re-check floating point values.
+ if (DataType::IsFloatingPointType(variable->GetType())) {
+ return;
+ }
+ DCHECK(!DataType::IsFloatingPointType(constant->GetType()));
+ }
+
+ // From this block forward we want to replace the SSA value. We use `starting_block` and not the
+ // `if` block as we want to update one of the branches but not the other.
+ HBasicBlock* starting_block =
+ condition->IsEqual() ? inst->IfTrueSuccessor() : inst->IfFalseSuccessor();
+
+ PropagateValue(starting_block, variable, constant);
+
+ // Special case for booleans since they have only two values so we know what to propagate in the
+ // other branch. However, sometimes our boolean values are not compared to 0 or 1. In those cases
+ // we cannot make an assumption for the `else` branch.
+ if (variable->GetType() == DataType::Type::kBool &&
+ constant->IsIntConstant() &&
+ (constant->AsIntConstant()->IsTrue() || constant->AsIntConstant()->IsFalse())) {
+ HBasicBlock* other_starting_block =
+ condition->IsEqual() ? inst->IfFalseSuccessor() : inst->IfTrueSuccessor();
+ DCHECK_NE(other_starting_block, starting_block);
+
+ HConstant* other_constant = constant->AsIntConstant()->IsTrue() ?
+ GetGraph()->GetIntConstant(0) :
+ GetGraph()->GetIntConstant(1);
+ DCHECK_NE(other_constant, constant);
+ PropagateValue(other_starting_block, variable, other_constant);
+ }
+}
void InstructionWithAbsorbingInputSimplifier::VisitShift(HBinaryOperation* instruction) {
DCHECK(instruction->IsShl() || instruction->IsShr() || instruction->IsUShr());
diff --git a/compiler/optimizing/constant_folding.h b/compiler/optimizing/constant_folding.h
index 72bd95b..1a5e904 100644
--- a/compiler/optimizing/constant_folding.h
+++ b/compiler/optimizing/constant_folding.h
@@ -17,10 +17,12 @@
#ifndef ART_COMPILER_OPTIMIZING_CONSTANT_FOLDING_H_
#define ART_COMPILER_OPTIMIZING_CONSTANT_FOLDING_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
+#include "optimizing/optimizing_compiler_stats.h"
-namespace art {
+namespace art HIDDEN {
/**
* Optimization pass performing a simple constant-expression
@@ -39,7 +41,8 @@
*/
class HConstantFolding : public HOptimization {
public:
- HConstantFolding(HGraph* graph, const char* name) : HOptimization(graph, name) {}
+ HConstantFolding(HGraph* graph, OptimizingCompilerStats* stats, const char* name)
+ : HOptimization(graph, name, stats) {}
bool Run() override;
diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc
index 74d9d3a..7c3dae2 100644
--- a/compiler/optimizing/constant_folding_test.cc
+++ b/compiler/optimizing/constant_folding_test.cc
@@ -17,6 +17,8 @@
#include <functional>
#include "constant_folding.h"
+
+#include "base/macros.h"
#include "dead_code_elimination.h"
#include "driver/compiler_options.h"
#include "graph_checker.h"
@@ -25,7 +27,7 @@
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
/**
* Fixture class for the constant folding and dce tests.
@@ -58,7 +60,7 @@
std::string actual_before = printer_before.str();
EXPECT_EQ(expected_before, actual_before);
- HConstantFolding(graph_, "constant_folding").Run();
+ HConstantFolding(graph_, /* stats= */ nullptr, "constant_folding").Run();
GraphChecker graph_checker_cf(graph_);
graph_checker_cf.Run();
ASSERT_TRUE(graph_checker_cf.IsValid());
diff --git a/compiler/optimizing/constructor_fence_redundancy_elimination.cc b/compiler/optimizing/constructor_fence_redundancy_elimination.cc
index 3a1a9e0..66ec377 100644
--- a/compiler/optimizing/constructor_fence_redundancy_elimination.cc
+++ b/compiler/optimizing/constructor_fence_redundancy_elimination.cc
@@ -20,7 +20,7 @@
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
-namespace art {
+namespace art HIDDEN {
static constexpr bool kCfreLogFenceInputCount = false;
@@ -147,16 +147,6 @@
void VisitAlias(HInstruction* aliasing_inst) {
// An object is considered "published" if it becomes aliased by other instructions.
if (HasInterestingPublishTargetAsInput(aliasing_inst)) {
- // Note that constructing a "NullCheck" for new-instance, new-array,
- // or a 'this' (receiver) reference is impossible.
- //
- // If by some reason we actually encounter such a NullCheck(FenceTarget),
- // we LOG(WARNING).
- if (UNLIKELY(aliasing_inst->IsNullCheck())) {
- LOG(kIsDebugBuild ? FATAL : WARNING)
- << "Unexpected instruction: NullCheck; should not be legal in graph";
- // We then do a best-effort to handle this case.
- }
MergeCandidateFences();
}
}
diff --git a/compiler/optimizing/constructor_fence_redundancy_elimination.h b/compiler/optimizing/constructor_fence_redundancy_elimination.h
index 014b342..e04b986 100644
--- a/compiler/optimizing/constructor_fence_redundancy_elimination.h
+++ b/compiler/optimizing/constructor_fence_redundancy_elimination.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_CONSTRUCTOR_FENCE_REDUNDANCY_ELIMINATION_H_
#define ART_COMPILER_OPTIMIZING_CONSTRUCTOR_FENCE_REDUNDANCY_ELIMINATION_H_
+#include "base/macros.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
/*
* Constructor Fence Redundancy Elimination (CFRE).
diff --git a/compiler/optimizing/critical_native_abi_fixup_arm.cc b/compiler/optimizing/critical_native_abi_fixup_arm.cc
index 3c4db4b..03fb26b 100644
--- a/compiler/optimizing/critical_native_abi_fixup_arm.cc
+++ b/compiler/optimizing/critical_native_abi_fixup_arm.cc
@@ -23,7 +23,7 @@
#include "scoped_thread_state_change-inl.h"
#include "well_known_classes.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
// Fix up FP arguments passed in core registers for call to @CriticalNative by inserting fake calls
@@ -45,9 +45,9 @@
if (DataType::IsFloatingPointType(input_type)) {
bool is_double = (input_type == DataType::Type::kFloat64);
DataType::Type converted_type = is_double ? DataType::Type::kInt64 : DataType::Type::kInt32;
- jmethodID known_method = is_double ? WellKnownClasses::java_lang_Double_doubleToRawLongBits
- : WellKnownClasses::java_lang_Float_floatToRawIntBits;
- ArtMethod* resolved_method = jni::DecodeArtMethod(known_method);
+ ArtMethod* resolved_method = is_double
+ ? WellKnownClasses::java_lang_Double_doubleToRawLongBits
+ : WellKnownClasses::java_lang_Float_floatToRawIntBits;
DCHECK(resolved_method != nullptr);
DCHECK(resolved_method->IsIntrinsic());
MethodReference target_method(nullptr, 0);
diff --git a/compiler/optimizing/critical_native_abi_fixup_arm.h b/compiler/optimizing/critical_native_abi_fixup_arm.h
index faa3c7a..c2068f5 100644
--- a/compiler/optimizing/critical_native_abi_fixup_arm.h
+++ b/compiler/optimizing/critical_native_abi_fixup_arm.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_OPTIMIZING_CRITICAL_NATIVE_ABI_FIXUP_ARM_H_
#define ART_COMPILER_OPTIMIZING_CRITICAL_NATIVE_ABI_FIXUP_ARM_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
class CriticalNativeAbiFixupArm : public HOptimization {
diff --git a/compiler/optimizing/data_type-inl.h b/compiler/optimizing/data_type-inl.h
index 1b33b77..bbfe904 100644
--- a/compiler/optimizing/data_type-inl.h
+++ b/compiler/optimizing/data_type-inl.h
@@ -20,7 +20,7 @@
#include "data_type.h"
#include "dex/primitive.h"
-namespace art {
+namespace art HIDDEN {
// Note: Not declared in data_type.h to avoid pulling in "primitive.h".
constexpr DataType::Type DataTypeFromPrimitive(Primitive::Type type) {
diff --git a/compiler/optimizing/data_type.cc b/compiler/optimizing/data_type.cc
index cb354f4..183cf2c 100644
--- a/compiler/optimizing/data_type.cc
+++ b/compiler/optimizing/data_type.cc
@@ -16,7 +16,7 @@
#include "data_type.h"
-namespace art {
+namespace art HIDDEN {
static const char* kTypeNames[] = {
"Reference",
diff --git a/compiler/optimizing/data_type.h b/compiler/optimizing/data_type.h
index ec6ca7a..b6d9519 100644
--- a/compiler/optimizing/data_type.h
+++ b/compiler/optimizing/data_type.h
@@ -22,8 +22,9 @@
#include <android-base/logging.h>
#include "base/bit_utils.h"
+#include "base/macros.h"
-namespace art {
+namespace art HIDDEN {
class DataType {
public:
diff --git a/compiler/optimizing/data_type_test.cc b/compiler/optimizing/data_type_test.cc
index 8fea22b..f6f614d 100644
--- a/compiler/optimizing/data_type_test.cc
+++ b/compiler/optimizing/data_type_test.cc
@@ -22,7 +22,7 @@
#include "base/macros.h"
#include "dex/primitive.h"
-namespace art {
+namespace art HIDDEN {
template <DataType::Type data_type, Primitive::Type primitive_type>
static void CheckConversion() {
diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc
index d808f2c..8e3010d 100644
--- a/compiler/optimizing/dead_code_elimination.cc
+++ b/compiler/optimizing/dead_code_elimination.cc
@@ -16,14 +16,17 @@
#include "dead_code_elimination.h"
+#include "android-base/logging.h"
#include "base/array_ref.h"
#include "base/bit_vector-inl.h"
+#include "base/logging.h"
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
#include "base/stl_util.h"
+#include "optimizing/nodes.h"
#include "ssa_phi_elimination.h"
-namespace art {
+namespace art HIDDEN {
static void MarkReachableBlocks(HGraph* graph, ArenaBitVector* visited) {
// Use local allocator for allocating memory.
@@ -213,6 +216,9 @@
// | ...
// | instr_n
// | foo() // always throws
+// | instr_n+2
+// | ...
+// | instr_n+m
// \ goto B2
// \ /
// B2
@@ -230,11 +236,14 @@
// B2 Exit
//
// Rationale:
-// Removal of the never taken edge to B2 may expose
-// other optimization opportunities, such as code sinking.
+// Removal of the never taken edge to B2 may expose other optimization opportunities, such as code
+// sinking.
+//
+// Note: The example above is a simple one that uses a `goto` but we could end the block with an If,
+// for example.
bool HDeadCodeElimination::SimplifyAlwaysThrows() {
HBasicBlock* exit = graph_->GetExitBlock();
- if (exit == nullptr) {
+ if (!graph_->HasAlwaysThrowingInvokes() || exit == nullptr) {
return false;
}
@@ -242,55 +251,56 @@
// Order does not matter, just pick one.
for (HBasicBlock* block : graph_->GetReversePostOrder()) {
- if (block->GetTryCatchInformation() != nullptr) {
+ if (block->IsTryBlock()) {
// We don't want to perform the simplify always throws optimizations for throws inside of
- // tries since those throws might not go to the exit block. We do that by checking the
- // TryCatchInformation of the blocks.
- //
- // As a special case the `catch_block` is the first block of the catch and it has
- // TryCatchInformation. Other blocks in the catch don't have try catch information (as long as
- // they are not part of an outer try). Knowing if a `catch_block` is part of an outer try is
- // possible by checking its successors, but other restrictions of the simplify always throws
- // optimization will block `catch_block` nevertheless (e.g. only one predecessor) so it is not
- // worth the effort.
-
- // TODO(solanes): Maybe we can do a `goto catch` if inside of a try catch instead of going to
- // the exit. If we do so, we have to take into account that we should go to the nearest valid
- // catch i.e. one that would accept our exception type.
+ // tries since those throws might not go to the exit block.
continue;
}
- HInstruction* last = block->GetLastInstruction();
- HInstruction* prev = last->GetPrevious();
- if (prev == nullptr) {
- DCHECK_EQ(block->GetFirstInstruction(), block->GetLastInstruction());
- continue;
- }
-
- if (prev->AlwaysThrows() &&
- last->IsGoto() &&
- block->GetPhis().IsEmpty() &&
- block->GetPredecessors().size() == 1u) {
- HBasicBlock* pred = block->GetSinglePredecessor();
- HBasicBlock* succ = block->GetSingleSuccessor();
- // Ensure no computations are merged through throwing block.
- // This does not prevent the optimization per se, but would
- // require an elaborate clean up of the SSA graph.
- if (succ != exit &&
- !block->Dominates(pred) &&
- pred->Dominates(succ) &&
- succ->GetPredecessors().size() > 1u &&
- succ->GetPhis().IsEmpty()) {
- block->ReplaceSuccessor(succ, exit);
- rerun_dominance_and_loop_analysis = true;
- MaybeRecordStat(stats_, MethodCompilationStat::kSimplifyThrowingInvoke);
- // Perform a quick follow up optimization on object != null control dependences
- // that is much cheaper to perform now than in a later phase.
- if (RemoveNonNullControlDependences(pred, block)) {
- MaybeRecordStat(stats_, MethodCompilationStat::kRemovedNullCheck);
- }
+ // We iterate to find the first instruction that always throws. If two instructions always
+ // throw, the first one will throw and the second one will never be reached.
+ HInstruction* throwing_invoke = nullptr;
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ if (it.Current()->IsInvoke() && it.Current()->AsInvoke()->AlwaysThrows()) {
+ throwing_invoke = it.Current();
+ break;
}
}
+
+ if (throwing_invoke == nullptr) {
+ // No always-throwing instruction found. Continue with the rest of the blocks.
+ continue;
+ }
+
+ // If we are already pointing at the exit block we could still remove the instructions
+ // between the always throwing instruction, and the exit block. If we have no other
+ // instructions, just continue since there's nothing to do.
+ if (block->GetSuccessors().size() == 1 &&
+ block->GetSingleSuccessor() == exit &&
+ block->GetLastInstruction()->GetPrevious() == throwing_invoke) {
+ continue;
+ }
+
+ // We split the block at the throwing instruction, and the instructions after the throwing
+ // instructions will be disconnected from the graph after `block` points to the exit.
+ // `RemoveDeadBlocks` will take care of removing this new block and its instructions.
+ // Even though `SplitBefore` doesn't guarantee the graph to remain in SSA form, it is fine
+ // since we do not break it.
+ HBasicBlock* new_block = block->SplitBefore(throwing_invoke->GetNext(),
+ /* require_graph_not_in_ssa_form= */ false);
+ DCHECK_EQ(block->GetSingleSuccessor(), new_block);
+ block->ReplaceSuccessor(new_block, exit);
+
+ rerun_dominance_and_loop_analysis = true;
+ MaybeRecordStat(stats_, MethodCompilationStat::kSimplifyThrowingInvoke);
+ // Perform a quick follow up optimization on object != null control dependences
+ // that is much cheaper to perform now than in a later phase.
+ // If there are multiple predecessors, none may end with a HIf as required in
+ // RemoveNonNullControlDependences because we split critical edges.
+ if (block->GetPredecessors().size() == 1u &&
+ RemoveNonNullControlDependences(block->GetSinglePredecessor(), block)) {
+ MaybeRecordStat(stats_, MethodCompilationStat::kRemovedNullCheck);
+ }
}
// We need to re-analyze the graph in order to run DCE afterwards.
@@ -466,7 +476,192 @@
}
}
-bool HDeadCodeElimination::RemoveDeadBlocks() {
+struct HDeadCodeElimination::TryBelongingInformation {
+ explicit TryBelongingInformation(ScopedArenaAllocator* allocator)
+ : blocks_in_try(allocator->Adapter(kArenaAllocDCE)),
+ coalesced_try_entries(allocator->Adapter(kArenaAllocDCE)) {}
+
+ // Which blocks belong in the try.
+ ScopedArenaSet<HBasicBlock*> blocks_in_try;
+ // Which other try entries are referencing this same try.
+ ScopedArenaSet<HBasicBlock*> coalesced_try_entries;
+};
+
+bool HDeadCodeElimination::CanPerformTryRemoval(const TryBelongingInformation& try_belonging_info) {
+ for (HBasicBlock* block : try_belonging_info.blocks_in_try) {
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ if (it.Current()->CanThrow()) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void HDeadCodeElimination::DisconnectHandlersAndUpdateTryBoundary(
+ HBasicBlock* block,
+ /* out */ bool* any_block_in_loop) {
+ if (block->IsInLoop()) {
+ *any_block_in_loop = true;
+ }
+
+ // Disconnect the handlers.
+ while (block->GetSuccessors().size() > 1) {
+ HBasicBlock* handler = block->GetSuccessors()[1];
+ DCHECK(handler->IsCatchBlock());
+ block->RemoveSuccessor(handler);
+ handler->RemovePredecessor(block);
+ if (handler->IsInLoop()) {
+ *any_block_in_loop = true;
+ }
+ }
+
+ // Change TryBoundary to Goto.
+ DCHECK(block->EndsWithTryBoundary());
+ HInstruction* last = block->GetLastInstruction();
+ block->RemoveInstruction(last);
+ block->AddInstruction(new (graph_->GetAllocator()) HGoto(last->GetDexPc()));
+ DCHECK_EQ(block->GetSuccessors().size(), 1u);
+}
+
+void HDeadCodeElimination::RemoveTry(HBasicBlock* try_entry,
+ const TryBelongingInformation& try_belonging_info,
+ /* out */ bool* any_block_in_loop) {
+ // Update all try entries.
+ DCHECK(try_entry->EndsWithTryBoundary());
+ DCHECK(try_entry->GetLastInstruction()->AsTryBoundary()->IsEntry());
+ DisconnectHandlersAndUpdateTryBoundary(try_entry, any_block_in_loop);
+
+ for (HBasicBlock* other_try_entry : try_belonging_info.coalesced_try_entries) {
+ DCHECK(other_try_entry->EndsWithTryBoundary());
+ DCHECK(other_try_entry->GetLastInstruction()->AsTryBoundary()->IsEntry());
+ DisconnectHandlersAndUpdateTryBoundary(other_try_entry, any_block_in_loop);
+ }
+
+ // Update the blocks in the try.
+ for (HBasicBlock* block : try_belonging_info.blocks_in_try) {
+ // Update the try catch information since now the try doesn't exist.
+ block->SetTryCatchInformation(nullptr);
+ if (block->IsInLoop()) {
+ *any_block_in_loop = true;
+ }
+
+ if (block->EndsWithTryBoundary()) {
+ // Try exits.
+ DCHECK(!block->GetLastInstruction()->AsTryBoundary()->IsEntry());
+ DisconnectHandlersAndUpdateTryBoundary(block, any_block_in_loop);
+
+ if (block->GetSingleSuccessor()->IsExitBlock()) {
+ // `block` used to be a single exit TryBoundary that got turned into a Goto. It
+ // is now pointing to the exit which we don't allow. To fix it, we disconnect
+ // `block` from its predecessor and RemoveDeadBlocks will remove it from the
+ // graph.
+ DCHECK(block->IsSingleGoto());
+ HBasicBlock* predecessor = block->GetSinglePredecessor();
+ predecessor->ReplaceSuccessor(block, graph_->GetExitBlock());
+
+ if (!block->GetDominatedBlocks().empty()) {
+ // Update domination tree if `block` dominates a block to keep the graph consistent.
+ DCHECK_EQ(block->GetDominatedBlocks().size(), 1u);
+ DCHECK_EQ(graph_->GetExitBlock()->GetDominator(), block);
+ predecessor->AddDominatedBlock(graph_->GetExitBlock());
+ graph_->GetExitBlock()->SetDominator(predecessor);
+ block->RemoveDominatedBlock(graph_->GetExitBlock());
+ }
+ }
+ }
+ }
+}
+
+bool HDeadCodeElimination::RemoveUnneededTries() {
+ if (!graph_->HasTryCatch()) {
+ return false;
+ }
+
+ // Use local allocator for allocating memory.
+ ScopedArenaAllocator allocator(graph_->GetArenaStack());
+
+ // Collect which blocks are part of which try.
+ std::unordered_map<HBasicBlock*, TryBelongingInformation> tries;
+ for (HBasicBlock* block : graph_->GetReversePostOrderSkipEntryBlock()) {
+ if (block->IsTryBlock()) {
+ HBasicBlock* key = block->GetTryCatchInformation()->GetTryEntry().GetBlock();
+ auto it = tries.find(key);
+ if (it == tries.end()) {
+ it = tries.insert({key, TryBelongingInformation(&allocator)}).first;
+ }
+ it->second.blocks_in_try.insert(block);
+ }
+ }
+
+ // Deduplicate the tries which have different try entries but they are really the same try.
+ for (auto it = tries.begin(); it != tries.end(); it++) {
+ DCHECK(it->first->EndsWithTryBoundary());
+ HTryBoundary* try_boundary = it->first->GetLastInstruction()->AsTryBoundary();
+ for (auto other_it = next(it); other_it != tries.end(); /*other_it++ in the loop*/) {
+ DCHECK(other_it->first->EndsWithTryBoundary());
+ HTryBoundary* other_try_boundary = other_it->first->GetLastInstruction()->AsTryBoundary();
+ if (try_boundary->HasSameExceptionHandlersAs(*other_try_boundary)) {
+ // Merge the entries as they are really the same one.
+ // Block merging.
+ it->second.blocks_in_try.insert(other_it->second.blocks_in_try.begin(),
+ other_it->second.blocks_in_try.end());
+
+ // Add the coalesced try entry to update it too.
+ it->second.coalesced_try_entries.insert(other_it->first);
+
+ // Erase the other entry.
+ other_it = tries.erase(other_it);
+ } else {
+ other_it++;
+ }
+ }
+ }
+
+ size_t removed_tries = 0;
+ bool any_block_in_loop = false;
+
+ // Check which tries contain throwing instructions.
+ for (const auto& entry : tries) {
+ if (CanPerformTryRemoval(entry.second)) {
+ ++removed_tries;
+ RemoveTry(entry.first, entry.second, &any_block_in_loop);
+ }
+ }
+
+ if (removed_tries != 0) {
+ // We want to:
+ // 1) Update the dominance information
+ // 2) Remove catch block subtrees, if they are now unreachable.
+ // If we run the dominance recomputation without removing the code, those catch blocks will
+ // not be part of the post order and won't be removed. If we don't run the dominance
+ // recomputation, we risk RemoveDeadBlocks not running it and leaving the graph in an
+ // inconsistent state. So, what we can do is run RemoveDeadBlocks and force a recomputation.
+ // Note that we are not guaranteed to remove a catch block if we have nested try blocks:
+ //
+ // try {
+ // ... nothing can throw. TryBoundary A ...
+ // try {
+ // ... can throw. TryBoundary B...
+ // } catch (Error e) {}
+ // } catch (Exception e) {}
+ //
+ // In the example above, we can remove the TryBoundary A but the Exception catch cannot be
+ // removed as the TryBoundary B might still throw into that catch. TryBoundary A and B don't get
+ // coalesced since they have different catch handlers.
+
+ RemoveDeadBlocks(/* force_recomputation= */ true, any_block_in_loop);
+ MaybeRecordStat(stats_, MethodCompilationStat::kRemovedTry, removed_tries);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool HDeadCodeElimination::RemoveDeadBlocks(bool force_recomputation,
+ bool force_loop_recomputation) {
+ DCHECK_IMPLIES(force_loop_recomputation, force_recomputation);
+
// Use local allocator for allocating memory.
ScopedArenaAllocator allocator(graph_->GetArenaStack());
@@ -495,8 +690,8 @@
// If we removed at least one block, we need to recompute the full
// dominator tree and try block membership.
- if (removed_one_or_more_blocks) {
- if (rerun_dominance_and_loop_analysis) {
+ if (removed_one_or_more_blocks || force_recomputation) {
+ if (rerun_dominance_and_loop_analysis || force_loop_recomputation) {
graph_->ClearLoopInformation();
graph_->ClearDominanceInformation();
graph_->BuildDominatorTree();
@@ -530,6 +725,33 @@
}
}
+void HDeadCodeElimination::UpdateGraphFlags() {
+ bool has_monitor_operations = false;
+ bool has_simd = false;
+ bool has_bounds_checks = false;
+ bool has_always_throwing_invokes = false;
+
+ for (HBasicBlock* block : graph_->GetReversePostOrder()) {
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ HInstruction* instruction = it.Current();
+ if (instruction->IsMonitorOperation()) {
+ has_monitor_operations = true;
+ } else if (instruction->IsVecOperation()) {
+ has_simd = true;
+ } else if (instruction->IsBoundsCheck()) {
+ has_bounds_checks = true;
+ } else if (instruction->IsInvoke() && instruction->AsInvoke()->AlwaysThrows()) {
+ has_always_throwing_invokes = true;
+ }
+ }
+ }
+
+ graph_->SetHasMonitorOperations(has_monitor_operations);
+ graph_->SetHasSIMD(has_simd);
+ graph_->SetHasBoundsChecks(has_bounds_checks);
+ graph_->SetHasAlwaysThrowingInvokes(has_always_throwing_invokes);
+}
+
bool HDeadCodeElimination::Run() {
// Do not eliminate dead blocks if the graph has irreducible loops. We could
// support it, but that would require changes in our loop representation to handle
@@ -541,6 +763,11 @@
did_any_simplification |= SimplifyAlwaysThrows();
did_any_simplification |= SimplifyIfs();
did_any_simplification |= RemoveDeadBlocks();
+ // We call RemoveDeadBlocks before RemoveUnneededTries to remove the dead blocks from the
+ // previous optimizations. Otherwise, we might detect that a try has throwing instructions but
+ // they are actually dead code. RemoveUnneededTryBoundary will call RemoveDeadBlocks again if
+ // needed.
+ did_any_simplification |= RemoveUnneededTries();
if (did_any_simplification) {
// Connect successive blocks created by dead branches.
ConnectSuccessiveBlocks();
@@ -548,6 +775,7 @@
}
SsaRedundantPhiElimination(graph_).Run();
RemoveDeadInstructions();
+ UpdateGraphFlags();
return true;
}
diff --git a/compiler/optimizing/dead_code_elimination.h b/compiler/optimizing/dead_code_elimination.h
index 799721a..b91006b 100644
--- a/compiler/optimizing/dead_code_elimination.h
+++ b/compiler/optimizing/dead_code_elimination.h
@@ -17,11 +17,12 @@
#ifndef ART_COMPILER_OPTIMIZING_DEAD_CODE_ELIMINATION_H_
#define ART_COMPILER_OPTIMIZING_DEAD_CODE_ELIMINATION_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
#include "optimizing_compiler_stats.h"
-namespace art {
+namespace art HIDDEN {
/**
* Optimization pass performing dead code elimination (removal of
@@ -39,11 +40,40 @@
private:
void MaybeRecordDeadBlock(HBasicBlock* block);
void MaybeRecordSimplifyIf();
- bool RemoveDeadBlocks();
+ // If `force_recomputation` is true, we will recompute the dominance information even when we
+ // didn't delete any blocks. `force_loop_recomputation` is similar but it also forces the loop
+ // information recomputation.
+ bool RemoveDeadBlocks(bool force_recomputation = false, bool force_loop_recomputation = false);
void RemoveDeadInstructions();
bool SimplifyAlwaysThrows();
bool SimplifyIfs();
void ConnectSuccessiveBlocks();
+ // Updates the graph flags related to instructions (e.g. HasSIMD()) since we may have eliminated
+ // the relevant instructions. There's no need to update `SetHasTryCatch` since we do that in
+ // `ComputeTryBlockInformation`. Similarly with `HasLoops` and `HasIrreducibleLoops`: They are
+ // cleared in `ClearLoopInformation` and then set as true as part of `HLoopInformation::Populate`,
+ // if needed.
+ void UpdateGraphFlags();
+
+ // Helper struct to eliminate tries.
+ struct TryBelongingInformation;
+ // Disconnects `block`'s handlers and update its `TryBoundary` instruction to a `Goto`.
+ // Sets `any_block_in_loop` to true if any block is currently a loop to later update the loop
+ // information if needed.
+ void DisconnectHandlersAndUpdateTryBoundary(HBasicBlock* block,
+ /* out */ bool* any_block_in_loop);
+ // Returns true iff the try doesn't contain throwing instructions.
+ bool CanPerformTryRemoval(const TryBelongingInformation& try_belonging_info);
+ // Removes the try by disconnecting all try entries and exits from their handlers. Also updates
+ // the graph in the case that a `TryBoundary` instruction of kind `exit` has the Exit block as
+ // its successor.
+ void RemoveTry(HBasicBlock* try_entry,
+ const TryBelongingInformation& try_belonging_info,
+ bool* any_block_in_loop);
+ // Checks which tries (if any) are currently in the graph, coalesces the different try entries
+ // that are referencing the same try, and removes the tries which don't contain any throwing
+ // instructions.
+ bool RemoveUnneededTries();
DISALLOW_COPY_AND_ASSIGN(HDeadCodeElimination);
};
diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc
index f5cd4dc..fcf4ca3 100644
--- a/compiler/optimizing/dead_code_elimination_test.cc
+++ b/compiler/optimizing/dead_code_elimination_test.cc
@@ -16,6 +16,7 @@
#include "dead_code_elimination.h"
+#include "base/macros.h"
#include "driver/compiler_options.h"
#include "graph_checker.h"
#include "optimizing_unit_test.h"
@@ -23,7 +24,7 @@
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
class DeadCodeEliminationTest : public OptimizingUnitTest {
protected:
diff --git a/compiler/optimizing/dominator_test.cc b/compiler/optimizing/dominator_test.cc
index 1d72ba1..d3b8cb1 100644
--- a/compiler/optimizing/dominator_test.cc
+++ b/compiler/optimizing/dominator_test.cc
@@ -15,6 +15,7 @@
*/
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "dex/dex_instruction.h"
#include "nodes.h"
@@ -22,7 +23,7 @@
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
class OptimizerTest : public OptimizingUnitTest {
protected:
diff --git a/compiler/optimizing/escape.cc b/compiler/optimizing/escape.cc
index 617833c..cebe94f 100644
--- a/compiler/optimizing/escape.cc
+++ b/compiler/optimizing/escape.cc
@@ -18,7 +18,7 @@
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
void VisitEscapes(HInstruction* reference, EscapeVisitor& escape_visitor) {
// References not allocated in the method are intrinsically escaped.
diff --git a/compiler/optimizing/escape.h b/compiler/optimizing/escape.h
index 5402cb1..3b284fb 100644
--- a/compiler/optimizing/escape.h
+++ b/compiler/optimizing/escape.h
@@ -17,7 +17,9 @@
#ifndef ART_COMPILER_OPTIMIZING_ESCAPE_H_
#define ART_COMPILER_OPTIMIZING_ESCAPE_H_
-namespace art {
+#include "base/macros.h"
+
+namespace art HIDDEN {
class HInstruction;
diff --git a/compiler/optimizing/execution_subgraph.cc b/compiler/optimizing/execution_subgraph.cc
index 66fdfcd..06aabbe 100644
--- a/compiler/optimizing/execution_subgraph.cc
+++ b/compiler/optimizing/execution_subgraph.cc
@@ -26,7 +26,7 @@
#include "base/scoped_arena_allocator.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
ExecutionSubgraph::ExecutionSubgraph(HGraph* graph, ScopedArenaAllocator* allocator)
: graph_(graph),
diff --git a/compiler/optimizing/execution_subgraph.h b/compiler/optimizing/execution_subgraph.h
index 7d2a660..5ddf17d 100644
--- a/compiler/optimizing/execution_subgraph.h
+++ b/compiler/optimizing/execution_subgraph.h
@@ -27,6 +27,7 @@
#include "base/bit_vector-inl.h"
#include "base/globals.h"
#include "base/iteration_range.h"
+#include "base/macros.h"
#include "base/mutex.h"
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
@@ -34,7 +35,7 @@
#include "base/transform_iterator.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
// Helper for transforming blocks to block_ids.
class BlockToBlockIdTransformer {
diff --git a/compiler/optimizing/execution_subgraph_test.cc b/compiler/optimizing/execution_subgraph_test.cc
index 74c243b..921ef05 100644
--- a/compiler/optimizing/execution_subgraph_test.cc
+++ b/compiler/optimizing/execution_subgraph_test.cc
@@ -37,7 +37,7 @@
#include "optimizing_unit_test.h"
#include "scoped_thread_state_change.h"
-namespace art {
+namespace art HIDDEN {
using BlockSet = std::unordered_set<const HBasicBlock*>;
diff --git a/compiler/optimizing/execution_subgraph_test.h b/compiler/optimizing/execution_subgraph_test.h
index 13cb2bc..cee105a 100644
--- a/compiler/optimizing/execution_subgraph_test.h
+++ b/compiler/optimizing/execution_subgraph_test.h
@@ -19,7 +19,9 @@
#include "android-base/macros.h"
-namespace art {
+#include "base/macros.h"
+
+namespace art HIDDEN {
class HGraph;
class ExecutionSubgraph;
diff --git a/compiler/optimizing/find_loops_test.cc b/compiler/optimizing/find_loops_test.cc
index 75b8e96..8d8c3a1 100644
--- a/compiler/optimizing/find_loops_test.cc
+++ b/compiler/optimizing/find_loops_test.cc
@@ -15,6 +15,7 @@
*/
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "dex/dex_file.h"
#include "dex/dex_instruction.h"
@@ -25,7 +26,7 @@
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
class FindLoopsTest : public OptimizingUnitTest {};
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index d1769ce..ca29a09 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -32,7 +32,7 @@
#include "scoped_thread_state_change-inl.h"
#include "subtype_check.h"
-namespace art {
+namespace art HIDDEN {
using android::base::StringPrintf;
@@ -80,9 +80,91 @@
// as the latter might visit dead blocks removed by the dominator
// computation.
VisitReversePostOrder();
+ CheckGraphFlags();
return current_size;
}
+void GraphChecker::VisitReversePostOrder() {
+ for (HBasicBlock* block : GetGraph()->GetReversePostOrder()) {
+ if (block->IsInLoop()) {
+ flag_info_.seen_loop = true;
+ if (block->GetLoopInformation()->IsIrreducible()) {
+ flag_info_.seen_irreducible_loop = true;
+ }
+ }
+
+ VisitBasicBlock(block);
+ }
+}
+
+static const char* StrBool(bool val) {
+ return val ? "true" : "false";
+}
+
+void GraphChecker::CheckGraphFlags() {
+ if (GetGraph()->HasMonitorOperations() != flag_info_.seen_monitor_operation) {
+ AddError(
+ StringPrintf("Flag mismatch: HasMonitorOperations() (%s) should be equal to "
+ "flag_info_.seen_monitor_operation (%s)",
+ StrBool(GetGraph()->HasMonitorOperations()),
+ StrBool(flag_info_.seen_monitor_operation)));
+ }
+
+ if (GetGraph()->HasTryCatch() != flag_info_.seen_try_boundary) {
+ AddError(
+ StringPrintf("Flag mismatch: HasTryCatch() (%s) should be equal to "
+ "flag_info_.seen_try_boundary (%s)",
+ StrBool(GetGraph()->HasTryCatch()),
+ StrBool(flag_info_.seen_try_boundary)));
+ }
+
+ if (GetGraph()->HasLoops() != flag_info_.seen_loop) {
+ AddError(
+ StringPrintf("Flag mismatch: HasLoops() (%s) should be equal to "
+ "flag_info_.seen_loop (%s)",
+ StrBool(GetGraph()->HasLoops()),
+ StrBool(flag_info_.seen_loop)));
+ }
+
+ if (GetGraph()->HasIrreducibleLoops() && !GetGraph()->HasLoops()) {
+ AddError(StringPrintf("Flag mismatch: HasIrreducibleLoops() (%s) implies HasLoops() (%s)",
+ StrBool(GetGraph()->HasIrreducibleLoops()),
+ StrBool(GetGraph()->HasLoops())));
+ }
+
+ if (GetGraph()->HasIrreducibleLoops() != flag_info_.seen_irreducible_loop) {
+ AddError(
+ StringPrintf("Flag mismatch: HasIrreducibleLoops() (%s) should be equal to "
+ "flag_info_.seen_irreducible_loop (%s)",
+ StrBool(GetGraph()->HasIrreducibleLoops()),
+ StrBool(flag_info_.seen_irreducible_loop)));
+ }
+
+ if (GetGraph()->HasSIMD() != flag_info_.seen_SIMD) {
+ AddError(
+ StringPrintf("Flag mismatch: HasSIMD() (%s) should be equal to "
+ "flag_info_.seen_SIMD (%s)",
+ StrBool(GetGraph()->HasSIMD()),
+ StrBool(flag_info_.seen_SIMD)));
+ }
+
+ if (GetGraph()->HasBoundsChecks() != flag_info_.seen_bounds_checks) {
+ AddError(
+ StringPrintf("Flag mismatch: HasBoundsChecks() (%s) should be equal to "
+ "flag_info_.seen_bounds_checks (%s)",
+ StrBool(GetGraph()->HasBoundsChecks()),
+ StrBool(flag_info_.seen_bounds_checks)));
+ }
+
+ if (GetGraph()->HasAlwaysThrowingInvokes() != flag_info_.seen_always_throwing_invokes) {
+ AddError(
+ StringPrintf("Flag mismatch: HasAlwaysThrowingInvokes() (%s) should be equal to "
+ "flag_info_.seen_always_throwing_invokes (%s)",
+ StrBool(GetGraph()->HasAlwaysThrowingInvokes()),
+ StrBool(flag_info_.seen_always_throwing_invokes)));
+ }
+}
+
void GraphChecker::VisitBasicBlock(HBasicBlock* block) {
current_block_ = block;
@@ -159,6 +241,24 @@
}
}
+ // Make sure the first instruction of a catch block is always a Nop that emits an environment.
+ if (block->IsCatchBlock()) {
+ if (!block->GetFirstInstruction()->IsNop()) {
+ AddError(StringPrintf("Block %d doesn't have a Nop as its first instruction.",
+ current_block_->GetBlockId()));
+ } else {
+ HNop* nop = block->GetFirstInstruction()->AsNop();
+ if (!nop->NeedsEnvironment()) {
+ AddError(
+ StringPrintf("%s:%d is a Nop and the first instruction of block %d, but it doesn't "
+ "need an environment.",
+ nop->DebugName(),
+ nop->GetId(),
+ current_block_->GetBlockId()));
+ }
+ }
+ }
+
// Visit this block's list of phis.
for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
HInstruction* current = it.Current();
@@ -291,27 +391,30 @@
}
void GraphChecker::VisitBoundsCheck(HBoundsCheck* check) {
+ VisitInstruction(check);
+
if (!GetGraph()->HasBoundsChecks()) {
- AddError(StringPrintf("Instruction %s:%d is a HBoundsCheck, "
- "but HasBoundsChecks() returns false",
- check->DebugName(),
- check->GetId()));
+ AddError(
+ StringPrintf("The graph doesn't have the HasBoundsChecks flag set but we saw "
+ "%s:%d in block %d.",
+ check->DebugName(),
+ check->GetId(),
+ check->GetBlock()->GetBlockId()));
}
- // Perform the instruction base checks too.
- VisitInstruction(check);
+ flag_info_.seen_bounds_checks = true;
}
void GraphChecker::VisitDeoptimize(HDeoptimize* deopt) {
+ VisitInstruction(deopt);
if (GetGraph()->IsCompilingOsr()) {
AddError(StringPrintf("A graph compiled OSR cannot have a HDeoptimize instruction"));
}
-
- // Perform the instruction base checks too.
- VisitInstruction(deopt);
}
void GraphChecker::VisitTryBoundary(HTryBoundary* try_boundary) {
+ VisitInstruction(try_boundary);
+
ArrayRef<HBasicBlock* const> handlers = try_boundary->GetExceptionHandlers();
// Ensure that all exception handlers are catch blocks.
@@ -338,24 +441,51 @@
}
}
- VisitInstruction(try_boundary);
+ if (!GetGraph()->HasTryCatch()) {
+ AddError(
+ StringPrintf("The graph doesn't have the HasTryCatch flag set but we saw "
+ "%s:%d in block %d.",
+ try_boundary->DebugName(),
+ try_boundary->GetId(),
+ try_boundary->GetBlock()->GetBlockId()));
+ }
+
+ flag_info_.seen_try_boundary = true;
}
void GraphChecker::VisitLoadException(HLoadException* load) {
- // Ensure that LoadException is the first instruction in a catch block.
+ VisitInstruction(load);
+
+ // Ensure that LoadException is the second instruction in a catch block. The first one should be a
+ // Nop (checked separately).
if (!load->GetBlock()->IsCatchBlock()) {
AddError(StringPrintf("%s:%d is in a non-catch block %d.",
load->DebugName(),
load->GetId(),
load->GetBlock()->GetBlockId()));
- } else if (load->GetBlock()->GetFirstInstruction() != load) {
- AddError(StringPrintf("%s:%d is not the first instruction in catch block %d.",
+ } else if (load->GetBlock()->GetFirstInstruction()->GetNext() != load) {
+ AddError(StringPrintf("%s:%d is not the second instruction in catch block %d.",
load->DebugName(),
load->GetId(),
load->GetBlock()->GetBlockId()));
}
}
+void GraphChecker::VisitMonitorOperation(HMonitorOperation* monitor_op) {
+ VisitInstruction(monitor_op);
+
+ if (!GetGraph()->HasMonitorOperations()) {
+ AddError(
+ StringPrintf("The graph doesn't have the HasMonitorOperations flag set but we saw "
+ "%s:%d in block %d.",
+ monitor_op->DebugName(),
+ monitor_op->GetId(),
+ monitor_op->GetBlock()->GetBlockId()));
+ }
+
+ flag_info_.seen_monitor_operation = true;
+}
+
void GraphChecker::VisitInstruction(HInstruction* instruction) {
if (seen_ids_.IsBitSet(instruction->GetId())) {
AddError(StringPrintf("Instruction id %d is duplicate in graph.",
@@ -513,17 +643,10 @@
instruction->GetId(),
current_block_->GetBlockId()));
} else if (instruction->CanThrowIntoCatchBlock()) {
- // Find the top-level environment. This corresponds to the environment of
- // the catch block since we do not inline methods with try/catch.
- HEnvironment* environment = instruction->GetEnvironment();
- while (environment->GetParent() != nullptr) {
- environment = environment->GetParent();
- }
-
- // Find all catch blocks and test that `instruction` has an environment
- // value for each one.
+ // Find all catch blocks and test that `instruction` has an environment value for each one.
const HTryBoundary& entry = instruction->GetBlock()->GetTryCatchInformation()->GetTryEntry();
for (HBasicBlock* catch_block : entry.GetExceptionHandlers()) {
+ const HEnvironment* environment = catch_block->GetFirstInstruction()->GetEnvironment();
for (HInstructionIterator phi_it(catch_block->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
HPhi* catch_phi = phi_it.Current()->AsPhi();
if (environment->GetInstructionAt(catch_phi->GetRegNumber()) == nullptr) {
@@ -541,9 +664,26 @@
}
}
-void GraphChecker::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
+void GraphChecker::VisitInvoke(HInvoke* invoke) {
VisitInstruction(invoke);
+ if (invoke->AlwaysThrows()) {
+ if (!GetGraph()->HasAlwaysThrowingInvokes()) {
+ AddError(
+ StringPrintf("The graph doesn't have the HasAlwaysThrowingInvokes flag set but we saw "
+ "%s:%d in block %d and it always throws.",
+ invoke->DebugName(),
+ invoke->GetId(),
+ invoke->GetBlock()->GetBlockId()));
+ }
+ flag_info_.seen_always_throwing_invokes = true;
+ }
+}
+
+void GraphChecker::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
+ // We call VisitInvoke and not VisitInstruction to de-duplicate the always throwing code check.
+ VisitInvoke(invoke);
+
if (invoke->IsStaticWithExplicitClinitCheck()) {
const HInstruction* last_input = invoke->GetInputs().back();
if (last_input == nullptr) {
@@ -674,13 +814,14 @@
loop_information->GetPreHeader()->GetSuccessors().size()));
}
- if (loop_information->GetSuspendCheck() == nullptr) {
- AddError(StringPrintf(
- "Loop with header %d does not have a suspend check.",
- loop_header->GetBlockId()));
+ if (!GetGraph()->SuspendChecksAreAllowedToNoOp() &&
+ loop_information->GetSuspendCheck() == nullptr) {
+ AddError(StringPrintf("Loop with header %d does not have a suspend check.",
+ loop_header->GetBlockId()));
}
- if (loop_information->GetSuspendCheck() != loop_header->GetFirstInstructionDisregardMoves()) {
+ if (!GetGraph()->SuspendChecksAreAllowedToNoOp() &&
+ loop_information->GetSuspendCheck() != loop_header->GetFirstInstructionDisregardMoves()) {
AddError(StringPrintf(
"Loop header %d does not have the loop suspend check as the first instruction.",
loop_header->GetBlockId()));
@@ -1051,6 +1192,21 @@
}
}
+void GraphChecker::VisitArraySet(HArraySet* instruction) {
+ VisitInstruction(instruction);
+
+ if (instruction->NeedsTypeCheck() !=
+ instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC())) {
+ AddError(
+ StringPrintf("%s %d has a flag mismatch. An ArraySet instruction can trigger a GC iff it "
+ "needs a type check. Needs type check: %s, Can trigger GC: %s",
+ instruction->DebugName(),
+ instruction->GetId(),
+ StrBool(instruction->NeedsTypeCheck()),
+ StrBool(instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC()))));
+ }
+}
+
void GraphChecker::VisitBinaryOperation(HBinaryOperation* op) {
VisitInstruction(op);
DataType::Type lhs_type = op->InputAt(0)->GetType();
@@ -1111,6 +1267,8 @@
}
void GraphChecker::VisitConstant(HConstant* instruction) {
+ VisitInstruction(instruction);
+
HBasicBlock* block = instruction->GetBlock();
if (!block->IsEntryBlock()) {
AddError(StringPrintf(
@@ -1149,6 +1307,18 @@
void GraphChecker::VisitVecOperation(HVecOperation* instruction) {
VisitInstruction(instruction);
+
+ if (!GetGraph()->HasSIMD()) {
+ AddError(
+ StringPrintf("The graph doesn't have the HasSIMD flag set but we saw "
+ "%s:%d in block %d.",
+ instruction->DebugName(),
+ instruction->GetId(),
+ instruction->GetBlock()->GetBlockId()));
+ }
+
+ flag_info_.seen_SIMD = true;
+
if (codegen_ == nullptr) {
return;
}
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
index 04c8d21..674798e 100644
--- a/compiler/optimizing/graph_checker.h
+++ b/compiler/optimizing/graph_checker.h
@@ -21,10 +21,11 @@
#include "base/arena_bit_vector.h"
#include "base/bit_vector-inl.h"
+#include "base/macros.h"
#include "base/scoped_arena_allocator.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
@@ -54,6 +55,7 @@
void VisitInstruction(HInstruction* instruction) override;
void VisitPhi(HPhi* phi) override;
+ void VisitArraySet(HArraySet* instruction) override;
void VisitBinaryOperation(HBinaryOperation* op) override;
void VisitBooleanNot(HBooleanNot* instruction) override;
void VisitBoundType(HBoundType* instruction) override;
@@ -64,8 +66,10 @@
void VisitDeoptimize(HDeoptimize* instruction) override;
void VisitIf(HIf* instruction) override;
void VisitInstanceOf(HInstanceOf* check) override;
+ void VisitInvoke(HInvoke* invoke) override;
void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) override;
void VisitLoadException(HLoadException* load) override;
+ void VisitMonitorOperation(HMonitorOperation* monitor_operation) override;
void VisitNeg(HNeg* instruction) override;
void VisitPackedSwitch(HPackedSwitch* instruction) override;
void VisitReturn(HReturn* ret) override;
@@ -123,6 +127,11 @@
ArenaVector<std::string> errors_;
private:
+ void VisitReversePostOrder();
+
+ // Checks that the graph's flags are set correctly.
+ void CheckGraphFlags();
+
// String displayed before dumped errors.
const char* const dump_prefix_;
ScopedArenaAllocator allocator_;
@@ -135,6 +144,17 @@
// Used to access target information.
CodeGenerator* codegen_;
+ struct FlagInfo {
+ bool seen_try_boundary = false;
+ bool seen_monitor_operation = false;
+ bool seen_loop = false;
+ bool seen_irreducible_loop = false;
+ bool seen_SIMD = false;
+ bool seen_bounds_checks = false;
+ bool seen_always_throwing_invokes = false;
+ };
+ FlagInfo flag_info_;
+
DISALLOW_COPY_AND_ASSIGN(GraphChecker);
};
diff --git a/compiler/optimizing/graph_checker_test.cc b/compiler/optimizing/graph_checker_test.cc
index 08bfa5d..9a2fc89 100644
--- a/compiler/optimizing/graph_checker_test.cc
+++ b/compiler/optimizing/graph_checker_test.cc
@@ -14,10 +14,11 @@
* limitations under the License.
*/
+#include "base/macros.h"
#include "graph_checker.h"
#include "optimizing_unit_test.h"
-namespace art {
+namespace art HIDDEN {
class GraphCheckerTest : public OptimizingUnitTest {
protected:
diff --git a/compiler/optimizing/graph_test.cc b/compiler/optimizing/graph_test.cc
index 29af808..b5d7127 100644
--- a/compiler/optimizing/graph_test.cc
+++ b/compiler/optimizing/graph_test.cc
@@ -15,6 +15,7 @@
*/
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
@@ -22,7 +23,7 @@
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
class GraphTest : public OptimizingUnitTest {
protected:
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 4a6ee13..96eaa61 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -43,7 +43,7 @@
#include "ssa_liveness_analysis.h"
#include "utils/assembler.h"
-namespace art {
+namespace art HIDDEN {
// Unique pass-name to identify that the dump is for printing to log.
constexpr const char* kDebugDumpName = "debug";
@@ -480,6 +480,10 @@
<< array_set->GetValueCanBeNull() << std::noboolalpha;
StartAttributeStream("needs_type_check") << std::boolalpha
<< array_set->NeedsTypeCheck() << std::noboolalpha;
+ StartAttributeStream("can_trigger_gc")
+ << std::boolalpha << array_set->GetSideEffects().Includes(SideEffects::CanTriggerGC())
+ << std::noboolalpha;
+ StartAttributeStream("write_barrier_kind") << array_set->GetWriteBarrierKind();
}
void VisitCompare(HCompare* compare) override {
@@ -549,7 +553,9 @@
iset->GetFieldInfo().GetDexFile().PrettyField(iset->GetFieldInfo().GetFieldIndex(),
/* with type */ false);
StartAttributeStream("field_type") << iset->GetFieldType();
- StartAttributeStream("predicated") << std::boolalpha << iset->GetIsPredicatedSet();
+ StartAttributeStream("predicated")
+ << std::boolalpha << iset->GetIsPredicatedSet() << std::noboolalpha;
+ StartAttributeStream("write_barrier_kind") << iset->GetWriteBarrierKind();
}
void VisitStaticFieldGet(HStaticFieldGet* sget) override {
@@ -564,6 +570,7 @@
sset->GetFieldInfo().GetDexFile().PrettyField(sset->GetFieldInfo().GetFieldIndex(),
/* with type */ false);
StartAttributeStream("field_type") << sset->GetFieldType();
+ StartAttributeStream("write_barrier_kind") << sset->GetWriteBarrierKind();
}
void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* field_access) override {
@@ -904,6 +911,11 @@
if (block->IsCatchBlock()) {
PrintProperty("flags", "catch_block");
+ } else if (block->IsTryBlock()) {
+ std::stringstream flags_properties;
+ flags_properties << "try_start "
+ << namer_.GetName(block->GetTryCatchInformation()->GetTryEntry().GetBlock());
+ PrintProperty("flags", flags_properties.str().c_str());
} else if (!IsDebugDump()) {
// Don't print useless information to logcat
PrintEmptyProperty("flags");
diff --git a/compiler/optimizing/graph_visualizer.h b/compiler/optimizing/graph_visualizer.h
index 3429c11..9878917 100644
--- a/compiler/optimizing/graph_visualizer.h
+++ b/compiler/optimizing/graph_visualizer.h
@@ -22,10 +22,11 @@
#include "arch/instruction_set.h"
#include "base/arena_containers.h"
+#include "base/macros.h"
#include "base/value_object.h"
#include "block_namer.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
class DexCompilationUnit;
diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc
index c7cd223..a6ca057 100644
--- a/compiler/optimizing/gvn.cc
+++ b/compiler/optimizing/gvn.cc
@@ -23,7 +23,7 @@
#include "base/utils.h"
#include "side_effects_analysis.h"
-namespace art {
+namespace art HIDDEN {
/**
* A ValueSet holds instructions that can replace other instructions. It is updated
diff --git a/compiler/optimizing/gvn.h b/compiler/optimizing/gvn.h
index bbf2265..df4e3a8 100644
--- a/compiler/optimizing/gvn.h
+++ b/compiler/optimizing/gvn.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_OPTIMIZING_GVN_H_
#define ART_COMPILER_OPTIMIZING_GVN_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
class SideEffectsAnalysis;
diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc
index 3bf4cc3..1eb6307 100644
--- a/compiler/optimizing/gvn_test.cc
+++ b/compiler/optimizing/gvn_test.cc
@@ -17,12 +17,13 @@
#include "gvn.h"
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
#include "side_effects_analysis.h"
-namespace art {
+namespace art HIDDEN {
class GVNTest : public OptimizingUnitTest {};
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index 3b5a2f1..5b5cd2e 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -18,7 +18,7 @@
#include "induction_var_range.h"
-namespace art {
+namespace art HIDDEN {
/**
* Returns true if the from/to types denote a narrowing, integral conversion (precision loss).
diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h
index 0941772..124c3b4 100644
--- a/compiler/optimizing/induction_var_analysis.h
+++ b/compiler/optimizing/induction_var_analysis.h
@@ -21,11 +21,12 @@
#include "base/arena_containers.h"
#include "base/array_ref.h"
+#include "base/macros.h"
#include "base/scoped_arena_containers.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
/**
* Induction variable analysis. This class does not have a direct public API.
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc
index 4c11ad4..80c1537 100644
--- a/compiler/optimizing/induction_var_analysis_test.cc
+++ b/compiler/optimizing/induction_var_analysis_test.cc
@@ -17,12 +17,13 @@
#include <regex>
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "induction_var_analysis.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
-namespace art {
+namespace art HIDDEN {
/**
* Fixture class for the InductionVarAnalysis tests.
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index ad3d1a9..9b78699 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -17,8 +17,9 @@
#include "induction_var_range.h"
#include <limits>
+#include "optimizing/nodes.h"
-namespace art {
+namespace art HIDDEN {
/** Returns true if 64-bit constant fits in 32-bit constant. */
static bool CanLongValueFitIntoInt(int64_t c) {
@@ -1064,10 +1065,13 @@
case HInductionVarAnalysis::kLinear:
if (*stride_value > 0) {
lower = nullptr;
+ return GenerateLastValueLinear(
+ context, loop, info, trip, graph, block, /*is_min=*/false, upper);
} else {
upper = nullptr;
+ return GenerateLastValueLinear(
+ context, loop, info, trip, graph, block, /*is_min=*/true, lower);
}
- break;
case HInductionVarAnalysis::kPolynomial:
return GenerateLastValuePolynomial(context, loop, info, trip, graph, block, lower);
case HInductionVarAnalysis::kGeometric:
@@ -1113,6 +1117,54 @@
GenerateCode(context, loop, info, trip, graph, block, /*is_min=*/ false, upper);
}
+bool InductionVarRange::GenerateLastValueLinear(const HBasicBlock* context,
+ const HLoopInformation* loop,
+ HInductionVarAnalysis::InductionInfo* info,
+ HInductionVarAnalysis::InductionInfo* trip,
+ HGraph* graph,
+ HBasicBlock* block,
+ bool is_min,
+ /*out*/ HInstruction** result) const {
+ DataType::Type type = info->type;
+ // Avoid any narrowing linear induction or any type mismatch between the linear induction and the
+ // trip count expression.
+ if (HInductionVarAnalysis::IsNarrowingLinear(info) || trip->type != type) {
+ return false;
+ }
+
+ // Stride value must be a known constant that fits into int32.
+ int64_t stride_value = 0;
+ if (!IsConstant(context, loop, info->op_a, kExact, &stride_value) ||
+ !CanLongValueFitIntoInt(stride_value)) {
+ return false;
+ }
+
+ // We require `a` to be a constant value that didn't overflow.
+ const bool is_min_a = stride_value >= 0 ? is_min : !is_min;
+ Value val_a = GetVal(context, loop, trip, trip, is_min_a);
+ HInstruction* opb;
+ if (!IsConstantValue(val_a) ||
+ !GenerateCode(context, loop, info->op_b, trip, graph, block, is_min, &opb)) {
+ return false;
+ }
+
+ if (graph != nullptr) {
+ ArenaAllocator* allocator = graph->GetAllocator();
+ HInstruction* oper;
+ HInstruction* opa = graph->GetConstant(type, val_a.b_constant);
+ if (stride_value == 1) {
+ oper = new (allocator) HAdd(type, opa, opb);
+ } else if (stride_value == -1) {
+ oper = new (graph->GetAllocator()) HSub(type, opb, opa);
+ } else {
+ HInstruction* mul = new (allocator) HMul(type, graph->GetConstant(type, stride_value), opa);
+ oper = new (allocator) HAdd(type, Insert(block, mul), opb);
+ }
+ *result = Insert(block, oper);
+ }
+ return true;
+}
+
bool InductionVarRange::GenerateLastValuePolynomial(const HBasicBlock* context,
const HLoopInformation* loop,
HInductionVarAnalysis::InductionInfo* info,
diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h
index 552837c..3e1212b 100644
--- a/compiler/optimizing/induction_var_range.h
+++ b/compiler/optimizing/induction_var_range.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_INDUCTION_VAR_RANGE_H_
#define ART_COMPILER_OPTIMIZING_INDUCTION_VAR_RANGE_H_
+#include "base/macros.h"
#include "induction_var_analysis.h"
-namespace art {
+namespace art HIDDEN {
/**
* This class implements range analysis on expressions within loops. It takes the results
@@ -317,6 +318,15 @@
/*out*/ bool* needs_finite_test,
/*out*/ bool* needs_taken_test) const;
+ bool GenerateLastValueLinear(const HBasicBlock* context,
+ const HLoopInformation* loop,
+ HInductionVarAnalysis::InductionInfo* info,
+ HInductionVarAnalysis::InductionInfo* trip,
+ HGraph* graph,
+ HBasicBlock* block,
+ bool is_min,
+ /*out*/ HInstruction** result) const;
+
bool GenerateLastValuePolynomial(const HBasicBlock* context,
const HLoopInformation* loop,
HInductionVarAnalysis::InductionInfo* info,
diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc
index 962123d..d879897 100644
--- a/compiler/optimizing/induction_var_range_test.cc
+++ b/compiler/optimizing/induction_var_range_test.cc
@@ -17,12 +17,13 @@
#include "induction_var_range.h"
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "induction_var_analysis.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
-namespace art {
+namespace art HIDDEN {
using Value = InductionVarRange::Value;
@@ -1064,10 +1065,6 @@
HInstruction* last = range_.GenerateLastValue(phi, graph_, loop_preheader_);
ASSERT_TRUE(last->IsSub());
ExpectInt(1000, last->InputAt(0));
- ASSERT_TRUE(last->InputAt(1)->IsNeg());
- last = last->InputAt(1)->InputAt(0);
- ASSERT_TRUE(last->IsSub());
- ExpectInt(0, last->InputAt(0));
ExpectInt(1000, last->InputAt(1));
// Loop logic.
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index f73c0d3..01f97ef 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -46,7 +46,7 @@
#include "thread.h"
#include "verifier/verifier_compiler_binding.h"
-namespace art {
+namespace art HIDDEN {
// Instruction limit to control memory.
static constexpr size_t kMaximumNumberOfTotalInstructions = 1024;
@@ -72,6 +72,9 @@
// Controls the use of inline caches in AOT mode.
static constexpr bool kUseAOTInlineCaches = true;
+// Controls the use of inlining try catches.
+static constexpr bool kInlineTryCatches = true;
+
// We check for line numbers to make sure the DepthString implementation
// aligns the output nicely.
#define LOG_INTERNAL(msg) \
@@ -141,7 +144,11 @@
}
bool did_inline = false;
- bool did_set_always_throws = false;
+ // The inliner is the only phase that sets invokes as `always throwing`, and since we only run the
+ // inliner once per graph this value should always be false at the beginning of the inlining
+ // phase. This is important since we use `HasAlwaysThrowingInvokes` to know whether the inliner
+ // phase performed a relevant change in the graph.
+ DCHECK(!graph_->HasAlwaysThrowingInvokes());
// Initialize the number of instructions for the method being compiled. Recursive calls
// to HInliner::Run have already updated the instruction count.
@@ -182,7 +189,7 @@
call->GetMethodReference().PrettyMethod(/* with_signature= */ false);
// Tests prevent inlining by having $noinline$ in their method names.
if (callee_name.find("$noinline$") == std::string::npos) {
- if (TryInline(call, &did_set_always_throws)) {
+ if (TryInline(call)) {
did_inline = true;
} else if (honor_inline_directives) {
bool should_have_inlined = (callee_name.find("$inline$") != std::string::npos);
@@ -192,7 +199,7 @@
} else {
DCHECK(!honor_inline_directives);
// Normal case: try to inline.
- if (TryInline(call, &did_set_always_throws)) {
+ if (TryInline(call)) {
did_inline = true;
}
}
@@ -201,7 +208,9 @@
}
}
- return did_inline || did_set_always_throws;
+ // We return true if we either inlined at least one method, or we marked one of our methods as
+ // always throwing.
+ return did_inline || graph_->HasAlwaysThrowingInvokes();
}
static bool IsMethodOrDeclaringClassFinal(ArtMethod* method)
@@ -436,7 +445,7 @@
return throw_seen;
}
-bool HInliner::TryInline(HInvoke* invoke_instruction, /*inout*/ bool* did_set_always_throws) {
+bool HInliner::TryInline(HInvoke* invoke_instruction) {
MaybeRecordStat(stats_, MethodCompilationStat::kTryInline);
// Don't bother to move further if we know the method is unresolved or the invocation is
@@ -472,7 +481,8 @@
bool result = TryInlineAndReplace(invoke_instruction,
actual_method,
ReferenceTypeInfo::CreateInvalid(),
- /* do_rtp= */ true);
+ /* do_rtp= */ true,
+ /* is_speculative= */ false);
if (result) {
MaybeRecordStat(stats_, MethodCompilationStat::kInlinedInvokeVirtualOrInterface);
if (outermost_graph_ == graph_) {
@@ -487,11 +497,10 @@
} else {
invoke_to_analyze = invoke_instruction;
}
- // Set always throws property for non-inlined method call with single
- // target.
- if (AlwaysThrows(actual_method)) {
- invoke_to_analyze->SetAlwaysThrows(true);
- *did_set_always_throws = true;
+ // Set always throws property for non-inlined method call with single target.
+ if (invoke_instruction->AlwaysThrows() || AlwaysThrows(actual_method)) {
+ invoke_to_analyze->SetAlwaysThrows(/* always_throws= */ true);
+ graph_->SetHasAlwaysThrowingInvokes(/* value= */ true);
}
}
return result;
@@ -499,10 +508,27 @@
DCHECK(!invoke_instruction->IsInvokeStaticOrDirect());
+ // No try catch inlining allowed here, or recursively. For try catch inlining we are banking on
+ // the fact that we have a unique dex pc list. We cannot guarantee that for some TryInline methods
+ // e.g. `TryInlinePolymorphicCall`.
+ // TODO(solanes): Setting `try_catch_inlining_allowed_` to false here covers all cases from
+ // `TryInlineFromCHA` and from `TryInlineFromInlineCache` as well (e.g.
+ // `TryInlinePolymorphicCall`). Reassess to see if we can inline inline catch blocks in
+ // `TryInlineFromCHA`, `TryInlineMonomorphicCall` and `TryInlinePolymorphicCallToSameTarget`.
+
+ // We store the value to restore it since we will use the same HInliner instance for other inlinee
+ // candidates.
+ const bool previous_value = try_catch_inlining_allowed_;
+ try_catch_inlining_allowed_ = false;
+
if (TryInlineFromCHA(invoke_instruction)) {
+ try_catch_inlining_allowed_ = previous_value;
return true;
}
- return TryInlineFromInlineCache(invoke_instruction);
+
+ const bool result = TryInlineFromInlineCache(invoke_instruction);
+ try_catch_inlining_allowed_ = previous_value;
+ return result;
}
bool HInliner::TryInlineFromCHA(HInvoke* invoke_instruction) {
@@ -518,7 +544,8 @@
if (!TryInlineAndReplace(invoke_instruction,
method,
ReferenceTypeInfo::CreateInvalid(),
- /* do_rtp= */ true)) {
+ /* do_rtp= */ true,
+ /* is_speculative= */ true)) {
return false;
}
AddCHAGuard(invoke_instruction, dex_pc, cursor, bb_cursor);
@@ -786,7 +813,8 @@
if (!TryInlineAndReplace(invoke_instruction,
resolved_method,
ReferenceTypeInfo::Create(monomorphic_type, /* is_exact= */ true),
- /* do_rtp= */ false)) {
+ /* do_rtp= */ false,
+ /* is_speculative= */ true)) {
return false;
}
@@ -802,7 +830,6 @@
// Run type propagation to get the guard typed, and eventually propagate the
// type of the receiver.
ReferenceTypePropagation rtp_fixup(graph_,
- outer_compilation_unit_.GetClassLoader(),
outer_compilation_unit_.GetDexCache(),
/* is_first_run= */ false);
rtp_fixup.Run();
@@ -982,7 +1009,8 @@
!TryBuildAndInline(invoke_instruction,
method,
ReferenceTypeInfo::Create(handle, /* is_exact= */ true),
- &return_replacement)) {
+ &return_replacement,
+ /* is_speculative= */ true)) {
all_targets_inlined = false;
} else {
one_target_inlined = true;
@@ -1024,7 +1052,6 @@
// Run type propagation to get the guards typed.
ReferenceTypePropagation rtp_fixup(graph_,
- outer_compilation_unit_.GetClassLoader(),
outer_compilation_unit_.GetDexCache(),
/* is_first_run= */ false);
rtp_fixup.Run();
@@ -1160,7 +1187,8 @@
if (!TryBuildAndInline(invoke_instruction,
actual_method,
ReferenceTypeInfo::CreateInvalid(),
- &return_replacement)) {
+ &return_replacement,
+ /* is_speculative= */ true)) {
return false;
}
@@ -1215,7 +1243,6 @@
// Run type propagation to get the guard typed.
ReferenceTypePropagation rtp_fixup(graph_,
- outer_compilation_unit_.GetClassLoader(),
outer_compilation_unit_.GetDexCache(),
/* is_first_run= */ false);
rtp_fixup.Run();
@@ -1232,7 +1259,6 @@
// Actual return value has a more specific type than the method's declared
// return type. Run RTP again on the outer graph to propagate it.
ReferenceTypePropagation(graph_,
- outer_compilation_unit_.GetClassLoader(),
outer_compilation_unit_.GetDexCache(),
/* is_first_run= */ false).Run();
}
@@ -1316,11 +1342,13 @@
bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction,
ArtMethod* method,
ReferenceTypeInfo receiver_type,
- bool do_rtp) {
+ bool do_rtp,
+ bool is_speculative) {
DCHECK(!invoke_instruction->IsIntrinsic());
HInstruction* return_replacement = nullptr;
- if (!TryBuildAndInline(invoke_instruction, method, receiver_type, &return_replacement)) {
+ if (!TryBuildAndInline(
+ invoke_instruction, method, receiver_type, &return_replacement, is_speculative)) {
return false;
}
@@ -1378,6 +1406,15 @@
return false;
}
+ if (annotations::MethodIsNeverInline(*method->GetDexFile(),
+ method->GetClassDef(),
+ method->GetDexMethodIndex())) {
+ LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedNeverInlineAnnotation)
+ << "Method " << method->PrettyMethod()
+ << " has the @NeverInline annotation so it won't be inlined";
+ return false;
+ }
+
return true;
}
@@ -1397,9 +1434,25 @@
}
if (accessor.TriesSize() != 0) {
- LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedTryCatchCallee)
- << "Method " << method->PrettyMethod() << " is not inlined because of try block";
- return false;
+ if (!kInlineTryCatches) {
+ LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedTryCatchDisabled)
+ << "Method " << method->PrettyMethod()
+ << " is not inlined because inlining try catches is disabled globally";
+ return false;
+ }
+ const bool disallowed_try_catch_inlining =
+ // Direct parent is a try block.
+ invoke_instruction->GetBlock()->IsTryBlock() ||
+ // Indirect parent disallows try catch inlining.
+ !try_catch_inlining_allowed_;
+ if (disallowed_try_catch_inlining) {
+ LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedTryCatchCallee)
+ << "Method " << method->PrettyMethod()
+ << " is not inlined because it has a try catch and we are not supporting it for this"
+ << " particular call. This is could be because e.g. it would be inlined inside another"
+ << " try block, we arrived here from TryInlinePolymorphicCall, etc.";
+ return false;
+ }
}
if (invoke_instruction->IsInvokeStaticOrDirect() &&
@@ -1416,9 +1469,9 @@
return true;
}
-// Returns whether our resource limits allow inlining this method.
-bool HInliner::IsInliningBudgetAvailable(ArtMethod* method,
- const CodeItemDataAccessor& accessor) const {
+bool HInliner::IsInliningEncouraged(const HInvoke* invoke_instruction,
+ ArtMethod* method,
+ const CodeItemDataAccessor& accessor) const {
if (CountRecursiveCallsOf(method) > kMaximumNumberOfRecursiveCalls) {
LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedRecursiveBudget)
<< "Method "
@@ -1438,13 +1491,21 @@
return false;
}
+ if (invoke_instruction->GetBlock()->GetLastInstruction()->IsThrow()) {
+ LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedEndsWithThrow)
+ << "Method " << method->PrettyMethod()
+ << " is not inlined because its block ends with a throw";
+ return false;
+ }
+
return true;
}
bool HInliner::TryBuildAndInline(HInvoke* invoke_instruction,
ArtMethod* method,
ReferenceTypeInfo receiver_type,
- HInstruction** return_replacement) {
+ HInstruction** return_replacement,
+ bool is_speculative) {
// If invoke_instruction is devirtualized to a different method, give intrinsics
// another chance before we try to inline it.
if (invoke_instruction->GetResolvedMethod() != method && method->IsIntrinsic()) {
@@ -1503,12 +1564,12 @@
return false;
}
- if (!IsInliningBudgetAvailable(method, accessor)) {
+ if (!IsInliningEncouraged(invoke_instruction, method, accessor)) {
return false;
}
if (!TryBuildAndInlineHelper(
- invoke_instruction, method, receiver_type, return_replacement)) {
+ invoke_instruction, method, receiver_type, return_replacement, is_speculative)) {
return false;
}
@@ -1684,7 +1745,6 @@
Handle<mirror::DexCache> dex_cache =
graph_->GetHandleCache()->NewHandle(referrer->GetDexCache());
ReferenceTypePropagation rtp(graph_,
- outer_compilation_unit_.GetClassLoader(),
dex_cache,
/* is_first_run= */ false);
rtp.Visit(iget);
@@ -1807,7 +1867,6 @@
// are more specific than the declared ones, run RTP again on the inner graph.
if (run_rtp || ArgumentTypesMoreSpecific(invoke_instruction, resolved_method)) {
ReferenceTypePropagation(callee_graph,
- outer_compilation_unit_.GetClassLoader(),
dex_compilation_unit.GetDexCache(),
/* is_first_run= */ false).Run();
}
@@ -1821,8 +1880,9 @@
// If this function returns true, it will also set out_number_of_instructions to
// the number of instructions in the inlined body.
bool HInliner::CanInlineBody(const HGraph* callee_graph,
- const HBasicBlock* target_block,
- size_t* out_number_of_instructions) const {
+ HInvoke* invoke,
+ size_t* out_number_of_instructions,
+ bool is_speculative) const {
ArtMethod* const resolved_method = callee_graph->GetArtMethod();
HBasicBlock* exit_block = callee_graph->GetExitBlock();
@@ -1835,15 +1895,30 @@
bool has_one_return = false;
for (HBasicBlock* predecessor : exit_block->GetPredecessors()) {
- if (predecessor->GetLastInstruction()->IsThrow()) {
- if (target_block->IsTryBlock()) {
- // TODO(ngeoffray): Support adding HTryBoundary in Hgraph::InlineInto.
- LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedTryCatchCaller)
+ const HInstruction* last_instruction = predecessor->GetLastInstruction();
+ // On inlinees, we can have Return/ReturnVoid/Throw -> TryBoundary -> Exit. To check for the
+ // actual last instruction, we have to skip the TryBoundary instruction.
+ if (last_instruction->IsTryBoundary()) {
+ predecessor = predecessor->GetSinglePredecessor();
+ last_instruction = predecessor->GetLastInstruction();
+
+ // If the last instruction chain is Return/ReturnVoid -> TryBoundary -> Exit we will have to
+ // split a critical edge in InlineInto and might recompute loop information, which is
+ // unsupported for irreducible loops.
+ if (!last_instruction->IsThrow() && graph_->HasIrreducibleLoops()) {
+ DCHECK(last_instruction->IsReturn() || last_instruction->IsReturnVoid());
+ // TODO(ngeoffray): Support re-computing loop information to graphs with
+ // irreducible loops?
+ LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedIrreducibleLoopCaller)
<< "Method " << resolved_method->PrettyMethod()
- << " could not be inlined because one branch always throws and"
- << " caller is in a try/catch block";
+ << " could not be inlined because we will have to recompute the loop information and"
+ << " the caller has irreducible loops";
return false;
- } else if (graph_->GetExitBlock() == nullptr) {
+ }
+ }
+
+ if (last_instruction->IsThrow()) {
+ if (graph_->GetExitBlock() == nullptr) {
// TODO(ngeoffray): Support adding HExit in the caller graph.
LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedInfiniteLoop)
<< "Method " << resolved_method->PrettyMethod()
@@ -1853,9 +1928,10 @@
} else if (graph_->HasIrreducibleLoops()) {
// TODO(ngeoffray): Support re-computing loop information to graphs with
// irreducible loops?
- VLOG(compiler) << "Method " << resolved_method->PrettyMethod()
- << " could not be inlined because one branch always throws and"
- << " caller has irreducible loops";
+ LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedIrreducibleLoopCaller)
+ << "Method " << resolved_method->PrettyMethod()
+ << " could not be inlined because one branch always throws and"
+ << " the caller has irreducible loops";
return false;
}
} else {
@@ -1864,6 +1940,15 @@
}
if (!has_one_return) {
+ if (!is_speculative) {
+ // If we know that the method always throws with the particular parameters, set it as such.
+ // This is better than using the dex instructions as we have more information about this
+ // particular call. We don't mark speculative inlines (e.g. the ones from the inline cache) as
+ // always throwing since they might not throw when executed.
+ invoke->SetAlwaysThrows(/* always_throws= */ true);
+ graph_->SetHasAlwaysThrowingInvokes(/* value= */ true);
+ }
+
LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedAlwaysThrows)
<< "Method " << resolved_method->PrettyMethod()
<< " could not be inlined because it always throws";
@@ -1882,7 +1967,7 @@
if (block->GetLoopInformation()->IsIrreducible()) {
// Don't inline methods with irreducible loops, they could prevent some
// optimizations to run.
- LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedIrreducibleLoop)
+ LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedIrreducibleLoopCallee)
<< "Method " << resolved_method->PrettyMethod()
<< " could not be inlined because it contains an irreducible loop";
return false;
@@ -1964,7 +2049,8 @@
bool HInliner::TryBuildAndInlineHelper(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
ReferenceTypeInfo receiver_type,
- HInstruction** return_replacement) {
+ HInstruction** return_replacement,
+ bool is_speculative) {
DCHECK(!(resolved_method->IsStatic() && receiver_type.IsValid()));
const dex::CodeItem* code_item = resolved_method->GetCodeItem();
const DexFile& callee_dex_file = *resolved_method->GetDexFile();
@@ -2057,10 +2143,18 @@
SubstituteArguments(callee_graph, invoke_instruction, receiver_type, dex_compilation_unit);
- RunOptimizations(callee_graph, code_item, dex_compilation_unit);
+ const bool try_catch_inlining_allowed_for_recursive_inline =
+ // It was allowed previously.
+ try_catch_inlining_allowed_ &&
+ // The current invoke is not a try block.
+ !invoke_instruction->GetBlock()->IsTryBlock();
+ RunOptimizations(callee_graph,
+ code_item,
+ dex_compilation_unit,
+ try_catch_inlining_allowed_for_recursive_inline);
size_t number_of_instructions = 0;
- if (!CanInlineBody(callee_graph, invoke_instruction->GetBlock(), &number_of_instructions)) {
+ if (!CanInlineBody(callee_graph, invoke_instruction, &number_of_instructions, is_speculative)) {
return false;
}
@@ -2095,16 +2189,17 @@
void HInliner::RunOptimizations(HGraph* callee_graph,
const dex::CodeItem* code_item,
- const DexCompilationUnit& dex_compilation_unit) {
+ const DexCompilationUnit& dex_compilation_unit,
+ bool try_catch_inlining_allowed_for_recursive_inline) {
// Note: if the outermost_graph_ is being compiled OSR, we should not run any
// optimization that could lead to a HDeoptimize. The following optimizations do not.
HDeadCodeElimination dce(callee_graph, inline_stats_, "dead_code_elimination$inliner");
- HConstantFolding fold(callee_graph, "constant_folding$inliner");
+ HConstantFolding fold(callee_graph, inline_stats_, "constant_folding$inliner");
InstructionSimplifier simplify(callee_graph, codegen_, inline_stats_);
HOptimization* optimizations[] = {
- &simplify,
&fold,
+ &simplify,
&dce,
};
@@ -2141,7 +2236,8 @@
total_number_of_dex_registers_ + accessor.RegistersSize(),
total_number_of_instructions_ + number_of_instructions,
this,
- depth_ + 1);
+ depth_ + 1,
+ try_catch_inlining_allowed_for_recursive_inline);
inliner.Run();
}
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index a2c2085..af067da 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -17,13 +17,14 @@
#ifndef ART_COMPILER_OPTIMIZING_INLINER_H_
#define ART_COMPILER_OPTIMIZING_INLINER_H_
+#include "base/macros.h"
#include "dex/dex_file_types.h"
#include "dex/invoke_type.h"
#include "jit/profiling_info.h"
#include "optimization.h"
#include "profile/profile_compilation_info.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
class DexCompilationUnit;
@@ -42,7 +43,8 @@
size_t total_number_of_dex_registers,
size_t total_number_of_instructions,
HInliner* parent,
- size_t depth = 0,
+ size_t depth,
+ bool try_catch_inlining_allowed,
const char* name = kInlinerPassName)
: HOptimization(outer_graph, name, stats),
outermost_graph_(outermost_graph),
@@ -54,6 +56,7 @@
parent_(parent),
depth_(depth),
inlining_budget_(0),
+ try_catch_inlining_allowed_(try_catch_inlining_allowed),
inline_stats_(nullptr) {}
bool Run() override;
@@ -70,9 +73,7 @@
kInlineCacheMissingTypes = 5
};
- // We set `did_set_always_throws` as true if we analyzed `invoke_instruction` and it always
- // throws.
- bool TryInline(HInvoke* invoke_instruction, /*inout*/ bool* did_set_always_throws);
+ bool TryInline(HInvoke* invoke_instruction);
// Try to inline `resolved_method` in place of `invoke_instruction`. `do_rtp` is whether
// reference type propagation can run after the inlining. If the inlining is successful, this
@@ -80,19 +81,22 @@
bool TryInlineAndReplace(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
ReferenceTypeInfo receiver_type,
- bool do_rtp)
+ bool do_rtp,
+ bool is_speculative)
REQUIRES_SHARED(Locks::mutator_lock_);
bool TryBuildAndInline(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
ReferenceTypeInfo receiver_type,
- HInstruction** return_replacement)
+ HInstruction** return_replacement,
+ bool is_speculative)
REQUIRES_SHARED(Locks::mutator_lock_);
bool TryBuildAndInlineHelper(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
ReferenceTypeInfo receiver_type,
- HInstruction** return_replacement)
+ HInstruction** return_replacement,
+ bool is_speculative)
REQUIRES_SHARED(Locks::mutator_lock_);
// Substitutes parameters in the callee graph with their values from the caller.
@@ -105,8 +109,9 @@
// Run simple optimizations on `callee_graph`.
void RunOptimizations(HGraph* callee_graph,
const dex::CodeItem* code_item,
- const DexCompilationUnit& dex_compilation_unit)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ const DexCompilationUnit& dex_compilation_unit,
+ bool try_catch_inlining_allowed_for_recursive_inline)
+ REQUIRES_SHARED(Locks::mutator_lock_);
// Try to recognize known simple patterns and replace invoke call with appropriate instructions.
bool TryPatternSubstitution(HInvoke* invoke_instruction,
@@ -129,12 +134,14 @@
const CodeItemDataAccessor& accessor) const
REQUIRES_SHARED(Locks::mutator_lock_);
- // Returns whether the inlining budget allows inlining method.
+ // Returns whether inlining is encouraged.
//
// For example, this checks whether the function has grown too large and
// inlining should be prevented.
- bool IsInliningBudgetAvailable(art::ArtMethod* method, const CodeItemDataAccessor& accessor) const
- REQUIRES_SHARED(Locks::mutator_lock_);
+ bool IsInliningEncouraged(const HInvoke* invoke_instruction,
+ art::ArtMethod* method,
+ const CodeItemDataAccessor& accessor) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
// Inspects the body of a method (callee_graph) and returns whether it can be
// inlined.
@@ -142,8 +149,9 @@
// This checks for instructions and constructs that we do not support
// inlining, such as inlining a throw instruction into a try block.
bool CanInlineBody(const HGraph* callee_graph,
- const HBasicBlock* target_block,
- size_t* out_number_of_instructions) const
+ HInvoke* invoke,
+ size_t* out_number_of_instructions,
+ bool is_speculative) const
REQUIRES_SHARED(Locks::mutator_lock_);
// Create a new HInstanceFieldGet.
@@ -320,6 +328,9 @@
// The budget left for inlining, in number of instructions.
size_t inlining_budget_;
+ // States if we are allowing try catch inlining to occur at this particular instance of inlining.
+ bool try_catch_inlining_allowed_;
+
// Used to record stats about optimizations on the inlined graph.
// If the inlining is successful, these stats are merged to the caller graph's stats.
OptimizingCompilerStats* inline_stats_;
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index e0bdd09..f9a5138 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -42,7 +42,7 @@
#include "ssa_builder.h"
#include "well_known_classes.h"
-namespace art {
+namespace art HIDDEN {
namespace {
@@ -343,6 +343,10 @@
// Suspend checks were inserted into loop headers during building of dominator tree.
DCHECK(block->GetFirstInstruction()->IsSuspendCheck());
return block->GetFirstInstruction() != block->GetLastInstruction();
+ } else if (block->IsCatchBlock()) {
+ // Nops were inserted into the beginning of catch blocks.
+ DCHECK(block->GetFirstInstruction()->IsNop());
+ return block->GetFirstInstruction() != block->GetLastInstruction();
} else {
return !block->GetInstructions().IsEmpty();
}
@@ -387,6 +391,11 @@
// This is slightly odd because the loop header might not be empty (TryBoundary).
// But we're still creating the environment with locals from the top of the block.
InsertInstructionAtTop(suspend_check);
+ } else if (current_block_->IsCatchBlock()) {
+ // We add an environment emitting instruction at the beginning of each catch block, in order
+ // to support try catch inlining.
+ // This is slightly odd because the catch block might not be empty (TryBoundary).
+ InsertInstructionAtTop(new (allocator_) HNop(block_dex_pc, /* needs_environment= */ true));
}
if (block_dex_pc == kNoDexPc || current_block_ != block_builder_->GetBlockAt(block_dex_pc)) {
@@ -414,7 +423,7 @@
}
if (native_debuggable && native_debug_info_locations->IsBitSet(dex_pc)) {
- AppendInstruction(new (allocator_) HNativeDebugInfo(dex_pc));
+ AppendInstruction(new (allocator_) HNop(dex_pc, /* needs_environment= */ true));
}
// Note: There may be no Thread for gtests.
@@ -972,11 +981,11 @@
*imt_or_vtable_index = resolved_method->GetVtableIndex();
} else if (*invoke_type == kInterface) {
// For HInvokeInterface we need the IMT index.
- *imt_or_vtable_index = ImTable::GetImtIndex(resolved_method);
+ *imt_or_vtable_index = resolved_method->GetImtIndex();
+ DCHECK_EQ(*imt_or_vtable_index, ImTable::GetImtIndex(resolved_method));
}
- *is_string_constructor =
- resolved_method->IsConstructor() && resolved_method->GetDeclaringClass()->IsStringClass();
+ *is_string_constructor = resolved_method->IsStringConstructor();
return resolved_method;
}
diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h
index 817fbaa..3d65d8f 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -18,6 +18,7 @@
#define ART_COMPILER_OPTIMIZING_INSTRUCTION_BUILDER_H_
#include "base/array_ref.h"
+#include "base/macros.h"
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
#include "data_type.h"
@@ -27,7 +28,7 @@
#include "handle.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
class ArenaBitVector;
class ArtField;
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 789f077..86b9dba 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -31,7 +31,7 @@
#include "sharpening.h"
#include "string_builder_append.h"
-namespace art {
+namespace art HIDDEN {
// Whether to run an exhaustive test of individual HInstructions cloning when each instruction
// is replaced with its copy if it is clonable.
@@ -1142,13 +1142,13 @@
if (value->IsArrayGet()) {
if (value->AsArrayGet()->GetArray() == instruction->GetArray()) {
// If the code is just swapping elements in the array, no need for a type check.
- instruction->ClearNeedsTypeCheck();
+ instruction->ClearTypeCheck();
return;
}
}
if (value->IsNullConstant()) {
- instruction->ClearNeedsTypeCheck();
+ instruction->ClearTypeCheck();
return;
}
@@ -1160,13 +1160,13 @@
}
if (value_rti.IsValid() && array_rti.CanArrayHold(value_rti)) {
- instruction->ClearNeedsTypeCheck();
+ instruction->ClearTypeCheck();
return;
}
if (array_rti.IsObjectArray()) {
if (array_rti.IsExact()) {
- instruction->ClearNeedsTypeCheck();
+ instruction->ClearTypeCheck();
return;
}
instruction->SetStaticTypeOfArrayIsObjectArray();
@@ -1860,13 +1860,16 @@
// Search HDiv having the specified dividend and divisor which is in the specified basic block.
// Return nullptr if nothing has been found.
-static HInstruction* FindDivWithInputsInBasicBlock(HInstruction* dividend,
- HInstruction* divisor,
- HBasicBlock* basic_block) {
+static HDiv* FindDivWithInputsInBasicBlock(HInstruction* dividend,
+ HInstruction* divisor,
+ HBasicBlock* basic_block) {
for (const HUseListNode<HInstruction*>& use : dividend->GetUses()) {
HInstruction* user = use.GetUser();
- if (user->GetBlock() == basic_block && user->IsDiv() && user->InputAt(1) == divisor) {
- return user;
+ if (user->GetBlock() == basic_block &&
+ user->IsDiv() &&
+ user->InputAt(0) == dividend &&
+ user->InputAt(1) == divisor) {
+ return user->AsDiv();
}
}
return nullptr;
@@ -1900,7 +1903,7 @@
}
}
- HInstruction* quotient = FindDivWithInputsInBasicBlock(dividend, divisor, basic_block);
+ HDiv* quotient = FindDivWithInputsInBasicBlock(dividend, divisor, basic_block);
if (quotient == nullptr) {
return;
}
@@ -2647,15 +2650,13 @@
// Collect args and check for unexpected uses.
// We expect one call to a constructor with no arguments, one constructor fence (unless
// eliminated), some number of append calls and one call to StringBuilder.toString().
- bool constructor_inlined = false;
bool seen_constructor = false;
bool seen_constructor_fence = false;
bool seen_to_string = false;
uint32_t format = 0u;
uint32_t num_args = 0u;
+ bool has_fp_args = false;
HInstruction* args[StringBuilderAppend::kMaxArgs]; // Added in reverse order.
- // When inlining, `maybe_new_array` tracks an environment use that we want to allow.
- HInstruction* maybe_new_array = nullptr;
for (HBackwardInstructionIterator iter(block->GetInstructions()); !iter.Done(); iter.Advance()) {
HInstruction* user = iter.Current();
// Instructions of interest apply to `sb`, skip those that do not involve `sb`.
@@ -2700,6 +2701,14 @@
case Intrinsics::kStringBuilderAppendLong:
arg = StringBuilderAppend::Argument::kLong;
break;
+ case Intrinsics::kStringBuilderAppendFloat:
+ arg = StringBuilderAppend::Argument::kFloat;
+ has_fp_args = true;
+ break;
+ case Intrinsics::kStringBuilderAppendDouble:
+ arg = StringBuilderAppend::Argument::kDouble;
+ has_fp_args = true;
+ break;
case Intrinsics::kStringBuilderAppendCharSequence: {
ReferenceTypeInfo rti = user->AsInvokeVirtual()->InputAt(1)->GetReferenceTypeInfo();
if (!rti.IsValid()) {
@@ -2719,10 +2728,6 @@
}
break;
}
- case Intrinsics::kStringBuilderAppendFloat:
- case Intrinsics::kStringBuilderAppendDouble:
- // TODO: Unimplemented, needs to call FloatingDecimal.getBinaryToASCIIConverter().
- return false;
default: {
return false;
}
@@ -2736,25 +2741,13 @@
format = (format << StringBuilderAppend::kBitsPerArg) | static_cast<uint32_t>(arg);
args[num_args] = as_invoke_virtual->InputAt(1u);
++num_args;
- } else if (!seen_constructor) {
- // At this point, we should see the constructor. However, we might have inlined it so we have
- // to take care of both cases. We accept only the constructor with no extra arguments. This
- // means that if we inline it, we have to check it is setting its field to a new array.
- if (user->IsInvokeStaticOrDirect() &&
- user->AsInvokeStaticOrDirect()->GetResolvedMethod() != nullptr &&
- user->AsInvokeStaticOrDirect()->GetResolvedMethod()->IsConstructor() &&
- user->AsInvokeStaticOrDirect()->GetNumberOfArguments() == 1u) {
- constructor_inlined = false;
- } else if (user->IsInstanceFieldSet() &&
- user->AsInstanceFieldSet()->GetFieldType() == DataType::Type::kReference &&
- user->AsInstanceFieldSet()->InputAt(0) == sb &&
- user->AsInstanceFieldSet()->GetValue()->IsNewArray()) {
- maybe_new_array = user->AsInstanceFieldSet()->GetValue();
- constructor_inlined = true;
- } else {
- // We were expecting a constructor but we haven't seen it. Abort optimization.
- return false;
- }
+ } else if (user->IsInvokeStaticOrDirect() &&
+ user->AsInvokeStaticOrDirect()->GetResolvedMethod() != nullptr &&
+ user->AsInvokeStaticOrDirect()->GetResolvedMethod()->IsConstructor() &&
+ user->AsInvokeStaticOrDirect()->GetNumberOfArguments() == 1u) {
+ // After arguments, we should see the constructor.
+ // We accept only the constructor with no extra arguments.
+ DCHECK(!seen_constructor);
DCHECK(!seen_constructor_fence);
seen_constructor = true;
} else if (user->IsConstructorFence()) {
@@ -2780,17 +2773,6 @@
// Accept only calls on the StringBuilder (which shall all be removed).
// TODO: Carve-out for const-string? Or rely on environment pruning (to be implemented)?
if (holder->InputCount() == 0 || holder->InputAt(0) != sb) {
- // When inlining the constructor, we have a NewArray and may have a LoadClass as an
- // environment use.
- if (constructor_inlined) {
- if (holder == maybe_new_array) {
- continue;
- }
- if (holder == maybe_new_array->InputAt(0)) {
- DCHECK(holder->IsLoadClass());
- continue;
- }
- }
return false;
}
}
@@ -2798,8 +2780,8 @@
// Create replacement instruction.
HIntConstant* fmt = block->GetGraph()->GetIntConstant(static_cast<int32_t>(format));
ArenaAllocator* allocator = block->GetGraph()->GetAllocator();
- HStringBuilderAppend* append =
- new (allocator) HStringBuilderAppend(fmt, num_args, allocator, invoke->GetDexPc());
+ HStringBuilderAppend* append = new (allocator) HStringBuilderAppend(
+ fmt, num_args, has_fp_args, allocator, invoke->GetDexPc());
append->SetReferenceTypeInfo(invoke->GetReferenceTypeInfo());
for (size_t i = 0; i != num_args; ++i) {
append->SetArgumentAt(i, args[num_args - 1u - i]);
@@ -2824,33 +2806,6 @@
while (sb->HasNonEnvironmentUses()) {
block->RemoveInstruction(sb->GetUses().front().GetUser());
}
- if (constructor_inlined) {
- // We need to remove the inlined constructor instructions,
- // and all remaining environment uses (if any).
- DCHECK(sb->HasEnvironmentUses());
- DCHECK(maybe_new_array != nullptr);
- DCHECK(maybe_new_array->IsNewArray());
- DCHECK(maybe_new_array->HasNonEnvironmentUses());
- HInstruction* fence = maybe_new_array->GetUses().front().GetUser();
- DCHECK(fence->IsConstructorFence());
- block->RemoveInstruction(fence);
- block->RemoveInstruction(maybe_new_array);
- if (sb->HasEnvironmentUses()) {
- // We know the only remaining uses are from the LoadClass.
- HInstruction* load_class = maybe_new_array->InputAt(0);
- DCHECK(load_class->IsLoadClass());
- for (HEnvironment* env = load_class->GetEnvironment();
- env != nullptr;
- env = env->GetParent()) {
- for (size_t i = 0, size = env->Size(); i != size; ++i) {
- if (env->GetInstructionAt(i) == sb) {
- env->RemoveAsUserOfInput(i);
- env->SetRawEnvAt(i, /*instruction=*/ nullptr);
- }
- }
- }
- }
- }
DCHECK(!sb->HasEnvironmentUses());
block->RemoveInstruction(sb);
return true;
diff --git a/compiler/optimizing/instruction_simplifier.h b/compiler/optimizing/instruction_simplifier.h
index feea771..98ebaaf 100644
--- a/compiler/optimizing/instruction_simplifier.h
+++ b/compiler/optimizing/instruction_simplifier.h
@@ -17,11 +17,12 @@
#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_H_
#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
#include "optimizing_compiler_stats.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc
index 1371ea7..88895b6 100644
--- a/compiler/optimizing/instruction_simplifier_arm.cc
+++ b/compiler/optimizing/instruction_simplifier_arm.cc
@@ -23,7 +23,7 @@
#include "mirror/string.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
using helpers::CanFitInShifterOperand;
using helpers::HasShifterOperand;
diff --git a/compiler/optimizing/instruction_simplifier_arm.h b/compiler/optimizing/instruction_simplifier_arm.h
index fca9341..0517e4f 100644
--- a/compiler/optimizing/instruction_simplifier_arm.h
+++ b/compiler/optimizing/instruction_simplifier_arm.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_
#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
class InstructionSimplifierArm : public HOptimization {
diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc
index a6ec020..bed7ff7 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.cc
+++ b/compiler/optimizing/instruction_simplifier_arm64.cc
@@ -21,7 +21,7 @@
#include "mirror/array-inl.h"
#include "mirror/string.h"
-namespace art {
+namespace art HIDDEN {
using helpers::CanFitInShifterOperand;
using helpers::HasShifterOperand;
diff --git a/compiler/optimizing/instruction_simplifier_arm64.h b/compiler/optimizing/instruction_simplifier_arm64.h
index 8d93c01..374638a 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.h
+++ b/compiler/optimizing/instruction_simplifier_arm64.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM64_H_
#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM64_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
class InstructionSimplifierArm64 : public HOptimization {
diff --git a/compiler/optimizing/instruction_simplifier_shared.cc b/compiler/optimizing/instruction_simplifier_shared.cc
index dc60ba6..34daae2 100644
--- a/compiler/optimizing/instruction_simplifier_shared.cc
+++ b/compiler/optimizing/instruction_simplifier_shared.cc
@@ -18,7 +18,7 @@
#include "mirror/array-inl.h"
-namespace art {
+namespace art HIDDEN {
namespace {
@@ -244,7 +244,7 @@
// The access may require a runtime call or the original array pointer.
return false;
}
- if (kEmitCompilerReadBarrier &&
+ if (gUseReadBarrier &&
!kUseBakerReadBarrier &&
access->IsArrayGet() &&
access->GetType() == DataType::Type::kReference) {
diff --git a/compiler/optimizing/instruction_simplifier_shared.h b/compiler/optimizing/instruction_simplifier_shared.h
index 876ed21..ddc3a86 100644
--- a/compiler/optimizing/instruction_simplifier_shared.h
+++ b/compiler/optimizing/instruction_simplifier_shared.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_
#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_
+#include "base/macros.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
namespace helpers {
diff --git a/compiler/optimizing/instruction_simplifier_test.cc b/compiler/optimizing/instruction_simplifier_test.cc
index c7c5b12..d360953 100644
--- a/compiler/optimizing/instruction_simplifier_test.cc
+++ b/compiler/optimizing/instruction_simplifier_test.cc
@@ -26,7 +26,7 @@
#include "optimizing/data_type.h"
#include "optimizing_unit_test.h"
-namespace art {
+namespace art HIDDEN {
namespace mirror {
class ClassExt;
diff --git a/compiler/optimizing/instruction_simplifier_x86.cc b/compiler/optimizing/instruction_simplifier_x86.cc
index 2d8f94a..805dd49 100644
--- a/compiler/optimizing/instruction_simplifier_x86.cc
+++ b/compiler/optimizing/instruction_simplifier_x86.cc
@@ -17,7 +17,7 @@
#include "instruction_simplifier_x86_shared.h"
#include "code_generator_x86.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
diff --git a/compiler/optimizing/instruction_simplifier_x86.h b/compiler/optimizing/instruction_simplifier_x86.h
index 6f10006..25ebe20 100644
--- a/compiler/optimizing/instruction_simplifier_x86.h
+++ b/compiler/optimizing/instruction_simplifier_x86.h
@@ -16,10 +16,11 @@
#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_H_
#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
namespace x86 {
diff --git a/compiler/optimizing/instruction_simplifier_x86_64.cc b/compiler/optimizing/instruction_simplifier_x86_64.cc
index 56c6b41..b783918 100644
--- a/compiler/optimizing/instruction_simplifier_x86_64.cc
+++ b/compiler/optimizing/instruction_simplifier_x86_64.cc
@@ -17,7 +17,7 @@
#include "instruction_simplifier_x86_shared.h"
#include "code_generator_x86_64.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
diff --git a/compiler/optimizing/instruction_simplifier_x86_64.h b/compiler/optimizing/instruction_simplifier_x86_64.h
index 6cae24d..1654dc4 100644
--- a/compiler/optimizing/instruction_simplifier_x86_64.h
+++ b/compiler/optimizing/instruction_simplifier_x86_64.h
@@ -16,10 +16,11 @@
#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_64_H_
#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_64_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
diff --git a/compiler/optimizing/instruction_simplifier_x86_shared.cc b/compiler/optimizing/instruction_simplifier_x86_shared.cc
index 2805abb..74c5ca2 100644
--- a/compiler/optimizing/instruction_simplifier_x86_shared.cc
+++ b/compiler/optimizing/instruction_simplifier_x86_shared.cc
@@ -14,9 +14,10 @@
*/
#include "instruction_simplifier_x86_shared.h"
+
#include "nodes_x86.h"
-namespace art {
+namespace art HIDDEN {
bool TryCombineAndNot(HAnd* instruction) {
DataType::Type type = instruction->GetType();
diff --git a/compiler/optimizing/instruction_simplifier_x86_shared.h b/compiler/optimizing/instruction_simplifier_x86_shared.h
index 7f94d7e..1a44d0f 100644
--- a/compiler/optimizing/instruction_simplifier_x86_shared.h
+++ b/compiler/optimizing/instruction_simplifier_x86_shared.h
@@ -16,13 +16,16 @@
#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_SHARED_H_
#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_SHARED_H_
+#include "base/macros.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
+
bool TryCombineAndNot(HAnd* instruction);
bool TryGenerateResetLeastSetBit(HAnd* instruction);
bool TryGenerateMaskUptoLeastSetBit(HXor* instruction);
bool AreLeastSetBitInputs(HInstruction* to_test, HInstruction* other);
+
} // namespace art
#endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_X86_SHARED_H_
diff --git a/compiler/optimizing/intrinsic_objects.cc b/compiler/optimizing/intrinsic_objects.cc
index 5f6f562..7e54211 100644
--- a/compiler/optimizing/intrinsic_objects.cc
+++ b/compiler/optimizing/intrinsic_objects.cc
@@ -22,7 +22,7 @@
#include "image.h"
#include "obj_ptr-inl.h"
-namespace art {
+namespace art HIDDEN {
static constexpr size_t kIntrinsicObjectsOffset =
enum_cast<size_t>(ImageHeader::kIntrinsicObjectsStart);
diff --git a/compiler/optimizing/intrinsic_objects.h b/compiler/optimizing/intrinsic_objects.h
index ed764bd..d750f29 100644
--- a/compiler/optimizing/intrinsic_objects.h
+++ b/compiler/optimizing/intrinsic_objects.h
@@ -19,9 +19,10 @@
#include "base/bit_field.h"
#include "base/bit_utils.h"
+#include "base/macros.h"
#include "base/mutex.h"
-namespace art {
+namespace art HIDDEN {
class ClassLinker;
template <class MirrorType> class ObjPtr;
@@ -56,15 +57,15 @@
}
// Functions for retrieving data for Integer.valueOf().
- static ObjPtr<mirror::ObjectArray<mirror::Object>> LookupIntegerCache(
+ EXPORT static ObjPtr<mirror::ObjectArray<mirror::Object>> LookupIntegerCache(
Thread* self, ClassLinker* class_linker) REQUIRES_SHARED(Locks::mutator_lock_);
- static ObjPtr<mirror::ObjectArray<mirror::Object>> GetIntegerValueOfCache(
+ EXPORT static ObjPtr<mirror::ObjectArray<mirror::Object>> GetIntegerValueOfCache(
ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects)
REQUIRES_SHARED(Locks::mutator_lock_);
- static ObjPtr<mirror::Object> GetIntegerValueOfObject(
+ EXPORT static ObjPtr<mirror::Object> GetIntegerValueOfObject(
ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects,
uint32_t index) REQUIRES_SHARED(Locks::mutator_lock_);
- static MemberOffset GetIntegerValueOfArrayDataOffset(
+ EXPORT static MemberOffset GetIntegerValueOfArrayDataOffset(
ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index f2d2b45..aa5e4f3 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -32,7 +32,7 @@
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
-namespace art {
+namespace art HIDDEN {
std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic) {
switch (intrinsic) {
@@ -392,7 +392,7 @@
}
void IntrinsicVisitor::CreateReferenceRefersToLocations(HInvoke* invoke) {
- if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
+ if (gUseReadBarrier && !kUseBakerReadBarrier) {
// Unimplemented for non-Baker read barrier.
return;
}
diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h
index 5109882..893cd04 100644
--- a/compiler/optimizing/intrinsics.h
+++ b/compiler/optimizing/intrinsics.h
@@ -17,12 +17,13 @@
#ifndef ART_COMPILER_OPTIMIZING_INTRINSICS_H_
#define ART_COMPILER_OPTIMIZING_INTRINSICS_H_
+#include "base/macros.h"
#include "code_generator.h"
#include "nodes.h"
#include "optimization.h"
#include "parallel_move_resolver.h"
-namespace art {
+namespace art HIDDEN {
class DexFile;
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index 646f4f2..40df479 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -46,7 +46,7 @@
#include "aarch64/macro-assembler-aarch64.h"
#pragma GCC diagnostic pop
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
@@ -92,7 +92,7 @@
public:
ReadBarrierSystemArrayCopySlowPathARM64(HInstruction* instruction, Location tmp)
: SlowPathCodeARM64(instruction), tmp_(tmp) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
DCHECK(kUseBakerReadBarrier);
}
@@ -711,7 +711,7 @@
Location trg_loc = locations->Out();
Register trg = RegisterFrom(trg_loc, type);
- if (type == DataType::Type::kReference && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (type == DataType::Type::kReference && gUseReadBarrier && kUseBakerReadBarrier) {
// UnsafeGetObject/UnsafeGetObjectVolatile with Baker's read barrier case.
Register temp = WRegisterFrom(locations->GetTemp(0));
MacroAssembler* masm = codegen->GetVIXLAssembler();
@@ -754,7 +754,7 @@
}
static void CreateIntIntIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) {
- bool can_call = kEmitCompilerReadBarrier && UnsafeGetIntrinsicOnCallList(invoke->GetIntrinsic());
+ bool can_call = gUseReadBarrier && UnsafeGetIntrinsicOnCallList(invoke->GetIntrinsic());
LocationSummary* locations =
new (allocator) LocationSummary(invoke,
can_call
@@ -1096,7 +1096,7 @@
}
static void CreateUnsafeCASLocations(ArenaAllocator* allocator, HInvoke* invoke) {
- const bool can_call = kEmitCompilerReadBarrier && IsUnsafeCASObject(invoke);
+ const bool can_call = gUseReadBarrier && IsUnsafeCASObject(invoke);
LocationSummary* locations =
new (allocator) LocationSummary(invoke,
can_call
@@ -1448,7 +1448,7 @@
vixl::aarch64::Label* exit_loop = &exit_loop_label;
vixl::aarch64::Label* cmp_failure = &exit_loop_label;
- if (kEmitCompilerReadBarrier && type == DataType::Type::kReference) {
+ if (gUseReadBarrier && type == DataType::Type::kReference) {
// We need to store the `old_value` in a non-scratch register to make sure
// the read barrier in the slow path does not clobber it.
old_value = WRegisterFrom(locations->GetTemp(0)); // The old value from main path.
@@ -1523,12 +1523,12 @@
}
void IntrinsicLocationsBuilderARM64::VisitJdkUnsafeCompareAndSetObject(HInvoke* invoke) {
// The only supported read barrier implementation is the Baker-style read barriers.
- if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
+ if (gUseReadBarrier && !kUseBakerReadBarrier) {
return;
}
CreateUnsafeCASLocations(allocator_, invoke);
- if (kEmitCompilerReadBarrier) {
+ if (gUseReadBarrier) {
// We need two non-scratch temporary registers for read barrier.
LocationSummary* locations = invoke->GetLocations();
if (kUseBakerReadBarrier) {
@@ -1578,7 +1578,7 @@
}
void IntrinsicCodeGeneratorARM64::VisitJdkUnsafeCompareAndSetObject(HInvoke* invoke) {
// The only supported read barrier implementation is the Baker-style read barriers.
- DCHECK_IMPLIES(kEmitCompilerReadBarrier, kUseBakerReadBarrier);
+ DCHECK_IMPLIES(gUseReadBarrier, kUseBakerReadBarrier);
GenUnsafeCas(invoke, DataType::Type::kReference, codegen_);
}
@@ -2814,7 +2814,7 @@
void IntrinsicLocationsBuilderARM64::VisitSystemArrayCopy(HInvoke* invoke) {
// The only read barrier implementation supporting the
// SystemArrayCopy intrinsic is the Baker-style read barriers.
- if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
+ if (gUseReadBarrier && !kUseBakerReadBarrier) {
return;
}
@@ -2866,7 +2866,7 @@
locations->AddTemp(Location::RequiresRegister());
locations->AddTemp(Location::RequiresRegister());
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// Temporary register IP0, obtained from the VIXL scratch register
// pool, cannot be used in ReadBarrierSystemArrayCopySlowPathARM64
// (because that register is clobbered by ReadBarrierMarkRegX
@@ -2884,7 +2884,7 @@
void IntrinsicCodeGeneratorARM64::VisitSystemArrayCopy(HInvoke* invoke) {
// The only read barrier implementation supporting the
// SystemArrayCopy intrinsic is the Baker-style read barriers.
- DCHECK_IMPLIES(kEmitCompilerReadBarrier, kUseBakerReadBarrier);
+ DCHECK_IMPLIES(gUseReadBarrier, kUseBakerReadBarrier);
MacroAssembler* masm = GetVIXLAssembler();
LocationSummary* locations = invoke->GetLocations();
@@ -2991,7 +2991,7 @@
UseScratchRegisterScope temps(masm);
Location temp3_loc; // Used only for Baker read barrier.
Register temp3;
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
temp3_loc = locations->GetTemp(2);
temp3 = WRegisterFrom(temp3_loc);
} else {
@@ -3004,7 +3004,7 @@
// or the destination is Object[]. If none of these checks succeed, we go to the
// slow path.
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
if (!optimizations.GetSourceIsNonPrimitiveArray()) {
// /* HeapReference<Class> */ temp1 = src->klass_
codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
@@ -3165,7 +3165,7 @@
} else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
// Bail out if the source is not a non primitive array.
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// /* HeapReference<Class> */ temp1 = src->klass_
codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
temp1_loc,
@@ -3215,7 +3215,7 @@
__ Cbz(WRegisterFrom(length), &done);
}
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// TODO: Also convert this intrinsic to the IsGcMarking strategy?
// SystemArrayCopy implementation for Baker read barriers (see
@@ -3335,7 +3335,7 @@
}
// We only need one card marking on the destination array.
- codegen_->MarkGCCard(dest.W(), Register(), /* value_can_be_null= */ false);
+ codegen_->MarkGCCard(dest.W(), Register(), /* emit_null_check= */ false);
__ Bind(intrinsic_slow_path->GetExitLabel());
}
@@ -3451,7 +3451,7 @@
void IntrinsicLocationsBuilderARM64::VisitReferenceGetReferent(HInvoke* invoke) {
IntrinsicVisitor::CreateReferenceGetReferentLocations(invoke, codegen_);
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier && invoke->GetLocations() != nullptr) {
+ if (gUseReadBarrier && kUseBakerReadBarrier && invoke->GetLocations() != nullptr) {
invoke->GetLocations()->AddTemp(Location::RequiresRegister());
}
}
@@ -3466,7 +3466,7 @@
SlowPathCodeARM64* slow_path = new (GetAllocator()) IntrinsicSlowPathARM64(invoke);
codegen_->AddSlowPath(slow_path);
- if (kEmitCompilerReadBarrier) {
+ if (gUseReadBarrier) {
// Check self->GetWeakRefAccessEnabled().
UseScratchRegisterScope temps(masm);
Register temp = temps.AcquireW();
@@ -3493,7 +3493,7 @@
// Load the value from the field.
uint32_t referent_offset = mirror::Reference::ReferentOffset().Uint32Value();
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
out,
WRegisterFrom(obj),
@@ -3533,7 +3533,7 @@
__ Cmp(tmp, other);
- if (kEmitCompilerReadBarrier) {
+ if (gUseReadBarrier) {
DCHECK(kUseBakerReadBarrier);
vixl::aarch64::Label calculate_result;
@@ -4629,7 +4629,7 @@
method.X(),
ArtField::DeclaringClassOffset().Int32Value(),
/*fixup_label=*/ nullptr,
- kCompilerReadBarrierOption);
+ gCompilerReadBarrierOption);
}
}
} else {
@@ -4683,7 +4683,7 @@
}
// Add a temporary for offset.
- if ((kEmitCompilerReadBarrier && !kUseBakerReadBarrier) &&
+ if ((gUseReadBarrier && !kUseBakerReadBarrier) &&
GetExpectedVarHandleCoordinatesCount(invoke) == 0u) { // For static fields.
// To preserve the offset value across the non-Baker read barrier slow path
// for loading the declaring class, use a fixed callee-save register.
@@ -4706,7 +4706,7 @@
return;
}
- if ((kEmitCompilerReadBarrier && !kUseBakerReadBarrier) &&
+ if ((gUseReadBarrier && !kUseBakerReadBarrier) &&
invoke->GetType() == DataType::Type::kReference &&
invoke->GetIntrinsic() != Intrinsics::kVarHandleGet &&
invoke->GetIntrinsic() != Intrinsics::kVarHandleGetOpaque) {
@@ -4746,7 +4746,7 @@
DCHECK(use_load_acquire || order == std::memory_order_relaxed);
// Load the value from the target location.
- if (type == DataType::Type::kReference && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (type == DataType::Type::kReference && gUseReadBarrier && kUseBakerReadBarrier) {
// Piggy-back on the field load path using introspection for the Baker read barrier.
// The `target.offset` is a temporary, use it for field address.
Register tmp_ptr = target.offset.X();
@@ -4898,7 +4898,7 @@
}
if (CodeGenerator::StoreNeedsWriteBarrier(value_type, invoke->InputAt(value_index))) {
- codegen->MarkGCCard(target.object, Register(value), /*value_can_be_null=*/ true);
+ codegen->MarkGCCard(target.object, Register(value), /* emit_null_check= */ true);
}
if (slow_path != nullptr) {
@@ -4947,7 +4947,7 @@
uint32_t number_of_arguments = invoke->GetNumberOfArguments();
DataType::Type value_type = GetDataTypeFromShorty(invoke, number_of_arguments - 1u);
- if ((kEmitCompilerReadBarrier && !kUseBakerReadBarrier) &&
+ if ((gUseReadBarrier && !kUseBakerReadBarrier) &&
value_type == DataType::Type::kReference) {
// Unsupported for non-Baker read barrier because the artReadBarrierSlow() ignores
// the passed reference and reloads it from the field. This breaks the read barriers
@@ -4961,7 +4961,7 @@
LocationSummary* locations = CreateVarHandleCommonLocations(invoke);
- if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
+ if (gUseReadBarrier && !kUseBakerReadBarrier) {
// We need callee-save registers for both the class object and offset instead of
// the temporaries reserved in CreateVarHandleCommonLocations().
static_assert(POPCOUNT(kArm64CalleeSaveRefSpills) >= 2u);
@@ -5002,7 +5002,7 @@
locations->AddTemp(Location::RequiresRegister());
}
}
- if (kEmitCompilerReadBarrier && value_type == DataType::Type::kReference) {
+ if (gUseReadBarrier && value_type == DataType::Type::kReference) {
// Add a temporary for the `old_value_temp` in slow path.
locations->AddTemp(Location::RequiresRegister());
}
@@ -5068,7 +5068,7 @@
// except for references that need the offset for the read barrier.
UseScratchRegisterScope temps(masm);
Register tmp_ptr = target.offset.X();
- if (kEmitCompilerReadBarrier && value_type == DataType::Type::kReference) {
+ if (gUseReadBarrier && value_type == DataType::Type::kReference) {
tmp_ptr = temps.AcquireX();
}
__ Add(tmp_ptr, target.object.X(), target.offset.X());
@@ -5151,7 +5151,7 @@
vixl::aarch64::Label* exit_loop = &exit_loop_label;
vixl::aarch64::Label* cmp_failure = &exit_loop_label;
- if (kEmitCompilerReadBarrier && value_type == DataType::Type::kReference) {
+ if (gUseReadBarrier && value_type == DataType::Type::kReference) {
// The `old_value_temp` is used first for the marked `old_value` and then for the unmarked
// reloaded old value for subsequent CAS in the slow path. It cannot be a scratch register.
size_t expected_coordinates_count = GetExpectedVarHandleCoordinatesCount(invoke);
@@ -5296,7 +5296,7 @@
return;
}
- if ((kEmitCompilerReadBarrier && !kUseBakerReadBarrier) &&
+ if ((gUseReadBarrier && !kUseBakerReadBarrier) &&
invoke->GetType() == DataType::Type::kReference) {
// Unsupported for non-Baker read barrier because the artReadBarrierSlow() ignores
// the passed reference and reloads it from the field, thus seeing the new value
@@ -5372,7 +5372,7 @@
// except for references that need the offset for the non-Baker read barrier.
UseScratchRegisterScope temps(masm);
Register tmp_ptr = target.offset.X();
- if ((kEmitCompilerReadBarrier && !kUseBakerReadBarrier) &&
+ if ((gUseReadBarrier && !kUseBakerReadBarrier) &&
value_type == DataType::Type::kReference) {
tmp_ptr = temps.AcquireX();
}
@@ -5402,7 +5402,7 @@
// the new value unless it is zero bit pattern (+0.0f or +0.0) and need another one
// in GenerateGetAndUpdate(). We have allocated a normal temporary to handle that.
old_value = CPURegisterFrom(locations->GetTemp(1u), load_store_type);
- } else if ((kEmitCompilerReadBarrier && kUseBakerReadBarrier) &&
+ } else if ((gUseReadBarrier && kUseBakerReadBarrier) &&
value_type == DataType::Type::kReference) {
// Load the old value initially to a scratch register.
// We shall move it to `out` later with a read barrier.
@@ -5450,7 +5450,7 @@
__ Sxtb(out.W(), old_value.W());
} else if (value_type == DataType::Type::kInt16) {
__ Sxth(out.W(), old_value.W());
- } else if (kEmitCompilerReadBarrier && value_type == DataType::Type::kReference) {
+ } else if (gUseReadBarrier && value_type == DataType::Type::kReference) {
if (kUseBakerReadBarrier) {
codegen->GenerateIntrinsicCasMoveWithBakerReadBarrier(out.W(), old_value.W());
} else {
diff --git a/compiler/optimizing/intrinsics_arm64.h b/compiler/optimizing/intrinsics_arm64.h
index 9c46efd..a0ccf87 100644
--- a/compiler/optimizing/intrinsics_arm64.h
+++ b/compiler/optimizing/intrinsics_arm64.h
@@ -17,6 +17,7 @@
#ifndef ART_COMPILER_OPTIMIZING_INTRINSICS_ARM64_H_
#define ART_COMPILER_OPTIMIZING_INTRINSICS_ARM64_H_
+#include "base/macros.h"
#include "intrinsics.h"
namespace vixl {
@@ -27,7 +28,7 @@
} // namespace aarch64
} // namespace vixl
-namespace art {
+namespace art HIDDEN {
class ArenaAllocator;
class HInvokeStaticOrDirect;
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index d850cad..55d51fe 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -34,7 +34,7 @@
#include "aarch32/constants-aarch32.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
#define __ assembler->GetVIXLAssembler()->
@@ -120,7 +120,7 @@
public:
explicit ReadBarrierSystemArrayCopySlowPathARMVIXL(HInstruction* instruction)
: SlowPathCodeARMVIXL(instruction) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
DCHECK(kUseBakerReadBarrier);
}
@@ -1242,7 +1242,7 @@
void IntrinsicLocationsBuilderARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) {
// The only read barrier implementation supporting the
// SystemArrayCopy intrinsic is the Baker-style read barriers.
- if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
+ if (gUseReadBarrier && !kUseBakerReadBarrier) {
return;
}
@@ -1265,7 +1265,7 @@
if (length != nullptr && !assembler_->ShifterOperandCanAlwaysHold(length->GetValue())) {
locations->SetInAt(4, Location::RequiresRegister());
}
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// Temporary register IP cannot be used in
// ReadBarrierSystemArrayCopySlowPathARM (because that register
// is clobbered by ReadBarrierMarkRegX entry points). Get an extra
@@ -1339,7 +1339,7 @@
void IntrinsicCodeGeneratorARMVIXL::VisitSystemArrayCopy(HInvoke* invoke) {
// The only read barrier implementation supporting the
// SystemArrayCopy intrinsic is the Baker-style read barriers.
- DCHECK_IMPLIES(kEmitCompilerReadBarrier, kUseBakerReadBarrier);
+ DCHECK_IMPLIES(gUseReadBarrier, kUseBakerReadBarrier);
ArmVIXLAssembler* assembler = GetAssembler();
LocationSummary* locations = invoke->GetLocations();
@@ -1453,7 +1453,7 @@
// or the destination is Object[]. If none of these checks succeed, we go to the
// slow path.
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
if (!optimizations.GetSourceIsNonPrimitiveArray()) {
// /* HeapReference<Class> */ temp1 = src->klass_
codegen_->GenerateFieldLoadWithBakerReadBarrier(
@@ -1584,7 +1584,7 @@
} else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
// Bail out if the source is not a non primitive array.
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// /* HeapReference<Class> */ temp1 = src->klass_
codegen_->GenerateFieldLoadWithBakerReadBarrier(
invoke, temp1_loc, src, class_offset, temp2_loc, /* needs_null_check= */ false);
@@ -1621,7 +1621,7 @@
__ CompareAndBranchIfZero(RegisterFrom(length), &done, /* is_far_target= */ false);
}
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// TODO: Also convert this intrinsic to the IsGcMarking strategy?
// SystemArrayCopy implementation for Baker read barriers (see
@@ -1723,7 +1723,7 @@
}
// We only need one card marking on the destination array.
- codegen_->MarkGCCard(temp1, temp2, dest, NoReg, /* value_can_be_null= */ false);
+ codegen_->MarkGCCard(temp1, temp2, dest, NoReg, /* emit_null_check= */ false);
__ Bind(intrinsic_slow_path->GetExitLabel());
}
@@ -2511,7 +2511,7 @@
SlowPathCodeARMVIXL* slow_path = new (GetAllocator()) IntrinsicSlowPathARMVIXL(invoke);
codegen_->AddSlowPath(slow_path);
- if (kEmitCompilerReadBarrier) {
+ if (gUseReadBarrier) {
// Check self->GetWeakRefAccessEnabled().
UseScratchRegisterScope temps(assembler->GetVIXLAssembler());
vixl32::Register temp = temps.Acquire();
@@ -2539,7 +2539,7 @@
// Load the value from the field.
uint32_t referent_offset = mirror::Reference::ReferentOffset().Uint32Value();
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
out,
RegisterFrom(obj),
@@ -2587,7 +2587,7 @@
assembler->MaybeUnpoisonHeapReference(tmp);
codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny); // `referent` is volatile.
- if (kEmitCompilerReadBarrier) {
+ if (gUseReadBarrier) {
DCHECK(kUseBakerReadBarrier);
vixl32::Label calculate_result;
@@ -2613,7 +2613,7 @@
__ Bind(&calculate_result);
} else {
- DCHECK(!kEmitCompilerReadBarrier);
+ DCHECK(!gUseReadBarrier);
__ Sub(out, tmp, other);
}
@@ -2732,7 +2732,7 @@
}
break;
case DataType::Type::kReference:
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// Piggy-back on the field load path using introspection for the Baker read barrier.
vixl32::Register temp = RegisterFrom(maybe_temp);
__ Add(temp, base, offset);
@@ -2777,7 +2777,7 @@
codegen->GenerateMemoryBarrier(
seq_cst_barrier ? MemBarrierKind::kAnyAny : MemBarrierKind::kLoadAny);
}
- if (type == DataType::Type::kReference && !(kEmitCompilerReadBarrier && kUseBakerReadBarrier)) {
+ if (type == DataType::Type::kReference && !(gUseReadBarrier && kUseBakerReadBarrier)) {
Location base_loc = LocationFrom(base);
Location index_loc = LocationFrom(offset);
codegen->MaybeGenerateReadBarrierSlow(invoke, out, out, base_loc, /* offset=*/ 0u, index_loc);
@@ -2802,7 +2802,7 @@
CodeGeneratorARMVIXL* codegen,
DataType::Type type,
bool atomic) {
- bool can_call = kEmitCompilerReadBarrier && UnsafeGetIntrinsicOnCallList(invoke->GetIntrinsic());
+ bool can_call = gUseReadBarrier && UnsafeGetIntrinsicOnCallList(invoke->GetIntrinsic());
ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetAllocator();
LocationSummary* locations =
new (allocator) LocationSummary(invoke,
@@ -2818,7 +2818,7 @@
locations->SetInAt(2, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister(),
(can_call ? Location::kOutputOverlap : Location::kNoOutputOverlap));
- if ((kEmitCompilerReadBarrier && kUseBakerReadBarrier && type == DataType::Type::kReference) ||
+ if ((gUseReadBarrier && kUseBakerReadBarrier && type == DataType::Type::kReference) ||
(type == DataType::Type::kInt64 && Use64BitExclusiveLoadStore(atomic, codegen))) {
// We need a temporary register for the read barrier marking slow
// path in CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier,
@@ -2837,7 +2837,7 @@
vixl32::Register offset = LowRegisterFrom(locations->InAt(2)); // Long offset, lo part only.
Location out = locations->Out();
Location maybe_temp = Location::NoLocation();
- if ((kEmitCompilerReadBarrier && kUseBakerReadBarrier && type == DataType::Type::kReference) ||
+ if ((gUseReadBarrier && kUseBakerReadBarrier && type == DataType::Type::kReference) ||
(type == DataType::Type::kInt64 && Use64BitExclusiveLoadStore(atomic, codegen))) {
maybe_temp = locations->GetTemp(0);
}
@@ -3470,7 +3470,7 @@
// branch goes to the read barrier slow path that clobbers `success` anyway.
bool init_failure_for_cmp =
success.IsValid() &&
- !(kEmitCompilerReadBarrier && type == DataType::Type::kReference && expected.IsRegister());
+ !(gUseReadBarrier && type == DataType::Type::kReference && expected.IsRegister());
// Instruction scheduling: Loading a constant between LDREX* and using the loaded value
// is essentially free, so prepare the failure value here if we can.
bool init_failure_for_cmp_early =
@@ -3655,7 +3655,7 @@
};
static void CreateUnsafeCASLocations(ArenaAllocator* allocator, HInvoke* invoke) {
- const bool can_call = kEmitCompilerReadBarrier && IsUnsafeCASObject(invoke);
+ const bool can_call = gUseReadBarrier && IsUnsafeCASObject(invoke);
LocationSummary* locations =
new (allocator) LocationSummary(invoke,
can_call
@@ -3706,7 +3706,7 @@
vixl32::Label* exit_loop = &exit_loop_label;
vixl32::Label* cmp_failure = &exit_loop_label;
- if (kEmitCompilerReadBarrier && type == DataType::Type::kReference) {
+ if (gUseReadBarrier && type == DataType::Type::kReference) {
// If marking, check if the stored reference is a from-space reference to the same
// object as the to-space reference `expected`. If so, perform a custom CAS loop.
ReadBarrierCasSlowPathARMVIXL* slow_path =
@@ -3770,7 +3770,7 @@
}
void IntrinsicLocationsBuilderARMVIXL::VisitJdkUnsafeCompareAndSetObject(HInvoke* invoke) {
// The only supported read barrier implementation is the Baker-style read barriers (b/173104084).
- if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
+ if (gUseReadBarrier && !kUseBakerReadBarrier) {
return;
}
@@ -3798,7 +3798,7 @@
}
void IntrinsicCodeGeneratorARMVIXL::VisitJdkUnsafeCompareAndSetObject(HInvoke* invoke) {
// The only supported read barrier implementation is the Baker-style read barriers (b/173104084).
- DCHECK_IMPLIES(kEmitCompilerReadBarrier, kUseBakerReadBarrier);
+ DCHECK_IMPLIES(gUseReadBarrier, kUseBakerReadBarrier);
GenUnsafeCas(invoke, DataType::Type::kReference, codegen_);
}
@@ -4351,7 +4351,7 @@
LocationFrom(target.object),
method,
ArtField::DeclaringClassOffset().Int32Value(),
- kCompilerReadBarrierOption);
+ gCompilerReadBarrierOption);
}
}
} else {
@@ -4403,7 +4403,7 @@
}
// Add a temporary for offset.
- if ((kEmitCompilerReadBarrier && !kUseBakerReadBarrier) &&
+ if ((gUseReadBarrier && !kUseBakerReadBarrier) &&
GetExpectedVarHandleCoordinatesCount(invoke) == 0u) { // For static fields.
// To preserve the offset value across the non-Baker read barrier slow path
// for loading the declaring class, use a fixed callee-save register.
@@ -4428,7 +4428,7 @@
return;
}
- if ((kEmitCompilerReadBarrier && !kUseBakerReadBarrier) &&
+ if ((gUseReadBarrier && !kUseBakerReadBarrier) &&
invoke->GetType() == DataType::Type::kReference &&
invoke->GetIntrinsic() != Intrinsics::kVarHandleGet &&
invoke->GetIntrinsic() != Intrinsics::kVarHandleGetOpaque) {
@@ -4476,7 +4476,7 @@
Location maybe_temp = Location::NoLocation();
Location maybe_temp2 = Location::NoLocation();
Location maybe_temp3 = Location::NoLocation();
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier && type == DataType::Type::kReference) {
+ if (gUseReadBarrier && kUseBakerReadBarrier && type == DataType::Type::kReference) {
// Reuse the offset temporary.
maybe_temp = LocationFrom(target.offset);
} else if (DataType::Is64BitType(type) && Use64BitExclusiveLoadStore(atomic, codegen)) {
@@ -4699,7 +4699,7 @@
vixl32::Register temp = target.offset;
vixl32::Register card = temps.Acquire();
vixl32::Register value_reg = RegisterFrom(value);
- codegen->MarkGCCard(temp, card, target.object, value_reg, /*value_can_be_null=*/ true);
+ codegen->MarkGCCard(temp, card, target.object, value_reg, /* emit_null_check= */ true);
}
if (slow_path != nullptr) {
@@ -4749,7 +4749,7 @@
uint32_t number_of_arguments = invoke->GetNumberOfArguments();
DataType::Type value_type = GetDataTypeFromShorty(invoke, number_of_arguments - 1u);
- if ((kEmitCompilerReadBarrier && !kUseBakerReadBarrier) &&
+ if ((gUseReadBarrier && !kUseBakerReadBarrier) &&
value_type == DataType::Type::kReference) {
// Unsupported for non-Baker read barrier because the artReadBarrierSlow() ignores
// the passed reference and reloads it from the field. This breaks the read barriers
@@ -4763,7 +4763,7 @@
LocationSummary* locations = CreateVarHandleCommonLocations(invoke);
- if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
+ if (gUseReadBarrier && !kUseBakerReadBarrier) {
// We need callee-save registers for both the class object and offset instead of
// the temporaries reserved in CreateVarHandleCommonLocations().
static_assert(POPCOUNT(kArmCalleeSaveRefSpills) >= 2u);
@@ -4799,7 +4799,7 @@
locations->AddRegisterTemps(2u);
}
}
- if (kEmitCompilerReadBarrier && value_type == DataType::Type::kReference) {
+ if (gUseReadBarrier && value_type == DataType::Type::kReference) {
// Add a temporary for store result, also used for the `old_value_temp` in slow path.
locations->AddTemp(Location::RequiresRegister());
}
@@ -4930,7 +4930,7 @@
vixl32::Label* exit_loop = &exit_loop_label;
vixl32::Label* cmp_failure = &exit_loop_label;
- if (kEmitCompilerReadBarrier && value_type == DataType::Type::kReference) {
+ if (gUseReadBarrier && value_type == DataType::Type::kReference) {
// The `old_value_temp` is used first for the marked `old_value` and then for the unmarked
// reloaded old value for subsequent CAS in the slow path. This must not clobber `old_value`.
vixl32::Register old_value_temp = return_success ? RegisterFrom(out) : store_result;
@@ -5086,7 +5086,7 @@
return;
}
- if ((kEmitCompilerReadBarrier && !kUseBakerReadBarrier) &&
+ if ((gUseReadBarrier && !kUseBakerReadBarrier) &&
invoke->GetType() == DataType::Type::kReference) {
// Unsupported for non-Baker read barrier because the artReadBarrierSlow() ignores
// the passed reference and reloads it from the field, thus seeing the new value
@@ -5107,7 +5107,7 @@
// Add temps needed to do the GenerateGetAndUpdate() with core registers.
size_t temps_needed = (value_type == DataType::Type::kFloat64) ? 5u : 3u;
locations->AddRegisterTemps(temps_needed - locations->GetTempCount());
- } else if ((kEmitCompilerReadBarrier && !kUseBakerReadBarrier) &&
+ } else if ((gUseReadBarrier && !kUseBakerReadBarrier) &&
value_type == DataType::Type::kReference) {
// We need to preserve the declaring class (if present) and offset for read barrier
// slow paths, so we must use a separate temporary for the exclusive store result.
@@ -5213,7 +5213,7 @@
if (byte_swap) {
GenerateReverseBytes(assembler, DataType::Type::kInt32, arg, arg);
}
- } else if (kEmitCompilerReadBarrier && value_type == DataType::Type::kReference) {
+ } else if (gUseReadBarrier && value_type == DataType::Type::kReference) {
if (kUseBakerReadBarrier) {
// Load the old value initially to a temporary register.
// We shall move it to `out` later with a read barrier.
@@ -5296,7 +5296,7 @@
} else {
__ Vmov(SRegisterFrom(out), RegisterFrom(old_value));
}
- } else if (kEmitCompilerReadBarrier && value_type == DataType::Type::kReference) {
+ } else if (gUseReadBarrier && value_type == DataType::Type::kReference) {
if (kUseBakerReadBarrier) {
codegen->GenerateIntrinsicCasMoveWithBakerReadBarrier(RegisterFrom(out),
RegisterFrom(old_value));
diff --git a/compiler/optimizing/intrinsics_arm_vixl.h b/compiler/optimizing/intrinsics_arm_vixl.h
index 3103cec..54475bc 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.h
+++ b/compiler/optimizing/intrinsics_arm_vixl.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_OPTIMIZING_INTRINSICS_ARM_VIXL_H_
#define ART_COMPILER_OPTIMIZING_INTRINSICS_ARM_VIXL_H_
+#include "base/macros.h"
#include "intrinsics.h"
#include "utils/arm/assembler_arm_vixl.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
diff --git a/compiler/optimizing/intrinsics_utils.h b/compiler/optimizing/intrinsics_utils.h
index 19f5e33..13cabda 100644
--- a/compiler/optimizing/intrinsics_utils.h
+++ b/compiler/optimizing/intrinsics_utils.h
@@ -29,7 +29,7 @@
#include "utils/assembler.h"
#include "utils/label.h"
-namespace art {
+namespace art HIDDEN {
// Default slow-path for fallback (calling the managed code to handle the intrinsic) in an
// intrinsified call. This will copy the arguments into the positions for a regular call.
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 7d90aae..a76b773 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -38,7 +38,7 @@
#include "utils/x86/assembler_x86.h"
#include "utils/x86/constants_x86.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
@@ -75,7 +75,7 @@
public:
explicit ReadBarrierSystemArrayCopySlowPathX86(HInstruction* instruction)
: SlowPathCode(instruction) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
DCHECK(kUseBakerReadBarrier);
}
@@ -1699,7 +1699,7 @@
case DataType::Type::kReference: {
Register output = output_loc.AsRegister<Register>();
- if (kEmitCompilerReadBarrier) {
+ if (gUseReadBarrier) {
if (kUseBakerReadBarrier) {
Address src(base, offset, ScaleFactor::TIMES_1, 0);
codegen->GenerateReferenceLoadWithBakerReadBarrier(
@@ -1757,7 +1757,7 @@
HInvoke* invoke,
DataType::Type type,
bool is_volatile) {
- bool can_call = kEmitCompilerReadBarrier && UnsafeGetIntrinsicOnCallList(invoke->GetIntrinsic());
+ bool can_call = gUseReadBarrier && UnsafeGetIntrinsicOnCallList(invoke->GetIntrinsic());
LocationSummary* locations =
new (allocator) LocationSummary(invoke,
can_call
@@ -2103,7 +2103,7 @@
static void CreateIntIntIntIntIntToInt(ArenaAllocator* allocator,
DataType::Type type,
HInvoke* invoke) {
- const bool can_call = kEmitCompilerReadBarrier &&
+ const bool can_call = gUseReadBarrier &&
kUseBakerReadBarrier &&
IsUnsafeCASObject(invoke);
LocationSummary* locations =
@@ -2175,7 +2175,7 @@
void IntrinsicLocationsBuilderX86::VisitJdkUnsafeCompareAndSetObject(HInvoke* invoke) {
// The only supported read barrier implementation is the Baker-style read barriers.
- if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
+ if (gUseReadBarrier && !kUseBakerReadBarrier) {
return;
}
@@ -2304,7 +2304,7 @@
DCHECK_EQ(expected, EAX);
DCHECK_NE(temp, temp2);
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// Need to make sure the reference stored in the field is a to-space
// one before attempting the CAS or the CAS could fail incorrectly.
codegen->GenerateReferenceLoadWithBakerReadBarrier(
@@ -2391,7 +2391,7 @@
if (type == DataType::Type::kReference) {
// The only read barrier implementation supporting the
// UnsafeCASObject intrinsic is the Baker-style read barriers.
- DCHECK_IMPLIES(kEmitCompilerReadBarrier, kUseBakerReadBarrier);
+ DCHECK_IMPLIES(gUseReadBarrier, kUseBakerReadBarrier);
Register temp = locations->GetTemp(0).AsRegister<Register>();
Register temp2 = locations->GetTemp(1).AsRegister<Register>();
@@ -2413,7 +2413,7 @@
void IntrinsicCodeGeneratorX86::VisitUnsafeCASObject(HInvoke* invoke) {
// The only read barrier implementation supporting the
// UnsafeCASObject intrinsic is the Baker-style read barriers.
- DCHECK_IMPLIES(kEmitCompilerReadBarrier, kUseBakerReadBarrier);
+ DCHECK_IMPLIES(gUseReadBarrier, kUseBakerReadBarrier);
GenCAS(DataType::Type::kReference, invoke, codegen_);
}
@@ -2443,7 +2443,7 @@
void IntrinsicCodeGeneratorX86::VisitJdkUnsafeCompareAndSetObject(HInvoke* invoke) {
// The only supported read barrier implementation is the Baker-style read barriers.
- DCHECK_IMPLIES(kEmitCompilerReadBarrier, kUseBakerReadBarrier);
+ DCHECK_IMPLIES(gUseReadBarrier, kUseBakerReadBarrier);
GenCAS(DataType::Type::kReference, invoke, codegen_);
}
@@ -2843,7 +2843,7 @@
void IntrinsicLocationsBuilderX86::VisitSystemArrayCopy(HInvoke* invoke) {
// The only read barrier implementation supporting the
// SystemArrayCopy intrinsic is the Baker-style read barriers.
- if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
+ if (gUseReadBarrier && !kUseBakerReadBarrier) {
return;
}
@@ -2875,7 +2875,7 @@
void IntrinsicCodeGeneratorX86::VisitSystemArrayCopy(HInvoke* invoke) {
// The only read barrier implementation supporting the
// SystemArrayCopy intrinsic is the Baker-style read barriers.
- DCHECK_IMPLIES(kEmitCompilerReadBarrier, kUseBakerReadBarrier);
+ DCHECK_IMPLIES(gUseReadBarrier, kUseBakerReadBarrier);
X86Assembler* assembler = GetAssembler();
LocationSummary* locations = invoke->GetLocations();
@@ -2995,7 +2995,7 @@
// slow path.
if (!optimizations.GetSourceIsNonPrimitiveArray()) {
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// /* HeapReference<Class> */ temp1 = src->klass_
codegen_->GenerateFieldLoadWithBakerReadBarrier(
invoke, temp1_loc, src, class_offset, /* needs_null_check= */ false);
@@ -3022,7 +3022,7 @@
__ j(kNotEqual, intrinsic_slow_path->GetEntryLabel());
}
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
if (length.Equals(Location::RegisterLocation(temp3))) {
// When Baker read barriers are enabled, register `temp3`,
// which in the present case contains the `length` parameter,
@@ -3120,7 +3120,7 @@
} else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
// Bail out if the source is not a non primitive array.
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// /* HeapReference<Class> */ temp1 = src->klass_
codegen_->GenerateFieldLoadWithBakerReadBarrier(
invoke, temp1_loc, src, class_offset, /* needs_null_check= */ false);
@@ -3151,7 +3151,7 @@
// Compute the base source address in `temp1`.
GenSystemArrayCopyBaseAddress(GetAssembler(), type, src, src_pos, temp1);
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// If it is needed (in the case of the fast-path loop), the base
// destination address is computed later, as `temp2` is used for
// intermediate computations.
@@ -3259,7 +3259,7 @@
}
// We only need one card marking on the destination array.
- codegen_->MarkGCCard(temp1, temp2, dest, Register(kNoRegister), /* value_can_be_null= */ false);
+ codegen_->MarkGCCard(temp1, temp2, dest, Register(kNoRegister), /* emit_null_check= */ false);
__ Bind(intrinsic_slow_path->GetExitLabel());
}
@@ -3377,7 +3377,7 @@
SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathX86(invoke);
codegen_->AddSlowPath(slow_path);
- if (kEmitCompilerReadBarrier) {
+ if (gUseReadBarrier) {
// Check self->GetWeakRefAccessEnabled().
ThreadOffset32 offset = Thread::WeakRefAccessEnabledOffset<kX86PointerSize>();
__ fs()->cmpl(Address::Absolute(offset),
@@ -3400,7 +3400,7 @@
// Load the value from the field.
uint32_t referent_offset = mirror::Reference::ReferentOffset().Uint32Value();
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
out,
obj.AsRegister<Register>(),
@@ -3442,7 +3442,7 @@
NearLabel end, return_true, return_false;
__ cmpl(out, other);
- if (kEmitCompilerReadBarrier) {
+ if (gUseReadBarrier) {
DCHECK(kUseBakerReadBarrier);
__ j(kEqual, &return_true);
@@ -3781,7 +3781,7 @@
Location::RegisterLocation(temp),
Address(temp, declaring_class_offset),
/* fixup_label= */ nullptr,
- kCompilerReadBarrierOption);
+ gCompilerReadBarrierOption);
return temp;
}
@@ -3794,7 +3794,7 @@
static void CreateVarHandleGetLocations(HInvoke* invoke) {
// The only read barrier implementation supporting the
// VarHandleGet intrinsic is the Baker-style read barriers.
- if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
+ if (gUseReadBarrier && !kUseBakerReadBarrier) {
return;
}
@@ -3836,7 +3836,7 @@
static void GenerateVarHandleGet(HInvoke* invoke, CodeGeneratorX86* codegen) {
// The only read barrier implementation supporting the
// VarHandleGet intrinsic is the Baker-style read barriers.
- DCHECK_IMPLIES(kEmitCompilerReadBarrier, kUseBakerReadBarrier);
+ DCHECK_IMPLIES(gUseReadBarrier, kUseBakerReadBarrier);
X86Assembler* assembler = codegen->GetAssembler();
LocationSummary* locations = invoke->GetLocations();
@@ -3860,7 +3860,7 @@
Address field_addr(ref, offset, TIMES_1, 0);
// Load the value from the field
- if (type == DataType::Type::kReference && kCompilerReadBarrierOption == kWithReadBarrier) {
+ if (type == DataType::Type::kReference && gCompilerReadBarrierOption == kWithReadBarrier) {
codegen->GenerateReferenceLoadWithBakerReadBarrier(
invoke, out, ref, field_addr, /* needs_null_check= */ false);
} else if (type == DataType::Type::kInt64 &&
@@ -3917,7 +3917,7 @@
static void CreateVarHandleSetLocations(HInvoke* invoke) {
// The only read barrier implementation supporting the
// VarHandleGet intrinsic is the Baker-style read barriers.
- if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
+ if (gUseReadBarrier && !kUseBakerReadBarrier) {
return;
}
@@ -3990,7 +3990,7 @@
static void GenerateVarHandleSet(HInvoke* invoke, CodeGeneratorX86* codegen) {
// The only read barrier implementation supporting the
// VarHandleGet intrinsic is the Baker-style read barriers.
- DCHECK_IMPLIES(kEmitCompilerReadBarrier, kUseBakerReadBarrier);
+ DCHECK_IMPLIES(gUseReadBarrier, kUseBakerReadBarrier);
X86Assembler* assembler = codegen->GetAssembler();
LocationSummary* locations = invoke->GetLocations();
@@ -4041,13 +4041,16 @@
InstructionCodeGeneratorX86* instr_codegen =
down_cast<InstructionCodeGeneratorX86*>(codegen->GetInstructionVisitor());
// Store the value to the field
- instr_codegen->HandleFieldSet(invoke,
- value_index,
- value_type,
- Address(reference, offset, TIMES_1, 0),
- reference,
- is_volatile,
- /* value_can_be_null */ true);
+ instr_codegen->HandleFieldSet(
+ invoke,
+ value_index,
+ value_type,
+ Address(reference, offset, TIMES_1, 0),
+ reference,
+ is_volatile,
+ /* value_can_be_null */ true,
+ // Value can be null, and this write barrier is not being relied on for other sets.
+ WriteBarrierKind::kEmitWithNullCheck);
__ Bind(slow_path->GetExitLabel());
}
@@ -4087,7 +4090,7 @@
static void CreateVarHandleGetAndSetLocations(HInvoke* invoke) {
// The only read barrier implementation supporting the
// VarHandleGet intrinsic is the Baker-style read barriers.
- if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
+ if (gUseReadBarrier && !kUseBakerReadBarrier) {
return;
}
@@ -4135,7 +4138,7 @@
static void GenerateVarHandleGetAndSet(HInvoke* invoke, CodeGeneratorX86* codegen) {
// The only read barrier implementation supporting the
// VarHandleGet intrinsic is the Baker-style read barriers.
- DCHECK_IMPLIES(kEmitCompilerReadBarrier, kUseBakerReadBarrier);
+ DCHECK_IMPLIES(gUseReadBarrier, kUseBakerReadBarrier);
X86Assembler* assembler = codegen->GetAssembler();
LocationSummary* locations = invoke->GetLocations();
@@ -4194,7 +4197,7 @@
__ movd(locations->Out().AsFpuRegister<XmmRegister>(), EAX);
break;
case DataType::Type::kReference: {
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// Need to make sure the reference stored in the field is a to-space
// one before attempting the CAS or the CAS could fail incorrectly.
codegen->GenerateReferenceLoadWithBakerReadBarrier(
@@ -4208,7 +4211,7 @@
&temp2);
}
codegen->MarkGCCard(
- temp, temp2, reference, value.AsRegister<Register>(), /* value_can_be_null= */ false);
+ temp, temp2, reference, value.AsRegister<Register>(), /* emit_null_check= */ false);
if (kPoisonHeapReferences) {
__ movl(temp, value.AsRegister<Register>());
__ PoisonHeapReference(temp);
@@ -4258,7 +4261,7 @@
static void CreateVarHandleCompareAndSetOrExchangeLocations(HInvoke* invoke) {
// The only read barrier implementation supporting the
// VarHandleGet intrinsic is the Baker-style read barriers.
- if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
+ if (gUseReadBarrier && !kUseBakerReadBarrier) {
return;
}
@@ -4322,7 +4325,7 @@
static void GenerateVarHandleCompareAndSetOrExchange(HInvoke* invoke, CodeGeneratorX86* codegen) {
// The only read barrier implementation supporting the
// VarHandleGet intrinsic is the Baker-style read barriers.
- DCHECK_IMPLIES(kEmitCompilerReadBarrier, kUseBakerReadBarrier);
+ DCHECK_IMPLIES(gUseReadBarrier, kUseBakerReadBarrier);
X86Assembler* assembler = codegen->GetAssembler();
LocationSummary* locations = invoke->GetLocations();
@@ -4441,7 +4444,7 @@
static void CreateVarHandleGetAndAddLocations(HInvoke* invoke) {
// The only read barrier implementation supporting the
// VarHandleGet intrinsic is the Baker-style read barriers.
- if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
+ if (gUseReadBarrier && !kUseBakerReadBarrier) {
return;
}
@@ -4490,7 +4493,7 @@
static void GenerateVarHandleGetAndAdd(HInvoke* invoke, CodeGeneratorX86* codegen) {
// The only read barrier implementation supporting the
// VarHandleGet intrinsic is the Baker-style read barriers.
- DCHECK_IMPLIES(kEmitCompilerReadBarrier, kUseBakerReadBarrier);
+ DCHECK_IMPLIES(gUseReadBarrier, kUseBakerReadBarrier);
X86Assembler* assembler = codegen->GetAssembler();
LocationSummary* locations = invoke->GetLocations();
@@ -4591,7 +4594,7 @@
static void CreateVarHandleGetAndBitwiseOpLocations(HInvoke* invoke) {
// The only read barrier implementation supporting the
// VarHandleGet intrinsic is the Baker-style read barriers.
- if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
+ if (gUseReadBarrier && !kUseBakerReadBarrier) {
return;
}
@@ -4659,7 +4662,7 @@
static void GenerateVarHandleGetAndBitwiseOp(HInvoke* invoke, CodeGeneratorX86* codegen) {
// The only read barrier implementation supporting the
// VarHandleGet intrinsic is the Baker-style read barriers.
- DCHECK_IMPLIES(kEmitCompilerReadBarrier, kUseBakerReadBarrier);
+ DCHECK_IMPLIES(gUseReadBarrier, kUseBakerReadBarrier);
X86Assembler* assembler = codegen->GetAssembler();
LocationSummary* locations = invoke->GetLocations();
diff --git a/compiler/optimizing/intrinsics_x86.h b/compiler/optimizing/intrinsics_x86.h
index ae150da..77c236d 100644
--- a/compiler/optimizing/intrinsics_x86.h
+++ b/compiler/optimizing/intrinsics_x86.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_INTRINSICS_X86_H_
#define ART_COMPILER_OPTIMIZING_INTRINSICS_X86_H_
+#include "base/macros.h"
#include "intrinsics.h"
-namespace art {
+namespace art HIDDEN {
class ArenaAllocator;
class HInvokeStaticOrDirect;
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 3c31374..cf3d63b 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -36,7 +36,7 @@
#include "utils/x86_64/assembler_x86_64.h"
#include "utils/x86_64/constants_x86_64.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
@@ -71,7 +71,7 @@
public:
explicit ReadBarrierSystemArrayCopySlowPathX86_64(HInstruction* instruction)
: SlowPathCode(instruction) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
DCHECK(kUseBakerReadBarrier);
}
@@ -836,7 +836,7 @@
void IntrinsicLocationsBuilderX86_64::VisitSystemArrayCopy(HInvoke* invoke) {
// The only read barrier implementation supporting the
// SystemArrayCopy intrinsic is the Baker-style read barriers.
- if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
+ if (gUseReadBarrier && !kUseBakerReadBarrier) {
return;
}
@@ -887,7 +887,7 @@
void IntrinsicCodeGeneratorX86_64::VisitSystemArrayCopy(HInvoke* invoke) {
// The only read barrier implementation supporting the
// SystemArrayCopy intrinsic is the Baker-style read barriers.
- DCHECK_IMPLIES(kEmitCompilerReadBarrier, kUseBakerReadBarrier);
+ DCHECK_IMPLIES(gUseReadBarrier, kUseBakerReadBarrier);
X86_64Assembler* assembler = GetAssembler();
LocationSummary* locations = invoke->GetLocations();
@@ -1002,7 +1002,7 @@
// slow path.
bool did_unpoison = false;
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// /* HeapReference<Class> */ temp1 = dest->klass_
codegen_->GenerateFieldLoadWithBakerReadBarrier(
invoke, temp1_loc, dest, class_offset, /* needs_null_check= */ false);
@@ -1034,7 +1034,7 @@
if (!optimizations.GetDestinationIsNonPrimitiveArray()) {
// Bail out if the destination is not a non primitive array.
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// /* HeapReference<Class> */ TMP = temp1->component_type_
codegen_->GenerateFieldLoadWithBakerReadBarrier(
invoke, TMP_loc, temp1, component_offset, /* needs_null_check= */ false);
@@ -1055,7 +1055,7 @@
if (!optimizations.GetSourceIsNonPrimitiveArray()) {
// Bail out if the source is not a non primitive array.
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// For the same reason given earlier, `temp1` is not trashed by the
// read barrier emitted by GenerateFieldLoadWithBakerReadBarrier below.
// /* HeapReference<Class> */ TMP = temp2->component_type_
@@ -1081,7 +1081,7 @@
if (optimizations.GetDestinationIsTypedObjectArray()) {
NearLabel do_copy;
__ j(kEqual, &do_copy);
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// /* HeapReference<Class> */ temp1 = temp1->component_type_
codegen_->GenerateFieldLoadWithBakerReadBarrier(
invoke, temp1_loc, temp1, component_offset, /* needs_null_check= */ false);
@@ -1109,7 +1109,7 @@
} else if (!optimizations.GetSourceIsNonPrimitiveArray()) {
DCHECK(optimizations.GetDestinationIsNonPrimitiveArray());
// Bail out if the source is not a non primitive array.
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// /* HeapReference<Class> */ temp1 = src->klass_
codegen_->GenerateFieldLoadWithBakerReadBarrier(
invoke, temp1_loc, src, class_offset, /* needs_null_check= */ false);
@@ -1141,7 +1141,7 @@
GenSystemArrayCopyAddresses(
GetAssembler(), type, src, src_pos, dest, dest_pos, length, temp1, temp2, temp3);
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// SystemArrayCopy implementation for Baker read barriers (see
// also CodeGeneratorX86_64::GenerateReferenceLoadWithBakerReadBarrier):
//
@@ -1224,7 +1224,7 @@
}
// We only need one card marking on the destination array.
- codegen_->MarkGCCard(temp1, temp2, dest, CpuRegister(kNoRegister), /* value_can_be_null= */ false);
+ codegen_->MarkGCCard(temp1, temp2, dest, CpuRegister(kNoRegister), /* emit_null_check= */ false);
__ Bind(intrinsic_slow_path->GetExitLabel());
}
@@ -1888,7 +1888,7 @@
break;
case DataType::Type::kReference: {
- if (kEmitCompilerReadBarrier) {
+ if (gUseReadBarrier) {
if (kUseBakerReadBarrier) {
Address src(base, offset, ScaleFactor::TIMES_1, 0);
codegen->GenerateReferenceLoadWithBakerReadBarrier(
@@ -1930,7 +1930,7 @@
}
static void CreateIntIntIntToIntLocations(ArenaAllocator* allocator, HInvoke* invoke) {
- bool can_call = kEmitCompilerReadBarrier && UnsafeGetIntrinsicOnCallList(invoke->GetIntrinsic());
+ bool can_call = gUseReadBarrier && UnsafeGetIntrinsicOnCallList(invoke->GetIntrinsic());
LocationSummary* locations =
new (allocator) LocationSummary(invoke,
can_call
@@ -2230,7 +2230,7 @@
static void CreateUnsafeCASLocations(ArenaAllocator* allocator,
DataType::Type type,
HInvoke* invoke) {
- const bool can_call = kEmitCompilerReadBarrier &&
+ const bool can_call = gUseReadBarrier &&
kUseBakerReadBarrier &&
IsUnsafeCASObject(invoke);
LocationSummary* locations =
@@ -2253,7 +2253,7 @@
// Need two temporaries for MarkGCCard.
locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too.
locations->AddTemp(Location::RequiresRegister());
- if (kEmitCompilerReadBarrier) {
+ if (gUseReadBarrier) {
// Need three temporaries for GenerateReferenceLoadWithBakerReadBarrier.
DCHECK(kUseBakerReadBarrier);
locations->AddTemp(Location::RequiresRegister());
@@ -2298,7 +2298,7 @@
void IntrinsicLocationsBuilderX86_64::VisitJdkUnsafeCompareAndSetObject(HInvoke* invoke) {
// The only supported read barrier implementation is the Baker-style read barriers.
- if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
+ if (gUseReadBarrier && !kUseBakerReadBarrier) {
return;
}
@@ -2438,7 +2438,7 @@
CpuRegister temp3,
bool is_cmpxchg) {
// The only supported read barrier implementation is the Baker-style read barriers.
- DCHECK_IMPLIES(kEmitCompilerReadBarrier, kUseBakerReadBarrier);
+ DCHECK_IMPLIES(gUseReadBarrier, kUseBakerReadBarrier);
X86_64Assembler* assembler = down_cast<X86_64Assembler*>(codegen->GetAssembler());
@@ -2447,7 +2447,7 @@
codegen->MarkGCCard(temp1, temp2, base, value, value_can_be_null);
Address field_addr(base, offset, TIMES_1, 0);
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// Need to make sure the reference stored in the field is a to-space
// one before attempting the CAS or the CAS could fail incorrectly.
codegen->GenerateReferenceLoadWithBakerReadBarrier(
@@ -2556,7 +2556,7 @@
CpuRegister new_value_reg = new_value.AsRegister<CpuRegister>();
CpuRegister temp1 = locations->GetTemp(temp1_index).AsRegister<CpuRegister>();
CpuRegister temp2 = locations->GetTemp(temp2_index).AsRegister<CpuRegister>();
- CpuRegister temp3 = kEmitCompilerReadBarrier
+ CpuRegister temp3 = gUseReadBarrier
? locations->GetTemp(temp3_index).AsRegister<CpuRegister>()
: CpuRegister(kNoRegister);
DCHECK(RegsAreAllDifferent({base, offset, temp1, temp2, temp3}));
@@ -2624,7 +2624,7 @@
void IntrinsicCodeGeneratorX86_64::VisitJdkUnsafeCompareAndSetObject(HInvoke* invoke) {
// The only supported read barrier implementation is the Baker-style read barriers.
- DCHECK_IMPLIES(kEmitCompilerReadBarrier, kUseBakerReadBarrier);
+ DCHECK_IMPLIES(gUseReadBarrier, kUseBakerReadBarrier);
GenCAS(DataType::Type::kReference, invoke, codegen_);
}
@@ -3128,7 +3128,7 @@
SlowPathCode* slow_path = new (GetAllocator()) IntrinsicSlowPathX86_64(invoke);
codegen_->AddSlowPath(slow_path);
- if (kEmitCompilerReadBarrier) {
+ if (gUseReadBarrier) {
// Check self->GetWeakRefAccessEnabled().
ThreadOffset64 offset = Thread::WeakRefAccessEnabledOffset<kX86_64PointerSize>();
__ gs()->cmpl(Address::Absolute(offset, /* no_rip= */ true),
@@ -3150,7 +3150,7 @@
// Load the value from the field.
uint32_t referent_offset = mirror::Reference::ReferentOffset().Uint32Value();
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
codegen_->GenerateFieldLoadWithBakerReadBarrier(invoke,
out,
obj.AsRegister<CpuRegister>(),
@@ -3191,7 +3191,7 @@
__ cmpl(out, other);
- if (kEmitCompilerReadBarrier) {
+ if (gUseReadBarrier) {
DCHECK(kUseBakerReadBarrier);
NearLabel calculate_result;
@@ -3771,7 +3771,7 @@
Location::RegisterLocation(target.object),
Address(method, ArtField::DeclaringClassOffset()),
/*fixup_label=*/ nullptr,
- kCompilerReadBarrierOption);
+ gCompilerReadBarrierOption);
}
}
} else {
@@ -3790,7 +3790,7 @@
static bool HasVarHandleIntrinsicImplementation(HInvoke* invoke) {
// The only supported read barrier implementation is the Baker-style read barriers.
- if (kEmitCompilerReadBarrier && !kUseBakerReadBarrier) {
+ if (gUseReadBarrier && !kUseBakerReadBarrier) {
return false;
}
@@ -3876,7 +3876,7 @@
Location out = locations->Out();
if (type == DataType::Type::kReference) {
- if (kEmitCompilerReadBarrier) {
+ if (gUseReadBarrier) {
DCHECK(kUseBakerReadBarrier);
codegen->GenerateReferenceLoadWithBakerReadBarrier(
invoke, out, CpuRegister(target.object), src, /* needs_null_check= */ false);
@@ -3985,16 +3985,19 @@
Address dst(CpuRegister(target.object), CpuRegister(target.offset), TIMES_1, 0);
// Store the value to the field.
- codegen->GetInstructionCodegen()->HandleFieldSet(invoke,
- value_index,
- last_temp_index,
- value_type,
- dst,
- CpuRegister(target.object),
- is_volatile,
- is_atomic,
- /*value_can_be_null=*/ true,
- byte_swap);
+ codegen->GetInstructionCodegen()->HandleFieldSet(
+ invoke,
+ value_index,
+ last_temp_index,
+ value_type,
+ dst,
+ CpuRegister(target.object),
+ is_volatile,
+ is_atomic,
+ /*value_can_be_null=*/true,
+ byte_swap,
+ // Value can be null, and this write barrier is not being relied on for other sets.
+ WriteBarrierKind::kEmitWithNullCheck);
// setVolatile needs kAnyAny barrier, but HandleFieldSet takes care of that.
@@ -4070,7 +4073,7 @@
// Need two temporaries for MarkGCCard.
locations->AddTemp(Location::RequiresRegister());
locations->AddTemp(Location::RequiresRegister());
- if (kEmitCompilerReadBarrier) {
+ if (gUseReadBarrier) {
// Need three temporaries for GenerateReferenceLoadWithBakerReadBarrier.
DCHECK(kUseBakerReadBarrier);
locations->AddTemp(Location::RequiresRegister());
@@ -4085,7 +4088,7 @@
CodeGeneratorX86_64* codegen,
bool is_cmpxchg,
bool byte_swap = false) {
- DCHECK_IMPLIES(kEmitCompilerReadBarrier, kUseBakerReadBarrier);
+ DCHECK_IMPLIES(gUseReadBarrier, kUseBakerReadBarrier);
X86_64Assembler* assembler = codegen->GetAssembler();
LocationSummary* locations = invoke->GetLocations();
@@ -4218,7 +4221,7 @@
// Need two temporaries for MarkGCCard.
locations->AddTemp(Location::RequiresRegister());
locations->AddTemp(Location::RequiresRegister());
- if (kEmitCompilerReadBarrier) {
+ if (gUseReadBarrier) {
// Need a third temporary for GenerateReferenceLoadWithBakerReadBarrier.
DCHECK(kUseBakerReadBarrier);
locations->AddTemp(Location::RequiresRegister());
@@ -4267,7 +4270,7 @@
CpuRegister temp2 = locations->GetTemp(temp_count - 2).AsRegister<CpuRegister>();
CpuRegister valreg = value.AsRegister<CpuRegister>();
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
codegen->GenerateReferenceLoadWithBakerReadBarrier(
invoke,
locations->GetTemp(temp_count - 3),
@@ -4278,7 +4281,7 @@
&temp1,
&temp2);
}
- codegen->MarkGCCard(temp1, temp2, ref, valreg, /*value_can_be_null=*/ false);
+ codegen->MarkGCCard(temp1, temp2, ref, valreg, /* emit_null_check= */ false);
DCHECK_EQ(valreg, out.AsRegister<CpuRegister>());
if (kPoisonHeapReferences) {
@@ -4647,7 +4650,7 @@
bool need_any_store_barrier,
bool need_any_any_barrier,
bool byte_swap = false) {
- DCHECK_IMPLIES(kEmitCompilerReadBarrier, kUseBakerReadBarrier);
+ DCHECK_IMPLIES(gUseReadBarrier, kUseBakerReadBarrier);
X86_64Assembler* assembler = codegen->GetAssembler();
LocationSummary* locations = invoke->GetLocations();
diff --git a/compiler/optimizing/intrinsics_x86_64.h b/compiler/optimizing/intrinsics_x86_64.h
index 199cfed..59fe815 100644
--- a/compiler/optimizing/intrinsics_x86_64.h
+++ b/compiler/optimizing/intrinsics_x86_64.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_INTRINSICS_X86_64_H_
#define ART_COMPILER_OPTIMIZING_INTRINSICS_X86_64_H_
+#include "base/macros.h"
#include "intrinsics.h"
-namespace art {
+namespace art HIDDEN {
class ArenaAllocator;
class HInvokeStaticOrDirect;
diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc
index 0edb23b..0c791b6 100644
--- a/compiler/optimizing/licm.cc
+++ b/compiler/optimizing/licm.cc
@@ -18,7 +18,7 @@
#include "side_effects_analysis.h"
-namespace art {
+namespace art HIDDEN {
static bool IsPhiOf(HInstruction* instruction, HBasicBlock* block) {
return instruction->IsPhi() && instruction->GetBlock() == block;
diff --git a/compiler/optimizing/licm.h b/compiler/optimizing/licm.h
index 9cafddb..1a86b6e 100644
--- a/compiler/optimizing/licm.h
+++ b/compiler/optimizing/licm.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_OPTIMIZING_LICM_H_
#define ART_COMPILER_OPTIMIZING_LICM_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
class SideEffectsAnalysis;
diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc
index adc3cab..f848109 100644
--- a/compiler/optimizing/licm_test.cc
+++ b/compiler/optimizing/licm_test.cc
@@ -17,12 +17,13 @@
#include "licm.h"
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
#include "side_effects_analysis.h"
-namespace art {
+namespace art HIDDEN {
/**
* Fixture class for the LICM tests.
diff --git a/compiler/optimizing/linear_order.cc b/compiler/optimizing/linear_order.cc
index 58e00a8..25ca866 100644
--- a/compiler/optimizing/linear_order.cc
+++ b/compiler/optimizing/linear_order.cc
@@ -19,7 +19,7 @@
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
-namespace art {
+namespace art HIDDEN {
static bool InSameLoop(HLoopInformation* first_loop, HLoopInformation* second_loop) {
return first_loop == second_loop;
diff --git a/compiler/optimizing/linear_order.h b/compiler/optimizing/linear_order.h
index 151db00..75e7504 100644
--- a/compiler/optimizing/linear_order.h
+++ b/compiler/optimizing/linear_order.h
@@ -19,9 +19,10 @@
#include <type_traits>
+#include "base/macros.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
void LinearizeGraphInternal(const HGraph* graph, ArrayRef<HBasicBlock*> linear_order);
diff --git a/compiler/optimizing/linearize_test.cc b/compiler/optimizing/linearize_test.cc
index d56ae11..b39bac5 100644
--- a/compiler/optimizing/linearize_test.cc
+++ b/compiler/optimizing/linearize_test.cc
@@ -17,6 +17,7 @@
#include <fstream>
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "code_generator.h"
#include "dex/dex_file.h"
@@ -28,7 +29,7 @@
#include "pretty_printer.h"
#include "ssa_liveness_analysis.h"
-namespace art {
+namespace art HIDDEN {
class LinearizeTest : public OptimizingUnitTest {
protected:
diff --git a/compiler/optimizing/live_interval_test.cc b/compiler/optimizing/live_interval_test.cc
index c60386d..b5d1336 100644
--- a/compiler/optimizing/live_interval_test.cc
+++ b/compiler/optimizing/live_interval_test.cc
@@ -15,12 +15,13 @@
*/
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "optimizing_unit_test.h"
#include "ssa_liveness_analysis.h"
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
TEST(LiveInterval, GetStart) {
ArenaPoolAndAllocator pool;
diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc
index bb8a4dc..4f52fdb 100644
--- a/compiler/optimizing/live_ranges_test.cc
+++ b/compiler/optimizing/live_ranges_test.cc
@@ -15,6 +15,7 @@
*/
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "code_generator.h"
#include "dex/dex_file.h"
@@ -25,7 +26,7 @@
#include "prepare_for_register_allocation.h"
#include "ssa_liveness_analysis.h"
-namespace art {
+namespace art HIDDEN {
class LiveRangesTest : public OptimizingUnitTest {
protected:
diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc
index ba3787e..a276373 100644
--- a/compiler/optimizing/liveness_test.cc
+++ b/compiler/optimizing/liveness_test.cc
@@ -15,6 +15,7 @@
*/
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "code_generator.h"
#include "dex/dex_file.h"
@@ -25,7 +26,7 @@
#include "prepare_for_register_allocation.h"
#include "ssa_liveness_analysis.h"
-namespace art {
+namespace art HIDDEN {
class LivenessTest : public OptimizingUnitTest {
protected:
diff --git a/compiler/optimizing/load_store_analysis.cc b/compiler/optimizing/load_store_analysis.cc
index 3fe42af..f1c50ac 100644
--- a/compiler/optimizing/load_store_analysis.cc
+++ b/compiler/optimizing/load_store_analysis.cc
@@ -19,7 +19,7 @@
#include "base/scoped_arena_allocator.h"
#include "optimizing/escape.h"
-namespace art {
+namespace art HIDDEN {
// A cap for the number of heap locations to prevent pathological time/space consumption.
// The number of heap locations for most of the methods stays below this threshold.
@@ -283,14 +283,6 @@
heap_location_collector_.CleanUp();
return false;
}
- if (heap_location_collector_.HasVolatile() || heap_location_collector_.HasMonitorOps()) {
- // Don't do load/store elimination if the method has volatile field accesses or
- // monitor operations, for now.
- // TODO: do it right.
- heap_location_collector_.CleanUp();
- return false;
- }
-
heap_location_collector_.BuildAliasingMatrix();
heap_location_collector_.DumpReferenceStats(stats_);
return true;
diff --git a/compiler/optimizing/load_store_analysis.h b/compiler/optimizing/load_store_analysis.h
index 4975bae..c0799db 100644
--- a/compiler/optimizing/load_store_analysis.h
+++ b/compiler/optimizing/load_store_analysis.h
@@ -20,6 +20,7 @@
#include "base/arena_allocator.h"
#include "base/arena_bit_vector.h"
#include "base/bit_vector-inl.h"
+#include "base/macros.h"
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
#include "base/stl_util.h"
@@ -28,7 +29,7 @@
#include "nodes.h"
#include "optimizing/optimizing_compiler_stats.h"
-namespace art {
+namespace art HIDDEN {
enum class LoadStoreAnalysisType {
kBasic,
@@ -253,8 +254,6 @@
heap_locations_(allocator->Adapter(kArenaAllocLSA)),
aliasing_matrix_(allocator, kInitialAliasingMatrixBitVectorSize, true, kArenaAllocLSA),
has_heap_stores_(false),
- has_volatile_(false),
- has_monitor_operations_(false),
lse_type_(lse_type) {
aliasing_matrix_.ClearAllBits();
}
@@ -350,14 +349,6 @@
return has_heap_stores_;
}
- bool HasVolatile() const {
- return has_volatile_;
- }
-
- bool HasMonitorOps() const {
- return has_monitor_operations_;
- }
-
// Find and return the heap location index in heap_locations_.
// NOTE: When heap locations are created, potentially aliasing/overlapping
// accesses are given different indexes. This find function also
@@ -540,9 +531,6 @@
}
void VisitFieldAccess(HInstruction* ref, const FieldInfo& field_info) {
- if (field_info.IsVolatile()) {
- has_volatile_ = true;
- }
DataType::Type type = field_info.GetFieldType();
const uint16_t declaring_class_def_index = field_info.GetDeclaringClassDefIndex();
const size_t offset = field_info.GetFieldOffset().SizeValue();
@@ -637,18 +625,12 @@
CreateReferenceInfoForReferenceType(instruction);
}
- void VisitMonitorOperation(HMonitorOperation* monitor ATTRIBUTE_UNUSED) override {
- has_monitor_operations_ = true;
- }
-
ScopedArenaAllocator* allocator_;
ScopedArenaVector<ReferenceInfo*> ref_info_array_; // All references used for heap accesses.
ScopedArenaVector<HeapLocation*> heap_locations_; // All heap locations.
ArenaBitVector aliasing_matrix_; // aliasing info between each pair of locations.
bool has_heap_stores_; // If there is no heap stores, LSE acts as GVN with better
// alias analysis and won't be as effective.
- bool has_volatile_; // If there are volatile field accesses.
- bool has_monitor_operations_; // If there are monitor operations.
LoadStoreAnalysisType lse_type_;
DISALLOW_COPY_AND_ASSIGN(HeapLocationCollector);
diff --git a/compiler/optimizing/load_store_analysis_test.cc b/compiler/optimizing/load_store_analysis_test.cc
index 3c26c8d..7930c0d 100644
--- a/compiler/optimizing/load_store_analysis_test.cc
+++ b/compiler/optimizing/load_store_analysis_test.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "base/macros.h"
#include "load_store_analysis.h"
#include <array>
@@ -36,7 +37,7 @@
#include "optimizing_unit_test.h"
#include "scoped_thread_state_change.h"
-namespace art {
+namespace art HIDDEN {
class LoadStoreAnalysisTest : public CommonCompilerTest, public OptimizingUnitTestHelper {
public:
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 9b8f07e..b385e03 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -319,7 +319,7 @@
* a hash map to the HeapLocationCollector.
*/
-namespace art {
+namespace art HIDDEN {
#define LSE_VLOG \
if (::art::LoadStoreElimination::kVerboseLoggingMode && VLOG_IS_ON(compiler)) LOG(INFO)
@@ -855,25 +855,6 @@
}
}
- // `instruction` is being removed. Try to see if the null check on it
- // can be removed. This can happen if the same value is set in two branches
- // but not in dominators. Such as:
- // int[] a = foo();
- // if () {
- // a[0] = 2;
- // } else {
- // a[0] = 2;
- // }
- // // a[0] can now be replaced with constant 2, and the null check on it can be removed.
- void TryRemovingNullCheck(HInstruction* instruction) {
- HInstruction* prev = instruction->GetPrevious();
- if ((prev != nullptr) && prev->IsNullCheck() && (prev == instruction->InputAt(0))) {
- // Previous instruction is a null check for this instruction. Remove the null check.
- prev->ReplaceWith(prev->InputAt(0));
- prev->GetBlock()->RemoveInstruction(prev);
- }
- }
-
HInstruction* GetDefaultValue(DataType::Type type) {
switch (type) {
case DataType::Type::kReference:
@@ -993,13 +974,63 @@
<< " but LSE should be the only source of predicated-ifield-gets!";
}
+ void HandleAcquireLoad(HInstruction* instruction) {
+ DCHECK((instruction->IsInstanceFieldGet() && instruction->AsInstanceFieldGet()->IsVolatile()) ||
+ (instruction->IsStaticFieldGet() && instruction->AsStaticFieldGet()->IsVolatile()) ||
+ (instruction->IsMonitorOperation() && instruction->AsMonitorOperation()->IsEnter()))
+ << "Unexpected instruction " << instruction->GetId() << ": " << instruction->DebugName();
+
+ // Acquire operations e.g. MONITOR_ENTER change the thread's view of the memory, so we must
+ // invalidate all current values.
+ ScopedArenaVector<ValueRecord>& heap_values =
+ heap_values_for_[instruction->GetBlock()->GetBlockId()];
+ for (size_t i = 0u, size = heap_values.size(); i != size; ++i) {
+ KeepStores(heap_values[i].stored_by);
+ heap_values[i].stored_by = Value::PureUnknown();
+ heap_values[i].value = Value::PartialUnknown(heap_values[i].value);
+ }
+
+ // Note that there's no need to record the load as subsequent acquire loads shouldn't be
+ // eliminated either.
+ }
+
+ void HandleReleaseStore(HInstruction* instruction) {
+ DCHECK((instruction->IsInstanceFieldSet() && instruction->AsInstanceFieldSet()->IsVolatile()) ||
+ (instruction->IsStaticFieldSet() && instruction->AsStaticFieldSet()->IsVolatile()) ||
+ (instruction->IsMonitorOperation() && !instruction->AsMonitorOperation()->IsEnter()))
+ << "Unexpected instruction " << instruction->GetId() << ": " << instruction->DebugName();
+
+ // Release operations e.g. MONITOR_EXIT do not affect this thread's view of the memory, but
+ // they will push the modifications for other threads to see. Therefore, we must keep the
+ // stores but there's no need to clobber the value.
+ ScopedArenaVector<ValueRecord>& heap_values =
+ heap_values_for_[instruction->GetBlock()->GetBlockId()];
+ for (size_t i = 0u, size = heap_values.size(); i != size; ++i) {
+ KeepStores(heap_values[i].stored_by);
+ heap_values[i].stored_by = Value::PureUnknown();
+ }
+
+ // Note that there's no need to record the store as subsequent release store shouldn't be
+ // eliminated either.
+ }
+
void VisitInstanceFieldGet(HInstanceFieldGet* instruction) override {
+ if (instruction->IsVolatile()) {
+ HandleAcquireLoad(instruction);
+ return;
+ }
+
HInstruction* object = instruction->InputAt(0);
const FieldInfo& field = instruction->GetFieldInfo();
VisitGetLocation(instruction, heap_location_collector_.GetFieldHeapLocation(object, &field));
}
void VisitInstanceFieldSet(HInstanceFieldSet* instruction) override {
+ if (instruction->IsVolatile()) {
+ HandleReleaseStore(instruction);
+ return;
+ }
+
HInstruction* object = instruction->InputAt(0);
const FieldInfo& field = instruction->GetFieldInfo();
HInstruction* value = instruction->InputAt(1);
@@ -1008,12 +1039,22 @@
}
void VisitStaticFieldGet(HStaticFieldGet* instruction) override {
+ if (instruction->IsVolatile()) {
+ HandleAcquireLoad(instruction);
+ return;
+ }
+
HInstruction* cls = instruction->InputAt(0);
const FieldInfo& field = instruction->GetFieldInfo();
VisitGetLocation(instruction, heap_location_collector_.GetFieldHeapLocation(cls, &field));
}
void VisitStaticFieldSet(HStaticFieldSet* instruction) override {
+ if (instruction->IsVolatile()) {
+ HandleReleaseStore(instruction);
+ return;
+ }
+
HInstruction* cls = instruction->InputAt(0);
const FieldInfo& field = instruction->GetFieldInfo();
HInstruction* value = instruction->InputAt(1);
@@ -1021,6 +1062,14 @@
VisitSetLocation(instruction, idx, value);
}
+ void VisitMonitorOperation(HMonitorOperation* monitor_op) override {
+ if (monitor_op->IsEnter()) {
+ HandleAcquireLoad(monitor_op);
+ } else {
+ HandleReleaseStore(monitor_op);
+ }
+ }
+
void VisitArrayGet(HArrayGet* instruction) override {
VisitGetLocation(instruction, heap_location_collector_.GetArrayHeapLocation(instruction));
}
@@ -1040,8 +1089,8 @@
}
void VisitDeoptimize(HDeoptimize* instruction) override {
- // If we are in a try catch, even singletons are observable.
- const bool in_try_catch = instruction->GetBlock()->GetTryCatchInformation() != nullptr;
+ // If we are in a try, even singletons are observable.
+ const bool inside_a_try = instruction->GetBlock()->IsTryBlock();
HBasicBlock* block = instruction->GetBlock();
ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[block->GetBlockId()];
for (size_t i = 0u, size = heap_values.size(); i != size; ++i) {
@@ -1053,7 +1102,7 @@
// for singletons that don't escape in the deoptimization environment.
bool observable = true;
ReferenceInfo* info = heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo();
- if (!in_try_catch && info->IsSingleton()) {
+ if (!inside_a_try && info->IsSingleton()) {
HInstruction* reference = info->GetReference();
// Finalizable objects always escape.
const bool finalizable_object =
@@ -1099,10 +1148,8 @@
void HandleThrowingInstruction(HInstruction* instruction) {
DCHECK(instruction->CanThrow());
- // If we are inside of a try catch, singletons can become visible since we may not exit the
- // method.
- HandleExit(instruction->GetBlock(),
- instruction->GetBlock()->GetTryCatchInformation() != nullptr);
+ // If we are inside of a try, singletons can become visible since we may not exit the method.
+ HandleExit(instruction->GetBlock(), instruction->GetBlock()->IsTryBlock());
}
void VisitMethodEntryHook(HMethodEntryHook* method_entry) override {
@@ -1137,6 +1184,14 @@
}
}
+ void VisitLoadMethodHandle(HLoadMethodHandle* load_method_handle) override {
+ HandleThrowingInstruction(load_method_handle);
+ }
+
+ void VisitLoadMethodType(HLoadMethodType* load_method_type) override {
+ HandleThrowingInstruction(load_method_type);
+ }
+
void VisitStringBuilderAppend(HStringBuilderAppend* sb_append) override {
HandleThrowingInstruction(sb_append);
}
@@ -1149,18 +1204,11 @@
HandleThrowingInstruction(check_cast);
}
- void VisitMonitorOperation(HMonitorOperation* monitor_op) override {
- if (monitor_op->CanThrow()) {
- HandleThrowingInstruction(monitor_op);
- }
- }
-
void HandleInvoke(HInstruction* instruction) {
// If `instruction` can throw we have to presume all stores are visible.
const bool can_throw = instruction->CanThrow();
- // If we are in a try catch, even singletons are observable.
- const bool can_throw_in_try_catch =
- can_throw && instruction->GetBlock()->GetTryCatchInformation() != nullptr;
+ // If we are in a try, even singletons are observable.
+ const bool can_throw_inside_a_try = can_throw && instruction->GetBlock()->IsTryBlock();
SideEffects side_effects = instruction->GetSideEffects();
ScopedArenaVector<ValueRecord>& heap_values =
heap_values_for_[instruction->GetBlock()->GetBlockId()];
@@ -1186,7 +1234,7 @@
return cohort.PrecedesBlock(blk);
});
};
- if (!can_throw_in_try_catch &&
+ if (!can_throw_inside_a_try &&
(ref_info->IsSingleton() ||
// partial and we aren't currently escaping and we haven't escaped yet.
(ref_info->IsPartialSingleton() && partial_singleton_did_not_escape(ref_info, blk)))) {
@@ -1235,8 +1283,8 @@
}
void VisitNewInstance(HNewInstance* new_instance) override {
- // If we are in a try catch, even singletons are observable.
- const bool in_try_catch = new_instance->GetBlock()->GetTryCatchInformation() != nullptr;
+ // If we are in a try, even singletons are observable.
+ const bool inside_a_try = new_instance->GetBlock()->IsTryBlock();
ReferenceInfo* ref_info = heap_location_collector_.FindReferenceInfoOf(new_instance);
if (ref_info == nullptr) {
// new_instance isn't used for field accesses. No need to process it.
@@ -1265,7 +1313,7 @@
heap_values[i].value = Value::ForInstruction(new_instance->GetLoadClass());
heap_values[i].stored_by = Value::PureUnknown();
}
- } else if (in_try_catch || IsEscapingObject(info, block, i)) {
+ } else if (inside_a_try || IsEscapingObject(info, block, i)) {
// Since NewInstance can throw, we presume all previous stores could be visible.
KeepStores(heap_values[i].stored_by);
heap_values[i].stored_by = Value::PureUnknown();
@@ -1274,8 +1322,8 @@
}
void VisitNewArray(HNewArray* new_array) override {
- // If we are in a try catch, even singletons are observable.
- const bool in_try_catch = new_array->GetBlock()->GetTryCatchInformation() != nullptr;
+ // If we are in a try, even singletons are observable.
+ const bool inside_a_try = new_array->GetBlock()->IsTryBlock();
ReferenceInfo* ref_info = heap_location_collector_.FindReferenceInfoOf(new_array);
if (ref_info == nullptr) {
// new_array isn't used for array accesses. No need to process it.
@@ -1300,7 +1348,7 @@
// Array elements are set to default heap values.
heap_values[i].value = Value::Default();
heap_values[i].stored_by = Value::PureUnknown();
- } else if (in_try_catch || IsEscapingObject(info, block, i)) {
+ } else if (inside_a_try || IsEscapingObject(info, block, i)) {
// Since NewArray can throw, we presume all previous stores could be visible.
KeepStores(heap_values[i].stored_by);
heap_values[i].stored_by = Value::PureUnknown();
@@ -1704,8 +1752,7 @@
ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[block->GetBlockId()];
DCHECK(heap_values.empty());
size_t num_heap_locations = heap_location_collector_.GetNumberOfHeapLocations();
- if (block->GetPredecessors().empty() || (block->GetTryCatchInformation() != nullptr &&
- block->GetTryCatchInformation()->IsCatchBlock())) {
+ if (block->GetPredecessors().empty() || block->IsCatchBlock()) {
DCHECK_IMPLIES(block->GetPredecessors().empty(), block->IsEntryBlock());
heap_values.resize(num_heap_locations,
{/*value=*/Value::PureUnknown(), /*stored_by=*/Value::PureUnknown()});
@@ -1764,7 +1811,6 @@
if (type == DataType::Type::kReference) {
// Update reference type information. Pass invalid handles, these are not used for Phis.
ReferenceTypePropagation rtp_fixup(block->GetGraph(),
- Handle<mirror::ClassLoader>(),
Handle<mirror::DexCache>(),
/* is_first_run= */ false);
rtp_fixup.Visit(phi);
@@ -1877,7 +1923,6 @@
}
HInstruction* heap_value = FindSubstitute(record.value.GetInstruction());
AddRemovedLoad(instruction, heap_value);
- TryRemovingNullCheck(instruction);
}
}
@@ -2352,7 +2397,6 @@
}
// Update reference type information. Pass invalid handles, these are not used for Phis.
ReferenceTypePropagation rtp_fixup(GetGraph(),
- Handle<mirror::ClassLoader>(),
Handle<mirror::DexCache>(),
/* is_first_run= */ false);
rtp_fixup.Visit(ArrayRef<HInstruction* const>(phis));
@@ -2639,7 +2683,6 @@
record.value = local_heap_values[idx];
HInstruction* heap_value = local_heap_values[idx].GetInstruction();
AddRemovedLoad(load_or_store, heap_value);
- TryRemovingNullCheck(load_or_store);
}
}
}
@@ -2698,7 +2741,6 @@
record.value = Replacement(record.value);
HInstruction* heap_value = record.value.GetInstruction();
AddRemovedLoad(load, heap_value);
- TryRemovingNullCheck(load);
}
}
}
@@ -3013,7 +3055,6 @@
return;
}
ReferenceTypePropagation rtp_fixup(GetGraph(),
- Handle<mirror::ClassLoader>(),
Handle<mirror::DexCache>(),
/* is_first_run= */ false);
rtp_fixup.Visit(ArrayRef<HInstruction* const>(new_ref_phis_));
diff --git a/compiler/optimizing/load_store_elimination.h b/compiler/optimizing/load_store_elimination.h
index 6ad2eb2..42de803 100644
--- a/compiler/optimizing/load_store_elimination.h
+++ b/compiler/optimizing/load_store_elimination.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_LOAD_STORE_ELIMINATION_H_
#define ART_COMPILER_OPTIMIZING_LOAD_STORE_ELIMINATION_H_
+#include "base/macros.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
class SideEffectsAnalysis;
diff --git a/compiler/optimizing/load_store_elimination_test.cc b/compiler/optimizing/load_store_elimination_test.cc
index 02dc939..a7a97f6 100644
--- a/compiler/optimizing/load_store_elimination_test.cc
+++ b/compiler/optimizing/load_store_elimination_test.cc
@@ -36,7 +36,7 @@
#include "optimizing_unit_test.h"
#include "scoped_thread_state_change.h"
-namespace art {
+namespace art HIDDEN {
#define CHECK_SUBROUTINE_FAILURE() \
do { \
@@ -542,6 +542,7 @@
AddVecStore(entry_block_, array_, j_);
HInstruction* vstore = AddVecStore(entry_block_, array_, i_);
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_FALSE(IsRemoved(vstore));
@@ -557,6 +558,7 @@
AddVecStore(entry_block_, array_, i_add1_);
HInstruction* vstore = AddVecStore(entry_block_, array_, i_);
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_FALSE(IsRemoved(vstore));
@@ -601,6 +603,7 @@
AddArraySet(entry_block_, array_, i_, c1);
HInstruction* vload5 = AddVecLoad(entry_block_, array_, i_);
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_TRUE(IsRemoved(load1));
@@ -634,6 +637,7 @@
// a[j] = 1;
HInstruction* array_set = AddArraySet(return_block_, array_, j_, c1);
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_TRUE(IsRemoved(array_set));
@@ -671,6 +675,7 @@
// a[j] = 0;
HInstruction* a_set = AddArraySet(return_block_, array_, j_, c0);
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_TRUE(IsRemoved(vload));
@@ -709,6 +714,7 @@
// x = a[j];
HInstruction* load = AddArrayGet(return_block_, array_, j_);
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_TRUE(IsRemoved(vload));
@@ -749,6 +755,7 @@
// down: a[i,... i + 3] = [1,...1]
HInstruction* vstore4 = AddVecStore(down, array_, i_, vdata);
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_TRUE(IsRemoved(vstore2));
@@ -839,6 +846,7 @@
HInstruction* vstore2 = AddVecStore(loop_, array_b, phi_, vload->AsVecLoad());
HInstruction* vstore3 = AddVecStore(loop_, array_a, phi_, vstore1->InputAt(2));
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_FALSE(IsRemoved(vstore1));
@@ -894,7 +902,7 @@
// loop:
// array2[i] = array[i]
// array[0] = 2
- HInstruction* store1 = AddArraySet(entry_block_, array_, c0, c2);
+ HInstruction* store1 = AddArraySet(pre_header_, array_, c0, c2);
HInstruction* load = AddArrayGet(loop_, array_, phi_);
HInstruction* store2 = AddArraySet(loop_, array2, phi_, load);
@@ -926,6 +934,7 @@
HInstruction* vload = AddVecLoad(loop_, array_a, phi_);
HInstruction* vstore = AddVecStore(return_block_, array_, c0, vload->AsVecLoad());
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_FALSE(IsRemoved(vload));
@@ -949,6 +958,7 @@
HInstruction* vload = AddVecLoad(pre_header_, array_a, c0);
HInstruction* vstore = AddVecStore(return_block_, array_, c0, vload->AsVecLoad());
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_FALSE(IsRemoved(vload));
@@ -1025,6 +1035,7 @@
HInstruction* vstore = AddVecStore(return_block_, array_, c0, vload->AsVecLoad());
HInstruction* store = AddArraySet(return_block_, array_, c0, load);
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_FALSE(IsRemoved(vload));
@@ -1055,6 +1066,7 @@
HInstruction* vstore = AddVecStore(return_block_, array_, c0, vload->AsVecLoad());
HInstruction* store = AddArraySet(return_block_, array_, c0, load);
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_FALSE(IsRemoved(vload));
@@ -1086,6 +1098,7 @@
HInstruction* vstore1 = AddVecStore(return_block_, array_, c0, vload1->AsVecLoad());
HInstruction* vstore2 = AddVecStore(return_block_, array_, c128, vload2->AsVecLoad());
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_FALSE(IsRemoved(vload1));
@@ -1116,6 +1129,7 @@
HInstruction* vstore1 = AddVecStore(return_block_, array_, c0, vload1->AsVecLoad());
HInstruction* vstore2 = AddVecStore(return_block_, array_, c128, vload2->AsVecLoad());
+ graph_->SetHasSIMD(true);
PerformLSE();
ASSERT_FALSE(IsRemoved(vload1));
@@ -4829,8 +4843,9 @@
CreateGraph(/*handles=*/&vshs);
AdjacencyListGraph blks(SetupFromAdjacencyList("entry",
"exit",
- {{"entry", "critical_break"},
- {"entry", "partial"},
+ {{"entry", "first_block"},
+ {"first_block", "critical_break"},
+ {"first_block", "partial"},
{"partial", "merge"},
{"critical_break", "merge"},
{"merge", "left"},
@@ -4839,7 +4854,7 @@
{"right", "breturn"},
{"breturn", "exit"}}));
#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name)
- GET_BLOCK(entry);
+ GET_BLOCK(first_block);
GET_BLOCK(merge);
GET_BLOCK(partial);
GET_BLOCK(critical_break);
@@ -4858,12 +4873,12 @@
HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32));
ComparisonInstructions cmp_instructions = GetComparisonInstructions(new_inst);
HInstruction* if_inst = new (GetAllocator()) HIf(cmp_instructions.cmp_);
- entry->AddInstruction(cls);
- entry->AddInstruction(new_inst);
- entry->AddInstruction(write_entry);
- cmp_instructions.AddSetup(entry);
- entry->AddInstruction(cmp_instructions.cmp_);
- entry->AddInstruction(if_inst);
+ first_block->AddInstruction(cls);
+ first_block->AddInstruction(new_inst);
+ first_block->AddInstruction(write_entry);
+ cmp_instructions.AddSetup(first_block);
+ first_block->AddInstruction(cmp_instructions.cmp_);
+ first_block->AddInstruction(if_inst);
ManuallyBuildEnvFor(cls, {});
cmp_instructions.AddEnvironment(cls->GetEnvironment());
new_inst->CopyEnvironmentFrom(cls->GetEnvironment());
diff --git a/compiler/optimizing/locations.cc b/compiler/optimizing/locations.cc
index 5879c6f..595064f 100644
--- a/compiler/optimizing/locations.cc
+++ b/compiler/optimizing/locations.cc
@@ -21,7 +21,7 @@
#include "code_generator.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
// Verify that Location is trivially copyable.
static_assert(std::is_trivially_copyable<Location>::value, "Location should be trivially copyable");
diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h
index acaea71..dc87284 100644
--- a/compiler/optimizing/locations.h
+++ b/compiler/optimizing/locations.h
@@ -22,9 +22,10 @@
#include "base/bit_field.h"
#include "base/bit_utils.h"
#include "base/bit_vector.h"
+#include "base/macros.h"
#include "base/value_object.h"
-namespace art {
+namespace art HIDDEN {
class HConstant;
class HInstruction;
diff --git a/compiler/optimizing/loop_analysis.cc b/compiler/optimizing/loop_analysis.cc
index 76bd849..95e8153 100644
--- a/compiler/optimizing/loop_analysis.cc
+++ b/compiler/optimizing/loop_analysis.cc
@@ -20,7 +20,7 @@
#include "code_generator.h"
#include "induction_var_range.h"
-namespace art {
+namespace art HIDDEN {
void LoopAnalysis::CalculateLoopBasicProperties(HLoopInformation* loop_info,
LoopAnalysisInfo* analysis_results,
diff --git a/compiler/optimizing/loop_analysis.h b/compiler/optimizing/loop_analysis.h
index fbf1516..cec00fe 100644
--- a/compiler/optimizing/loop_analysis.h
+++ b/compiler/optimizing/loop_analysis.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_LOOP_ANALYSIS_H_
#define ART_COMPILER_OPTIMIZING_LOOP_ANALYSIS_H_
+#include "base/macros.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
class InductionVarRange;
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index 2d7c208..7a52502 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -27,7 +27,7 @@
#include "mirror/array-inl.h"
#include "mirror/string.h"
-namespace art {
+namespace art HIDDEN {
// Enables vectorization (SIMDization) in the loop optimizer.
static constexpr bool kEnableVectorization = true;
@@ -507,9 +507,8 @@
graph_->SetHasLoops(false); // no more loops
}
- // Detach.
+ // Detach allocator.
loop_allocator_ = nullptr;
- last_loop_ = top_loop_ = nullptr;
return did_loop_opt;
}
@@ -530,11 +529,7 @@
AddLoop(block->GetLoopInformation());
}
}
-
- // TODO(solanes): How can `top_loop_` be null if `graph_->HasLoops()` is true?
- if (top_loop_ == nullptr) {
- return false;
- }
+ DCHECK(top_loop_ != nullptr);
// Traverse the loop hierarchy inner-to-outer and optimize. Traversal can use
// temporary data structures using the phase-local allocator. All new HIR
@@ -681,6 +676,50 @@
}
//
+// This optimization applies to loops with plain simple operations
+// (I.e. no calls to java code or runtime) with a known small trip_count * instr_count
+// value.
+//
+bool HLoopOptimization::TryToRemoveSuspendCheckFromLoopHeader(LoopAnalysisInfo* analysis_info,
+ bool generate_code) {
+ if (!graph_->SuspendChecksAreAllowedToNoOp()) {
+ return false;
+ }
+
+ int64_t trip_count = analysis_info->GetTripCount();
+
+ if (trip_count == LoopAnalysisInfo::kUnknownTripCount) {
+ return false;
+ }
+
+ int64_t instruction_count = analysis_info->GetNumberOfInstructions();
+ int64_t total_instruction_count = trip_count * instruction_count;
+
+ // The inclusion of the HasInstructionsPreventingScalarOpts() prevents this
+ // optimization from being applied to loops that have calls.
+ bool can_optimize =
+ total_instruction_count <= HLoopOptimization::kMaxTotalInstRemoveSuspendCheck &&
+ !analysis_info->HasInstructionsPreventingScalarOpts();
+
+ if (!can_optimize) {
+ return false;
+ }
+
+ // If we should do the optimization, disable codegen for the SuspendCheck.
+ if (generate_code) {
+ HLoopInformation* loop_info = analysis_info->GetLoopInfo();
+ HBasicBlock* header = loop_info->GetHeader();
+ HSuspendCheck* instruction = header->GetLoopInformation()->GetSuspendCheck();
+ // As other optimizations depend on SuspendCheck
+ // (e.g: CHAGuardVisitor::HoistGuard), disable its codegen instead of
+ // removing the SuspendCheck instruction.
+ instruction->SetIsNoOp(true);
+ }
+
+ return true;
+}
+
+//
// Optimization.
//
@@ -824,7 +863,7 @@
}
bool HLoopOptimization::OptimizeInnerLoop(LoopNode* node) {
- return TryOptimizeInnerLoopFinite(node) || TryPeelingAndUnrolling(node);
+ return TryOptimizeInnerLoopFinite(node) || TryLoopScalarOpts(node);
}
//
@@ -928,7 +967,7 @@
return true;
}
-bool HLoopOptimization::TryPeelingAndUnrolling(LoopNode* node) {
+bool HLoopOptimization::TryLoopScalarOpts(LoopNode* node) {
HLoopInformation* loop_info = node->loop_info;
int64_t trip_count = LoopAnalysis::GetLoopTripCount(loop_info, &induction_range_);
LoopAnalysisInfo analysis_info(loop_info);
@@ -941,10 +980,16 @@
if (!TryFullUnrolling(&analysis_info, /*generate_code*/ false) &&
!TryPeelingForLoopInvariantExitsElimination(&analysis_info, /*generate_code*/ false) &&
- !TryUnrollingForBranchPenaltyReduction(&analysis_info, /*generate_code*/ false)) {
+ !TryUnrollingForBranchPenaltyReduction(&analysis_info, /*generate_code*/ false) &&
+ !TryToRemoveSuspendCheckFromLoopHeader(&analysis_info, /*generate_code*/ false)) {
return false;
}
+ // Try the suspend check removal even for non-clonable loops. Also this
+ // optimization doesn't interfere with other scalar loop optimizations so it can
+ // be done prior to them.
+ bool removed_suspend_check = TryToRemoveSuspendCheckFromLoopHeader(&analysis_info);
+
// Run 'IsLoopClonable' the last as it might be time-consuming.
if (!LoopClonerHelper::IsLoopClonable(loop_info)) {
return false;
@@ -952,7 +997,7 @@
return TryFullUnrolling(&analysis_info) ||
TryPeelingForLoopInvariantExitsElimination(&analysis_info) ||
- TryUnrollingForBranchPenaltyReduction(&analysis_info);
+ TryUnrollingForBranchPenaltyReduction(&analysis_info) || removed_suspend_check;
}
//
diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h
index b178616..6dd778b 100644
--- a/compiler/optimizing/loop_optimization.h
+++ b/compiler/optimizing/loop_optimization.h
@@ -17,6 +17,7 @@
#ifndef ART_COMPILER_OPTIMIZING_LOOP_OPTIMIZATION_H_
#define ART_COMPILER_OPTIMIZING_LOOP_OPTIMIZATION_H_
+#include "base/macros.h"
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
#include "induction_var_range.h"
@@ -25,7 +26,7 @@
#include "optimization.h"
#include "superblock_cloner.h"
-namespace art {
+namespace art HIDDEN {
class CompilerOptions;
class ArchNoOptsLoopHelper;
@@ -47,6 +48,11 @@
static constexpr const char* kLoopOptimizationPassName = "loop_optimization";
+ // The maximum number of total instructions (trip_count * instruction_count),
+ // where the optimization of removing SuspendChecks from the loop header could
+ // be performed.
+ static constexpr int64_t kMaxTotalInstRemoveSuspendCheck = 128;
+
private:
/**
* A single loop inside the loop hierarchy representation.
@@ -179,8 +185,19 @@
// should be actually applied.
bool TryFullUnrolling(LoopAnalysisInfo* analysis_info, bool generate_code = true);
- // Tries to apply scalar loop peeling and unrolling.
- bool TryPeelingAndUnrolling(LoopNode* node);
+ // Tries to remove SuspendCheck for plain loops with a low trip count. The
+ // SuspendCheck in the codegen makes sure that the thread can be interrupted
+ // during execution for GC. Not being able to do so might decrease the
+ // responsiveness of GC when a very long loop or a long recursion is being
+ // executed. However, for plain loops with a small trip count, the removal of
+ // SuspendCheck should not affect the GC's responsiveness by a large margin.
+ // Consequently, since the thread won't be interrupted for plain loops, it is
+ // assumed that the performance might increase by removing SuspendCheck.
+ bool TryToRemoveSuspendCheckFromLoopHeader(LoopAnalysisInfo* analysis_info,
+ bool generate_code = true);
+
+ // Tries to apply scalar loop optimizations.
+ bool TryLoopScalarOpts(LoopNode* node);
//
// Vectorization analysis and synthesis.
diff --git a/compiler/optimizing/loop_optimization_test.cc b/compiler/optimizing/loop_optimization_test.cc
index bda2528..7f694fb 100644
--- a/compiler/optimizing/loop_optimization_test.cc
+++ b/compiler/optimizing/loop_optimization_test.cc
@@ -14,12 +14,13 @@
* limitations under the License.
*/
+#include "base/macros.h"
#include "code_generator.h"
#include "driver/compiler_options.h"
#include "loop_optimization.h"
#include "optimizing_unit_test.h"
-namespace art {
+namespace art HIDDEN {
/**
* Fixture class for the loop optimization tests. These unit tests focus
@@ -94,10 +95,7 @@
void PerformAnalysis() {
graph_->BuildDominatorTree();
iva_->Run();
- // Do not release the loop hierarchy.
- ScopedArenaAllocator loop_allocator(GetArenaStack());
- loop_opt_->loop_allocator_ = &loop_allocator;
- loop_opt_->LocalRun();
+ loop_opt_->Run();
}
/** Constructs string representation of computed loop hierarchy. */
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index d35ed1c..0876ce0 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -40,7 +40,7 @@
#include "scoped_thread_state_change-inl.h"
#include "ssa_builder.h"
-namespace art {
+namespace art HIDDEN {
// Enable floating-point static evaluation during constant folding
// only if all floating-point operations and constants evaluate in the
@@ -150,30 +150,54 @@
RemoveEnvironmentUses(instruction);
}
-void HGraph::RemoveInstructionsAsUsersFromDeadBlocks(const ArenaBitVector& visited) const {
+void HGraph::RemoveDeadBlocksInstructionsAsUsersAndDisconnect(const ArenaBitVector& visited) const {
for (size_t i = 0; i < blocks_.size(); ++i) {
if (!visited.IsBitSet(i)) {
HBasicBlock* block = blocks_[i];
if (block == nullptr) continue;
+
+ // Remove as user.
for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
RemoveAsUser(it.Current());
}
for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
RemoveAsUser(it.Current());
}
+
+ // Remove non-catch phi uses, and disconnect the block.
+ block->DisconnectFromSuccessors(&visited);
+ }
+ }
+}
+
+// This method assumes `insn` has been removed from all users with the exception of catch
+// phis because of missing exceptional edges in the graph. It removes the
+// instruction from catch phi uses, together with inputs of other catch phis in
+// the catch block at the same index, as these must be dead too.
+static void RemoveCatchPhiUsesOfDeadInstruction(HInstruction* insn) {
+ DCHECK(!insn->HasEnvironmentUses());
+ while (insn->HasNonEnvironmentUses()) {
+ const HUseListNode<HInstruction*>& use = insn->GetUses().front();
+ size_t use_index = use.GetIndex();
+ HBasicBlock* user_block = use.GetUser()->GetBlock();
+ DCHECK(use.GetUser()->IsPhi());
+ DCHECK(user_block->IsCatchBlock());
+ for (HInstructionIterator phi_it(user_block->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
+ phi_it.Current()->AsPhi()->RemoveInputAt(use_index);
}
}
}
void HGraph::RemoveDeadBlocks(const ArenaBitVector& visited) {
+ DCHECK(reverse_post_order_.empty()) << "We shouldn't have dominance information.";
for (size_t i = 0; i < blocks_.size(); ++i) {
if (!visited.IsBitSet(i)) {
HBasicBlock* block = blocks_[i];
if (block == nullptr) continue;
- // We only need to update the successor, which might be live.
- for (HBasicBlock* successor : block->GetSuccessors()) {
- successor->RemovePredecessor(block);
- }
+
+ // Remove all remaining uses (which should be only catch phi uses), and the instructions.
+ block->RemoveCatchPhiUsesAndInstruction(/* building_dominator_tree = */ true);
+
// Remove the block from the list of blocks, so that further analyses
// never see it.
blocks_[i] = nullptr;
@@ -200,7 +224,8 @@
// (2) Remove instructions and phis from blocks not visited during
// the initial DFS as users from other instructions, so that
// users can be safely removed before uses later.
- RemoveInstructionsAsUsersFromDeadBlocks(visited);
+ // Also disconnect the block from its successors, updating the successor's phis if needed.
+ RemoveDeadBlocksInstructionsAsUsersAndDisconnect(visited);
// (3) Remove blocks not visited during the initial DFS.
// Step (5) requires dead blocks to be removed from the
@@ -237,6 +262,7 @@
}
void HGraph::ClearLoopInformation() {
+ SetHasLoops(false);
SetHasIrreducibleLoops(false);
for (HBasicBlock* block : GetActiveBlocks()) {
block->SetLoopInformation(nullptr);
@@ -544,6 +570,15 @@
}
}
+HBasicBlock* HGraph::SplitEdgeAndUpdateRPO(HBasicBlock* block, HBasicBlock* successor) {
+ HBasicBlock* new_block = SplitEdge(block, successor);
+ // In the RPO we have {... , block, ... , successor}. We want to insert `new_block` right after
+ // `block` to have a consistent RPO without recomputing the whole graph's RPO.
+ reverse_post_order_.insert(
+ reverse_post_order_.begin() + IndexOfElement(reverse_post_order_, block) + 1, new_block);
+ return new_block;
+}
+
// Reorder phi inputs to match reordering of the block's predecessors.
static void FixPhisAfterPredecessorsReodering(HBasicBlock* block, size_t first, size_t second) {
for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
@@ -708,6 +743,8 @@
void HGraph::ComputeTryBlockInformation() {
// Iterate in reverse post order to propagate try membership information from
// predecessors to their successors.
+ bool graph_has_try_catch = false;
+
for (HBasicBlock* block : GetReversePostOrder()) {
if (block->IsEntryBlock() || block->IsCatchBlock()) {
// Catch blocks after simplification have only exceptional predecessors
@@ -722,6 +759,7 @@
DCHECK_IMPLIES(block->IsLoopHeader(),
!block->GetLoopInformation()->IsBackEdge(*first_predecessor));
const HTryBoundary* try_entry = first_predecessor->ComputeTryEntryOfSuccessors();
+ graph_has_try_catch |= try_entry != nullptr;
if (try_entry != nullptr &&
(block->GetTryCatchInformation() == nullptr ||
try_entry != &block->GetTryCatchInformation()->GetTryEntry())) {
@@ -730,6 +768,8 @@
block->SetTryCatchInformation(new (allocator_) TryCatchInformation(*try_entry));
}
}
+
+ SetHasTryCatch(graph_has_try_catch);
}
void HGraph::SimplifyCFG() {
@@ -1459,6 +1499,10 @@
UNREACHABLE();
}
+bool HInstruction::Dominates(HInstruction* other_instruction) const {
+ return other_instruction == this || StrictlyDominates(other_instruction);
+}
+
bool HInstruction::StrictlyDominates(HInstruction* other_instruction) const {
if (other_instruction == this) {
// An instruction does not strictly dominate itself.
@@ -1518,14 +1562,19 @@
DCHECK(env_uses_.empty());
}
-void HInstruction::ReplaceUsesDominatedBy(HInstruction* dominator, HInstruction* replacement) {
+void HInstruction::ReplaceUsesDominatedBy(HInstruction* dominator,
+ HInstruction* replacement,
+ bool strictly_dominated) {
const HUseList<HInstruction*>& uses = GetUses();
for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
HInstruction* user = it->GetUser();
size_t index = it->GetIndex();
// Increment `it` now because `*it` may disappear thanks to user->ReplaceInput().
++it;
- if (dominator->StrictlyDominates(user)) {
+ const bool dominated =
+ strictly_dominated ? dominator->StrictlyDominates(user) : dominator->Dominates(user);
+
+ if (dominated) {
user->ReplaceInput(replacement, index);
} else if (user->IsPhi() && !user->AsPhi()->IsCatchPhi()) {
// If the input flows from a block dominated by `dominator`, we can replace it.
@@ -2108,8 +2157,9 @@
MoveBefore(insert_pos);
}
-HBasicBlock* HBasicBlock::SplitBefore(HInstruction* cursor) {
- DCHECK(!graph_->IsInSsaForm()) << "Support for SSA form not implemented.";
+HBasicBlock* HBasicBlock::SplitBefore(HInstruction* cursor, bool require_graph_not_in_ssa_form) {
+ DCHECK_IMPLIES(require_graph_not_in_ssa_form, !graph_->IsInSsaForm())
+ << "Support for SSA form not implemented.";
DCHECK_EQ(cursor->GetBlock(), this);
HBasicBlock* new_block =
@@ -2376,24 +2426,6 @@
}
}
-// Should be called on instructions in a dead block in post order. This method
-// assumes `insn` has been removed from all users with the exception of catch
-// phis because of missing exceptional edges in the graph. It removes the
-// instruction from catch phi uses, together with inputs of other catch phis in
-// the catch block at the same index, as these must be dead too.
-static void RemoveUsesOfDeadInstruction(HInstruction* insn) {
- DCHECK(!insn->HasEnvironmentUses());
- while (insn->HasNonEnvironmentUses()) {
- const HUseListNode<HInstruction*>& use = insn->GetUses().front();
- size_t use_index = use.GetIndex();
- HBasicBlock* user_block = use.GetUser()->GetBlock();
- DCHECK(use.GetUser()->IsPhi() && user_block->IsCatchBlock());
- for (HInstructionIterator phi_it(user_block->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
- phi_it.Current()->AsPhi()->RemoveInputAt(use_index);
- }
- }
-}
-
void HBasicBlock::DisconnectAndDelete() {
// Dominators must be removed after all the blocks they dominate. This way
// a loop header is removed last, a requirement for correct loop information
@@ -2418,52 +2450,14 @@
}
// (2) Disconnect the block from its successors and update their phis.
- for (HBasicBlock* successor : successors_) {
- // Delete this block from the list of predecessors.
- size_t this_index = successor->GetPredecessorIndexOf(this);
- successor->predecessors_.erase(successor->predecessors_.begin() + this_index);
-
- // Check that `successor` has other predecessors, otherwise `this` is the
- // dominator of `successor` which violates the order DCHECKed at the top.
- DCHECK(!successor->predecessors_.empty());
-
- // Remove this block's entries in the successor's phis. Skip exceptional
- // successors because catch phi inputs do not correspond to predecessor
- // blocks but throwing instructions. The inputs of the catch phis will be
- // updated in step (3).
- if (!successor->IsCatchBlock()) {
- if (successor->predecessors_.size() == 1u) {
- // The successor has just one predecessor left. Replace phis with the only
- // remaining input.
- for (HInstructionIterator phi_it(successor->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
- HPhi* phi = phi_it.Current()->AsPhi();
- phi->ReplaceWith(phi->InputAt(1 - this_index));
- successor->RemovePhi(phi);
- }
- } else {
- for (HInstructionIterator phi_it(successor->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
- phi_it.Current()->AsPhi()->RemoveInputAt(this_index);
- }
- }
- }
- }
- successors_.clear();
+ DisconnectFromSuccessors();
// (3) Remove instructions and phis. Instructions should have no remaining uses
// except in catch phis. If an instruction is used by a catch phi at `index`,
// remove `index`-th input of all phis in the catch block since they are
// guaranteed dead. Note that we may miss dead inputs this way but the
// graph will always remain consistent.
- for (HBackwardInstructionIterator it(GetInstructions()); !it.Done(); it.Advance()) {
- HInstruction* insn = it.Current();
- RemoveUsesOfDeadInstruction(insn);
- RemoveInstruction(insn);
- }
- for (HInstructionIterator it(GetPhis()); !it.Done(); it.Advance()) {
- HPhi* insn = it.Current()->AsPhi();
- RemoveUsesOfDeadInstruction(insn);
- RemovePhi(insn);
- }
+ RemoveCatchPhiUsesAndInstruction(/* building_dominator_tree = */ false);
// (4) Disconnect the block from its predecessors and update their
// control-flow instructions.
@@ -2537,6 +2531,70 @@
SetGraph(nullptr);
}
+void HBasicBlock::DisconnectFromSuccessors(const ArenaBitVector* visited) {
+ for (HBasicBlock* successor : successors_) {
+ // Delete this block from the list of predecessors.
+ size_t this_index = successor->GetPredecessorIndexOf(this);
+ successor->predecessors_.erase(successor->predecessors_.begin() + this_index);
+
+ if (visited != nullptr && !visited->IsBitSet(successor->GetBlockId())) {
+ // `successor` itself is dead. Therefore, there is no need to update its phis.
+ continue;
+ }
+
+ DCHECK(!successor->predecessors_.empty());
+
+ // Remove this block's entries in the successor's phis. Skips exceptional
+ // successors because catch phi inputs do not correspond to predecessor
+ // blocks but throwing instructions. They are removed in `RemoveCatchPhiUses`.
+ if (!successor->IsCatchBlock()) {
+ if (successor->predecessors_.size() == 1u) {
+ // The successor has just one predecessor left. Replace phis with the only
+ // remaining input.
+ for (HInstructionIterator phi_it(successor->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
+ HPhi* phi = phi_it.Current()->AsPhi();
+ phi->ReplaceWith(phi->InputAt(1 - this_index));
+ successor->RemovePhi(phi);
+ }
+ } else {
+ for (HInstructionIterator phi_it(successor->GetPhis()); !phi_it.Done(); phi_it.Advance()) {
+ phi_it.Current()->AsPhi()->RemoveInputAt(this_index);
+ }
+ }
+ }
+ }
+ successors_.clear();
+}
+
+void HBasicBlock::RemoveCatchPhiUsesAndInstruction(bool building_dominator_tree) {
+ for (HBackwardInstructionIterator it(GetInstructions()); !it.Done(); it.Advance()) {
+ HInstruction* insn = it.Current();
+ RemoveCatchPhiUsesOfDeadInstruction(insn);
+
+ // If we are building the dominator tree, we removed all input records previously.
+ // `RemoveInstruction` will try to remove them again but that's not something we support and we
+ // will crash. We check here since we won't be checking that in RemoveInstruction.
+ if (building_dominator_tree) {
+ DCHECK(insn->GetUses().empty());
+ DCHECK(insn->GetEnvUses().empty());
+ }
+ RemoveInstruction(insn, /* ensure_safety= */ !building_dominator_tree);
+ }
+ for (HInstructionIterator it(GetPhis()); !it.Done(); it.Advance()) {
+ HPhi* insn = it.Current()->AsPhi();
+ RemoveCatchPhiUsesOfDeadInstruction(insn);
+
+ // If we are building the dominator tree, we removed all input records previously.
+ // `RemovePhi` will try to remove them again but that's not something we support and we
+ // will crash. We check here since we won't be checking that in RemovePhi.
+ if (building_dominator_tree) {
+ DCHECK(insn->GetUses().empty());
+ DCHECK(insn->GetEnvUses().empty());
+ }
+ RemovePhi(insn, /* ensure_safety= */ !building_dominator_tree);
+ }
+}
+
void HBasicBlock::MergeInstructionsWith(HBasicBlock* other) {
DCHECK(EndsWithControlFlowInstruction());
RemoveInstruction(GetLastInstruction());
@@ -2660,7 +2718,8 @@
void HGraph::UpdateLoopAndTryInformationOfNewBlock(HBasicBlock* block,
HBasicBlock* reference,
- bool replace_if_back_edge) {
+ bool replace_if_back_edge,
+ bool has_more_specific_try_catch_info) {
if (block->IsLoopHeader()) {
// Clear the information of which blocks are contained in that loop. Since the
// information is stored as a bit vector based on block ids, we have to update
@@ -2687,11 +2746,16 @@
}
}
- // Copy TryCatchInformation if `reference` is a try block, not if it is a catch block.
- TryCatchInformation* try_catch_info = reference->IsTryBlock()
- ? reference->GetTryCatchInformation()
- : nullptr;
- block->SetTryCatchInformation(try_catch_info);
+ DCHECK_IMPLIES(has_more_specific_try_catch_info, !reference->IsTryBlock())
+ << "We don't allow to inline try catches inside of other try blocks.";
+
+ // Update the TryCatchInformation, if we are not inlining a try catch.
+ if (!has_more_specific_try_catch_info) {
+ // Copy TryCatchInformation if `reference` is a try block, not if it is a catch block.
+ TryCatchInformation* try_catch_info =
+ reference->IsTryBlock() ? reference->GetTryCatchInformation() : nullptr;
+ block->SetTryCatchInformation(try_catch_info);
+ }
}
HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
@@ -2730,9 +2794,15 @@
if (HasTryCatch()) {
outer_graph->SetHasTryCatch(true);
}
+ if (HasMonitorOperations()) {
+ outer_graph->SetHasMonitorOperations(true);
+ }
if (HasSIMD()) {
outer_graph->SetHasSIMD(true);
}
+ if (HasAlwaysThrowingInvokes()) {
+ outer_graph->SetHasAlwaysThrowingInvokes(true);
+ }
HInstruction* return_value = nullptr;
if (GetBlocks().size() == 3) {
@@ -2771,6 +2841,7 @@
HBasicBlock* first = entry_block_->GetSuccessors()[0];
DCHECK(!first->IsInLoop());
+ DCHECK(first->GetTryCatchInformation() == nullptr);
at->MergeWithInlined(first);
exit_block_->ReplaceWith(to);
@@ -2801,12 +2872,14 @@
// and (4) to the blocks that apply.
for (HBasicBlock* current : GetReversePostOrder()) {
if (current != exit_block_ && current != entry_block_ && current != first) {
- DCHECK(current->GetTryCatchInformation() == nullptr);
DCHECK(current->GetGraph() == this);
current->SetGraph(outer_graph);
outer_graph->AddBlock(current);
outer_graph->reverse_post_order_[++index_of_at] = current;
- UpdateLoopAndTryInformationOfNewBlock(current, at, /* replace_if_back_edge= */ false);
+ UpdateLoopAndTryInformationOfNewBlock(current,
+ at,
+ /* replace_if_back_edge= */ false,
+ current->GetTryCatchInformation() != nullptr);
}
}
@@ -2820,25 +2893,62 @@
// Update all predecessors of the exit block (now the `to` block)
// to not `HReturn` but `HGoto` instead. Special case throwing blocks
- // to now get the outer graph exit block as successor. Note that the inliner
- // currently doesn't support inlining methods with try/catch.
+ // to now get the outer graph exit block as successor.
HPhi* return_value_phi = nullptr;
bool rerun_dominance = false;
bool rerun_loop_analysis = false;
for (size_t pred = 0; pred < to->GetPredecessors().size(); ++pred) {
HBasicBlock* predecessor = to->GetPredecessors()[pred];
HInstruction* last = predecessor->GetLastInstruction();
+
+ // At this point we might either have:
+ // A) Return/ReturnVoid/Throw as the last instruction, or
+ // B) `Return/ReturnVoid/Throw->TryBoundary` as the last instruction chain
+
+ const bool saw_try_boundary = last->IsTryBoundary();
+ if (saw_try_boundary) {
+ DCHECK(predecessor->IsSingleTryBoundary());
+ DCHECK(!last->AsTryBoundary()->IsEntry());
+ predecessor = predecessor->GetSinglePredecessor();
+ last = predecessor->GetLastInstruction();
+ }
+
if (last->IsThrow()) {
- DCHECK(!at->IsTryBlock());
- predecessor->ReplaceSuccessor(to, outer_graph->GetExitBlock());
+ if (at->IsTryBlock()) {
+ DCHECK(!saw_try_boundary) << "We don't support inlining of try blocks into try blocks.";
+ // Create a TryBoundary of kind:exit and point it to the Exit block.
+ HBasicBlock* new_block = outer_graph->SplitEdge(predecessor, to);
+ new_block->AddInstruction(
+ new (allocator) HTryBoundary(HTryBoundary::BoundaryKind::kExit, last->GetDexPc()));
+ new_block->ReplaceSuccessor(to, outer_graph->GetExitBlock());
+
+ // Copy information from the predecessor.
+ new_block->SetLoopInformation(predecessor->GetLoopInformation());
+ TryCatchInformation* try_catch_info = predecessor->GetTryCatchInformation();
+ new_block->SetTryCatchInformation(try_catch_info);
+ for (HBasicBlock* xhandler :
+ try_catch_info->GetTryEntry().GetBlock()->GetExceptionalSuccessors()) {
+ new_block->AddSuccessor(xhandler);
+ }
+ DCHECK(try_catch_info->GetTryEntry().HasSameExceptionHandlersAs(
+ *new_block->GetLastInstruction()->AsTryBoundary()));
+ } else {
+ // We either have `Throw->TryBoundary` or `Throw`. We want to point the whole chain to the
+ // exit, so we recompute `predecessor`
+ predecessor = to->GetPredecessors()[pred];
+ predecessor->ReplaceSuccessor(to, outer_graph->GetExitBlock());
+ }
+
--pred;
// We need to re-run dominance information, as the exit block now has
- // a new dominator.
+ // a new predecessor and potential new dominator.
+ // TODO(solanes): See if it's worth it to hand-modify the domination chain instead of
+ // rerunning the dominance for the whole graph.
rerun_dominance = true;
if (predecessor->GetLoopInformation() != nullptr) {
- // The exit block and blocks post dominated by the exit block do not belong
- // to any loop. Because we do not compute the post dominators, we need to re-run
- // loop analysis to get the loop information correct.
+ // The loop information might have changed e.g. `predecessor` might not be in a loop
+ // anymore. We only do this if `predecessor` has loop information as it is impossible for
+ // predecessor to end up in a loop if it wasn't in one before.
rerun_loop_analysis = true;
}
} else {
@@ -2863,6 +2973,19 @@
}
predecessor->AddInstruction(new (allocator) HGoto(last->GetDexPc()));
predecessor->RemoveInstruction(last);
+
+ if (saw_try_boundary) {
+ predecessor = to->GetPredecessors()[pred];
+ DCHECK(predecessor->EndsWithTryBoundary());
+ DCHECK_EQ(predecessor->GetNormalSuccessors().size(), 1u);
+ if (predecessor->GetSuccessors()[0]->GetPredecessors().size() > 1) {
+ outer_graph->SplitCriticalEdge(predecessor, to);
+ rerun_dominance = true;
+ if (predecessor->GetLoopInformation() != nullptr) {
+ rerun_loop_analysis = true;
+ }
+ }
+ }
}
}
if (rerun_loop_analysis) {
@@ -3047,6 +3170,7 @@
HSuspendCheck* suspend_check = new (allocator_) HSuspendCheck(header->GetDexPc());
new_header->AddInstruction(suspend_check);
new_body->AddInstruction(new (allocator_) HGoto());
+ DCHECK(loop->GetSuspendCheck() != nullptr);
suspend_check->CopyEnvironmentFromWithLoopPhiAdjustment(
loop->GetSuspendCheck()->GetEnvironment(), header);
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 7a0059f..58ce9ce 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -29,6 +29,7 @@
#include "base/array_ref.h"
#include "base/intrusive_forward_list.h"
#include "base/iteration_range.h"
+#include "base/macros.h"
#include "base/mutex.h"
#include "base/quasi_atomic.h"
#include "base/stl_util.h"
@@ -51,7 +52,7 @@
#include "mirror/method_type.h"
#include "offsets.h"
-namespace art {
+namespace art HIDDEN {
class ArenaStack;
class CodeGenerator;
@@ -406,6 +407,7 @@
has_loops_(false),
has_irreducible_loops_(false),
has_direct_critical_native_call_(false),
+ has_always_throwing_invokes_(false),
dead_reference_safe_(dead_reference_safe),
debuggable_(debuggable),
current_instruction_id_(start_instruction_id),
@@ -485,9 +487,11 @@
// Update the loop and try membership of `block`, which was spawned from `reference`.
// In case `reference` is a back edge, `replace_if_back_edge` notifies whether `block`
// should be the new back edge.
+ // `has_more_specific_try_catch_info` will be set to true when inlining a try catch.
void UpdateLoopAndTryInformationOfNewBlock(HBasicBlock* block,
HBasicBlock* reference,
- bool replace_if_back_edge);
+ bool replace_if_back_edge,
+ bool has_more_specific_try_catch_info = false);
// Need to add a couple of blocks to test if the loop body is entered and
// put deoptimization instructions, etc.
@@ -510,6 +514,11 @@
HBasicBlock* SplitEdge(HBasicBlock* block, HBasicBlock* successor);
void SplitCriticalEdge(HBasicBlock* block, HBasicBlock* successor);
+
+ // Splits the edge between `block` and `successor` and then updates the graph's RPO to keep
+ // consistency without recomputing the whole graph.
+ HBasicBlock* SplitEdgeAndUpdateRPO(HBasicBlock* block, HBasicBlock* successor);
+
void OrderLoopHeaderPredecessors(HBasicBlock* header);
// Transform a loop into a format with a single preheader.
@@ -678,6 +687,13 @@
return cha_single_implementation_list_;
}
+ // In case of OSR we intend to use SuspendChecks as an entry point to the
+ // function; for debuggable graphs we might deoptimize to interpreter from
+ // SuspendChecks. In these cases we should always generate code for them.
+ bool SuspendChecksAreAllowedToNoOp() const {
+ return !IsDebuggable() && !IsCompilingOsr();
+ }
+
void AddCHASingleImplementationDependency(ArtMethod* method) {
cha_single_implementation_list_.insert(method);
}
@@ -704,6 +720,9 @@
bool HasDirectCriticalNativeCall() const { return has_direct_critical_native_call_; }
void SetHasDirectCriticalNativeCall(bool value) { has_direct_critical_native_call_ = value; }
+ bool HasAlwaysThrowingInvokes() const { return has_always_throwing_invokes_; }
+ void SetHasAlwaysThrowingInvokes(bool value) { has_always_throwing_invokes_ = value; }
+
ArtMethod* GetArtMethod() const { return art_method_; }
void SetArtMethod(ArtMethod* method) { art_method_ = method; }
@@ -719,12 +738,12 @@
return ReferenceTypeInfo::Create(handle_cache_.GetObjectClassHandle(), /* is_exact= */ false);
}
- uint32_t GetNumberOfCHAGuards() { return number_of_cha_guards_; }
+ uint32_t GetNumberOfCHAGuards() const { return number_of_cha_guards_; }
void SetNumberOfCHAGuards(uint32_t num) { number_of_cha_guards_ = num; }
void IncrementNumberOfCHAGuards() { number_of_cha_guards_++; }
private:
- void RemoveInstructionsAsUsersFromDeadBlocks(const ArenaBitVector& visited) const;
+ void RemoveDeadBlocksInstructionsAsUsersAndDisconnect(const ArenaBitVector& visited) const;
void RemoveDeadBlocks(const ArenaBitVector& visited);
template <class InstructionType, typename ValueType>
@@ -792,14 +811,11 @@
size_t temporaries_vreg_slots_;
// Flag whether there are bounds checks in the graph. We can skip
- // BCE if it's false. It's only best effort to keep it up to date in
- // the presence of code elimination so there might be false positives.
+ // BCE if it's false.
bool has_bounds_checks_;
// Flag whether there are try/catch blocks in the graph. We will skip
- // try/catch-related passes if it's false. It's only best effort to keep
- // it up to date in the presence of code elimination so there might be
- // false positives.
+ // try/catch-related passes if it's false.
bool has_try_catch_;
// Flag whether there are any HMonitorOperation in the graph. If yes this will mandate
@@ -812,20 +828,19 @@
bool has_simd_;
// Flag whether there are any loops in the graph. We can skip loop
- // optimization if it's false. It's only best effort to keep it up
- // to date in the presence of code elimination so there might be false
- // positives.
+ // optimization if it's false.
bool has_loops_;
- // Flag whether there are any irreducible loops in the graph. It's only
- // best effort to keep it up to date in the presence of code elimination
- // so there might be false positives.
+ // Flag whether there are any irreducible loops in the graph.
bool has_irreducible_loops_;
// Flag whether there are any direct calls to native code registered
// for @CriticalNative methods.
bool has_direct_critical_native_call_;
+ // Flag whether the graph contains invokes that always throw.
+ bool has_always_throwing_invokes_;
+
// Is the code known to be robust against eliminating dead references
// and the effects of early finalization? If false, dead reference variables
// are kept if they might be visible to the garbage collector.
@@ -1291,7 +1306,7 @@
// graph, create a Goto at the end of the former block and will create an edge
// between the blocks. It will not, however, update the reverse post order or
// loop and try/catch information.
- HBasicBlock* SplitBefore(HInstruction* cursor);
+ HBasicBlock* SplitBefore(HInstruction* cursor, bool require_graph_not_in_ssa_form = true);
// Split the block into two blocks just before `cursor`. Returns the newly
// created block. Note that this method just updates raw block information,
@@ -1332,6 +1347,20 @@
// are safely updated.
void DisconnectAndDelete();
+ // Disconnects `this` from all its successors and updates their phis, if the successors have them.
+ // If `visited` is provided, it will use the information to know if a successor is reachable and
+ // skip updating those phis.
+ void DisconnectFromSuccessors(const ArenaBitVector* visited = nullptr);
+
+ // Removes the catch phi uses of the instructions in `this`, and then remove the instruction
+ // itself. If `building_dominator_tree` is true, it will not remove the instruction as user, since
+ // we do it in a previous step. This is a special case for building up the dominator tree: we want
+ // to eliminate uses before inputs but we don't have domination information, so we remove all
+ // connections from input/uses first before removing any instruction.
+ // This method assumes the instructions have been removed from all users with the exception of
+ // catch phis because of missing exceptional edges in the graph.
+ void RemoveCatchPhiUsesAndInstruction(bool building_dominator_tree);
+
void AddInstruction(HInstruction* instruction);
// Insert `instruction` before/after an existing instruction `cursor`.
void InsertInstructionBefore(HInstruction* instruction, HInstruction* cursor);
@@ -1540,10 +1569,10 @@
M(Min, BinaryOperation) \
M(MonitorOperation, Instruction) \
M(Mul, BinaryOperation) \
- M(NativeDebugInfo, Instruction) \
M(Neg, UnaryOperation) \
M(NewArray, Instruction) \
M(NewInstance, Instruction) \
+ M(Nop, Instruction) \
M(Not, UnaryOperation) \
M(NotEqual, Condition) \
M(NullConstant, Instruction) \
@@ -2408,7 +2437,7 @@
!CanThrow() &&
!IsSuspendCheck() &&
!IsControlFlow() &&
- !IsNativeDebugInfo() &&
+ !IsNop() &&
!IsParameterValue() &&
// If we added an explicit barrier then we should keep it.
!IsMemoryBarrier() &&
@@ -2419,9 +2448,12 @@
return IsRemovable() && !HasUses();
}
- // Does this instruction strictly dominate `other_instruction`?
- // Returns false if this instruction and `other_instruction` are the same.
- // Aborts if this instruction and `other_instruction` are both phis.
+ // Does this instruction dominate `other_instruction`?
+ // Aborts if this instruction and `other_instruction` are different phis.
+ bool Dominates(HInstruction* other_instruction) const;
+
+ // Same but with `strictly dominates` i.e. returns false if this instruction and
+ // `other_instruction` are the same.
bool StrictlyDominates(HInstruction* other_instruction) const;
int GetId() const { return id_; }
@@ -2486,7 +2518,9 @@
void SetLocations(LocationSummary* locations) { locations_ = locations; }
void ReplaceWith(HInstruction* instruction);
- void ReplaceUsesDominatedBy(HInstruction* dominator, HInstruction* replacement);
+ void ReplaceUsesDominatedBy(HInstruction* dominator,
+ HInstruction* replacement,
+ bool strictly_dominated = true);
void ReplaceEnvUsesDominatedBy(HInstruction* dominator, HInstruction* replacement);
void ReplaceInput(HInstruction* replacement, size_t index);
@@ -4700,7 +4734,7 @@
void SetAlwaysThrows(bool always_throws) { SetPackedFlag<kFlagAlwaysThrows>(always_throws); }
- bool AlwaysThrows() const override { return GetPackedFlag<kFlagAlwaysThrows>(); }
+ bool AlwaysThrows() const override final { return GetPackedFlag<kFlagAlwaysThrows>(); }
bool CanBeMoved() const override { return IsIntrinsic() && !DoesAnyWrite(); }
@@ -6362,6 +6396,27 @@
const FieldInfo field_info_;
};
+enum class WriteBarrierKind {
+ // Emit the write barrier, with a runtime optimization which checks if the value that it is being
+ // set is null.
+ kEmitWithNullCheck,
+ // Emit the write barrier, without the runtime null check optimization. This could be set because:
+ // A) It is a write barrier for an ArraySet (which does the optimization with the type check, so
+ // it never does the optimization at the write barrier stage)
+ // B) We know that the input can't be null
+ // C) This write barrier is actually several write barriers coalesced into one. Potentially we
+ // could ask if every value is null for a runtime optimization at the cost of compile time / code
+ // size. At the time of writing it was deemed not worth the effort.
+ kEmitNoNullCheck,
+ // Skip emitting the write barrier. This could be set because:
+ // A) The write barrier is not needed (e.g. it is not a reference, or the value is the null
+ // constant)
+ // B) This write barrier was coalesced into another one so there's no need to emit it.
+ kDontEmit,
+ kLast = kDontEmit
+};
+std::ostream& operator<<(std::ostream& os, WriteBarrierKind rhs);
+
class HInstanceFieldSet final : public HExpression<2> {
public:
HInstanceFieldSet(HInstruction* object,
@@ -6386,6 +6441,7 @@
dex_file) {
SetPackedFlag<kFlagValueCanBeNull>(true);
SetPackedFlag<kFlagIsPredicatedSet>(false);
+ SetPackedField<WriteBarrierKindField>(WriteBarrierKind::kEmitWithNullCheck);
SetRawInputAt(0, object);
SetRawInputAt(1, value);
}
@@ -6406,6 +6462,12 @@
void ClearValueCanBeNull() { SetPackedFlag<kFlagValueCanBeNull>(false); }
bool GetIsPredicatedSet() const { return GetPackedFlag<kFlagIsPredicatedSet>(); }
void SetIsPredicatedSet(bool value = true) { SetPackedFlag<kFlagIsPredicatedSet>(value); }
+ WriteBarrierKind GetWriteBarrierKind() { return GetPackedField<WriteBarrierKindField>(); }
+ void SetWriteBarrierKind(WriteBarrierKind kind) {
+ DCHECK(kind != WriteBarrierKind::kEmitWithNullCheck)
+ << "We shouldn't go back to the original value.";
+ SetPackedField<WriteBarrierKindField>(kind);
+ }
DECLARE_INSTRUCTION(InstanceFieldSet);
@@ -6415,11 +6477,17 @@
private:
static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits;
static constexpr size_t kFlagIsPredicatedSet = kFlagValueCanBeNull + 1;
- static constexpr size_t kNumberOfInstanceFieldSetPackedBits = kFlagIsPredicatedSet + 1;
+ static constexpr size_t kWriteBarrierKind = kFlagIsPredicatedSet + 1;
+ static constexpr size_t kWriteBarrierKindSize =
+ MinimumBitsToStore(static_cast<size_t>(WriteBarrierKind::kLast));
+ static constexpr size_t kNumberOfInstanceFieldSetPackedBits =
+ kWriteBarrierKind + kWriteBarrierKindSize;
static_assert(kNumberOfInstanceFieldSetPackedBits <= kMaxNumberOfPackedBits,
"Too many packed fields.");
const FieldInfo field_info_;
+ using WriteBarrierKindField =
+ BitField<WriteBarrierKind, kWriteBarrierKind, kWriteBarrierKindSize>;
};
class HArrayGet final : public HExpression<2> {
@@ -6540,6 +6608,8 @@
SetPackedFlag<kFlagNeedsTypeCheck>(value->GetType() == DataType::Type::kReference);
SetPackedFlag<kFlagValueCanBeNull>(true);
SetPackedFlag<kFlagStaticTypeOfArrayIsObjectArray>(false);
+ // ArraySets never do the null check optimization at the write barrier stage.
+ SetPackedField<WriteBarrierKindField>(WriteBarrierKind::kEmitNoNullCheck);
SetRawInputAt(0, array);
SetRawInputAt(1, index);
SetRawInputAt(2, value);
@@ -6560,8 +6630,10 @@
return false;
}
- void ClearNeedsTypeCheck() {
+ void ClearTypeCheck() {
SetPackedFlag<kFlagNeedsTypeCheck>(false);
+ // Clear the `CanTriggerGC` flag too as we can only trigger a GC when doing a type check.
+ SetSideEffects(GetSideEffects().Exclusion(SideEffects::CanTriggerGC()));
}
void ClearValueCanBeNull() {
@@ -6610,6 +6682,16 @@
: SideEffects::None();
}
+ WriteBarrierKind GetWriteBarrierKind() { return GetPackedField<WriteBarrierKindField>(); }
+
+ void SetWriteBarrierKind(WriteBarrierKind kind) {
+ DCHECK(kind != WriteBarrierKind::kEmitNoNullCheck)
+ << "We shouldn't go back to the original value.";
+ DCHECK(kind != WriteBarrierKind::kEmitWithNullCheck)
+ << "We never do the null check optimization for ArraySets.";
+ SetPackedField<WriteBarrierKindField>(kind);
+ }
+
DECLARE_INSTRUCTION(ArraySet);
protected:
@@ -6625,11 +6707,16 @@
// Cached information for the reference_type_info_ so that codegen
// does not need to inspect the static type.
static constexpr size_t kFlagStaticTypeOfArrayIsObjectArray = kFlagValueCanBeNull + 1;
- static constexpr size_t kNumberOfArraySetPackedBits =
- kFlagStaticTypeOfArrayIsObjectArray + 1;
+ static constexpr size_t kWriteBarrierKind = kFlagStaticTypeOfArrayIsObjectArray + 1;
+ static constexpr size_t kWriteBarrierKindSize =
+ MinimumBitsToStore(static_cast<size_t>(WriteBarrierKind::kLast));
+ static constexpr size_t kNumberOfArraySetPackedBits = kWriteBarrierKind + kWriteBarrierKindSize;
static_assert(kNumberOfArraySetPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
using ExpectedComponentTypeField =
BitField<DataType::Type, kFieldExpectedComponentType, kFieldExpectedComponentTypeSize>;
+
+ using WriteBarrierKindField =
+ BitField<WriteBarrierKind, kWriteBarrierKind, kWriteBarrierKindSize>;
};
class HArrayLength final : public HExpression<1> {
@@ -6714,9 +6801,10 @@
class HSuspendCheck final : public HExpression<0> {
public:
- explicit HSuspendCheck(uint32_t dex_pc = kNoDexPc)
+ explicit HSuspendCheck(uint32_t dex_pc = kNoDexPc, bool is_no_op = false)
: HExpression(kSuspendCheck, SideEffects::CanTriggerGC(), dex_pc),
slow_path_(nullptr) {
+ SetPackedFlag<kFlagIsNoOp>(is_no_op);
}
bool IsClonable() const override { return true; }
@@ -6725,6 +6813,10 @@
return true;
}
+ void SetIsNoOp(bool is_no_op) { SetPackedFlag<kFlagIsNoOp>(is_no_op); }
+ bool IsNoOp() const { return GetPackedFlag<kFlagIsNoOp>(); }
+
+
void SetSlowPath(SlowPathCode* slow_path) { slow_path_ = slow_path; }
SlowPathCode* GetSlowPath() const { return slow_path_; }
@@ -6733,28 +6825,42 @@
protected:
DEFAULT_COPY_CONSTRUCTOR(SuspendCheck);
+ // True if the HSuspendCheck should not emit any code during codegen. It is
+ // not possible to simply remove this instruction to disable codegen, as
+ // other optimizations (e.g: CHAGuardVisitor::HoistGuard) depend on
+ // HSuspendCheck being present in every loop.
+ static constexpr size_t kFlagIsNoOp = kNumberOfGenericPackedBits;
+ static constexpr size_t kNumberOfSuspendCheckPackedBits = kFlagIsNoOp + 1;
+ static_assert(kNumberOfSuspendCheckPackedBits <= HInstruction::kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+
private:
// Only used for code generation, in order to share the same slow path between back edges
// of a same loop.
SlowPathCode* slow_path_;
};
-// Pseudo-instruction which provides the native debugger with mapping information.
-// It ensures that we can generate line number and local variables at this point.
-class HNativeDebugInfo : public HExpression<0> {
+// Pseudo-instruction which doesn't generate any code.
+// If `emit_environment` is true, it can be used to generate an environment. It is used, for
+// example, to provide the native debugger with mapping information. It ensures that we can generate
+// line number and local variables at this point.
+class HNop : public HExpression<0> {
public:
- explicit HNativeDebugInfo(uint32_t dex_pc)
- : HExpression<0>(kNativeDebugInfo, SideEffects::None(), dex_pc) {
+ explicit HNop(uint32_t dex_pc, bool needs_environment)
+ : HExpression<0>(kNop, SideEffects::None(), dex_pc), needs_environment_(needs_environment) {
}
bool NeedsEnvironment() const override {
- return true;
+ return needs_environment_;
}
- DECLARE_INSTRUCTION(NativeDebugInfo);
+ DECLARE_INSTRUCTION(Nop);
protected:
- DEFAULT_COPY_CONSTRUCTOR(NativeDebugInfo);
+ DEFAULT_COPY_CONSTRUCTOR(Nop);
+
+ private:
+ bool needs_environment_;
};
/**
@@ -7222,6 +7328,10 @@
return SideEffects::CanTriggerGC();
}
+ bool CanThrow() const override { return true; }
+
+ bool NeedsEnvironment() const override { return true; }
+
DECLARE_INSTRUCTION(LoadMethodHandle);
protected:
@@ -7266,6 +7376,10 @@
return SideEffects::CanTriggerGC();
}
+ bool CanThrow() const override { return true; }
+
+ bool NeedsEnvironment() const override { return true; }
+
DECLARE_INSTRUCTION(LoadMethodType);
protected:
@@ -7400,6 +7514,7 @@
declaring_class_def_index,
dex_file) {
SetPackedFlag<kFlagValueCanBeNull>(true);
+ SetPackedField<WriteBarrierKindField>(WriteBarrierKind::kEmitWithNullCheck);
SetRawInputAt(0, cls);
SetRawInputAt(1, value);
}
@@ -7415,6 +7530,13 @@
bool GetValueCanBeNull() const { return GetPackedFlag<kFlagValueCanBeNull>(); }
void ClearValueCanBeNull() { SetPackedFlag<kFlagValueCanBeNull>(false); }
+ WriteBarrierKind GetWriteBarrierKind() { return GetPackedField<WriteBarrierKindField>(); }
+ void SetWriteBarrierKind(WriteBarrierKind kind) {
+ DCHECK(kind != WriteBarrierKind::kEmitWithNullCheck)
+ << "We shouldn't go back to the original value.";
+ SetPackedField<WriteBarrierKindField>(kind);
+ }
+
DECLARE_INSTRUCTION(StaticFieldSet);
protected:
@@ -7422,25 +7544,34 @@
private:
static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits;
- static constexpr size_t kNumberOfStaticFieldSetPackedBits = kFlagValueCanBeNull + 1;
+ static constexpr size_t kWriteBarrierKind = kFlagValueCanBeNull + 1;
+ static constexpr size_t kWriteBarrierKindSize =
+ MinimumBitsToStore(static_cast<size_t>(WriteBarrierKind::kLast));
+ static constexpr size_t kNumberOfStaticFieldSetPackedBits =
+ kWriteBarrierKind + kWriteBarrierKindSize;
static_assert(kNumberOfStaticFieldSetPackedBits <= kMaxNumberOfPackedBits,
"Too many packed fields.");
const FieldInfo field_info_;
+ using WriteBarrierKindField =
+ BitField<WriteBarrierKind, kWriteBarrierKind, kWriteBarrierKindSize>;
};
class HStringBuilderAppend final : public HVariableInputSizeInstruction {
public:
HStringBuilderAppend(HIntConstant* format,
uint32_t number_of_arguments,
+ bool has_fp_args,
ArenaAllocator* allocator,
uint32_t dex_pc)
: HVariableInputSizeInstruction(
kStringBuilderAppend,
DataType::Type::kReference,
- // The runtime call may read memory from inputs. It never writes outside
- // of the newly allocated result object (or newly allocated helper objects).
- SideEffects::AllReads().Union(SideEffects::CanTriggerGC()),
+ SideEffects::CanTriggerGC().Union(
+ // The runtime call may read memory from inputs. It never writes outside
+ // of the newly allocated result object or newly allocated helper objects,
+ // except for float/double arguments where we reuse thread-local helper objects.
+ has_fp_args ? SideEffects::AllWritesAndReads() : SideEffects::AllReads()),
dex_pc,
allocator,
number_of_arguments + /* format */ 1u,
@@ -8393,7 +8524,7 @@
#include "nodes_x86.h"
#endif
-namespace art {
+namespace art HIDDEN {
class OptimizingCompilerStats;
diff --git a/compiler/optimizing/nodes_shared.cc b/compiler/optimizing/nodes_shared.cc
index eca97d7..b3a7ad9 100644
--- a/compiler/optimizing/nodes_shared.cc
+++ b/compiler/optimizing/nodes_shared.cc
@@ -23,7 +23,7 @@
#include "instruction_simplifier_shared.h"
-namespace art {
+namespace art HIDDEN {
using helpers::CanFitInShifterOperand;
diff --git a/compiler/optimizing/nodes_shared.h b/compiler/optimizing/nodes_shared.h
index 7dcac17..27e6103 100644
--- a/compiler/optimizing/nodes_shared.h
+++ b/compiler/optimizing/nodes_shared.h
@@ -22,7 +22,7 @@
// (defining `HInstruction` and co).
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
class HMultiplyAccumulate final : public HExpression<3> {
public:
diff --git a/compiler/optimizing/nodes_test.cc b/compiler/optimizing/nodes_test.cc
index 34f0e9b..29210fe 100644
--- a/compiler/optimizing/nodes_test.cc
+++ b/compiler/optimizing/nodes_test.cc
@@ -17,11 +17,12 @@
#include "nodes.h"
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "optimizing_unit_test.h"
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
class NodeTest : public OptimizingUnitTest {};
diff --git a/compiler/optimizing/nodes_vector.h b/compiler/optimizing/nodes_vector.h
index a2cd86d..73f6c40 100644
--- a/compiler/optimizing/nodes_vector.h
+++ b/compiler/optimizing/nodes_vector.h
@@ -21,7 +21,7 @@
// is included in the header file nodes.h itself. However it gives editing tools better context.
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
// Memory alignment, represented as an offset relative to a base, where 0 <= offset < base,
// and base is a power of two. For example, the value Alignment(16, 0) means memory is
diff --git a/compiler/optimizing/nodes_vector_test.cc b/compiler/optimizing/nodes_vector_test.cc
index b0a665d..e0a48db 100644
--- a/compiler/optimizing/nodes_vector_test.cc
+++ b/compiler/optimizing/nodes_vector_test.cc
@@ -15,10 +15,11 @@
*/
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
-namespace art {
+namespace art HIDDEN {
/**
* Fixture class for testing vector nodes.
diff --git a/compiler/optimizing/nodes_x86.h b/compiler/optimizing/nodes_x86.h
index 8e8fbc1..e246390 100644
--- a/compiler/optimizing/nodes_x86.h
+++ b/compiler/optimizing/nodes_x86.h
@@ -17,7 +17,7 @@
#ifndef ART_COMPILER_OPTIMIZING_NODES_X86_H_
#define ART_COMPILER_OPTIMIZING_NODES_X86_H_
-namespace art {
+namespace art HIDDEN {
// Compute the address of the method for X86 Constant area support.
class HX86ComputeBaseMethodAddress final : public HExpression<0> {
diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc
index 2cac38b..73a4751 100644
--- a/compiler/optimizing/optimization.cc
+++ b/compiler/optimizing/optimization.cc
@@ -55,10 +55,11 @@
#include "select_generator.h"
#include "sharpening.h"
#include "side_effects_analysis.h"
+#include "write_barrier_elimination.h"
// Decide between default or alternative pass name.
-namespace art {
+namespace art HIDDEN {
const char* OptimizationPassName(OptimizationPass pass) {
switch (pass) {
@@ -95,6 +96,8 @@
return ConstructorFenceRedundancyElimination::kCFREPassName;
case OptimizationPass::kScheduling:
return HInstructionScheduling::kInstructionSchedulingPassName;
+ case OptimizationPass::kWriteBarrierElimination:
+ return WriteBarrierElimination::kWBEPassName;
#ifdef ART_ENABLE_CODEGEN_arm
case OptimizationPass::kInstructionSimplifierArm:
return arm::InstructionSimplifierArm::kInstructionSimplifierArmPassName;
@@ -221,7 +224,7 @@
// Regular passes.
//
case OptimizationPass::kConstantFolding:
- opt = new (allocator) HConstantFolding(graph, pass_name);
+ opt = new (allocator) HConstantFolding(graph, stats, pass_name);
break;
case OptimizationPass::kDeadCodeElimination:
opt = new (allocator) HDeadCodeElimination(graph, stats, pass_name);
@@ -239,6 +242,7 @@
/* total_number_of_instructions= */ 0,
/* parent= */ nullptr,
/* depth= */ 0,
+ /* try_catch_inlining_allowed= */ true,
pass_name);
break;
}
@@ -267,6 +271,9 @@
case OptimizationPass::kLoadStoreElimination:
opt = new (allocator) LoadStoreElimination(graph, stats, pass_name);
break;
+ case OptimizationPass::kWriteBarrierElimination:
+ opt = new (allocator) WriteBarrierElimination(graph, stats, pass_name);
+ break;
case OptimizationPass::kScheduling:
opt = new (allocator) HInstructionScheduling(
graph, codegen->GetCompilerOptions().GetInstructionSet(), codegen, pass_name);
diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h
index 2113df0..c3ba175 100644
--- a/compiler/optimizing/optimization.h
+++ b/compiler/optimizing/optimization.h
@@ -18,10 +18,11 @@
#define ART_COMPILER_OPTIMIZING_OPTIMIZATION_H_
#include "base/arena_object.h"
+#include "base/macros.h"
#include "nodes.h"
#include "optimizing_compiler_stats.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
class DexCompilationUnit;
@@ -83,6 +84,7 @@
kScheduling,
kSelectGenerator,
kSideEffectsAnalysis,
+ kWriteBarrierElimination,
#ifdef ART_ENABLE_CODEGEN_arm
kInstructionSimplifierArm,
kCriticalNativeAbiFixupArm,
diff --git a/compiler/optimizing/optimizing_cfi_test.cc b/compiler/optimizing/optimizing_cfi_test.cc
index bad540e..f12e748 100644
--- a/compiler/optimizing/optimizing_cfi_test.cc
+++ b/compiler/optimizing/optimizing_cfi_test.cc
@@ -18,6 +18,7 @@
#include <vector>
#include "arch/instruction_set.h"
+#include "base/macros.h"
#include "base/runtime_debug.h"
#include "cfi_test.h"
#include "driver/compiler_options.h"
@@ -32,7 +33,7 @@
namespace vixl32 = vixl::aarch32;
-namespace art {
+namespace art HIDDEN {
// Run the tests only on host.
#ifndef ART_TARGET_ANDROID
@@ -167,9 +168,20 @@
// barrier configuration, and as such is removed from the set of
// callee-save registers in the ARM64 code generator of the Optimizing
// compiler.
-#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
-TEST_ISA(kArm64)
-#endif
+//
+// We can't use compile-time macros for read-barrier as the introduction
+// of userfaultfd-GC has made it a runtime choice.
+TEST_F(OptimizingCFITest, kArm64) {
+ if (kUseBakerReadBarrier && gUseReadBarrier) {
+ std::vector<uint8_t> expected_asm(
+ expected_asm_kArm64,
+ expected_asm_kArm64 + arraysize(expected_asm_kArm64));
+ std::vector<uint8_t> expected_cfi(
+ expected_cfi_kArm64,
+ expected_cfi_kArm64 + arraysize(expected_cfi_kArm64));
+ TestImpl(InstructionSet::kArm64, "kArm64", expected_asm, expected_cfi);
+ }
+}
#endif
#ifdef ART_ENABLE_CODEGEN_x86
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 6eb3d01..dbf247c 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -33,12 +33,11 @@
#include "base/timing_logger.h"
#include "builder.h"
#include "code_generator.h"
-#include "compiled_method.h"
#include "compiler.h"
#include "debug/elf_debug_writer.h"
#include "debug/method_debug_info.h"
#include "dex/dex_file_types.h"
-#include "driver/compiled_method_storage.h"
+#include "driver/compiled_code_storage.h"
#include "driver/compiler_options.h"
#include "driver/dex_compilation_unit.h"
#include "graph_checker.h"
@@ -52,6 +51,7 @@
#include "linker/linker_patch.h"
#include "nodes.h"
#include "oat_quick_method_header.h"
+#include "optimizing/write_barrier_elimination.h"
#include "prepare_for_register_allocation.h"
#include "reference_type_propagation.h"
#include "register_allocator_linear_scan.h"
@@ -62,7 +62,7 @@
#include "stack_map_stream.h"
#include "utils/assembler.h"
-namespace art {
+namespace art HIDDEN {
static constexpr size_t kArenaAllocatorMemoryReportThreshold = 8 * MB;
@@ -269,7 +269,7 @@
class OptimizingCompiler final : public Compiler {
public:
explicit OptimizingCompiler(const CompilerOptions& compiler_options,
- CompiledMethodStorage* storage);
+ CompiledCodeStorage* storage);
~OptimizingCompiler() override;
bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file) const override;
@@ -359,11 +359,11 @@
const DexCompilationUnit& dex_compilation_unit,
PassObserver* pass_observer) const;
- private:
// Create a 'CompiledMethod' for an optimized graph.
CompiledMethod* Emit(ArenaAllocator* allocator,
CodeVectorAllocator* code_allocator,
CodeGenerator* codegen,
+ bool is_intrinsic,
const dex::CodeItem* item) const;
// Try compiling a method and return the code generator used for
@@ -413,7 +413,7 @@
static const int kMaximumCompilationTimeBeforeWarning = 100; /* ms */
OptimizingCompiler::OptimizingCompiler(const CompilerOptions& compiler_options,
- CompiledMethodStorage* storage)
+ CompiledCodeStorage* storage)
: Compiler(compiler_options, storage, kMaximumCompilationTimeBeforeWarning) {
// Enable C1visualizer output.
const std::string& cfg_file_name = compiler_options.GetDumpCfgFileName();
@@ -671,17 +671,25 @@
"constant_folding$after_bce"),
OptDef(OptimizationPass::kAggressiveInstructionSimplifier,
"instruction_simplifier$after_bce"),
+ OptDef(OptimizationPass::kDeadCodeElimination,
+ "dead_code_elimination$after_bce"),
// Other high-level optimizations.
OptDef(OptimizationPass::kLoadStoreElimination),
- OptDef(OptimizationPass::kCHAGuardOptimization),
OptDef(OptimizationPass::kDeadCodeElimination,
- "dead_code_elimination$final"),
+ "dead_code_elimination$after_lse",
+ OptimizationPass::kLoadStoreElimination),
+ OptDef(OptimizationPass::kCHAGuardOptimization),
OptDef(OptimizationPass::kCodeSinking),
// The codegen has a few assumptions that only the instruction simplifier
// can satisfy. For example, the code generator does not expect to see a
// HTypeConversion from a type to the same type.
OptDef(OptimizationPass::kAggressiveInstructionSimplifier,
"instruction_simplifier$before_codegen"),
+ // Simplification may result in dead code that should be removed prior to
+ // code generation.
+ OptDef(OptimizationPass::kDeadCodeElimination,
+ "dead_code_elimination$before_codegen",
+ OptimizationPass::kAggressiveInstructionSimplifier),
// Eliminate constructor fences after code sinking to avoid
// complicated sinking logic to split a fence with many inputs.
OptDef(OptimizationPass::kConstructorFenceRedundancyElimination)
@@ -711,18 +719,19 @@
CompiledMethod* OptimizingCompiler::Emit(ArenaAllocator* allocator,
CodeVectorAllocator* code_allocator,
CodeGenerator* codegen,
+ bool is_intrinsic,
const dex::CodeItem* code_item_for_osr_check) const {
ArenaVector<linker::LinkerPatch> linker_patches = EmitAndSortLinkerPatches(codegen);
ScopedArenaVector<uint8_t> stack_map = codegen->BuildStackMaps(code_item_for_osr_check);
- CompiledMethodStorage* storage = GetCompiledMethodStorage();
- CompiledMethod* compiled_method = CompiledMethod::SwapAllocCompiledMethod(
- storage,
+ CompiledCodeStorage* storage = GetCompiledCodeStorage();
+ CompiledMethod* compiled_method = storage->CreateCompiledMethod(
codegen->GetInstructionSet(),
code_allocator->GetMemory(),
ArrayRef<const uint8_t>(stack_map),
ArrayRef<const uint8_t>(*codegen->GetAssembler()->cfi().data()),
- ArrayRef<const linker::LinkerPatch>(linker_patches));
+ ArrayRef<const linker::LinkerPatch>(linker_patches),
+ is_intrinsic);
for (const linker::LinkerPatch& patch : linker_patches) {
if (codegen->NeedsThunkCode(patch) && storage->GetThunkCode(patch).empty()) {
@@ -891,6 +900,8 @@
RunBaselineOptimizations(graph, codegen.get(), dex_compilation_unit, &pass_observer);
} else {
RunOptimizations(graph, codegen.get(), dex_compilation_unit, &pass_observer);
+ PassScope scope(WriteBarrierElimination::kWBEPassName, &pass_observer);
+ WriteBarrierElimination(graph, compilation_stats_.get()).Run();
}
RegisterAllocator::Strategy regalloc_strategy =
@@ -984,6 +995,10 @@
optimizations);
RunArchOptimizations(graph, codegen.get(), dex_compilation_unit, &pass_observer);
+ {
+ PassScope scope(WriteBarrierElimination::kWBEPassName, &pass_observer);
+ WriteBarrierElimination(graph, compilation_stats_.get()).Run();
+ }
AllocateRegisters(graph,
codegen.get(),
@@ -1079,10 +1094,8 @@
compiled_method = Emit(&allocator,
&code_allocator,
codegen.get(),
+ compiled_intrinsic,
compiled_intrinsic ? nullptr : code_item);
- if (compiled_intrinsic) {
- compiled_method->MarkAsIntrinsic();
- }
if (kArenaAllocatorCountAllocations) {
codegen.reset(); // Release codegen's ScopedArenaAllocator for memory accounting.
@@ -1115,17 +1128,18 @@
static ScopedArenaVector<uint8_t> CreateJniStackMap(ScopedArenaAllocator* allocator,
const JniCompiledMethod& jni_compiled_method,
- size_t code_size) {
+ size_t code_size,
+ bool debuggable) {
// StackMapStream is quite large, so allocate it using the ScopedArenaAllocator
// to stay clear of the frame size limit.
std::unique_ptr<StackMapStream> stack_map_stream(
new (allocator) StackMapStream(allocator, jni_compiled_method.GetInstructionSet()));
- stack_map_stream->BeginMethod(
- jni_compiled_method.GetFrameSize(),
- jni_compiled_method.GetCoreSpillMask(),
- jni_compiled_method.GetFpSpillMask(),
- /* num_dex_registers= */ 0,
- /* baseline= */ false);
+ stack_map_stream->BeginMethod(jni_compiled_method.GetFrameSize(),
+ jni_compiled_method.GetCoreSpillMask(),
+ jni_compiled_method.GetFpSpillMask(),
+ /* num_dex_registers= */ 0,
+ /* baseline= */ false,
+ debuggable);
stack_map_stream->EndMethod(code_size);
return stack_map_stream->Encode();
}
@@ -1172,12 +1186,11 @@
method,
&handles));
if (codegen != nullptr) {
- CompiledMethod* compiled_method = Emit(&allocator,
- &code_allocator,
- codegen.get(),
- /* item= */ nullptr);
- compiled_method->MarkAsIntrinsic();
- return compiled_method;
+ return Emit(&allocator,
+ &code_allocator,
+ codegen.get(),
+ /*is_intrinsic=*/ true,
+ /*item=*/ nullptr);
}
}
}
@@ -1187,19 +1200,22 @@
MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kCompiledNativeStub);
ScopedArenaAllocator stack_map_allocator(&arena_stack); // Will hold the stack map.
- ScopedArenaVector<uint8_t> stack_map = CreateJniStackMap(
- &stack_map_allocator, jni_compiled_method, jni_compiled_method.GetCode().size());
- return CompiledMethod::SwapAllocCompiledMethod(
- GetCompiledMethodStorage(),
+ ScopedArenaVector<uint8_t> stack_map =
+ CreateJniStackMap(&stack_map_allocator,
+ jni_compiled_method,
+ jni_compiled_method.GetCode().size(),
+ compiler_options.GetDebuggable() && compiler_options.IsJitCompiler());
+ return GetCompiledCodeStorage()->CreateCompiledMethod(
jni_compiled_method.GetInstructionSet(),
jni_compiled_method.GetCode(),
ArrayRef<const uint8_t>(stack_map),
jni_compiled_method.GetCfi(),
- /* patches= */ ArrayRef<const linker::LinkerPatch>());
+ /*patches=*/ ArrayRef<const linker::LinkerPatch>(),
+ /*is_intrinsic=*/ false);
}
Compiler* CreateOptimizingCompiler(const CompilerOptions& compiler_options,
- CompiledMethodStorage* storage) {
+ CompiledCodeStorage* storage) {
return new OptimizingCompiler(compiler_options, storage);
}
@@ -1233,6 +1249,14 @@
ArenaAllocator allocator(runtime->GetJitArenaPool());
if (UNLIKELY(method->IsNative())) {
+ // Use GenericJniTrampoline for critical native methods in debuggable runtimes. We don't
+ // support calling method entry / exit hooks for critical native methods yet.
+ // TODO(mythria): Add support for calling method entry / exit hooks in JITed stubs for critical
+ // native methods too.
+ if (runtime->IsJavaDebuggable() && method->IsCriticalNative()) {
+ DCHECK(compiler_options.IsJitCompiler());
+ return false;
+ }
JniCompiledMethod jni_compiled_method = ArtQuickJniCompileMethod(
compiler_options, access_flags, method_idx, *dex_file, &allocator);
std::vector<Handle<mirror::Object>> roots;
@@ -1241,8 +1265,11 @@
ArenaStack arena_stack(runtime->GetJitArenaPool());
// StackMapStream is large and it does not fit into this frame, so we need helper method.
ScopedArenaAllocator stack_map_allocator(&arena_stack); // Will hold the stack map.
- ScopedArenaVector<uint8_t> stack_map = CreateJniStackMap(
- &stack_map_allocator, jni_compiled_method, jni_compiled_method.GetCode().size());
+ ScopedArenaVector<uint8_t> stack_map =
+ CreateJniStackMap(&stack_map_allocator,
+ jni_compiled_method,
+ jni_compiled_method.GetCode().size(),
+ compiler_options.GetDebuggable() && compiler_options.IsJitCompiler());
ArrayRef<const uint8_t> reserved_code;
ArrayRef<const uint8_t> reserved_data;
diff --git a/compiler/optimizing/optimizing_compiler.h b/compiler/optimizing/optimizing_compiler.h
index cd6d684..737ffd0 100644
--- a/compiler/optimizing/optimizing_compiler.h
+++ b/compiler/optimizing/optimizing_compiler.h
@@ -18,18 +18,19 @@
#define ART_COMPILER_OPTIMIZING_OPTIMIZING_COMPILER_H_
#include "base/globals.h"
+#include "base/macros.h"
#include "base/mutex.h"
-namespace art {
+namespace art HIDDEN {
class ArtMethod;
+class CompiledCodeStorage;
class Compiler;
-class CompiledMethodStorage;
class CompilerOptions;
class DexFile;
Compiler* CreateOptimizingCompiler(const CompilerOptions& compiler_options,
- CompiledMethodStorage* storage);
+ CompiledCodeStorage* storage);
bool EncodeArtMethodInInlineInfo(ArtMethod* method);
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index d458e42..698a147 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -26,8 +26,9 @@
#include "base/atomic.h"
#include "base/globals.h"
+#include "base/macros.h"
-namespace art {
+namespace art HIDDEN {
enum class MethodCompilationStat {
kAttemptBytecodeCompilation = 0,
@@ -46,6 +47,7 @@
kUnresolvedFieldNotAFastAccess,
kRemovedCheckedCast,
kRemovedDeadInstruction,
+ kRemovedTry,
kRemovedNullCheck,
kNotCompiledSkipped,
kNotCompiledInvalidBytecode,
@@ -59,6 +61,7 @@
kNotCompiledSpaceFilter,
kNotCompiledUnhandledInstruction,
kNotCompiledUnsupportedIsa,
+ kNotCompiledInliningIrreducibleLoop,
kNotCompiledIrreducibleLoopAndStringInit,
kNotCompiledPhiEquivalentInOsr,
kInlinedMonomorphicCall,
@@ -73,6 +76,7 @@
kLoopVectorizedIdiom,
kSelectGenerated,
kRemovedInstanceOf,
+ kPropagatedIfValue,
kInlinedInvokeVirtualOrInterface,
kInlinedLastInvokeVirtualOrInterface,
kImplicitNullCheckGenerated,
@@ -88,16 +92,19 @@
kNotInlinedEnvironmentBudget,
kNotInlinedInstructionBudget,
kNotInlinedLoopWithoutExit,
- kNotInlinedIrreducibleLoop,
+ kNotInlinedIrreducibleLoopCallee,
+ kNotInlinedIrreducibleLoopCaller,
kNotInlinedAlwaysThrows,
kNotInlinedInfiniteLoop,
- kNotInlinedTryCatchCaller,
kNotInlinedTryCatchCallee,
+ kNotInlinedTryCatchDisabled,
kNotInlinedRegisterAllocator,
kNotInlinedCannotBuild,
+ kNotInlinedNeverInlineAnnotation,
kNotInlinedNotCompilable,
kNotInlinedNotVerified,
kNotInlinedCodeItem,
+ kNotInlinedEndsWithThrow,
kNotInlinedWont,
kNotInlinedRecursiveBudget,
kNotInlinedPolymorphicRecursiveBudget,
@@ -111,6 +118,8 @@
kConstructorFenceRemovedLSE,
kConstructorFenceRemovedPFRA,
kConstructorFenceRemovedCFRE,
+ kPossibleWriteBarrier,
+ kRemovedWriteBarrier,
kBitstringTypeCheck,
kJitOutOfMemoryForCommit,
kFullLSEAllocationRemoved,
diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h
index e836880..e62ccf0 100644
--- a/compiler/optimizing/optimizing_unit_test.h
+++ b/compiler/optimizing/optimizing_unit_test.h
@@ -25,6 +25,7 @@
#include <vector>
#include <variant>
+#include "base/macros.h"
#include "base/indenter.h"
#include "base/malloc_arena_pool.h"
#include "base/scoped_arena_allocator.h"
@@ -46,7 +47,7 @@
#include "ssa_builder.h"
#include "ssa_liveness_analysis.h"
-namespace art {
+namespace art HIDDEN {
#define NUM_INSTRUCTIONS(...) \
(sizeof((uint16_t[]) {__VA_ARGS__}) /sizeof(uint16_t))
@@ -278,7 +279,7 @@
/* class_linker= */ nullptr,
graph->GetDexFile(),
code_item,
- /* class_def_index= */ DexFile::kDexNoIndex16,
+ /* class_def_idx= */ DexFile::kDexNoIndex16,
/* method_idx= */ dex::kDexNoIndex,
/* access_flags= */ 0u,
/* verified_method= */ nullptr,
@@ -559,7 +560,7 @@
class OptimizingUnitTest : public CommonArtTest, public OptimizingUnitTestHelper {};
// Naive string diff data type.
-typedef std::list<std::pair<std::string, std::string>> diff_t;
+using diff_t = std::list<std::pair<std::string, std::string>>;
// An alias for the empty string used to make it clear that a line is
// removed in a diff.
diff --git a/compiler/optimizing/parallel_move_resolver.cc b/compiler/optimizing/parallel_move_resolver.cc
index 2036b4a..9fc4cc8 100644
--- a/compiler/optimizing/parallel_move_resolver.cc
+++ b/compiler/optimizing/parallel_move_resolver.cc
@@ -19,7 +19,7 @@
#include "base/stl_util.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
void ParallelMoveResolver::BuildInitialMoveList(HParallelMove* parallel_move) {
// Perform a linear sweep of the moves to add them to the initial list of
diff --git a/compiler/optimizing/parallel_move_resolver.h b/compiler/optimizing/parallel_move_resolver.h
index 5fadcab..17d5122 100644
--- a/compiler/optimizing/parallel_move_resolver.h
+++ b/compiler/optimizing/parallel_move_resolver.h
@@ -18,11 +18,12 @@
#define ART_COMPILER_OPTIMIZING_PARALLEL_MOVE_RESOLVER_H_
#include "base/arena_containers.h"
+#include "base/macros.h"
#include "base/value_object.h"
#include "data_type.h"
#include "locations.h"
-namespace art {
+namespace art HIDDEN {
class HParallelMove;
class MoveOperands;
diff --git a/compiler/optimizing/parallel_move_test.cc b/compiler/optimizing/parallel_move_test.cc
index a8ab6cd..a1c05e9 100644
--- a/compiler/optimizing/parallel_move_test.cc
+++ b/compiler/optimizing/parallel_move_test.cc
@@ -15,6 +15,7 @@
*/
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "base/malloc_arena_pool.h"
#include "nodes.h"
#include "parallel_move_resolver.h"
@@ -22,7 +23,7 @@
#include "gtest/gtest-typed-test.h"
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
constexpr int kScratchRegisterStartIndexForTest = 100;
diff --git a/compiler/optimizing/pc_relative_fixups_x86.cc b/compiler/optimizing/pc_relative_fixups_x86.cc
index 17f37f0..b7e773f 100644
--- a/compiler/optimizing/pc_relative_fixups_x86.cc
+++ b/compiler/optimizing/pc_relative_fixups_x86.cc
@@ -18,7 +18,7 @@
#include "code_generator_x86.h"
#include "intrinsics_x86.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
/**
diff --git a/compiler/optimizing/pc_relative_fixups_x86.h b/compiler/optimizing/pc_relative_fixups_x86.h
index 3b470a6..45578d8 100644
--- a/compiler/optimizing/pc_relative_fixups_x86.h
+++ b/compiler/optimizing/pc_relative_fixups_x86.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_OPTIMIZING_PC_RELATIVE_FIXUPS_X86_H_
#define ART_COMPILER_OPTIMIZING_PC_RELATIVE_FIXUPS_X86_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc
index c2f3d0e..707783e 100644
--- a/compiler/optimizing/prepare_for_register_allocation.cc
+++ b/compiler/optimizing/prepare_for_register_allocation.cc
@@ -22,7 +22,7 @@
#include "optimizing_compiler_stats.h"
#include "well_known_classes.h"
-namespace art {
+namespace art HIDDEN {
void PrepareForRegisterAllocation::Run() {
// Order does not matter.
@@ -83,7 +83,7 @@
if (check->IsStringCharAt()) {
// Add a fake environment for String.charAt() inline info as we want the exception
// to appear as being thrown from there. Skip if we're compiling String.charAt() itself.
- ArtMethod* char_at_method = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt);
+ ArtMethod* char_at_method = WellKnownClasses::java_lang_String_charAt;
if (GetGraph()->GetArtMethod() != char_at_method) {
ArenaAllocator* allocator = GetGraph()->GetAllocator();
HEnvironment* environment = new (allocator) HEnvironment(allocator,
@@ -109,7 +109,7 @@
if (value->IsNullConstant()) {
DCHECK_EQ(value->GetType(), DataType::Type::kReference);
if (instruction->NeedsTypeCheck()) {
- instruction->ClearNeedsTypeCheck();
+ instruction->ClearTypeCheck();
}
}
}
diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h
index e0bb76e..0426f84 100644
--- a/compiler/optimizing/prepare_for_register_allocation.h
+++ b/compiler/optimizing/prepare_for_register_allocation.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_PREPARE_FOR_REGISTER_ALLOCATION_H_
#define ART_COMPILER_OPTIMIZING_PREPARE_FOR_REGISTER_ALLOCATION_H_
+#include "base/macros.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
class CompilerOptions;
class OptimizingCompilerStats;
diff --git a/compiler/optimizing/pretty_printer.h b/compiler/optimizing/pretty_printer.h
index 8ef9ce4..77ddb97 100644
--- a/compiler/optimizing/pretty_printer.h
+++ b/compiler/optimizing/pretty_printer.h
@@ -19,9 +19,10 @@
#include "android-base/stringprintf.h"
+#include "base/macros.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
class HPrettyPrinter : public HGraphVisitor {
public:
diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc
index 6ef386b..5ed3944 100644
--- a/compiler/optimizing/pretty_printer_test.cc
+++ b/compiler/optimizing/pretty_printer_test.cc
@@ -17,6 +17,7 @@
#include "pretty_printer.h"
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "dex/dex_file.h"
#include "dex/dex_instruction.h"
@@ -25,7 +26,7 @@
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
class PrettyPrinterTest : public OptimizingUnitTest {
protected:
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index e6024b0..2b0a118 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -29,7 +29,7 @@
#include "mirror/dex_cache.h"
#include "scoped_thread_state_change-inl.h"
-namespace art {
+namespace art HIDDEN {
static inline ObjPtr<mirror::DexCache> FindDexCacheWithHint(
Thread* self, const DexFile& dex_file, Handle<mirror::DexCache> hint_dex_cache)
@@ -43,16 +43,12 @@
class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor {
public:
- RTPVisitor(HGraph* graph,
- Handle<mirror::ClassLoader> class_loader,
- Handle<mirror::DexCache> hint_dex_cache,
- bool is_first_run)
- : HGraphDelegateVisitor(graph),
- class_loader_(class_loader),
- hint_dex_cache_(hint_dex_cache),
- allocator_(graph->GetArenaStack()),
- worklist_(allocator_.Adapter(kArenaAllocReferenceTypePropagation)),
- is_first_run_(is_first_run) {
+ RTPVisitor(HGraph* graph, Handle<mirror::DexCache> hint_dex_cache, bool is_first_run)
+ : HGraphDelegateVisitor(graph),
+ hint_dex_cache_(hint_dex_cache),
+ allocator_(graph->GetArenaStack()),
+ worklist_(allocator_.Adapter(kArenaAllocReferenceTypePropagation)),
+ is_first_run_(is_first_run) {
worklist_.reserve(kDefaultWorklistSize);
}
@@ -110,7 +106,6 @@
static constexpr size_t kDefaultWorklistSize = 8;
- Handle<mirror::ClassLoader> class_loader_;
Handle<mirror::DexCache> hint_dex_cache_;
// Use local allocator for allocating memory.
@@ -122,15 +117,10 @@
};
ReferenceTypePropagation::ReferenceTypePropagation(HGraph* graph,
- Handle<mirror::ClassLoader> class_loader,
Handle<mirror::DexCache> hint_dex_cache,
bool is_first_run,
const char* name)
- : HOptimization(graph, name),
- class_loader_(class_loader),
- hint_dex_cache_(hint_dex_cache),
- is_first_run_(is_first_run) {
-}
+ : HOptimization(graph, name), hint_dex_cache_(hint_dex_cache), is_first_run_(is_first_run) {}
void ReferenceTypePropagation::ValidateTypes() {
// TODO: move this to the graph checker. Note: There may be no Thread for gtests.
@@ -167,18 +157,12 @@
}
void ReferenceTypePropagation::Visit(HInstruction* instruction) {
- RTPVisitor visitor(graph_,
- class_loader_,
- hint_dex_cache_,
- is_first_run_);
+ RTPVisitor visitor(graph_, hint_dex_cache_, is_first_run_);
instruction->Accept(&visitor);
}
void ReferenceTypePropagation::Visit(ArrayRef<HInstruction* const> instructions) {
- RTPVisitor visitor(graph_,
- class_loader_,
- hint_dex_cache_,
- is_first_run_);
+ RTPVisitor visitor(graph_, hint_dex_cache_, is_first_run_);
for (HInstruction* instruction : instructions) {
if (instruction->IsPhi()) {
// Need to force phis to recalculate null-ness.
@@ -349,7 +333,7 @@
}
bool ReferenceTypePropagation::Run() {
- RTPVisitor visitor(graph_, class_loader_, hint_dex_cache_, is_first_run_);
+ RTPVisitor visitor(graph_, hint_dex_cache_, is_first_run_);
// To properly propagate type info we need to visit in the dominator-based order.
// Reverse post order guarantees a node's dominators are visited first.
@@ -446,10 +430,13 @@
if (rhs->AsIntConstant()->IsTrue()) {
// Case (1a)
*trueBranch = ifInstruction->IfTrueSuccessor();
- } else {
+ } else if (rhs->AsIntConstant()->IsFalse()) {
// Case (2a)
- DCHECK(rhs->AsIntConstant()->IsFalse()) << rhs->AsIntConstant()->GetValue();
*trueBranch = ifInstruction->IfFalseSuccessor();
+ } else {
+ // Sometimes we see a comparison of instance-of with a constant which is neither 0 nor 1.
+ // In those cases, we cannot do the match if+instance-of.
+ return false;
}
*instanceOf = lhs->AsInstanceOf();
return true;
@@ -463,10 +450,13 @@
if (rhs->AsIntConstant()->IsFalse()) {
// Case (1b)
*trueBranch = ifInstruction->IfTrueSuccessor();
- } else {
+ } else if (rhs->AsIntConstant()->IsTrue()) {
// Case (2b)
- DCHECK(rhs->AsIntConstant()->IsTrue()) << rhs->AsIntConstant()->GetValue();
*trueBranch = ifInstruction->IfFalseSuccessor();
+ } else {
+ // Sometimes we see a comparison of instance-of with a constant which is neither 0 nor 1.
+ // In those cases, we cannot do the match if+instance-of.
+ return false;
}
*instanceOf = lhs->AsInstanceOf();
return true;
@@ -583,7 +573,7 @@
ScopedObjectAccess soa(Thread::Current());
ObjPtr<mirror::DexCache> dex_cache = FindDexCacheWithHint(soa.Self(), dex_file, hint_dex_cache_);
ObjPtr<mirror::Class> klass = Runtime::Current()->GetClassLinker()->LookupResolvedType(
- type_idx, dex_cache, class_loader_.Get());
+ type_idx, dex_cache, dex_cache->GetClassLoader());
SetClassAsTypeInfo(instr, klass, is_exact);
}
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
index 889a846..d696e28 100644
--- a/compiler/optimizing/reference_type_propagation.h
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -18,12 +18,13 @@
#define ART_COMPILER_OPTIMIZING_REFERENCE_TYPE_PROPAGATION_H_
#include "base/arena_containers.h"
+#include "base/macros.h"
#include "mirror/class-inl.h"
#include "nodes.h"
#include "obj_ptr.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
/**
* Propagates reference types to instructions.
@@ -31,7 +32,6 @@
class ReferenceTypePropagation : public HOptimization {
public:
ReferenceTypePropagation(HGraph* graph,
- Handle<mirror::ClassLoader> class_loader,
Handle<mirror::DexCache> hint_dex_cache,
bool is_first_run,
const char* name = kReferenceTypePropagationPassName);
@@ -73,8 +73,6 @@
void ValidateTypes();
- Handle<mirror::ClassLoader> class_loader_;
-
// Note: hint_dex_cache_ is usually, but not necessarily, the dex cache associated with
// graph_->GetDexFile(). Since we may look up also in other dex files, it's used only
// as a hint, to reduce the number of calls to the costly ClassLinker::FindDexCache().
diff --git a/compiler/optimizing/reference_type_propagation_test.cc b/compiler/optimizing/reference_type_propagation_test.cc
index d1bcab0..2b012fc 100644
--- a/compiler/optimizing/reference_type_propagation_test.cc
+++ b/compiler/optimizing/reference_type_propagation_test.cc
@@ -19,6 +19,7 @@
#include <random>
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "base/transform_array_ref.h"
#include "base/transform_iterator.h"
#include "builder.h"
@@ -26,7 +27,7 @@
#include "object_lock.h"
#include "optimizing_unit_test.h"
-namespace art {
+namespace art HIDDEN {
// TODO It would be good to use the following but there is a miniscule amount of
// chance for flakiness so we'll just use a set seed instead.
@@ -47,11 +48,8 @@
void SetupPropagation(VariableSizedHandleScope* handles) {
graph_ = CreateGraph(handles);
- propagation_ = new (GetAllocator()) ReferenceTypePropagation(graph_,
- Handle<mirror::ClassLoader>(),
- Handle<mirror::DexCache>(),
- true,
- "test_prop");
+ propagation_ = new (GetAllocator())
+ ReferenceTypePropagation(graph_, Handle<mirror::DexCache>(), true, "test_prop");
}
// Relay method to merge type in reference type propagation.
diff --git a/compiler/optimizing/register_allocation_resolver.cc b/compiler/optimizing/register_allocation_resolver.cc
index 875c633..53e11f2 100644
--- a/compiler/optimizing/register_allocation_resolver.cc
+++ b/compiler/optimizing/register_allocation_resolver.cc
@@ -21,7 +21,7 @@
#include "linear_order.h"
#include "ssa_liveness_analysis.h"
-namespace art {
+namespace art HIDDEN {
RegisterAllocationResolver::RegisterAllocationResolver(CodeGenerator* codegen,
const SsaLivenessAnalysis& liveness)
diff --git a/compiler/optimizing/register_allocation_resolver.h b/compiler/optimizing/register_allocation_resolver.h
index 2783717..f4782eb 100644
--- a/compiler/optimizing/register_allocation_resolver.h
+++ b/compiler/optimizing/register_allocation_resolver.h
@@ -18,10 +18,11 @@
#define ART_COMPILER_OPTIMIZING_REGISTER_ALLOCATION_RESOLVER_H_
#include "base/array_ref.h"
+#include "base/macros.h"
#include "base/value_object.h"
#include "data_type.h"
-namespace art {
+namespace art HIDDEN {
class ArenaAllocator;
class CodeGenerator;
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index a9c217f..e4c2d74 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -27,7 +27,7 @@
#include "register_allocator_linear_scan.h"
#include "ssa_liveness_analysis.h"
-namespace art {
+namespace art HIDDEN {
RegisterAllocator::RegisterAllocator(ScopedArenaAllocator* allocator,
CodeGenerator* codegen,
diff --git a/compiler/optimizing/register_allocator.h b/compiler/optimizing/register_allocator.h
index 4d22687..453e339 100644
--- a/compiler/optimizing/register_allocator.h
+++ b/compiler/optimizing/register_allocator.h
@@ -22,7 +22,7 @@
#include "base/arena_object.h"
#include "base/macros.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
class HBasicBlock;
diff --git a/compiler/optimizing/register_allocator_graph_color.cc b/compiler/optimizing/register_allocator_graph_color.cc
index 684aaf5..a7c891d 100644
--- a/compiler/optimizing/register_allocator_graph_color.cc
+++ b/compiler/optimizing/register_allocator_graph_color.cc
@@ -22,7 +22,7 @@
#include "ssa_liveness_analysis.h"
#include "thread-current-inl.h"
-namespace art {
+namespace art HIDDEN {
// Highest number of registers that we support for any platform. This can be used for std::bitset,
// for example, which needs to know its size at compile time.
diff --git a/compiler/optimizing/register_allocator_graph_color.h b/compiler/optimizing/register_allocator_graph_color.h
index e5b86ea..0e10152 100644
--- a/compiler/optimizing/register_allocator_graph_color.h
+++ b/compiler/optimizing/register_allocator_graph_color.h
@@ -24,7 +24,7 @@
#include "base/scoped_arena_containers.h"
#include "register_allocator.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
class HBasicBlock;
diff --git a/compiler/optimizing/register_allocator_linear_scan.cc b/compiler/optimizing/register_allocator_linear_scan.cc
index 833c24d..fcdaa2d 100644
--- a/compiler/optimizing/register_allocator_linear_scan.cc
+++ b/compiler/optimizing/register_allocator_linear_scan.cc
@@ -26,7 +26,7 @@
#include "register_allocation_resolver.h"
#include "ssa_liveness_analysis.h"
-namespace art {
+namespace art HIDDEN {
static constexpr size_t kMaxLifetimePosition = -1;
static constexpr size_t kDefaultNumberOfSpillSlots = 4;
diff --git a/compiler/optimizing/register_allocator_linear_scan.h b/compiler/optimizing/register_allocator_linear_scan.h
index 9a1e0d7..c71a9e9 100644
--- a/compiler/optimizing/register_allocator_linear_scan.h
+++ b/compiler/optimizing/register_allocator_linear_scan.h
@@ -22,7 +22,7 @@
#include "base/scoped_arena_containers.h"
#include "register_allocator.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
class HBasicBlock;
diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc
index 6823155..dabd4a3 100644
--- a/compiler/optimizing/register_allocator_test.cc
+++ b/compiler/optimizing/register_allocator_test.cc
@@ -18,6 +18,7 @@
#include "arch/x86/instruction_set_features_x86.h"
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "code_generator.h"
#include "code_generator_x86.h"
@@ -31,7 +32,7 @@
#include "ssa_liveness_analysis.h"
#include "ssa_phi_elimination.h"
-namespace art {
+namespace art HIDDEN {
using Strategy = RegisterAllocator::Strategy;
diff --git a/compiler/optimizing/scheduler.cc b/compiler/optimizing/scheduler.cc
index 8f18ccf..116f526 100644
--- a/compiler/optimizing/scheduler.cc
+++ b/compiler/optimizing/scheduler.cc
@@ -32,7 +32,7 @@
#include "scheduler_arm.h"
#endif
-namespace art {
+namespace art HIDDEN {
void SchedulingGraph::AddDependency(SchedulingNode* node,
SchedulingNode* dependency,
@@ -718,9 +718,10 @@
// HLoadException
// HMemoryBarrier
// HMonitorOperation
- // HNativeDebugInfo
+ // HNop
// HThrow
// HTryBoundary
+ // All volatile field access e.g. HInstanceFieldGet
// TODO: Some of the instructions above may be safe to schedule (maybe as
// scheduling barriers).
return instruction->IsArrayGet() ||
diff --git a/compiler/optimizing/scheduler.h b/compiler/optimizing/scheduler.h
index f7180a0..299fbc9 100644
--- a/compiler/optimizing/scheduler.h
+++ b/compiler/optimizing/scheduler.h
@@ -19,6 +19,7 @@
#include <fstream>
+#include "base/macros.h"
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
#include "base/stl_util.h"
@@ -28,7 +29,7 @@
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
// General description of instruction scheduling.
//
diff --git a/compiler/optimizing/scheduler_arm.cc b/compiler/optimizing/scheduler_arm.cc
index 965e1bd..3f931c4 100644
--- a/compiler/optimizing/scheduler_arm.cc
+++ b/compiler/optimizing/scheduler_arm.cc
@@ -23,7 +23,7 @@
#include "mirror/array-inl.h"
#include "mirror/string.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
using helpers::Int32ConstantFrom;
@@ -669,7 +669,7 @@
}
case DataType::Type::kReference: {
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
last_visited_latency_ = kArmLoadWithBakerReadBarrierLatency;
} else {
if (index->IsConstant()) {
@@ -937,7 +937,7 @@
break;
case DataType::Type::kReference:
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
last_visited_internal_latency_ = kArmMemoryLoadLatency + kArmIntegerOpLatency;
last_visited_latency_ = kArmMemoryLoadLatency;
} else {
diff --git a/compiler/optimizing/scheduler_arm.h b/compiler/optimizing/scheduler_arm.h
index d11222d..73103df 100644
--- a/compiler/optimizing/scheduler_arm.h
+++ b/compiler/optimizing/scheduler_arm.h
@@ -17,14 +17,12 @@
#ifndef ART_COMPILER_OPTIMIZING_SCHEDULER_ARM_H_
#define ART_COMPILER_OPTIMIZING_SCHEDULER_ARM_H_
+#include "base/macros.h"
#include "code_generator_arm_vixl.h"
#include "scheduler.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
-// TODO: Replace CodeGeneratorARMType with CodeGeneratorARMVIXL everywhere?
-typedef CodeGeneratorARMVIXL CodeGeneratorARMType;
-
// AArch32 instruction latencies.
// We currently assume that all ARM CPUs share the same instruction latency list.
// The following latencies were tuned based on performance experiments and
@@ -52,7 +50,7 @@
class SchedulingLatencyVisitorARM : public SchedulingLatencyVisitor {
public:
explicit SchedulingLatencyVisitorARM(CodeGenerator* codegen)
- : codegen_(down_cast<CodeGeneratorARMType*>(codegen)) {}
+ : codegen_(down_cast<CodeGeneratorARMVIXL*>(codegen)) {}
// Default visitor for instructions not handled specifically below.
void VisitInstruction(HInstruction* ATTRIBUTE_UNUSED) override {
@@ -133,7 +131,7 @@
// The latency setting for each HInstruction depends on how CodeGenerator may generate code,
// latency visitors may query CodeGenerator for such information for accurate latency settings.
- CodeGeneratorARMType* codegen_;
+ CodeGeneratorARMVIXL* codegen_;
};
class HSchedulerARM : public HScheduler {
diff --git a/compiler/optimizing/scheduler_arm64.cc b/compiler/optimizing/scheduler_arm64.cc
index 4f504c2..3071afd 100644
--- a/compiler/optimizing/scheduler_arm64.cc
+++ b/compiler/optimizing/scheduler_arm64.cc
@@ -20,7 +20,7 @@
#include "mirror/array-inl.h"
#include "mirror/string.h"
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
void SchedulingLatencyVisitorARM64::VisitBinaryOperation(HBinaryOperation* instr) {
diff --git a/compiler/optimizing/scheduler_arm64.h b/compiler/optimizing/scheduler_arm64.h
index ba5a743..420f558 100644
--- a/compiler/optimizing/scheduler_arm64.h
+++ b/compiler/optimizing/scheduler_arm64.h
@@ -17,9 +17,10 @@
#ifndef ART_COMPILER_OPTIMIZING_SCHEDULER_ARM64_H_
#define ART_COMPILER_OPTIMIZING_SCHEDULER_ARM64_H_
+#include "base/macros.h"
#include "scheduler.h"
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
static constexpr uint32_t kArm64MemoryLoadLatency = 5;
diff --git a/compiler/optimizing/scheduler_test.cc b/compiler/optimizing/scheduler_test.cc
index a1cc202..6eea87f 100644
--- a/compiler/optimizing/scheduler_test.cc
+++ b/compiler/optimizing/scheduler_test.cc
@@ -17,6 +17,7 @@
#include "scheduler.h"
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "codegen_test_utils.h"
#include "common_compiler_test.h"
@@ -34,7 +35,7 @@
#include "scheduler_arm.h"
#endif
-namespace art {
+namespace art HIDDEN {
// Return all combinations of ISA and code generator that are executable on
// hardware, or on simulator, and that we'd like to test.
diff --git a/compiler/optimizing/select_generator.cc b/compiler/optimizing/select_generator.cc
index 5405382..761e7b5 100644
--- a/compiler/optimizing/select_generator.cc
+++ b/compiler/optimizing/select_generator.cc
@@ -16,10 +16,10 @@
#include "select_generator.h"
-#include "base/scoped_arena_containers.h"
+#include "optimizing/nodes.h"
#include "reference_type_propagation.h"
-namespace art {
+namespace art HIDDEN {
static constexpr size_t kMaxInstructionsInBranch = 1u;
@@ -69,156 +69,277 @@
return block1->GetSingleSuccessor() == block2->GetSingleSuccessor();
}
-// Returns nullptr if `block` has either no phis or there is more than one phi
-// with different inputs at `index1` and `index2`. Otherwise returns that phi.
-static HPhi* GetSingleChangedPhi(HBasicBlock* block, size_t index1, size_t index2) {
+// Returns nullptr if `block` has either no phis or there is more than one phi. Otherwise returns
+// that phi.
+static HPhi* GetSinglePhi(HBasicBlock* block, size_t index1, size_t index2) {
DCHECK_NE(index1, index2);
HPhi* select_phi = nullptr;
for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
HPhi* phi = it.Current()->AsPhi();
- if (phi->InputAt(index1) != phi->InputAt(index2)) {
- if (select_phi == nullptr) {
- // First phi with different inputs for the two indices found.
- select_phi = phi;
- } else {
- // More than one phis has different inputs for the two indices.
- return nullptr;
- }
+ if (select_phi == nullptr) {
+ // First phi found.
+ select_phi = phi;
+ } else {
+ // More than one phi found, return null.
+ return nullptr;
}
}
return select_phi;
}
+bool HSelectGenerator::TryGenerateSelectSimpleDiamondPattern(
+ HBasicBlock* block, ScopedArenaSafeMap<HInstruction*, HSelect*>* cache) {
+ DCHECK(block->GetLastInstruction()->IsIf());
+ HIf* if_instruction = block->GetLastInstruction()->AsIf();
+ HBasicBlock* true_block = if_instruction->IfTrueSuccessor();
+ HBasicBlock* false_block = if_instruction->IfFalseSuccessor();
+ DCHECK_NE(true_block, false_block);
+
+ if (!IsSimpleBlock(true_block) ||
+ !IsSimpleBlock(false_block) ||
+ !BlocksMergeTogether(true_block, false_block)) {
+ return false;
+ }
+ HBasicBlock* merge_block = true_block->GetSingleSuccessor();
+
+ // If the branches are not empty, move instructions in front of the If.
+ // TODO(dbrazdil): This puts an instruction between If and its condition.
+ // Implement moving of conditions to first users if possible.
+ while (!true_block->IsSingleGoto() && !true_block->IsSingleReturn()) {
+ HInstruction* instr = true_block->GetFirstInstruction();
+ DCHECK(!instr->CanThrow());
+ instr->MoveBefore(if_instruction);
+ }
+ while (!false_block->IsSingleGoto() && !false_block->IsSingleReturn()) {
+ HInstruction* instr = false_block->GetFirstInstruction();
+ DCHECK(!instr->CanThrow());
+ instr->MoveBefore(if_instruction);
+ }
+ DCHECK(true_block->IsSingleGoto() || true_block->IsSingleReturn());
+ DCHECK(false_block->IsSingleGoto() || false_block->IsSingleReturn());
+
+ // Find the resulting true/false values.
+ size_t predecessor_index_true = merge_block->GetPredecessorIndexOf(true_block);
+ size_t predecessor_index_false = merge_block->GetPredecessorIndexOf(false_block);
+ DCHECK_NE(predecessor_index_true, predecessor_index_false);
+
+ bool both_successors_return = true_block->IsSingleReturn() && false_block->IsSingleReturn();
+ // TODO(solanes): Extend to support multiple phis? e.g.
+ // int a, b;
+ // if (bool) {
+ // a = 0; b = 1;
+ // } else {
+ // a = 1; b = 2;
+ // }
+ // // use a and b
+ HPhi* phi = GetSinglePhi(merge_block, predecessor_index_true, predecessor_index_false);
+
+ HInstruction* true_value = nullptr;
+ HInstruction* false_value = nullptr;
+ if (both_successors_return) {
+ true_value = true_block->GetFirstInstruction()->InputAt(0);
+ false_value = false_block->GetFirstInstruction()->InputAt(0);
+ } else if (phi != nullptr) {
+ true_value = phi->InputAt(predecessor_index_true);
+ false_value = phi->InputAt(predecessor_index_false);
+ } else {
+ return false;
+ }
+ DCHECK(both_successors_return || phi != nullptr);
+
+ // Create the Select instruction and insert it in front of the If.
+ HInstruction* condition = if_instruction->InputAt(0);
+ HSelect* select = new (graph_->GetAllocator()) HSelect(condition,
+ true_value,
+ false_value,
+ if_instruction->GetDexPc());
+ if (both_successors_return) {
+ if (true_value->GetType() == DataType::Type::kReference) {
+ DCHECK(false_value->GetType() == DataType::Type::kReference);
+ ReferenceTypePropagation::FixUpInstructionType(select, graph_->GetHandleCache());
+ }
+ } else if (phi->GetType() == DataType::Type::kReference) {
+ select->SetReferenceTypeInfo(phi->GetReferenceTypeInfo());
+ }
+ block->InsertInstructionBefore(select, if_instruction);
+
+ // Remove the true branch which removes the corresponding Phi
+ // input if needed. If left only with the false branch, the Phi is
+ // automatically removed.
+ if (both_successors_return) {
+ false_block->GetFirstInstruction()->ReplaceInput(select, 0);
+ } else {
+ phi->ReplaceInput(select, predecessor_index_false);
+ }
+
+ bool only_two_predecessors = (merge_block->GetPredecessors().size() == 2u);
+ true_block->DisconnectAndDelete();
+
+ // Merge remaining blocks which are now connected with Goto.
+ DCHECK_EQ(block->GetSingleSuccessor(), false_block);
+ block->MergeWith(false_block);
+ if (!both_successors_return && only_two_predecessors) {
+ DCHECK_EQ(only_two_predecessors, phi->GetBlock() == nullptr);
+ DCHECK_EQ(block->GetSingleSuccessor(), merge_block);
+ block->MergeWith(merge_block);
+ }
+
+ MaybeRecordStat(stats_, MethodCompilationStat::kSelectGenerated);
+
+ // Very simple way of finding common subexpressions in the generated HSelect statements
+ // (since this runs after GVN). Lookup by condition, and reuse latest one if possible
+ // (due to post order, latest select is most likely replacement). If needed, we could
+ // improve this by e.g. using the operands in the map as well.
+ auto it = cache->find(condition);
+ if (it == cache->end()) {
+ cache->Put(condition, select);
+ } else {
+ // Found cached value. See if latest can replace cached in the HIR.
+ HSelect* cached_select = it->second;
+ DCHECK_EQ(cached_select->GetCondition(), select->GetCondition());
+ if (cached_select->GetTrueValue() == select->GetTrueValue() &&
+ cached_select->GetFalseValue() == select->GetFalseValue() &&
+ select->StrictlyDominates(cached_select)) {
+ cached_select->ReplaceWith(select);
+ cached_select->GetBlock()->RemoveInstruction(cached_select);
+ }
+ it->second = select; // always cache latest
+ }
+
+ // No need to update dominance information, as we are simplifying
+ // a simple diamond shape, where the join block is merged with the
+ // entry block. Any following blocks would have had the join block
+ // as a dominator, and `MergeWith` handles changing that to the
+ // entry block
+ return true;
+}
+
+HBasicBlock* HSelectGenerator::TryFixupDoubleDiamondPattern(HBasicBlock* block) {
+ DCHECK(block->GetLastInstruction()->IsIf());
+ HIf* if_instruction = block->GetLastInstruction()->AsIf();
+ HBasicBlock* true_block = if_instruction->IfTrueSuccessor();
+ HBasicBlock* false_block = if_instruction->IfFalseSuccessor();
+ DCHECK_NE(true_block, false_block);
+
+ // One branch must be a single goto, and the other one the inner if.
+ if (true_block->IsSingleGoto() == false_block->IsSingleGoto()) {
+ return nullptr;
+ }
+
+ HBasicBlock* single_goto = true_block->IsSingleGoto() ? true_block : false_block;
+ HBasicBlock* inner_if_block = true_block->IsSingleGoto() ? false_block : true_block;
+
+ // The innner if branch has to be a block with just a comparison and an if.
+ if (!inner_if_block->EndsWithIf() ||
+ inner_if_block->GetLastInstruction()->AsIf()->InputAt(0) !=
+ inner_if_block->GetFirstInstruction() ||
+ inner_if_block->GetLastInstruction()->GetPrevious() !=
+ inner_if_block->GetFirstInstruction() ||
+ !inner_if_block->GetFirstInstruction()->IsCondition()) {
+ return nullptr;
+ }
+
+ HIf* inner_if_instruction = inner_if_block->GetLastInstruction()->AsIf();
+ HBasicBlock* inner_if_true_block = inner_if_instruction->IfTrueSuccessor();
+ HBasicBlock* inner_if_false_block = inner_if_instruction->IfFalseSuccessor();
+ if (!inner_if_true_block->IsSingleGoto() || !inner_if_false_block->IsSingleGoto()) {
+ return nullptr;
+ }
+
+ // One must merge into the outer condition and the other must not.
+ if (BlocksMergeTogether(single_goto, inner_if_true_block) ==
+ BlocksMergeTogether(single_goto, inner_if_false_block)) {
+ return nullptr;
+ }
+
+ // First merge merges the outer if with one of the inner if branches. The block must be a Phi and
+ // a Goto.
+ HBasicBlock* first_merge = single_goto->GetSingleSuccessor();
+ if (first_merge->GetNumberOfPredecessors() != 2 ||
+ first_merge->GetPhis().CountSize() != 1 ||
+ !first_merge->GetLastInstruction()->IsGoto() ||
+ first_merge->GetFirstInstruction() != first_merge->GetLastInstruction()) {
+ return nullptr;
+ }
+
+ HPhi* first_phi = first_merge->GetFirstPhi()->AsPhi();
+
+ // Second merge is first_merge and the remainder branch merging. It must be phi + goto, or phi +
+ // return. Depending on the first merge, we define the second merge.
+ HBasicBlock* merges_into_second_merge =
+ BlocksMergeTogether(single_goto, inner_if_true_block)
+ ? inner_if_false_block
+ : inner_if_true_block;
+ if (!BlocksMergeTogether(first_merge, merges_into_second_merge)) {
+ return nullptr;
+ }
+
+ HBasicBlock* second_merge = merges_into_second_merge->GetSingleSuccessor();
+ if (second_merge->GetNumberOfPredecessors() != 2 ||
+ second_merge->GetPhis().CountSize() != 1 ||
+ !(second_merge->GetLastInstruction()->IsGoto() ||
+ second_merge->GetLastInstruction()->IsReturn()) ||
+ second_merge->GetFirstInstruction() != second_merge->GetLastInstruction()) {
+ return nullptr;
+ }
+
+ size_t index = second_merge->GetPredecessorIndexOf(merges_into_second_merge);
+ HPhi* second_phi = second_merge->GetFirstPhi()->AsPhi();
+
+ // Merge the phis.
+ first_phi->AddInput(second_phi->InputAt(index));
+ merges_into_second_merge->ReplaceSuccessor(second_merge, first_merge);
+ second_phi->ReplaceWith(first_phi);
+ second_merge->RemovePhi(second_phi);
+
+ // Sort out the new domination before merging the blocks
+ DCHECK_EQ(second_merge->GetSinglePredecessor(), first_merge);
+ second_merge->GetDominator()->RemoveDominatedBlock(second_merge);
+ second_merge->SetDominator(first_merge);
+ first_merge->AddDominatedBlock(second_merge);
+ first_merge->MergeWith(second_merge);
+
+ // No need to update dominance information. There's a chance that `merges_into_second_merge`
+ // doesn't come before `first_merge` but we don't need to fix it since `merges_into_second_merge`
+ // will disappear from the graph altogether when doing the follow-up
+ // TryGenerateSelectSimpleDiamondPattern.
+
+ return inner_if_block;
+}
+
bool HSelectGenerator::Run() {
- bool didSelect = false;
+ bool did_select = false;
// Select cache with local allocator.
ScopedArenaAllocator allocator(graph_->GetArenaStack());
- ScopedArenaSafeMap<HInstruction*, HSelect*> cache(
- std::less<HInstruction*>(), allocator.Adapter(kArenaAllocSelectGenerator));
+ ScopedArenaSafeMap<HInstruction*, HSelect*> cache(std::less<HInstruction*>(),
+ allocator.Adapter(kArenaAllocSelectGenerator));
// Iterate in post order in the unlikely case that removing one occurrence of
// the selection pattern empties a branch block of another occurrence.
for (HBasicBlock* block : graph_->GetPostOrder()) {
- if (!block->EndsWithIf()) continue;
-
- // Find elements of the diamond pattern.
- HIf* if_instruction = block->GetLastInstruction()->AsIf();
- HBasicBlock* true_block = if_instruction->IfTrueSuccessor();
- HBasicBlock* false_block = if_instruction->IfFalseSuccessor();
- DCHECK_NE(true_block, false_block);
-
- if (!IsSimpleBlock(true_block) ||
- !IsSimpleBlock(false_block) ||
- !BlocksMergeTogether(true_block, false_block)) {
+ if (!block->EndsWithIf()) {
continue;
}
- HBasicBlock* merge_block = true_block->GetSingleSuccessor();
- // If the branches are not empty, move instructions in front of the If.
- // TODO(dbrazdil): This puts an instruction between If and its condition.
- // Implement moving of conditions to first users if possible.
- while (!true_block->IsSingleGoto() && !true_block->IsSingleReturn()) {
- HInstruction* instr = true_block->GetFirstInstruction();
- DCHECK(!instr->CanThrow());
- instr->MoveBefore(if_instruction);
- }
- while (!false_block->IsSingleGoto() && !false_block->IsSingleReturn()) {
- HInstruction* instr = false_block->GetFirstInstruction();
- DCHECK(!instr->CanThrow());
- instr->MoveBefore(if_instruction);
- }
- DCHECK(true_block->IsSingleGoto() || true_block->IsSingleReturn());
- DCHECK(false_block->IsSingleGoto() || false_block->IsSingleReturn());
-
- // Find the resulting true/false values.
- size_t predecessor_index_true = merge_block->GetPredecessorIndexOf(true_block);
- size_t predecessor_index_false = merge_block->GetPredecessorIndexOf(false_block);
- DCHECK_NE(predecessor_index_true, predecessor_index_false);
-
- bool both_successors_return = true_block->IsSingleReturn() && false_block->IsSingleReturn();
- HPhi* phi = GetSingleChangedPhi(merge_block, predecessor_index_true, predecessor_index_false);
-
- HInstruction* true_value = nullptr;
- HInstruction* false_value = nullptr;
- if (both_successors_return) {
- true_value = true_block->GetFirstInstruction()->InputAt(0);
- false_value = false_block->GetFirstInstruction()->InputAt(0);
- } else if (phi != nullptr) {
- true_value = phi->InputAt(predecessor_index_true);
- false_value = phi->InputAt(predecessor_index_false);
+ if (TryGenerateSelectSimpleDiamondPattern(block, &cache)) {
+ did_select = true;
} else {
- continue;
- }
- DCHECK(both_successors_return || phi != nullptr);
-
- // Create the Select instruction and insert it in front of the If.
- HInstruction* condition = if_instruction->InputAt(0);
- HSelect* select = new (graph_->GetAllocator()) HSelect(condition,
- true_value,
- false_value,
- if_instruction->GetDexPc());
- if (both_successors_return) {
- if (true_value->GetType() == DataType::Type::kReference) {
- DCHECK(false_value->GetType() == DataType::Type::kReference);
- ReferenceTypePropagation::FixUpInstructionType(select, graph_->GetHandleCache());
+ // Try to fix up the odd version of the double diamond pattern. If we could do it, it means
+ // that we can generate two selects.
+ HBasicBlock* inner_if_block = TryFixupDoubleDiamondPattern(block);
+ if (inner_if_block != nullptr) {
+ // Generate the selects now since `inner_if_block` should be after `block` in PostOrder.
+ bool result = TryGenerateSelectSimpleDiamondPattern(inner_if_block, &cache);
+ DCHECK(result);
+ result = TryGenerateSelectSimpleDiamondPattern(block, &cache);
+ DCHECK(result);
+ did_select = true;
}
- } else if (phi->GetType() == DataType::Type::kReference) {
- select->SetReferenceTypeInfo(phi->GetReferenceTypeInfo());
}
- block->InsertInstructionBefore(select, if_instruction);
-
- // Remove the true branch which removes the corresponding Phi
- // input if needed. If left only with the false branch, the Phi is
- // automatically removed.
- if (both_successors_return) {
- false_block->GetFirstInstruction()->ReplaceInput(select, 0);
- } else {
- phi->ReplaceInput(select, predecessor_index_false);
- }
-
- bool only_two_predecessors = (merge_block->GetPredecessors().size() == 2u);
- true_block->DisconnectAndDelete();
-
- // Merge remaining blocks which are now connected with Goto.
- DCHECK_EQ(block->GetSingleSuccessor(), false_block);
- block->MergeWith(false_block);
- if (!both_successors_return && only_two_predecessors) {
- DCHECK_EQ(only_two_predecessors, phi->GetBlock() == nullptr);
- DCHECK_EQ(block->GetSingleSuccessor(), merge_block);
- block->MergeWith(merge_block);
- }
-
- MaybeRecordStat(stats_, MethodCompilationStat::kSelectGenerated);
-
- // Very simple way of finding common subexpressions in the generated HSelect statements
- // (since this runs after GVN). Lookup by condition, and reuse latest one if possible
- // (due to post order, latest select is most likely replacement). If needed, we could
- // improve this by e.g. using the operands in the map as well.
- auto it = cache.find(condition);
- if (it == cache.end()) {
- cache.Put(condition, select);
- } else {
- // Found cached value. See if latest can replace cached in the HIR.
- HSelect* cached = it->second;
- DCHECK_EQ(cached->GetCondition(), select->GetCondition());
- if (cached->GetTrueValue() == select->GetTrueValue() &&
- cached->GetFalseValue() == select->GetFalseValue() &&
- select->StrictlyDominates(cached)) {
- cached->ReplaceWith(select);
- cached->GetBlock()->RemoveInstruction(cached);
- }
- it->second = select; // always cache latest
- }
-
- // No need to update dominance information, as we are simplifying
- // a simple diamond shape, where the join block is merged with the
- // entry block. Any following blocks would have had the join block
- // as a dominator, and `MergeWith` handles changing that to the
- // entry block.
- didSelect = true;
}
- return didSelect;
+
+ return did_select;
}
} // namespace art
diff --git a/compiler/optimizing/select_generator.h b/compiler/optimizing/select_generator.h
index 30ac8a8..7aa0803 100644
--- a/compiler/optimizing/select_generator.h
+++ b/compiler/optimizing/select_generator.h
@@ -57,9 +57,12 @@
#ifndef ART_COMPILER_OPTIMIZING_SELECT_GENERATOR_H_
#define ART_COMPILER_OPTIMIZING_SELECT_GENERATOR_H_
+#include "base/macros.h"
+#include "base/scoped_arena_containers.h"
#include "optimization.h"
+#include "optimizing/nodes.h"
-namespace art {
+namespace art HIDDEN {
class HSelectGenerator : public HOptimization {
public:
@@ -72,6 +75,43 @@
static constexpr const char* kSelectGeneratorPassName = "select_generator";
private:
+ bool TryGenerateSelectSimpleDiamondPattern(HBasicBlock* block,
+ ScopedArenaSafeMap<HInstruction*, HSelect*>* cache);
+
+ // When generating code for nested ternary operators (e.g. `return (x > 100) ? 100 : ((x < -100) ?
+ // -100 : x);`), a dexer can generate a double diamond pattern but it is not a clear cut one due
+ // to the merging of the blocks. `TryFixupDoubleDiamondPattern` recognizes that pattern and fixes
+ // up the graph to have a clean double diamond that `TryGenerateSelectSimpleDiamondPattern` can
+ // use to generate selects.
+ //
+ // In ASCII, it turns:
+ //
+ // 1 (outer if)
+ // / \
+ // 2 3 (inner if)
+ // | / \
+ // | 4 5
+ // \/ |
+ // 6 |
+ // \ |
+ // 7
+ // |
+ // 8
+ // into:
+ // 1 (outer if)
+ // / \
+ // 2 3 (inner if)
+ // | / \
+ // | 4 5
+ // \/ /
+ // 6
+ // |
+ // 8
+ //
+ // In short, block 7 disappears and we merge 6 and 7. Now we have a diamond with {3,4,5,6}, and
+ // when that gets resolved we get another one with the outer if.
+ HBasicBlock* TryFixupDoubleDiamondPattern(HBasicBlock* block);
+
DISALLOW_COPY_AND_ASSIGN(HSelectGenerator);
};
diff --git a/compiler/optimizing/select_generator_test.cc b/compiler/optimizing/select_generator_test.cc
index b18d41a..fc9e150 100644
--- a/compiler/optimizing/select_generator_test.cc
+++ b/compiler/optimizing/select_generator_test.cc
@@ -17,12 +17,13 @@
#include "select_generator.h"
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
#include "side_effects_analysis.h"
-namespace art {
+namespace art HIDDEN {
class SelectGeneratorTest : public OptimizingUnitTest {
protected:
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index 17cf3d3..277edff 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -34,7 +34,7 @@
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
-namespace art {
+namespace art HIDDEN {
static bool IsInBootImage(ArtMethod* method) {
gc::Heap* heap = Runtime::Current()->GetHeap();
@@ -63,9 +63,9 @@
bool for_interface_call,
CodeGenerator* codegen) {
if (kIsDebugBuild) {
- ScopedObjectAccess soa(Thread::Current()); // Required for GetDeclaringClass below.
+ ScopedObjectAccess soa(Thread::Current()); // Required for `IsStringConstructor()` below.
DCHECK(callee != nullptr);
- DCHECK(!(callee->IsConstructor() && callee->GetDeclaringClass()->IsStringClass()));
+ DCHECK(!callee->IsStringConstructor());
}
MethodLoadKind method_load_kind;
diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h
index 9753669..6dfe904 100644
--- a/compiler/optimizing/sharpening.h
+++ b/compiler/optimizing/sharpening.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_OPTIMIZING_SHARPENING_H_
#define ART_COMPILER_OPTIMIZING_SHARPENING_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
class DexCompilationUnit;
diff --git a/compiler/optimizing/side_effects_analysis.cc b/compiler/optimizing/side_effects_analysis.cc
index ba97b43..56719b1 100644
--- a/compiler/optimizing/side_effects_analysis.cc
+++ b/compiler/optimizing/side_effects_analysis.cc
@@ -16,7 +16,7 @@
#include "side_effects_analysis.h"
-namespace art {
+namespace art HIDDEN {
bool SideEffectsAnalysis::Run() {
// Inlining might have created more blocks, so we need to increase the size
diff --git a/compiler/optimizing/side_effects_analysis.h b/compiler/optimizing/side_effects_analysis.h
index 56a01e6..47fcdc5 100644
--- a/compiler/optimizing/side_effects_analysis.h
+++ b/compiler/optimizing/side_effects_analysis.h
@@ -18,10 +18,11 @@
#define ART_COMPILER_OPTIMIZING_SIDE_EFFECTS_ANALYSIS_H_
#include "base/arena_containers.h"
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
class SideEffectsAnalysis : public HOptimization {
public:
diff --git a/compiler/optimizing/side_effects_test.cc b/compiler/optimizing/side_effects_test.cc
index 268798c..f2b781d 100644
--- a/compiler/optimizing/side_effects_test.cc
+++ b/compiler/optimizing/side_effects_test.cc
@@ -16,10 +16,11 @@
#include <gtest/gtest.h>
+#include "base/macros.h"
#include "data_type.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
// Only runtime types other than void are allowed.
static const DataType::Type kTestTypes[] = {
diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc
index 67ee83c..a658252 100644
--- a/compiler/optimizing/ssa_builder.cc
+++ b/compiler/optimizing/ssa_builder.cc
@@ -27,7 +27,7 @@
#include "scoped_thread_state_change-inl.h"
#include "ssa_phi_elimination.h"
-namespace art {
+namespace art HIDDEN {
void SsaBuilder::FixNullConstantType() {
// The order doesn't matter here.
@@ -538,7 +538,6 @@
// Compute type of reference type instructions. The pass assumes that
// NullConstant has been fixed up.
ReferenceTypePropagation(graph_,
- class_loader_,
dex_cache_,
/* is_first_run= */ true).Run();
diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h
index a7d4e0e..99a5469 100644
--- a/compiler/optimizing/ssa_builder.h
+++ b/compiler/optimizing/ssa_builder.h
@@ -17,12 +17,13 @@
#ifndef ART_COMPILER_OPTIMIZING_SSA_BUILDER_H_
#define ART_COMPILER_OPTIMIZING_SSA_BUILDER_H_
+#include "base/macros.h"
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
/**
* Transforms a graph into SSA form. The liveness guarantees of
diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc
index 18942a1..317e099 100644
--- a/compiler/optimizing/ssa_liveness_analysis.cc
+++ b/compiler/optimizing/ssa_liveness_analysis.cc
@@ -21,7 +21,7 @@
#include "linear_order.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
void SsaLivenessAnalysis::Analyze() {
// Compute the linear order directly in the graph's data structure
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 7f31585..cc2b49c 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -21,11 +21,12 @@
#include "base/intrusive_forward_list.h"
#include "base/iteration_range.h"
+#include "base/macros.h"
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
class SsaLivenessAnalysis;
diff --git a/compiler/optimizing/ssa_liveness_analysis_test.cc b/compiler/optimizing/ssa_liveness_analysis_test.cc
index a477893..2df0f34 100644
--- a/compiler/optimizing/ssa_liveness_analysis_test.cc
+++ b/compiler/optimizing/ssa_liveness_analysis_test.cc
@@ -20,12 +20,13 @@
#include "arch/instruction_set_features.h"
#include "base/arena_allocator.h"
#include "base/arena_containers.h"
+#include "base/macros.h"
#include "code_generator.h"
#include "driver/compiler_options.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
-namespace art {
+namespace art HIDDEN {
class SsaLivenessAnalysisTest : public OptimizingUnitTest {
protected:
diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc
index 8fd6962..ce343df 100644
--- a/compiler/optimizing/ssa_phi_elimination.cc
+++ b/compiler/optimizing/ssa_phi_elimination.cc
@@ -21,7 +21,7 @@
#include "base/scoped_arena_containers.h"
#include "base/bit_vector-inl.h"
-namespace art {
+namespace art HIDDEN {
bool SsaDeadPhiElimination::Run() {
MarkDeadPhis();
diff --git a/compiler/optimizing/ssa_phi_elimination.h b/compiler/optimizing/ssa_phi_elimination.h
index c5cc752..f606f92 100644
--- a/compiler/optimizing/ssa_phi_elimination.h
+++ b/compiler/optimizing/ssa_phi_elimination.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_OPTIMIZING_SSA_PHI_ELIMINATION_H_
#define ART_COMPILER_OPTIMIZING_SSA_PHI_ELIMINATION_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
/**
* Optimization phase that removes dead phis from the graph. Dead phis are unused
diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc
index e679893..008e435 100644
--- a/compiler/optimizing/ssa_test.cc
+++ b/compiler/optimizing/ssa_test.cc
@@ -17,6 +17,7 @@
#include "android-base/stringprintf.h"
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "builder.h"
#include "dex/dex_file.h"
#include "dex/dex_instruction.h"
@@ -27,7 +28,7 @@
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
class SsaTest : public OptimizingUnitTest {
protected:
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index f55bbee..1a368ed 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -20,6 +20,7 @@
#include <vector>
#include "art_method-inl.h"
+#include "base/globals.h"
#include "base/stl_util.h"
#include "class_linker.h"
#include "dex/dex_file.h"
@@ -32,7 +33,7 @@
#include "scoped_thread_state_change-inl.h"
#include "stack_map.h"
-namespace art {
+namespace art HIDDEN {
constexpr static bool kVerifyStackMaps = kIsDebugBuild;
@@ -49,7 +50,8 @@
size_t core_spill_mask,
size_t fp_spill_mask,
uint32_t num_dex_registers,
- bool baseline) {
+ bool baseline,
+ bool debuggable) {
DCHECK(!in_method_) << "Mismatched Begin/End calls";
in_method_ = true;
DCHECK_EQ(packed_frame_size_, 0u) << "BeginMethod was already called";
@@ -60,6 +62,7 @@
fp_spill_mask_ = fp_spill_mask;
num_dex_registers_ = num_dex_registers;
baseline_ = baseline;
+ debuggable_ = debuggable;
if (kVerifyStackMaps) {
dchecks_.emplace_back([=](const CodeInfo& code_info) {
@@ -99,16 +102,21 @@
}
}
-void StackMapStream::BeginStackMapEntry(uint32_t dex_pc,
- uint32_t native_pc_offset,
- uint32_t register_mask,
- BitVector* stack_mask,
- StackMap::Kind kind,
- bool needs_vreg_info) {
+void StackMapStream::BeginStackMapEntry(
+ uint32_t dex_pc,
+ uint32_t native_pc_offset,
+ uint32_t register_mask,
+ BitVector* stack_mask,
+ StackMap::Kind kind,
+ bool needs_vreg_info,
+ const std::vector<uint32_t>& dex_pc_list_for_catch_verification) {
DCHECK(in_method_) << "Call BeginMethod first";
DCHECK(!in_stack_map_) << "Mismatched Begin/End calls";
in_stack_map_ = true;
+ DCHECK_IMPLIES(!dex_pc_list_for_catch_verification.empty(), kind == StackMap::Kind::Catch);
+ DCHECK_IMPLIES(!dex_pc_list_for_catch_verification.empty(), kIsDebugBuild);
+
current_stack_map_ = BitTableBuilder<StackMap>::Entry();
current_stack_map_[StackMap::kKind] = static_cast<uint32_t>(kind);
current_stack_map_[StackMap::kPackedNativePc] =
@@ -149,7 +157,8 @@
instruction_set_);
CHECK_EQ(stack_map.Row(), stack_map_index);
} else if (kind == StackMap::Kind::Catch) {
- StackMap stack_map = code_info.GetCatchStackMapForDexPc(dex_pc);
+ StackMap stack_map = code_info.GetCatchStackMapForDexPc(
+ ArrayRef<const uint32_t>(dex_pc_list_for_catch_verification));
CHECK_EQ(stack_map.Row(), stack_map_index);
}
StackMap stack_map = code_info.GetStackMapAt(stack_map_index);
@@ -367,6 +376,7 @@
uint32_t flags = (inline_infos_.size() > 0) ? CodeInfo::kHasInlineInfo : 0;
flags |= baseline_ ? CodeInfo::kIsBaseline : 0;
+ flags |= debuggable_ ? CodeInfo::kIsDebuggable : 0;
DCHECK_LE(flags, kVarintMax); // Ensure flags can be read directly as byte.
uint32_t bit_table_flags = 0;
ForEachBitTable([&bit_table_flags](size_t i, auto bit_table) {
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index 27145a1..643af2d 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -21,6 +21,7 @@
#include "base/arena_bit_vector.h"
#include "base/bit_table.h"
#include "base/bit_vector-inl.h"
+#include "base/macros.h"
#include "base/memory_region.h"
#include "base/scoped_arena_containers.h"
#include "base/value_object.h"
@@ -28,7 +29,7 @@
#include "nodes.h"
#include "stack_map.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
@@ -64,15 +65,19 @@
size_t core_spill_mask,
size_t fp_spill_mask,
uint32_t num_dex_registers,
- bool baseline = false);
+ bool baseline,
+ bool debuggable);
void EndMethod(size_t code_size);
- void BeginStackMapEntry(uint32_t dex_pc,
- uint32_t native_pc_offset,
- uint32_t register_mask = 0,
- BitVector* sp_mask = nullptr,
- StackMap::Kind kind = StackMap::Kind::Default,
- bool needs_vreg_info = true);
+ void BeginStackMapEntry(
+ uint32_t dex_pc,
+ uint32_t native_pc_offset,
+ uint32_t register_mask = 0,
+ BitVector* sp_mask = nullptr,
+ StackMap::Kind kind = StackMap::Kind::Default,
+ bool needs_vreg_info = true,
+ const std::vector<uint32_t>& dex_pc_list_for_catch_verification = std::vector<uint32_t>());
+
void EndStackMapEntry();
void AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value) {
@@ -125,6 +130,7 @@
uint32_t fp_spill_mask_ = 0;
uint32_t num_dex_registers_ = 0;
bool baseline_;
+ bool debuggable_;
BitTableBuilder<StackMap> stack_maps_;
BitTableBuilder<RegisterMask> register_masks_;
BitmapTableBuilder stack_masks_;
diff --git a/compiler/optimizing/stack_map_test.cc b/compiler/optimizing/stack_map_test.cc
index f6a739e..a2c30e7 100644
--- a/compiler/optimizing/stack_map_test.cc
+++ b/compiler/optimizing/stack_map_test.cc
@@ -18,12 +18,13 @@
#include "art_method.h"
#include "base/arena_bit_vector.h"
+#include "base/macros.h"
#include "base/malloc_arena_pool.h"
#include "stack_map_stream.h"
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
// Check that the stack mask of given stack map is identical
// to the given bit vector. Returns true if they are same.
@@ -52,7 +53,12 @@
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 2);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 2,
+ /* baseline= */ false,
+ /* debuggable= */ false);
ArenaBitVector sp_mask(&allocator, 0, false);
size_t number_of_dex_registers = 2;
@@ -106,7 +112,12 @@
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 2);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 2,
+ /* baseline= */ false,
+ /* debuggable= */ false);
ArtMethod art_method;
ArenaBitVector sp_mask1(&allocator, 0, true);
@@ -300,7 +311,12 @@
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 2);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 2,
+ /* baseline= */ false,
+ /* debuggable= */ false);
ArtMethod art_method;
ArenaBitVector sp_mask1(&allocator, 0, true);
@@ -363,7 +379,12 @@
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 2);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 2,
+ /* baseline= */ false,
+ /* debuggable= */ false);
ArenaBitVector sp_mask(&allocator, 0, false);
uint32_t number_of_dex_registers = 2;
@@ -411,7 +432,12 @@
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 2);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 2,
+ /* baseline= */ false,
+ /* debuggable= */ false);
ArenaBitVector sp_mask(&allocator, 0, false);
uint32_t number_of_dex_registers = 2;
@@ -467,7 +493,12 @@
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 1);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 1,
+ /* baseline= */ false,
+ /* debuggable= */ false);
ArenaBitVector sp_mask(&allocator, 0, false);
stream.BeginStackMapEntry(0, 64 * kPcAlign, 0x3, &sp_mask);
@@ -512,7 +543,12 @@
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 2);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 2,
+ /* baseline= */ false,
+ /* debuggable= */ false);
ArtMethod art_method;
ArenaBitVector sp_mask1(&allocator, 0, true);
@@ -702,7 +738,12 @@
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 0);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 0,
+ /* baseline= */ false,
+ /* debuggable= */ false);
ArenaBitVector sp_mask(&allocator, 0, true);
sp_mask.SetBit(1);
diff --git a/compiler/optimizing/superblock_cloner.cc b/compiler/optimizing/superblock_cloner.cc
index a5f919c..ba56eb8 100644
--- a/compiler/optimizing/superblock_cloner.cc
+++ b/compiler/optimizing/superblock_cloner.cc
@@ -22,7 +22,7 @@
#include <sstream>
-namespace art {
+namespace art HIDDEN {
using HBasicBlockMap = SuperblockCloner::HBasicBlockMap;
using HInstructionMap = SuperblockCloner::HInstructionMap;
diff --git a/compiler/optimizing/superblock_cloner.h b/compiler/optimizing/superblock_cloner.h
index 1f6ee74..421701f 100644
--- a/compiler/optimizing/superblock_cloner.h
+++ b/compiler/optimizing/superblock_cloner.h
@@ -20,9 +20,10 @@
#include "base/arena_bit_vector.h"
#include "base/arena_containers.h"
#include "base/bit_vector-inl.h"
+#include "base/macros.h"
#include "nodes.h"
-namespace art {
+namespace art HIDDEN {
class InductionVarRange;
diff --git a/compiler/optimizing/superblock_cloner_test.cc b/compiler/optimizing/superblock_cloner_test.cc
index d8d68b7..ea2563e 100644
--- a/compiler/optimizing/superblock_cloner_test.cc
+++ b/compiler/optimizing/superblock_cloner_test.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "base/macros.h"
#include "graph_checker.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
@@ -21,7 +22,7 @@
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
using HBasicBlockMap = SuperblockCloner::HBasicBlockMap;
using HInstructionMap = SuperblockCloner::HInstructionMap;
diff --git a/compiler/optimizing/suspend_check_test.cc b/compiler/optimizing/suspend_check_test.cc
index 33823e2..93a84b4 100644
--- a/compiler/optimizing/suspend_check_test.cc
+++ b/compiler/optimizing/suspend_check_test.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "base/macros.h"
#include "builder.h"
#include "dex/dex_instruction.h"
#include "nodes.h"
@@ -22,7 +23,7 @@
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
/**
* Check that the HGraphBuilder adds suspend checks to backward branches.
diff --git a/compiler/optimizing/write_barrier_elimination.cc b/compiler/optimizing/write_barrier_elimination.cc
new file mode 100644
index 0000000..9023cc2
--- /dev/null
+++ b/compiler/optimizing/write_barrier_elimination.cc
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2022 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 "write_barrier_elimination.h"
+
+#include "base/arena_allocator.h"
+#include "base/scoped_arena_allocator.h"
+#include "base/scoped_arena_containers.h"
+#include "optimizing/nodes.h"
+
+namespace art HIDDEN {
+
+class WBEVisitor : public HGraphVisitor {
+ public:
+ WBEVisitor(HGraph* graph, OptimizingCompilerStats* stats)
+ : HGraphVisitor(graph),
+ scoped_allocator_(graph->GetArenaStack()),
+ current_write_barriers_(scoped_allocator_.Adapter(kArenaAllocWBE)),
+ stats_(stats) {}
+
+ void VisitBasicBlock(HBasicBlock* block) override {
+ // We clear the map to perform this optimization only in the same block. Doing it across blocks
+ // would entail non-trivial merging of states.
+ current_write_barriers_.clear();
+ HGraphVisitor::VisitBasicBlock(block);
+ }
+
+ void VisitInstanceFieldSet(HInstanceFieldSet* instruction) override {
+ DCHECK(!instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC()));
+
+ if (instruction->GetFieldType() != DataType::Type::kReference ||
+ instruction->GetValue()->IsNullConstant()) {
+ instruction->SetWriteBarrierKind(WriteBarrierKind::kDontEmit);
+ return;
+ }
+
+ MaybeRecordStat(stats_, MethodCompilationStat::kPossibleWriteBarrier);
+ HInstruction* obj = HuntForOriginalReference(instruction->InputAt(0));
+ auto it = current_write_barriers_.find(obj);
+ if (it != current_write_barriers_.end()) {
+ DCHECK(it->second->IsInstanceFieldSet());
+ DCHECK(it->second->AsInstanceFieldSet()->GetWriteBarrierKind() !=
+ WriteBarrierKind::kDontEmit);
+ DCHECK_EQ(it->second->GetBlock(), instruction->GetBlock());
+ it->second->AsInstanceFieldSet()->SetWriteBarrierKind(WriteBarrierKind::kEmitNoNullCheck);
+ instruction->SetWriteBarrierKind(WriteBarrierKind::kDontEmit);
+ MaybeRecordStat(stats_, MethodCompilationStat::kRemovedWriteBarrier);
+ } else {
+ const bool inserted = current_write_barriers_.insert({obj, instruction}).second;
+ DCHECK(inserted);
+ DCHECK(instruction->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit);
+ }
+ }
+
+ void VisitStaticFieldSet(HStaticFieldSet* instruction) override {
+ DCHECK(!instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC()));
+
+ if (instruction->GetFieldType() != DataType::Type::kReference ||
+ instruction->GetValue()->IsNullConstant()) {
+ instruction->SetWriteBarrierKind(WriteBarrierKind::kDontEmit);
+ return;
+ }
+
+ MaybeRecordStat(stats_, MethodCompilationStat::kPossibleWriteBarrier);
+ HInstruction* cls = HuntForOriginalReference(instruction->InputAt(0));
+ auto it = current_write_barriers_.find(cls);
+ if (it != current_write_barriers_.end()) {
+ DCHECK(it->second->IsStaticFieldSet());
+ DCHECK(it->second->AsStaticFieldSet()->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit);
+ DCHECK_EQ(it->second->GetBlock(), instruction->GetBlock());
+ it->second->AsStaticFieldSet()->SetWriteBarrierKind(WriteBarrierKind::kEmitNoNullCheck);
+ instruction->SetWriteBarrierKind(WriteBarrierKind::kDontEmit);
+ MaybeRecordStat(stats_, MethodCompilationStat::kRemovedWriteBarrier);
+ } else {
+ const bool inserted = current_write_barriers_.insert({cls, instruction}).second;
+ DCHECK(inserted);
+ DCHECK(instruction->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit);
+ }
+ }
+
+ void VisitArraySet(HArraySet* instruction) override {
+ if (instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC())) {
+ ClearCurrentValues();
+ }
+
+ if (instruction->GetComponentType() != DataType::Type::kReference ||
+ instruction->GetValue()->IsNullConstant()) {
+ instruction->SetWriteBarrierKind(WriteBarrierKind::kDontEmit);
+ return;
+ }
+
+ HInstruction* arr = HuntForOriginalReference(instruction->InputAt(0));
+ MaybeRecordStat(stats_, MethodCompilationStat::kPossibleWriteBarrier);
+ auto it = current_write_barriers_.find(arr);
+ if (it != current_write_barriers_.end()) {
+ DCHECK(it->second->IsArraySet());
+ DCHECK(it->second->AsArraySet()->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit);
+ DCHECK_EQ(it->second->GetBlock(), instruction->GetBlock());
+ // We never skip the null check in ArraySets so that value is already set.
+ DCHECK(it->second->AsArraySet()->GetWriteBarrierKind() == WriteBarrierKind::kEmitNoNullCheck);
+ instruction->SetWriteBarrierKind(WriteBarrierKind::kDontEmit);
+ MaybeRecordStat(stats_, MethodCompilationStat::kRemovedWriteBarrier);
+ } else {
+ const bool inserted = current_write_barriers_.insert({arr, instruction}).second;
+ DCHECK(inserted);
+ DCHECK(instruction->GetWriteBarrierKind() != WriteBarrierKind::kDontEmit);
+ }
+ }
+
+ void VisitInstruction(HInstruction* instruction) override {
+ if (instruction->GetSideEffects().Includes(SideEffects::CanTriggerGC())) {
+ ClearCurrentValues();
+ }
+ }
+
+ private:
+ void ClearCurrentValues() { current_write_barriers_.clear(); }
+
+ HInstruction* HuntForOriginalReference(HInstruction* ref) const {
+ // An original reference can be transformed by instructions like:
+ // i0 NewArray
+ // i1 HInstruction(i0) <-- NullCheck, BoundType, IntermediateAddress.
+ // i2 ArraySet(i1, index, value)
+ DCHECK(ref != nullptr);
+ while (ref->IsNullCheck() || ref->IsBoundType() || ref->IsIntermediateAddress()) {
+ ref = ref->InputAt(0);
+ }
+ return ref;
+ }
+
+ ScopedArenaAllocator scoped_allocator_;
+
+ // Stores a map of <Receiver, InstructionWhereTheWriteBarrierIs>.
+ // `InstructionWhereTheWriteBarrierIs` is used for DCHECKs only.
+ ScopedArenaHashMap<HInstruction*, HInstruction*> current_write_barriers_;
+
+ OptimizingCompilerStats* const stats_;
+
+ DISALLOW_COPY_AND_ASSIGN(WBEVisitor);
+};
+
+bool WriteBarrierElimination::Run() {
+ WBEVisitor wbe_visitor(graph_, stats_);
+ wbe_visitor.VisitReversePostOrder();
+ return true;
+}
+
+} // namespace art
diff --git a/compiler/optimizing/write_barrier_elimination.h b/compiler/optimizing/write_barrier_elimination.h
new file mode 100644
index 0000000..a3769e7
--- /dev/null
+++ b/compiler/optimizing/write_barrier_elimination.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_WRITE_BARRIER_ELIMINATION_H_
+#define ART_COMPILER_OPTIMIZING_WRITE_BARRIER_ELIMINATION_H_
+
+#include "base/macros.h"
+#include "optimization.h"
+
+namespace art HIDDEN {
+
+// Eliminates unnecessary write barriers from InstanceFieldSet, StaticFieldSet, and ArraySet.
+//
+// We can eliminate redundant write barriers as we don't need several for the same receiver. For
+// example:
+// MyObject o;
+// o.inner_obj = io;
+// o.inner_obj2 = io2;
+// o.inner_obj3 = io3;
+// We can keep the write barrier for `inner_obj` and remove the other two.
+//
+// In order to do this, we set the WriteBarrierKind of the instruction. The instruction's kind are
+// set to kEmitNoNullCheck (if this write barrier coalesced other write barriers, we don't want to
+// perform the null check optimization), or to kDontEmit (if the write barrier as a whole is not
+// needed).
+class WriteBarrierElimination : public HOptimization {
+ public:
+ WriteBarrierElimination(HGraph* graph,
+ OptimizingCompilerStats* stats,
+ const char* name = kWBEPassName)
+ : HOptimization(graph, name, stats) {}
+
+ bool Run() override;
+
+ static constexpr const char* kWBEPassName = "write_barrier_elimination";
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WriteBarrierElimination);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_WRITE_BARRIER_ELIMINATION_H_
diff --git a/compiler/optimizing/x86_memory_gen.cc b/compiler/optimizing/x86_memory_gen.cc
index b1abcf6..8265214 100644
--- a/compiler/optimizing/x86_memory_gen.cc
+++ b/compiler/optimizing/x86_memory_gen.cc
@@ -18,7 +18,7 @@
#include "code_generator.h"
#include "driver/compiler_options.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
/**
diff --git a/compiler/optimizing/x86_memory_gen.h b/compiler/optimizing/x86_memory_gen.h
index 3f4178d..1cae1a5 100644
--- a/compiler/optimizing/x86_memory_gen.h
+++ b/compiler/optimizing/x86_memory_gen.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_OPTIMIZING_X86_MEMORY_GEN_H_
#define ART_COMPILER_OPTIMIZING_X86_MEMORY_GEN_H_
+#include "base/macros.h"
#include "nodes.h"
#include "optimization.h"
-namespace art {
+namespace art HIDDEN {
class CodeGenerator;
namespace x86 {
diff --git a/compiler/trampolines/trampoline_compiler.cc b/compiler/trampolines/trampoline_compiler.cc
index 0aaeaa5..5e7ac83 100644
--- a/compiler/trampolines/trampoline_compiler.cc
+++ b/compiler/trampolines/trampoline_compiler.cc
@@ -38,7 +38,7 @@
#define __ assembler.
-namespace art {
+namespace art HIDDEN {
#ifdef ART_ENABLE_CODEGEN_arm
namespace arm {
diff --git a/compiler/trampolines/trampoline_compiler.h b/compiler/trampolines/trampoline_compiler.h
index f0086b5..32e35ae 100644
--- a/compiler/trampolines/trampoline_compiler.h
+++ b/compiler/trampolines/trampoline_compiler.h
@@ -22,9 +22,10 @@
#include <vector>
#include "arch/instruction_set.h"
+#include "base/macros.h"
#include "offsets.h"
-namespace art {
+namespace art HIDDEN {
enum EntryPointCallingConvention {
// ABI of invocations to a method's interpreter entry point.
@@ -36,12 +37,10 @@
};
// Create code that will invoke the function held in thread local storage.
-std::unique_ptr<const std::vector<uint8_t>> CreateTrampoline32(InstructionSet isa,
- EntryPointCallingConvention abi,
- ThreadOffset32 entry_point_offset);
-std::unique_ptr<const std::vector<uint8_t>> CreateTrampoline64(InstructionSet isa,
- EntryPointCallingConvention abi,
- ThreadOffset64 entry_point_offset);
+EXPORT std::unique_ptr<const std::vector<uint8_t>> CreateTrampoline32(
+ InstructionSet isa, EntryPointCallingConvention abi, ThreadOffset32 entry_point_offset);
+EXPORT std::unique_ptr<const std::vector<uint8_t>> CreateTrampoline64(
+ InstructionSet isa, EntryPointCallingConvention abi, ThreadOffset64 entry_point_offset);
} // namespace art
diff --git a/compiler/utils/arm/assembler_arm_shared.h b/compiler/utils/arm/assembler_arm_shared.h
index 7464052..77118c8 100644
--- a/compiler/utils/arm/assembler_arm_shared.h
+++ b/compiler/utils/arm/assembler_arm_shared.h
@@ -17,7 +17,9 @@
#ifndef ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_SHARED_H_
#define ART_COMPILER_UTILS_ARM_ASSEMBLER_ARM_SHARED_H_
-namespace art {
+#include "base/macros.h"
+
+namespace art HIDDEN {
namespace arm {
enum LoadOperandType {
diff --git a/compiler/utils/arm/assembler_arm_vixl.cc b/compiler/utils/arm/assembler_arm_vixl.cc
index 77f5d70..c7ca003 100644
--- a/compiler/utils/arm/assembler_arm_vixl.cc
+++ b/compiler/utils/arm/assembler_arm_vixl.cc
@@ -26,7 +26,7 @@
using namespace vixl::aarch32; // NOLINT(build/namespaces)
-namespace art {
+namespace art HIDDEN {
namespace arm {
#ifdef ___
@@ -81,9 +81,7 @@
}
void ArmVIXLAssembler::GenerateMarkingRegisterCheck(vixl32::Register temp, int code) {
- // The Marking Register is only used in the Baker read barrier configuration.
- DCHECK(kEmitCompilerReadBarrier);
- DCHECK(kUseBakerReadBarrier);
+ DCHECK(kReserveMarkingRegister);
vixl32::Label mr_is_ok;
diff --git a/compiler/utils/arm/assembler_arm_vixl.h b/compiler/utils/arm/assembler_arm_vixl.h
index 5bc8a70..f2c0b76 100644
--- a/compiler/utils/arm/assembler_arm_vixl.h
+++ b/compiler/utils/arm/assembler_arm_vixl.h
@@ -37,7 +37,7 @@
namespace vixl32 = vixl::aarch32;
-namespace art {
+namespace art HIDDEN {
namespace arm {
inline dwarf::Reg DWARFReg(vixl32::Register reg) {
diff --git a/compiler/utils/arm/constants_arm.cc b/compiler/utils/arm/constants_arm.cc
index b02b343..a927fc2 100644
--- a/compiler/utils/arm/constants_arm.cc
+++ b/compiler/utils/arm/constants_arm.cc
@@ -16,7 +16,7 @@
#include "constants_arm.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
std::ostream& operator<<(std::ostream& os, const DRegister& rhs) {
diff --git a/compiler/utils/arm/constants_arm.h b/compiler/utils/arm/constants_arm.h
index f42fd97..ef6d48d 100644
--- a/compiler/utils/arm/constants_arm.h
+++ b/compiler/utils/arm/constants_arm.h
@@ -26,8 +26,9 @@
#include "arch/arm/registers_arm.h"
#include "base/casts.h"
#include "base/globals.h"
+#include "base/macros.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
// Defines constants and accessor classes to assemble, disassemble and
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
index 6e6d40d..699df80 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
@@ -29,7 +29,7 @@
using vixl::ExactAssemblyScope;
using vixl::CodeBufferCheckScope;
-namespace art {
+namespace art HIDDEN {
namespace arm {
#ifdef ___
@@ -155,7 +155,7 @@
// Pop LR to PC unless we need to emit some read barrier code just before returning.
bool emit_code_before_return =
- (kEmitCompilerReadBarrier && kUseBakerReadBarrier) &&
+ (gUseReadBarrier && kUseBakerReadBarrier) &&
(may_suspend || (kIsDebugBuild && emit_run_time_checks_in_debug_mode_));
if ((core_spill_mask & (1u << lr.GetCode())) != 0u && !emit_code_before_return) {
DCHECK_EQ(core_spill_mask & (1u << pc.GetCode()), 0u);
@@ -215,7 +215,9 @@
}
}
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ // Emit marking register refresh even with all GCs as we are still using the
+ // register due to nterp's dependency.
+ if (kReserveMarkingRegister) {
if (may_suspend) {
// The method may be suspended; refresh the Marking Register.
___ Ldr(mr, MemOperand(tr, Thread::IsGcMarkingOffset<kArmPointerSize>().Int32Value()));
@@ -428,8 +430,15 @@
asm_.StoreToOffset(kStoreWord, scratch, tr, thr_offs.Int32Value());
}
-void ArmVIXLJNIMacroAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs) {
- asm_.StoreToOffset(kStoreWord, sp, tr, thr_offs.Int32Value());
+void ArmVIXLJNIMacroAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs, bool tag_sp) {
+ if (tag_sp) {
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ vixl32::Register reg = temps.Acquire();
+ ___ Orr(reg, sp, 0x2);
+ asm_.StoreToOffset(kStoreWord, reg, tr, thr_offs.Int32Value());
+ } else {
+ asm_.StoreToOffset(kStoreWord, sp, tr, thr_offs.Int32Value());
+ }
}
void ArmVIXLJNIMacroAssembler::SignExtend(ManagedRegister mreg ATTRIBUTE_UNUSED,
@@ -869,6 +878,11 @@
}
}
+void ArmVIXLJNIMacroAssembler::Move(ManagedRegister mdst, size_t value) {
+ ArmManagedRegister dst = mdst.AsArm();
+ ___ Mov(AsVIXLRegister(dst), static_cast<uint32_t>(value));
+}
+
void ArmVIXLJNIMacroAssembler::Copy(FrameOffset dest, FrameOffset src, size_t size) {
DCHECK(size == 4 || size == 8) << size;
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
@@ -1165,7 +1179,7 @@
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
vixl32::Register test_reg;
DCHECK_EQ(Thread::IsGcMarkingSize(), 4u);
- DCHECK(kUseReadBarrier);
+ DCHECK(gUseReadBarrier);
if (kUseBakerReadBarrier) {
// TestGcMarking() is used in the JNI stub entry when the marking register is up to date.
if (kIsDebugBuild && emit_run_time_checks_in_debug_mode_) {
@@ -1213,6 +1227,14 @@
}
}
+void ArmVIXLJNIMacroAssembler::TestByteAndJumpIfNotZero(uintptr_t address, JNIMacroLabel* label) {
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ vixl32::Register scratch = temps.Acquire();
+ ___ Mov(scratch, static_cast<uint32_t>(address));
+ ___ Ldrb(scratch, MemOperand(scratch, 0));
+ ___ CompareAndBranchIfNonZero(scratch, ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
+}
+
void ArmVIXLJNIMacroAssembler::Bind(JNIMacroLabel* label) {
CHECK(label != nullptr);
___ Bind(ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.h b/compiler/utils/arm/jni_macro_assembler_arm_vixl.h
index ed453ae..df14d04 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.h
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.h
@@ -29,7 +29,7 @@
#include "utils/assembler.h"
#include "utils/jni_macro_assembler.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
class ArmVIXLJNIMacroAssembler final
@@ -70,7 +70,7 @@
void StoreStackOffsetToThread(ThreadOffset32 thr_offs, FrameOffset fr_offs) override;
- void StoreStackPointerToThread(ThreadOffset32 thr_offs) override;
+ void StoreStackPointerToThread(ThreadOffset32 thr_offs, bool tag_sp) override;
void StoreSpanning(FrameOffset dest, ManagedRegister src, FrameOffset in_off) override;
@@ -100,6 +100,8 @@
void Move(ManagedRegister dest, ManagedRegister src, size_t size) override;
+ void Move(ManagedRegister dest, size_t value) override;
+
void CopyRawPtrFromThread(FrameOffset fr_offs, ThreadOffset32 thr_offs) override;
void CopyRawPtrToThread(ThreadOffset32 thr_offs,
@@ -213,6 +215,8 @@
void TestGcMarking(JNIMacroLabel* label, JNIMacroUnaryCondition cond) override;
// Emit a conditional jump to the label by applying a unary condition test to object's mark bit.
void TestMarkBit(ManagedRegister ref, JNIMacroLabel* label, JNIMacroUnaryCondition cond) override;
+ // Emit a conditional jump to label if the loaded value from specified locations is not zero.
+ void TestByteAndJumpIfNotZero(uintptr_t address, JNIMacroLabel* label) override;
// Code at this offset will serve as the target for the Jump call.
void Bind(JNIMacroLabel* label) override;
diff --git a/compiler/utils/arm/managed_register_arm.cc b/compiler/utils/arm/managed_register_arm.cc
index deff658..07d50da 100644
--- a/compiler/utils/arm/managed_register_arm.cc
+++ b/compiler/utils/arm/managed_register_arm.cc
@@ -18,7 +18,7 @@
#include "base/globals.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
// Returns true if this managed-register overlaps the other managed-register.
diff --git a/compiler/utils/arm/managed_register_arm.h b/compiler/utils/arm/managed_register_arm.h
index 6d942fa..b3d436c 100644
--- a/compiler/utils/arm/managed_register_arm.h
+++ b/compiler/utils/arm/managed_register_arm.h
@@ -19,10 +19,11 @@
#include <android-base/logging.h>
+#include "base/macros.h"
#include "constants_arm.h"
#include "utils/managed_register.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
// Values for register pairs.
diff --git a/compiler/utils/arm/managed_register_arm_test.cc b/compiler/utils/arm/managed_register_arm_test.cc
index 6f440a7..60f6090 100644
--- a/compiler/utils/arm/managed_register_arm_test.cc
+++ b/compiler/utils/arm/managed_register_arm_test.cc
@@ -16,9 +16,10 @@
#include "managed_register_arm.h"
#include "base/globals.h"
+#include "base/macros.h"
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
TEST(ArmManagedRegister, NoRegister) {
diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc
index 6100ed9..cc50883 100644
--- a/compiler/utils/arm64/assembler_arm64.cc
+++ b/compiler/utils/arm64/assembler_arm64.cc
@@ -24,7 +24,7 @@
using namespace vixl::aarch64; // NOLINT(build/namespaces)
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
#ifdef ___
@@ -187,9 +187,7 @@
}
void Arm64Assembler::GenerateMarkingRegisterCheck(Register temp, int code) {
- // The Marking Register is only used in the Baker read barrier configuration.
- DCHECK(kEmitCompilerReadBarrier);
- DCHECK(kUseBakerReadBarrier);
+ DCHECK(kReserveMarkingRegister);
vixl::aarch64::Register mr = reg_x(MR); // Marking Register.
vixl::aarch64::Register tr = reg_x(TR); // Thread Register.
diff --git a/compiler/utils/arm64/assembler_arm64.h b/compiler/utils/arm64/assembler_arm64.h
index b49a13a..62f807f 100644
--- a/compiler/utils/arm64/assembler_arm64.h
+++ b/compiler/utils/arm64/assembler_arm64.h
@@ -38,7 +38,7 @@
#include "aarch64/macro-assembler-aarch64.h"
#pragma GCC diagnostic pop
-namespace art {
+namespace art HIDDEN {
class Arm64InstructionSetFeatures;
diff --git a/compiler/utils/arm64/jni_macro_assembler_arm64.cc b/compiler/utils/arm64/jni_macro_assembler_arm64.cc
index 50ca468..27d275f 100644
--- a/compiler/utils/arm64/jni_macro_assembler_arm64.cc
+++ b/compiler/utils/arm64/jni_macro_assembler_arm64.cc
@@ -24,7 +24,7 @@
using namespace vixl::aarch64; // NOLINT(build/namespaces)
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
#ifdef ___
@@ -218,10 +218,13 @@
___ Str(scratch, MEM_OP(reg_x(TR), tr_offs.Int32Value()));
}
-void Arm64JNIMacroAssembler::StoreStackPointerToThread(ThreadOffset64 tr_offs) {
+void Arm64JNIMacroAssembler::StoreStackPointerToThread(ThreadOffset64 tr_offs, bool tag_sp) {
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
Register scratch = temps.AcquireX();
___ Mov(scratch, reg_x(SP));
+ if (tag_sp) {
+ ___ Orr(scratch, scratch, 0x2);
+ }
___ Str(scratch, MEM_OP(reg_x(TR), tr_offs.Int32Value()));
}
@@ -640,6 +643,12 @@
}
}
+void Arm64JNIMacroAssembler::Move(ManagedRegister m_dst, size_t value) {
+ Arm64ManagedRegister dst = m_dst.AsArm64();
+ DCHECK(dst.IsXRegister());
+ ___ Mov(reg_x(dst.AsXRegister()), value);
+}
+
void Arm64JNIMacroAssembler::CopyRawPtrFromThread(FrameOffset fr_offs, ThreadOffset64 tr_offs) {
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
Register scratch = temps.AcquireX();
@@ -989,7 +998,7 @@
UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
Register test_reg;
DCHECK_EQ(Thread::IsGcMarkingSize(), 4u);
- DCHECK(kUseReadBarrier);
+ DCHECK(gUseReadBarrier);
if (kUseBakerReadBarrier) {
// TestGcMarking() is used in the JNI stub entry when the marking register is up to date.
if (kIsDebugBuild && emit_run_time_checks_in_debug_mode_) {
@@ -1037,6 +1046,14 @@
}
}
+void Arm64JNIMacroAssembler::TestByteAndJumpIfNotZero(uintptr_t address, JNIMacroLabel* label) {
+ UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+ Register scratch = temps.AcquireX();
+ ___ Mov(scratch, address);
+ ___ Ldrb(scratch.W(), MEM_OP(scratch, 0));
+ ___ Cbnz(scratch.W(), Arm64JNIMacroLabel::Cast(label)->AsArm64());
+}
+
void Arm64JNIMacroAssembler::Bind(JNIMacroLabel* label) {
CHECK(label != nullptr);
___ Bind(Arm64JNIMacroLabel::Cast(label)->AsArm64());
@@ -1107,7 +1124,9 @@
asm_.UnspillRegisters(core_reg_list, frame_size - core_reg_size);
asm_.UnspillRegisters(fp_reg_list, frame_size - core_reg_size - fp_reg_size);
- if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
+ // Emit marking register refresh even with all GCs as we are still using the
+ // register due to nterp's dependency.
+ if (kReserveMarkingRegister) {
vixl::aarch64::Register mr = reg_x(MR); // Marking Register.
vixl::aarch64::Register tr = reg_x(TR); // Thread Register.
diff --git a/compiler/utils/arm64/jni_macro_assembler_arm64.h b/compiler/utils/arm64/jni_macro_assembler_arm64.h
index 2c04184..3f6a4fb 100644
--- a/compiler/utils/arm64/jni_macro_assembler_arm64.h
+++ b/compiler/utils/arm64/jni_macro_assembler_arm64.h
@@ -37,7 +37,7 @@
#include "aarch64/macro-assembler-aarch64.h"
#pragma GCC diagnostic pop
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
class Arm64JNIMacroAssembler final : public JNIMacroAssemblerFwd<Arm64Assembler, PointerSize::k64> {
@@ -72,7 +72,7 @@
void StoreRawPtr(FrameOffset dest, ManagedRegister src) override;
void StoreImmediateToFrame(FrameOffset dest, uint32_t imm) override;
void StoreStackOffsetToThread(ThreadOffset64 thr_offs, FrameOffset fr_offs) override;
- void StoreStackPointerToThread(ThreadOffset64 thr_offs) override;
+ void StoreStackPointerToThread(ThreadOffset64 thr_offs, bool tag_sp) override;
void StoreSpanning(FrameOffset dest, ManagedRegister src, FrameOffset in_off) override;
// Load routines.
@@ -92,6 +92,7 @@
ArrayRef<ArgumentLocation> srcs,
ArrayRef<FrameOffset> refs) override;
void Move(ManagedRegister dest, ManagedRegister src, size_t size) override;
+ void Move(ManagedRegister dest, size_t value) override;
void CopyRawPtrFromThread(FrameOffset fr_offs, ThreadOffset64 thr_offs) override;
void CopyRawPtrToThread(ThreadOffset64 thr_offs, FrameOffset fr_offs, ManagedRegister scratch)
override;
@@ -197,6 +198,8 @@
void TestGcMarking(JNIMacroLabel* label, JNIMacroUnaryCondition cond) override;
// Emit a conditional jump to the label by applying a unary condition test to object's mark bit.
void TestMarkBit(ManagedRegister ref, JNIMacroLabel* label, JNIMacroUnaryCondition cond) override;
+ // Emit a conditional jump to label if the loaded value from specified locations is not zero.
+ void TestByteAndJumpIfNotZero(uintptr_t address, JNIMacroLabel* label) override;
// Code at this offset will serve as the target for the Jump call.
void Bind(JNIMacroLabel* label) override;
diff --git a/compiler/utils/arm64/managed_register_arm64.cc b/compiler/utils/arm64/managed_register_arm64.cc
index 5632265..74a3545 100644
--- a/compiler/utils/arm64/managed_register_arm64.cc
+++ b/compiler/utils/arm64/managed_register_arm64.cc
@@ -17,7 +17,7 @@
#include "managed_register_arm64.h"
#include "base/globals.h"
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
// TODO: Define convention
diff --git a/compiler/utils/arm64/managed_register_arm64.h b/compiler/utils/arm64/managed_register_arm64.h
index 8a06f63..7e8c976 100644
--- a/compiler/utils/arm64/managed_register_arm64.h
+++ b/compiler/utils/arm64/managed_register_arm64.h
@@ -20,9 +20,10 @@
#include <android-base/logging.h>
#include "arch/arm64/registers_arm64.h"
+#include "base/macros.h"
#include "utils/managed_register.h"
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
const int kNumberOfXRegIds = kNumberOfXRegisters;
diff --git a/compiler/utils/arm64/managed_register_arm64_test.cc b/compiler/utils/arm64/managed_register_arm64_test.cc
index d151ac9..f250360 100644
--- a/compiler/utils/arm64/managed_register_arm64_test.cc
+++ b/compiler/utils/arm64/managed_register_arm64_test.cc
@@ -18,9 +18,10 @@
#include "assembler_arm64.h"
#include "base/globals.h"
+#include "base/macros.h"
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
namespace arm64 {
TEST(Arm64ManagedRegister, NoRegister) {
diff --git a/compiler/utils/assembler.cc b/compiler/utils/assembler.cc
index d1d2a3d..b82f0dc 100644
--- a/compiler/utils/assembler.cc
+++ b/compiler/utils/assembler.cc
@@ -23,7 +23,7 @@
#include "base/globals.h"
#include "base/memory_region.h"
-namespace art {
+namespace art HIDDEN {
AssemblerBuffer::AssemblerBuffer(ArenaAllocator* allocator)
: allocator_(allocator) {
diff --git a/compiler/utils/assembler.h b/compiler/utils/assembler.h
index 4b4fb14..13a5d9f 100644
--- a/compiler/utils/assembler.h
+++ b/compiler/utils/assembler.h
@@ -37,7 +37,7 @@
#include "x86/constants_x86.h"
#include "x86_64/constants_x86_64.h"
-namespace art {
+namespace art HIDDEN {
class Assembler;
class AssemblerBuffer;
diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h
index bb22fe5..d03e5a7 100644
--- a/compiler/utils/assembler_test.h
+++ b/compiler/utils/assembler_test.h
@@ -26,11 +26,12 @@
#include <fstream>
#include <iterator>
+#include "base/macros.h"
#include "base/malloc_arena_pool.h"
#include "assembler_test_base.h"
#include "common_runtime_test.h" // For ScratchFile
-namespace art {
+namespace art HIDDEN {
// Helper for a constexpr string length.
constexpr size_t ConstexprStrLen(char const* str, size_t count = 0) {
@@ -59,7 +60,7 @@
return assembler_.get();
}
- typedef std::string (*TestFn)(AssemblerTest* assembler_test, Ass* assembler);
+ using TestFn = std::string (*)(AssemblerTest *, Ass *);
void DriverFn(TestFn f, const std::string& test_name) {
DriverWrapper(f(this, assembler_.get()), test_name);
@@ -259,7 +260,7 @@
std::string (AssemblerTest::*GetName1)(const Reg1&),
std::string (AssemblerTest::*GetName2)(const Reg2&),
std::string (AssemblerTest::*GetName3)(const Reg3&),
- std::string fmt,
+ const std::string& fmt,
int bias) {
std::string str;
std::vector<int64_t> imms = CreateImmediateValuesBits(abs(imm_bits), (imm_bits > 0));
diff --git a/compiler/utils/assembler_test_base.h b/compiler/utils/assembler_test_base.h
index bf73808..a342d9a 100644
--- a/compiler/utils/assembler_test_base.h
+++ b/compiler/utils/assembler_test_base.h
@@ -26,6 +26,7 @@
#include "android-base/strings.h"
+#include "base/macros.h"
#include "base/os.h"
#include "base/utils.h"
#include "common_runtime_test.h" // For ScratchDir.
@@ -34,7 +35,7 @@
#include "exec_utils.h"
#include "stream/file_output_stream.h"
-namespace art {
+namespace art HIDDEN {
// If you want to take a look at the differences between the ART assembler and clang,
// set this flag to true. The disassembled files will then remain in the tmp directory.
@@ -59,7 +60,7 @@
// This is intended to be run as a test.
bool CheckTools() {
- for (auto cmd : { GetAssemblerCommand()[0], GetDisassemblerCommand()[0] }) {
+ for (const std::string& cmd : { GetAssemblerCommand()[0], GetDisassemblerCommand()[0] }) {
if (!OS::FileExists(cmd.c_str())) {
LOG(ERROR) << "Could not find " << cmd;
return false;
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index b2d4dcd..7d08426 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -30,10 +30,11 @@
#include "utils/assembler_test_base.h"
#include "base/hex_dump.h"
+#include "base/macros.h"
#include "base/malloc_arena_pool.h"
#include "common_runtime_test.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
// Include results file (generated manually)
@@ -159,7 +160,8 @@
__ StoreRef(FrameOffset(48), scratch_register);
__ StoreSpanning(FrameOffset(48), method_register, FrameOffset(48));
__ StoreStackOffsetToThread(ThreadOffset32(512), FrameOffset(4096));
- __ StoreStackPointerToThread(ThreadOffset32(512));
+ __ StoreStackPointerToThread(ThreadOffset32(512), false);
+ __ StoreStackPointerToThread(ThreadOffset32(512), true);
// Other
__ Call(method_register, FrameOffset(48));
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index b6c6025..b220068 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -1,258 +1,260 @@
const char* const VixlJniHelpersResults = {
- " 0: 2d e9 e0 4d push.w {r5, r6, r7, r8, r10, r11, lr}\n"
- " 4: 2d ed 10 8a vpush {s16, s17, s18, s19, s20, s21, s22, s23, s24, s25, s26, s27, s28, s29, s30, s31}\n"
- " 8: 81 b0 sub sp, #4\n"
- " a: 00 90 str r0, [sp]\n"
- " c: 19 91 str r1, [sp, #100]\n"
- " e: 8d ed 1a 0a vstr s0, [sp, #104]\n"
- " 12: 1b 92 str r2, [sp, #108]\n"
- " 14: 1c 93 str r3, [sp, #112]\n"
- " 16: 88 b0 sub sp, #32\n"
- " 18: ad f5 80 5d sub.w sp, sp, #4096\n"
- " 1c: 08 98 ldr r0, [sp, #32]\n"
- " 1e: 1f 98 ldr r0, [sp, #124]\n"
- " 20: 21 98 ldr r0, [sp, #132]\n"
- " 22: ff 98 ldr r0, [sp, #1020]\n"
- " 24: dd f8 00 04 ldr.w r0, [sp, #1024]\n"
- " 28: dd f8 fc cf ldr.w r12, [sp, #4092]\n"
- " 2c: 0d f5 80 5c add.w r12, sp, #4096\n"
- " 30: dc f8 00 c0 ldr.w r12, [r12]\n"
- " 34: d9 f8 00 c2 ldr.w r12, [r9, #512]\n"
- " 38: dc f8 80 00 ldr.w r0, [r12, #128]\n"
- " 3c: 08 90 str r0, [sp, #32]\n"
- " 3e: 1f 90 str r0, [sp, #124]\n"
- " 40: 21 90 str r0, [sp, #132]\n"
- " 42: ff 90 str r0, [sp, #1020]\n"
- " 44: cd f8 00 04 str.w r0, [sp, #1024]\n"
- " 48: cd f8 fc cf str.w r12, [sp, #4092]\n"
- " 4c: 4d f8 04 5d str r5, [sp, #-4]!\n"
- " 50: 0d f5 80 55 add.w r5, sp, #4096\n"
- " 54: c5 f8 04 c0 str.w r12, [r5, #4]\n"
- " 58: 5d f8 04 5b ldr r5, [sp], #4\n"
- " 5c: 4f f0 ff 0c mov.w r12, #255\n"
- " 60: cd f8 30 c0 str.w r12, [sp, #48]\n"
- " 64: 6f f0 7f 4c mvn r12, #4278190080\n"
- " 68: cd f8 30 c0 str.w r12, [sp, #48]\n"
- " 6c: cd f8 30 c0 str.w r12, [sp, #48]\n"
- " 70: cd f8 30 c0 str.w r12, [sp, #48]\n"
- " 74: 0c 90 str r0, [sp, #48]\n"
- " 76: dd f8 30 c0 ldr.w r12, [sp, #48]\n"
- " 7a: cd f8 34 c0 str.w r12, [sp, #52]\n"
- " 7e: 0d f5 80 5c add.w r12, sp, #4096\n"
- " 82: c9 f8 00 c2 str.w r12, [r9, #512]\n"
- " 86: c9 f8 00 d2 str.w sp, [r9, #512]\n"
- " 8a: d0 f8 30 e0 ldr.w lr, [r0, #48]\n"
- " 8e: f0 47 blx lr\n"
- " 90: dd f8 2c c0 ldr.w r12, [sp, #44]\n"
- " 94: cd f8 30 c0 str.w r12, [sp, #48]\n"
- " 98: d9 f8 00 c2 ldr.w r12, [r9, #512]\n"
- " 9c: cd f8 2c c0 str.w r12, [sp, #44]\n"
- " a0: dd f8 2c c0 ldr.w r12, [sp, #44]\n"
- " a4: cd f8 30 c0 str.w r12, [sp, #48]\n"
- " a8: 48 46 mov r0, r9\n"
- " aa: cd f8 30 90 str.w r9, [sp, #48]\n"
- " ae: 04 46 mov r4, r0\n"
- " b0: 0d f1 30 0c add.w r12, sp, #48\n"
- " b4: bb f1 00 0f cmp.w r11, #0\n"
- " b8: 18 bf it ne\n"
- " ba: e3 46 movne r11, r12\n"
- " bc: 0d f1 30 0b add.w r11, sp, #48\n"
- " c0: 5f ea 0b 00 movs.w r0, r11\n"
- " c4: 18 bf it ne\n"
- " c6: 0c a8 addne r0, sp, #48\n"
- " c8: dd f8 40 c0 ldr.w r12, [sp, #64]\n"
- " cc: bc f1 00 0f cmp.w r12, #0\n"
- " d0: 18 bf it ne\n"
- " d2: 0d f1 40 0c addne.w r12, sp, #64\n"
- " d6: cd f8 30 c0 str.w r12, [sp, #48]\n"
- " da: 5f ea 0b 00 movs.w r0, r11\n"
- " de: 18 bf it ne\n"
- " e0: 00 a8 addne r0, sp, #0\n"
- " e2: 0d f2 04 40 addw r0, sp, #1028\n"
- " e6: bb f1 00 0f cmp.w r11, #0\n"
- " ea: 08 bf it eq\n"
- " ec: 58 46 moveq r0, r11\n"
- " ee: 0d f2 04 4c addw r12, sp, #1028\n"
- " f2: bb f1 00 0f cmp.w r11, #0\n"
- " f6: 18 bf it ne\n"
- " f8: e3 46 movne r11, r12\n"
- " fa: d9 f8 94 c0 ldr.w r12, [r9, #148]\n"
- " fe: bc f1 00 0f cmp.w r12, #0\n"
- " 102: 71 d1 bne 0x1e8 @ imm = #226\n"
- " 104: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 108: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 10c: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 110: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 114: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 118: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 11c: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 120: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 124: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 128: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 12c: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 130: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 134: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 138: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 13c: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 140: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 144: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 148: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 14c: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 150: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 154: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 158: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 15c: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 160: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 164: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 168: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 16c: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 170: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 174: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 178: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 17c: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 180: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 184: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 188: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 18c: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 190: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 194: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 198: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 19c: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1a0: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1a4: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1a8: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1ac: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1b0: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1b4: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1b8: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1bc: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1c0: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1c4: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1c8: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1cc: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1d0: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1d4: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1d8: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1dc: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1e0: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1e4: 00 f0 02 b8 b.w 0x1ec @ imm = #4\n"
- " 1e8: 00 f0 1b b8 b.w 0x222 @ imm = #54\n"
- " 1ec: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1f0: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1f4: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1f8: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 1fc: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 200: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 204: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 208: cd f8 ff c7 str.w r12, [sp, #2047]\n"
- " 20c: 0d f5 80 5d add.w sp, sp, #4096\n"
- " 210: 08 b0 add sp, #32\n"
- " 212: 01 b0 add sp, #4\n"
- " 214: bd ec 10 8a vpop {s16, s17, s18, s19, s20, s21, s22, s23, s24, s25, s26, s27, s28, s29, s30, s31}\n"
- " 218: bd e8 e0 4d pop.w {r5, r6, r7, r8, r10, r11, lr}\n"
- " 21c: d9 f8 24 80 ldr.w r8, [r9, #36]\n"
- " 220: 70 47 bx lr\n"
- " 222: d9 f8 94 00 ldr.w r0, [r9, #148]\n"
- " 226: d9 f8 c8 e2 ldr.w lr, [r9, #712]\n"
- " 22a: f0 47 blx lr\n"
+ " 0: e92d 4de0 push.w {r5, r6, r7, r8, r10, r11, lr}\n"
+ " 4: ed2d 8a10 vpush {s16, s17, s18, s19, s20, s21, s22, s23, s24, s25, s26, s27, s28, s29, s30, s31}\n"
+ " 8: b081 sub sp, #4\n"
+ " a: 9000 str r0, [sp]\n"
+ " c: 9119 str r1, [sp, #100]\n"
+ " e: ed8d 0a1a vstr s0, [sp, #104]\n"
+ " 12: 921b str r2, [sp, #108]\n"
+ " 14: 931c str r3, [sp, #112]\n"
+ " 16: b088 sub sp, #32\n"
+ " 18: f5ad 5d80 sub.w sp, sp, #4096\n"
+ " 1c: 9808 ldr r0, [sp, #32]\n"
+ " 1e: 981f ldr r0, [sp, #124]\n"
+ " 20: 9821 ldr r0, [sp, #132]\n"
+ " 22: 98ff ldr r0, [sp, #1020]\n"
+ " 24: f8dd 0400 ldr.w r0, [sp, #1024]\n"
+ " 28: f8dd cffc ldr.w r12, [sp, #4092]\n"
+ " 2c: f50d 5c80 add.w r12, sp, #4096\n"
+ " 30: f8dc c000 ldr.w r12, [r12]\n"
+ " 34: f8d9 c200 ldr.w r12, [r9, #512]\n"
+ " 38: f8dc 0080 ldr.w r0, [r12, #128]\n"
+ " 3c: 9008 str r0, [sp, #32]\n"
+ " 3e: 901f str r0, [sp, #124]\n"
+ " 40: 9021 str r0, [sp, #132]\n"
+ " 42: 90ff str r0, [sp, #1020]\n"
+ " 44: f8cd 0400 str.w r0, [sp, #1024]\n"
+ " 48: f8cd cffc str.w r12, [sp, #4092]\n"
+ " 4c: f84d 5d04 str r5, [sp, #-4]!\n"
+ " 50: f50d 5580 add.w r5, sp, #4096\n"
+ " 54: f8c5 c004 str.w r12, [r5, #4]\n"
+ " 58: f85d 5b04 ldr r5, [sp], #4\n"
+ " 5c: f04f 0cff mov.w r12, #255\n"
+ " 60: f8cd c030 str.w r12, [sp, #48]\n"
+ " 64: f06f 4c7f mvn r12, #4278190080\n"
+ " 68: f8cd c030 str.w r12, [sp, #48]\n"
+ " 6c: f8cd c030 str.w r12, [sp, #48]\n"
+ " 70: f8cd c030 str.w r12, [sp, #48]\n"
+ " 74: 900c str r0, [sp, #48]\n"
+ " 76: f8dd c030 ldr.w r12, [sp, #48]\n"
+ " 7a: f8cd c034 str.w r12, [sp, #52]\n"
+ " 7e: f50d 5c80 add.w r12, sp, #4096\n"
+ " 82: f8c9 c200 str.w r12, [r9, #512]\n"
+ " 86: f8c9 d200 str.w sp, [r9, #512]\n"
+ " 8a: f04d 0c02 orr r12, sp, #2\n"
+ " 8e: f8c9 c200 str.w r12, [r9, #512]\n"
+ " 92: f8d0 e030 ldr.w lr, [r0, #48]\n"
+ " 96: 47f0 blx lr\n"
+ " 98: f8dd c02c ldr.w r12, [sp, #44]\n"
+ " 9c: f8cd c030 str.w r12, [sp, #48]\n"
+ " a0: f8d9 c200 ldr.w r12, [r9, #512]\n"
+ " a4: f8cd c02c str.w r12, [sp, #44]\n"
+ " a8: f8dd c02c ldr.w r12, [sp, #44]\n"
+ " ac: f8cd c030 str.w r12, [sp, #48]\n"
+ " b0: 4648 mov r0, r9\n"
+ " b2: f8cd 9030 str.w r9, [sp, #48]\n"
+ " b6: 4604 mov r4, r0\n"
+ " b8: f10d 0c30 add.w r12, sp, #48\n"
+ " bc: f1bb 0f00 cmp.w r11, #0\n"
+ " c0: bf18 it ne\n"
+ " c2: 46e3 movne r11, r12\n"
+ " c4: f10d 0b30 add.w r11, sp, #48\n"
+ " c8: ea5f 000b movs.w r0, r11\n"
+ " cc: bf18 it ne\n"
+ " ce: a80c addne r0, sp, #48\n"
+ " d0: f8dd c040 ldr.w r12, [sp, #64]\n"
+ " d4: f1bc 0f00 cmp.w r12, #0\n"
+ " d8: bf18 it ne\n"
+ " da: f10d 0c40 addne.w r12, sp, #64\n"
+ " de: f8cd c030 str.w r12, [sp, #48]\n"
+ " e2: ea5f 000b movs.w r0, r11\n"
+ " e6: bf18 it ne\n"
+ " e8: a800 addne r0, sp, #0\n"
+ " ea: f20d 4004 addw r0, sp, #1028\n"
+ " ee: f1bb 0f00 cmp.w r11, #0\n"
+ " f2: bf08 it eq\n"
+ " f4: 4658 moveq r0, r11\n"
+ " f6: f20d 4c04 addw r12, sp, #1028\n"
+ " fa: f1bb 0f00 cmp.w r11, #0\n"
+ " fe: bf18 it ne\n"
+ " 100: 46e3 movne r11, r12\n"
+ " 102: f8d9 c09c ldr.w r12, [r9, #156]\n"
+ " 106: f1bc 0f00 cmp.w r12, #0\n"
+ " 10a: d171 bne 0x1f0 @ imm = #226\n"
+ " 10c: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 110: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 114: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 118: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 11c: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 120: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 124: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 128: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 12c: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 130: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 134: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 138: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 13c: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 140: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 144: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 148: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 14c: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 150: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 154: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 158: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 15c: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 160: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 164: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 168: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 16c: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 170: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 174: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 178: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 17c: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 180: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 184: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 188: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 18c: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 190: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 194: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 198: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 19c: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1a0: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1a4: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1a8: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1ac: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1b0: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1b4: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1b8: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1bc: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1c0: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1c4: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1c8: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1cc: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1d0: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1d4: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1d8: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1dc: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1e0: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1e4: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1e8: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1ec: f000 b802 b.w 0x1f4 @ imm = #4\n"
+ " 1f0: f000 b81b b.w 0x22a @ imm = #54\n"
+ " 1f4: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1f8: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 1fc: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 200: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 204: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 208: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 20c: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 210: f8cd c7ff str.w r12, [sp, #2047]\n"
+ " 214: f50d 5d80 add.w sp, sp, #4096\n"
+ " 218: b008 add sp, #32\n"
+ " 21a: b001 add sp, #4\n"
+ " 21c: ecbd 8a10 vpop {s16, s17, s18, s19, s20, s21, s22, s23, s24, s25, s26, s27, s28, s29, s30, s31}\n"
+ " 220: e8bd 4de0 pop.w {r5, r6, r7, r8, r10, r11, lr}\n"
+ " 224: f8d9 8024 ldr.w r8, [r9, #36]\n"
+ " 228: 4770 bx lr\n"
+ " 22a: f8d9 009c ldr.w r0, [r9, #156]\n"
+ " 22e: f8d9 e2d4 ldr.w lr, [r9, #724]\n"
+ " 232: 47f0 blx lr\n"
};
const char* const VixlLoadFromOffsetResults = {
- " 0: e2 68 ldr r2, [r4, #12]\n"
- " 2: d4 f8 ff 2f ldr.w r2, [r4, #4095]\n"
- " 6: 04 f5 80 52 add.w r2, r4, #4096\n"
- " a: 12 68 ldr r2, [r2]\n"
- " c: 04 f5 80 12 add.w r2, r4, #1048576\n"
- " 10: d2 f8 a4 20 ldr.w r2, [r2, #164]\n"
- " 14: 4f f4 80 52 mov.w r2, #4096\n"
- " 18: c0 f2 10 02 movt r2, #16\n"
- " 1c: 22 44 add r2, r4\n"
- " 1e: 12 68 ldr r2, [r2]\n"
- " 20: 4f f4 80 5c mov.w r12, #4096\n"
- " 24: c0 f2 10 0c movt r12, #16\n"
- " 28: 64 44 add r4, r12\n"
- " 2a: 24 68 ldr r4, [r4]\n"
- " 2c: a2 89 ldrh r2, [r4, #12]\n"
- " 2e: b4 f8 ff 2f ldrh.w r2, [r4, #4095]\n"
- " 32: 04 f5 80 52 add.w r2, r4, #4096\n"
- " 36: 12 88 ldrh r2, [r2]\n"
- " 38: 04 f5 80 12 add.w r2, r4, #1048576\n"
- " 3c: b2 f8 a4 20 ldrh.w r2, [r2, #164]\n"
- " 40: 4f f4 80 52 mov.w r2, #4096\n"
- " 44: c0 f2 10 02 movt r2, #16\n"
- " 48: 22 44 add r2, r4\n"
- " 4a: 12 88 ldrh r2, [r2]\n"
- " 4c: 4f f4 80 5c mov.w r12, #4096\n"
- " 50: c0 f2 10 0c movt r12, #16\n"
- " 54: 64 44 add r4, r12\n"
- " 56: 24 88 ldrh r4, [r4]\n"
- " 58: d4 e9 03 23 ldrd r2, r3, [r4, #12]\n"
- " 5c: d4 e9 ff 23 ldrd r2, r3, [r4, #1020]\n"
- " 60: 04 f5 80 62 add.w r2, r4, #1024\n"
- " 64: d2 e9 00 23 ldrd r2, r3, [r2]\n"
- " 68: 04 f5 80 22 add.w r2, r4, #262144\n"
- " 6c: d2 e9 29 23 ldrd r2, r3, [r2, #164]\n"
- " 70: 4f f4 80 62 mov.w r2, #1024\n"
- " 74: c0 f2 04 02 movt r2, #4\n"
- " 78: 22 44 add r2, r4\n"
- " 7a: d2 e9 00 23 ldrd r2, r3, [r2]\n"
- " 7e: 4f f4 80 6c mov.w r12, #1024\n"
- " 82: c0 f2 04 0c movt r12, #4\n"
- " 86: 64 44 add r4, r12\n"
- " 88: d4 e9 00 45 ldrd r4, r5, [r4]\n"
- " 8c: dc f8 0c 00 ldr.w r0, [r12, #12]\n"
- " 90: a4 f5 80 12 sub.w r2, r4, #1048576\n"
- " 94: d2 f8 a4 20 ldr.w r2, [r2, #164]\n"
- " 98: 94 f9 0c 20 ldrsb.w r2, [r4, #12]\n"
- " 9c: 22 7b ldrb r2, [r4, #12]\n"
- " 9e: b4 f9 0c 20 ldrsh.w r2, [r4, #12]\n"
+ " 0: 68e2 ldr r2, [r4, #12]\n"
+ " 2: f8d4 2fff ldr.w r2, [r4, #4095]\n"
+ " 6: f504 5280 add.w r2, r4, #4096\n"
+ " a: 6812 ldr r2, [r2]\n"
+ " c: f504 1280 add.w r2, r4, #1048576\n"
+ " 10: f8d2 20a4 ldr.w r2, [r2, #164]\n"
+ " 14: f44f 5280 mov.w r2, #4096\n"
+ " 18: f2c0 0210 movt r2, #16\n"
+ " 1c: 4422 add r2, r4\n"
+ " 1e: 6812 ldr r2, [r2]\n"
+ " 20: f44f 5c80 mov.w r12, #4096\n"
+ " 24: f2c0 0c10 movt r12, #16\n"
+ " 28: 4464 add r4, r12\n"
+ " 2a: 6824 ldr r4, [r4]\n"
+ " 2c: 89a2 ldrh r2, [r4, #12]\n"
+ " 2e: f8b4 2fff ldrh.w r2, [r4, #4095]\n"
+ " 32: f504 5280 add.w r2, r4, #4096\n"
+ " 36: 8812 ldrh r2, [r2]\n"
+ " 38: f504 1280 add.w r2, r4, #1048576\n"
+ " 3c: f8b2 20a4 ldrh.w r2, [r2, #164]\n"
+ " 40: f44f 5280 mov.w r2, #4096\n"
+ " 44: f2c0 0210 movt r2, #16\n"
+ " 48: 4422 add r2, r4\n"
+ " 4a: 8812 ldrh r2, [r2]\n"
+ " 4c: f44f 5c80 mov.w r12, #4096\n"
+ " 50: f2c0 0c10 movt r12, #16\n"
+ " 54: 4464 add r4, r12\n"
+ " 56: 8824 ldrh r4, [r4]\n"
+ " 58: e9d4 2303 ldrd r2, r3, [r4, #12]\n"
+ " 5c: e9d4 23ff ldrd r2, r3, [r4, #1020]\n"
+ " 60: f504 6280 add.w r2, r4, #1024\n"
+ " 64: e9d2 2300 ldrd r2, r3, [r2]\n"
+ " 68: f504 2280 add.w r2, r4, #262144\n"
+ " 6c: e9d2 2329 ldrd r2, r3, [r2, #164]\n"
+ " 70: f44f 6280 mov.w r2, #1024\n"
+ " 74: f2c0 0204 movt r2, #4\n"
+ " 78: 4422 add r2, r4\n"
+ " 7a: e9d2 2300 ldrd r2, r3, [r2]\n"
+ " 7e: f44f 6c80 mov.w r12, #1024\n"
+ " 82: f2c0 0c04 movt r12, #4\n"
+ " 86: 4464 add r4, r12\n"
+ " 88: e9d4 4500 ldrd r4, r5, [r4]\n"
+ " 8c: f8dc 000c ldr.w r0, [r12, #12]\n"
+ " 90: f5a4 1280 sub.w r2, r4, #1048576\n"
+ " 94: f8d2 20a4 ldr.w r2, [r2, #164]\n"
+ " 98: f994 200c ldrsb.w r2, [r4, #12]\n"
+ " 9c: 7b22 ldrb r2, [r4, #12]\n"
+ " 9e: f9b4 200c ldrsh.w r2, [r4, #12]\n"
};
const char* const VixlStoreToOffsetResults = {
- " 0: e2 60 str r2, [r4, #12]\n"
- " 2: c4 f8 ff 2f str.w r2, [r4, #4095]\n"
- " 6: 04 f5 80 5c add.w r12, r4, #4096\n"
- " a: cc f8 00 20 str.w r2, [r12]\n"
- " e: 04 f5 80 1c add.w r12, r4, #1048576\n"
- " 12: cc f8 a4 20 str.w r2, [r12, #164]\n"
- " 16: 4f f4 80 5c mov.w r12, #4096\n"
- " 1a: c0 f2 10 0c movt r12, #16\n"
- " 1e: a4 44 add r12, r4\n"
- " 20: cc f8 00 20 str.w r2, [r12]\n"
- " 24: 4f f4 80 5c mov.w r12, #4096\n"
- " 28: c0 f2 10 0c movt r12, #16\n"
- " 2c: a4 44 add r12, r4\n"
- " 2e: cc f8 00 40 str.w r4, [r12]\n"
- " 32: a2 81 strh r2, [r4, #12]\n"
- " 34: a4 f8 ff 2f strh.w r2, [r4, #4095]\n"
- " 38: 04 f5 80 5c add.w r12, r4, #4096\n"
- " 3c: ac f8 00 20 strh.w r2, [r12]\n"
- " 40: 04 f5 80 1c add.w r12, r4, #1048576\n"
- " 44: ac f8 a4 20 strh.w r2, [r12, #164]\n"
- " 48: 4f f4 80 5c mov.w r12, #4096\n"
- " 4c: c0 f2 10 0c movt r12, #16\n"
- " 50: a4 44 add r12, r4\n"
- " 52: ac f8 00 20 strh.w r2, [r12]\n"
- " 56: 4f f4 80 5c mov.w r12, #4096\n"
- " 5a: c0 f2 10 0c movt r12, #16\n"
- " 5e: a4 44 add r12, r4\n"
- " 60: ac f8 00 40 strh.w r4, [r12]\n"
- " 64: c4 e9 03 23 strd r2, r3, [r4, #12]\n"
- " 68: c4 e9 ff 23 strd r2, r3, [r4, #1020]\n"
- " 6c: 04 f5 80 6c add.w r12, r4, #1024\n"
- " 70: cc e9 00 23 strd r2, r3, [r12]\n"
- " 74: 04 f5 80 2c add.w r12, r4, #262144\n"
- " 78: cc e9 29 23 strd r2, r3, [r12, #164]\n"
- " 7c: 4f f4 80 6c mov.w r12, #1024\n"
- " 80: c0 f2 04 0c movt r12, #4\n"
- " 84: a4 44 add r12, r4\n"
- " 86: cc e9 00 23 strd r2, r3, [r12]\n"
- " 8a: 4f f4 80 6c mov.w r12, #1024\n"
- " 8e: c0 f2 04 0c movt r12, #4\n"
- " 92: a4 44 add r12, r4\n"
- " 94: cc e9 00 45 strd r4, r5, [r12]\n"
- " 98: cc f8 0c 00 str.w r0, [r12, #12]\n"
- " 9c: a4 f5 80 1c sub.w r12, r4, #1048576\n"
- " a0: cc f8 a4 20 str.w r2, [r12, #164]\n"
- " a4: 22 73 strb r2, [r4, #12]\n"
+ " 0: 60e2 str r2, [r4, #12]\n"
+ " 2: f8c4 2fff str.w r2, [r4, #4095]\n"
+ " 6: f504 5c80 add.w r12, r4, #4096\n"
+ " a: f8cc 2000 str.w r2, [r12]\n"
+ " e: f504 1c80 add.w r12, r4, #1048576\n"
+ " 12: f8cc 20a4 str.w r2, [r12, #164]\n"
+ " 16: f44f 5c80 mov.w r12, #4096\n"
+ " 1a: f2c0 0c10 movt r12, #16\n"
+ " 1e: 44a4 add r12, r4\n"
+ " 20: f8cc 2000 str.w r2, [r12]\n"
+ " 24: f44f 5c80 mov.w r12, #4096\n"
+ " 28: f2c0 0c10 movt r12, #16\n"
+ " 2c: 44a4 add r12, r4\n"
+ " 2e: f8cc 4000 str.w r4, [r12]\n"
+ " 32: 81a2 strh r2, [r4, #12]\n"
+ " 34: f8a4 2fff strh.w r2, [r4, #4095]\n"
+ " 38: f504 5c80 add.w r12, r4, #4096\n"
+ " 3c: f8ac 2000 strh.w r2, [r12]\n"
+ " 40: f504 1c80 add.w r12, r4, #1048576\n"
+ " 44: f8ac 20a4 strh.w r2, [r12, #164]\n"
+ " 48: f44f 5c80 mov.w r12, #4096\n"
+ " 4c: f2c0 0c10 movt r12, #16\n"
+ " 50: 44a4 add r12, r4\n"
+ " 52: f8ac 2000 strh.w r2, [r12]\n"
+ " 56: f44f 5c80 mov.w r12, #4096\n"
+ " 5a: f2c0 0c10 movt r12, #16\n"
+ " 5e: 44a4 add r12, r4\n"
+ " 60: f8ac 4000 strh.w r4, [r12]\n"
+ " 64: e9c4 2303 strd r2, r3, [r4, #12]\n"
+ " 68: e9c4 23ff strd r2, r3, [r4, #1020]\n"
+ " 6c: f504 6c80 add.w r12, r4, #1024\n"
+ " 70: e9cc 2300 strd r2, r3, [r12]\n"
+ " 74: f504 2c80 add.w r12, r4, #262144\n"
+ " 78: e9cc 2329 strd r2, r3, [r12, #164]\n"
+ " 7c: f44f 6c80 mov.w r12, #1024\n"
+ " 80: f2c0 0c04 movt r12, #4\n"
+ " 84: 44a4 add r12, r4\n"
+ " 86: e9cc 2300 strd r2, r3, [r12]\n"
+ " 8a: f44f 6c80 mov.w r12, #1024\n"
+ " 8e: f2c0 0c04 movt r12, #4\n"
+ " 92: 44a4 add r12, r4\n"
+ " 94: e9cc 4500 strd r4, r5, [r12]\n"
+ " 98: f8cc 000c str.w r0, [r12, #12]\n"
+ " 9c: f5a4 1c80 sub.w r12, r4, #1048576\n"
+ " a0: f8cc 20a4 str.w r2, [r12, #164]\n"
+ " a4: 7322 strb r2, [r4, #12]\n"
};
diff --git a/compiler/utils/atomic_dex_ref_map-inl.h b/compiler/utils/atomic_dex_ref_map-inl.h
index 377b7fe..5f68a7c 100644
--- a/compiler/utils/atomic_dex_ref_map-inl.h
+++ b/compiler/utils/atomic_dex_ref_map-inl.h
@@ -21,12 +21,13 @@
#include <type_traits>
+#include "base/macros.h"
#include "dex/class_reference.h"
#include "dex/dex_file-inl.h"
#include "dex/method_reference.h"
#include "dex/type_reference.h"
-namespace art {
+namespace art HIDDEN {
template <typename DexFileReferenceType, typename Value>
inline size_t AtomicDexRefMap<DexFileReferenceType, Value>::NumberOfDexIndices(
diff --git a/compiler/utils/atomic_dex_ref_map.h b/compiler/utils/atomic_dex_ref_map.h
index a8c285f..b10fef5 100644
--- a/compiler/utils/atomic_dex_ref_map.h
+++ b/compiler/utils/atomic_dex_ref_map.h
@@ -19,10 +19,11 @@
#include "base/atomic.h"
#include "base/dchecked_vector.h"
+#include "base/macros.h"
#include "base/safe_map.h"
#include "dex/dex_file_reference.h"
-namespace art {
+namespace art HIDDEN {
class DexFile;
diff --git a/compiler/utils/atomic_dex_ref_map_test.cc b/compiler/utils/atomic_dex_ref_map_test.cc
index 864531e..329735b 100644
--- a/compiler/utils/atomic_dex_ref_map_test.cc
+++ b/compiler/utils/atomic_dex_ref_map_test.cc
@@ -18,12 +18,13 @@
#include <memory>
+#include "base/macros.h"
#include "common_runtime_test.h"
#include "dex/dex_file-inl.h"
#include "dex/method_reference.h"
#include "scoped_thread_state_change-inl.h"
-namespace art {
+namespace art HIDDEN {
class AtomicDexRefMapTest : public CommonRuntimeTest {};
diff --git a/compiler/utils/dedupe_set-inl.h b/compiler/utils/dedupe_set-inl.h
index d4a9cc8..db744c5 100644
--- a/compiler/utils/dedupe_set-inl.h
+++ b/compiler/utils/dedupe_set-inl.h
@@ -27,11 +27,12 @@
#include "android-base/stringprintf.h"
#include "base/hash_set.h"
+#include "base/macros.h"
#include "base/mutex.h"
#include "base/stl_util.h"
#include "base/time_utils.h"
-namespace art {
+namespace art HIDDEN {
template <typename InKey,
typename StoreKey,
diff --git a/compiler/utils/dedupe_set.h b/compiler/utils/dedupe_set.h
index a1ba208..42db8e3 100644
--- a/compiler/utils/dedupe_set.h
+++ b/compiler/utils/dedupe_set.h
@@ -23,7 +23,7 @@
#include "base/macros.h"
-namespace art {
+namespace art HIDDEN {
class Thread;
diff --git a/compiler/utils/dedupe_set_test.cc b/compiler/utils/dedupe_set_test.cc
index b390508..89385e7 100644
--- a/compiler/utils/dedupe_set_test.cc
+++ b/compiler/utils/dedupe_set_test.cc
@@ -21,11 +21,12 @@
#include <vector>
#include "base/array_ref.h"
+#include "base/macros.h"
#include "dedupe_set-inl.h"
#include "gtest/gtest.h"
#include "thread-current-inl.h"
-namespace art {
+namespace art HIDDEN {
class DedupeSetTestHashFunc {
public:
diff --git a/compiler/utils/jni_macro_assembler.cc b/compiler/utils/jni_macro_assembler.cc
index d6d49f8..f11b48d 100644
--- a/compiler/utils/jni_macro_assembler.cc
+++ b/compiler/utils/jni_macro_assembler.cc
@@ -35,7 +35,7 @@
#include "base/globals.h"
#include "base/memory_region.h"
-namespace art {
+namespace art HIDDEN {
using MacroAsm32UniquePtr = std::unique_ptr<JNIMacroAssembler<PointerSize::k32>>;
diff --git a/compiler/utils/jni_macro_assembler.h b/compiler/utils/jni_macro_assembler.h
index 7022e3d..79dd987 100644
--- a/compiler/utils/jni_macro_assembler.h
+++ b/compiler/utils/jni_macro_assembler.h
@@ -30,7 +30,7 @@
#include "managed_register.h"
#include "offsets.h"
-namespace art {
+namespace art HIDDEN {
class ArenaAllocator;
class DebugFrameOpCodeWriterForAssembler;
@@ -126,7 +126,11 @@
virtual void StoreStackOffsetToThread(ThreadOffset<kPointerSize> thr_offs,
FrameOffset fr_offs) = 0;
- virtual void StoreStackPointerToThread(ThreadOffset<kPointerSize> thr_offs) = 0;
+ // Stores stack pointer by tagging it if required so we can walk the stack. In debuggable runtimes
+ // we use tag to tell if we are using JITed code or AOT code. In non-debuggable runtimes we never
+ // use JITed code when AOT code is present. So checking for AOT code is sufficient to detect which
+ // code is being executed. We avoid tagging in non-debuggable runtimes to reduce instructions.
+ virtual void StoreStackPointerToThread(ThreadOffset<kPointerSize> thr_offs, bool tag_sp) = 0;
virtual void StoreSpanning(FrameOffset dest,
ManagedRegister src,
@@ -211,6 +215,8 @@
ManagedRegister scratch,
size_t size) = 0;
+ virtual void Move(ManagedRegister dst, size_t value) = 0;
+
virtual void MemoryBarrier(ManagedRegister scratch) = 0;
// Sign extension
@@ -282,6 +288,8 @@
virtual void TestMarkBit(ManagedRegister ref,
JNIMacroLabel* label,
JNIMacroUnaryCondition cond) = 0;
+ // Emit a conditional jump to label if the loaded value from specified locations is not zero.
+ virtual void TestByteAndJumpIfNotZero(uintptr_t address, JNIMacroLabel* label) = 0;
// Code at this offset will serve as the target for the Jump call.
virtual void Bind(JNIMacroLabel* label) = 0;
diff --git a/compiler/utils/jni_macro_assembler_test.h b/compiler/utils/jni_macro_assembler_test.h
index e77177e..ac8e7d3 100644
--- a/compiler/utils/jni_macro_assembler_test.h
+++ b/compiler/utils/jni_macro_assembler_test.h
@@ -20,6 +20,7 @@
#include "jni_macro_assembler.h"
#include "assembler_test_base.h"
+#include "base/macros.h"
#include "base/malloc_arena_pool.h"
#include "common_runtime_test.h" // For ScratchFile
@@ -30,7 +31,7 @@
#include <fstream>
#include <iterator>
-namespace art {
+namespace art HIDDEN {
template<typename Ass>
class JNIMacroAssemblerTest : public AssemblerTestBase {
@@ -39,7 +40,7 @@
return assembler_.get();
}
- typedef std::string (*TestFn)(JNIMacroAssemblerTest* assembler_test, Ass* assembler);
+ using TestFn = std::string (*)(JNIMacroAssemblerTest *, Ass *);
void DriverFn(TestFn f, const std::string& test_name) {
DriverWrapper(f(this, assembler_.get()), test_name);
diff --git a/compiler/utils/label.h b/compiler/utils/label.h
index 282500b..0368d90 100644
--- a/compiler/utils/label.h
+++ b/compiler/utils/label.h
@@ -20,7 +20,9 @@
#include <android-base/logging.h>
#include <android-base/macros.h>
-namespace art {
+#include "base/macros.h"
+
+namespace art HIDDEN {
class Assembler;
class AssemblerBuffer;
diff --git a/compiler/utils/managed_register.h b/compiler/utils/managed_register.h
index a3b33ba..72acbd5 100644
--- a/compiler/utils/managed_register.h
+++ b/compiler/utils/managed_register.h
@@ -20,9 +20,10 @@
#include <type_traits>
#include <vector>
+#include "base/macros.h"
#include "base/value_object.h"
-namespace art {
+namespace art HIDDEN {
namespace arm {
class ArmManagedRegister;
diff --git a/compiler/utils/stack_checks.h b/compiler/utils/stack_checks.h
index c348f2c..d0fff73 100644
--- a/compiler/utils/stack_checks.h
+++ b/compiler/utils/stack_checks.h
@@ -18,8 +18,9 @@
#define ART_COMPILER_UTILS_STACK_CHECKS_H_
#include "arch/instruction_set.h"
+#include "base/macros.h"
-namespace art {
+namespace art HIDDEN {
// Size of a frame that we definitely consider large. Anything larger than this should
// definitely get a stack overflow check.
diff --git a/compiler/utils/swap_space.h b/compiler/utils/swap_space.h
deleted file mode 100644
index 827e9a6..0000000
--- a/compiler/utils/swap_space.h
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_COMPILER_UTILS_SWAP_SPACE_H_
-#define ART_COMPILER_UTILS_SWAP_SPACE_H_
-
-#include <stddef.h>
-#include <stdint.h>
-#include <cstdlib>
-#include <list>
-#include <set>
-#include <vector>
-
-#include <android-base/logging.h>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/mutex.h"
-
-namespace art {
-
-// An arena pool that creates arenas backed by an mmaped file.
-class SwapSpace {
- public:
- SwapSpace(int fd, size_t initial_size);
- ~SwapSpace();
- void* Alloc(size_t size) REQUIRES(!lock_);
- void Free(void* ptr, size_t size) REQUIRES(!lock_);
-
- size_t GetSize() {
- return size_;
- }
-
- private:
- // Chunk of space.
- struct SpaceChunk {
- // We need mutable members as we keep these objects in a std::set<> (providing only const
- // access) but we modify these members while carefully preserving the std::set<> ordering.
- mutable uint8_t* ptr;
- mutable size_t size;
-
- uintptr_t Start() const {
- return reinterpret_cast<uintptr_t>(ptr);
- }
- uintptr_t End() const {
- return reinterpret_cast<uintptr_t>(ptr) + size;
- }
- };
-
- class SortChunkByPtr {
- public:
- bool operator()(const SpaceChunk& a, const SpaceChunk& b) const {
- return reinterpret_cast<uintptr_t>(a.ptr) < reinterpret_cast<uintptr_t>(b.ptr);
- }
- };
-
- using FreeByStartSet = std::set<SpaceChunk, SortChunkByPtr>;
-
- // Map size to an iterator to free_by_start_'s entry.
- struct FreeBySizeEntry {
- FreeBySizeEntry(size_t sz, FreeByStartSet::const_iterator entry)
- : size(sz), free_by_start_entry(entry) { }
-
- // We need mutable members as we keep these objects in a std::set<> (providing only const
- // access) but we modify these members while carefully preserving the std::set<> ordering.
- mutable size_t size;
- mutable FreeByStartSet::const_iterator free_by_start_entry;
- };
- struct FreeBySizeComparator {
- bool operator()(const FreeBySizeEntry& lhs, const FreeBySizeEntry& rhs) const {
- if (lhs.size != rhs.size) {
- return lhs.size < rhs.size;
- } else {
- return lhs.free_by_start_entry->Start() < rhs.free_by_start_entry->Start();
- }
- }
- };
- using FreeBySizeSet = std::set<FreeBySizeEntry, FreeBySizeComparator>;
-
- SpaceChunk NewFileChunk(size_t min_size) REQUIRES(lock_);
-
- void RemoveChunk(FreeBySizeSet::const_iterator free_by_size_pos) REQUIRES(lock_);
- void InsertChunk(const SpaceChunk& chunk) REQUIRES(lock_);
-
- int fd_;
- size_t size_;
-
- // NOTE: Boost.Bimap would be useful for the two following members.
-
- // Map start of a free chunk to its size.
- FreeByStartSet free_by_start_ GUARDED_BY(lock_);
- // Free chunks ordered by size.
- FreeBySizeSet free_by_size_ GUARDED_BY(lock_);
-
- mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- DISALLOW_COPY_AND_ASSIGN(SwapSpace);
-};
-
-template <typename T> class SwapAllocator;
-
-template <>
-class SwapAllocator<void> {
- public:
- using value_type = void;
- using pointer = void*;
- using const_pointer = const void*;
-
- template <typename U>
- struct rebind {
- using other = SwapAllocator<U>;
- };
-
- explicit SwapAllocator(SwapSpace* swap_space) : swap_space_(swap_space) {}
-
- template <typename U>
- SwapAllocator(const SwapAllocator<U>& other)
- : swap_space_(other.swap_space_) {}
-
- SwapAllocator(const SwapAllocator& other) = default;
- SwapAllocator& operator=(const SwapAllocator& other) = default;
- ~SwapAllocator() = default;
-
- private:
- SwapSpace* swap_space_;
-
- template <typename U>
- friend class SwapAllocator;
-
- template <typename U>
- friend bool operator==(const SwapAllocator<U>& lhs, const SwapAllocator<U>& rhs);
-};
-
-template <typename T>
-class SwapAllocator {
- public:
- using value_type = T;
- using pointer = T*;
- using reference = T&;
- using const_pointer = const T*;
- using const_reference = const T&;
- using size_type = size_t;
- using difference_type = ptrdiff_t;
-
- template <typename U>
- struct rebind {
- using other = SwapAllocator<U>;
- };
-
- explicit SwapAllocator(SwapSpace* swap_space) : swap_space_(swap_space) {}
-
- template <typename U>
- SwapAllocator(const SwapAllocator<U>& other)
- : swap_space_(other.swap_space_) {}
-
- SwapAllocator(const SwapAllocator& other) = default;
- SwapAllocator& operator=(const SwapAllocator& other) = default;
- ~SwapAllocator() = default;
-
- size_type max_size() const {
- return static_cast<size_type>(-1) / sizeof(T);
- }
-
- pointer address(reference x) const { return &x; }
- const_pointer address(const_reference x) const { return &x; }
-
- pointer allocate(size_type n, SwapAllocator<void>::pointer hint ATTRIBUTE_UNUSED = nullptr) {
- DCHECK_LE(n, max_size());
- if (swap_space_ == nullptr) {
- T* result = reinterpret_cast<T*>(malloc(n * sizeof(T)));
- CHECK_IMPLIES(result == nullptr, n == 0u); // Abort if malloc() fails.
- return result;
- } else {
- return reinterpret_cast<T*>(swap_space_->Alloc(n * sizeof(T)));
- }
- }
- void deallocate(pointer p, size_type n) {
- if (swap_space_ == nullptr) {
- free(p);
- } else {
- swap_space_->Free(p, n * sizeof(T));
- }
- }
-
- void construct(pointer p, const_reference val) {
- new (static_cast<void*>(p)) value_type(val);
- }
- template <class U, class... Args>
- void construct(U* p, Args&&... args) {
- ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...);
- }
- void destroy(pointer p) {
- p->~value_type();
- }
-
- inline bool operator==(SwapAllocator const& other) {
- return swap_space_ == other.swap_space_;
- }
- inline bool operator!=(SwapAllocator const& other) {
- return !operator==(other);
- }
-
- private:
- SwapSpace* swap_space_;
-
- template <typename U>
- friend class SwapAllocator;
-
- template <typename U>
- friend bool operator==(const SwapAllocator<U>& lhs, const SwapAllocator<U>& rhs);
-};
-
-template <typename T>
-inline bool operator==(const SwapAllocator<T>& lhs, const SwapAllocator<T>& rhs) {
- return lhs.swap_space_ == rhs.swap_space_;
-}
-
-template <typename T>
-inline bool operator!=(const SwapAllocator<T>& lhs, const SwapAllocator<T>& rhs) {
- return !(lhs == rhs);
-}
-
-template <typename T>
-using SwapVector = std::vector<T, SwapAllocator<T>>;
-template <typename T, typename Comparator>
-using SwapSet = std::set<T, Comparator, SwapAllocator<T>>;
-
-} // namespace art
-
-#endif // ART_COMPILER_UTILS_SWAP_SPACE_H_
diff --git a/compiler/utils/swap_space_test.cc b/compiler/utils/swap_space_test.cc
deleted file mode 100644
index 1650080..0000000
--- a/compiler/utils/swap_space_test.cc
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2014 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 "utils/swap_space.h"
-
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <cstdio>
-
-#include "gtest/gtest.h"
-
-#include "base/os.h"
-#include "base/unix_file/fd_file.h"
-#include "common_runtime_test.h"
-
-namespace art {
-
-class SwapSpaceTest : public CommonRuntimeTest {
-};
-
-static void SwapTest(bool use_file) {
- ScratchFile scratch;
- int fd = scratch.GetFd();
- unlink(scratch.GetFilename().c_str());
-
- SwapSpace pool(fd, 1 * MB);
- SwapAllocator<void> alloc(use_file ? &pool : nullptr);
-
- SwapVector<int32_t> v(alloc);
- v.reserve(1000000);
- for (int32_t i = 0; i < 1000000; ++i) {
- v.push_back(i);
- EXPECT_EQ(i, v[i]);
- }
-
- SwapVector<int32_t> v2(alloc);
- v2.reserve(1000000);
- for (int32_t i = 0; i < 1000000; ++i) {
- v2.push_back(i);
- EXPECT_EQ(i, v2[i]);
- }
-
- SwapVector<int32_t> v3(alloc);
- v3.reserve(500000);
- for (int32_t i = 0; i < 1000000; ++i) {
- v3.push_back(i);
- EXPECT_EQ(i, v2[i]);
- }
-
- // Verify contents.
- for (int32_t i = 0; i < 1000000; ++i) {
- EXPECT_EQ(i, v[i]);
- EXPECT_EQ(i, v2[i]);
- EXPECT_EQ(i, v3[i]);
- }
-
- scratch.Close();
-}
-
-TEST_F(SwapSpaceTest, Memory) {
- SwapTest(false);
-}
-
-TEST_F(SwapSpaceTest, Swap) {
- SwapTest(true);
-}
-
-} // namespace art
diff --git a/compiler/utils/x86/assembler_x86.cc b/compiler/utils/x86/assembler_x86.cc
index 861b27e..a6b9011 100644
--- a/compiler/utils/x86/assembler_x86.cc
+++ b/compiler/utils/x86/assembler_x86.cc
@@ -21,7 +21,7 @@
#include "entrypoints/quick/quick_entrypoints.h"
#include "thread.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
std::ostream& operator<<(std::ostream& os, const XmmRegister& reg) {
diff --git a/compiler/utils/x86/assembler_x86.h b/compiler/utils/x86/assembler_x86.h
index c346ba9..0f7854d 100644
--- a/compiler/utils/x86/assembler_x86.h
+++ b/compiler/utils/x86/assembler_x86.h
@@ -32,7 +32,7 @@
#include "offsets.h"
#include "utils/assembler.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
class Immediate : public ValueObject {
diff --git a/compiler/utils/x86/assembler_x86_test.cc b/compiler/utils/x86/assembler_x86_test.cc
index 89c73c0..5da6f04 100644
--- a/compiler/utils/x86/assembler_x86_test.cc
+++ b/compiler/utils/x86/assembler_x86_test.cc
@@ -17,11 +17,12 @@
#include "assembler_x86.h"
#include "base/arena_allocator.h"
+#include "base/macros.h"
#include "base/malloc_arena_pool.h"
#include "base/stl_util.h"
#include "utils/assembler_test.h"
-namespace art {
+namespace art HIDDEN {
TEST(AssemblerX86, CreateBuffer) {
MallocArenaPool pool;
diff --git a/compiler/utils/x86/constants_x86.h b/compiler/utils/x86/constants_x86.h
index 477b915..0c0a7d4 100644
--- a/compiler/utils/x86/constants_x86.h
+++ b/compiler/utils/x86/constants_x86.h
@@ -25,7 +25,7 @@
#include "base/globals.h"
#include "base/macros.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
enum ByteRegister {
diff --git a/compiler/utils/x86/jni_macro_assembler_x86.cc b/compiler/utils/x86/jni_macro_assembler_x86.cc
index 685f5f1..7bb167c 100644
--- a/compiler/utils/x86/jni_macro_assembler_x86.cc
+++ b/compiler/utils/x86/jni_macro_assembler_x86.cc
@@ -22,7 +22,7 @@
#include "thread.h"
#include "utils/assembler.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
static Register GetScratchRegister() {
@@ -187,8 +187,18 @@
__ fs()->movl(Address::Absolute(thr_offs), scratch);
}
-void X86JNIMacroAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs) {
- __ fs()->movl(Address::Absolute(thr_offs), ESP);
+void X86JNIMacroAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs, bool tag_sp) {
+ if (tag_sp) {
+ // There is no free register, store contents onto stack and restore back later.
+ Register scratch = ECX;
+ __ movl(Address(ESP, -32), scratch);
+ __ movl(scratch, ESP);
+ __ orl(scratch, Immediate(0x2));
+ __ fs()->movl(Address::Absolute(thr_offs), scratch);
+ __ movl(scratch, Address(ESP, -32));
+ } else {
+ __ fs()->movl(Address::Absolute(thr_offs), ESP);
+ }
}
void X86JNIMacroAssembler::StoreSpanning(FrameOffset /*dst*/,
@@ -402,6 +412,11 @@
}
}
+void X86JNIMacroAssembler::Move(ManagedRegister mdest, size_t value) {
+ X86ManagedRegister dest = mdest.AsX86();
+ __ movl(dest.AsCpuRegister(), Immediate(value));
+}
+
void X86JNIMacroAssembler::CopyRef(FrameOffset dest, FrameOffset src) {
Register scratch = GetScratchRegister();
__ movl(scratch, Address(ESP, src));
@@ -724,6 +739,12 @@
__ j(UnaryConditionToX86Condition(cond), X86JNIMacroLabel::Cast(label)->AsX86());
}
+
+void X86JNIMacroAssembler::TestByteAndJumpIfNotZero(uintptr_t address, JNIMacroLabel* label) {
+ __ cmpb(Address::Absolute(address), Immediate(0));
+ __ j(kNotZero, X86JNIMacroLabel::Cast(label)->AsX86());
+}
+
void X86JNIMacroAssembler::Bind(JNIMacroLabel* label) {
CHECK(label != nullptr);
__ Bind(X86JNIMacroLabel::Cast(label)->AsX86());
diff --git a/compiler/utils/x86/jni_macro_assembler_x86.h b/compiler/utils/x86/jni_macro_assembler_x86.h
index 29fccfd..eba4b99 100644
--- a/compiler/utils/x86/jni_macro_assembler_x86.h
+++ b/compiler/utils/x86/jni_macro_assembler_x86.h
@@ -27,7 +27,7 @@
#include "offsets.h"
#include "utils/jni_macro_assembler.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
class X86JNIMacroLabel;
@@ -66,7 +66,7 @@
void StoreStackOffsetToThread(ThreadOffset32 thr_offs, FrameOffset fr_offs) override;
- void StoreStackPointerToThread(ThreadOffset32 thr_offs) override;
+ void StoreStackPointerToThread(ThreadOffset32 thr_offs, bool tag_sp) override;
void StoreSpanning(FrameOffset dest, ManagedRegister src, FrameOffset in_off) override;
@@ -92,6 +92,8 @@
void Move(ManagedRegister dest, ManagedRegister src, size_t size) override;
+ void Move(ManagedRegister dest, size_t value) override;
+
void CopyRawPtrFromThread(FrameOffset fr_offs, ThreadOffset32 thr_offs) override;
void CopyRawPtrToThread(ThreadOffset32 thr_offs, FrameOffset fr_offs, ManagedRegister scratch)
@@ -189,6 +191,8 @@
void TestGcMarking(JNIMacroLabel* label, JNIMacroUnaryCondition cond) override;
// Emit a conditional jump to the label by applying a unary condition test to object's mark bit.
void TestMarkBit(ManagedRegister ref, JNIMacroLabel* label, JNIMacroUnaryCondition cond) override;
+ // Emit a conditional jump to label if the loaded value from specified locations is not zero.
+ void TestByteAndJumpIfNotZero(uintptr_t address, JNIMacroLabel* label) override;
// Code at this offset will serve as the target for the Jump call.
void Bind(JNIMacroLabel* label) override;
diff --git a/compiler/utils/x86/managed_register_x86.cc b/compiler/utils/x86/managed_register_x86.cc
index cc7cedf..bef9480 100644
--- a/compiler/utils/x86/managed_register_x86.cc
+++ b/compiler/utils/x86/managed_register_x86.cc
@@ -18,7 +18,7 @@
#include "base/globals.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
// Define register pairs.
diff --git a/compiler/utils/x86/managed_register_x86.h b/compiler/utils/x86/managed_register_x86.h
index 27555bf..def4f68 100644
--- a/compiler/utils/x86/managed_register_x86.h
+++ b/compiler/utils/x86/managed_register_x86.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_UTILS_X86_MANAGED_REGISTER_X86_H_
#define ART_COMPILER_UTILS_X86_MANAGED_REGISTER_X86_H_
+#include "base/macros.h"
#include "constants_x86.h"
#include "utils/managed_register.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
// Values for register pairs.
diff --git a/compiler/utils/x86/managed_register_x86_test.cc b/compiler/utils/x86/managed_register_x86_test.cc
index 28af531..9f5e197 100644
--- a/compiler/utils/x86/managed_register_x86_test.cc
+++ b/compiler/utils/x86/managed_register_x86_test.cc
@@ -17,9 +17,10 @@
#include "managed_register_x86.h"
#include "base/globals.h"
+#include "base/macros.h"
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
namespace x86 {
TEST(X86ManagedRegister, NoRegister) {
diff --git a/compiler/utils/x86_64/assembler_x86_64.cc b/compiler/utils/x86_64/assembler_x86_64.cc
index 21a4481..3fdf05b 100644
--- a/compiler/utils/x86_64/assembler_x86_64.cc
+++ b/compiler/utils/x86_64/assembler_x86_64.cc
@@ -21,7 +21,7 @@
#include "entrypoints/quick/quick_entrypoints.h"
#include "thread.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
std::ostream& operator<<(std::ostream& os, const CpuRegister& reg) {
diff --git a/compiler/utils/x86_64/assembler_x86_64.h b/compiler/utils/x86_64/assembler_x86_64.h
index ea944c2..9402fe3 100644
--- a/compiler/utils/x86_64/assembler_x86_64.h
+++ b/compiler/utils/x86_64/assembler_x86_64.h
@@ -32,7 +32,7 @@
#include "utils/assembler.h"
#include "utils/jni_macro_assembler.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
// Encodes an immediate value for operands.
diff --git a/compiler/utils/x86_64/assembler_x86_64_test.cc b/compiler/utils/x86_64/assembler_x86_64_test.cc
index f7e890d..a7c206a 100644
--- a/compiler/utils/x86_64/assembler_x86_64_test.cc
+++ b/compiler/utils/x86_64/assembler_x86_64_test.cc
@@ -21,13 +21,14 @@
#include <random>
#include "base/bit_utils.h"
+#include "base/macros.h"
#include "base/malloc_arena_pool.h"
#include "base/stl_util.h"
#include "jni_macro_assembler_x86_64.h"
#include "utils/assembler_test.h"
#include "utils/jni_macro_assembler_test.h"
-namespace art {
+namespace art HIDDEN {
TEST(AssemblerX86_64, CreateBuffer) {
MallocArenaPool pool;
diff --git a/compiler/utils/x86_64/constants_x86_64.h b/compiler/utils/x86_64/constants_x86_64.h
index 301c8fc..52ac987 100644
--- a/compiler/utils/x86_64/constants_x86_64.h
+++ b/compiler/utils/x86_64/constants_x86_64.h
@@ -25,7 +25,7 @@
#include "base/globals.h"
#include "base/macros.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
class CpuRegister {
diff --git a/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc b/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
index d5d1bba..044027a 100644
--- a/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
+++ b/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
@@ -22,7 +22,7 @@
#include "lock_word.h"
#include "thread.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
static dwarf::Reg DWARFReg(Register reg) {
@@ -217,8 +217,15 @@
__ gs()->movq(Address::Absolute(thr_offs, true), scratch);
}
-void X86_64JNIMacroAssembler::StoreStackPointerToThread(ThreadOffset64 thr_offs) {
- __ gs()->movq(Address::Absolute(thr_offs, true), CpuRegister(RSP));
+void X86_64JNIMacroAssembler::StoreStackPointerToThread(ThreadOffset64 thr_offs, bool tag_sp) {
+ if (tag_sp) {
+ CpuRegister reg = GetScratchRegister();
+ __ movq(reg, CpuRegister(RSP));
+ __ orq(reg, Immediate(0x2));
+ __ gs()->movq(Address::Absolute(thr_offs, true), reg);
+ } else {
+ __ gs()->movq(Address::Absolute(thr_offs, true), CpuRegister(RSP));
+ }
}
void X86_64JNIMacroAssembler::StoreSpanning(FrameOffset /*dst*/,
@@ -477,6 +484,12 @@
}
}
+
+void X86_64JNIMacroAssembler::Move(ManagedRegister mdest, size_t value) {
+ X86_64ManagedRegister dest = mdest.AsX86_64();
+ __ movq(dest.AsCpuRegister(), Immediate(value));
+}
+
void X86_64JNIMacroAssembler::CopyRef(FrameOffset dest, FrameOffset src) {
CpuRegister scratch = GetScratchRegister();
__ movl(scratch, Address(CpuRegister(RSP), src));
@@ -803,6 +816,13 @@
__ j(UnaryConditionToX86_64Condition(cond), X86_64JNIMacroLabel::Cast(label)->AsX86_64());
}
+void X86_64JNIMacroAssembler::TestByteAndJumpIfNotZero(uintptr_t address, JNIMacroLabel* label) {
+ CpuRegister scratch = GetScratchRegister();
+ __ movq(scratch, Immediate(address));
+ __ cmpb(Address(scratch, 0), Immediate(0));
+ __ j(kNotZero, X86_64JNIMacroLabel::Cast(label)->AsX86_64());
+}
+
void X86_64JNIMacroAssembler::Bind(JNIMacroLabel* label) {
CHECK(label != nullptr);
__ Bind(X86_64JNIMacroLabel::Cast(label)->AsX86_64());
diff --git a/compiler/utils/x86_64/jni_macro_assembler_x86_64.h b/compiler/utils/x86_64/jni_macro_assembler_x86_64.h
index e080f0b..3308c7e 100644
--- a/compiler/utils/x86_64/jni_macro_assembler_x86_64.h
+++ b/compiler/utils/x86_64/jni_macro_assembler_x86_64.h
@@ -28,7 +28,7 @@
#include "utils/assembler.h"
#include "utils/jni_macro_assembler.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
class X86_64JNIMacroAssembler final : public JNIMacroAssemblerFwd<X86_64Assembler,
@@ -67,7 +67,7 @@
void StoreStackOffsetToThread(ThreadOffset64 thr_offs, FrameOffset fr_offs) override;
- void StoreStackPointerToThread(ThreadOffset64 thr_offs) override;
+ void StoreStackPointerToThread(ThreadOffset64 thr_offs, bool tag_sp) override;
void StoreSpanning(FrameOffset dest, ManagedRegister src, FrameOffset in_off) override;
@@ -95,6 +95,8 @@
void Move(ManagedRegister dest, ManagedRegister src, size_t size) override;
+ void Move(ManagedRegister dest, size_t value) override;
+
void CopyRawPtrFromThread(FrameOffset fr_offs, ThreadOffset64 thr_offs) override;
void CopyRawPtrToThread(ThreadOffset64 thr_offs, FrameOffset fr_offs, ManagedRegister scratch)
@@ -209,6 +211,8 @@
void TestGcMarking(JNIMacroLabel* label, JNIMacroUnaryCondition cond) override;
// Emit a conditional jump to the label by applying a unary condition test to object's mark bit.
void TestMarkBit(ManagedRegister ref, JNIMacroLabel* label, JNIMacroUnaryCondition cond) override;
+ // Emit a conditional jump to label if the loaded value from specified locations is not zero.
+ void TestByteAndJumpIfNotZero(uintptr_t address, JNIMacroLabel* label) override;
// Code at this offset will serve as the target for the Jump call.
void Bind(JNIMacroLabel* label) override;
diff --git a/compiler/utils/x86_64/managed_register_x86_64.cc b/compiler/utils/x86_64/managed_register_x86_64.cc
index c0eec9d..75ff8aa 100644
--- a/compiler/utils/x86_64/managed_register_x86_64.cc
+++ b/compiler/utils/x86_64/managed_register_x86_64.cc
@@ -18,7 +18,7 @@
#include "base/globals.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
// Define register pairs.
diff --git a/compiler/utils/x86_64/managed_register_x86_64.h b/compiler/utils/x86_64/managed_register_x86_64.h
index 62c0e37..7a1be0b 100644
--- a/compiler/utils/x86_64/managed_register_x86_64.h
+++ b/compiler/utils/x86_64/managed_register_x86_64.h
@@ -17,10 +17,11 @@
#ifndef ART_COMPILER_UTILS_X86_64_MANAGED_REGISTER_X86_64_H_
#define ART_COMPILER_UTILS_X86_64_MANAGED_REGISTER_X86_64_H_
+#include "base/macros.h"
#include "constants_x86_64.h"
#include "utils/managed_register.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
// Values for register pairs.
diff --git a/compiler/utils/x86_64/managed_register_x86_64_test.cc b/compiler/utils/x86_64/managed_register_x86_64_test.cc
index 46a405f..048268b 100644
--- a/compiler/utils/x86_64/managed_register_x86_64_test.cc
+++ b/compiler/utils/x86_64/managed_register_x86_64_test.cc
@@ -16,9 +16,10 @@
#include "managed_register_x86_64.h"
#include "base/globals.h"
+#include "base/macros.h"
#include "gtest/gtest.h"
-namespace art {
+namespace art HIDDEN {
namespace x86_64 {
TEST(X86_64ManagedRegister, NoRegister) {
diff --git a/default_build b/default_build
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/default_build
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index 26cbd51..2f7d2f1 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -29,6 +29,9 @@
host_supported: true,
srcs: [
"dex/quick_compiler_callbacks.cc",
+ "dex/verification_results.cc",
+ "driver/compiled_method.cc",
+ "driver/compiled_method_storage.cc",
"driver/compiler_driver.cc",
"linker/code_info_table_deduper.cc",
"linker/elf_writer.cc",
@@ -37,6 +40,7 @@
"linker/multi_oat_relative_patcher.cc",
"linker/oat_writer.cc",
"linker/relative_patcher.cc",
+ "utils/swap_space.cc",
],
codegen: {
@@ -106,6 +110,9 @@
"libartpalette",
"libprofile",
],
+ static_libs: [
+ "libelffile",
+ ],
apex_available: [
"com.android.art",
"com.android.art.debug",
@@ -157,6 +164,9 @@
"libartpalette",
"libprofiled",
],
+ static_libs: [
+ "libelffiled",
+ ],
apex_available: [
"com.android.art.debug",
],
@@ -242,6 +252,12 @@
profile_file: "art/dex2oat_arm_arm64.profdata",
},
},
+ android_riscv64: {
+ pgo: {
+ enable_profile_use: false,
+ profile_file: "",
+ },
+ },
android_x86_64: {
pgo: {
profile_file: "art/dex2oat_x86_x86_64.profdata",
@@ -296,6 +312,7 @@
],
static_libs: [
"libart-dex2oat",
+ "libelffile",
],
lto: {
thin: true,
@@ -355,6 +372,7 @@
],
static_libs: [
"libartd-dex2oat",
+ "libelffiled",
],
},
host: {
@@ -459,6 +477,7 @@
name: "art_dex2oat_tests_defaults",
data: [
":art-gtest-jars-AbstractMethod",
+ ":art-gtest-jars-ArrayClassWithUnresolvedComponent",
":art-gtest-jars-DefaultMethods",
":art-gtest-jars-Dex2oatVdexPublicSdkDex",
":art-gtest-jars-Dex2oatVdexTestDex",
@@ -479,6 +498,7 @@
":art-gtest-jars-StaticLeafMethods",
":art-gtest-jars-Statics",
":art-gtest-jars-StringLiterals",
+ ":art-gtest-jars-SuperWithAccessChecks",
":art-gtest-jars-VerifierDeps",
":art-gtest-jars-VerifierDepsMulti",
":art-gtest-jars-VerifySoftFailDuringClinit",
@@ -493,6 +513,7 @@
"dex2oat_test.cc",
"dex2oat_vdex_test.cc",
"dex2oat_image_test.cc",
+ "driver/compiled_method_storage_test.cc",
"driver/compiler_driver_test.cc",
"linker/code_info_table_deduper_test.cc",
"linker/elf_writer_test.cc",
@@ -502,6 +523,7 @@
"linker/multi_oat_relative_patcher_test.cc",
"linker/oat_writer_test.cc",
"verifier_deps_test.cc",
+ "utils/swap_space_test.cc",
],
target: {
host: {
@@ -532,6 +554,9 @@
},
},
+ static_libs: [
+ "libziparchive",
+ ],
shared_libs: [
"libartpalette",
"libbase",
@@ -539,7 +564,7 @@
"liblz4", // libart(d)-dex2oat dependency; must be repeated here since it's a static lib.
"liblog",
"libsigchain",
- "libziparchive",
+ "libz", // libziparchive dependency; must be repeated here since it's a static lib.
],
}
@@ -553,13 +578,15 @@
],
shared_libs: [
"libartbased",
- "libartd-compiler",
"libartd-dexlayout",
+ "liblzma",
"libprofiled",
],
static_libs: [
- "libartd-dex2oat-gtest",
+ "libartd-compiler",
"libartd-dex2oat",
+ "libartd-dex2oat-gtest",
+ "libelffiled",
"libvixld",
],
}
@@ -573,14 +600,16 @@
],
data: [":generate-boot-image"],
shared_libs: [
- "libartbase",
- "libart-compiler",
"libart-dexlayout",
+ "libartbase",
+ "liblzma",
"libprofile",
],
static_libs: [
- "libart-dex2oat-gtest",
+ "libart-compiler",
"libart-dex2oat",
+ "libart-dex2oat-gtest",
+ "libelffile",
"libvixl",
],
test_config: "art_standalone_dex2oat_tests.xml",
@@ -599,6 +628,12 @@
":art-gtest-jars-Nested",
":generate-boot-image",
],
+ shared_libs: [
+ "libz", // libziparchive dependency; must be repeated here since it's a static lib.
+ ],
+ static_libs: [
+ "libziparchive",
+ ],
test_config: "art_standalone_dex2oat_cts_tests.xml",
test_suites: ["cts"],
}
diff --git a/dex2oat/art_standalone_dex2oat_tests.xml b/dex2oat/art_standalone_dex2oat_tests.xml
index 32346f2..1cb5fef 100644
--- a/dex2oat/art_standalone_dex2oat_tests.xml
+++ b/dex2oat/art_standalone_dex2oat_tests.xml
@@ -14,6 +14,8 @@
limitations under the License.
-->
<configuration description="Runs art_standalone_dex2oat_tests.">
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.art.apex" />
+
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="art_standalone_dex2oat_tests->/data/local/tmp/art_standalone_dex2oat_tests/art_standalone_dex2oat_tests" />
@@ -23,6 +25,8 @@
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="art-gtest-jars-AbstractMethod.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-AbstractMethod.jar" />
+ <option name="push" value="art-gtest-jars-ArrayClassWithUnresolvedComponent.dex->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-ArrayClassWithUnresolvedComponent.dex" />
+ <option name="push" value="art-gtest-jars-SuperWithAccessChecks.dex->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-SuperWithAccessChecks.dex" />
<option name="push" value="art-gtest-jars-DefaultMethods.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-DefaultMethods.jar" />
<option name="push" value="art-gtest-jars-Dex2oatVdexPublicSdkDex.dex->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-Dex2oatVdexPublicSdkDex.dex" />
<option name="push" value="art-gtest-jars-Dex2oatVdexTestDex.jar->/data/local/tmp/art_standalone_dex2oat_tests/art-gtest-jars-Dex2oatVdexTestDex.jar" />
@@ -62,6 +66,9 @@
</target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-timeout" value="5m" />
+ <!-- Set this to the same as native-test-timeout to effectively disable per test case timeout. -->
+ <option name="test-case-timeout" value="5m" />
<option name="native-test-device-path" value="/data/local/tmp/art_standalone_dex2oat_tests" />
<option name="module-name" value="art_standalone_dex2oat_tests" />
<option name="ld-library-path-32" value="/apex/com.android.art/lib" />
diff --git a/dex2oat/common_compiler_driver_test.cc b/dex2oat/common_compiler_driver_test.cc
index 7e71976..7a1d11c 100644
--- a/dex2oat/common_compiler_driver_test.cc
+++ b/dex2oat/common_compiler_driver_test.cc
@@ -21,6 +21,7 @@
#include "base/casts.h"
#include "base/timing_logger.h"
#include "dex/quick_compiler_callbacks.h"
+#include "dex/verification_results.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
#include "utils/atomic_dex_ref_map-inl.h"
@@ -46,9 +47,7 @@
// Verification results in the `callback_` should not be used during compilation.
down_cast<QuickCompilerCallbacks*>(callbacks_.get())->SetVerificationResults(
reinterpret_cast<VerificationResults*>(inaccessible_page_));
- compiler_options_->verification_results_ = verification_results_.get();
compiler_driver_->CompileAll(class_loader, dex_files, timings);
- compiler_options_->verification_results_ = nullptr;
down_cast<QuickCompilerCallbacks*>(callbacks_.get())->SetVerificationResults(
verification_results_.get());
@@ -89,6 +88,7 @@
compiler_options_->image_classes_.swap(*GetImageClasses());
compiler_options_->profile_compilation_info_ = GetProfileCompilationInfo();
compiler_driver_.reset(new CompilerDriver(compiler_options_.get(),
+ verification_results_.get(),
compiler_kind_,
number_of_threads_,
/* swap_fd= */ -1));
@@ -97,6 +97,7 @@
void CommonCompilerDriverTest::SetUpRuntimeOptions(RuntimeOptions* options) {
CommonCompilerTest::SetUpRuntimeOptions(options);
+ verification_results_.reset(new VerificationResults());
QuickCompilerCallbacks* callbacks =
new QuickCompilerCallbacks(CompilerCallbacks::CallbackMode::kCompileApp);
callbacks->SetVerificationResults(verification_results_.get());
@@ -121,6 +122,7 @@
}
image_reservation_.Reset();
compiler_driver_.reset();
+ verification_results_.reset();
CommonCompilerTest::TearDown();
}
diff --git a/dex2oat/common_compiler_driver_test.h b/dex2oat/common_compiler_driver_test.h
index 1ff88e5..b49d18f 100644
--- a/dex2oat/common_compiler_driver_test.h
+++ b/dex2oat/common_compiler_driver_test.h
@@ -31,6 +31,7 @@
class DexFile;
class ProfileCompilationInfo;
class TimingLogger;
+class VerificationResults;
class CommonCompilerDriverTest : public CommonCompilerTest {
public:
@@ -62,6 +63,7 @@
size_t number_of_threads_ = 2u;
+ std::unique_ptr<VerificationResults> verification_results_;
std::unique_ptr<CompilerDriver> compiler_driver_;
private:
diff --git a/dex2oat/dex/quick_compiler_callbacks.cc b/dex2oat/dex/quick_compiler_callbacks.cc
index 11223ff..7611374 100644
--- a/dex2oat/dex/quick_compiler_callbacks.cc
+++ b/dex2oat/dex/quick_compiler_callbacks.cc
@@ -28,6 +28,12 @@
}
}
+void QuickCompilerCallbacks::AddUncompilableClass(ClassReference ref) {
+ if (verification_results_ != nullptr) {
+ verification_results_->AddUncompilableClass(ref);
+ }
+}
+
void QuickCompilerCallbacks::ClassRejected(ClassReference ref) {
if (verification_results_ != nullptr) {
verification_results_->AddRejectedClass(ref);
diff --git a/dex2oat/dex/quick_compiler_callbacks.h b/dex2oat/dex/quick_compiler_callbacks.h
index 4447e02..a7a482b 100644
--- a/dex2oat/dex/quick_compiler_callbacks.h
+++ b/dex2oat/dex/quick_compiler_callbacks.h
@@ -34,6 +34,7 @@
~QuickCompilerCallbacks() { }
void AddUncompilableMethod(MethodReference ref) override;
+ void AddUncompilableClass(ClassReference ref) override;
void ClassRejected(ClassReference ref) override;
diff --git a/dex2oat/dex/verification_results.cc b/dex2oat/dex/verification_results.cc
new file mode 100644
index 0000000..bc0329a
--- /dev/null
+++ b/dex2oat/dex/verification_results.cc
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2013 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 "verification_results.h"
+
+#include <android-base/logging.h>
+
+#include "base/mutex-inl.h"
+#include "base/stl_util.h"
+#include "dex/class_accessor-inl.h"
+#include "runtime.h"
+#include "thread-current-inl.h"
+#include "thread.h"
+
+namespace art {
+
+VerificationResults::VerificationResults()
+ : uncompilable_methods_lock_("compiler uncompilable methods lock"),
+ rejected_classes_lock_("compiler rejected classes lock") {}
+
+// Non-inline version of the destructor, as it does some implicit work not worth
+// inlining.
+VerificationResults::~VerificationResults() {}
+
+void VerificationResults::AddRejectedClass(ClassReference ref) {
+ {
+ WriterMutexLock mu(Thread::Current(), rejected_classes_lock_);
+ rejected_classes_.insert(ref);
+ }
+ DCHECK(IsClassRejected(ref));
+}
+
+bool VerificationResults::IsClassRejected(ClassReference ref) const {
+ ReaderMutexLock mu(Thread::Current(), rejected_classes_lock_);
+ return rejected_classes_.find(ref) != rejected_classes_.end();
+}
+
+void VerificationResults::AddUncompilableMethod(MethodReference ref) {
+ {
+ WriterMutexLock mu(Thread::Current(), uncompilable_methods_lock_);
+ uncompilable_methods_.insert(ref);
+ }
+ DCHECK(IsUncompilableMethod(ref));
+}
+
+void VerificationResults::AddUncompilableClass(ClassReference ref) {
+ const DexFile& dex_file = *ref.dex_file;
+ const dex::ClassDef& class_def = dex_file.GetClassDef(ref.ClassDefIdx());
+ WriterMutexLock mu(Thread::Current(), uncompilable_methods_lock_);
+ ClassAccessor accessor(dex_file, class_def);
+ for (const ClassAccessor::Method& method : accessor.GetMethods()) {
+ MethodReference method_ref(&dex_file, method.GetIndex());
+ uncompilable_methods_.insert(method_ref);
+ }
+}
+
+bool VerificationResults::IsUncompilableMethod(MethodReference ref) const {
+ ReaderMutexLock mu(Thread::Current(), uncompilable_methods_lock_);
+ return uncompilable_methods_.find(ref) != uncompilable_methods_.end();
+}
+
+
+} // namespace art
diff --git a/dex2oat/dex/verification_results.h b/dex2oat/dex/verification_results.h
new file mode 100644
index 0000000..0a85e43
--- /dev/null
+++ b/dex2oat/dex/verification_results.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_DEX2OAT_DEX_VERIFICATION_RESULTS_H_
+#define ART_DEX2OAT_DEX_VERIFICATION_RESULTS_H_
+
+#include <set>
+
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "dex/class_reference.h"
+#include "dex/method_reference.h"
+
+namespace art {
+
+namespace verifier {
+class VerifierDepsTest;
+} // namespace verifier
+
+// Used by CompilerCallbacks to track verification information from the Runtime.
+class VerificationResults {
+ public:
+ VerificationResults();
+ ~VerificationResults();
+
+ void AddRejectedClass(ClassReference ref) REQUIRES(!rejected_classes_lock_);
+ bool IsClassRejected(ClassReference ref) const REQUIRES(!rejected_classes_lock_);
+
+ void AddUncompilableClass(ClassReference ref) REQUIRES(!uncompilable_methods_lock_);
+ void AddUncompilableMethod(MethodReference ref) REQUIRES(!uncompilable_methods_lock_);
+ bool IsUncompilableMethod(MethodReference ref) const REQUIRES(!uncompilable_methods_lock_);
+
+ private:
+ // TODO: External locking during CompilerDriver::PreCompile(), no locking during compilation.
+ mutable ReaderWriterMutex uncompilable_methods_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ std::set<MethodReference> uncompilable_methods_ GUARDED_BY(uncompilable_methods_lock_);
+
+ // Rejected classes.
+ // TODO: External locking during CompilerDriver::PreCompile(), no locking during compilation.
+ mutable ReaderWriterMutex rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ std::set<ClassReference> rejected_classes_ GUARDED_BY(rejected_classes_lock_);
+
+ friend class verifier::VerifierDepsTest;
+};
+
+} // namespace art
+
+#endif // ART_DEX2OAT_DEX_VERIFICATION_RESULTS_H_
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 9e6103b..1754a68 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -39,6 +39,7 @@
#endif
#include "android-base/parseint.h"
+#include "android-base/properties.h"
#include "android-base/scopeguard.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
@@ -65,6 +66,7 @@
#include "base/zip_archive.h"
#include "class_linker.h"
#include "class_loader_context.h"
+#include "class_root-inl.h"
#include "cmdline_parser.h"
#include "compiler.h"
#include "compiler_callbacks.h"
@@ -108,7 +110,6 @@
#include "stream/file_output_stream.h"
#include "vdex_file.h"
#include "verifier/verifier_deps.h"
-#include "well_known_classes.h"
namespace art {
@@ -607,8 +608,13 @@
}
void ParseInstructionSetVariant(const std::string& option, ParserOptions* parser_options) {
- compiler_options_->instruction_set_features_ = InstructionSetFeatures::FromVariant(
- compiler_options_->instruction_set_, option, &parser_options->error_msg);
+ if (kIsTargetBuild) {
+ compiler_options_->instruction_set_features_ = InstructionSetFeatures::FromVariantAndHwcap(
+ compiler_options_->instruction_set_, option, &parser_options->error_msg);
+ } else {
+ compiler_options_->instruction_set_features_ = InstructionSetFeatures::FromVariant(
+ compiler_options_->instruction_set_, option, &parser_options->error_msg);
+ }
if (compiler_options_->instruction_set_features_ == nullptr) {
Usage("%s", parser_options->error_msg.c_str());
}
@@ -957,7 +963,7 @@
compiler_options_->GetNativeDebuggable());
key_value_store_->Put(OatHeader::kCompilerFilter,
CompilerFilter::NameOfFilter(compiler_options_->GetCompilerFilter()));
- key_value_store_->Put(OatHeader::kConcurrentCopying, kUseReadBarrier);
+ key_value_store_->Put(OatHeader::kConcurrentCopying, gUseReadBarrier);
if (invocation_file_.get() != -1) {
std::ostringstream oss;
for (int i = 0; i < argc; ++i) {
@@ -1095,6 +1101,19 @@
AssignIfExists(args, M::PublicSdk, &public_sdk_);
AssignIfExists(args, M::ApexVersions, &apex_versions_argument_);
+ // Check for phenotype flag to override compact_dex_level_, if it isn't "none" already.
+ // TODO(b/256664509): Clean this up.
+ if (compact_dex_level_ != CompactDexLevel::kCompactDexLevelNone) {
+ std::string ph_disable_compact_dex =
+ android::base::GetProperty(kPhDisableCompactDex, "false");
+ if (ph_disable_compact_dex == "true") {
+ LOG(WARNING)
+ << "Overriding --compact-dex-level due to "
+ "persist.device_config.runtime_native_boot.disable_compact_dex set to `true`";
+ compact_dex_level_ = CompactDexLevel::kCompactDexLevelNone;
+ }
+ }
+
AssignIfExists(args, M::Backend, &compiler_kind_);
parser_options->requested_specific_compiler = args.Exists(M::Backend);
@@ -1187,7 +1206,7 @@
if (!parser_options->boot_image_filename.empty()) {
Usage("Option --boot-image and --force-jit-zygote cannot be specified together");
}
- parser_options->boot_image_filename = "boot.art:/nonx/boot-framework.art";
+ parser_options->boot_image_filename = GetJitZygoteBootImageLocation();
}
// If we have a profile, change the default compiler filter to speed-profile
@@ -1815,7 +1834,7 @@
}
// Set up and create the compiler driver and then invoke it to compile all the dex files.
- jobject Compile() {
+ jobject Compile() REQUIRES(!Locks::mutator_lock_) {
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
TimingLogger::ScopedTiming t("dex2oat Compile", timings_);
@@ -1879,6 +1898,7 @@
compiler_options_->profile_compilation_info_ = profile_compilation_info_.get();
driver_.reset(new CompilerDriver(compiler_options_.get(),
+ verification_results_.get(),
compiler_kind_,
thread_count_,
swap_fd_));
@@ -1964,7 +1984,6 @@
timings_,
&compiler_options_->image_classes_);
callbacks_->SetVerificationResults(nullptr); // Should not be needed anymore.
- compiler_options_->verification_results_ = verification_results_.get();
driver_->CompileAll(class_loader, dex_files, timings_);
driver_->FreeThreadPools();
return class_loader;
@@ -2538,16 +2557,16 @@
}
bool PreparePreloadedClasses() {
- preloaded_classes_ = std::make_unique<HashSet<std::string>>();
if (!preloaded_classes_fds_.empty()) {
for (int fd : preloaded_classes_fds_) {
- if (!ReadCommentedInputFromFd(fd, nullptr, preloaded_classes_.get())) {
+ if (!ReadCommentedInputFromFd(fd, nullptr, &compiler_options_->preloaded_classes_)) {
return false;
}
}
} else {
for (const std::string& file : preloaded_classes_files_) {
- if (!ReadCommentedInputFromFile(file.c_str(), nullptr, preloaded_classes_.get())) {
+ if (!ReadCommentedInputFromFile(
+ file.c_str(), nullptr, &compiler_options_->preloaded_classes_)) {
return false;
}
}
@@ -2633,6 +2652,7 @@
bool do_oat_writer_layout = DoDexLayoutOptimizations() || DoOatLayoutOptimizations();
oat_writers_.emplace_back(new linker::OatWriter(
*compiler_options_,
+ verification_results_.get(),
timings_,
do_oat_writer_layout ? profile_compilation_info_.get() : nullptr,
compact_dex_level_));
@@ -2731,19 +2751,14 @@
interpreter::UnstartedRuntime::Initialize();
Thread* self = Thread::Current();
+ runtime_->GetClassLinker()->RunEarlyRootClinits(self);
+ InitializeIntrinsics();
runtime_->RunRootClinits(self);
// Runtime::Create acquired the mutator_lock_ that is normally given away when we
// Runtime::Start, give it away now so that we don't starve GC.
self->TransitionFromRunnableToSuspended(ThreadState::kNative);
- // Now that we are in native state, initialize well known classes and
- // intrinsics if we don't have a boot image.
- WellKnownClasses::Init(self->GetJniEnv());
- if (IsBootImage() || runtime_->GetHeap()->GetBootImageSpaces().empty()) {
- InitializeIntrinsics();
- }
-
WatchDog::SetRuntime(runtime_.get());
return true;
@@ -2942,7 +2957,6 @@
const char* dirty_image_objects_filename_;
int dirty_image_objects_fd_;
std::unique_ptr<HashSet<std::string>> dirty_image_objects_;
- std::unique_ptr<HashSet<std::string>> preloaded_classes_;
std::unique_ptr<std::vector<std::string>> passes_to_run_;
bool is_host_;
std::string android_root_;
@@ -3054,7 +3068,8 @@
jobject obj_;
};
-static dex2oat::ReturnCode DoCompilation(Dex2Oat& dex2oat) {
+static dex2oat::ReturnCode DoCompilation(Dex2Oat& dex2oat) REQUIRES(!Locks::mutator_lock_) {
+ Locks::mutator_lock_->AssertNotHeld(Thread::Current());
dex2oat.LoadClassProfileDescriptors();
jobject class_loader = dex2oat.Compile();
// Keep the class loader that was used for compilation live for the rest of the compilation
diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index 2978fb4..4518905 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -431,6 +431,7 @@
relocate,
/*executable=*/ true,
/*extra_reservation_size=*/ 0u,
+ /*allow_in_memory_compilation=*/ true,
&boot_image_spaces,
&extra_reservation);
};
diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc
index 1f9138e..cf60008 100644
--- a/dex2oat/dex2oat_options.cc
+++ b/dex2oat/dex2oat_options.cc
@@ -55,6 +55,7 @@
using Builder = Parser::Builder;
static void AddInputMappings(Builder& builder) {
+ // clang-format off
builder.
Define("--dex-file=_")
.WithType<std::vector<std::string>>().AppendValues()
@@ -102,9 +103,11 @@
" class path components' paths)\n"
"Default: $ANDROID_ROOT/system/framework/boot.art")
.IntoKey(M::BootImage);
+ // clang-format on
}
static void AddGeneratedArtifactMappings(Builder& builder) {
+ // clang-format off
builder.
Define("--input-vdex-fd=_")
.WithType<int>()
@@ -156,9 +159,11 @@
"specified by --oat-fd.\n"
"Eg: --oat-location=/data/dalvik-cache/system@app@Calculator.apk.oat")
.IntoKey(M::OatLocation);
+ // clang-format on
}
static void AddImageMappings(Builder& builder) {
+ // clang-format off
builder.
Define("--image=_")
.WithType<std::string>()
@@ -214,9 +219,11 @@
.WithHelp("Which format to store the image Defaults to uncompressed. Eg:"
" --image-format=lz4")
.IntoKey(M::ImageFormat);
+ // clang-format on
}
static void AddSwapMappings(Builder& builder) {
+ // clang-format off
builder.
Define("--swap-file=_")
.WithType<std::string>()
@@ -234,9 +241,11 @@
.WithType<unsigned int>()
.WithHelp("specifies the minimum number of dex file to allow the use of swap.")
.IntoKey(M::SwapDexCountThreshold);
+ // clang-format on
}
static void AddCompilerMappings(Builder& builder) {
+ // clang-format off
builder.
Define("--run-passes=_")
.WithType<std::string>()
@@ -264,9 +273,11 @@
.WithType<std::vector<int>>().AppendValues()
.WithHelp("Specify files containing list of classes preloaded in the zygote.")
.IntoKey(M::PreloadedClassesFds);
+ // clang-format on
}
static void AddTargetMappings(Builder& builder) {
+ // clang-format off
builder.
Define("--instruction-set=_")
.WithType<InstructionSet>()
@@ -288,6 +299,7 @@
"Example: --instruction-set-features=div\n"
"Default: default")
.IntoKey(M::TargetInstructionSetFeatures);
+ // clang-format on
}
Parser CreateDex2oatArgumentParser() {
@@ -300,6 +312,7 @@
AddCompilerMappings(*parser_builder);
AddTargetMappings(*parser_builder);
+ // clang-format off
parser_builder->
Define({"--watch-dog", "--no-watch-dog"})
.WithHelp("Enable or disable the watchdog timer.")
@@ -448,7 +461,9 @@
.IntoKey(M::ForceJitZygote)
.Define("--force-palette-compilation-hooks")
.WithHelp("Force PaletteNotify{Start,End}Dex2oatCompilation calls.")
- .IntoKey(M::ForcePaletteCompilationHooks);
+ .IntoKey(M::ForcePaletteCompilationHooks)
+ .Ignore({"--comments=_"});
+ // clang-format on
AddCompilerOptionsArgumentParserOptions<Dex2oatArgumentMap>(*parser_builder);
diff --git a/dex2oat/dex2oat_vdex_test.cc b/dex2oat/dex2oat_vdex_test.cc
index 895e9c2..df76d58 100644
--- a/dex2oat/dex2oat_vdex_test.cc
+++ b/dex2oat/dex2oat_vdex_test.cc
@@ -19,10 +19,8 @@
#include "common_runtime_test.h"
#include "dex2oat_environment_test.h"
-
#include "vdex_file.h"
#include "verifier/verifier_deps.h"
-#include "ziparchive/zip_writer.h"
namespace art {
@@ -113,23 +111,6 @@
return deps->GetVerifiedClasses(dex_file)[class_def_idx];
}
- void CreateDexMetadata(const std::string& vdex, const std::string& out_dm) {
- // Read the vdex bytes.
- std::unique_ptr<File> vdex_file(OS::OpenFileForReading(vdex.c_str()));
- std::vector<uint8_t> data(vdex_file->GetLength());
- ASSERT_TRUE(vdex_file->ReadFully(data.data(), data.size()));
-
- // Zip the content.
- FILE* file = fopen(out_dm.c_str(), "wb");
- ZipWriter writer(file);
- writer.StartEntry("primary.vdex", ZipWriter::kAlign32);
- writer.WriteBytes(data.data(), data.size());
- writer.FinishEntry();
- writer.Finish();
- fflush(file);
- fclose(file);
- }
-
std::string GetFilename(const std::unique_ptr<const DexFile>& dex_file) {
const std::string& str = dex_file->GetLocation();
size_t idx = str.rfind('/');
diff --git a/dex2oat/driver/compiled_method-inl.h b/dex2oat/driver/compiled_method-inl.h
new file mode 100644
index 0000000..77ac85c
--- /dev/null
+++ b/dex2oat/driver/compiled_method-inl.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_DEX2OAT_DRIVER_COMPILED_METHOD_INL_H_
+#define ART_DEX2OAT_DRIVER_COMPILED_METHOD_INL_H_
+
+#include "compiled_method.h"
+
+#include "base/array_ref.h"
+#include "base/length_prefixed_array.h"
+#include "linker/linker_patch.h"
+
+namespace art {
+
+inline ArrayRef<const uint8_t> CompiledCode::GetQuickCode() const {
+ return GetArray(quick_code_);
+}
+
+template <typename T>
+inline ArrayRef<const T> CompiledCode::GetArray(const LengthPrefixedArray<T>* array) {
+ if (array == nullptr) {
+ return ArrayRef<const T>();
+ }
+ DCHECK_NE(array->size(), 0u);
+ return ArrayRef<const T>(&array->At(0), array->size());
+}
+
+inline ArrayRef<const uint8_t> CompiledMethod::GetVmapTable() const {
+ return GetArray(vmap_table_);
+}
+
+inline ArrayRef<const uint8_t> CompiledMethod::GetCFIInfo() const {
+ return GetArray(cfi_info_);
+}
+
+inline ArrayRef<const linker::LinkerPatch> CompiledMethod::GetPatches() const {
+ return GetArray(patches_);
+}
+
+} // namespace art
+
+#endif // ART_DEX2OAT_DRIVER_COMPILED_METHOD_INL_H_
diff --git a/dex2oat/driver/compiled_method.cc b/dex2oat/driver/compiled_method.cc
new file mode 100644
index 0000000..0a0a005
--- /dev/null
+++ b/dex2oat/driver/compiled_method.cc
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2011 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 "compiled_method.h"
+
+#include "driver/compiled_method_storage.h"
+#include "utils/swap_space.h"
+
+namespace art {
+
+CompiledCode::CompiledCode(CompiledMethodStorage* storage,
+ InstructionSet instruction_set,
+ const ArrayRef<const uint8_t>& quick_code)
+ : storage_(storage),
+ quick_code_(storage->DeduplicateCode(quick_code)),
+ packed_fields_(InstructionSetField::Encode(instruction_set)) {
+}
+
+CompiledCode::~CompiledCode() {
+ GetStorage()->ReleaseCode(quick_code_);
+}
+
+bool CompiledCode::operator==(const CompiledCode& rhs) const {
+ if (quick_code_ != nullptr) {
+ if (rhs.quick_code_ == nullptr) {
+ return false;
+ } else if (quick_code_->size() != rhs.quick_code_->size()) {
+ return false;
+ } else {
+ return std::equal(quick_code_->begin(), quick_code_->end(), rhs.quick_code_->begin());
+ }
+ }
+ return (rhs.quick_code_ == nullptr);
+}
+
+size_t CompiledCode::AlignCode(size_t offset) const {
+ return AlignCode(offset, GetInstructionSet());
+}
+
+size_t CompiledCode::AlignCode(size_t offset, InstructionSet instruction_set) {
+ return RoundUp(offset, GetInstructionSetCodeAlignment(instruction_set));
+}
+
+size_t CompiledCode::GetEntryPointAdjustment() const {
+ return GetInstructionSetEntryPointAdjustment(GetInstructionSet());
+}
+
+CompiledMethod::CompiledMethod(CompiledMethodStorage* storage,
+ InstructionSet instruction_set,
+ const ArrayRef<const uint8_t>& quick_code,
+ const ArrayRef<const uint8_t>& vmap_table,
+ const ArrayRef<const uint8_t>& cfi_info,
+ const ArrayRef<const linker::LinkerPatch>& patches)
+ : CompiledCode(storage, instruction_set, quick_code),
+ vmap_table_(storage->DeduplicateVMapTable(vmap_table)),
+ cfi_info_(storage->DeduplicateCFIInfo(cfi_info)),
+ patches_(storage->DeduplicateLinkerPatches(patches)) {
+}
+
+CompiledMethod* CompiledMethod::SwapAllocCompiledMethod(
+ CompiledMethodStorage* storage,
+ InstructionSet instruction_set,
+ const ArrayRef<const uint8_t>& quick_code,
+ const ArrayRef<const uint8_t>& vmap_table,
+ const ArrayRef<const uint8_t>& cfi_info,
+ const ArrayRef<const linker::LinkerPatch>& patches) {
+ SwapAllocator<CompiledMethod> alloc(storage->GetSwapSpaceAllocator());
+ CompiledMethod* ret = alloc.allocate(1);
+ alloc.construct(ret,
+ storage,
+ instruction_set,
+ quick_code,
+ vmap_table,
+ cfi_info, patches);
+ return ret;
+}
+
+void CompiledMethod::ReleaseSwapAllocatedCompiledMethod(CompiledMethodStorage* storage,
+ CompiledMethod* m) {
+ SwapAllocator<CompiledMethod> alloc(storage->GetSwapSpaceAllocator());
+ alloc.destroy(m);
+ alloc.deallocate(m, 1);
+}
+
+CompiledMethod::~CompiledMethod() {
+ CompiledMethodStorage* storage = GetStorage();
+ storage->ReleaseLinkerPatches(patches_);
+ storage->ReleaseCFIInfo(cfi_info_);
+ storage->ReleaseVMapTable(vmap_table_);
+}
+
+} // namespace art
diff --git a/dex2oat/driver/compiled_method.h b/dex2oat/driver/compiled_method.h
new file mode 100644
index 0000000..a92c757
--- /dev/null
+++ b/dex2oat/driver/compiled_method.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_DEX2OAT_DRIVER_COMPILED_METHOD_H_
+#define ART_DEX2OAT_DRIVER_COMPILED_METHOD_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "arch/instruction_set.h"
+#include "base/bit_field.h"
+#include "base/bit_utils.h"
+
+namespace art {
+
+template <typename T> class ArrayRef;
+class CompiledMethodStorage;
+template<typename T> class LengthPrefixedArray;
+
+namespace linker {
+class LinkerPatch;
+} // namespace linker
+
+class CompiledCode {
+ public:
+ // For Quick to supply an code blob
+ CompiledCode(CompiledMethodStorage* storage,
+ InstructionSet instruction_set,
+ const ArrayRef<const uint8_t>& quick_code);
+
+ virtual ~CompiledCode();
+
+ InstructionSet GetInstructionSet() const {
+ return GetPackedField<InstructionSetField>();
+ }
+
+ ArrayRef<const uint8_t> GetQuickCode() const;
+
+ bool operator==(const CompiledCode& rhs) const;
+
+ // To align an offset from a page-aligned value to make it suitable
+ // for code storage. For example on ARM, to ensure that PC relative
+ // valu computations work out as expected.
+ size_t AlignCode(size_t offset) const;
+ static size_t AlignCode(size_t offset, InstructionSet instruction_set);
+
+ // Returns the difference between the code address and a usable PC.
+ // Mainly to cope with `kThumb2` where the lower bit must be set.
+ size_t GetEntryPointAdjustment() const;
+
+ protected:
+ static constexpr size_t kInstructionSetFieldSize =
+ MinimumBitsToStore(static_cast<size_t>(InstructionSet::kLast));
+ static constexpr size_t kNumberOfCompiledCodePackedBits = kInstructionSetFieldSize;
+ static constexpr size_t kMaxNumberOfPackedBits = sizeof(uint32_t) * kBitsPerByte;
+
+ template <typename T>
+ static ArrayRef<const T> GetArray(const LengthPrefixedArray<T>* array);
+
+ CompiledMethodStorage* GetStorage() {
+ return storage_;
+ }
+
+ template <typename BitFieldType>
+ typename BitFieldType::value_type GetPackedField() const {
+ return BitFieldType::Decode(packed_fields_);
+ }
+
+ template <typename BitFieldType>
+ void SetPackedField(typename BitFieldType::value_type value) {
+ DCHECK(IsUint<BitFieldType::size>(static_cast<uintptr_t>(value)));
+ packed_fields_ = BitFieldType::Update(value, packed_fields_);
+ }
+
+ private:
+ using InstructionSetField = BitField<InstructionSet, 0u, kInstructionSetFieldSize>;
+
+ CompiledMethodStorage* const storage_;
+
+ // Used to store the compiled code.
+ const LengthPrefixedArray<uint8_t>* const quick_code_;
+
+ uint32_t packed_fields_;
+};
+
+class CompiledMethod final : public CompiledCode {
+ public:
+ // Constructs a CompiledMethod.
+ // Note: Consider using the static allocation methods below that will allocate the CompiledMethod
+ // in the swap space.
+ CompiledMethod(CompiledMethodStorage* storage,
+ InstructionSet instruction_set,
+ const ArrayRef<const uint8_t>& quick_code,
+ const ArrayRef<const uint8_t>& vmap_table,
+ const ArrayRef<const uint8_t>& cfi_info,
+ const ArrayRef<const linker::LinkerPatch>& patches);
+
+ virtual ~CompiledMethod();
+
+ static CompiledMethod* SwapAllocCompiledMethod(
+ CompiledMethodStorage* storage,
+ InstructionSet instruction_set,
+ const ArrayRef<const uint8_t>& quick_code,
+ const ArrayRef<const uint8_t>& vmap_table,
+ const ArrayRef<const uint8_t>& cfi_info,
+ const ArrayRef<const linker::LinkerPatch>& patches);
+
+ static void ReleaseSwapAllocatedCompiledMethod(CompiledMethodStorage* storage, CompiledMethod* m);
+
+ bool IsIntrinsic() const {
+ return GetPackedField<IsIntrinsicField>();
+ }
+
+ // Marks the compiled method as being generated using an intrinsic codegen.
+ // Such methods have no relationships to their code items.
+ // This affects debug information generated at link time.
+ void MarkAsIntrinsic() {
+ DCHECK(!IsIntrinsic());
+ SetPackedField<IsIntrinsicField>(/* value= */ true);
+ }
+
+ ArrayRef<const uint8_t> GetVmapTable() const;
+
+ ArrayRef<const uint8_t> GetCFIInfo() const;
+
+ ArrayRef<const linker::LinkerPatch> GetPatches() const;
+
+ private:
+ static constexpr size_t kIsIntrinsicLsb = kNumberOfCompiledCodePackedBits;
+ static constexpr size_t kIsIntrinsicSize = 1u;
+ static constexpr size_t kNumberOfCompiledMethodPackedBits = kIsIntrinsicLsb + kIsIntrinsicSize;
+ static_assert(kNumberOfCompiledMethodPackedBits <= CompiledCode::kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+
+ using IsIntrinsicField = BitField<bool, kIsIntrinsicLsb, kIsIntrinsicSize>;
+
+ // For quick code, holds code infos which contain stack maps, inline information, and etc.
+ const LengthPrefixedArray<uint8_t>* const vmap_table_;
+ // For quick code, a FDE entry for the debug_frame section.
+ const LengthPrefixedArray<uint8_t>* const cfi_info_;
+ // For quick code, linker patches needed by the method.
+ const LengthPrefixedArray<linker::LinkerPatch>* const patches_;
+};
+
+} // namespace art
+
+#endif // ART_DEX2OAT_DRIVER_COMPILED_METHOD_H_
diff --git a/dex2oat/driver/compiled_method_storage.cc b/dex2oat/driver/compiled_method_storage.cc
new file mode 100644
index 0000000..0e46f4e
--- /dev/null
+++ b/dex2oat/driver/compiled_method_storage.cc
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2015 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 <algorithm>
+#include <ostream>
+
+#include "compiled_method_storage.h"
+
+#include <android-base/logging.h>
+
+#include "base/data_hash.h"
+#include "base/utils.h"
+#include "compiled_method.h"
+#include "linker/linker_patch.h"
+#include "thread-current-inl.h"
+#include "utils/dedupe_set-inl.h"
+#include "utils/swap_space.h"
+
+namespace art {
+
+namespace { // anonymous namespace
+
+template <typename T>
+const LengthPrefixedArray<T>* CopyArray(SwapSpace* swap_space, const ArrayRef<const T>& array) {
+ DCHECK(!array.empty());
+ SwapAllocator<uint8_t> allocator(swap_space);
+ void* storage = allocator.allocate(LengthPrefixedArray<T>::ComputeSize(array.size()));
+ LengthPrefixedArray<T>* array_copy = new(storage) LengthPrefixedArray<T>(array.size());
+ std::copy(array.begin(), array.end(), array_copy->begin());
+ return array_copy;
+}
+
+template <typename T>
+void ReleaseArray(SwapSpace* swap_space, const LengthPrefixedArray<T>* array) {
+ SwapAllocator<uint8_t> allocator(swap_space);
+ size_t size = LengthPrefixedArray<T>::ComputeSize(array->size());
+ array->~LengthPrefixedArray<T>();
+ allocator.deallocate(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(array)), size);
+}
+
+} // anonymous namespace
+
+template <typename T, typename DedupeSetType>
+inline const LengthPrefixedArray<T>* CompiledMethodStorage::AllocateOrDeduplicateArray(
+ const ArrayRef<const T>& data,
+ DedupeSetType* dedupe_set) {
+ if (data.empty()) {
+ return nullptr;
+ } else if (!DedupeEnabled()) {
+ return CopyArray(swap_space_.get(), data);
+ } else {
+ return dedupe_set->Add(Thread::Current(), data);
+ }
+}
+
+template <typename T>
+inline void CompiledMethodStorage::ReleaseArrayIfNotDeduplicated(
+ const LengthPrefixedArray<T>* array) {
+ if (array != nullptr && !DedupeEnabled()) {
+ ReleaseArray(swap_space_.get(), array);
+ }
+}
+
+template <typename ContentType>
+class CompiledMethodStorage::DedupeHashFunc {
+ private:
+ static constexpr bool kUseMurmur3Hash = true;
+
+ public:
+ size_t operator()(const ArrayRef<ContentType>& array) const {
+ return DataHash()(array);
+ }
+};
+
+template <typename T>
+class CompiledMethodStorage::LengthPrefixedArrayAlloc {
+ public:
+ explicit LengthPrefixedArrayAlloc(SwapSpace* swap_space)
+ : swap_space_(swap_space) {
+ }
+
+ const LengthPrefixedArray<T>* Copy(const ArrayRef<const T>& array) {
+ return CopyArray(swap_space_, array);
+ }
+
+ void Destroy(const LengthPrefixedArray<T>* array) {
+ ReleaseArray(swap_space_, array);
+ }
+
+ private:
+ SwapSpace* const swap_space_;
+};
+
+class CompiledMethodStorage::ThunkMapKey {
+ public:
+ ThunkMapKey(linker::LinkerPatch::Type type, uint32_t custom_value1, uint32_t custom_value2)
+ : type_(type), custom_value1_(custom_value1), custom_value2_(custom_value2) {}
+
+ bool operator<(const ThunkMapKey& other) const {
+ if (custom_value1_ != other.custom_value1_) {
+ return custom_value1_ < other.custom_value1_;
+ }
+ if (custom_value2_ != other.custom_value2_) {
+ return custom_value2_ < other.custom_value2_;
+ }
+ return type_ < other.type_;
+ }
+
+ private:
+ linker::LinkerPatch::Type type_;
+ uint32_t custom_value1_;
+ uint32_t custom_value2_;
+};
+
+class CompiledMethodStorage::ThunkMapValue {
+ public:
+ ThunkMapValue(std::vector<uint8_t, SwapAllocator<uint8_t>>&& code,
+ const std::string& debug_name)
+ : code_(std::move(code)), debug_name_(debug_name) {}
+
+ ArrayRef<const uint8_t> GetCode() const {
+ return ArrayRef<const uint8_t>(code_);
+ }
+
+ const std::string& GetDebugName() const {
+ return debug_name_;
+ }
+
+ private:
+ std::vector<uint8_t, SwapAllocator<uint8_t>> code_;
+ std::string debug_name_;
+};
+
+CompiledMethodStorage::CompiledMethodStorage(int swap_fd)
+ : swap_space_(swap_fd == -1 ? nullptr : new SwapSpace(swap_fd, 10 * MB)),
+ dedupe_enabled_(true),
+ dedupe_code_("dedupe code", LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
+ dedupe_vmap_table_("dedupe vmap table",
+ LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
+ dedupe_cfi_info_("dedupe cfi info", LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())),
+ dedupe_linker_patches_("dedupe cfi info",
+ LengthPrefixedArrayAlloc<linker::LinkerPatch>(swap_space_.get())),
+ thunk_map_lock_("thunk_map_lock"),
+ thunk_map_(std::less<ThunkMapKey>(), SwapAllocator<ThunkMapValueType>(swap_space_.get())) {
+}
+
+CompiledMethodStorage::~CompiledMethodStorage() {
+ // All done by member destructors.
+}
+
+void CompiledMethodStorage::DumpMemoryUsage(std::ostream& os, bool extended) const {
+ if (swap_space_.get() != nullptr) {
+ const size_t swap_size = swap_space_->GetSize();
+ os << " swap=" << PrettySize(swap_size) << " (" << swap_size << "B)";
+ }
+ if (extended) {
+ Thread* self = Thread::Current();
+ os << "\nCode dedupe: " << dedupe_code_.DumpStats(self);
+ os << "\nVmap table dedupe: " << dedupe_vmap_table_.DumpStats(self);
+ os << "\nCFI info dedupe: " << dedupe_cfi_info_.DumpStats(self);
+ }
+}
+
+const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateCode(
+ const ArrayRef<const uint8_t>& code) {
+ return AllocateOrDeduplicateArray(code, &dedupe_code_);
+}
+
+void CompiledMethodStorage::ReleaseCode(const LengthPrefixedArray<uint8_t>* code) {
+ ReleaseArrayIfNotDeduplicated(code);
+}
+
+size_t CompiledMethodStorage::UniqueCodeEntries() const {
+ DCHECK(DedupeEnabled());
+ return dedupe_code_.Size(Thread::Current());
+}
+
+const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateVMapTable(
+ const ArrayRef<const uint8_t>& table) {
+ return AllocateOrDeduplicateArray(table, &dedupe_vmap_table_);
+}
+
+void CompiledMethodStorage::ReleaseVMapTable(const LengthPrefixedArray<uint8_t>* table) {
+ ReleaseArrayIfNotDeduplicated(table);
+}
+
+size_t CompiledMethodStorage::UniqueVMapTableEntries() const {
+ DCHECK(DedupeEnabled());
+ return dedupe_vmap_table_.Size(Thread::Current());
+}
+
+const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateCFIInfo(
+ const ArrayRef<const uint8_t>& cfi_info) {
+ return AllocateOrDeduplicateArray(cfi_info, &dedupe_cfi_info_);
+}
+
+void CompiledMethodStorage::ReleaseCFIInfo(const LengthPrefixedArray<uint8_t>* cfi_info) {
+ ReleaseArrayIfNotDeduplicated(cfi_info);
+}
+
+size_t CompiledMethodStorage::UniqueCFIInfoEntries() const {
+ DCHECK(DedupeEnabled());
+ return dedupe_cfi_info_.Size(Thread::Current());
+}
+
+const LengthPrefixedArray<linker::LinkerPatch>* CompiledMethodStorage::DeduplicateLinkerPatches(
+ const ArrayRef<const linker::LinkerPatch>& linker_patches) {
+ return AllocateOrDeduplicateArray(linker_patches, &dedupe_linker_patches_);
+}
+
+void CompiledMethodStorage::ReleaseLinkerPatches(
+ const LengthPrefixedArray<linker::LinkerPatch>* linker_patches) {
+ ReleaseArrayIfNotDeduplicated(linker_patches);
+}
+
+size_t CompiledMethodStorage::UniqueLinkerPatchesEntries() const {
+ DCHECK(DedupeEnabled());
+ return dedupe_linker_patches_.Size(Thread::Current());
+}
+
+CompiledMethodStorage::ThunkMapKey CompiledMethodStorage::GetThunkMapKey(
+ const linker::LinkerPatch& linker_patch) {
+ uint32_t custom_value1 = 0u;
+ uint32_t custom_value2 = 0u;
+ switch (linker_patch.GetType()) {
+ case linker::LinkerPatch::Type::kCallEntrypoint:
+ custom_value1 = linker_patch.EntrypointOffset();
+ break;
+ case linker::LinkerPatch::Type::kBakerReadBarrierBranch:
+ custom_value1 = linker_patch.GetBakerCustomValue1();
+ custom_value2 = linker_patch.GetBakerCustomValue2();
+ break;
+ case linker::LinkerPatch::Type::kCallRelative:
+ // No custom values.
+ break;
+ default:
+ LOG(FATAL) << "Unexpected patch type: " << linker_patch.GetType();
+ UNREACHABLE();
+ }
+ return ThunkMapKey(linker_patch.GetType(), custom_value1, custom_value2);
+}
+
+CompiledMethod* CompiledMethodStorage::CreateCompiledMethod(
+ InstructionSet instruction_set,
+ ArrayRef<const uint8_t> code,
+ ArrayRef<const uint8_t> stack_map,
+ ArrayRef<const uint8_t> cfi,
+ ArrayRef<const linker::LinkerPatch> patches,
+ bool is_intrinsic) {
+ CompiledMethod* compiled_method = CompiledMethod::SwapAllocCompiledMethod(
+ this, instruction_set, code, stack_map, cfi, patches);
+ if (is_intrinsic) {
+ compiled_method->MarkAsIntrinsic();
+ }
+ return compiled_method;
+}
+
+ArrayRef<const uint8_t> CompiledMethodStorage::GetThunkCode(const linker::LinkerPatch& linker_patch,
+ /*out*/ std::string* debug_name) {
+ ThunkMapKey key = GetThunkMapKey(linker_patch);
+ MutexLock lock(Thread::Current(), thunk_map_lock_);
+ auto it = thunk_map_.find(key);
+ if (it != thunk_map_.end()) {
+ const ThunkMapValue& value = it->second;
+ if (debug_name != nullptr) {
+ *debug_name = value.GetDebugName();
+ }
+ return value.GetCode();
+ } else {
+ if (debug_name != nullptr) {
+ *debug_name = std::string();
+ }
+ return ArrayRef<const uint8_t>();
+ }
+}
+
+void CompiledMethodStorage::SetThunkCode(const linker::LinkerPatch& linker_patch,
+ ArrayRef<const uint8_t> code,
+ const std::string& debug_name) {
+ DCHECK(!code.empty());
+ ThunkMapKey key = GetThunkMapKey(linker_patch);
+ std::vector<uint8_t, SwapAllocator<uint8_t>> code_copy(
+ code.begin(), code.end(), SwapAllocator<uint8_t>(swap_space_.get()));
+ ThunkMapValue value(std::move(code_copy), debug_name);
+ MutexLock lock(Thread::Current(), thunk_map_lock_);
+ // Note: Multiple threads can try and compile the same thunk, so this may not create a new entry.
+ thunk_map_.emplace(key, std::move(value));
+}
+
+} // namespace art
diff --git a/dex2oat/driver/compiled_method_storage.h b/dex2oat/driver/compiled_method_storage.h
new file mode 100644
index 0000000..3b0304e
--- /dev/null
+++ b/dex2oat/driver/compiled_method_storage.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_DEX2OAT_DRIVER_COMPILED_METHOD_STORAGE_H_
+#define ART_DEX2OAT_DRIVER_COMPILED_METHOD_STORAGE_H_
+
+#include <iosfwd>
+#include <map>
+#include <memory>
+
+#include "base/array_ref.h"
+#include "base/length_prefixed_array.h"
+#include "base/macros.h"
+#include "driver/compiled_code_storage.h"
+#include "utils/dedupe_set.h"
+#include "utils/swap_space.h"
+
+namespace art {
+
+namespace linker {
+class LinkerPatch;
+} // namespace linker
+
+// TODO: Find a better name. This stores both method and non-method (thunks) code.
+class CompiledMethodStorage final : public CompiledCodeStorage {
+ public:
+ explicit CompiledMethodStorage(int swap_fd);
+ ~CompiledMethodStorage();
+
+ void DumpMemoryUsage(std::ostream& os, bool extended) const;
+
+ void SetDedupeEnabled(bool dedupe_enabled) {
+ dedupe_enabled_ = dedupe_enabled;
+ }
+ bool DedupeEnabled() const {
+ return dedupe_enabled_;
+ }
+
+ SwapAllocator<void> GetSwapSpaceAllocator() {
+ return SwapAllocator<void>(swap_space_.get());
+ }
+
+ const LengthPrefixedArray<uint8_t>* DeduplicateCode(const ArrayRef<const uint8_t>& code);
+ void ReleaseCode(const LengthPrefixedArray<uint8_t>* code);
+ size_t UniqueCodeEntries() const;
+
+ const LengthPrefixedArray<uint8_t>* DeduplicateVMapTable(const ArrayRef<const uint8_t>& table);
+ void ReleaseVMapTable(const LengthPrefixedArray<uint8_t>* table);
+ size_t UniqueVMapTableEntries() const;
+
+ const LengthPrefixedArray<uint8_t>* DeduplicateCFIInfo(const ArrayRef<const uint8_t>& cfi_info);
+ void ReleaseCFIInfo(const LengthPrefixedArray<uint8_t>* cfi_info);
+ size_t UniqueCFIInfoEntries() const;
+
+ const LengthPrefixedArray<linker::LinkerPatch>* DeduplicateLinkerPatches(
+ const ArrayRef<const linker::LinkerPatch>& linker_patches);
+ void ReleaseLinkerPatches(const LengthPrefixedArray<linker::LinkerPatch>* linker_patches);
+ size_t UniqueLinkerPatchesEntries() const;
+
+ CompiledMethod* CreateCompiledMethod(InstructionSet instruction_set,
+ ArrayRef<const uint8_t> code,
+ ArrayRef<const uint8_t> stack_map,
+ ArrayRef<const uint8_t> cfi,
+ ArrayRef<const linker::LinkerPatch> patches,
+ bool is_intrinsic) override;
+
+ // Returns the code associated with the given patch.
+ // If the code has not been set, returns empty data.
+ // If `debug_name` is not null, stores the associated debug name in `*debug_name`.
+ ArrayRef<const uint8_t> GetThunkCode(const linker::LinkerPatch& linker_patch,
+ /*out*/ std::string* debug_name = nullptr) override;
+
+ // Sets the code and debug name associated with the given patch.
+ void SetThunkCode(const linker::LinkerPatch& linker_patch,
+ ArrayRef<const uint8_t> code,
+ const std::string& debug_name) override;
+
+ private:
+ class ThunkMapKey;
+ class ThunkMapValue;
+ using ThunkMapValueType = std::pair<const ThunkMapKey, ThunkMapValue>;
+ using ThunkMap = std::map<ThunkMapKey,
+ ThunkMapValue,
+ std::less<ThunkMapKey>,
+ SwapAllocator<ThunkMapValueType>>;
+ static_assert(std::is_same<ThunkMapValueType, ThunkMap::value_type>::value, "Value type check.");
+
+ static ThunkMapKey GetThunkMapKey(const linker::LinkerPatch& linker_patch);
+
+ template <typename T, typename DedupeSetType>
+ const LengthPrefixedArray<T>* AllocateOrDeduplicateArray(const ArrayRef<const T>& data,
+ DedupeSetType* dedupe_set);
+
+ template <typename T>
+ void ReleaseArrayIfNotDeduplicated(const LengthPrefixedArray<T>* array);
+
+ // DeDuplication data structures.
+ template <typename ContentType>
+ class DedupeHashFunc;
+
+ template <typename T>
+ class LengthPrefixedArrayAlloc;
+
+ template <typename T>
+ using ArrayDedupeSet = DedupeSet<ArrayRef<const T>,
+ LengthPrefixedArray<T>,
+ LengthPrefixedArrayAlloc<T>,
+ size_t,
+ DedupeHashFunc<const T>,
+ 4>;
+
+ // Swap pool and allocator used for native allocations. May be file-backed. Needs to be first
+ // as other fields rely on this.
+ std::unique_ptr<SwapSpace> swap_space_;
+
+ bool dedupe_enabled_;
+
+ ArrayDedupeSet<uint8_t> dedupe_code_;
+ ArrayDedupeSet<uint8_t> dedupe_vmap_table_;
+ ArrayDedupeSet<uint8_t> dedupe_cfi_info_;
+ ArrayDedupeSet<linker::LinkerPatch> dedupe_linker_patches_;
+
+ Mutex thunk_map_lock_;
+ ThunkMap thunk_map_ GUARDED_BY(thunk_map_lock_);
+
+ DISALLOW_COPY_AND_ASSIGN(CompiledMethodStorage);
+};
+
+} // namespace art
+
+#endif // ART_DEX2OAT_DRIVER_COMPILED_METHOD_STORAGE_H_
diff --git a/compiler/driver/compiled_method_storage_test.cc b/dex2oat/driver/compiled_method_storage_test.cc
similarity index 100%
rename from compiler/driver/compiled_method_storage_test.cc
rename to dex2oat/driver/compiled_method_storage_test.cc
diff --git a/dex2oat/driver/compiler_driver.cc b/dex2oat/driver/compiler_driver.cc
index d9509b0..f199ede 100644
--- a/dex2oat/driver/compiler_driver.cc
+++ b/dex2oat/driver/compiler_driver.cc
@@ -87,6 +87,7 @@
#include "verifier/class_verifier.h"
#include "verifier/verifier_deps.h"
#include "verifier/verifier_enums.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -252,10 +253,12 @@
CompilerDriver::CompilerDriver(
const CompilerOptions* compiler_options,
+ const VerificationResults* verification_results,
Compiler::Kind compiler_kind,
size_t thread_count,
int swap_fd)
: compiler_options_(compiler_options),
+ verification_results_(verification_results),
compiler_(),
compiler_kind_(compiler_kind),
number_of_soft_verifier_failures_(0),
@@ -493,7 +496,7 @@
// Method is annotated with @NeverCompile and should not be compiled.
} else {
const CompilerOptions& compiler_options = driver->GetCompilerOptions();
- const VerificationResults* results = compiler_options.GetVerificationResults();
+ const VerificationResults* results = driver->GetVerificationResults();
DCHECK(results != nullptr);
MethodReference method_ref(&dex_file, method_idx);
// Don't compile class initializers unless kEverything.
@@ -1008,13 +1011,68 @@
HashSet<std::string>* const image_classes_;
};
-// Add classes which contain intrinsics methods to the list of image classes.
-static void AddClassesContainingIntrinsics(/* out */ HashSet<std::string>* image_classes) {
-#define ADD_INTRINSIC_OWNER_CLASS(_, __, ___, ____, _____, ClassName, ______, _______) \
- image_classes->insert(ClassName);
+// Verify that classes which contain intrinsics methods are in the list of image classes.
+static void VerifyClassesContainingIntrinsicsAreImageClasses(HashSet<std::string>* image_classes) {
+#define CHECK_INTRINSIC_OWNER_CLASS(_, __, ___, ____, _____, ClassName, ______, _______) \
+ CHECK(image_classes->find(std::string_view(ClassName)) != image_classes->end());
- INTRINSICS_LIST(ADD_INTRINSIC_OWNER_CLASS)
-#undef ADD_INTRINSIC_OWNER_CLASS
+ INTRINSICS_LIST(CHECK_INTRINSIC_OWNER_CLASS)
+#undef CHECK_INTRINSIC_OWNER_CLASS
+}
+
+// We need to put classes required by app class loaders to the boot image,
+// otherwise we would not be able to store app class loaders in app images.
+static void AddClassLoaderClasses(/* out */ HashSet<std::string>* image_classes) {
+ ScopedObjectAccess soa(Thread::Current());
+ // Well known classes have been loaded and shall be added to image classes
+ // by the `RecordImageClassesVisitor`. However, there are fields with array
+ // types which we need to add to the image classes explicitly.
+ ArtField* class_loader_array_fields[] = {
+ WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders,
+ // BaseDexClassLoader.sharedLibraryLoadersAfter has the same array type as above.
+ WellKnownClasses::dalvik_system_DexPathList_dexElements,
+ };
+ for (ArtField* field : class_loader_array_fields) {
+ const char* field_type_descriptor = field->GetTypeDescriptor();
+ DCHECK_EQ(field_type_descriptor[0], '[');
+ image_classes->insert(field_type_descriptor);
+ }
+}
+
+static void VerifyClassLoaderClassesAreImageClasses(/* out */ HashSet<std::string>* image_classes) {
+ ScopedObjectAccess soa(Thread::Current());
+ ScopedAssertNoThreadSuspension sants(__FUNCTION__);
+ ObjPtr<mirror::Class> class_loader_classes[] = {
+ WellKnownClasses::dalvik_system_BaseDexClassLoader.Get(),
+ WellKnownClasses::dalvik_system_DelegateLastClassLoader.Get(),
+ WellKnownClasses::dalvik_system_DexClassLoader.Get(),
+ WellKnownClasses::dalvik_system_DexFile.Get(),
+ WellKnownClasses::dalvik_system_DexPathList.Get(),
+ WellKnownClasses::dalvik_system_DexPathList__Element.Get(),
+ WellKnownClasses::dalvik_system_InMemoryDexClassLoader.Get(),
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
+ WellKnownClasses::java_lang_BootClassLoader.Get(),
+ WellKnownClasses::java_lang_ClassLoader.Get(),
+ };
+ for (ObjPtr<mirror::Class> klass : class_loader_classes) {
+ std::string temp;
+ std::string_view descriptor = klass->GetDescriptor(&temp);
+ CHECK(image_classes->find(descriptor) != image_classes->end());
+ }
+ ArtField* class_loader_fields[] = {
+ WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList,
+ WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders,
+ WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter,
+ WellKnownClasses::dalvik_system_DexFile_cookie,
+ WellKnownClasses::dalvik_system_DexFile_fileName,
+ WellKnownClasses::dalvik_system_DexPathList_dexElements,
+ WellKnownClasses::dalvik_system_DexPathList__Element_dexFile,
+ WellKnownClasses::java_lang_ClassLoader_parent,
+ };
+ for (ArtField* field : class_loader_fields) {
+ std::string_view field_type_descriptor = field->GetTypeDescriptor();
+ CHECK(image_classes->find(field_type_descriptor) != image_classes->end());
+ }
}
// Make a list of descriptors for classes to include in the image
@@ -1028,7 +1086,10 @@
TimingLogger::ScopedTiming t("LoadImageClasses", timings);
if (GetCompilerOptions().IsBootImage()) {
- AddClassesContainingIntrinsics(image_classes);
+ // Image classes of intrinsics are loaded and shall be added
+ // to image classes by the `RecordImageClassesVisitor`.
+ // Add classes needed for storing class loaders in app images.
+ AddClassLoaderClasses(image_classes);
}
// Make a first pass to load all classes explicitly listed in the file
@@ -1099,6 +1160,11 @@
RecordImageClassesVisitor visitor(image_classes);
class_linker->VisitClasses(&visitor);
+ if (kIsDebugBuild && GetCompilerOptions().IsBootImage()) {
+ VerifyClassesContainingIntrinsicsAreImageClasses(image_classes);
+ VerifyClassLoaderClassesAreImageClasses(image_classes);
+ }
+
if (GetCompilerOptions().IsBootImage()) {
CHECK(!image_classes->empty());
}
@@ -1109,6 +1175,7 @@
HashSet<std::string>* image_classes)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK_EQ(self, Thread::Current());
+ DCHECK(klass->IsResolved());
Runtime* runtime = Runtime::Current();
gc::Heap* heap = runtime->GetHeap();
if (heap->ObjectIsInBootImageSpace(klass)) {
@@ -1536,8 +1603,7 @@
mirror::Throwable* exception = soa.Self()->GetException();
DCHECK(exception != nullptr);
VLOG(compiler) << "Exception during type resolution: " << exception->Dump();
- if (exception->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_OutOfMemoryError)) {
+ if (exception->GetClass() == WellKnownClasses::java_lang_OutOfMemoryError.Get()) {
// There's little point continuing compilation if the heap is exhausted.
// Trying to do so would also introduce non-deterministic compilation results.
LOG(FATAL) << "Out of memory during type resolution for compilation";
@@ -1653,8 +1719,8 @@
bool CompilerDriver::FastVerify(jobject jclass_loader,
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings) {
- verifier::VerifierDeps* verifier_deps =
- Runtime::Current()->GetCompilerCallbacks()->GetVerifierDeps();
+ CompilerCallbacks* callbacks = Runtime::Current()->GetCompilerCallbacks();
+ verifier::VerifierDeps* verifier_deps = callbacks->GetVerifierDeps();
// If there exist VerifierDeps that aren't the ones we just created to output, use them to verify.
if (verifier_deps == nullptr || verifier_deps->OutputOnly()) {
return false;
@@ -1672,6 +1738,9 @@
class_loader,
dex_files,
&error_msg)) {
+ // Clear the information we have as we are going to re-verify and we do not
+ // want to keep that a class is verified.
+ verifier_deps->ClearData(dex_files);
LOG(WARNING) << "Fast verification failed: " << error_msg;
return false;
}
@@ -1690,28 +1759,34 @@
const std::vector<bool>& verified_classes = verifier_deps->GetVerifiedClasses(*dex_file);
DCHECK_EQ(verified_classes.size(), dex_file->NumClassDefs());
for (ClassAccessor accessor : dex_file->GetClasses()) {
- if (verified_classes[accessor.GetClassDefIndex()]) {
- if (compiler_only_verifies) {
- // Just update the compiled_classes_ map. The compiler doesn't need to resolve
- // the type.
- ClassReference ref(dex_file, accessor.GetClassDefIndex());
- const ClassStatus existing = ClassStatus::kNotReady;
- ClassStateTable::InsertResult result =
- compiled_classes_.Insert(ref, existing, ClassStatus::kVerifiedNeedsAccessChecks);
- CHECK_EQ(result, ClassStateTable::kInsertResultSuccess) << ref.dex_file->GetLocation();
- } else {
- // Update the class status, so later compilation stages know they don't need to verify
- // the class.
- LoadAndUpdateStatus(
- accessor, ClassStatus::kVerifiedNeedsAccessChecks, class_loader, soa.Self());
- }
- } else if (!compiler_only_verifies) {
- // Make sure later compilation stages know they should not try to verify
- // this class again.
- LoadAndUpdateStatus(accessor,
- ClassStatus::kRetryVerificationAtRuntime,
- class_loader,
- soa.Self());
+ ClassStatus status = verified_classes[accessor.GetClassDefIndex()]
+ ? ClassStatus::kVerifiedNeedsAccessChecks
+ : ClassStatus::kRetryVerificationAtRuntime;
+ if (compiler_only_verifies) {
+ // Just update the compiled_classes_ map. The compiler doesn't need to resolve
+ // the type.
+ ClassReference ref(dex_file, accessor.GetClassDefIndex());
+ const ClassStatus existing = ClassStatus::kNotReady;
+ // Note: when dex files are compiled inidividually, the class may have
+ // been verified in a previous stage. This means this insertion can
+ // fail, but that's OK.
+ compiled_classes_.Insert(ref, existing, status);
+ } else {
+ // Update the class status, so later compilation stages know they don't need to verify
+ // the class.
+ LoadAndUpdateStatus(accessor, status, class_loader, soa.Self());
+ }
+
+ // Vdex marks class as unverified for two reasons only:
+ // 1. It has a hard failure, or
+ // 2. Once of its method needs lock counting.
+ //
+ // The optimizing compiler expects a method to not have a hard failure before
+ // compiling it, so for simplicity just disable any compilation of methods
+ // of these classes.
+ if (status == ClassStatus::kRetryVerificationAtRuntime) {
+ ClassReference ref(dex_file, accessor.GetClassDefIndex());
+ callbacks->AddUncompilableClass(ref);
}
}
}
@@ -2232,6 +2307,19 @@
// Make sure the class initialization did not leave any local references.
self->GetJniEnv()->AssertLocalsEmpty();
}
+
+ if (!klass->IsVisiblyInitialized() &&
+ (is_boot_image || is_boot_image_extension) &&
+ !compiler_options.IsPreloadedClass(PrettyDescriptor(descriptor).c_str())) {
+ klass->SetInBootImageAndNotInPreloadedClasses();
+ }
+
+ if (compiler_options.CompileArtTest()) {
+ // For stress testing and unit-testing the clinit check in compiled code feature.
+ if (kIsDebugBuild || EndsWith(std::string_view(descriptor), "$NoPreloadHolder;")) {
+ klass->SetInBootImageAndNotInPreloadedClasses();
+ }
+ }
}
private:
@@ -2493,7 +2581,8 @@
ClassAccessor accessor(dex_file, class_def_index);
CompilerDriver* const driver = context.GetCompiler();
// Skip compiling classes with generic verifier failures since they will still fail at runtime
- if (driver->GetCompilerOptions().GetVerificationResults()->IsClassRejected(ref)) {
+ DCHECK(driver->GetVerificationResults() != nullptr);
+ if (driver->GetVerificationResults()->IsClassRejected(ref)) {
return;
}
// Use a scoped object access to perform to the quick SkipClass check.
diff --git a/dex2oat/driver/compiler_driver.h b/dex2oat/driver/compiler_driver.h
index ed8fc2f..7985771 100644
--- a/dex2oat/driver/compiler_driver.h
+++ b/dex2oat/driver/compiler_driver.h
@@ -85,6 +85,7 @@
// can assume will be in the image, with null implying all available
// classes.
CompilerDriver(const CompilerOptions* compiler_options,
+ const VerificationResults* verification_results,
Compiler::Kind compiler_kind,
size_t thread_count,
int swap_fd);
@@ -115,6 +116,10 @@
return *compiler_options_;
}
+ const VerificationResults* GetVerificationResults() const {
+ return verification_results_;
+ }
+
Compiler* GetCompiler() const {
return compiler_.get();
}
@@ -295,6 +300,7 @@
/*inout*/ TimingLogger* timings);
const CompilerOptions* const compiler_options_;
+ const VerificationResults* const verification_results_;
std::unique_ptr<Compiler> compiler_;
Compiler::Kind compiler_kind_;
diff --git a/dex2oat/driver/compiler_driver_test.cc b/dex2oat/driver/compiler_driver_test.cc
index 65aa888..759426a 100644
--- a/dex2oat/driver/compiler_driver_test.cc
+++ b/dex2oat/driver/compiler_driver_test.cc
@@ -25,6 +25,7 @@
#include "base/casts.h"
#include "class_linker-inl.h"
#include "common_compiler_driver_test.h"
+#include "compiled_method-inl.h"
#include "compiler_callbacks.h"
#include "dex/dex_file.h"
#include "dex/dex_file_types.h"
@@ -80,14 +81,19 @@
void MakeExecutable(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
CHECK(method != nullptr);
- const CompiledMethod* compiled_method = nullptr;
+ const void* method_code = nullptr;
if (!method->IsAbstract()) {
- const DexFile& dex_file = *method->GetDexFile();
- compiled_method =
- compiler_driver_->GetCompiledMethod(MethodReference(&dex_file,
- method->GetDexMethodIndex()));
+ MethodReference method_ref(method->GetDexFile(), method->GetDexMethodIndex());
+ const CompiledMethod* compiled_method = compiler_driver_->GetCompiledMethod(method_ref);
+ // If the code size is 0 it means the method was skipped due to profile guided compilation.
+ if (compiled_method != nullptr && compiled_method->GetQuickCode().size() != 0u) {
+ method_code = CommonCompilerTest::MakeExecutable(compiled_method->GetQuickCode(),
+ compiled_method->GetVmapTable(),
+ compiled_method->GetInstructionSet());
+ LOG(INFO) << "MakeExecutable " << method->PrettyMethod() << " code=" << method_code;
+ }
}
- CommonCompilerTest::MakeExecutable(method, compiled_method);
+ runtime_->GetInstrumentation()->InitializeMethodsCode(method, /*aot_code=*/ method_code);
}
void MakeDexFileExecutable(jobject class_loader, const DexFile& dex_file) {
@@ -124,19 +130,15 @@
ASSERT_TRUE(java_lang_dex_file_ != nullptr);
const DexFile& dex = *java_lang_dex_file_;
ObjPtr<mirror::DexCache> dex_cache = class_linker_->FindDexCache(soa.Self(), dex);
- EXPECT_EQ(dex.NumStringIds(), dex_cache->NumStrings());
for (size_t i = 0; i < dex_cache->NumStrings(); i++) {
const ObjPtr<mirror::String> string = dex_cache->GetResolvedString(dex::StringIndex(i));
EXPECT_TRUE(string != nullptr) << "string_idx=" << i;
}
- EXPECT_EQ(dex.NumTypeIds(), dex_cache->NumResolvedTypes());
for (size_t i = 0; i < dex_cache->NumResolvedTypes(); i++) {
const ObjPtr<mirror::Class> type = dex_cache->GetResolvedType(dex::TypeIndex(i));
EXPECT_TRUE(type != nullptr)
<< "type_idx=" << i << " " << dex.GetTypeDescriptor(dex.GetTypeId(dex::TypeIndex(i)));
}
- EXPECT_TRUE(dex_cache->StaticMethodSize() == dex_cache->NumResolvedMethods()
- || dex.NumMethodIds() == dex_cache->NumResolvedMethods());
for (size_t i = 0; i < dex_cache->NumResolvedMethods(); i++) {
// FIXME: This is outdated for hash-based method array.
ArtMethod* method = dex_cache->GetResolvedMethod(i);
@@ -147,8 +149,6 @@
<< " " << dex.GetMethodDeclaringClassDescriptor(dex.GetMethodId(i)) << " "
<< dex.GetMethodName(dex.GetMethodId(i));
}
- EXPECT_TRUE(dex_cache->StaticArtFieldSize() == dex_cache->NumResolvedFields()
- || dex.NumFieldIds() == dex_cache->NumResolvedFields());
for (size_t i = 0; i < dex_cache->NumResolvedFields(); i++) {
// FIXME: This is outdated for hash-based field array.
ArtField* field = dex_cache->GetResolvedField(i);
diff --git a/dex2oat/linker/arm/relative_patcher_arm_base.cc b/dex2oat/linker/arm/relative_patcher_arm_base.cc
index 35e799a..1cb72f6 100644
--- a/dex2oat/linker/arm/relative_patcher_arm_base.cc
+++ b/dex2oat/linker/arm/relative_patcher_arm_base.cc
@@ -17,9 +17,9 @@
#include "linker/arm/relative_patcher_arm_base.h"
#include "base/stl_util.h"
-#include "compiled_method-inl.h"
#include "debug/method_debug_info.h"
#include "dex/dex_file_types.h"
+#include "driver/compiled_method-inl.h"
#include "linker/linker_patch.h"
#include "oat.h"
#include "oat_quick_method_header.h"
@@ -462,7 +462,7 @@
}
unreserved_thunks_.insert(unreserved_thunks_.begin() + index, data);
// We may need to update the max next offset(s) if the thunk code would not fit.
- size_t alignment = GetInstructionSetAlignment(instruction_set_);
+ size_t alignment = GetInstructionSetCodeAlignment(instruction_set_);
if (index + 1u != unreserved_thunks_.size()) {
// Note: Ignore the return value as we need to process previous thunks regardless.
data->MakeSpaceBefore(*unreserved_thunks_[index + 1u], alignment);
@@ -501,7 +501,8 @@
if (!result.first) {
break;
}
- uint32_t target_offset = result.second - CompiledCode::CodeDelta(instruction_set_);
+ uint32_t target_offset =
+ result.second - GetInstructionSetEntryPointAdjustment(instruction_set_);
if (target_offset >= patch_offset) {
DCHECK_LE(target_offset - patch_offset, max_positive_displacement);
} else if (patch_offset - target_offset > max_negative_displacement) {
@@ -535,7 +536,7 @@
inline uint32_t ArmBaseRelativePatcher::CalculateMaxNextOffset(uint32_t patch_offset,
const ThunkKey& key) {
return RoundDown(patch_offset + MaxPositiveDisplacement(key),
- GetInstructionSetAlignment(instruction_set_));
+ GetInstructionSetCodeAlignment(instruction_set_));
}
inline ArmBaseRelativePatcher::ThunkData ArmBaseRelativePatcher::ThunkDataForPatch(
diff --git a/dex2oat/linker/arm/relative_patcher_thumb2.cc b/dex2oat/linker/arm/relative_patcher_thumb2.cc
index 99728cf..45a4e8b 100644
--- a/dex2oat/linker/arm/relative_patcher_thumb2.cc
+++ b/dex2oat/linker/arm/relative_patcher_thumb2.cc
@@ -22,7 +22,7 @@
#include "art_method.h"
#include "base/bit_utils.h"
#include "base/malloc_arena_pool.h"
-#include "compiled_method.h"
+#include "driver/compiled_method.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "linker/linker_patch.h"
#include "lock_word.h"
diff --git a/dex2oat/linker/arm/relative_patcher_thumb2_test.cc b/dex2oat/linker/arm/relative_patcher_thumb2_test.cc
index 296bf61..f7abd6b 100644
--- a/dex2oat/linker/arm/relative_patcher_thumb2_test.cc
+++ b/dex2oat/linker/arm/relative_patcher_thumb2_test.cc
@@ -145,7 +145,7 @@
const ArrayRef<const uint8_t>& last_method_code,
const ArrayRef<const LinkerPatch>& last_method_patches,
uint32_t distance_without_thunks) {
- CHECK_EQ(distance_without_thunks % kArmAlignment, 0u);
+ CHECK_EQ(distance_without_thunks % kArmCodeAlignment, 0u);
uint32_t method1_offset =
kTrampolineSize + CodeAlignmentSize(kTrampolineSize) + sizeof(OatQuickMethodHeader);
AddCompiledMethod(MethodRef(1u), method1_code, method1_patches);
@@ -153,7 +153,7 @@
// We want to put the last method at a very precise offset.
const uint32_t last_method_offset = method1_offset + distance_without_thunks;
- CHECK_ALIGNED(last_method_offset, kArmAlignment);
+ CHECK_ALIGNED(last_method_offset, kArmCodeAlignment);
const uint32_t gap_end = last_method_offset - sizeof(OatQuickMethodHeader);
// Fill the gap with intermediate methods in chunks of 2MiB and the first in [2MiB, 4MiB).
@@ -562,24 +562,25 @@
bl_offset_in_method1 + just_over_max_positive_disp);
ASSERT_EQ(kExpectedLastMethodIdx, last_method_idx);
uint32_t method_after_thunk_idx = last_method_idx;
- if (sizeof(OatQuickMethodHeader) < kArmAlignment) {
- // The thunk needs to start on a kArmAlignment-aligned address before the address where the
+ if (sizeof(OatQuickMethodHeader) < kArmCodeAlignment) {
+ // The thunk needs to start on a kArmCodeAlignment-aligned address before the address where the
// last method would have been if there was no thunk. If the size of the OatQuickMethodHeader
- // is at least kArmAlignment, the thunk start shall fit between the previous filler method
+ // is at least kArmCodeAlignment, the thunk start shall fit between the previous filler method
// and that address. Otherwise, it shall be inserted before that filler method.
method_after_thunk_idx -= 1u;
}
uint32_t method1_offset = GetMethodOffset(1u);
uint32_t method_after_thunk_offset = GetMethodOffset(method_after_thunk_idx);
- ASSERT_TRUE(IsAligned<kArmAlignment>(method_after_thunk_offset));
+ ASSERT_TRUE(IsAligned<kArmCodeAlignment>(method_after_thunk_offset));
uint32_t method_after_thunk_header_offset =
method_after_thunk_offset - sizeof(OatQuickMethodHeader);
uint32_t thunk_size = MethodCallThunkSize();
- uint32_t thunk_offset = RoundDown(method_after_thunk_header_offset - thunk_size, kArmAlignment);
+ uint32_t thunk_offset =
+ RoundDown(method_after_thunk_header_offset - thunk_size, kArmCodeAlignment);
DCHECK_EQ(thunk_offset + thunk_size + CodeAlignmentSize(thunk_offset + thunk_size),
method_after_thunk_header_offset);
- ASSERT_TRUE(IsAligned<kArmAlignment>(thunk_offset));
+ ASSERT_TRUE(IsAligned<kArmCodeAlignment>(thunk_offset));
uint32_t diff = thunk_offset - (method1_offset + bl_offset_in_method1 + 4u /* PC adjustment */);
ASSERT_TRUE(IsAligned<2u>(diff));
ASSERT_GE(diff, 16 * MB - (1u << 22)); // Simple encoding, unknown bits fit into imm10:imm11:0.
@@ -725,7 +726,7 @@
Link();
// All thunks are at the end.
- uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment);
+ uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmCodeAlignment);
method_idx = 0u;
for (uint32_t base_reg : kBakerValidRegs) {
for (uint32_t holder_reg : kBakerValidRegs) {
@@ -791,7 +792,7 @@
// Do not check the rest of the implementation.
// The next thunk follows on the next aligned offset.
- thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment);
+ thunk_offset += RoundUp(expected_thunk.size(), kArmCodeAlignment);
}
}
}
@@ -823,7 +824,7 @@
Link();
// All thunks are at the end.
- uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment);
+ uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmCodeAlignment);
method_idx = 0u;
for (uint32_t base_reg : kBakerValidRegs) {
if (base_reg >= 8u) {
@@ -892,7 +893,7 @@
// Do not check the rest of the implementation.
// The next thunk follows on the next aligned offset.
- thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment);
+ thunk_offset += RoundUp(expected_thunk.size(), kArmCodeAlignment);
}
}
}
@@ -945,9 +946,10 @@
constexpr uint32_t expected_thunk_offset =
kLiteralOffset1 + kPcAdjustment + /* kMaxBcondPositiveDisplacement */ ((1 << 20) - 2u);
- static_assert(IsAligned<kArmAlignment>(expected_thunk_offset), "Target offset must be aligned.");
+ static_assert(IsAligned<kArmCodeAlignment>(expected_thunk_offset),
+ "Target offset must be aligned.");
size_t filler1_size = expected_thunk_offset -
- RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmAlignment);
+ RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmCodeAlignment);
std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 2u);
ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
AddCompiledMethod(MethodRef(2u), filler1_code);
@@ -956,7 +958,7 @@
AddCompiledMethod(MethodRef(3u), kNopCode);
constexpr uint32_t kLiteralOffset2 = 4;
- static_assert(IsAligned<kArmAlignment>(kLiteralOffset2 + kPcAdjustment),
+ static_assert(IsAligned<kArmCodeAlignment>(kLiteralOffset2 + kPcAdjustment),
"PC for BNE must be aligned.");
// Allow reaching the thunk from the very beginning of a method almost 1MiB away. Backward branch
@@ -968,8 +970,8 @@
CompileBakerOffsetThunk(/* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false).size();
size_t filler2_size =
1 * MB - (kLiteralOffset2 + kPcAdjustment)
- - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArmAlignment)
- - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArmAlignment)
+ - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArmCodeAlignment)
+ - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArmCodeAlignment)
- sizeof(OatQuickMethodHeader);
std::vector<uint8_t> raw_filler2_code = GenNops(filler2_size / 2u);
ArrayRef<const uint8_t> filler2_code(raw_filler2_code);
@@ -1013,16 +1015,18 @@
constexpr uint32_t expected_thunk_offset =
kLiteralOffset1 + kPcAdjustment + /* kMaxBcondPositiveDisplacement + 2 */ (1u << 20);
- static_assert(IsAligned<kArmAlignment>(expected_thunk_offset), "Target offset must be aligned.");
+ static_assert(IsAligned<kArmCodeAlignment>(expected_thunk_offset),
+ "Target offset must be aligned.");
size_t filler1_size = expected_thunk_offset -
- RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmAlignment);
+ RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmCodeAlignment);
std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 2u);
ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
AddCompiledMethod(MethodRef(2u), filler1_code);
Link();
- const uint32_t bne = BneWWithOffset(kLiteralOffset1, RoundUp(raw_code1.size(), kArmAlignment));
+ const uint32_t bne =
+ BneWWithOffset(kLiteralOffset1, RoundUp(raw_code1.size(), kArmCodeAlignment));
const std::vector<uint8_t> expected_code1 = RawCode({kNopWInsn, bne, kLdrWInsn, kNopInsn});
ASSERT_TRUE(CheckLinkedMethod(MethodRef(1), ArrayRef<const uint8_t>(expected_code1)));
}
@@ -1043,9 +1047,10 @@
constexpr uint32_t expected_thunk_offset =
kLiteralOffset1 + kPcAdjustment + /* kMaxBcondPositiveDisplacement */ ((1 << 20) - 2u);
- static_assert(IsAligned<kArmAlignment>(expected_thunk_offset), "Target offset must be aligned.");
+ static_assert(IsAligned<kArmCodeAlignment>(expected_thunk_offset),
+ "Target offset must be aligned.");
size_t filler1_size = expected_thunk_offset -
- RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmAlignment);
+ RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmCodeAlignment);
std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 2u);
ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
AddCompiledMethod(MethodRef(2u), filler1_code);
@@ -1055,7 +1060,7 @@
constexpr uint32_t kReachableFromOffset2 = 4;
constexpr uint32_t kLiteralOffset2 = kReachableFromOffset2 + 2;
- static_assert(IsAligned<kArmAlignment>(kReachableFromOffset2 + kPcAdjustment),
+ static_assert(IsAligned<kArmCodeAlignment>(kReachableFromOffset2 + kPcAdjustment),
"PC for BNE must be aligned.");
// If not for the extra NOP, this would allow reaching the thunk from the BNE
@@ -1068,8 +1073,8 @@
CompileBakerOffsetThunk(/* base_reg */ 0, /* holder_reg */ 0, /* narrow */ false).size();
size_t filler2_size =
1 * MB - (kReachableFromOffset2 + kPcAdjustment)
- - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArmAlignment)
- - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArmAlignment)
+ - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArmCodeAlignment)
+ - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArmCodeAlignment)
- sizeof(OatQuickMethodHeader);
std::vector<uint8_t> raw_filler2_code = GenNops(filler2_size / 2u);
ArrayRef<const uint8_t> filler2_code(raw_filler2_code);
@@ -1091,7 +1096,7 @@
const uint32_t bne_max_forward = kBneWPlus0 | 0x003f2fff;
const uint32_t bne_last =
- BneWWithOffset(kLiteralOffset2, RoundUp(raw_code2.size(), kArmAlignment));
+ BneWWithOffset(kLiteralOffset2, RoundUp(raw_code2.size(), kArmCodeAlignment));
const std::vector<uint8_t> expected_code1 =
RawCode({kNopWInsn, kNopInsn, bne_max_forward, kLdrWInsn});
const std::vector<uint8_t> expected_code2 =
@@ -1123,7 +1128,7 @@
Link();
// All thunks are at the end.
- uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment);
+ uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmCodeAlignment);
method_idx = 0u;
for (uint32_t base_reg : kBakerValidRegs) {
++method_idx;
@@ -1177,7 +1182,7 @@
// Do not check the rest of the implementation.
// The next thunk follows on the next aligned offset.
- thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment);
+ thunk_offset += RoundUp(expected_thunk.size(), kArmCodeAlignment);
}
}
@@ -1200,7 +1205,7 @@
Link();
// All thunks are at the end.
- uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment);
+ uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmCodeAlignment);
method_idx = 0u;
for (uint32_t root_reg : kBakerValidRegs) {
++method_idx;
@@ -1232,7 +1237,7 @@
// Do not check the rest of the implementation.
// The next thunk follows on the next aligned offset.
- thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment);
+ thunk_offset += RoundUp(expected_thunk.size(), kArmCodeAlignment);
}
}
@@ -1255,7 +1260,7 @@
Link();
// All thunks are at the end.
- uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmAlignment);
+ uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArmCodeAlignment);
method_idx = 0u;
for (uint32_t root_reg : kBakerValidRegsNarrow) {
++method_idx;
@@ -1281,7 +1286,7 @@
// Do not check the rest of the implementation.
// The next thunk follows on the next aligned offset.
- thunk_offset += RoundUp(expected_thunk.size(), kArmAlignment);
+ thunk_offset += RoundUp(expected_thunk.size(), kArmCodeAlignment);
}
}
@@ -1309,7 +1314,7 @@
Link();
// The thunk is right after the method code.
- DCHECK_ALIGNED(1 * MB, kArmAlignment);
+ DCHECK_ALIGNED(1 * MB, kArmCodeAlignment);
std::vector<uint8_t> expected_code;
for (size_t i = 0; i != num_patches; ++i) {
PushBackInsn(&expected_code, ldr);
@@ -1343,7 +1348,7 @@
// Add a method with the right size that the method code for the next one starts 1MiB
// after code for method 1.
size_t filler_size =
- 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmAlignment)
+ 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArmCodeAlignment)
- sizeof(OatQuickMethodHeader);
std::vector<uint8_t> filler_code = GenNops(filler_size / 2u);
++method_idx;
@@ -1358,16 +1363,16 @@
}
// Add 2 Baker GC root patches to the last method, one that would allow the thunk at
- // 1MiB + kArmAlignment, i.e. kArmAlignment after the method call thunk, and the
- // second that needs it kArmAlignment after that. Given the size of the GC root thunk
- // is more than the space required by the method call thunk plus kArmAlignment,
+ // 1MiB + kArmCodeAlignment, i.e. kArmCodeAlignment after the method call thunk, and the
+ // second that needs it kArmCodeAlignment after that. Given the size of the GC root thunk
+ // is more than the space required by the method call thunk plus kArmCodeAlignment,
// this pushes the first GC root thunk's pending MaxNextOffset() before the method call
// thunk's pending MaxNextOffset() which needs to be adjusted.
- ASSERT_LT(RoundUp(CompileMethodCallThunk().size(), kArmAlignment) + kArmAlignment,
+ ASSERT_LT(RoundUp(CompileMethodCallThunk().size(), kArmCodeAlignment) + kArmCodeAlignment,
CompileBakerGcRootThunk(/* root_reg */ 0, /* narrow */ false).size());
- static_assert(kArmAlignment == 8, "Code below assumes kArmAlignment == 8");
- constexpr size_t kBakerLiteralOffset1 = kArmAlignment + 2u - kPcAdjustment;
- constexpr size_t kBakerLiteralOffset2 = kBakerLiteralOffset1 + kArmAlignment;
+ static_assert(kArmCodeAlignment == 8, "Code below assumes kArmCodeAlignment == 8");
+ constexpr size_t kBakerLiteralOffset1 = kArmCodeAlignment + 2u - kPcAdjustment;
+ constexpr size_t kBakerLiteralOffset2 = kBakerLiteralOffset1 + kArmCodeAlignment;
// Use offset = 0, base_reg = 0, the LDR is simply `kLdrWInsn | (root_reg << 12)`.
const uint32_t ldr1 = kLdrWInsn | (/* root_reg */ 1 << 12);
const uint32_t ldr2 = kLdrWInsn | (/* root_reg */ 2 << 12);
diff --git a/dex2oat/linker/arm64/relative_patcher_arm64.cc b/dex2oat/linker/arm64/relative_patcher_arm64.cc
index 4028f75..6b84472 100644
--- a/dex2oat/linker/arm64/relative_patcher_arm64.cc
+++ b/dex2oat/linker/arm64/relative_patcher_arm64.cc
@@ -21,7 +21,7 @@
#include "art_method.h"
#include "base/bit_utils.h"
#include "base/malloc_arena_pool.h"
-#include "compiled_method-inl.h"
+#include "driver/compiled_method-inl.h"
#include "driver/compiler_driver.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "heap_poisoning.h"
@@ -251,7 +251,7 @@
} else {
if ((insn & 0xfffffc00) == 0x91000000) {
// ADD immediate, 64-bit with imm12 == 0 (unset).
- if (!kEmitCompilerReadBarrier) {
+ if (!gUseReadBarrier) {
DCHECK(patch.GetType() == LinkerPatch::Type::kIntrinsicReference ||
patch.GetType() == LinkerPatch::Type::kMethodRelative ||
patch.GetType() == LinkerPatch::Type::kTypeRelative ||
diff --git a/dex2oat/linker/arm64/relative_patcher_arm64_test.cc b/dex2oat/linker/arm64/relative_patcher_arm64_test.cc
index 8bae5d4..ce61f43 100644
--- a/dex2oat/linker/arm64/relative_patcher_arm64_test.cc
+++ b/dex2oat/linker/arm64/relative_patcher_arm64_test.cc
@@ -112,7 +112,7 @@
const ArrayRef<const uint8_t>& last_method_code,
const ArrayRef<const LinkerPatch>& last_method_patches,
uint32_t distance_without_thunks) {
- CHECK_EQ(distance_without_thunks % kArm64Alignment, 0u);
+ CHECK_EQ(distance_without_thunks % kArm64CodeAlignment, 0u);
uint32_t method1_offset =
kTrampolineSize + CodeAlignmentSize(kTrampolineSize) + sizeof(OatQuickMethodHeader);
AddCompiledMethod(MethodRef(1u), method1_code, method1_patches);
@@ -120,7 +120,7 @@
// We want to put the last method at a very precise offset.
const uint32_t last_method_offset = method1_offset + distance_without_thunks;
- CHECK_ALIGNED(last_method_offset, kArm64Alignment);
+ CHECK_ALIGNED(last_method_offset, kArm64CodeAlignment);
const uint32_t gap_end = last_method_offset - sizeof(OatQuickMethodHeader);
// Fill the gap with intermediate methods in chunks of 2MiB and the first in [2MiB, 4MiB).
@@ -733,24 +733,26 @@
bl_offset_in_method1 + just_over_max_positive_disp);
ASSERT_EQ(kExpectedLastMethodIdx, last_method_idx);
uint32_t method_after_thunk_idx = last_method_idx;
- if (sizeof(OatQuickMethodHeader) < kArm64Alignment) {
- // The thunk needs to start on a kArm64Alignment-aligned address before the address where the
- // last method would have been if there was no thunk. If the size of the OatQuickMethodHeader
- // is at least kArm64Alignment, the thunk start shall fit between the previous filler method
- // and that address. Otherwise, it shall be inserted before that filler method.
+ if (sizeof(OatQuickMethodHeader) < kArm64CodeAlignment) {
+ // The thunk needs to start on a kArm64CodeAlignment-aligned address before the address where
+ // the last method would have been if there was no thunk. If the size of the
+ // OatQuickMethodHeader is at least kArm64CodeAlignment, the thunk start shall fit between the
+ // previous filler method and that address. Otherwise, it shall be inserted before that filler
+ // method.
method_after_thunk_idx -= 1u;
}
uint32_t method1_offset = GetMethodOffset(1u);
uint32_t method_after_thunk_offset = GetMethodOffset(method_after_thunk_idx);
- ASSERT_TRUE(IsAligned<kArm64Alignment>(method_after_thunk_offset));
+ ASSERT_TRUE(IsAligned<kArm64CodeAlignment>(method_after_thunk_offset));
uint32_t method_after_thunk_header_offset =
method_after_thunk_offset - sizeof(OatQuickMethodHeader);
uint32_t thunk_size = MethodCallThunkSize();
- uint32_t thunk_offset = RoundDown(method_after_thunk_header_offset - thunk_size, kArm64Alignment);
+ uint32_t thunk_offset = RoundDown(
+ method_after_thunk_header_offset - thunk_size, kArm64CodeAlignment);
DCHECK_EQ(thunk_offset + thunk_size + CodeAlignmentSize(thunk_offset + thunk_size),
method_after_thunk_header_offset);
- ASSERT_TRUE(IsAligned<kArm64Alignment>(thunk_offset));
+ ASSERT_TRUE(IsAligned<kArm64CodeAlignment>(thunk_offset));
uint32_t diff = thunk_offset - (method1_offset + bl_offset_in_method1);
ASSERT_TRUE(IsAligned<4u>(diff));
ASSERT_LT(diff, 128 * MB);
@@ -1065,7 +1067,8 @@
Link();
// All thunks are at the end.
- uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64Alignment);
+ uint32_t thunk_offset =
+ GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64CodeAlignment);
method_idx = 0u;
for (uint32_t base_reg : valid_regs) {
for (uint32_t holder_reg : valid_regs) {
@@ -1118,7 +1121,7 @@
// Do not check the rest of the implementation.
// The next thunk follows on the next aligned offset.
- thunk_offset += RoundUp(expected_thunk.size(), kArm64Alignment);
+ thunk_offset += RoundUp(expected_thunk.size(), kArm64CodeAlignment);
}
}
}
@@ -1155,7 +1158,7 @@
// Allow thunk at 1MiB offset from the start of the method above. Literal offset being 4
// allows the branch to reach that thunk.
size_t filler1_size =
- 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment);
+ 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64CodeAlignment);
std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 4u);
ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
AddCompiledMethod(MethodRef(2u), filler1_code);
@@ -1170,8 +1173,8 @@
// - method 4 header (let there be no padding between method 4 code and method 5 pre-header).
size_t thunk_size = CompileBakerOffsetThunk(/* base_reg */ 0, /* holder_reg */ 0).size();
size_t filler2_size =
- 1 * MB - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArm64Alignment)
- - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArm64Alignment)
+ 1 * MB - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArm64CodeAlignment)
+ - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArm64CodeAlignment)
- sizeof(OatQuickMethodHeader);
std::vector<uint8_t> raw_filler2_code = GenNops(filler2_size / 4u);
ArrayRef<const uint8_t> filler2_code(raw_filler2_code);
@@ -1215,14 +1218,14 @@
// Allow thunk at 1MiB offset from the start of the method above. Literal offset being 4
// allows the branch to reach that thunk.
size_t filler1_size =
- 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment);
+ 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64CodeAlignment);
std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 4u);
ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
AddCompiledMethod(MethodRef(2u), filler1_code);
Link();
- const uint32_t cbnz_offset = RoundUp(raw_code1.size(), kArm64Alignment) - kLiteralOffset1;
+ const uint32_t cbnz_offset = RoundUp(raw_code1.size(), kArm64CodeAlignment) - kLiteralOffset1;
const uint32_t cbnz = kCbnzIP1Plus0Insn | (cbnz_offset << (5 - 2));
const std::vector<uint8_t> expected_code1 = RawCode({cbnz, kLdrWInsn, kNopInsn});
ASSERT_TRUE(CheckLinkedMethod(MethodRef(1), ArrayRef<const uint8_t>(expected_code1)));
@@ -1244,7 +1247,7 @@
// Allow thunk at 1MiB offset from the start of the method above. Literal offset being 4
// allows the branch to reach that thunk.
size_t filler1_size =
- 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment);
+ 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64CodeAlignment);
std::vector<uint8_t> raw_filler1_code = GenNops(filler1_size / 4u);
ArrayRef<const uint8_t> filler1_code(raw_filler1_code);
AddCompiledMethod(MethodRef(2u), filler1_code);
@@ -1259,8 +1262,8 @@
// - method 4 header (let there be no padding between method 4 code and method 5 pre-header).
size_t thunk_size = CompileBakerOffsetThunk(/* base_reg */ 0, /* holder_reg */ 0).size();
size_t filler2_size =
- 1 * MB - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArm64Alignment)
- - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArm64Alignment)
+ 1 * MB - RoundUp(thunk_size + sizeof(OatQuickMethodHeader), kArm64CodeAlignment)
+ - RoundUp(kNopCode.size() + sizeof(OatQuickMethodHeader), kArm64CodeAlignment)
- sizeof(OatQuickMethodHeader);
std::vector<uint8_t> raw_filler2_code = GenNops(filler2_size / 4u);
ArrayRef<const uint8_t> filler2_code(raw_filler2_code);
@@ -1278,7 +1281,8 @@
Link();
const uint32_t cbnz_max_forward = kCbnzIP1Plus0Insn | 0x007fffe0;
- const uint32_t cbnz_last_offset = RoundUp(raw_code2.size(), kArm64Alignment) - kLiteralOffset2;
+ const uint32_t cbnz_last_offset =
+ RoundUp(raw_code2.size(), kArm64CodeAlignment) - kLiteralOffset2;
const uint32_t cbnz_last = kCbnzIP1Plus0Insn | (cbnz_last_offset << (5 - 2));
const std::vector<uint8_t> expected_code1 = RawCode({kNopInsn, cbnz_max_forward, kLdrWInsn});
const std::vector<uint8_t> expected_code2 = RawCode({kNopInsn, cbnz_last, kLdrWInsn});
@@ -1315,7 +1319,8 @@
Link();
// All thunks are at the end.
- uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64Alignment);
+ uint32_t thunk_offset =
+ GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64CodeAlignment);
method_idx = 0u;
for (uint32_t base_reg : valid_regs) {
++method_idx;
@@ -1363,7 +1368,7 @@
// Do not check the rest of the implementation.
// The next thunk follows on the next aligned offset.
- thunk_offset += RoundUp(expected_thunk.size(), kArm64Alignment);
+ thunk_offset += RoundUp(expected_thunk.size(), kArm64CodeAlignment);
}
}
@@ -1392,7 +1397,8 @@
Link();
// All thunks are at the end.
- uint32_t thunk_offset = GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64Alignment);
+ uint32_t thunk_offset =
+ GetMethodOffset(method_idx) + RoundUp(kMethodCodeSize, kArm64CodeAlignment);
method_idx = 0u;
for (uint32_t root_reg : valid_regs) {
++method_idx;
@@ -1419,7 +1425,7 @@
// Do not check the rest of the implementation.
// The next thunk follows on the next aligned offset.
- thunk_offset += RoundUp(expected_thunk.size(), kArm64Alignment);
+ thunk_offset += RoundUp(expected_thunk.size(), kArm64CodeAlignment);
}
}
@@ -1447,7 +1453,7 @@
// Add a method with the right size that the method code for the next one starts 1MiB
// after code for method 1.
size_t filler_size =
- 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64Alignment)
+ 1 * MB - RoundUp(raw_code1.size() + sizeof(OatQuickMethodHeader), kArm64CodeAlignment)
- sizeof(OatQuickMethodHeader);
std::vector<uint8_t> filler_code = GenNops(filler_size / 4u);
++method_idx;
@@ -1462,16 +1468,16 @@
}
// Add 2 Baker GC root patches to the last method, one that would allow the thunk at
- // 1MiB + kArm64Alignment, i.e. kArm64Alignment after the method call thunk, and the
- // second that needs it kArm64Alignment after that. Given the size of the GC root thunk
- // is more than the space required by the method call thunk plus kArm64Alignment,
+ // 1MiB + kArm64CodeAlignment, i.e. kArm64CodeAlignment after the method call thunk, and the
+ // second that needs it kArm64CodeAlignment after that. Given the size of the GC root thunk
+ // is more than the space required by the method call thunk plus kArm64CodeAlignment,
// this pushes the first GC root thunk's pending MaxNextOffset() before the method call
// thunk's pending MaxNextOffset() which needs to be adjusted.
- ASSERT_LT(RoundUp(CompileMethodCallThunk().size(), kArm64Alignment) + kArm64Alignment,
+ ASSERT_LT(RoundUp(CompileMethodCallThunk().size(), kArm64CodeAlignment) + kArm64CodeAlignment,
CompileBakerGcRootThunk(/* root_reg */ 0).size());
- static_assert(kArm64Alignment == 16, "Code below assumes kArm64Alignment == 16");
- constexpr size_t kBakerLiteralOffset1 = 4u + kArm64Alignment;
- constexpr size_t kBakerLiteralOffset2 = 4u + 2 * kArm64Alignment;
+ static_assert(kArm64CodeAlignment == 16, "Code below assumes kArm64CodeAlignment == 16");
+ constexpr size_t kBakerLiteralOffset1 = 4u + kArm64CodeAlignment;
+ constexpr size_t kBakerLiteralOffset2 = 4u + 2 * kArm64CodeAlignment;
// Use offset = 0, base_reg = 0, the LDR is simply `kLdrWInsn | root_reg`.
const uint32_t ldr1 = kLdrWInsn | /* root_reg */ 1;
const uint32_t ldr2 = kLdrWInsn | /* root_reg */ 2;
diff --git a/dex2oat/linker/code_info_table_deduper_test.cc b/dex2oat/linker/code_info_table_deduper_test.cc
index 8913b07..54b7dd5 100644
--- a/dex2oat/linker/code_info_table_deduper_test.cc
+++ b/dex2oat/linker/code_info_table_deduper_test.cc
@@ -35,7 +35,12 @@
ArenaStack arena_stack(&pool);
ScopedArenaAllocator allocator(&arena_stack);
StackMapStream stream(&allocator, kRuntimeISA);
- stream.BeginMethod(32, 0, 0, 2);
+ stream.BeginMethod(/* frame_size_in_bytes= */ 32,
+ /* core_spill_mask= */ 0,
+ /* fp_spill_mask= */ 0,
+ /* num_dex_registers= */ 2,
+ /* baseline= */ false,
+ /* debuggable= */ false);
stream.BeginStackMapEntry(0, 64 * kPcAlign);
stream.AddDexRegisterEntry(Kind::kInStack, 0);
diff --git a/dex2oat/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc
index 424c252..61e5783 100644
--- a/dex2oat/linker/elf_writer_quick.cc
+++ b/dex2oat/linker/elf_writer_quick.cc
@@ -25,7 +25,6 @@
#include "base/globals.h"
#include "base/leb128.h"
#include "base/utils.h"
-#include "compiled_method.h"
#include "debug/elf_debug_writer.h"
#include "debug/method_debug_info.h"
#include "driver/compiler_options.h"
diff --git a/dex2oat/linker/image_test.cc b/dex2oat/linker/image_test.cc
index 33d122b..82c87f5 100644
--- a/dex2oat/linker/image_test.cc
+++ b/dex2oat/linker/image_test.cc
@@ -176,5 +176,37 @@
/*image_classes_failing_aot_clinit=*/ {"LClassToInitialize;"});
}
+TEST_F(ImageTest, TestImageClassWithArrayClassWithUnresolvedComponent) {
+ CompilationHelper helper;
+ Compile(ImageHeader::kStorageModeUncompressed,
+ /*max_image_block_size=*/std::numeric_limits<uint32_t>::max(),
+ helper,
+ "ArrayClassWithUnresolvedComponent",
+ /*image_classes=*/ {"LClassWithStatic;",
+ "LClassWithStaticConst;",
+ "[LClassWithMissingInterface;",
+ "[[LClassWithMissingInterface;",
+ "[LClassWithMissingSuper",
+ "[[LClassWithMissingSuper"},
+ /*image_classes_failing_aot_clinit=*/ {
+ "LClassWithStatic;",
+ "LClassWithStaticConst;"},
+ /*image_classes_failing_resolution=*/ {
+ "[LClassWithMissingInterface;",
+ "[[LClassWithMissingInterface;",
+ "[LClassWithMissingSuper",
+ "[[LClassWithMissingSuper"});
+}
+
+TEST_F(ImageTest, TestSuperWithAccessChecks) {
+ CompilationHelper helper;
+ Compile(ImageHeader::kStorageModeUncompressed,
+ /*max_image_block_size=*/std::numeric_limits<uint32_t>::max(),
+ helper,
+ "SuperWithAccessChecks",
+ /*image_classes=*/ {"LSubClass;", "LImplementsClass;"},
+ /*image_classes_failing_aot_clinit=*/ {"LSubClass;", "LImplementsClass;"});
+}
+
} // namespace linker
} // namespace art
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index 5c2d84c..8dea9a6 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -50,6 +50,7 @@
#include "mirror/object-inl.h"
#include "oat.h"
#include "oat_writer.h"
+#include "read_barrier_config.h"
#include "scoped_thread_state_change-inl.h"
#include "signal_catcher.h"
#include "stream/buffered_output_stream.h"
@@ -63,6 +64,7 @@
struct CompilationHelper {
std::vector<std::string> dex_file_locations;
std::vector<ScratchFile> image_locations;
+ std::string extra_dex;
std::vector<std::unique_ptr<const DexFile>> extra_dex_files;
std::vector<ScratchFile> image_files;
std::vector<ScratchFile> oat_files;
@@ -78,7 +80,7 @@
protected:
void SetUp() override {
ReserveImageSpace();
- CommonCompilerTest::SetUp();
+ CommonCompilerDriverTest::SetUp();
}
void Compile(ImageHeader::StorageMode storage_mode,
@@ -86,10 +88,11 @@
/*out*/ CompilationHelper& out_helper,
const std::string& extra_dex = "",
const std::initializer_list<std::string>& image_classes = {},
- const std::initializer_list<std::string>& image_classes_failing_aot_clinit = {});
+ const std::initializer_list<std::string>& image_classes_failing_aot_clinit = {},
+ const std::initializer_list<std::string>& image_classes_failing_resolution = {});
void SetUpRuntimeOptions(RuntimeOptions* options) override {
- CommonCompilerTest::SetUpRuntimeOptions(options);
+ CommonCompilerDriverTest::SetUpRuntimeOptions(options);
QuickCompilerCallbacks* new_callbacks =
new QuickCompilerCallbacks(CompilerCallbacks::CallbackMode::kCompileBootImage);
new_callbacks->SetVerificationResults(verification_results_.get());
@@ -149,18 +152,11 @@
inline void ImageTest::DoCompile(ImageHeader::StorageMode storage_mode,
/*out*/ CompilationHelper& out_helper) {
CompilerDriver* driver = compiler_driver_.get();
+ Runtime::Current()->AppendToBootClassPath(
+ out_helper.extra_dex, out_helper.extra_dex, out_helper.extra_dex_files);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
std::vector<const DexFile*> class_path = class_linker->GetBootClassPath();
- for (const std::unique_ptr<const DexFile>& dex_file : out_helper.extra_dex_files) {
- {
- ScopedObjectAccess soa(Thread::Current());
- // Inject in boot class path so that the compiler driver can see it.
- class_linker->AppendToBootClassPath(soa.Self(), dex_file.get());
- }
- class_path.push_back(dex_file.get());
- }
-
// Enable write for dex2dex.
for (const DexFile* dex_file : class_path) {
out_helper.dex_file_locations.push_back(dex_file->GetLocation());
@@ -228,6 +224,8 @@
key_value_store.Put(OatHeader::kBootClassPathKey,
android::base::Join(out_helper.dex_file_locations, ':'));
key_value_store.Put(OatHeader::kApexVersionsKey, Runtime::Current()->GetApexVersions());
+ key_value_store.Put(OatHeader::kConcurrentCopying,
+ gUseReadBarrier ? OatHeader::kTrueValue : OatHeader::kFalseValue);
std::vector<std::unique_ptr<ElfWriter>> elf_writers;
std::vector<std::unique_ptr<OatWriter>> oat_writers;
@@ -235,6 +233,7 @@
elf_writers.emplace_back(CreateElfWriterQuick(*compiler_options_, oat_file.GetFile()));
elf_writers.back()->Start();
oat_writers.emplace_back(new OatWriter(*compiler_options_,
+ verification_results_.get(),
&timings,
/*profile_compilation_info*/nullptr,
CompactDexLevel::kCompactDexLevelNone));
@@ -352,7 +351,8 @@
CompilationHelper& helper,
const std::string& extra_dex,
const std::initializer_list<std::string>& image_classes,
- const std::initializer_list<std::string>& image_classes_failing_aot_clinit) {
+ const std::initializer_list<std::string>& image_classes_failing_aot_clinit,
+ const std::initializer_list<std::string>& image_classes_failing_resolution) {
for (const std::string& image_class : image_classes_failing_aot_clinit) {
ASSERT_TRUE(ContainsElement(image_classes, image_class));
}
@@ -366,6 +366,7 @@
compiler_options_->SetMaxImageBlockSize(max_image_block_size);
image_classes_.clear();
if (!extra_dex.empty()) {
+ helper.extra_dex = extra_dex;
helper.extra_dex_files = OpenTestDexFiles(extra_dex.c_str());
}
DoCompile(storage_mode, helper);
@@ -375,12 +376,14 @@
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
for (const std::string& image_class : image_classes) {
ObjPtr<mirror::Class> klass =
- class_linker->FindSystemClass(Thread::Current(), image_class.c_str());
- EXPECT_TRUE(klass != nullptr);
- EXPECT_TRUE(klass->IsResolved());
- if (ContainsElement(image_classes_failing_aot_clinit, image_class)) {
+ class_linker->LookupClass(Thread::Current(), image_class.c_str(), nullptr);
+ if (ContainsElement(image_classes_failing_resolution, image_class)) {
+ EXPECT_TRUE(klass == nullptr || klass->IsErroneousUnresolved());
+ } else if (ContainsElement(image_classes_failing_aot_clinit, image_class)) {
+ ASSERT_TRUE(klass != nullptr) << image_class;
EXPECT_FALSE(klass->IsInitialized());
} else {
+ ASSERT_TRUE(klass != nullptr) << image_class;
EXPECT_TRUE(klass->IsInitialized());
}
}
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index 56fbe90..2c3c220 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -35,7 +35,6 @@
#include "base/unix_file/fd_file.h"
#include "class_linker-inl.h"
#include "class_root-inl.h"
-#include "compiled_method.h"
#include "dex/dex_file-inl.h"
#include "dex/dex_file_types.h"
#include "driver/compiler_options.h"
@@ -83,7 +82,7 @@
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "subtype_check.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
using ::art::mirror::Class;
using ::art::mirror::DexCache;
@@ -101,7 +100,7 @@
// to make them full. We never insert additional elements to them, so we do not want to waste
// extra memory. And unlike runtime class tables, we do not want this to depend on runtime
// properties (see `Runtime::GetHashTableMaxLoadFactor()` checking for low memory mode).
-constexpr double kImageClassTableMaxLoadFactor = 0.7;
+constexpr double kImageClassTableMaxLoadFactor = 0.6;
// The actual value of `kImageInternTableMinLoadFactor` is irrelevant because image intern tables
// are never resized, but we still need to pass a reasonable value to the constructor.
@@ -110,7 +109,7 @@
// to make them full. We never insert additional elements to them, so we do not want to waste
// extra memory. And unlike runtime intern tables, we do not want this to depend on runtime
// properties (see `Runtime::GetHashTableMaxLoadFactor()` checking for low memory mode).
-constexpr double kImageInternTableMaxLoadFactor = 0.7;
+constexpr double kImageInternTableMaxLoadFactor = 0.6;
static ArrayRef<const uint8_t> MaybeCompressData(ArrayRef<const uint8_t> source,
ImageHeader::StorageMode image_storage_mode,
@@ -155,11 +154,19 @@
<< PrettyDuration(NanoTime() - compress_start_time);
if (kIsDebugBuild) {
dchecked_vector<uint8_t> decompressed(source.size());
- const size_t decompressed_size = LZ4_decompress_safe(
+ size_t decompressed_size;
+ std::string error_msg;
+ bool ok = LZ4_decompress_safe_checked(
reinterpret_cast<char*>(storage->data()),
reinterpret_cast<char*>(decompressed.data()),
storage->size(),
- decompressed.size());
+ decompressed.size(),
+ &decompressed_size,
+ &error_msg);
+ if (!ok) {
+ LOG(FATAL) << error_msg;
+ UNREACHABLE();
+ }
CHECK_EQ(decompressed_size, decompressed.size());
CHECK_EQ(memcmp(source.data(), decompressed.data(), source.size()), 0) << image_storage_mode;
}
@@ -265,8 +272,8 @@
auto visitor = [](Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(obj != nullptr);
Class* klass = obj->GetClass();
- if (klass == WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DexFile)) {
- ArtField* field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
+ if (klass == WellKnownClasses::dalvik_system_DexFile) {
+ ArtField* field = WellKnownClasses::dalvik_system_DexFile_cookie;
// Null out the cookie to enable determinism. b/34090128
field->SetObject</*kTransactionActive*/false>(obj, nullptr);
}
@@ -2217,12 +2224,11 @@
CHECK(ref != nullptr);
CHECK(image_writer_->IsImageBinSlotAssigned(ref.Ptr()));
ObjPtr<mirror::Class> ref_klass = ref->GetClass<kVerifyNone, kWithoutReadBarrier>();
- CHECK(ref_klass ==
- DecodeGlobalWithoutRB<mirror::Class>(vm, WellKnownClasses::dalvik_system_DexFile));
+ CHECK(ref_klass == WellKnownClasses::dalvik_system_DexFile.Get<kWithoutReadBarrier>());
// Note: The app class loader is used only for checking against the runtime
// class loader, the dex file cookie is cleared and therefore we do not need
// to run the finalizer even if we implement app image objects collection.
- ArtField* field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
+ ArtField* field = WellKnownClasses::dalvik_system_DexFile_cookie;
CHECK(field->GetObject<kWithoutReadBarrier>(ref) == nullptr);
return;
}
@@ -3336,8 +3342,7 @@
quick_code = GetOatAddressForOffset(quick_oat_code_offset, image_info);
}
- bool needs_clinit_check = NeedsClinitCheckBeforeCall(method) &&
- !method->GetDeclaringClass<kWithoutReadBarrier>()->IsVisiblyInitialized();
+ bool still_needs_clinit_check = method->StillNeedsClinitCheck<kWithoutReadBarrier>();
if (quick_code == nullptr) {
// If we don't have code, use generic jni / interpreter.
@@ -3347,7 +3352,7 @@
} else if (CanMethodUseNterp(method, compiler_options_.GetInstructionSet())) {
// The nterp trampoline doesn't do initialization checks, so install the
// resolution stub if needed.
- if (needs_clinit_check) {
+ if (still_needs_clinit_check) {
quick_code = GetOatAddress(StubType::kQuickResolutionTrampoline);
} else {
quick_code = GetOatAddress(StubType::kNterpTrampoline);
@@ -3356,7 +3361,7 @@
// The interpreter brige performs class initialization check if needed.
quick_code = GetOatAddress(StubType::kQuickToInterpreterBridge);
}
- } else if (needs_clinit_check) {
+ } else if (still_needs_clinit_check && !compiler_options_.ShouldCompileWithClinitCheck(method)) {
// If we do have code but the method needs a class initialization check before calling
// that code, install the resolution stub that will perform the check.
quick_code = GetOatAddress(StubType::kQuickResolutionTrampoline);
diff --git a/dex2oat/linker/multi_oat_relative_patcher_test.cc b/dex2oat/linker/multi_oat_relative_patcher_test.cc
index 2a05816..a393eb8 100644
--- a/dex2oat/linker/multi_oat_relative_patcher_test.cc
+++ b/dex2oat/linker/multi_oat_relative_patcher_test.cc
@@ -16,8 +16,8 @@
#include "multi_oat_relative_patcher.h"
-#include "compiled_method.h"
#include "debug/method_debug_info.h"
+#include "driver/compiled_method.h"
#include "gtest/gtest.h"
#include "linker/linker_patch.h"
#include "stream/vector_output_stream.h"
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 19e77de..cf86006 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -37,7 +37,6 @@
#include "class_linker.h"
#include "class_table-inl.h"
#include "code_info_table_deduper.h"
-#include "compiled_method-inl.h"
#include "debug/method_debug_info.h"
#include "dex/art_dex_file_loader.h"
#include "dex/class_accessor-inl.h"
@@ -49,6 +48,7 @@
#include "dex/verification_results.h"
#include "dex_container.h"
#include "dexlayout.h"
+#include "driver/compiled_method-inl.h"
#include "driver/compiler_driver-inl.h"
#include "driver/compiler_options.h"
#include "gc/space/image_space.h"
@@ -384,6 +384,7 @@
<< "file_offset=" << file_offset << " offset_=" << offset_
OatWriter::OatWriter(const CompilerOptions& compiler_options,
+ const VerificationResults* verification_results,
TimingLogger* timings,
ProfileCompilationInfo* info,
CompactDexLevel compact_dex_level)
@@ -395,6 +396,7 @@
zipped_dex_file_locations_(),
compiler_driver_(nullptr),
compiler_options_(compiler_options),
+ verification_results_(verification_results),
image_writer_(nullptr),
extract_dex_files_into_vdex_(true),
vdex_begin_(nullptr),
@@ -1013,7 +1015,7 @@
ClassStatus status;
bool found = writer_->compiler_driver_->GetCompiledClass(class_ref, &status);
if (!found) {
- const VerificationResults* results = writer_->compiler_options_.GetVerificationResults();
+ const VerificationResults* results = writer_->verification_results_;
if (results != nullptr && results->IsClassRejected(class_ref)) {
// The oat class status is used only for verification of resolved classes,
// so use ClassStatus::kErrorResolved whether the class was resolved or unresolved
@@ -1351,7 +1353,7 @@
ArrayRef<const uint8_t> quick_code = compiled_method->GetQuickCode();
uint32_t code_size = quick_code.size() * sizeof(uint8_t);
- uint32_t thumb_offset = compiled_method->CodeDelta();
+ uint32_t thumb_offset = compiled_method->GetEntryPointAdjustment();
// Deduplicate code arrays if we are not producing debuggable code.
bool deduped = true;
@@ -1486,7 +1488,7 @@
offset_ = relative_patcher_->ReserveSpace(offset_, compiled_method, method_ref);
offset_ += CodeAlignmentSize(offset_, *compiled_method);
DCHECK_ALIGNED_PARAM(offset_ + sizeof(OatQuickMethodHeader),
- GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
+ GetInstructionSetCodeAlignment(compiled_method->GetInstructionSet()));
return offset_ + sizeof(OatQuickMethodHeader) + thumb_offset;
}
@@ -1797,9 +1799,10 @@
DCHECK_OFFSET_();
}
DCHECK_ALIGNED_PARAM(offset_ + sizeof(OatQuickMethodHeader),
- GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
- DCHECK_EQ(method_offsets.code_offset_,
- offset_ + sizeof(OatQuickMethodHeader) + compiled_method->CodeDelta())
+ GetInstructionSetCodeAlignment(compiled_method->GetInstructionSet()));
+ DCHECK_EQ(
+ method_offsets.code_offset_,
+ offset_ + sizeof(OatQuickMethodHeader) + compiled_method->GetEntryPointAdjustment())
<< dex_file_->PrettyMethod(method_ref.index);
const OatQuickMethodHeader& method_header =
oat_class->method_headers_[method_offsets_index];
@@ -2396,22 +2399,22 @@
const bool generate_debug_info = GetCompilerOptions().GenerateAnyDebugInfo();
size_t adjusted_offset = offset;
- #define DO_TRAMPOLINE(field, fn_name) \
- /* Pad with at least four 0xFFs so we can do DCHECKs in OatQuickMethodHeader */ \
- offset = CompiledCode::AlignCode(offset + 4, instruction_set); \
- adjusted_offset = offset + CompiledCode::CodeDelta(instruction_set); \
- oat_header_->Set ## fn_name ## Offset(adjusted_offset); \
- (field) = compiler_driver_->Create ## fn_name(); \
- if (generate_debug_info) { \
- debug::MethodDebugInfo info = {}; \
- info.custom_name = #fn_name; \
- info.isa = instruction_set; \
- info.is_code_address_text_relative = true; \
- /* Use the code offset rather than the `adjusted_offset`. */ \
- info.code_address = offset - oat_header_->GetExecutableOffset(); \
- info.code_size = (field)->size(); \
- method_info_.push_back(std::move(info)); \
- } \
+ #define DO_TRAMPOLINE(field, fn_name) \
+ /* Pad with at least four 0xFFs so we can do DCHECKs in OatQuickMethodHeader */ \
+ offset = CompiledCode::AlignCode(offset + 4, instruction_set); \
+ adjusted_offset = offset + GetInstructionSetEntryPointAdjustment(instruction_set); \
+ oat_header_->Set ## fn_name ## Offset(adjusted_offset); \
+ (field) = compiler_driver_->Create ## fn_name(); \
+ if (generate_debug_info) { \
+ debug::MethodDebugInfo info = {}; \
+ info.custom_name = #fn_name; \
+ info.isa = instruction_set; \
+ info.is_code_address_text_relative = true; \
+ /* Use the code offset rather than the `adjusted_offset`. */ \
+ info.code_address = offset - oat_header_->GetExecutableOffset(); \
+ info.code_size = (field)->size(); \
+ method_info_.push_back(std::move(info)); \
+ } \
offset += (field)->size();
DO_TRAMPOLINE(jni_dlsym_lookup_trampoline_, JniDlsymLookupTrampoline);
diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h
index ebff7a1..bfacf60 100644
--- a/dex2oat/linker/oat_writer.h
+++ b/dex2oat/linker/oat_writer.h
@@ -49,6 +49,7 @@
class TimingLogger;
class TypeLookupTable;
class VdexFile;
+class VerificationResults;
class ZipEntry;
namespace debug {
@@ -115,6 +116,7 @@
class OatWriter {
public:
OatWriter(const CompilerOptions& compiler_options,
+ const VerificationResults* verification_results,
TimingLogger* timings,
ProfileCompilationInfo* info,
CompactDexLevel compact_dex_level);
@@ -391,6 +393,7 @@
const CompilerDriver* compiler_driver_;
const CompilerOptions& compiler_options_;
+ const VerificationResults* const verification_results_;
ImageWriter* image_writer_;
// Whether the dex files being compiled are going to be extracted to the vdex.
bool extract_dex_files_into_vdex_;
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index 6b2198d..6742cf7 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -24,7 +24,6 @@
#include "base/unix_file/fd_file.h"
#include "class_linker.h"
#include "common_compiler_driver_test.h"
-#include "compiled_method-inl.h"
#include "compiler.h"
#include "debug/method_debug_info.h"
#include "dex/class_accessor-inl.h"
@@ -32,6 +31,7 @@
#include "dex/quick_compiler_callbacks.h"
#include "dex/test_dex_file_builder.h"
#include "dex/verification_results.h"
+#include "driver/compiled_method-inl.h"
#include "driver/compiler_driver.h"
#include "driver/compiler_options.h"
#include "entrypoints/quick/quick_entrypoints.h"
@@ -107,6 +107,7 @@
TimingLogger timings("WriteElf", false, false);
ClearBootImageOption();
OatWriter oat_writer(*compiler_options_,
+ verification_results_.get(),
&timings,
/*profile_compilation_info*/nullptr,
CompactDexLevel::kCompactDexLevelNone);
@@ -134,6 +135,7 @@
TimingLogger timings("WriteElf", false, false);
ClearBootImageOption();
OatWriter oat_writer(*compiler_options_,
+ verification_results_.get(),
&timings,
profile_compilation_info,
CompactDexLevel::kCompactDexLevelNone);
@@ -156,6 +158,7 @@
TimingLogger timings("WriteElf", false, false);
ClearBootImageOption();
OatWriter oat_writer(*compiler_options_,
+ verification_results_.get(),
&timings,
profile_compilation_info,
CompactDexLevel::kCompactDexLevelNone);
@@ -181,7 +184,7 @@
if (!oat_writer.WriteAndOpenDexFiles(
vdex_file,
verify,
- /*update_input_vdex=*/ false,
+ /*use_existing_vdex=*/ false,
copy,
&opened_dex_files_maps,
&opened_dex_files)) {
@@ -505,7 +508,7 @@
EXPECT_EQ(68U, sizeof(OatHeader));
EXPECT_EQ(4U, sizeof(OatMethodOffsets));
EXPECT_EQ(4U, sizeof(OatQuickMethodHeader));
- EXPECT_EQ(167 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
+ EXPECT_EQ(170 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
sizeof(QuickEntryPoints));
}
diff --git a/dex2oat/linker/relative_patcher_test.h b/dex2oat/linker/relative_patcher_test.h
index 56e0dc1..2900523 100644
--- a/dex2oat/linker/relative_patcher_test.h
+++ b/dex2oat/linker/relative_patcher_test.h
@@ -24,9 +24,9 @@
#include "base/array_ref.h"
#include "base/globals.h"
#include "base/macros.h"
-#include "compiled_method-inl.h"
#include "dex/method_reference.h"
#include "dex/string_reference.h"
+#include "driver/compiled_method-inl.h"
#include "driver/compiled_method_storage.h"
#include "linker/relative_patcher.h"
#include "oat_quick_method_header.h"
@@ -132,7 +132,7 @@
offset += alignment_size;
offset += sizeof(OatQuickMethodHeader);
- uint32_t quick_code_offset = offset + compiled_method->CodeDelta();
+ uint32_t quick_code_offset = offset + compiled_method->GetEntryPointAdjustment();
const auto code = compiled_method->GetQuickCode();
offset += code.size();
@@ -172,7 +172,8 @@
if (patch.GetType() == LinkerPatch::Type::kCallRelative) {
auto result = method_offset_map_.FindMethodOffset(patch.TargetMethod());
uint32_t target_offset =
- result.first ? result.second : kTrampolineOffset + compiled_method->CodeDelta();
+ result.first ? result.second
+ : kTrampolineOffset + compiled_method->GetEntryPointAdjustment();
patcher_->PatchCall(&patched_code_,
patch.LiteralOffset(),
offset + patch.LiteralOffset(),
@@ -227,7 +228,7 @@
auto result = method_offset_map_.FindMethodOffset(method_ref);
CHECK(result.first); // Must have been linked.
- size_t offset = result.second - compiled_methods_[idx]->CodeDelta();
+ size_t offset = result.second - compiled_methods_[idx]->GetEntryPointAdjustment();
CHECK_LT(offset, output_.size());
CHECK_LE(offset + expected_code.size(), output_.size());
ArrayRef<const uint8_t> linked_code(&output_[offset], expected_code.size());
diff --git a/dex2oat/linker/x86/relative_patcher_x86.cc b/dex2oat/linker/x86/relative_patcher_x86.cc
index e3b94b0..a444446 100644
--- a/dex2oat/linker/x86/relative_patcher_x86.cc
+++ b/dex2oat/linker/x86/relative_patcher_x86.cc
@@ -16,7 +16,6 @@
#include "linker/x86/relative_patcher_x86.h"
-#include "compiled_method.h"
#include "linker/linker_patch.h"
namespace art {
diff --git a/dex2oat/linker/x86_64/relative_patcher_x86_64.cc b/dex2oat/linker/x86_64/relative_patcher_x86_64.cc
index 0b9d07e..629affc 100644
--- a/dex2oat/linker/x86_64/relative_patcher_x86_64.cc
+++ b/dex2oat/linker/x86_64/relative_patcher_x86_64.cc
@@ -16,7 +16,6 @@
#include "linker/x86_64/relative_patcher_x86_64.h"
-#include "compiled_method.h"
#include "linker/linker_patch.h"
namespace art {
diff --git a/compiler/utils/swap_space.cc b/dex2oat/utils/swap_space.cc
similarity index 100%
rename from compiler/utils/swap_space.cc
rename to dex2oat/utils/swap_space.cc
diff --git a/dex2oat/utils/swap_space.h b/dex2oat/utils/swap_space.h
new file mode 100644
index 0000000..aba6485
--- /dev/null
+++ b/dex2oat/utils/swap_space.h
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_DEX2OAT_UTILS_SWAP_SPACE_H_
+#define ART_DEX2OAT_UTILS_SWAP_SPACE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <cstdlib>
+#include <list>
+#include <set>
+#include <vector>
+
+#include <android-base/logging.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+
+namespace art {
+
+// An arena pool that creates arenas backed by an mmaped file.
+class SwapSpace {
+ public:
+ SwapSpace(int fd, size_t initial_size);
+ ~SwapSpace();
+ void* Alloc(size_t size) REQUIRES(!lock_);
+ void Free(void* ptr, size_t size) REQUIRES(!lock_);
+
+ size_t GetSize() {
+ return size_;
+ }
+
+ private:
+ // Chunk of space.
+ struct SpaceChunk {
+ // We need mutable members as we keep these objects in a std::set<> (providing only const
+ // access) but we modify these members while carefully preserving the std::set<> ordering.
+ mutable uint8_t* ptr;
+ mutable size_t size;
+
+ uintptr_t Start() const {
+ return reinterpret_cast<uintptr_t>(ptr);
+ }
+ uintptr_t End() const {
+ return reinterpret_cast<uintptr_t>(ptr) + size;
+ }
+ };
+
+ class SortChunkByPtr {
+ public:
+ bool operator()(const SpaceChunk& a, const SpaceChunk& b) const {
+ return reinterpret_cast<uintptr_t>(a.ptr) < reinterpret_cast<uintptr_t>(b.ptr);
+ }
+ };
+
+ using FreeByStartSet = std::set<SpaceChunk, SortChunkByPtr>;
+
+ // Map size to an iterator to free_by_start_'s entry.
+ struct FreeBySizeEntry {
+ FreeBySizeEntry(size_t sz, FreeByStartSet::const_iterator entry)
+ : size(sz), free_by_start_entry(entry) { }
+
+ // We need mutable members as we keep these objects in a std::set<> (providing only const
+ // access) but we modify these members while carefully preserving the std::set<> ordering.
+ mutable size_t size;
+ mutable FreeByStartSet::const_iterator free_by_start_entry;
+ };
+ struct FreeBySizeComparator {
+ bool operator()(const FreeBySizeEntry& lhs, const FreeBySizeEntry& rhs) const {
+ if (lhs.size != rhs.size) {
+ return lhs.size < rhs.size;
+ } else {
+ return lhs.free_by_start_entry->Start() < rhs.free_by_start_entry->Start();
+ }
+ }
+ };
+ using FreeBySizeSet = std::set<FreeBySizeEntry, FreeBySizeComparator>;
+
+ SpaceChunk NewFileChunk(size_t min_size) REQUIRES(lock_);
+
+ void RemoveChunk(FreeBySizeSet::const_iterator free_by_size_pos) REQUIRES(lock_);
+ void InsertChunk(const SpaceChunk& chunk) REQUIRES(lock_);
+
+ int fd_;
+ size_t size_;
+
+ // NOTE: Boost.Bimap would be useful for the two following members.
+
+ // Map start of a free chunk to its size.
+ FreeByStartSet free_by_start_ GUARDED_BY(lock_);
+ // Free chunks ordered by size.
+ FreeBySizeSet free_by_size_ GUARDED_BY(lock_);
+
+ mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ DISALLOW_COPY_AND_ASSIGN(SwapSpace);
+};
+
+template <typename T> class SwapAllocator;
+
+template <>
+class SwapAllocator<void> {
+ public:
+ using value_type = void;
+ using pointer = void*;
+ using const_pointer = const void*;
+
+ template <typename U>
+ struct rebind {
+ using other = SwapAllocator<U>;
+ };
+
+ explicit SwapAllocator(SwapSpace* swap_space) : swap_space_(swap_space) {}
+
+ template <typename U>
+ SwapAllocator(const SwapAllocator<U>& other)
+ : swap_space_(other.swap_space_) {}
+
+ SwapAllocator(const SwapAllocator& other) = default;
+ SwapAllocator& operator=(const SwapAllocator& other) = default;
+ ~SwapAllocator() = default;
+
+ private:
+ SwapSpace* swap_space_;
+
+ template <typename U>
+ friend class SwapAllocator;
+
+ template <typename U>
+ friend bool operator==(const SwapAllocator<U>& lhs, const SwapAllocator<U>& rhs);
+};
+
+template <typename T>
+class SwapAllocator {
+ public:
+ using value_type = T;
+ using pointer = T*;
+ using reference = T&;
+ using const_pointer = const T*;
+ using const_reference = const T&;
+ using size_type = size_t;
+ using difference_type = ptrdiff_t;
+
+ template <typename U>
+ struct rebind {
+ using other = SwapAllocator<U>;
+ };
+
+ explicit SwapAllocator(SwapSpace* swap_space) : swap_space_(swap_space) {}
+
+ template <typename U>
+ SwapAllocator(const SwapAllocator<U>& other)
+ : swap_space_(other.swap_space_) {}
+
+ SwapAllocator(const SwapAllocator& other) = default;
+ SwapAllocator& operator=(const SwapAllocator& other) = default;
+ ~SwapAllocator() = default;
+
+ size_type max_size() const {
+ return static_cast<size_type>(-1) / sizeof(T);
+ }
+
+ pointer address(reference x) const { return &x; }
+ const_pointer address(const_reference x) const { return &x; }
+
+ pointer allocate(size_type n, SwapAllocator<void>::pointer hint ATTRIBUTE_UNUSED = nullptr) {
+ DCHECK_LE(n, max_size());
+ if (swap_space_ == nullptr) {
+ T* result = reinterpret_cast<T*>(malloc(n * sizeof(T)));
+ CHECK_IMPLIES(result == nullptr, n == 0u); // Abort if malloc() fails.
+ return result;
+ } else {
+ return reinterpret_cast<T*>(swap_space_->Alloc(n * sizeof(T)));
+ }
+ }
+ void deallocate(pointer p, size_type n) {
+ if (swap_space_ == nullptr) {
+ free(p);
+ } else {
+ swap_space_->Free(p, n * sizeof(T));
+ }
+ }
+
+ void construct(pointer p, const_reference val) {
+ new (static_cast<void*>(p)) value_type(val);
+ }
+ template <class U, class... Args>
+ void construct(U* p, Args&&... args) {
+ ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...);
+ }
+ void destroy(pointer p) {
+ p->~value_type();
+ }
+
+ inline bool operator==(SwapAllocator const& other) {
+ return swap_space_ == other.swap_space_;
+ }
+ inline bool operator!=(SwapAllocator const& other) {
+ return !operator==(other);
+ }
+
+ private:
+ SwapSpace* swap_space_;
+
+ template <typename U>
+ friend class SwapAllocator;
+
+ template <typename U>
+ friend bool operator==(const SwapAllocator<U>& lhs, const SwapAllocator<U>& rhs);
+};
+
+template <typename T>
+inline bool operator==(const SwapAllocator<T>& lhs, const SwapAllocator<T>& rhs) {
+ return lhs.swap_space_ == rhs.swap_space_;
+}
+
+template <typename T>
+inline bool operator!=(const SwapAllocator<T>& lhs, const SwapAllocator<T>& rhs) {
+ return !(lhs == rhs);
+}
+
+template <typename T>
+using SwapVector = std::vector<T, SwapAllocator<T>>;
+template <typename T, typename Comparator>
+using SwapSet = std::set<T, Comparator, SwapAllocator<T>>;
+
+} // namespace art
+
+#endif // ART_DEX2OAT_UTILS_SWAP_SPACE_H_
diff --git a/dex2oat/utils/swap_space_test.cc b/dex2oat/utils/swap_space_test.cc
new file mode 100644
index 0000000..eb79cfd
--- /dev/null
+++ b/dex2oat/utils/swap_space_test.cc
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014 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 "utils/swap_space.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <cstdio>
+
+#include "gtest/gtest.h"
+
+#include "base/common_art_test.h"
+#include "base/os.h"
+#include "base/unix_file/fd_file.h"
+
+namespace art {
+
+class SwapSpaceTest : public CommonArtTest {};
+
+static void SwapTest(bool use_file) {
+ ScratchFile scratch;
+ int fd = scratch.GetFd();
+ unlink(scratch.GetFilename().c_str());
+
+ SwapSpace pool(fd, 1 * MB);
+ SwapAllocator<void> alloc(use_file ? &pool : nullptr);
+
+ SwapVector<int32_t> v(alloc);
+ v.reserve(1000000);
+ for (int32_t i = 0; i < 1000000; ++i) {
+ v.push_back(i);
+ EXPECT_EQ(i, v[i]);
+ }
+
+ SwapVector<int32_t> v2(alloc);
+ v2.reserve(1000000);
+ for (int32_t i = 0; i < 1000000; ++i) {
+ v2.push_back(i);
+ EXPECT_EQ(i, v2[i]);
+ }
+
+ SwapVector<int32_t> v3(alloc);
+ v3.reserve(500000);
+ for (int32_t i = 0; i < 1000000; ++i) {
+ v3.push_back(i);
+ EXPECT_EQ(i, v2[i]);
+ }
+
+ // Verify contents.
+ for (int32_t i = 0; i < 1000000; ++i) {
+ EXPECT_EQ(i, v[i]);
+ EXPECT_EQ(i, v2[i]);
+ EXPECT_EQ(i, v3[i]);
+ }
+
+ scratch.Close();
+}
+
+TEST_F(SwapSpaceTest, Memory) {
+ SwapTest(false);
+}
+
+TEST_F(SwapSpaceTest, Swap) {
+ SwapTest(true);
+}
+
+} // namespace art
diff --git a/dex2oat/verifier_deps_test.cc b/dex2oat/verifier_deps_test.cc
index 708af04..6a2deba 100644
--- a/dex2oat/verifier_deps_test.cc
+++ b/dex2oat/verifier_deps_test.cc
@@ -47,6 +47,7 @@
deps_(nullptr) {}
void AddUncompilableMethod(MethodReference ref ATTRIBUTE_UNUSED) override {}
+ void AddUncompilableClass(ClassReference ref ATTRIBUTE_UNUSED) override {}
void ClassRejected(ClassReference ref ATTRIBUTE_UNUSED) override {}
verifier::VerifierDeps* GetVerifierDeps() const override { return deps_; }
@@ -630,12 +631,5 @@
ASSERT_FALSE(buffer.empty());
}
-TEST_F(VerifierDepsTest, Assignable_Arrays) {
- ASSERT_TRUE(TestAssignabilityRecording(/* dst= */ "[LIface;",
- /* src= */ "[LMyClassExtendingInterface;"));
- ASSERT_FALSE(HasAssignable(
- "LIface;", "LMyClassExtendingInterface;"));
-}
-
} // namespace verifier
} // namespace art
diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h
index 66ca84b..c819c67 100644
--- a/dexlayout/dex_ir.h
+++ b/dexlayout/dex_ir.h
@@ -20,7 +20,7 @@
#define ART_DEXLAYOUT_DEX_IR_H_
#include <stdint.h>
-
+#include <unordered_map>
#include <vector>
#include "base/iteration_range.h"
@@ -262,13 +262,21 @@
// Sort the vector by copying pointers over.
template <typename MapType>
void SortByMapOrder(const MapType& map) {
- auto it = map.begin();
CHECK_EQ(map.size(), Size());
+
+ // Move all pointers to a temporary map owning the pointers.
+ std::unordered_map<T*, ElementType> pointers_map;
+ pointers_map.reserve(Size());
+ for (std::unique_ptr<T>& element : collection_) {
+ pointers_map[element.get()] = std::move(element);
+ }
+
+ // Move back the pointers to the original vector according to the map order.
+ auto it = map.begin();
for (size_t i = 0; i < Size(); ++i) {
- // There are times when the array will temporarily contain the same pointer twice, doing the
- // release here sure there is no double free errors.
- collection_[i].release();
- collection_[i].reset(it->second);
+ auto element_it = pointers_map.find(it->second);
+ DCHECK(element_it != pointers_map.end());
+ collection_[i] = std::move(element_it->second);
++it;
}
}
diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc
index 7f05ae8..e7473c0 100644
--- a/dexlayout/dex_writer.cc
+++ b/dexlayout/dex_writer.cc
@@ -30,8 +30,6 @@
namespace art {
-constexpr uint32_t DexWriter::kDataSectionAlignment;
-
static size_t EncodeIntValue(int32_t value, uint8_t* buffer) {
size_t length = 0;
if (value >= 0) {
diff --git a/dexoptanalyzer/Android.bp b/dexoptanalyzer/Android.bp
index 1e37b8d..82f0c1a 100644
--- a/dexoptanalyzer/Android.bp
+++ b/dexoptanalyzer/Android.bp
@@ -83,8 +83,12 @@
art_cc_defaults {
name: "art_dexoptanalyzer_tests_defaults",
+ static_libs: [
+ "libziparchive",
+ ],
shared_libs: [
- "libbacktrace",
+ "libunwindstack",
+ "libz", // libziparchive dependency; must be repeated here since it's a static lib.
],
data: [
":art-gtest-jars-LinkageTest",
diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc
index 0cc2cdb..ef43157 100644
--- a/dexoptanalyzer/dexoptanalyzer.cc
+++ b/dexoptanalyzer/dexoptanalyzer.cc
@@ -122,9 +122,6 @@
UsageError(" print a colon-separated list of its dex files to standard output. Dexopt");
UsageError(" needed analysis is not performed when this option is set.");
UsageError("");
- UsageError(" --validate-bcp: validates the boot class path files (.art, .oat, .vdex).");
- UsageError(" Requires --isa and --image options to locate artifacts.");
- UsageError("");
UsageError("Return code:");
UsageError(" To make it easier to integrate with the internal tools this command will make");
UsageError(" available its result (dexoptNeeded) as the exit/return code. i.e. it will not");
@@ -147,10 +144,7 @@
class DexoptAnalyzer final {
public:
- DexoptAnalyzer() :
- only_flatten_context_(false),
- only_validate_bcp_(false),
- downgrade_(false) {}
+ DexoptAnalyzer() : only_flatten_context_(false), downgrade_(false) {}
void ParseArgs(int argc, char **argv) {
original_argc = argc;
@@ -236,8 +230,6 @@
}
} else if (option == "--flatten-class-loader-context") {
only_flatten_context_ = true;
- } else if (option == "--validate-bcp") {
- only_validate_bcp_ = true;
} else {
Usage("Unknown argument '%s'", raw_option);
}
@@ -324,6 +316,7 @@
class_loader_context.get(),
/*load_executable=*/ false,
/*only_load_trusted_executable=*/ false,
+ /*runtime_options=*/ nullptr,
vdex_fd_,
oat_fd_,
zip_fd_);
@@ -362,82 +355,6 @@
}
}
- // Validates the boot classpath and boot classpath extensions by checking the image checksums,
- // the oat files and the vdex files.
- //
- // Returns `ReturnCode::kNoDexOptNeeded` when all the files are up-to-date,
- // `ReturnCode::kDex2OatFromScratch` if any of the files are missing or out-of-date, and
- // `ReturnCode::kErrorCannotCreateRuntime` if the files could not be tested due to problem
- // creating a runtime.
- ReturnCode ValidateBcp() const {
- using ImageSpace = gc::space::ImageSpace;
-
- if (!CreateRuntime()) {
- return ReturnCode::kErrorCannotCreateRuntime;
- }
- std::unique_ptr<Runtime> runtime(Runtime::Current());
-
- auto dex_files = ArrayRef<const DexFile* const>(runtime->GetClassLinker()->GetBootClassPath());
- auto boot_image_spaces = ArrayRef<ImageSpace* const>(runtime->GetHeap()->GetBootImageSpaces());
- const std::string checksums = ImageSpace::GetBootClassPathChecksums(boot_image_spaces,
- dex_files);
-
- std::string error_msg;
- const std::vector<std::string>& bcp = runtime->GetBootClassPath();
- const std::vector<std::string>& bcp_locations = runtime->GetBootClassPathLocations();
- const std::vector<int>& bcp_fds = runtime->GetBootClassPathFds();
- const std::vector<std::string>& image_locations = runtime->GetImageLocations();
- const std::string bcp_locations_path = android::base::Join(bcp_locations, ':');
- if (!ImageSpace::VerifyBootClassPathChecksums(checksums,
- bcp_locations_path,
- ArrayRef<const std::string>(image_locations),
- ArrayRef<const std::string>(bcp_locations),
- ArrayRef<const std::string>(bcp),
- ArrayRef<const int>(bcp_fds),
- runtime->GetInstructionSet(),
- &error_msg)) {
- LOG(INFO) << "Failed to verify boot class path checksums: " << error_msg;
- return ReturnCode::kDex2OatFromScratch;
- }
-
- const auto& image_spaces = runtime->GetHeap()->GetBootImageSpaces();
- size_t bcp_component_count = 0;
- for (const auto& image_space : image_spaces) {
- if (!image_space->GetImageHeader().IsValid()) {
- LOG(INFO) << "Image header is not valid: " << image_space->GetImageFilename();
- return ReturnCode::kDex2OatFromScratch;
- }
- const OatFile* oat_file = image_space->GetOatFile();
- if (oat_file == nullptr) {
- const std::string oat_path = ReplaceFileExtension(image_space->GetImageFilename(), "oat");
- LOG(INFO) << "Oat file missing: " << oat_path;
- return ReturnCode::kDex2OatFromScratch;
- }
- if (!oat_file->GetOatHeader().IsValid() ||
- !ImageSpace::ValidateOatFile(*oat_file, &error_msg)) {
- LOG(INFO) << "Oat file is not valid: " << oat_file->GetLocation() << " " << error_msg;
- return ReturnCode::kDex2OatFromScratch;
- }
- const VdexFile* vdex_file = oat_file->GetVdexFile();
- if (vdex_file == nullptr || !vdex_file->IsValid()) {
- LOG(INFO) << "Vdex file is not valid : " << oat_file->GetLocation();
- return ReturnCode::kDex2OatFromScratch;
- }
- bcp_component_count += image_space->GetComponentCount();
- }
-
- // If the number of components encountered in the image spaces does not match the number
- // of components expected from the boot classpath locations then something is missing.
- if (bcp_component_count != bcp_locations.size()) {
- for (size_t i = bcp_component_count; i < bcp_locations.size(); ++i) {
- LOG(INFO) << "Missing image file for " << bcp_locations[i];
- }
- return ReturnCode::kDex2OatFromScratch;
- }
-
- return ReturnCode::kNoDexOptNeeded;
- }
-
ReturnCode FlattenClassLoaderContext() const {
DCHECK(only_flatten_context_);
if (context_str_.empty()) {
@@ -449,15 +366,13 @@
Usage("Invalid --class-loader-context '%s'", context_str_.c_str());
}
- std::cout << context->FlattenDexPaths() << std::flush;
+ std::cout << android::base::Join(context->FlattenDexPaths(), ':') << std::flush;
return ReturnCode::kFlattenClassLoaderContextSuccess;
}
ReturnCode Run() const {
if (only_flatten_context_) {
return FlattenClassLoaderContext();
- } else if (only_validate_bcp_) {
- return ValidateBcp();
} else {
return GetDexOptNeeded();
}
@@ -469,7 +384,6 @@
CompilerFilter::Filter compiler_filter_;
std::string context_str_;
bool only_flatten_context_;
- bool only_validate_bcp_;
ProfileAnalysisResult profile_analysis_result_;
bool downgrade_;
std::string image_;
diff --git a/disassembler/Android.bp b/disassembler/Android.bp
index 4b55673..b7f758f 100644
--- a/disassembler/Android.bp
+++ b/disassembler/Android.bp
@@ -104,7 +104,6 @@
],
},
},
-
apex_available: [
"com.android.art",
"com.android.art.debug",
@@ -132,3 +131,38 @@
"com.android.art",
],
}
+
+art_cc_defaults {
+ name: "art_disassembler_tests_defaults",
+ codegen: {
+ arm64: {
+ srcs: ["disassembler_arm64_test.cc"],
+ },
+ },
+}
+
+// Version of ART gtest `art_disassembler_tests` bundled with the ART APEX on target.
+// TODO(b/192274705): Remove this module when the migration to standalone ART gtests is complete.
+art_cc_test {
+ name: "art_disassembler_tests",
+ defaults: [
+ "art_gtest_defaults",
+ "art_disassembler_tests_defaults",
+ ],
+ static_libs: [
+ "libvixld",
+ ],
+}
+
+// Standalone version of ART gtest `art_disassembler_tests`,
+// not bundled with the ART APEX on target.
+art_cc_test {
+ name: "art_standalone_disassembler_tests",
+ defaults: [
+ "art_standalone_gtest_defaults",
+ "art_disassembler_tests_defaults",
+ ],
+ static_libs: [
+ "libvixl",
+ ],
+}
diff --git a/disassembler/disassembler_arm64.cc b/disassembler/disassembler_arm64.cc
index 0d51cfd..23472a8 100644
--- a/disassembler/disassembler_arm64.cc
+++ b/disassembler/disassembler_arm64.cc
@@ -18,6 +18,8 @@
#include <inttypes.h>
+#include <regex>
+
#include <sstream>
#include "android-base/logging.h"
@@ -58,9 +60,34 @@
Disassembler::AppendRegisterNameToOutput(instr, reg);
}
-void CustomDisassembler::VisitLoadLiteral(const Instruction* instr) {
- Disassembler::VisitLoadLiteral(instr);
+void CustomDisassembler::Visit(vixl::aarch64::Metadata* metadata, const Instruction* instr) {
+ vixl::aarch64::Disassembler::Visit(metadata, instr);
+ const std::string& form = (*metadata)["form"];
+ // These regexs are long, but it is an attempt to match the mapping entry keys in the
+ // #define DEFAULT_FORM_TO_VISITOR_MAP(VISITORCLASS) in the file
+ // external/vixl/src/aarch64/decoder-visitor-map-aarch64.h
+ // for the ::VisitLoadLiteralInstr, ::VisitLoadStoreUnsignedOffset or ::VisitUnconditionalBranch
+ // function addresess key values.
+ // N.B. the mapping are many to one.
+ if (std::regex_match(form, std::regex("(ldrsw|ldr|prfm)_(32|64|d|b|h|q|s)_loadlit"))) {
+ VisitLoadLiteralInstr(instr);
+ return;
+ }
+
+ if (std::regex_match(form, std::regex(
+ "(ldrb|ldrh|ldrsb|ldrsh|ldrsw|ldr|prfm|strb|strh|str)_(32|64|d|b|h|q|s)_ldst_pos"))) {
+ VisitLoadStoreUnsignedOffsetInstr(instr);
+ return;
+ }
+
+ if (std::regex_match(form, std::regex("(bl|b)_only_branch_imm"))) {
+ VisitUnconditionalBranchInstr(instr);
+ return;
+ }
+}
+
+void CustomDisassembler::VisitLoadLiteralInstr(const Instruction* instr) {
if (!read_literals_) {
return;
}
@@ -69,6 +96,7 @@
// avoid trying to fetch invalid literals (we can encounter this when
// interpreting raw data as instructions).
void* data_address = instr->GetLiteralAddress<void*>();
+
if (data_address < base_address_ || data_address >= end_address_) {
AppendToOutput(" (?)");
return;
@@ -97,17 +125,13 @@
}
}
-void CustomDisassembler::VisitLoadStoreUnsignedOffset(const Instruction* instr) {
- Disassembler::VisitLoadStoreUnsignedOffset(instr);
-
+void CustomDisassembler::VisitLoadStoreUnsignedOffsetInstr(const Instruction* instr) {
if (instr->GetRn() == TR) {
AppendThreadOfsetName(instr);
}
}
-void CustomDisassembler::VisitUnconditionalBranch(const Instruction* instr) {
- Disassembler::VisitUnconditionalBranch(instr);
-
+void CustomDisassembler::VisitUnconditionalBranchInstr(const Instruction* instr) {
if (instr->Mask(UnconditionalBranchMask) == BL) {
const Instruction* target = instr->GetImmPCOffsetTarget();
if (target >= base_address_ &&
diff --git a/disassembler/disassembler_arm64.h b/disassembler/disassembler_arm64.h
index a895dfe..d0443d2 100644
--- a/disassembler/disassembler_arm64.h
+++ b/disassembler/disassembler_arm64.h
@@ -47,16 +47,20 @@
void AppendRegisterNameToOutput(const vixl::aarch64::Instruction* instr,
const vixl::aarch64::CPURegister& reg) override;
- // Improve the disassembly of literal load instructions.
- void VisitLoadLiteral(const vixl::aarch64::Instruction* instr) override;
-
- // Improve the disassembly of thread offset.
- void VisitLoadStoreUnsignedOffset(const vixl::aarch64::Instruction* instr) override;
-
- // Improve the disassembly of branch to thunk jumping to pointer from thread entrypoint.
- void VisitUnconditionalBranch(const vixl::aarch64::Instruction* instr) override;
+ // Intercepts the instruction flow captured by the parent method,
+ // to specially instrument for particular instruction types.
+ void Visit(vixl::aarch64::Metadata* metadata, const vixl::aarch64::Instruction* instr) override;
private:
+ // Improve the disassembly of literal load instructions.
+ void VisitLoadLiteralInstr(const vixl::aarch64::Instruction* instr);
+
+ // Improve the disassembly of thread offset.
+ void VisitLoadStoreUnsignedOffsetInstr(const vixl::aarch64::Instruction* instr);
+
+ // Improve the disassembly of branch to thunk jumping to pointer from thread entrypoint.
+ void VisitUnconditionalBranchInstr(const vixl::aarch64::Instruction* instr);
+
void AppendThreadOfsetName(const vixl::aarch64::Instruction* instr);
// Indicate if the disassembler should read data loaded from literal pools.
diff --git a/disassembler/disassembler_arm64_test.cc b/disassembler/disassembler_arm64_test.cc
new file mode 100644
index 0000000..81c067a
--- /dev/null
+++ b/disassembler/disassembler_arm64_test.cc
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2022 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 <inttypes.h>
+
+#include <regex>
+
+#include <sstream>
+
+#include "base/common_art_test.h"
+#include "disassembler_arm64.h"
+#include "thread.h"
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+#include "aarch64/disasm-aarch64.h"
+#include "aarch64/macro-assembler-aarch64.h"
+#pragma GCC diagnostic pop
+
+
+using namespace vixl::aarch64; // NOLINT(build/namespaces)
+
+namespace art {
+namespace arm64 {
+
+/**
+ * Fixture class for the ArtDisassemblerTest tests.
+ */
+class ArtDisassemblerTest : public CommonArtTest {
+ public:
+ ArtDisassemblerTest() {
+ }
+
+ void SetupAssembly(uint64_t end_address) {
+ masm.GetCPUFeatures()->Combine(vixl::CPUFeatures::All());
+
+ disamOptions.reset(new DisassemblerOptions(/* absolute_addresses= */ true,
+ reinterpret_cast<uint8_t*>(0x0),
+ reinterpret_cast<uint8_t*>(end_address),
+ /* can_read_literals_= */ true,
+ &Thread::DumpThreadOffset<PointerSize::k64>));
+ disasm.reset(new CustomDisassembler(&*disamOptions));
+ decoder.AppendVisitor(disasm.get());
+ masm.SetGenerateSimulatorCode(false);
+ }
+
+ static constexpr size_t kMaxSizeGenerated = 1024;
+
+ template <typename LamdaType>
+ void ImplantInstruction(LamdaType fn) {
+ vixl::ExactAssemblyScope guard(&masm,
+ kMaxSizeGenerated,
+ vixl::ExactAssemblyScope::kMaximumSize);
+ fn();
+ }
+
+ // Appends an instruction to the existing buffer and then
+ // attempts to match the output of that instructions disassembly
+ // against a regex expression. Fails if no match is found.
+ template <typename LamdaType>
+ void CompareInstruction(LamdaType fn, const char* EXP) {
+ ImplantInstruction(fn);
+ masm.FinalizeCode();
+
+ // This gets the last instruction in the buffer.
+ // The end address of the buffer is at the end of the last instruction.
+ // sizeof(Instruction) is 1 byte as it in an empty class.
+ // Therefore we need to go back kInstructionSize * sizeof(Instruction) bytes
+ // in order to get to the start of the last instruction.
+ const Instruction* targetInstruction =
+ masm.GetBuffer()->GetEndAddress<Instruction*>()->
+ GetInstructionAtOffset(-static_cast<signed>(kInstructionSize));
+
+ decoder.Decode(targetInstruction);
+
+ const char* disassembly = disasm->GetOutput();
+
+ if (!std::regex_match(disassembly, std::regex(EXP))) {
+ const uint32_t encoding = static_cast<uint32_t>(targetInstruction->GetInstructionBits());
+
+ printf("\nEncoding: %08" PRIx32 "\nExpected: %s\nFound: %s\n",
+ encoding,
+ EXP,
+ disassembly);
+
+ ADD_FAILURE();
+ }
+ printf("----\n%s\n", disassembly);
+ }
+
+ std::unique_ptr<CustomDisassembler> disasm;
+ std::unique_ptr<DisassemblerOptions> disamOptions;
+ Decoder decoder;
+ MacroAssembler masm;
+};
+
+#define IMPLANT(fn) \
+ do { \
+ ImplantInstruction([&]() { this->masm.fn; }); \
+ } while (0)
+
+#define COMPARE(fn, output) \
+ do { \
+ CompareInstruction([&]() { this->masm.fn; }, (output)); \
+ } while (0)
+
+// These tests map onto the named per instruction instrumentation functions in:
+// ART/art/disassembler/disassembler_arm.cc
+// Context can be found in the logic conditional on incoming instruction types and sequences in the
+// ART disassembler. As of writing the functionality we are testing for that of additional
+// diagnostic info being appended to the end of the ART disassembly output.
+TEST_F(ArtDisassemblerTest, LoadLiteralVisitBadAddress) {
+ SetupAssembly(0xffffff);
+
+ // Check we append an erroneous hint "(?)" for literal load instructions with
+ // out of scope literal pool value addresses.
+ COMPARE(ldr(x0, vixl::aarch64::Assembler::ImmLLiteral(1000)),
+ "ldr x0, pc\\+128000 \\(addr -?0x[0-9a-fA-F]+\\) \\(\\?\\)");
+}
+
+TEST_F(ArtDisassemblerTest, LoadLiteralVisit) {
+ SetupAssembly(0xffffffffffffffff);
+
+ // Test that we do not append anything for ineligible instruction.
+ COMPARE(ldr(x0, MemOperand(x18, 0)), "ldr x0, \\[x18\\]$");
+
+ // Check we do append some extra info in the right text format for valid literal load instruction.
+ COMPARE(ldr(w0, vixl::aarch64::Assembler::ImmLLiteral(0)),
+ "ldr w0, pc\\+0 \\(addr -?0x[0-9a-f]+\\) \\(0x18000000 / 402653184\\)");
+ // We don't compare with exact value even though it's a known literal (the encoding of the
+ // instruction itself) since the precision of printed floating point values could change.
+ COMPARE(ldr(s0, vixl::aarch64::Assembler::ImmLLiteral(0)),
+ "ldr s0, pc\\+0 \\(addr -?0x[0-9a-f]+\\) \\([0-9]+.[0-9]+e(\\+|-)[0-9]+\\)");
+}
+
+TEST_F(ArtDisassemblerTest, LoadStoreUnsignedOffsetVisit) {
+ SetupAssembly(0xffffffffffffffff);
+
+ // Test that we do not append anything for ineligible instruction.
+ COMPARE(ldr(x0, MemOperand(x18, 8)), "ldr x0, \\[x18, #8\\]$");
+ // Test that we do append the function name if the instruction is a load from the address
+ // stored in the TR register.
+ COMPARE(ldr(x0, MemOperand(x19, 8)), "ldr x0, \\[tr, #8\\] ; thin_lock_thread_id");
+}
+
+TEST_F(ArtDisassemblerTest, UnconditionalBranchNoAppendVisit) {
+ SetupAssembly(0xffffffffffffffff);
+
+ vixl::aarch64::Label destination;
+ masm.Bind(&destination);
+
+ IMPLANT(ldr(x16, MemOperand(x18, 0)));
+
+ // Test that we do not append anything for ineligible instruction.
+ COMPARE(bl(&destination),
+ "bl #-0x4 \\(addr -?0x[0-9a-f]+\\)$");
+}
+
+TEST_F(ArtDisassemblerTest, UnconditionalBranchVisit) {
+ SetupAssembly(0xffffffffffffffff);
+
+ vixl::aarch64::Label destination;
+ masm.Bind(&destination);
+
+ IMPLANT(ldr(x16, MemOperand(x19, 0)));
+ IMPLANT(br(x16));
+
+ // Test that we do append the function name if the instruction is a branch
+ // to a load that reads data from the address in the TR register, into the IPO register
+ // followed by a BR branching using the IPO register.
+ COMPARE(bl(&destination),
+ "bl #-0x8 \\(addr -?0x[0-9a-f]+\\) ; state_and_flags");
+}
+
+
+} // namespace arm64
+} // namespace art
diff --git a/dt_fd_forward/Android.bp b/dt_fd_forward/Android.bp
index 772b55e..e0507d5 100644
--- a/dt_fd_forward/Android.bp
+++ b/dt_fd_forward/Android.bp
@@ -39,10 +39,10 @@
visibility: [":__subpackages__"],
license_kinds: [
"SPDX-license-identifier-Apache-2.0",
- "SPDX-license-identifier-GPL-with-classpath-exception",
+ "SPDX-license-identifier-GPL-2.0-with-classpath-exception",
],
license_text: [
- "NOTICE",
+ "LICENSE",
],
}
diff --git a/dt_fd_forward/NOTICE b/dt_fd_forward/LICENSE
similarity index 100%
rename from dt_fd_forward/NOTICE
rename to dt_fd_forward/LICENSE
diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc
index 745475e..fe70b10 100644
--- a/imgdiag/imgdiag.cc
+++ b/imgdiag/imgdiag.cc
@@ -17,11 +17,10 @@
#include <stdio.h>
#include <stdlib.h>
-#include <fstream>
#include <functional>
-#include <iostream>
#include <map>
#include <optional>
+#include <ostream>
#include <set>
#include <string>
#include <unordered_set>
@@ -73,6 +72,16 @@
kImageAndZygote
};
+std::ostream& operator<<(std::ostream& os, RemoteProcesses remotes) {
+ switch (remotes) {
+ case RemoteProcesses::kImageOnly: os << "ImageOnly"; break;
+ case RemoteProcesses::kZygoteOnly: os << "ZygoteOnly"; break;
+ case RemoteProcesses::kImageAndZygote: os << "ImageAndZygote"; break;
+ default: UNREACHABLE();
+ }
+ return os;
+}
+
struct MappingData {
// The count of pages that are considered dirty by the OS.
size_t dirty_pages = 0;
@@ -90,6 +99,8 @@
size_t false_dirty_pages = 0;
// Set of the local virtual page indices that are dirty.
std::set<size_t> dirty_page_set;
+ // Private dirty page counts for each section of the image
+ std::array<size_t, ImageHeader::kSectionCount> private_dirty_pages_for_section = {};
};
static std::string GetClassDescriptor(mirror::Class* klass)
@@ -209,14 +220,24 @@
return reinterpret_cast<T*>(const_cast<uint8_t*>(local_ptr));
}
-template <typename T> size_t EntrySize(T* entry);
-template<> size_t EntrySize(mirror::Object* object) REQUIRES_SHARED(Locks::mutator_lock_) {
+size_t EntrySize(mirror::Object* object) REQUIRES_SHARED(Locks::mutator_lock_) {
return object->SizeOf();
}
-template<> size_t EntrySize(ArtMethod* art_method) REQUIRES_SHARED(Locks::mutator_lock_) {
+size_t EntrySize(ArtMethod* art_method) REQUIRES_SHARED(Locks::mutator_lock_) {
return sizeof(*art_method);
}
+// Print all pages the entry belongs to
+void PrintEntryPages(uintptr_t entry_address, size_t entry_size, std::ostream& os) {
+ const char* tabs = " ";
+ const uintptr_t first_page_idx = entry_address / kPageSize;
+ const uintptr_t last_page_idx = RoundUp(entry_address + entry_size,
+ kObjectAlignment) / kPageSize;
+ for (uintptr_t page_idx = first_page_idx; page_idx <= last_page_idx; ++page_idx) {
+ os << tabs << "page_idx=" << page_idx << "\n";
+ }
+}
+
// entry1 and entry2 might be relocated, this means we must use the runtime image's entry
// (image_entry) to avoid crashes.
template <typename T>
@@ -268,7 +289,6 @@
size_t GetDirtyEntryBytes() const { return dirty_entry_bytes_; }
size_t GetFalseDirtyEntryCount() const { return false_dirty_entries_.size(); }
size_t GetFalseDirtyEntryBytes() const { return false_dirty_entry_bytes_; }
- size_t GetZygoteDirtyEntryCount() const { return zygote_dirty_entries_.size(); }
protected:
bool IsEntryOnDirtyPage(T* entry, const std::set<size_t>& dirty_pages) const
@@ -289,10 +309,6 @@
return false;
}
- void AddZygoteDirtyEntry(T* entry) REQUIRES_SHARED(Locks::mutator_lock_) {
- zygote_dirty_entries_.insert(entry);
- }
-
void AddImageDirtyEntry(T* entry) REQUIRES_SHARED(Locks::mutator_lock_) {
image_dirty_entries_.insert(entry);
}
@@ -327,11 +343,6 @@
// If zygote_pid_only_ == false, these are private dirty entries in the application.
std::set<T*> image_dirty_entries_;
- // Zygote dirty entries (probably private dirty).
- // We only add entries here if they differed in both the image and the zygote, so
- // they are probably private dirty.
- std::set<T*> zygote_dirty_entries_;
-
std::map<off_t /* field offset */, size_t /* count */> field_dirty_count_;
private:
@@ -451,6 +462,7 @@
os_ << tabs
<< "Instance of " << mirror::Class::PrettyClass(klass) << " " << entry << "\n";
}
+ PrintEntryPages(reinterpret_cast<uintptr_t>(entry), EntrySize(entry), os_);
std::unordered_set<ArtField*> dirty_instance_fields;
std::unordered_set<ArtField*> dirty_static_fields;
@@ -768,6 +780,7 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
const char* tabs = " ";
os_ << tabs << "ArtMethod " << ArtMethod::PrettyMethod(method) << "\n";
+ PrintEntryPages(reinterpret_cast<uintptr_t>(method), EntrySize(method), os_);
std::unordered_set<size_t> dirty_members;
// Examine the members comprising the ArtMethod, computing which members are dirty.
@@ -1009,24 +1022,11 @@
os_ << " Application dirty entries (unknown whether private or shared dirty): ";
break;
}
- DiffDirtyEntries(ProcessType::kRemote,
+ DiffDirtyEntries(RegionCommon<T>::image_dirty_entries_,
begin_image_ptr,
RegionCommon<T>::remote_contents_,
base_ptr,
/*log_dirty_objects=*/true);
- // Print shared dirty after since it's less important.
- if (RegionCommon<T>::GetZygoteDirtyEntryCount() != 0) {
- // We only reach this point if both pids were specified. Furthermore,
- // entries are only displayed here if they differed in both the image
- // and the zygote, so they are probably private dirty.
- CHECK(remotes == RemoteProcesses::kImageAndZygote);
- os_ << "\n" << " Zygote dirty entries (probably shared dirty): ";
- DiffDirtyEntries(ProcessType::kZygote,
- begin_image_ptr,
- RegionCommon<T>::zygote_contents_,
- begin_image_ptr,
- /*log_dirty_objects=*/false);
- }
RegionSpecializedBase<T>::DumpDirtyObjects();
RegionSpecializedBase<T>::DumpDirtyEntries();
RegionSpecializedBase<T>::DumpFalseDirtyEntries();
@@ -1036,17 +1036,13 @@
private:
std::ostream& os_;
- void DiffDirtyEntries(ProcessType process_type,
+ void DiffDirtyEntries(const std::set<T*>& entries,
const uint8_t* begin_image_ptr,
ArrayRef<uint8_t> contents,
const uint8_t* base_ptr,
bool log_dirty_objects)
REQUIRES_SHARED(Locks::mutator_lock_) {
os_ << RegionCommon<T>::dirty_entries_.size() << "\n";
- const std::set<T*>& entries =
- (process_type == ProcessType::kZygote) ?
- RegionCommon<T>::zygote_dirty_entries_:
- RegionCommon<T>::image_dirty_entries_;
for (T* entry : entries) {
uint8_t* entry_bytes = reinterpret_cast<uint8_t*>(entry);
ptrdiff_t offset = entry_bytes - begin_image_ptr;
@@ -1077,17 +1073,11 @@
// Test private dirty first.
bool is_dirty = false;
if (have_zygote) {
- bool private_dirty = EntriesDiffer(entry, entry_zygote, entry_remote);
- if (private_dirty) {
+ if (EntriesDiffer(entry, entry_zygote, entry_remote)) {
// Private dirty, app vs zygote.
is_dirty = true;
RegionCommon<T>::AddImageDirtyEntry(entry);
}
- if (EntriesDiffer(entry, entry_zygote, entry)) {
- // Shared dirty, zygote vs image.
- is_dirty = true;
- RegionCommon<T>::AddZygoteDirtyEntry(entry);
- }
} else if (EntriesDiffer(entry, entry_remote, entry)) {
// Shared or private dirty, app vs image.
is_dirty = true;
@@ -1127,8 +1117,10 @@
bool Init() {
std::ostream& os = *os_;
- if (image_diff_pid_ < 0 && zygote_diff_pid_ < 0) {
- os << "Either --image-diff-pid or --zygote-diff-pid (or both) must be specified.\n";
+ if (image_diff_pid_ < 0 || zygote_diff_pid_ < 0) {
+ // TODO: ComputeDirtyBytes must be modified
+ // to support single app/zygote to bootimage comparison
+ os << "Both --image-diff-pid and --zygote-diff-pid must be specified.\n";
return false;
}
@@ -1203,69 +1195,14 @@
}
}
- std::unique_ptr<File> clean_pagemap_file;
std::unique_ptr<File> kpageflags_file;
std::unique_ptr<File> kpagecount_file;
- if (!open_file("/proc/self/pagemap", &clean_pagemap_file) ||
- !open_file("/proc/kpageflags", &kpageflags_file) ||
+ if (!open_file("/proc/kpageflags", &kpageflags_file) ||
!open_file("/proc/kpagecount", &kpagecount_file)) {
return false;
}
- // Note: the boot image is not really clean but close enough.
- // For now, log pages found to be dirty.
// TODO: Rewrite imgdiag to load boot image without creating a runtime.
- // FIXME: The following does not reliably detect dirty pages.
- Runtime* runtime = Runtime::Current();
- CHECK(!runtime->ShouldRelocate());
- size_t total_dirty_pages = 0u;
- for (gc::space::ImageSpace* space : runtime->GetHeap()->GetBootImageSpaces()) {
- const ImageHeader& image_header = space->GetImageHeader();
- const uint8_t* image_begin = image_header.GetImageBegin();
- const uint8_t* image_end = AlignUp(image_begin + image_header.GetImageSize(), kPageSize);
- size_t virtual_page_idx_begin = reinterpret_cast<uintptr_t>(image_begin) / kPageSize;
- size_t virtual_page_idx_end = reinterpret_cast<uintptr_t>(image_end) / kPageSize;
- size_t num_virtual_pages = virtual_page_idx_end - virtual_page_idx_begin;
-
- std::string error_msg;
- std::vector<uint64_t> page_frame_numbers(num_virtual_pages);
- if (!GetPageFrameNumbers(clean_pagemap_file.get(),
- virtual_page_idx_begin,
- ArrayRef<uint64_t>(page_frame_numbers),
- &error_msg)) {
- os << "Failed to get page frame numbers for image space " << space->GetImageLocation()
- << ", error: " << error_msg;
- return false;
- }
-
- std::vector<uint64_t> page_flags(num_virtual_pages);
- if (!GetPageFlagsOrCounts(kpageflags_file.get(),
- ArrayRef<const uint64_t>(page_frame_numbers),
- ArrayRef<uint64_t>(page_flags),
- &error_msg)) {
- os << "Failed to get page flags for image space " << space->GetImageLocation()
- << ", error: " << error_msg;
- return false;
- }
-
- size_t num_dirty_pages = 0u;
- std::optional<size_t> first_dirty_page;
- for (size_t i = 0u, size = page_flags.size(); i != size; ++i) {
- if (UNLIKELY((page_flags[i] & kPageFlagsDirtyMask) != 0u)) {
- ++num_dirty_pages;
- if (!first_dirty_page.has_value()) {
- first_dirty_page = i;
- }
- }
- }
- if (num_dirty_pages != 0u) {
- DCHECK(first_dirty_page.has_value());
- os << "Found " << num_dirty_pages << " dirty pages for " << space->GetImageLocation()
- << ", first dirty page: " << first_dirty_page.value_or(0u);
- total_dirty_pages += num_dirty_pages;
- }
- }
- os << "Found " << total_dirty_pages << " dirty pages in total ";
// Commit the mappings and files.
image_proc_maps_ = std::move(image_proc_maps);
@@ -1276,7 +1213,6 @@
zygote_mem_file_ = std::move(*zygote_mem_file);
zygote_pagemap_file_ = std::move(*zygote_pagemap_file);
}
- clean_pagemap_file_ = std::move(*clean_pagemap_file);
kpageflags_file_ = std::move(*kpageflags_file);
kpagecount_file_ = std::move(*kpagecount_file);
@@ -1313,128 +1249,110 @@
}
bool ComputeDirtyBytes(const ImageHeader& image_header,
- const uint8_t* image_begin,
const android::procinfo::MapInfo& boot_map,
ArrayRef<uint8_t> remote_contents,
- MappingData* mapping_data /*out*/) {
- std::ostream& os = *os_;
-
- size_t virtual_page_idx = 0; // Virtual page number (for an absolute memory address)
- size_t page_idx = 0; // Page index relative to 0
- size_t previous_page_idx = 0; // Previous page index relative to 0
-
-
+ ArrayRef<uint8_t> zygote_contents,
+ MappingData* mapping_data /*out*/,
+ std::string* error_msg /*out*/) {
// Iterate through one page at a time. Boot map begin/end already implicitly aligned.
for (uintptr_t begin = boot_map.start; begin != boot_map.end; begin += kPageSize) {
- ptrdiff_t offset = begin - boot_map.start;
+ const ptrdiff_t offset = begin - boot_map.start;
// We treat the image header as part of the memory map for now
// If we wanted to change this, we could pass base=start+sizeof(ImageHeader)
// But it might still be interesting to see if any of the ImageHeader data mutated
- const uint8_t* local_ptr = reinterpret_cast<const uint8_t*>(&image_header) + offset;
+ const uint8_t* zygote_ptr = &zygote_contents[offset];
const uint8_t* remote_ptr = &remote_contents[offset];
- if (memcmp(local_ptr, remote_ptr, kPageSize) != 0) {
+ if (memcmp(zygote_ptr, remote_ptr, kPageSize) != 0) {
mapping_data->different_pages++;
// Count the number of 32-bit integers that are different.
for (size_t i = 0; i < kPageSize / sizeof(uint32_t); ++i) {
const uint32_t* remote_ptr_int32 = reinterpret_cast<const uint32_t*>(remote_ptr);
- const uint32_t* local_ptr_int32 = reinterpret_cast<const uint32_t*>(local_ptr);
+ const uint32_t* zygote_ptr_int32 = reinterpret_cast<const uint32_t*>(zygote_ptr);
- if (remote_ptr_int32[i] != local_ptr_int32[i]) {
+ if (remote_ptr_int32[i] != zygote_ptr_int32[i]) {
mapping_data->different_int32s++;
}
}
+ // Count the number of bytes that are different.
+ for (size_t i = 0; i < kPageSize; ++i) {
+ if (remote_ptr[i] != zygote_ptr[i]) {
+ mapping_data->different_bytes++;
+ }
+ }
}
}
- std::vector<size_t> private_dirty_pages_for_section(ImageHeader::kSectionCount, 0u);
-
- // Iterate through one byte at a time.
- ptrdiff_t page_off_begin = image_header.GetImageBegin() - image_begin;
- for (uintptr_t begin = boot_map.start; begin != boot_map.end; ++begin) {
- previous_page_idx = page_idx;
+ for (uintptr_t begin = boot_map.start; begin != boot_map.end; begin += kPageSize) {
ptrdiff_t offset = begin - boot_map.start;
- // We treat the image header as part of the memory map for now
- // If we wanted to change this, we could pass base=start+sizeof(ImageHeader)
- // But it might still be interesting to see if any of the ImageHeader data mutated
- const uint8_t* local_ptr = reinterpret_cast<const uint8_t*>(&image_header) + offset;
- const uint8_t* remote_ptr = &remote_contents[offset];
+ // Virtual page number (for an absolute memory address)
+ size_t virtual_page_idx = begin / kPageSize;
- virtual_page_idx = reinterpret_cast<uintptr_t>(local_ptr) / kPageSize;
-
- // Calculate the page index, relative to the 0th page where the image begins
- page_idx = (offset + page_off_begin) / kPageSize;
- if (*local_ptr != *remote_ptr) {
- // Track number of bytes that are different
- mapping_data->different_bytes++;
+ uint64_t page_count = 0xC0FFEE;
+ // TODO: virtual_page_idx needs to be from the same process
+ int dirtiness = (IsPageDirty(&image_pagemap_file_, // Image-diff-pid procmap
+ &zygote_pagemap_file_, // Zygote procmap
+ &kpageflags_file_,
+ &kpagecount_file_,
+ virtual_page_idx, // compare same page in image
+ virtual_page_idx, // and zygote
+ &page_count,
+ error_msg));
+ if (dirtiness < 0) {
+ return false;
+ } else if (dirtiness > 0) {
+ mapping_data->dirty_pages++;
+ mapping_data->dirty_page_set.insert(mapping_data->dirty_page_set.end(), virtual_page_idx);
}
- // Independently count the # of dirty pages on the remote side
- size_t remote_virtual_page_idx = begin / kPageSize;
- if (previous_page_idx != page_idx) {
- uint64_t page_count = 0xC0FFEE;
- // TODO: virtual_page_idx needs to be from the same process
- std::string error_msg;
- int dirtiness = (IsPageDirty(&image_pagemap_file_, // Image-diff-pid procmap
- &clean_pagemap_file_, // Self procmap
- &kpageflags_file_,
- &kpagecount_file_,
- remote_virtual_page_idx, // potentially "dirty" page
- virtual_page_idx, // true "clean" page
- &page_count,
- &error_msg));
- if (dirtiness < 0) {
- os << error_msg;
- return false;
- } else if (dirtiness > 0) {
- mapping_data->dirty_pages++;
- mapping_data->dirty_page_set.insert(mapping_data->dirty_page_set.end(), virtual_page_idx);
- }
+ const bool is_dirty = dirtiness > 0;
+ const bool is_private = page_count == 1;
- bool is_dirty = dirtiness > 0;
- bool is_private = page_count == 1;
+ if (is_private) {
+ mapping_data->private_pages++;
+ }
- if (page_count == 1) {
- mapping_data->private_pages++;
- }
-
- if (is_dirty && is_private) {
- mapping_data->private_dirty_pages++;
- for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) {
- const ImageHeader::ImageSections section = static_cast<ImageHeader::ImageSections>(i);
- if (image_header.GetImageSection(section).Contains(offset)) {
- ++private_dirty_pages_for_section[i];
- }
+ if (is_dirty && is_private) {
+ mapping_data->private_dirty_pages++;
+ for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) {
+ const ImageHeader::ImageSections section = static_cast<ImageHeader::ImageSections>(i);
+ if (image_header.GetImageSection(section).Contains(offset)) {
+ mapping_data->private_dirty_pages_for_section[i] += 1;
}
}
}
}
mapping_data->false_dirty_pages = mapping_data->dirty_pages - mapping_data->different_pages;
+
+ return true;
+ }
+
+ void PrintMappingData(const MappingData& mapping_data, const ImageHeader& image_header) {
+ std::ostream& os = *os_;
// Print low-level (bytes, int32s, pages) statistics.
- os << mapping_data->different_bytes << " differing bytes,\n "
- << mapping_data->different_int32s << " differing int32s,\n "
- << mapping_data->different_pages << " differing pages,\n "
- << mapping_data->dirty_pages << " pages are dirty;\n "
- << mapping_data->false_dirty_pages << " pages are false dirty;\n "
- << mapping_data->private_pages << " pages are private;\n "
- << mapping_data->private_dirty_pages << " pages are Private_Dirty\n "
+ os << mapping_data.different_bytes << " differing bytes,\n "
+ << mapping_data.different_int32s << " differing int32s,\n "
+ << mapping_data.different_pages << " differing pages,\n "
+ << mapping_data.dirty_pages << " pages are dirty;\n "
+ << mapping_data.false_dirty_pages << " pages are false dirty;\n "
+ << mapping_data.private_pages << " pages are private;\n "
+ << mapping_data.private_dirty_pages << " pages are Private_Dirty\n "
<< "\n";
- size_t total_private_dirty_pages = std::accumulate(private_dirty_pages_for_section.begin(),
- private_dirty_pages_for_section.end(),
- 0u);
+ size_t total_private_dirty_pages = std::accumulate(
+ mapping_data.private_dirty_pages_for_section.begin(),
+ mapping_data.private_dirty_pages_for_section.end(),
+ 0u);
os << "Image sections (total private dirty pages " << total_private_dirty_pages << ")\n";
for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) {
const ImageHeader::ImageSections section = static_cast<ImageHeader::ImageSections>(i);
os << section << " " << image_header.GetImageSection(section)
- << " private dirty pages=" << private_dirty_pages_for_section[i] << "\n";
+ << " private dirty pages=" << mapping_data.private_dirty_pages_for_section[i] << "\n";
}
os << "\n";
-
- return true;
}
// Look at /proc/$pid/mem and only diff the things from there
@@ -1575,13 +1493,7 @@
// For more validation should also check the ImageHeader from the file
}
- MappingData mapping_data;
- os << "Mapping at [" << reinterpret_cast<void*>(boot_map.start) << ", "
- << reinterpret_cast<void*>(boot_map.end) << ") had:\n ";
- if (!ComputeDirtyBytes(image_header, image_begin, boot_map, remote_contents, &mapping_data)) {
- return false;
- }
RemoteProcesses remotes;
if (zygote_pid_only_) {
remotes = RemoteProcesses::kZygoteOnly;
@@ -1591,6 +1503,23 @@
remotes = RemoteProcesses::kImageOnly;
}
+ // Only app vs zygote is supported at the moment
+ CHECK_EQ(remotes, RemoteProcesses::kImageAndZygote);
+
+ MappingData mapping_data;
+ if (!ComputeDirtyBytes(image_header,
+ boot_map,
+ remote_contents,
+ zygote_contents,
+ &mapping_data,
+ &error_msg)) {
+ os << error_msg;
+ return false;
+ }
+ os << "Mapping at [" << reinterpret_cast<void*>(boot_map.start) << ", "
+ << reinterpret_cast<void*>(boot_map.end) << ") had:\n ";
+ PrintMappingData(mapping_data, image_header);
+
// Check all the mirror::Object entries in the image.
RegionData<mirror::Object> object_region_data(os_,
remote_contents,
@@ -1815,8 +1744,6 @@
// A File for reading /proc/<zygote_diff_pid_>/pagemap.
File zygote_pagemap_file_;
- // A File for reading /proc/self/pagemap.
- File clean_pagemap_file_;
// A File for reading /proc/kpageflags.
File kpageflags_file_;
// A File for reading /proc/kpagecount.
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index f937523..84b797b 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -39,7 +39,6 @@
"base/file_utils.cc",
"base/flags.cc",
"base/hex_dump.cc",
- "base/hiddenapi_flags.cc",
"base/logging.cc",
"base/malloc_arena_pool.cc",
"base/membarrier.cc",
@@ -66,10 +65,15 @@
"base/globals_unix.cc",
"base/mem_map_unix.cc",
],
+ static: {
+ cflags: ["-DART_STATIC_LIBARTBASE"],
+ },
static_libs: [
+ "libcap",
// ZipArchive support, the order matters here to get all symbols.
"libziparchive",
],
+ whole_static_libs: ["libtinyxml2"],
shared_libs: [
"libz",
"liblog",
@@ -79,6 +83,9 @@
"libbase",
],
export_shared_lib_headers: ["libbase"], // ART's macros.h depends on libbase's macros.h.
+ export_static_lib_headers: [
+ "libcap",
+ ],
},
not_windows: {
srcs: [
@@ -88,6 +95,7 @@
static: {
cflags: ["-DART_STATIC_LIBARTBASE"],
},
+ whole_static_libs: ["libtinyxml2"],
shared_libs: [
"libziparchive",
"libz",
@@ -99,6 +107,14 @@
],
export_shared_lib_headers: ["libbase"], // ART's macros.h depends on libbase's macros.h.
},
+ linux_glibc: {
+ static_libs: [
+ "libcap",
+ ],
+ export_static_lib_headers: [
+ "libcap",
+ ],
+ },
windows: {
srcs: [
"base/mem_map_windows.cc",
@@ -112,6 +128,7 @@
// For common macros.
"libbase",
],
+ whole_static_libs: ["libtinyxml2"],
export_static_lib_headers: ["libbase"], // ART's macros.h depends on libbase's macros.h.
cflags: ["-Wno-thread-safety"],
@@ -134,6 +151,18 @@
"libz",
"libziparchive",
],
+ target: {
+ android: {
+ whole_static_libs: [
+ "libcap",
+ ],
+ },
+ linux_glibc: {
+ whole_static_libs: [
+ "libcap",
+ ],
+ },
+ },
}
cc_defaults {
@@ -181,13 +210,11 @@
"com.android.art.debug",
],
- shared_libs: [
- "libbase",
- "libziparchive",
- ],
- export_shared_lib_headers: ["libbase"],
target: {
windows: {
+ // Control the enabled property here rather than in
+ // libartbase_defaults, to ensure it overrides properties inherited
+ // from other defaults.
enabled: true,
shared: {
enabled: false,
@@ -206,13 +233,13 @@
apex_available: [
"com.android.art.debug",
],
- shared_libs: [
- "libbase",
- "libziparchive",
- ],
- export_shared_lib_headers: ["libbase"],
+
target: {
windows: {
+ // Control the enabled property here rather than in
+ // libartbase_defaults, to ensure it overrides properties inherited
+ // from other defaults, in particular any inherited via
+ // art_debug_defaults.
enabled: true,
shared: {
enabled: false,
@@ -228,7 +255,7 @@
],
shared_libs: [
"libbase",
- "libbacktrace",
+ "libunwindstack",
],
header_libs: [
"libnativehelper_header_only",
@@ -236,11 +263,13 @@
static: {
whole_static_libs: [
"libc++fs",
+ "libcap",
],
},
shared: {
static_libs: [
"libc++fs",
+ "libcap",
],
},
}
@@ -391,6 +420,8 @@
shared_libs: ["libbase"],
export_shared_lib_headers: ["libbase"],
+ whole_static_libs: ["libtinyxml2"],
+
apex_available: [
"com.android.art",
"com.android.art.debug",
diff --git a/libartbase/arch/instruction_set.cc b/libartbase/arch/instruction_set.cc
index 9ec66fe..811e723 100644
--- a/libartbase/arch/instruction_set.cc
+++ b/libartbase/arch/instruction_set.cc
@@ -17,6 +17,8 @@
#include "instruction_set.h"
#include "android-base/logging.h"
+#include "android-base/properties.h"
+#include "android-base/stringprintf.h"
#include "base/bit_utils.h"
#include "base/globals.h"
@@ -27,6 +29,7 @@
case InstructionSet::kArm:
case InstructionSet::kThumb2:
case InstructionSet::kArm64:
+ case InstructionSet::kRiscv64:
case InstructionSet::kX86:
case InstructionSet::kX86_64:
case InstructionSet::kNone:
@@ -44,6 +47,8 @@
return "arm";
case InstructionSet::kArm64:
return "arm64";
+ case InstructionSet::kRiscv64:
+ return "riscv64";
case InstructionSet::kX86:
return "x86";
case InstructionSet::kX86_64:
@@ -62,6 +67,8 @@
return InstructionSet::kArm;
} else if (strcmp("arm64", isa_str) == 0) {
return InstructionSet::kArm64;
+ } else if (strcmp("riscv64", isa_str) == 0) {
+ return InstructionSet::kRiscv64;
} else if (strcmp("x86", isa_str) == 0) {
return InstructionSet::kX86;
} else if (strcmp("x86_64", isa_str) == 0) {
@@ -71,24 +78,45 @@
return InstructionSet::kNone;
}
-size_t GetInstructionSetAlignment(InstructionSet isa) {
- switch (isa) {
- case InstructionSet::kArm:
- // Fall-through.
- case InstructionSet::kThumb2:
- return kArmAlignment;
- case InstructionSet::kArm64:
- return kArm64Alignment;
- case InstructionSet::kX86:
- // Fall-through.
- case InstructionSet::kX86_64:
- return kX86Alignment;
- case InstructionSet::kNone:
- LOG(FATAL) << "ISA kNone does not have alignment.";
- UNREACHABLE();
+std::vector<InstructionSet> GetSupportedInstructionSets(std::string* error_msg) {
+ std::string zygote_kinds = android::base::GetProperty("ro.zygote", {});
+ if (zygote_kinds.empty()) {
+ *error_msg = "Unable to get Zygote kinds";
+ return {};
}
- LOG(FATAL) << "Unknown ISA " << isa;
- UNREACHABLE();
+
+ switch (kRuntimeISA) {
+ case InstructionSet::kArm:
+ case InstructionSet::kArm64:
+ if (zygote_kinds == "zygote64_32" || zygote_kinds == "zygote32_64") {
+ return {InstructionSet::kArm64, InstructionSet::kArm};
+ } else if (zygote_kinds == "zygote64") {
+ return {InstructionSet::kArm64};
+ } else if (zygote_kinds == "zygote32") {
+ return {InstructionSet::kArm};
+ } else {
+ *error_msg = android::base::StringPrintf("Unknown Zygote kinds '%s'", zygote_kinds.c_str());
+ return {};
+ }
+ case InstructionSet::kRiscv64:
+ return {InstructionSet::kRiscv64};
+ case InstructionSet::kX86:
+ case InstructionSet::kX86_64:
+ if (zygote_kinds == "zygote64_32" || zygote_kinds == "zygote32_64") {
+ return {InstructionSet::kX86_64, InstructionSet::kX86};
+ } else if (zygote_kinds == "zygote64") {
+ return {InstructionSet::kX86_64};
+ } else if (zygote_kinds == "zygote32") {
+ return {InstructionSet::kX86};
+ } else {
+ *error_msg = android::base::StringPrintf("Unknown Zygote kinds '%s'", zygote_kinds.c_str());
+ return {};
+ }
+ default:
+ *error_msg = android::base::StringPrintf("Unknown runtime ISA '%s'",
+ GetInstructionSetString(kRuntimeISA));
+ return {};
+ }
}
namespace instruction_set_details {
diff --git a/libartbase/arch/instruction_set.h b/libartbase/arch/instruction_set.h
index faf881d..8d59f1b 100644
--- a/libartbase/arch/instruction_set.h
+++ b/libartbase/arch/instruction_set.h
@@ -19,6 +19,7 @@
#include <iosfwd>
#include <string>
+#include <vector>
#include "base/enums.h"
#include "base/macros.h"
@@ -30,6 +31,7 @@
kArm,
kArm64,
kThumb2,
+ kRiscv64,
kX86,
kX86_64,
kLast = kX86_64
@@ -40,6 +42,8 @@
static constexpr InstructionSet kRuntimeISA = InstructionSet::kArm;
#elif defined(__aarch64__)
static constexpr InstructionSet kRuntimeISA = InstructionSet::kArm64;
+#elif defined (__riscv)
+static constexpr InstructionSet kRuntimeISA = InstructionSet::kRiscv64;
#elif defined(__i386__)
static constexpr InstructionSet kRuntimeISA = InstructionSet::kX86;
#elif defined(__x86_64__)
@@ -51,25 +55,29 @@
// Architecture-specific pointer sizes
static constexpr PointerSize kArmPointerSize = PointerSize::k32;
static constexpr PointerSize kArm64PointerSize = PointerSize::k64;
+static constexpr PointerSize kRiscv64PointerSize = PointerSize::k64;
static constexpr PointerSize kX86PointerSize = PointerSize::k32;
static constexpr PointerSize kX86_64PointerSize = PointerSize::k64;
-// ARM instruction alignment. ARM processors require code to be 4-byte aligned,
-// but ARM ELF requires 8..
-static constexpr size_t kArmAlignment = 8;
-
-// ARM64 instruction alignment. This is the recommended alignment for maximum performance.
-static constexpr size_t kArm64Alignment = 16;
-
// ARM64 default SVE vector length.
static constexpr size_t kArm64DefaultSVEVectorLength = 256;
-// X86 instruction alignment. This is the recommended alignment for maximum performance.
-static constexpr size_t kX86Alignment = 16;
+// Code alignment (used for the first instruction of a subroutine, such as an entrypoint).
+// This is the recommended alignment for maximum performance.
+// ARM processors require code to be 4-byte aligned, but ARM ELF requires 8.
+static constexpr size_t kArmCodeAlignment = 8;
+static constexpr size_t kArm64CodeAlignment = 16;
+static constexpr size_t kRiscv64CodeAlignment = 16;
+static constexpr size_t kX86CodeAlignment = 16;
-// Different than code alignment since code alignment is only first instruction of method.
+// Instruction alignment (every instruction must be aligned at this boundary). This differs from
+// code alignment, which applies only to the first instruction of a subroutine.
+// Android requires the RISC-V compressed instruction extension, and that allows
+// *all* instructions (not just compressed ones) to be 2-byte aligned rather
+// than the usual 4-byte alignment requirement.
static constexpr size_t kThumb2InstructionAlignment = 2;
static constexpr size_t kArm64InstructionAlignment = 4;
+static constexpr size_t kRiscv64InstructionAlignment = 2;
static constexpr size_t kX86InstructionAlignment = 1;
static constexpr size_t kX86_64InstructionAlignment = 1;
@@ -89,6 +97,8 @@
return kArmPointerSize;
case InstructionSet::kArm64:
return kArm64PointerSize;
+ case InstructionSet::kRiscv64:
+ return kRiscv64PointerSize;
case InstructionSet::kX86:
return kX86PointerSize;
case InstructionSet::kX86_64:
@@ -100,30 +110,12 @@
InstructionSetAbort(isa);
}
-constexpr size_t GetInstructionSetInstructionAlignment(InstructionSet isa) {
- switch (isa) {
- case InstructionSet::kArm:
- // Fall-through.
- case InstructionSet::kThumb2:
- return kThumb2InstructionAlignment;
- case InstructionSet::kArm64:
- return kArm64InstructionAlignment;
- case InstructionSet::kX86:
- return kX86InstructionAlignment;
- case InstructionSet::kX86_64:
- return kX86_64InstructionAlignment;
-
- case InstructionSet::kNone:
- break;
- }
- InstructionSetAbort(isa);
-}
-
constexpr bool IsValidInstructionSet(InstructionSet isa) {
switch (isa) {
case InstructionSet::kArm:
case InstructionSet::kThumb2:
case InstructionSet::kArm64:
+ case InstructionSet::kRiscv64:
case InstructionSet::kX86:
case InstructionSet::kX86_64:
return true;
@@ -134,7 +126,68 @@
return false;
}
-size_t GetInstructionSetAlignment(InstructionSet isa);
+constexpr size_t GetInstructionSetInstructionAlignment(InstructionSet isa) {
+ switch (isa) {
+ case InstructionSet::kArm:
+ // Fall-through.
+ case InstructionSet::kThumb2:
+ return kThumb2InstructionAlignment;
+ case InstructionSet::kArm64:
+ return kArm64InstructionAlignment;
+ case InstructionSet::kRiscv64:
+ return kRiscv64InstructionAlignment;
+ case InstructionSet::kX86:
+ return kX86InstructionAlignment;
+ case InstructionSet::kX86_64:
+ return kX86_64InstructionAlignment;
+
+ case InstructionSet::kNone:
+ break;
+ }
+ InstructionSetAbort(isa);
+}
+
+constexpr size_t GetInstructionSetCodeAlignment(InstructionSet isa) {
+ switch (isa) {
+ case InstructionSet::kArm:
+ // Fall-through.
+ case InstructionSet::kThumb2:
+ return kArmCodeAlignment;
+ case InstructionSet::kArm64:
+ return kArm64CodeAlignment;
+ case InstructionSet::kRiscv64:
+ return kRiscv64CodeAlignment;
+ case InstructionSet::kX86:
+ // Fall-through.
+ case InstructionSet::kX86_64:
+ return kX86CodeAlignment;
+
+ case InstructionSet::kNone:
+ break;
+ }
+ InstructionSetAbort(isa);
+}
+
+// Returns the difference between the code address and a usable PC.
+// Mainly to cope with `kThumb2` where the lower bit must be set.
+constexpr size_t GetInstructionSetEntryPointAdjustment(InstructionSet isa) {
+ switch (isa) {
+ case InstructionSet::kArm:
+ case InstructionSet::kArm64:
+ case InstructionSet::kRiscv64:
+ case InstructionSet::kX86:
+ case InstructionSet::kX86_64:
+ return 0;
+ case InstructionSet::kThumb2: {
+ // +1 to set the low-order bit so a BLX will switch to Thumb mode
+ return 1;
+ }
+
+ case InstructionSet::kNone:
+ break;
+ }
+ InstructionSetAbort(isa);
+}
constexpr bool Is64BitInstructionSet(InstructionSet isa) {
switch (isa) {
@@ -144,6 +197,7 @@
return false;
case InstructionSet::kArm64:
+ case InstructionSet::kRiscv64:
case InstructionSet::kX86_64:
return true;
@@ -165,6 +219,8 @@
return 4;
case InstructionSet::kArm64:
return 8;
+ case InstructionSet::kRiscv64:
+ return 8;
case InstructionSet::kX86:
return 4;
case InstructionSet::kX86_64:
@@ -184,6 +240,8 @@
return 4;
case InstructionSet::kArm64:
return 8;
+ case InstructionSet::kRiscv64:
+ return 8;
case InstructionSet::kX86:
return 8;
case InstructionSet::kX86_64:
@@ -195,17 +253,22 @@
InstructionSetAbort(isa);
}
+// Returns the instruction sets supported by the device, or an empty list on failure.
+std::vector<InstructionSet> GetSupportedInstructionSets(std::string* error_msg);
+
namespace instruction_set_details {
#if !defined(ART_STACK_OVERFLOW_GAP_arm) || !defined(ART_STACK_OVERFLOW_GAP_arm64) || \
+ !defined(ART_STACK_OVERFLOW_GAP_riscv64) || \
!defined(ART_STACK_OVERFLOW_GAP_x86) || !defined(ART_STACK_OVERFLOW_GAP_x86_64)
#error "Missing defines for stack overflow gap"
#endif
-static constexpr size_t kArmStackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_arm;
-static constexpr size_t kArm64StackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_arm64;
-static constexpr size_t kX86StackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_x86;
-static constexpr size_t kX86_64StackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_x86_64;
+static constexpr size_t kArmStackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_arm;
+static constexpr size_t kArm64StackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_arm64;
+static constexpr size_t kRiscv64StackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_riscv64;
+static constexpr size_t kX86StackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_x86;
+static constexpr size_t kX86_64StackOverflowReservedBytes = ART_STACK_OVERFLOW_GAP_x86_64;
NO_RETURN void GetStackOverflowReservedBytesFailure(const char* error_msg);
@@ -221,6 +284,9 @@
case InstructionSet::kArm64:
return instruction_set_details::kArm64StackOverflowReservedBytes;
+ case InstructionSet::kRiscv64:
+ return instruction_set_details::kRiscv64StackOverflowReservedBytes;
+
case InstructionSet::kX86:
return instruction_set_details::kX86StackOverflowReservedBytes;
diff --git a/libartbase/base/arena_allocator.cc b/libartbase/base/arena_allocator.cc
index 76f2883..d38a64e 100644
--- a/libartbase/base/arena_allocator.cc
+++ b/libartbase/base/arena_allocator.cc
@@ -28,8 +28,6 @@
namespace art {
-constexpr size_t kMemoryToolRedZoneBytes = 8;
-
template <bool kCount>
const char* const ArenaAllocatorStatsImpl<kCount>::kAllocNames[] = {
// Every name should have the same width and end with a space. Abbreviate if necessary:
@@ -75,6 +73,7 @@
"LSE ",
"CFRE ",
"LICM ",
+ "WBE ",
"LoopOpt ",
"SsaLiveness ",
"SsaPhiElim ",
@@ -187,9 +186,6 @@
MEMORY_TOOL_MAKE_NOACCESS(ptr, size);
}
-Arena::Arena() : bytes_allocated_(0), memory_(nullptr), size_(0), next_(nullptr) {
-}
-
size_t ArenaAllocator::BytesAllocated() const {
return ArenaAllocatorStats::BytesAllocated();
}
@@ -247,7 +243,7 @@
size_t rounded_bytes = bytes + kMemoryToolRedZoneBytes;
DCHECK_ALIGNED(rounded_bytes, 8); // `bytes` is 16-byte aligned, red zone is 8-byte aligned.
uintptr_t padding =
- ((reinterpret_cast<uintptr_t>(ptr_) + 15u) & 15u) - reinterpret_cast<uintptr_t>(ptr_);
+ RoundUp(reinterpret_cast<uintptr_t>(ptr_), 16) - reinterpret_cast<uintptr_t>(ptr_);
ArenaAllocatorStats::RecordAlloc(rounded_bytes, kind);
uint8_t* ret;
if (UNLIKELY(padding + rounded_bytes > static_cast<size_t>(end_ - ptr_))) {
@@ -270,6 +266,13 @@
pool_->FreeArenaChain(arena_head_);
}
+void ArenaAllocator::ResetCurrentArena() {
+ UpdateBytesAllocated();
+ begin_ = nullptr;
+ ptr_ = nullptr;
+ end_ = nullptr;
+}
+
uint8_t* ArenaAllocator::AllocFromNewArena(size_t bytes) {
Arena* new_arena = pool_->AllocArena(std::max(arena_allocator::kArenaDefaultSize, bytes));
DCHECK(new_arena != nullptr);
diff --git a/libartbase/base/arena_allocator.h b/libartbase/base/arena_allocator.h
index 12a44d5..c4f713a 100644
--- a/libartbase/base/arena_allocator.h
+++ b/libartbase/base/arena_allocator.h
@@ -84,6 +84,7 @@
kArenaAllocLSE,
kArenaAllocCFRE,
kArenaAllocLICM,
+ kArenaAllocWBE,
kArenaAllocLoopOptimization,
kArenaAllocSsaLiveness,
kArenaAllocSsaPhiElimination,
@@ -152,7 +153,7 @@
class ArenaAllocatorMemoryTool {
public:
- bool IsRunningOnMemoryTool() { return kMemoryToolIsAvailable; }
+ static constexpr bool IsRunningOnMemoryTool() { return kMemoryToolIsAvailable; }
void MakeDefined(void* ptr, size_t size) {
if (UNLIKELY(IsRunningOnMemoryTool())) {
@@ -178,19 +179,18 @@
class Arena {
public:
- Arena();
+ Arena() : bytes_allocated_(0), memory_(nullptr), size_(0), next_(nullptr) {}
+
virtual ~Arena() { }
// Reset is for pre-use and uses memset for performance.
void Reset();
// Release is used inbetween uses and uses madvise for memory usage.
virtual void Release() { }
- uint8_t* Begin() {
+ uint8_t* Begin() const {
return memory_;
}
- uint8_t* End() {
- return memory_ + size_;
- }
+ uint8_t* End() const { return memory_ + size_; }
size_t Size() const {
return size_;
@@ -205,9 +205,9 @@
}
// Return true if ptr is contained in the arena.
- bool Contains(const void* ptr) const {
- return memory_ <= ptr && ptr < memory_ + bytes_allocated_;
- }
+ bool Contains(const void* ptr) const { return memory_ <= ptr && ptr < memory_ + size_; }
+
+ Arena* Next() const { return next_; }
protected:
size_t bytes_allocated_;
@@ -289,7 +289,7 @@
return AllocWithMemoryToolAlign16(bytes, kind);
}
uintptr_t padding =
- ((reinterpret_cast<uintptr_t>(ptr_) + 15u) & 15u) - reinterpret_cast<uintptr_t>(ptr_);
+ RoundUp(reinterpret_cast<uintptr_t>(ptr_), 16) - reinterpret_cast<uintptr_t>(ptr_);
ArenaAllocatorStats::RecordAlloc(bytes, kind);
if (UNLIKELY(padding + bytes > static_cast<size_t>(end_ - ptr_))) {
static_assert(kArenaAlignment >= 16, "Expecting sufficient alignment for new Arena.");
@@ -355,6 +355,22 @@
return pool_;
}
+ Arena* GetHeadArena() const {
+ return arena_head_;
+ }
+
+ uint8_t* CurrentPtr() const {
+ return ptr_;
+ }
+
+ size_t CurrentArenaUnusedBytes() const {
+ DCHECK_LE(ptr_, end_);
+ return end_ - ptr_;
+ }
+ // Resets the current arena in use, which will force us to get a new arena
+ // on next allocation.
+ void ResetCurrentArena();
+
bool Contains(const void* ptr) const;
// The alignment guaranteed for individual allocations.
@@ -363,6 +379,9 @@
// The alignment required for the whole Arena rather than individual allocations.
static constexpr size_t kArenaAlignment = 16u;
+ // Extra bytes required by the memory tool.
+ static constexpr size_t kMemoryToolRedZoneBytes = 8u;
+
private:
void* AllocWithMemoryTool(size_t bytes, ArenaAllocKind kind);
void* AllocWithMemoryToolAlign16(size_t bytes, ArenaAllocKind kind);
diff --git a/libartbase/base/array_slice.h b/libartbase/base/array_slice.h
index a58ff44..067d9f2 100644
--- a/libartbase/base/array_slice.h
+++ b/libartbase/base/array_slice.h
@@ -65,9 +65,9 @@
lpa != nullptr ? lpa->size() : 0,
element_size) {}
ArraySlice(const ArraySlice<T>&) = default;
- ArraySlice(ArraySlice<T>&&) = default;
+ ArraySlice(ArraySlice<T>&&) noexcept = default;
ArraySlice<T>& operator=(const ArraySlice<T>&) = default;
- ArraySlice<T>& operator=(ArraySlice<T>&&) = default;
+ ArraySlice<T>& operator=(ArraySlice<T>&&) noexcept = default;
// Iterators.
iterator begin() { return iterator(&AtUnchecked(0), element_size_); }
diff --git a/libartbase/base/bit_memory_region.h b/libartbase/base/bit_memory_region.h
index c5224a5..baac2f5 100644
--- a/libartbase/base/bit_memory_region.h
+++ b/libartbase/base/bit_memory_region.h
@@ -324,8 +324,37 @@
size_t bit_size_ = 0;
};
-constexpr uint32_t kVarintBits = 4; // Minimum number of bits used for varint.
-constexpr uint32_t kVarintMax = 11; // Maximum value which is stored "inline".
+// Minimum number of bits used for varint. A varint represents either a value stored "inline" or
+// the number of bytes that are required to encode the value.
+constexpr uint32_t kVarintBits = 4;
+// Maximum value which is stored "inline". We use the rest of the values to encode the number of
+// bytes required to encode the value when the value is greater than kVarintMax.
+// We encode any value less than or equal to 11 inline. We use 12, 13, 14 and 15
+// to represent that the value is encoded in 1, 2, 3 and 4 bytes respectively.
+//
+// For example if we want to encode 1, 15, 16, 7, 11, 256:
+//
+// Low numbers (1, 7, 11) are encoded inline. 15 and 12 are set with 12 to show
+// we need to load one byte for each to have their real values (15 and 12), and
+// 256 is set with 13 to show we need to load two bytes. This is done to
+// compress the values in the bit array and keep the size down. Where the actual value
+// is read from depends on the use case.
+//
+// Values greater than kVarintMax could be encoded as a separate list referred
+// to as InterleavedVarints (see ReadInterleavedVarints / WriteInterleavedVarints).
+// This is used when there are fixed number of fields like CodeInfo headers.
+// In our example the interleaved encoding looks like below:
+//
+// Meaning: 1--- 15-- 12-- 7--- 11-- 256- 15------- 12------- 256----------------
+// Bits: 0001 1100 1100 0111 1011 1101 0000 1111 0000 1100 0000 0001 0000 0000
+//
+// In other cases the value is recorded just following the size encoding. This is
+// referred as consecutive encoding (See ReadVarint / WriteVarint). In our
+// example the consecutively encoded varints looks like below:
+//
+// Meaning: 1--- 15-- 15------- 12-- 12------- 7--- 11-- 256- 256----------------
+// Bits: 0001 1100 0000 1100 1100 0000 1100 0111 1011 1101 0000 0001 0000 0000
+constexpr uint32_t kVarintMax = 11;
class BitMemoryReader {
public:
diff --git a/libartbase/base/casts.h b/libartbase/base/casts.h
index c88f589..70a7035 100644
--- a/libartbase/base/casts.h
+++ b/libartbase/base/casts.h
@@ -57,17 +57,7 @@
// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because
// how do you know the pointer is really of type SubclassOfFoo? It
// could be a bare Foo, or of type DifferentSubclassOfFoo. Thus,
-// when you downcast, you should use this macro. In debug mode, we
-// use dynamic_cast<> to double-check the downcast is legal (we die
-// if it's not). In normal mode, we do the efficient static_cast<>
-// instead. Thus, it's important to test in debug mode to make sure
-// the cast is legal!
-// This is the only place in the code we should use dynamic_cast<>.
-// In particular, you SHOULDN'T be using dynamic_cast<> in order to
-// do RTTI (eg code like this:
-// if (dynamic_cast<Subclass1>(foo)) HandleASubclass1Object(foo);
-// if (dynamic_cast<Subclass2>(foo)) HandleASubclass2Object(foo);
-// You should design the code some other way not to need this.
+// when you downcast, you should use this macro.
template<typename To, typename From> // use like this: down_cast<T*>(foo);
inline To down_cast(From* f) { // so we only accept pointers
diff --git a/libartbase/base/common_art_test.cc b/libartbase/base/common_art_test.cc
index c74f3de..1aba547 100644
--- a/libartbase/base/common_art_test.cc
+++ b/libartbase/base/common_art_test.cc
@@ -22,14 +22,17 @@
#include <ftw.h>
#include <libgen.h>
#include <stdlib.h>
+#include <sys/capability.h>
#include <unistd.h>
#include <cstdio>
#include <filesystem>
+#include <functional>
#include "android-base/file.h"
#include "android-base/logging.h"
#include "android-base/process.h"
+#include "android-base/scopeguard.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "android-base/unique_fd.h"
@@ -41,6 +44,7 @@
#include "base/mutex.h"
#include "base/os.h"
#include "base/runtime_debug.h"
+#include "base/scoped_cap.h"
#include "base/stl_util.h"
#include "base/string_view_cpp20.h"
#include "base/testing.h"
@@ -69,15 +73,7 @@
ScratchDir::~ScratchDir() {
if (!keep_files_) {
- // Recursively delete the directory and all its content.
- nftw(path_.c_str(), [](const char* name, const struct stat*, int type, struct FTW *) {
- if (type == FTW_F) {
- unlink(name);
- } else if (type == FTW_DP) {
- rmdir(name);
- }
- return 0;
- }, 256 /* max open file descriptors */, FTW_DEPTH);
+ std::filesystem::remove_all(path_);
}
}
@@ -145,6 +141,29 @@
CHECK_EQ(0, unlink_result);
}
+// Temporarily drops all root capabilities when the test is run as root. This is a noop otherwise.
+android::base::ScopeGuard<std::function<void()>> ScopedUnroot() {
+ ScopedCap old_cap(cap_get_proc());
+ CHECK_NE(old_cap.Get(), nullptr);
+ ScopedCap new_cap(cap_dup(old_cap.Get()));
+ CHECK_NE(new_cap.Get(), nullptr);
+ CHECK_EQ(cap_clear_flag(new_cap.Get(), CAP_EFFECTIVE), 0);
+ CHECK_EQ(cap_set_proc(new_cap.Get()), 0);
+ // `old_cap` is actually not shared with anyone else, but we have to wrap it with a `shared_ptr`
+ // because `std::function` requires captures to be copyable.
+ return android::base::make_scope_guard(
+ [old_cap = std::make_shared<ScopedCap>(std::move(old_cap))]() {
+ CHECK_EQ(cap_set_proc(old_cap->Get()), 0);
+ });
+}
+
+// Temporarily drops write permission on a file/directory.
+android::base::ScopeGuard<std::function<void()>> ScopedInaccessible(const std::string& path) {
+ std::filesystem::perms old_perms = std::filesystem::status(path).permissions();
+ std::filesystem::permissions(path, std::filesystem::perms::none);
+ return android::base::make_scope_guard([=]() { std::filesystem::permissions(path, old_perms); });
+}
+
std::string CommonArtTestImpl::GetAndroidBuildTop() {
CHECK(IsHost());
std::string android_build_top;
@@ -313,7 +332,7 @@
android_system_ext_.append("/system_ext");
int mkdir_result = mkdir(android_system_ext_.c_str(), 0700);
ASSERT_EQ(mkdir_result, 0);
- setenv("ANDROID_SYSTEM_EXT", android_system_ext_.c_str(), 1);
+ setenv("SYSTEM_EXT_ROOT", android_system_ext_.c_str(), 1);
std::string system_ext_framework = android_system_ext_ + "/framework";
mkdir_result = mkdir(system_ext_framework.c_str(), 0700);
diff --git a/libartbase/base/common_art_test.h b/libartbase/base/common_art_test.h
index bd06043..f4935cc 100644
--- a/libartbase/base/common_art_test.h
+++ b/libartbase/base/common_art_test.h
@@ -25,6 +25,7 @@
#include <vector>
#include "android-base/logging.h"
+#include "android-base/scopeguard.h"
#include "base/file_utils.h"
#include "base/globals.h"
#include "base/memory_tool.h"
@@ -122,6 +123,12 @@
DISALLOW_COPY_AND_ASSIGN(ScopedUnsetEnvironmentVariable);
};
+// Temporarily drops all root capabilities when the test is run as root. This is a noop otherwise.
+android::base::ScopeGuard<std::function<void()>> ScopedUnroot();
+
+// Temporarily drops all permissions on a file/directory.
+android::base::ScopeGuard<std::function<void()>> ScopedInaccessible(const std::string& path);
+
class CommonArtTestImpl {
public:
CommonArtTestImpl() = default;
diff --git a/libartbase/base/data_hash.h b/libartbase/base/data_hash.h
index 3399899..ccb8736 100644
--- a/libartbase/base/data_hash.h
+++ b/libartbase/base/data_hash.h
@@ -44,7 +44,7 @@
uint32_t hash = Murmur3Start();
const size_t nblocks = length_in_bytes / 4;
- typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t;
+ using unaligned_uint32_t __attribute__((__aligned__(1))) = uint32_t;
const unaligned_uint32_t* blocks = reinterpret_cast<const unaligned_uint32_t*>(data);
for (size_t i = 0; i != nblocks; ++i) {
hash = Murmur3Update(hash, blocks[i]);
diff --git a/libartbase/base/file_utils.cc b/libartbase/base/file_utils.cc
index 0b670e7..2396289 100644
--- a/libartbase/base/file_utils.cc
+++ b/libartbase/base/file_utils.cc
@@ -69,7 +69,7 @@
static constexpr const char* kClassesDex = "classes.dex";
static constexpr const char* kAndroidRootEnvVar = "ANDROID_ROOT";
static constexpr const char* kAndroidRootDefaultPath = "/system";
-static constexpr const char* kAndroidSystemExtRootEnvVar = "ANDROID_SYSTEM_EXT";
+static constexpr const char* kAndroidSystemExtRootEnvVar = "SYSTEM_EXT_ROOT";
static constexpr const char* kAndroidSystemExtRootDefaultPath = "/system_ext";
static constexpr const char* kAndroidDataEnvVar = "ANDROID_DATA";
static constexpr const char* kAndroidDataDefaultPath = "/data";
@@ -106,56 +106,6 @@
return "";
}
-std::string GetAndroidRootSafe(std::string* error_msg) {
-#ifdef _WIN32
- UNUSED(kAndroidRootEnvVar, kAndroidRootDefaultPath, GetRootContainingLibartbase);
- *error_msg = "GetAndroidRootSafe unsupported for Windows.";
- return "";
-#else
- // Prefer ANDROID_ROOT if it's set.
- const char* android_root_from_env = getenv(kAndroidRootEnvVar);
- if (android_root_from_env != nullptr) {
- if (!OS::DirectoryExists(android_root_from_env)) {
- *error_msg =
- StringPrintf("Failed to find %s directory %s", kAndroidRootEnvVar, android_root_from_env);
- return "";
- }
- return android_root_from_env;
- }
-
- // On host, libartbase is currently installed in "$ANDROID_ROOT/lib"
- // (e.g. something like "$ANDROID_BUILD_TOP/out/host/linux-x86/lib". Use this
- // information to infer the location of the Android Root (on host only).
- //
- // Note that this could change in the future, if we decided to install ART
- // artifacts in a different location, e.g. within an "ART APEX" directory.
- if (!kIsTargetBuild) {
- std::string root_containing_libartbase = GetRootContainingLibartbase();
- if (!root_containing_libartbase.empty()) {
- return root_containing_libartbase;
- }
- }
-
- // Try the default path.
- if (!OS::DirectoryExists(kAndroidRootDefaultPath)) {
- *error_msg =
- StringPrintf("Failed to find default Android Root directory %s", kAndroidRootDefaultPath);
- return "";
- }
- return kAndroidRootDefaultPath;
-#endif
-}
-
-std::string GetAndroidRoot() {
- std::string error_msg;
- std::string ret = GetAndroidRootSafe(&error_msg);
- if (ret.empty()) {
- LOG(FATAL) << error_msg;
- UNREACHABLE();
- }
- return ret;
-}
-
static const char* GetAndroidDirSafe(const char* env_var,
const char* default_dir,
bool must_exist,
@@ -189,6 +139,62 @@
}
}
+std::string GetAndroidRootSafe(std::string* error_msg) {
+#ifdef _WIN32
+ UNUSED(kAndroidRootEnvVar, kAndroidRootDefaultPath, GetRootContainingLibartbase);
+ *error_msg = "GetAndroidRootSafe unsupported for Windows.";
+ return "";
+#else
+ std::string local_error_msg;
+ const char* dir = GetAndroidDirSafe(kAndroidRootEnvVar, kAndroidRootDefaultPath,
+ /*must_exist=*/ true, &local_error_msg);
+ if (dir == nullptr) {
+ // On host, libartbase is currently installed in "$ANDROID_ROOT/lib"
+ // (e.g. something like "$ANDROID_BUILD_TOP/out/host/linux-x86/lib". Use this
+ // information to infer the location of the Android Root (on host only).
+ //
+ // Note that this could change in the future, if we decided to install ART
+ // artifacts in a different location, e.g. within an "ART APEX" directory.
+ if (!kIsTargetBuild) {
+ std::string root_containing_libartbase = GetRootContainingLibartbase();
+ if (!root_containing_libartbase.empty()) {
+ return root_containing_libartbase;
+ }
+ }
+ *error_msg = std::move(local_error_msg);
+ return "";
+ }
+
+ return dir;
+#endif
+}
+
+std::string GetAndroidRoot() {
+ std::string error_msg;
+ std::string ret = GetAndroidRootSafe(&error_msg);
+ CHECK(!ret.empty()) << error_msg;
+ return ret;
+}
+
+std::string GetSystemExtRootSafe(std::string* error_msg) {
+#ifdef _WIN32
+ UNUSED(kAndroidSystemExtRootEnvVar, kAndroidSystemExtRootDefaultPath);
+ *error_msg = "GetSystemExtRootSafe unsupported for Windows.";
+ return "";
+#else
+ const char* dir = GetAndroidDirSafe(kAndroidSystemExtRootEnvVar, kAndroidSystemExtRootDefaultPath,
+ /*must_exist=*/ true, error_msg);
+ return dir ? dir : "";
+#endif
+}
+
+std::string GetSystemExtRoot() {
+ std::string error_msg;
+ std::string ret = GetSystemExtRootSafe(&error_msg);
+ CHECK(!ret.empty()) << error_msg;
+ return ret;
+}
+
static std::string GetArtRootSafe(bool must_exist, /*out*/ std::string* error_msg) {
#ifdef _WIN32
UNUSED(kAndroidArtRootEnvVar, kAndroidArtApexDefaultPath, GetRootContainingLibartbase);
@@ -365,6 +371,12 @@
return GetDefaultBootImageLocation(android_root, /*deny_art_apex_data_files=*/false);
}
+std::string GetJitZygoteBootImageLocation() {
+ // Intentionally use a non-existing location so that the runtime will fail to find the boot image
+ // and JIT bootclasspath with the given profiles.
+ return "/nonx/boot.art!/apex/com.android.art/etc/boot-image.prof!/system/etc/boot-image.prof";
+}
+
static /*constinit*/ std::string_view dalvik_cache_sub_dir = "dalvik-cache";
void OverrideDalvikCacheSubDirectory(std::string sub_dir) {
@@ -679,8 +691,27 @@
#endif
}
+bool LocationIsOnSystemExt(const std::string& location) {
+#ifdef _WIN32
+ UNUSED(location);
+ LOG(FATAL) << "LocationIsOnSystemExt is unsupported on Windows.";
+ return false;
+#else
+ return IsLocationOn(location,
+ kAndroidSystemExtRootEnvVar,
+ kAndroidSystemExtRootDefaultPath) ||
+ // When the 'system_ext' partition is not present, builds will create
+ // '/system/system_ext' instead.
+ IsLocationOn(location,
+ kAndroidRootEnvVar,
+ kAndroidRootDefaultPath,
+ /* subdir= */ "system_ext/");
+#endif
+}
+
bool LocationIsTrusted(const std::string& location, bool trust_art_apex_data_files) {
- if (LocationIsOnSystem(location) || LocationIsOnArtModule(location)) {
+ if (LocationIsOnSystem(location) || LocationIsOnSystemExt(location)
+ || LocationIsOnArtModule(location)) {
return true;
}
return LocationIsOnArtApexData(location) & trust_art_apex_data_files;
@@ -704,7 +735,7 @@
#if defined(__linux__)
return fcntl(fd, F_DUPFD_CLOEXEC, 0);
#else
- return dup(fd);
+ return dup(fd); // NOLINT
#endif
}
diff --git a/libartbase/base/file_utils.h b/libartbase/base/file_utils.h
index c1c45bc..f539f5f 100644
--- a/libartbase/base/file_utils.h
+++ b/libartbase/base/file_utils.h
@@ -47,6 +47,11 @@
// Find $ANDROID_ROOT, /system, or return an empty string.
std::string GetAndroidRootSafe(/*out*/ std::string* error_msg);
+// Find $SYSTEM_EXT_ROOT, /system_ext, or abort.
+std::string GetSystemExtRoot();
+// Find $SYSTEM_EXT_ROOT, /system_ext, or return an empty string.
+std::string GetSystemExtRootSafe(/*out*/ std::string* error_msg);
+
// These methods return the ART Root, which is the location of the (activated)
// ART APEX module. On target, this is normally "/apex/com.android.art". On
// host, this is usually a subdirectory of the Android Root, e.g.
@@ -81,6 +86,9 @@
std::string GetDefaultBootImageLocation(const std::string& android_root,
bool deny_art_apex_data_files);
+// Returns the boot image location that forces the runtime to run in JIT Zygote mode.
+std::string GetJitZygoteBootImageLocation();
+
// Allows the name to be used for the dalvik cache directory (normally "dalvik-cache") to be
// overridden with a new value.
void OverrideDalvikCacheSubDirectory(std::string sub_dir);
@@ -160,6 +168,9 @@
// Return whether the location is on system (i.e. android root).
bool LocationIsOnSystem(const std::string& location);
+// Return whether the location is on system_ext
+bool LocationIsOnSystemExt(const std::string& location);
+
// Return whether the location is on system/framework (i.e. $ANDROID_ROOT/framework).
bool LocationIsOnSystemFramework(std::string_view location);
diff --git a/libartbase/base/flags.h b/libartbase/base/flags.h
index d1e1ca6..4c38fba 100644
--- a/libartbase/base/flags.h
+++ b/libartbase/base/flags.h
@@ -236,8 +236,8 @@
//
// Flag<int> WriteMetricsToLog{"my-feature-test.flag", 42, FlagType::kDeviceConfig};
//
-// This creates a boolean flag that can be read through gFlags.WriteMetricsToLog(). The default
-// value is false. Note that the default value can be left unspecified, in which the value of the
+// This creates an integer flag that can be read through gFlags.WriteMetricsToLog(). The default
+// value is 42. Note that the default value can be left unspecified, in which case the value of the
// type's default constructor will be used.
//
// The flag can be set through the following generated means:
@@ -304,6 +304,12 @@
// Note that the actual write is still controlled by
// MetricsReportingMods and MetricsReportingNumMods.
Flag<std::string> MetricsWriteToFile{"metrics.write-to-file", "", FlagType::kCmdlineOnly};
+
+ // The output format for metrics. This is only used
+ // when writing metrics to a file; metrics written
+ // to logcat will be in human-readable text format.
+ // Supported values are "text" and "xml".
+ Flag<std::string> MetricsFormat{"metrics.format", "text", FlagType::kCmdlineOnly};
};
// This is the actual instance of all the flags.
diff --git a/libartbase/base/globals.h b/libartbase/base/globals.h
index 8d37b8a..4103154 100644
--- a/libartbase/base/globals.h
+++ b/libartbase/base/globals.h
@@ -38,6 +38,17 @@
// compile-time constant so the compiler can generate better code.
static constexpr size_t kPageSize = 4096;
+// TODO: Kernels for arm and x86 in both, 32-bit and 64-bit modes use 512 entries per page-table
+// page. Find a way to confirm that in userspace.
+// Address range covered by 1 Page Middle Directory (PMD) entry in the page table
+static constexpr size_t kPMDSize = (kPageSize / sizeof(uint64_t)) * kPageSize;
+// Address range covered by 1 Page Upper Directory (PUD) entry in the page table
+static constexpr size_t kPUDSize = (kPageSize / sizeof(uint64_t)) * kPMDSize;
+// Returns the ideal alignment corresponding to page-table levels for the
+// given size.
+static constexpr size_t BestPageTableAlignment(size_t size) {
+ return size < kPUDSize ? kPMDSize : kPUDSize;
+}
// Clion, clang analyzer, etc can falsely believe that "if (kIsDebugBuild)" always
// returns the same value. By wrapping into a call to another constexpr function, we force it
// to realize that is not actually always evaluating to the same value.
@@ -107,6 +118,12 @@
static constexpr bool kHostStaticBuildEnabled = false;
#endif
+// System property for phenotype flag to test disabling compact dex and in
+// particular dexlayout.
+// TODO(b/256664509): Clean this up.
+static constexpr char kPhDisableCompactDex[] =
+ "persist.device_config.runtime_native_boot.disable_compact_dex";
+
} // namespace art
#endif // ART_LIBARTBASE_BASE_GLOBALS_H_
diff --git a/libartbase/base/hash_set.h b/libartbase/base/hash_set.h
index c4af1b6..3f3c8f2 100644
--- a/libartbase/base/hash_set.h
+++ b/libartbase/base/hash_set.h
@@ -139,6 +139,17 @@
}
};
+template <>
+class DefaultEmptyFn<std::string> {
+ public:
+ void MakeEmpty(std::string& item) const {
+ item = std::string();
+ }
+ bool IsEmpty(const std::string& item) const {
+ return item.empty();
+ }
+};
+
template <class T>
using DefaultHashFn = std::conditional_t<std::is_same_v<T, std::string>, DataHash, std::hash<T>>;
diff --git a/libartbase/base/hiddenapi_flags.cc b/libartbase/base/hiddenapi_flags.cc
deleted file mode 100644
index ea57cb7..0000000
--- a/libartbase/base/hiddenapi_flags.cc
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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 "hiddenapi_flags.h"
-
-namespace art {
-namespace hiddenapi {
-
-constexpr const char* ApiList::kValueNames[ApiList::kValueCount];
-constexpr const char* ApiList::kDomainApiNames[ApiList::kDomainApiCount];
-constexpr SdkVersion ApiList::kMaxSdkVersions[ApiList::kValueCount];
-
-} // namespace hiddenapi
-} // namespace art
diff --git a/libartbase/base/logging.h b/libartbase/base/logging.h
index 7a421a4..ef03894 100644
--- a/libartbase/base/logging.h
+++ b/libartbase/base/logging.h
@@ -56,7 +56,7 @@
bool verifier;
bool verifier_debug; // Only works in debug builds.
bool image;
- bool systrace_lock_logging; // Enabled with "-verbose:sys-locks".
+ bool systrace_lock_logging; // Enabled with "-verbose:systrace-locks".
bool agents;
bool dex; // Some dex access output etc.
bool plugin; // Used by some plugins.
diff --git a/libartbase/base/macros.h b/libartbase/base/macros.h
index eec73cb..13e87d7 100644
--- a/libartbase/base/macros.h
+++ b/libartbase/base/macros.h
@@ -75,6 +75,8 @@
#define FLATTEN __attribute__ ((flatten))
#endif
+#define NO_STACK_PROTECTOR __attribute__ ((no_stack_protector))
+
// clang doesn't like attributes on lambda functions. It would be nice to say:
// #define ALWAYS_INLINE_LAMBDA ALWAYS_INLINE
#define ALWAYS_INLINE_LAMBDA
diff --git a/libartbase/base/mem_map.cc b/libartbase/base/mem_map.cc
index aa07f1c..b3e2840 100644
--- a/libartbase/base/mem_map.cc
+++ b/libartbase/base/mem_map.cc
@@ -389,6 +389,32 @@
reuse);
}
+MemMap MemMap::MapAnonymousAligned(const char* name,
+ size_t byte_count,
+ int prot,
+ bool low_4gb,
+ size_t alignment,
+ /*out=*/std::string* error_msg) {
+ DCHECK(IsPowerOfTwo(alignment));
+ DCHECK_GT(alignment, kPageSize);
+ // Allocate extra 'alignment - kPageSize' bytes so that the mapping can be aligned.
+ MemMap ret = MapAnonymous(name,
+ /*addr=*/nullptr,
+ byte_count + alignment - kPageSize,
+ prot,
+ low_4gb,
+ /*reuse=*/false,
+ /*reservation=*/nullptr,
+ error_msg);
+ if (LIKELY(ret.IsValid())) {
+ ret.AlignBy(alignment, /*align_both_ends=*/false);
+ ret.SetSize(byte_count);
+ DCHECK_EQ(ret.Size(), byte_count);
+ DCHECK_ALIGNED_PARAM(ret.Begin(), alignment);
+ }
+ return ret;
+}
+
MemMap MemMap::MapPlaceholder(const char* name, uint8_t* addr, size_t byte_count) {
if (byte_count == 0) {
return Invalid();
@@ -777,11 +803,11 @@
return MemMap(tail_name, actual, tail_size, actual, tail_base_size, tail_prot, false);
}
-MemMap MemMap::TakeReservedMemory(size_t byte_count) {
+MemMap MemMap::TakeReservedMemory(size_t byte_count, bool reuse) {
uint8_t* begin = Begin();
ReleaseReservedMemory(byte_count); // Performs necessary DCHECK()s on this reservation.
size_t base_size = RoundUp(byte_count, kPageSize);
- return MemMap(name_, begin, byte_count, begin, base_size, prot_, /* reuse= */ false);
+ return MemMap(name_, begin, byte_count, begin, base_size, prot_, reuse);
}
void MemMap::ReleaseReservedMemory(size_t byte_count) {
@@ -1247,40 +1273,46 @@
}
}
-void MemMap::AlignBy(size_t size) {
+void MemMap::AlignBy(size_t alignment, bool align_both_ends) {
CHECK_EQ(begin_, base_begin_) << "Unsupported";
CHECK_EQ(size_, base_size_) << "Unsupported";
- CHECK_GT(size, static_cast<size_t>(kPageSize));
- CHECK_ALIGNED(size, kPageSize);
+ CHECK_GT(alignment, static_cast<size_t>(kPageSize));
+ CHECK_ALIGNED(alignment, kPageSize);
CHECK(!reuse_);
- if (IsAlignedParam(reinterpret_cast<uintptr_t>(base_begin_), size) &&
- IsAlignedParam(base_size_, size)) {
+ if (IsAlignedParam(reinterpret_cast<uintptr_t>(base_begin_), alignment) &&
+ (!align_both_ends || IsAlignedParam(base_size_, alignment))) {
// Already aligned.
return;
}
uint8_t* base_begin = reinterpret_cast<uint8_t*>(base_begin_);
- uint8_t* base_end = base_begin + base_size_;
- uint8_t* aligned_base_begin = AlignUp(base_begin, size);
- uint8_t* aligned_base_end = AlignDown(base_end, size);
+ uint8_t* aligned_base_begin = AlignUp(base_begin, alignment);
CHECK_LE(base_begin, aligned_base_begin);
- CHECK_LE(aligned_base_end, base_end);
- size_t aligned_base_size = aligned_base_end - aligned_base_begin;
- CHECK_LT(aligned_base_begin, aligned_base_end)
- << "base_begin = " << reinterpret_cast<void*>(base_begin)
- << " base_end = " << reinterpret_cast<void*>(base_end);
- CHECK_GE(aligned_base_size, size);
- // Unmap the unaligned parts.
if (base_begin < aligned_base_begin) {
MEMORY_TOOL_MAKE_UNDEFINED(base_begin, aligned_base_begin - base_begin);
CHECK_EQ(TargetMUnmap(base_begin, aligned_base_begin - base_begin), 0)
<< "base_begin=" << reinterpret_cast<void*>(base_begin)
<< " aligned_base_begin=" << reinterpret_cast<void*>(aligned_base_begin);
}
- if (aligned_base_end < base_end) {
- MEMORY_TOOL_MAKE_UNDEFINED(aligned_base_end, base_end - aligned_base_end);
- CHECK_EQ(TargetMUnmap(aligned_base_end, base_end - aligned_base_end), 0)
- << "base_end=" << reinterpret_cast<void*>(base_end)
- << " aligned_base_end=" << reinterpret_cast<void*>(aligned_base_end);
+ uint8_t* base_end = base_begin + base_size_;
+ size_t aligned_base_size;
+ if (align_both_ends) {
+ uint8_t* aligned_base_end = AlignDown(base_end, alignment);
+ CHECK_LE(aligned_base_end, base_end);
+ CHECK_LT(aligned_base_begin, aligned_base_end)
+ << "base_begin = " << reinterpret_cast<void*>(base_begin)
+ << " base_end = " << reinterpret_cast<void*>(base_end);
+ aligned_base_size = aligned_base_end - aligned_base_begin;
+ CHECK_GE(aligned_base_size, alignment);
+ if (aligned_base_end < base_end) {
+ MEMORY_TOOL_MAKE_UNDEFINED(aligned_base_end, base_end - aligned_base_end);
+ CHECK_EQ(TargetMUnmap(aligned_base_end, base_end - aligned_base_end), 0)
+ << "base_end=" << reinterpret_cast<void*>(base_end)
+ << " aligned_base_end=" << reinterpret_cast<void*>(aligned_base_end);
+ }
+ } else {
+ CHECK_LT(aligned_base_begin, base_end)
+ << "base_begin = " << reinterpret_cast<void*>(base_begin);
+ aligned_base_size = base_end - aligned_base_begin;
}
std::lock_guard<std::mutex> mu(*mem_maps_lock_);
if (base_begin < aligned_base_begin) {
diff --git a/libartbase/base/mem_map.h b/libartbase/base/mem_map.h
index 4c41388..42120a3 100644
--- a/libartbase/base/mem_map.h
+++ b/libartbase/base/mem_map.h
@@ -137,6 +137,17 @@
/*inout*/MemMap* reservation,
/*out*/std::string* error_msg,
bool use_debug_name = true);
+
+ // Request an aligned anonymous region. We can't directly ask for a MAP_SHARED (anonymous or
+ // otherwise) mapping to be aligned as in that case file offset is involved and could make
+ // the starting offset to be out of sync with another mapping of the same file.
+ static MemMap MapAnonymousAligned(const char* name,
+ size_t byte_count,
+ int prot,
+ bool low_4gb,
+ size_t alignment,
+ /*out=*/std::string* error_msg);
+
static MemMap MapAnonymous(const char* name,
size_t byte_count,
int prot,
@@ -290,8 +301,9 @@
// exceed the size of this reservation.
//
// Returns a mapping owning `byte_count` bytes rounded up to entire pages
- // with size set to the passed `byte_count`.
- MemMap TakeReservedMemory(size_t byte_count);
+ // with size set to the passed `byte_count`. If 'reuse' is true then the caller
+ // is responsible for unmapping the taken pages.
+ MemMap TakeReservedMemory(size_t byte_count, bool reuse = false);
static bool CheckNoGaps(MemMap& begin_map, MemMap& end_map)
REQUIRES(!MemMap::mem_maps_lock_);
@@ -309,8 +321,9 @@
// intermittently.
void TryReadable();
- // Align the map by unmapping the unaligned parts at the lower and the higher ends.
- void AlignBy(size_t size);
+ // Align the map by unmapping the unaligned part at the lower end and if 'align_both_ends' is
+ // true, then the higher end as well.
+ void AlignBy(size_t alignment, bool align_both_ends = true);
// For annotation reasons.
static std::mutex* GetMemMapsLock() RETURN_CAPABILITY(mem_maps_lock_) {
@@ -321,6 +334,9 @@
// in the parent process.
void ResetInForkedProcess();
+ // 'redzone_size_ == 0' indicates that we are not using memory-tool on this mapping.
+ size_t GetRedzoneSize() const { return redzone_size_; }
+
private:
MemMap(const std::string& name,
uint8_t* begin,
diff --git a/libartbase/base/mem_map_windows.cc b/libartbase/base/mem_map_windows.cc
index 84e14ea..dfa75a1 100644
--- a/libartbase/base/mem_map_windows.cc
+++ b/libartbase/base/mem_map_windows.cc
@@ -31,7 +31,6 @@
namespace art {
-using android::base::MappedFile;
using android::base::StringPrintf;
static off_t allocation_granularity;
diff --git a/libartbase/base/metrics/metrics.h b/libartbase/base/metrics/metrics.h
index d6f2463..8432be5 100644
--- a/libartbase/base/metrics/metrics.h
+++ b/libartbase/base/metrics/metrics.h
@@ -30,32 +30,67 @@
#include "android-base/logging.h"
#include "base/bit_utils.h"
#include "base/time_utils.h"
+#include "tinyxml2.h"
#pragma clang diagnostic push
#pragma clang diagnostic error "-Wconversion"
// See README.md in this directory for how to define metrics.
-#define ART_METRICS(METRIC) \
- METRIC(ClassLoadingTotalTime, MetricsCounter) \
- METRIC(ClassVerificationTotalTime, MetricsCounter) \
- METRIC(ClassVerificationCount, MetricsCounter) \
- METRIC(WorldStopTimeDuringGCAvg, MetricsAverage) \
- METRIC(YoungGcCount, MetricsCounter) \
- METRIC(FullGcCount, MetricsCounter) \
- METRIC(TotalBytesAllocated, MetricsCounter) \
- METRIC(TotalGcCollectionTime, MetricsCounter) \
- METRIC(YoungGcThroughputAvg, MetricsAverage) \
- METRIC(FullGcThroughputAvg, MetricsAverage) \
- METRIC(YoungGcTracingThroughputAvg, MetricsAverage) \
- METRIC(FullGcTracingThroughputAvg, MetricsAverage) \
- METRIC(JitMethodCompileTotalTime, MetricsCounter) \
- METRIC(JitMethodCompileCount, MetricsCounter) \
- METRIC(YoungGcCollectionTime, MetricsHistogram, 15, 0, 60'000) \
- METRIC(FullGcCollectionTime, MetricsHistogram, 15, 0, 60'000) \
- METRIC(YoungGcThroughput, MetricsHistogram, 15, 0, 10'000) \
- METRIC(FullGcThroughput, MetricsHistogram, 15, 0, 10'000) \
- METRIC(YoungGcTracingThroughput, MetricsHistogram, 15, 0, 10'000) \
- METRIC(FullGcTracingThroughput, MetricsHistogram, 15, 0, 10'000)
+
+// Metrics reported as Event Metrics.
+#define ART_EVENT_METRICS(METRIC) \
+ METRIC(ClassLoadingTotalTime, MetricsCounter) \
+ METRIC(ClassVerificationTotalTime, MetricsCounter) \
+ METRIC(ClassVerificationCount, MetricsCounter) \
+ METRIC(WorldStopTimeDuringGCAvg, MetricsAverage) \
+ METRIC(YoungGcCount, MetricsCounter) \
+ METRIC(FullGcCount, MetricsCounter) \
+ METRIC(TotalBytesAllocated, MetricsCounter) \
+ METRIC(TotalGcCollectionTime, MetricsCounter) \
+ METRIC(YoungGcThroughputAvg, MetricsAverage) \
+ METRIC(FullGcThroughputAvg, MetricsAverage) \
+ METRIC(YoungGcTracingThroughputAvg, MetricsAverage) \
+ METRIC(FullGcTracingThroughputAvg, MetricsAverage) \
+ METRIC(JitMethodCompileTotalTime, MetricsCounter) \
+ METRIC(JitMethodCompileCount, MetricsCounter) \
+ METRIC(YoungGcCollectionTime, MetricsHistogram, 15, 0, 60'000) \
+ METRIC(FullGcCollectionTime, MetricsHistogram, 15, 0, 60'000) \
+ METRIC(YoungGcThroughput, MetricsHistogram, 15, 0, 10'000) \
+ METRIC(FullGcThroughput, MetricsHistogram, 15, 0, 10'000) \
+ METRIC(YoungGcTracingThroughput, MetricsHistogram, 15, 0, 10'000) \
+ METRIC(FullGcTracingThroughput, MetricsHistogram, 15, 0, 10'000) \
+ METRIC(GcWorldStopTime, MetricsCounter) \
+ METRIC(GcWorldStopCount, MetricsCounter) \
+ METRIC(YoungGcScannedBytes, MetricsCounter) \
+ METRIC(YoungGcFreedBytes, MetricsCounter) \
+ METRIC(YoungGcDuration, MetricsCounter) \
+ METRIC(FullGcScannedBytes, MetricsCounter) \
+ METRIC(FullGcFreedBytes, MetricsCounter) \
+ METRIC(FullGcDuration, MetricsCounter)
+
+// Increasing counter metrics, reported as Value Metrics in delta increments.
+#define ART_VALUE_METRICS(METRIC) \
+ METRIC(GcWorldStopTimeDelta, MetricsDeltaCounter) \
+ METRIC(GcWorldStopCountDelta, MetricsDeltaCounter) \
+ METRIC(YoungGcScannedBytesDelta, MetricsDeltaCounter) \
+ METRIC(YoungGcFreedBytesDelta, MetricsDeltaCounter) \
+ METRIC(YoungGcDurationDelta, MetricsDeltaCounter) \
+ METRIC(FullGcScannedBytesDelta, MetricsDeltaCounter) \
+ METRIC(FullGcFreedBytesDelta, MetricsDeltaCounter) \
+ METRIC(FullGcDurationDelta, MetricsDeltaCounter) \
+ METRIC(JitMethodCompileTotalTimeDelta, MetricsDeltaCounter) \
+ METRIC(JitMethodCompileCountDelta, MetricsDeltaCounter) \
+ METRIC(ClassVerificationTotalTimeDelta, MetricsDeltaCounter) \
+ METRIC(ClassVerificationCountDelta, MetricsDeltaCounter) \
+ METRIC(ClassLoadingTotalTimeDelta, MetricsDeltaCounter) \
+ METRIC(TotalBytesAllocatedDelta, MetricsDeltaCounter) \
+ METRIC(TotalGcCollectionTimeDelta, MetricsDeltaCounter) \
+ METRIC(YoungGcCountDelta, MetricsDeltaCounter) \
+ METRIC(FullGcCountDelta, MetricsDeltaCounter)
+
+#define ART_METRICS(METRIC) \
+ ART_EVENT_METRICS(METRIC) \
+ ART_VALUE_METRICS(METRIC)
// A lot of the metrics implementation code is generated by passing one-off macros into ART_COUNTERS
// and ART_HISTOGRAMS. This means metrics.h and metrics.cc are very #define-heavy, which can be
@@ -115,7 +150,7 @@
#define REASON_NAME(kind, kind_name) \
case CompilationReason::kind: return kind_name;
#define REASON_FROM_NAME(kind, kind_name) \
- if (name == kind_name) { return CompilationReason::kind; }
+ if (name == (kind_name)) { return CompilationReason::kind; }
constexpr const char* CompilationReasonName(CompilationReason reason) {
switch (reason) {
@@ -129,7 +164,7 @@
}
#undef REASON_NAME
-#undef ReasonFromName
+#undef REASON_FROM_NAME
#define COMPILER_FILTER_REPORTING_LIST(V) \
V(kError, "error") /* Error (invalid value) condition */ \
@@ -156,7 +191,7 @@
#define FILTER_NAME(kind, kind_name) \
case CompilerFilterReporting::kind: return kind_name;
#define FILTER_FROM_NAME(kind, kind_name) \
- if (name == kind_name) { return CompilerFilterReporting::kind; }
+ if (name == (kind_name)) { return CompilerFilterReporting::kind; }
constexpr const char* CompilerFilterReportingName(CompilerFilterReporting filter) {
switch (filter) {
@@ -234,6 +269,8 @@
template <DatumId counter_type, typename T>
friend class MetricsCounter;
+ template <DatumId counter_type, typename T>
+ friend class MetricsDeltaCounter;
template <DatumId histogram_type, size_t num_buckets, int64_t low_value, int64_t high_value>
friend class MetricsHistogram;
template <DatumId datum_id, typename T, const T& AccumulatorFunction(const T&, const T&)>
@@ -265,13 +302,14 @@
void AddOne() { Add(1u); }
void Add(value_t value) { value_.fetch_add(value, std::memory_order::memory_order_relaxed); }
- void Report(MetricsBackend* backend) const { backend->ReportCounter(counter_type, Value()); }
-
- protected:
- void Reset() {
- value_ = 0;
+ void Report(const std::vector<MetricsBackend*>& backends) const {
+ for (MetricsBackend* backend : backends) {
+ backend->ReportCounter(counter_type, Value());
+ }
}
+ protected:
+ void Reset() { value_ = 0; }
value_t Value() const { return value_.load(std::memory_order::memory_order_relaxed); }
private:
@@ -308,11 +346,14 @@
count_.fetch_add(1, std::memory_order::memory_order_release);
}
- void Report(MetricsBackend* backend) const {
+ void Report(const std::vector<MetricsBackend*>& backends) const {
+ count_t value = MetricsCounter<datum_id, value_t>::Value();
count_t count = count_.load(std::memory_order::memory_order_acquire);
- backend->ReportCounter(datum_id,
- // Avoid divide-by-0.
- count != 0 ? MetricsCounter<datum_id, value_t>::Value() / count : 0);
+ // Avoid divide-by-0.
+ count_t average_value = count != 0 ? value / count : 0;
+ for (MetricsBackend* backend : backends) {
+ backend->ReportCounter(datum_id, average_value);
+ }
}
protected:
@@ -328,6 +369,40 @@
friend class ArtMetrics;
};
+template <DatumId datum_id, typename T = uint64_t>
+class MetricsDeltaCounter : public MetricsBase<T> {
+ public:
+ using value_t = T;
+
+ explicit constexpr MetricsDeltaCounter(uint64_t value = 0) : value_{value} {
+ // Ensure we do not have any unnecessary data in this class.
+ // Adding intptr_t to accommodate vtable, and rounding up to incorporate
+ // padding.
+ static_assert(RoundUp(sizeof(*this), sizeof(uint64_t)) ==
+ RoundUp(sizeof(intptr_t) + sizeof(value_t), sizeof(uint64_t)));
+ }
+
+ void Add(value_t value) override {
+ value_.fetch_add(value, std::memory_order::memory_order_relaxed);
+ }
+ void AddOne() { Add(1u); }
+
+ void ReportAndReset(const std::vector<MetricsBackend*>& backends) {
+ value_t value = value_.exchange(0, std::memory_order::memory_order_relaxed);
+ for (MetricsBackend* backend : backends) {
+ backend->ReportCounter(datum_id, value);
+ }
+ }
+
+ void Reset() { value_ = 0; }
+
+ private:
+ std::atomic<value_t> value_;
+ static_assert(std::atomic<value_t>::is_always_lock_free);
+
+ friend class ArtMetrics;
+};
+
template <DatumId histogram_type_,
size_t num_buckets_,
int64_t minimum_value_,
@@ -352,8 +427,10 @@
buckets_[i].fetch_add(1u, std::memory_order::memory_order_relaxed);
}
- void Report(MetricsBackend* backend) const {
- backend->ReportHistogram(histogram_type_, minimum_value_, maximum_value_, GetBuckets());
+ void Report(const std::vector<MetricsBackend*>& backends) const {
+ for (MetricsBackend* backend : backends) {
+ backend->ReportHistogram(histogram_type_, minimum_value_, maximum_value_, GetBuckets());
+ }
}
protected:
@@ -433,12 +510,80 @@
friend class ArtMetrics;
};
-// A backend that writes metrics in a human-readable format to a string.
+// Base class for formatting metrics into different formats
+// (human-readable text, JSON, etc.)
+class MetricsFormatter {
+ public:
+ virtual ~MetricsFormatter() = default;
+
+ virtual void FormatBeginReport(uint64_t timestamp_since_start_ms,
+ const std::optional<SessionData>& session_data) = 0;
+ virtual void FormatEndReport() = 0;
+ virtual void FormatReportCounter(DatumId counter_type, uint64_t value) = 0;
+ virtual void FormatReportHistogram(DatumId histogram_type,
+ int64_t low_value,
+ int64_t high_value,
+ const std::vector<uint32_t>& buckets) = 0;
+ virtual std::string GetAndResetBuffer() = 0;
+
+ protected:
+ const std::string version = "1.0";
+};
+
+// Formatter outputting metrics in human-readable text format
+class TextFormatter : public MetricsFormatter {
+ public:
+ TextFormatter() = default;
+
+ void FormatBeginReport(uint64_t timestamp_millis,
+ const std::optional<SessionData>& session_data) override;
+
+ void FormatReportCounter(DatumId counter_type, uint64_t value) override;
+
+ void FormatReportHistogram(DatumId histogram_type,
+ int64_t low_value,
+ int64_t high_value,
+ const std::vector<uint32_t>& buckets) override;
+
+ void FormatEndReport() override;
+
+ std::string GetAndResetBuffer() override;
+
+ private:
+ std::ostringstream os_;
+};
+
+// Formatter outputting metrics in XML format
+class XmlFormatter : public MetricsFormatter {
+ public:
+ XmlFormatter() = default;
+
+ void FormatBeginReport(uint64_t timestamp_millis,
+ const std::optional<SessionData>& session_data) override;
+
+ void FormatReportCounter(DatumId counter_type, uint64_t value) override;
+
+ void FormatReportHistogram(DatumId histogram_type,
+ int64_t low_value,
+ int64_t high_value,
+ const std::vector<uint32_t>& buckets) override;
+
+ void FormatEndReport() override;
+
+ std::string GetAndResetBuffer() override;
+
+ private:
+ tinyxml2::XMLDocument document_;
+};
+
+// A backend that writes metrics to a string.
+// The format of the metrics' output is delegated
+// to the MetricsFormatter class.
//
// This is used as a base for LogBackend and FileBackend.
class StringBackend : public MetricsBackend {
public:
- StringBackend();
+ explicit StringBackend(std::unique_ptr<MetricsFormatter> formatter);
void BeginOrUpdateSession(const SessionData& session_data) override;
@@ -456,14 +601,15 @@
std::string GetAndResetBuffer();
private:
- std::ostringstream os_;
+ std::unique_ptr<MetricsFormatter> formatter_;
std::optional<SessionData> session_data_;
};
// A backend that writes metrics in human-readable format to the log (i.e. logcat).
class LogBackend : public StringBackend {
public:
- explicit LogBackend(android::base::LogSeverity level);
+ explicit LogBackend(std::unique_ptr<MetricsFormatter> formatter,
+ android::base::LogSeverity level);
void BeginReport(uint64_t timestamp_millis) override;
void EndReport() override;
@@ -473,12 +619,10 @@
};
// A backend that writes metrics to a file.
-//
-// These are currently written in the same human-readable format used by StringBackend and
-// LogBackend, but we will probably want a more machine-readable format in the future.
class FileBackend : public StringBackend {
public:
- explicit FileBackend(const std::string& filename);
+ explicit FileBackend(std::unique_ptr<MetricsFormatter> formatter,
+ const std::string& filename);
void BeginReport(uint64_t timestamp_millis) override;
void EndReport() override;
@@ -563,8 +707,8 @@
public:
ArtMetrics();
- void ReportAllMetrics(MetricsBackend* backend) const;
- void DumpForSigQuit(std::ostream& os) const;
+ void ReportAllMetricsAndResetValueMetrics(const std::vector<MetricsBackend*>& backends);
+ void DumpForSigQuit(std::ostream& os);
// Resets all metrics to their initial value. This is intended to be used after forking from the
// zygote so we don't attribute parent values to the child process.
diff --git a/libartbase/base/metrics/metrics_common.cc b/libartbase/base/metrics/metrics_common.cc
index f09987b..2732088 100644
--- a/libartbase/base/metrics/metrics_common.cc
+++ b/libartbase/base/metrics/metrics_common.cc
@@ -65,36 +65,43 @@
{
}
-void ArtMetrics::ReportAllMetrics(MetricsBackend* backend) const {
- backend->BeginReport(MilliTime() - beginning_timestamp_);
+void ArtMetrics::ReportAllMetricsAndResetValueMetrics(
+ const std::vector<MetricsBackend*>& backends) {
+ for (auto& backend : backends) {
+ backend->BeginReport(MilliTime() - beginning_timestamp_);
+ }
-#define ART_METRIC(name, Kind, ...) name()->Report(backend);
- ART_METRICS(ART_METRIC)
-#undef ART_METRIC
+#define REPORT_METRIC(name, Kind, ...) name()->Report(backends);
+ ART_EVENT_METRICS(REPORT_METRIC)
+#undef REPORT_METRIC
- backend->EndReport();
+#define REPORT_METRIC(name, Kind, ...) name()->ReportAndReset(backends);
+ ART_VALUE_METRICS(REPORT_METRIC)
+#undef REPORT_METRIC
+
+ for (auto& backend : backends) {
+ backend->EndReport();
+ }
}
-void ArtMetrics::DumpForSigQuit(std::ostream& os) const {
- StringBackend backend;
- ReportAllMetrics(&backend);
+void ArtMetrics::DumpForSigQuit(std::ostream& os) {
+ StringBackend backend(std::make_unique<TextFormatter>());
+ ReportAllMetricsAndResetValueMetrics({&backend});
os << backend.GetAndResetBuffer();
}
void ArtMetrics::Reset() {
beginning_timestamp_ = MilliTime();
-#define ART_METRIC(name, kind, ...) name##_.Reset();
- ART_METRICS(ART_METRIC);
-#undef ART_METRIC
+#define RESET_METRIC(name, ...) name##_.Reset();
+ ART_METRICS(RESET_METRIC)
+#undef RESET_METRIC
}
-StringBackend::StringBackend() {}
+StringBackend::StringBackend(std::unique_ptr<MetricsFormatter> formatter)
+ : formatter_(std::move(formatter)) {}
std::string StringBackend::GetAndResetBuffer() {
- std::string result = os_.str();
- os_.clear();
- os_.str("");
- return result;
+ return formatter_->GetAndResetBuffer();
}
void StringBackend::BeginOrUpdateSession(const SessionData& session_data) {
@@ -102,32 +109,51 @@
}
void StringBackend::BeginReport(uint64_t timestamp_since_start_ms) {
- os_ << "\n*** ART internal metrics ***\n";
- os_ << " Metadata:\n";
- os_ << " timestamp_since_start_ms: " << timestamp_since_start_ms << "\n";
- if (session_data_.has_value()) {
- os_ << " session_id: " << session_data_->session_id << "\n";
- os_ << " uid: " << session_data_->uid << "\n";
- os_ << " compilation_reason: " << CompilationReasonName(session_data_->compilation_reason)
- << "\n";
- os_ << " compiler_filter: " << CompilerFilterReportingName(session_data_->compiler_filter)
- << "\n";
- }
- os_ << " Metrics:\n";
+ formatter_->FormatBeginReport(timestamp_since_start_ms, session_data_);
}
-void StringBackend::EndReport() { os_ << "*** Done dumping ART internal metrics ***\n"; }
+void StringBackend::EndReport() {
+ formatter_->FormatEndReport();
+}
void StringBackend::ReportCounter(DatumId counter_type, uint64_t value) {
- os_ << " " << DatumName(counter_type) << ": count = " << value << "\n";
+ formatter_->FormatReportCounter(counter_type, value);
}
void StringBackend::ReportHistogram(DatumId histogram_type,
int64_t minimum_value_,
int64_t maximum_value_,
const std::vector<uint32_t>& buckets) {
- os_ << " " << DatumName(histogram_type) << ": range = " << minimum_value_ << "..." << maximum_value_;
- if (buckets.size() > 0) {
+ formatter_->FormatReportHistogram(histogram_type, minimum_value_, maximum_value_, buckets);
+}
+
+void TextFormatter::FormatBeginReport(uint64_t timestamp_since_start_ms,
+ const std::optional<SessionData>& session_data) {
+ os_ << "\n*** ART internal metrics ***\n";
+ os_ << " Metadata:\n";
+ os_ << " timestamp_since_start_ms: " << timestamp_since_start_ms << "\n";
+ if (session_data.has_value()) {
+ os_ << " session_id: " << session_data->session_id << "\n";
+ os_ << " uid: " << session_data->uid << "\n";
+ os_ << " compilation_reason: " << CompilationReasonName(session_data->compilation_reason)
+ << "\n";
+ os_ << " compiler_filter: " << CompilerFilterReportingName(session_data->compiler_filter)
+ << "\n";
+ }
+ os_ << " Metrics:\n";
+}
+
+void TextFormatter::FormatReportCounter(DatumId counter_type, uint64_t value) {
+ os_ << " " << DatumName(counter_type) << ": count = " << value << "\n";
+}
+
+void TextFormatter::FormatReportHistogram(DatumId histogram_type,
+ int64_t minimum_value_,
+ int64_t maximum_value_,
+ const std::vector<uint32_t>& buckets) {
+ os_ << " " << DatumName(histogram_type) << ": range = "
+ << minimum_value_ << "..." << maximum_value_;
+ if (!buckets.empty()) {
os_ << ", buckets: ";
bool first = true;
for (const auto& count : buckets) {
@@ -143,22 +169,100 @@
}
}
-LogBackend::LogBackend(android::base::LogSeverity level) : level_{level} {}
+void TextFormatter::FormatEndReport() {
+ os_ << "*** Done dumping ART internal metrics ***\n";
+}
+
+std::string TextFormatter::GetAndResetBuffer() {
+ std::string result = os_.str();
+ os_.clear();
+ os_.str("");
+ return result;
+}
+
+void XmlFormatter::FormatBeginReport(uint64_t timestamp_millis,
+ const std::optional<SessionData>& session_data) {
+ tinyxml2::XMLElement* art_runtime_metrics = document_.NewElement("art_runtime_metrics");
+ document_.InsertEndChild(art_runtime_metrics);
+
+ art_runtime_metrics->InsertNewChildElement("version")->SetText(version.data());
+
+ tinyxml2::XMLElement* metadata = art_runtime_metrics->InsertNewChildElement("metadata");
+ metadata->InsertNewChildElement("timestamp_since_start_ms")->SetText(timestamp_millis);
+
+ if (session_data.has_value()) {
+ metadata->InsertNewChildElement("session_id")->SetText(session_data->session_id);
+ metadata->InsertNewChildElement("uid")->SetText(session_data->uid);
+ metadata
+ ->InsertNewChildElement("compilation_reason")
+ ->SetText(CompilationReasonName(session_data->compilation_reason));
+ metadata
+ ->InsertNewChildElement("compiler_filter")
+ ->SetText(CompilerFilterReportingName(session_data->compiler_filter));
+ }
+
+ art_runtime_metrics->InsertNewChildElement("metrics");
+}
+
+void XmlFormatter::FormatReportCounter(DatumId counter_type, uint64_t value) {
+ tinyxml2::XMLElement* metrics = document_.RootElement()->FirstChildElement("metrics");
+
+ tinyxml2::XMLElement* counter = metrics->InsertNewChildElement(DatumName(counter_type).data());
+ counter->InsertNewChildElement("counter_type")->SetText("count");
+ counter->InsertNewChildElement("value")->SetText(value);
+}
+
+void XmlFormatter::FormatReportHistogram(DatumId histogram_type,
+ int64_t low_value,
+ int64_t high_value,
+ const std::vector<uint32_t>& buckets) {
+ tinyxml2::XMLElement* metrics = document_.RootElement()->FirstChildElement("metrics");
+
+ tinyxml2::XMLElement* histogram =
+ metrics->InsertNewChildElement(DatumName(histogram_type).data());
+ histogram->InsertNewChildElement("counter_type")->SetText("histogram");
+ histogram->InsertNewChildElement("minimum_value")->SetText(low_value);
+ histogram->InsertNewChildElement("maximum_value")->SetText(high_value);
+
+ tinyxml2::XMLElement* buckets_element = histogram->InsertNewChildElement("buckets");
+ for (const auto& count : buckets) {
+ buckets_element->InsertNewChildElement("bucket")->SetText(count);
+ }
+}
+
+void XmlFormatter::FormatEndReport() {}
+
+std::string XmlFormatter::GetAndResetBuffer() {
+ tinyxml2::XMLPrinter printer(/*file=*/nullptr, /*compact=*/true);
+ document_.Print(&printer);
+ std::string result = printer.CStr();
+ document_.Clear();
+
+ return result;
+}
+
+LogBackend::LogBackend(std::unique_ptr<MetricsFormatter> formatter,
+ android::base::LogSeverity level)
+ : StringBackend{std::move(formatter)}, level_{level}
+{}
void LogBackend::BeginReport(uint64_t timestamp_since_start_ms) {
- GetAndResetBuffer();
+ StringBackend::GetAndResetBuffer();
StringBackend::BeginReport(timestamp_since_start_ms);
}
void LogBackend::EndReport() {
StringBackend::EndReport();
- LOG_STREAM(level_) << GetAndResetBuffer();
+ LOG_STREAM(level_) << StringBackend::GetAndResetBuffer();
}
-FileBackend::FileBackend(const std::string& filename) : filename_{filename} {}
+FileBackend::FileBackend(std::unique_ptr<MetricsFormatter> formatter,
+ const std::string& filename)
+ : StringBackend{std::move(formatter)}, filename_{filename}
+{}
void FileBackend::BeginReport(uint64_t timestamp_since_start_ms) {
- GetAndResetBuffer();
+ StringBackend::GetAndResetBuffer();
StringBackend::BeginReport(timestamp_since_start_ms);
}
@@ -170,7 +274,7 @@
if (file.get() == nullptr) {
LOG(WARNING) << "Could open metrics file '" << filename_ << "': " << error_message;
} else {
- if (!android::base::WriteStringToFd(GetAndResetBuffer(), file.get()->Fd())) {
+ if (!android::base::WriteStringToFd(StringBackend::GetAndResetBuffer(), file.get()->Fd())) {
PLOG(WARNING) << "Error writing metrics to file";
}
}
diff --git a/libartbase/base/metrics/metrics_test.cc b/libartbase/base/metrics/metrics_test.cc
index ed6f88d..dff90b1 100644
--- a/libartbase/base/metrics/metrics_test.cc
+++ b/libartbase/base/metrics/metrics_test.cc
@@ -16,6 +16,8 @@
#include "metrics.h"
+#include "base/macros.h"
+#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "metrics_test.h"
@@ -231,7 +233,7 @@
bool found_histogram_{false};
} backend;
- metrics.ReportAllMetrics(&backend);
+ metrics.ReportAllMetricsAndResetValueMetrics({&backend});
}
TEST_F(MetricsTest, HistogramTimer) {
@@ -248,9 +250,9 @@
// Makes sure all defined metrics are included when dumping through StreamBackend.
TEST_F(MetricsTest, StreamBackendDumpAllMetrics) {
ArtMetrics metrics;
- StringBackend backend;
+ StringBackend backend(std::make_unique<TextFormatter>());
- metrics.ReportAllMetrics(&backend);
+ metrics.ReportAllMetricsAndResetValueMetrics({&backend});
// Make sure the resulting string lists all the metrics.
const std::string result = backend.GetAndResetBuffer();
@@ -270,11 +272,14 @@
class NonZeroBackend : public TestBackendBase {
public:
- void ReportCounter(DatumId, uint64_t value) override {
+ void ReportCounter(DatumId counter_type [[gnu::unused]], uint64_t value) override {
EXPECT_NE(value, 0u);
}
- void ReportHistogram(DatumId, int64_t, int64_t, const std::vector<uint32_t>& buckets) override {
+ void ReportHistogram(DatumId histogram_type [[gnu::unused]],
+ int64_t minimum_value [[gnu::unused]],
+ int64_t maximum_value [[gnu::unused]],
+ const std::vector<uint32_t>& buckets) override {
bool nonzero = false;
for (const auto value : buckets) {
nonzero |= (value != 0u);
@@ -284,25 +289,357 @@
} non_zero_backend;
// Make sure the metrics all have a nonzero value.
- metrics.ReportAllMetrics(&non_zero_backend);
+ metrics.ReportAllMetricsAndResetValueMetrics({&non_zero_backend});
// Reset the metrics and make sure they are all zero again
metrics.Reset();
class ZeroBackend : public TestBackendBase {
public:
- void ReportCounter(DatumId, uint64_t value) override {
+ void ReportCounter(DatumId counter_type [[gnu::unused]], uint64_t value) override {
EXPECT_EQ(value, 0u);
}
- void ReportHistogram(DatumId, int64_t, int64_t, const std::vector<uint32_t>& buckets) override {
+ void ReportHistogram(DatumId histogram_type [[gnu::unused]],
+ int64_t minimum_value [[gnu::unused]],
+ int64_t maximum_value [[gnu::unused]],
+ const std::vector<uint32_t>& buckets) override {
for (const auto value : buckets) {
EXPECT_EQ(value, 0u);
}
}
} zero_backend;
- metrics.ReportAllMetrics(&zero_backend);
+ metrics.ReportAllMetricsAndResetValueMetrics({&zero_backend});
+}
+
+TEST_F(MetricsTest, KeepEventMetricsResetValueMetricsAfterReporting) {
+ ArtMetrics metrics;
+
+ // Add something to each of the metrics.
+#define METRIC(name, type, ...) metrics.name()->Add(42);
+ ART_METRICS(METRIC)
+#undef METRIC
+
+ class FirstBackend : public TestBackendBase {
+ public:
+ void ReportCounter(DatumId counter_type [[gnu::unused]], uint64_t value) override {
+ EXPECT_NE(value, 0u);
+ }
+
+ void ReportHistogram(DatumId histogram_type [[gnu::unused]],
+ int64_t minimum_value [[gnu::unused]],
+ int64_t maximum_value [[gnu::unused]],
+ const std::vector<uint32_t>& buckets) override {
+ EXPECT_NE(buckets[0], 0u) << "Bucket 0 should have a non-zero value";
+ for (size_t i = 1; i < buckets.size(); i++) {
+ EXPECT_EQ(buckets[i], 0u) << "Bucket " << i << " should have a zero value";
+ }
+ }
+ } first_backend;
+
+ // Make sure the metrics all have a nonzero value, and they are not reset between backends.
+ metrics.ReportAllMetricsAndResetValueMetrics({&first_backend, &first_backend});
+
+ // After reporting, the Value Metrics should have been reset.
+ class SecondBackend : public TestBackendBase {
+ public:
+ void ReportCounter(DatumId datum_id, uint64_t value) override {
+ switch (datum_id) {
+ // Value metrics - expected to have been reset
+#define CHECK_METRIC(name, ...) case DatumId::k##name:
+ ART_VALUE_METRICS(CHECK_METRIC)
+#undef CHECK_METRIC
+ EXPECT_EQ(value, 0u);
+ return;
+
+ // Event metrics - expected to have retained their previous value
+#define CHECK_METRIC(name, ...) case DatumId::k##name:
+ ART_EVENT_METRICS(CHECK_METRIC)
+#undef CHECK_METRIC
+ EXPECT_NE(value, 0u);
+ return;
+
+ default:
+ // unknown metric - it should not be possible to reach this path
+ FAIL();
+ UNREACHABLE();
+ }
+ }
+
+ // All histograms are event metrics.
+ void ReportHistogram(DatumId histogram_type [[gnu::unused]],
+ int64_t minimum_value [[gnu::unused]],
+ int64_t maximum_value [[gnu::unused]],
+ const std::vector<uint32_t>& buckets) override {
+ EXPECT_NE(buckets[0], 0u) << "Bucket 0 should have a non-zero value";
+ for (size_t i = 1; i < buckets.size(); i++) {
+ EXPECT_EQ(buckets[i], 0u) << "Bucket " << i << " should have a zero value";
+ }
+ }
+ } second_backend;
+
+ metrics.ReportAllMetricsAndResetValueMetrics({&second_backend});
+}
+
+TEST(TextFormatterTest, ReportMetrics_WithBuckets) {
+ TextFormatter text_formatter;
+ SessionData session_data {
+ .session_id = 1000,
+ .uid = 50,
+ .compilation_reason = CompilationReason::kInstall,
+ .compiler_filter = CompilerFilterReporting::kSpeed,
+ };
+
+ text_formatter.FormatBeginReport(200, session_data);
+ text_formatter.FormatReportCounter(DatumId::kFullGcCount, 1u);
+ text_formatter.FormatReportHistogram(DatumId::kFullGcCollectionTime,
+ 50,
+ 200,
+ {2, 4, 7, 1});
+ text_formatter.FormatEndReport();
+
+ const std::string result = text_formatter.GetAndResetBuffer();
+ ASSERT_EQ(result,
+ "\n*** ART internal metrics ***\n"
+ " Metadata:\n"
+ " timestamp_since_start_ms: 200\n"
+ " session_id: 1000\n"
+ " uid: 50\n"
+ " compilation_reason: install\n"
+ " compiler_filter: speed\n"
+ " Metrics:\n"
+ " FullGcCount: count = 1\n"
+ " FullGcCollectionTime: range = 50...200, buckets: 2,4,7,1\n"
+ "*** Done dumping ART internal metrics ***\n");
+}
+
+TEST(TextFormatterTest, ReportMetrics_NoBuckets) {
+ TextFormatter text_formatter;
+ SessionData session_data {
+ .session_id = 500,
+ .uid = 15,
+ .compilation_reason = CompilationReason::kCmdLine,
+ .compiler_filter = CompilerFilterReporting::kExtract,
+ };
+
+ text_formatter.FormatBeginReport(400, session_data);
+ text_formatter.FormatReportHistogram(DatumId::kFullGcCollectionTime, 10, 20, {});
+ text_formatter.FormatEndReport();
+
+ std::string result = text_formatter.GetAndResetBuffer();
+ ASSERT_EQ(result,
+ "\n*** ART internal metrics ***\n"
+ " Metadata:\n"
+ " timestamp_since_start_ms: 400\n"
+ " session_id: 500\n"
+ " uid: 15\n"
+ " compilation_reason: cmdline\n"
+ " compiler_filter: extract\n"
+ " Metrics:\n"
+ " FullGcCollectionTime: range = 10...20, no buckets\n"
+ "*** Done dumping ART internal metrics ***\n");
+}
+
+TEST(TextFormatterTest, BeginReport_NoSessionData) {
+ TextFormatter text_formatter;
+ std::optional<SessionData> empty_session_data;
+
+ text_formatter.FormatBeginReport(100, empty_session_data);
+ text_formatter.FormatEndReport();
+
+ std::string result = text_formatter.GetAndResetBuffer();
+ ASSERT_EQ(result,
+ "\n*** ART internal metrics ***\n"
+ " Metadata:\n"
+ " timestamp_since_start_ms: 100\n"
+ " Metrics:\n"
+ "*** Done dumping ART internal metrics ***\n");
+}
+
+TEST(TextFormatterTest, GetAndResetBuffer_ActuallyResetsBuffer) {
+ TextFormatter text_formatter;
+ std::optional<SessionData> empty_session_data;
+
+ text_formatter.FormatBeginReport(200, empty_session_data);
+ text_formatter.FormatReportCounter(DatumId::kFullGcCount, 1u);
+ text_formatter.FormatEndReport();
+
+ std::string result = text_formatter.GetAndResetBuffer();
+ ASSERT_EQ(result,
+ "\n*** ART internal metrics ***\n"
+ " Metadata:\n"
+ " timestamp_since_start_ms: 200\n"
+ " Metrics:\n"
+ " FullGcCount: count = 1\n"
+ "*** Done dumping ART internal metrics ***\n");
+
+ text_formatter.FormatBeginReport(300, empty_session_data);
+ text_formatter.FormatReportCounter(DatumId::kFullGcCount, 5u);
+ text_formatter.FormatEndReport();
+
+ result = text_formatter.GetAndResetBuffer();
+ ASSERT_EQ(result,
+ "\n*** ART internal metrics ***\n"
+ " Metadata:\n"
+ " timestamp_since_start_ms: 300\n"
+ " Metrics:\n"
+ " FullGcCount: count = 5\n"
+ "*** Done dumping ART internal metrics ***\n");
+}
+
+TEST(XmlFormatterTest, ReportMetrics_WithBuckets) {
+ XmlFormatter xml_formatter;
+ SessionData session_data {
+ .session_id = 123,
+ .uid = 456,
+ .compilation_reason = CompilationReason::kFirstBoot,
+ .compiler_filter = CompilerFilterReporting::kSpace,
+ };
+
+ xml_formatter.FormatBeginReport(250, session_data);
+ xml_formatter.FormatReportCounter(DatumId::kYoungGcCount, 3u);
+ xml_formatter.FormatReportHistogram(DatumId::kYoungGcCollectionTime,
+ 300,
+ 600,
+ {1, 5, 3});
+ xml_formatter.FormatEndReport();
+
+ const std::string result = xml_formatter.GetAndResetBuffer();
+ ASSERT_EQ(result,
+ "<art_runtime_metrics>"
+ "<version>1.0</version>"
+ "<metadata>"
+ "<timestamp_since_start_ms>250</timestamp_since_start_ms>"
+ "<session_id>123</session_id>"
+ "<uid>456</uid>"
+ "<compilation_reason>first-boot</compilation_reason>"
+ "<compiler_filter>space</compiler_filter>"
+ "</metadata>"
+ "<metrics>"
+ "<YoungGcCount>"
+ "<counter_type>count</counter_type>"
+ "<value>3</value>"
+ "</YoungGcCount>"
+ "<YoungGcCollectionTime>"
+ "<counter_type>histogram</counter_type>"
+ "<minimum_value>300</minimum_value>"
+ "<maximum_value>600</maximum_value>"
+ "<buckets>"
+ "<bucket>1</bucket>"
+ "<bucket>5</bucket>"
+ "<bucket>3</bucket>"
+ "</buckets>"
+ "</YoungGcCollectionTime>"
+ "</metrics>"
+ "</art_runtime_metrics>");
+}
+
+TEST(XmlFormatterTest, ReportMetrics_NoBuckets) {
+ XmlFormatter xml_formatter;
+ SessionData session_data {
+ .session_id = 234,
+ .uid = 345,
+ .compilation_reason = CompilationReason::kFirstBoot,
+ .compiler_filter = CompilerFilterReporting::kSpace,
+ };
+
+ xml_formatter.FormatBeginReport(160, session_data);
+ xml_formatter.FormatReportCounter(DatumId::kYoungGcCount, 4u);
+ xml_formatter.FormatReportHistogram(DatumId::kYoungGcCollectionTime, 20, 40, {});
+ xml_formatter.FormatEndReport();
+
+ const std::string result = xml_formatter.GetAndResetBuffer();
+ ASSERT_EQ(result,
+ "<art_runtime_metrics>"
+ "<version>1.0</version>"
+ "<metadata>"
+ "<timestamp_since_start_ms>160</timestamp_since_start_ms>"
+ "<session_id>234</session_id>"
+ "<uid>345</uid>"
+ "<compilation_reason>first-boot</compilation_reason>"
+ "<compiler_filter>space</compiler_filter>"
+ "</metadata>"
+ "<metrics>"
+ "<YoungGcCount>"
+ "<counter_type>count</counter_type>"
+ "<value>4</value>"
+ "</YoungGcCount>"
+ "<YoungGcCollectionTime>"
+ "<counter_type>histogram</counter_type>"
+ "<minimum_value>20</minimum_value>"
+ "<maximum_value>40</maximum_value>"
+ "<buckets/>"
+ "</YoungGcCollectionTime>"
+ "</metrics>"
+ "</art_runtime_metrics>");
+}
+
+TEST(XmlFormatterTest, BeginReport_NoSessionData) {
+ XmlFormatter xml_formatter;
+ std::optional<SessionData> empty_session_data;
+
+ xml_formatter.FormatBeginReport(100, empty_session_data);
+ xml_formatter.FormatReportCounter(DatumId::kYoungGcCount, 3u);
+ xml_formatter.FormatEndReport();
+
+ std::string result = xml_formatter.GetAndResetBuffer();
+ ASSERT_EQ(result,
+ "<art_runtime_metrics>"
+ "<version>1.0</version>"
+ "<metadata>"
+ "<timestamp_since_start_ms>100</timestamp_since_start_ms>"
+ "</metadata>"
+ "<metrics>"
+ "<YoungGcCount>"
+ "<counter_type>count</counter_type>"
+ "<value>3</value>"
+ "</YoungGcCount>"
+ "</metrics>"
+ "</art_runtime_metrics>");
+}
+
+TEST(XmlFormatterTest, GetAndResetBuffer_ActuallyResetsBuffer) {
+ XmlFormatter xml_formatter;
+ std::optional<SessionData> empty_session_data;
+
+ xml_formatter.FormatBeginReport(200, empty_session_data);
+ xml_formatter.FormatReportCounter(DatumId::kFullGcCount, 1u);
+ xml_formatter.FormatEndReport();
+
+ std::string result = xml_formatter.GetAndResetBuffer();
+ ASSERT_EQ(result,
+ "<art_runtime_metrics>"
+ "<version>1.0</version>"
+ "<metadata>"
+ "<timestamp_since_start_ms>200</timestamp_since_start_ms>"
+ "</metadata>"
+ "<metrics>"
+ "<FullGcCount>"
+ "<counter_type>count</counter_type>"
+ "<value>1</value>"
+ "</FullGcCount>"
+ "</metrics>"
+ "</art_runtime_metrics>");
+
+ xml_formatter.FormatBeginReport(300, empty_session_data);
+ xml_formatter.FormatReportCounter(DatumId::kFullGcCount, 5u);
+ xml_formatter.FormatEndReport();
+
+ result = xml_formatter.GetAndResetBuffer();
+ ASSERT_EQ(result,
+ "<art_runtime_metrics>"
+ "<version>1.0</version>"
+ "<metadata>"
+ "<timestamp_since_start_ms>300</timestamp_since_start_ms>"
+ "</metadata>"
+ "<metrics>"
+ "<FullGcCount>"
+ "<counter_type>count</counter_type>"
+ "<value>5</value>"
+ "</FullGcCount>"
+ "</metrics>"
+ "</art_runtime_metrics>");
}
TEST(CompilerFilterReportingTest, FromName) {
@@ -400,6 +737,8 @@
CompilationReason::kCmdLine);
ASSERT_EQ(CompilationReasonFromName("error"),
CompilationReason::kError);
+ ASSERT_EQ(CompilationReasonFromName("vdex"),
+ CompilationReason::kVdex);
}
TEST(CompilerReason, Name) {
@@ -439,6 +778,8 @@
"cmdline");
ASSERT_EQ(CompilationReasonName(CompilationReason::kError),
"error");
+ ASSERT_EQ(CompilationReasonName(CompilationReason::kVdex),
+ "vdex");
}
} // namespace metrics
} // namespace art
diff --git a/libartbase/base/metrics/metrics_test.h b/libartbase/base/metrics/metrics_test.h
index 3e8b42a..07b4e9d 100644
--- a/libartbase/base/metrics/metrics_test.h
+++ b/libartbase/base/metrics/metrics_test.h
@@ -58,7 +58,7 @@
uint64_t* counter_value_;
} backend{&counter_value};
- counter.Report(&backend);
+ counter.Report({&backend});
return counter_value;
}
@@ -75,7 +75,7 @@
std::vector<uint32_t>* buckets_;
} backend{&buckets};
- histogram.Report(&backend);
+ histogram.Report({&backend});
return buckets;
}
diff --git a/libartbase/base/safe_copy.cc b/libartbase/base/safe_copy.cc
index ad75aa7..7b0b895 100644
--- a/libartbase/base/safe_copy.cc
+++ b/libartbase/base/safe_copy.cc
@@ -56,10 +56,10 @@
}
src_iovs[iovecs_used].iov_base = const_cast<char*>(cur);
- if (!IsAlignedParam(cur, PAGE_SIZE)) {
- src_iovs[iovecs_used].iov_len = AlignUp(cur, PAGE_SIZE) - cur;
+ if (!IsAlignedParam(cur, kPageSize)) {
+ src_iovs[iovecs_used].iov_len = AlignUp(cur, kPageSize) - cur;
} else {
- src_iovs[iovecs_used].iov_len = PAGE_SIZE;
+ src_iovs[iovecs_used].iov_len = kPageSize;
}
src_iovs[iovecs_used].iov_len = std::min(src_iovs[iovecs_used].iov_len, len);
diff --git a/libartbase/base/safe_copy_test.cc b/libartbase/base/safe_copy_test.cc
index 9f7d409..01ed7cd 100644
--- a/libartbase/base/safe_copy_test.cc
+++ b/libartbase/base/safe_copy_test.cc
@@ -31,7 +31,7 @@
#if defined(__linux__)
TEST(SafeCopyTest, smoke) {
- DCHECK_EQ(kPageSize, static_cast<decltype(kPageSize)>(PAGE_SIZE));
+ DCHECK_EQ(kPageSize, static_cast<size_t>(sysconf(_SC_PAGE_SIZE)));
// Map four pages, mark the second one as PROT_NONE, unmap the last one.
void* map = mmap(nullptr, kPageSize * 4, PROT_READ | PROT_WRITE,
@@ -79,7 +79,7 @@
}
TEST(SafeCopyTest, alignment) {
- DCHECK_EQ(kPageSize, static_cast<decltype(kPageSize)>(PAGE_SIZE));
+ DCHECK_EQ(kPageSize, static_cast<size_t>(sysconf(_SC_PAGE_SIZE)));
// Copy the middle of a mapping to the end of another one.
void* src_map = mmap(nullptr, kPageSize * 3, PROT_READ | PROT_WRITE,
diff --git a/libartbase/base/safe_map.h b/libartbase/base/safe_map.h
index 7ae85d4..c6d4353 100644
--- a/libartbase/base/safe_map.h
+++ b/libartbase/base/safe_map.h
@@ -149,7 +149,7 @@
template <typename CreateFn>
V& GetOrCreate(const K& k, CreateFn create) {
- static_assert(std::is_same_v<V, std::result_of_t<CreateFn()>>,
+ static_assert(std::is_same_v<V, std::invoke_result_t<CreateFn>>,
"Argument `create` should return a value of type V.");
auto lb = lower_bound(k);
if (lb != end() && !key_comp()(k, lb->first)) {
diff --git a/libartbase/base/scoped_cap.h b/libartbase/base/scoped_cap.h
new file mode 100644
index 0000000..c369821
--- /dev/null
+++ b/libartbase/base/scoped_cap.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_LIBARTBASE_BASE_SCOPED_CAP_H_
+#define ART_LIBARTBASE_BASE_SCOPED_CAP_H_
+
+#include <sys/capability.h>
+
+#include <utility>
+
+#include "android-base/logging.h"
+
+namespace art {
+
+// A wrapper of `cap_t` that automatically calls `cap_free`.
+class ScopedCap {
+ public:
+ explicit ScopedCap(cap_t cap) : cap_(cap) {}
+
+ ScopedCap(const ScopedCap&) = delete;
+ ScopedCap& operator=(const ScopedCap&) = delete;
+ ScopedCap(ScopedCap&& other) noexcept : cap_(std::exchange(other.cap_, nullptr)) {}
+
+ ~ScopedCap() {
+ if (cap_ != nullptr) {
+ if (cap_free(cap_) != 0) {
+ PLOG(ERROR) << "Failed to call cap_free";
+ }
+ }
+ }
+
+ cap_t Get() const { return cap_; }
+
+ private:
+ cap_t cap_;
+};
+
+} // namespace art
+
+#endif // ART_LIBARTBASE_BASE_SCOPED_CAP_H_
diff --git a/libartbase/base/scoped_flock.cc b/libartbase/base/scoped_flock.cc
index b16a45a..3f44e25 100644
--- a/libartbase/base/scoped_flock.cc
+++ b/libartbase/base/scoped_flock.cc
@@ -27,7 +27,7 @@
namespace art {
-using android::base::StringPrintf;
+using android::base::StringPrintf; // NOLINT - StringPrintf is actually used
/* static */ ScopedFlock LockedFile::Open(const char* filename, std::string* error_msg) {
return Open(filename, O_CREAT | O_RDWR, true, error_msg);
diff --git a/libartbase/base/sdk_version.h b/libartbase/base/sdk_version.h
index 07c3c2c..d39aa95 100644
--- a/libartbase/base/sdk_version.h
+++ b/libartbase/base/sdk_version.h
@@ -36,7 +36,8 @@
kQ = 29u,
kR = 30u,
kS = 31u,
- kT = 32u,
+ kS_V2 = 32u,
+ kT = 33u,
kMax = std::numeric_limits<uint32_t>::max(),
};
diff --git a/libartbase/base/stl_util.h b/libartbase/base/stl_util.h
index 0ae4fd2..2c9547f 100644
--- a/libartbase/base/stl_util.h
+++ b/libartbase/base/stl_util.h
@@ -278,11 +278,10 @@
std::optional<RealIter> end_;
};
-template <typename Iter, typename Filter>
-static inline IterationRange<FilterIterator<Iter, Filter>> Filter(
- IterationRange<Iter> it, Filter cond) {
- auto end = it.end();
- auto start = std::find_if(it.begin(), end, cond);
+template <typename BaseRange, typename Filter>
+static inline auto Filter(BaseRange&& range, Filter cond) {
+ auto end = range.end();
+ auto start = std::find_if(range.begin(), end, cond);
return MakeIterationRange(FilterIterator(start, cond, std::make_optional(end)),
FilterIterator(end, cond, std::make_optional(end)));
}
diff --git a/libartbase/base/stride_iterator.h b/libartbase/base/stride_iterator.h
index 67c0d38..6a7e4be 100644
--- a/libartbase/base/stride_iterator.h
+++ b/libartbase/base/stride_iterator.h
@@ -30,9 +30,9 @@
typename std::iterator<std::random_access_iterator_tag, T>::difference_type;
StrideIterator(const StrideIterator&) = default;
- StrideIterator(StrideIterator&&) = default;
+ StrideIterator(StrideIterator&&) noexcept = default;
StrideIterator& operator=(const StrideIterator&) = default;
- StrideIterator& operator=(StrideIterator&&) = default;
+ StrideIterator& operator=(StrideIterator&&) noexcept = default;
StrideIterator(T* ptr, size_t stride)
: ptr_(reinterpret_cast<uintptr_t>(ptr)),
diff --git a/libartbase/base/string_view_cpp20.h b/libartbase/base/string_view_cpp20.h
index 2c11a2f..9bd29d5 100644
--- a/libartbase/base/string_view_cpp20.h
+++ b/libartbase/base/string_view_cpp20.h
@@ -21,18 +21,23 @@
namespace art {
-// Replacement functions for std::string_view::starts_with(), ends_with()
-// which shall be available in C++20.
-#if __cplusplus >= 202000L
-#error "When upgrading to C++20, remove this error and file a bug to remove this workaround."
-#endif
+// When this code is only compiled on C++20+, these wrappers can be removed and
+// calling code changed to call the string_view methods directly.
inline bool StartsWith(std::string_view sv, std::string_view prefix) {
+#if !defined(__cpp_lib_starts_ends_with) || __cpp_lib_starts_ends_with < 201711L
return sv.substr(0u, prefix.size()) == prefix;
+#else
+ return sv.starts_with(prefix);
+#endif
}
inline bool EndsWith(std::string_view sv, std::string_view suffix) {
+#if !defined(__cpp_lib_starts_ends_with) || __cpp_lib_starts_ends_with < 201711L
return sv.size() >= suffix.size() && sv.substr(sv.size() - suffix.size()) == suffix;
+#else
+ return sv.ends_with(suffix);
+#endif
}
} // namespace art
diff --git a/libartbase/base/transform_iterator.h b/libartbase/base/transform_iterator.h
index 062c88b..552f31f 100644
--- a/libartbase/base/transform_iterator.h
+++ b/libartbase/base/transform_iterator.h
@@ -160,7 +160,7 @@
}
template <typename BaseRange, typename Function>
-auto MakeTransformRange(BaseRange& range, Function f) {
+auto MakeTransformRange(BaseRange&& range, Function f) {
return MakeIterationRange(MakeTransformIterator(range.begin(), f),
MakeTransformIterator(range.end(), f));
}
diff --git a/libartbase/base/utils.cc b/libartbase/base/utils.cc
index ba62f30..bff9f45 100644
--- a/libartbase/base/utils.cc
+++ b/libartbase/base/utils.cc
@@ -50,7 +50,6 @@
#if defined(__linux__)
#include <linux/unistd.h>
#include <sys/syscall.h>
-#include <sys/utsname.h>
#endif
#if defined(_WIN32)
@@ -64,7 +63,7 @@
namespace art {
-using android::base::ReadFileToString;
+using android::base::ReadFileToString; // NOLINT - ReadFileToString is actually used
using android::base::StringPrintf;
#if defined(__arm__)
@@ -91,7 +90,7 @@
CHECK_LT(start, limit);
CHECK_EQ(RoundDown(start, kPageSize), RoundDown(limit - 1, kPageSize)) << "range spans pages";
// Declare a volatile variable so the compiler does not elide reads from the page being touched.
- volatile uint8_t v = 0;
+ [[maybe_unused]] volatile uint8_t v = 0;
for (size_t i = 0; i < attempts; ++i) {
// Touch page to maximize chance page is resident.
v = *reinterpret_cast<uint8_t*>(start);
@@ -158,6 +157,17 @@
#endif
+#if defined(__linux__)
+bool IsKernelVersionAtLeast(int reqd_major, int reqd_minor) {
+ struct utsname uts;
+ int major, minor;
+ CHECK_EQ(uname(&uts), 0);
+ CHECK_EQ(strcmp(uts.sysname, "Linux"), 0);
+ CHECK_EQ(sscanf(uts.release, "%d.%d:", &major, &minor), 2);
+ return major > reqd_major || (major == reqd_major && minor >= reqd_minor);
+}
+#endif
+
bool CacheOperationsMaySegFault() {
#if defined(__linux__) && defined(__aarch64__)
// Avoid issue on older ARM64 kernels where data cache operations could be classified as writes
@@ -167,18 +177,10 @@
//
// This behaviour means we should avoid the dual view JIT on the device. This is just
// an issue when running tests on devices that have an old kernel.
- static constexpr int kRequiredMajor = 3;
- static constexpr int kRequiredMinor = 12;
- struct utsname uts;
- int major, minor;
- if (uname(&uts) != 0 ||
- strcmp(uts.sysname, "Linux") != 0 ||
- sscanf(uts.release, "%d.%d", &major, &minor) != 2 ||
- (major < kRequiredMajor || (major == kRequiredMajor && minor < kRequiredMinor))) {
- return true;
- }
-#endif
+ return !IsKernelVersionAtLeast(3, 12);
+#else
return false;
+#endif
}
uint32_t GetTid() {
@@ -249,6 +251,9 @@
template void Split(const std::string_view& s,
char separator,
std::vector<std::string_view>* out_result);
+template void Split(const std::string_view& s,
+ char separator,
+ std::vector<std::string>* out_result);
template <typename Str>
void Split(const Str& s, char separator, size_t len, Str* out_result) {
diff --git a/libartbase/base/utils.h b/libartbase/base/utils.h
index 0e8231a..f311f09 100644
--- a/libartbase/base/utils.h
+++ b/libartbase/base/utils.h
@@ -31,6 +31,10 @@
#include "globals.h"
#include "macros.h"
+#if defined(__linux__)
+#include <sys/utsname.h>
+#endif
+
namespace art {
static inline uint32_t PointerToLowMemUInt32(const void* p) {
@@ -125,6 +129,10 @@
// Flush CPU caches. Returns true on success, false if flush failed.
WARN_UNUSED bool FlushCpuCaches(void* begin, void* end);
+#if defined(__linux__)
+bool IsKernelVersionAtLeast(int reqd_major, int reqd_minor);
+#endif
+
// On some old kernels, a cache operation may segfault.
WARN_UNUSED bool CacheOperationsMaySegFault();
@@ -158,6 +166,13 @@
}
}
+// Forces the compiler to emit a load instruction, but discards the value.
+// Useful when dealing with memory paging.
+template <typename T>
+inline void ForceRead(const T* pointer) {
+ static_cast<void>(*const_cast<volatile T*>(pointer));
+}
+
// Lookup value for a given key in /proc/self/status. Keys and values are separated by a ':' in
// the status file. Returns value found on success and "<unknown>" if the key is not found or
// there is an I/O error.
diff --git a/libartpalette/Android.bp b/libartpalette/Android.bp
index 37464f0..c488532 100644
--- a/libartpalette/Android.bp
+++ b/libartpalette/Android.bp
@@ -66,7 +66,7 @@
runtime_libs: ["libartpalette-system"],
srcs: ["apex/palette.cc"],
shared_libs: ["liblog"],
- version_script: "libartpalette.map.txt",
+ version_script: "libartpalette.map",
},
host_linux: {
header_libs: ["libbase_headers"],
@@ -77,7 +77,7 @@
"liblog",
],
},
- version_script: "libartpalette.map.txt",
+ version_script: "libartpalette.map",
},
// Targets without support for dlopen just use the sources for
// the system library which actually implements functionality.
diff --git a/libartpalette/apex/palette.cc b/libartpalette/apex/palette.cc
index 75a3878..9a50683 100644
--- a/libartpalette/apex/palette.cc
+++ b/libartpalette/apex/palette.cc
@@ -110,6 +110,8 @@
extern "C" {
+// Methods in version 1 API, corresponding to SDK level 31.
+
palette_status_t PaletteSchedSetPriority(int32_t tid, int32_t java_priority) {
PaletteSchedSetPriorityMethod m = PaletteLoader::Instance().GetPaletteSchedSetPriorityMethod();
return m(tid, java_priority);
@@ -218,6 +220,8 @@
return m(env);
}
+// Methods in version 2 API, corresponding to SDK level 33.
+
palette_status_t PaletteReportLockContention(JNIEnv* env,
int32_t wait_ms,
const char* filename,
diff --git a/libartpalette/apex/palette_test.cc b/libartpalette/apex/palette_test.cc
index 853dc29..9585d93 100644
--- a/libartpalette/apex/palette_test.cc
+++ b/libartpalette/apex/palette_test.cc
@@ -21,7 +21,7 @@
#include <sys/syscall.h>
#include <unistd.h>
-#include "common_runtime_test.h"
+#include "base/common_art_test.h"
#include "gtest/gtest.h"
namespace {
@@ -70,14 +70,35 @@
#endif
}
-class PaletteClientJniTest : public art::CommonRuntimeTest {};
+class PaletteClientJniTest : public art::CommonArtTest {};
TEST_F(PaletteClientJniTest, JniInvocation) {
bool enabled;
EXPECT_EQ(PALETTE_STATUS_OK, PaletteShouldReportJniInvocations(&enabled));
- JNIEnv* env = art::Thread::Current()->GetJniEnv();
+ std::string boot_class_path_string =
+ GetClassPathOption("-Xbootclasspath:", GetLibCoreDexFileNames());
+ std::string boot_class_path_locations_string =
+ GetClassPathOption("-Xbootclasspath-locations:", GetLibCoreDexLocations());
+
+ JavaVMOption options[] = {
+ {.optionString = boot_class_path_string.c_str(), .extraInfo = nullptr},
+ {.optionString = boot_class_path_locations_string.c_str(), .extraInfo = nullptr},
+ };
+ JavaVMInitArgs vm_args = {
+ .version = JNI_VERSION_1_6,
+ .nOptions = std::size(options),
+ .options = options,
+ .ignoreUnrecognized = JNI_TRUE,
+ };
+
+ JavaVM* jvm = nullptr;
+ JNIEnv* env = nullptr;
+ EXPECT_EQ(JNI_OK, JNI_CreateJavaVM(&jvm, &env, &vm_args));
ASSERT_NE(nullptr, env);
+
PaletteNotifyBeginJniInvocation(env);
PaletteNotifyEndJniInvocation(env);
+
+ EXPECT_EQ(JNI_OK, jvm->DestroyJavaVM());
}
diff --git a/libartpalette/include/palette/palette_method_list.h b/libartpalette/include/palette/palette_method_list.h
index 066f24f..6db389c 100644
--- a/libartpalette/include/palette/palette_method_list.h
+++ b/libartpalette/include/palette/palette_method_list.h
@@ -23,8 +23,8 @@
#include "jni.h"
-// Methods in version 1 API
#define PALETTE_METHOD_LIST(M) \
+ /* Methods in version 1 API, corresponding to SDK level 31. */ \
M(PaletteSchedSetPriority, int32_t tid, int32_t java_priority) \
M(PaletteSchedGetPriority, int32_t tid, /*out*/int32_t* java_priority) \
M(PaletteWriteCrashThreadStacks, const char* stacks, size_t stacks_len) \
@@ -53,6 +53,7 @@
M(PaletteShouldReportJniInvocations, bool*) \
M(PaletteNotifyBeginJniInvocation, JNIEnv* env) \
M(PaletteNotifyEndJniInvocation, JNIEnv* env) \
+ /* Methods in version 2 API, corresponding to SDK level 33. */ \
M(PaletteReportLockContention, JNIEnv* env, \
int32_t wait_ms, \
const char* filename, \
diff --git a/libartpalette/include/palette/palette_types.h b/libartpalette/include/palette/palette_types.h
index 905a341..3c02544 100644
--- a/libartpalette/include/palette/palette_types.h
+++ b/libartpalette/include/palette/palette_types.h
@@ -23,7 +23,7 @@
extern "C" {
#endif // __cplusplus
-typedef int32_t palette_status_t;
+using palette_status_t = int32_t;
// Palette function return value when the function completed successfully.
#define PALETTE_STATUS_OK ((palette_status_t) 0)
diff --git a/libartpalette/libartpalette.map b/libartpalette/libartpalette.map
new file mode 100644
index 0000000..be5d9b4
--- /dev/null
+++ b/libartpalette/libartpalette.map
@@ -0,0 +1,47 @@
+#
+# 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.
+#
+
+LIBARTPALETTE_1 { # introduced=31
+ global:
+ # --- VERSION 01 API ---
+ PaletteSchedSetPriority; # apex
+ PaletteSchedGetPriority; # apex
+ PaletteWriteCrashThreadStacks; # apex
+ PaletteTraceEnabled; # apex
+ PaletteTraceBegin; # apex
+ PaletteTraceEnd; # apex
+ PaletteTraceIntegerValue; # apex
+ PaletteAshmemCreateRegion; # apex
+ PaletteAshmemSetProtRegion; # apex
+ PaletteCreateOdrefreshStagingDirectory; # apex
+ PaletteShouldReportDex2oatCompilation; # apex
+ PaletteNotifyStartDex2oatCompilation; # apex
+ PaletteNotifyEndDex2oatCompilation; # apex
+ PaletteNotifyDexFileLoaded; # apex
+ PaletteNotifyOatFileLoaded; # apex
+ PaletteShouldReportJniInvocations; # apex
+ PaletteNotifyBeginJniInvocation; # apex
+ PaletteNotifyEndJniInvocation; # apex
+
+ local:
+ *;
+};
+
+LIBARTPALETTE_2 { # introduced=33
+ global:
+ # --- VERSION 02 API ---
+ PaletteReportLockContention; # apex
+} LIBARTPALETTE_1;
diff --git a/libartpalette/libartpalette.map.txt b/libartpalette/libartpalette.map.txt
deleted file mode 100644
index 6401010..0000000
--- a/libartpalette/libartpalette.map.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# 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.
-#
-
-LIBARTPALETTE_1 {
- global:
- # --- VERSION 01 API ---
- PaletteSchedSetPriority; # apex
- PaletteSchedGetPriority; # apex
- PaletteWriteCrashThreadStacks; #apex
- PaletteTraceEnabled; # apex
- PaletteTraceBegin; # apex
- PaletteTraceEnd; # apex
- PaletteTraceIntegerValue; # apex
- PaletteAshmemCreateRegion; # apex
- PaletteAshmemSetProtRegion; # apex
- PaletteCreateOdrefreshStagingDirectory; # apex
- PaletteShouldReportDex2oatCompilation; #apex
- PaletteNotifyStartDex2oatCompilation; #apex
- PaletteNotifyEndDex2oatCompilation; #apex
- PaletteNotifyDexFileLoaded; #apex
- PaletteNotifyOatFileLoaded; #apex
- PaletteShouldReportJniInvocations; #apex
- PaletteNotifyBeginJniInvocation; #apex
- PaletteNotifyEndJniInvocation; #apex
- PaletteReportLockContention; #apex
-
- local:
- *;
-};
diff --git a/libartpalette/system/palette_fake.cc b/libartpalette/system/palette_fake.cc
index bbf8f2d..e1e05c9 100644
--- a/libartpalette/system/palette_fake.cc
+++ b/libartpalette/system/palette_fake.cc
@@ -25,6 +25,8 @@
#include "palette_system.h"
+// Methods in version 1 API, corresponding to SDK level 31.
+
// Cached thread priority for testing. No thread priorities are ever affected.
static std::mutex g_tid_priority_map_mutex;
static std::map<int32_t, int32_t> g_tid_priority_map;
@@ -129,6 +131,8 @@
return PALETTE_STATUS_OK;
}
+// Methods in version 2 API, corresponding to SDK level 33.
+
palette_status_t PaletteReportLockContention(JNIEnv* env ATTRIBUTE_UNUSED,
int32_t wait_ms ATTRIBUTE_UNUSED,
const char* filename ATTRIBUTE_UNUSED,
diff --git a/libartservice/Android.bp b/libartservice/Android.bp
deleted file mode 100644
index 985a2eb..0000000
--- a/libartservice/Android.bp
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright (C) 2021 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 {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "art_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["art_license"],
-}
-
-cc_library {
- // This native library contains JNI support code for the ART Service Java
- // Language library.
-
- name: "libartservice",
- defaults: ["art_defaults"],
- host_supported: true,
- srcs: [
- "service/native/service.cc",
- ],
- export_include_dirs: ["."],
- apex_available: [
- "com.android.art",
- "com.android.art.debug",
- ],
- shared_libs: [
- "libbase",
- ],
- export_shared_lib_headers: ["libbase"],
-}
-
-// Provides the API and implementation of the ART Service class that will be
-// loaded by the System Server.
-java_sdk_library {
- // This target is named 'service-art' to conform to the naming conventions
- // for JAR files in the System Server.
- name: "service-art",
- defaults: ["framework-system-server-module-defaults"],
-
- permitted_packages: ["com.android.server.art"],
-
- visibility: [
- "//art:__subpackages__",
- "//frameworks/base/services/core",
- ],
-
- impl_library_visibility: [
- "//art/libartservice/tests",
- ],
-
- stubs_library_visibility: ["//visibility:public"],
- stubs_source_visibility: ["//visibility:private"],
-
- apex_available: [
- "com.android.art",
- "com.android.art.debug",
- ],
- sdk_version: "core_platform",
- min_sdk_version: "31",
-
- public: {
- // Override the setting of "module_current" from the defaults as that is
- // not available in all manifests where this needs to be built.
- sdk_version: "core_current",
- },
-
- system_server: {
- // Override the setting of "module_current" from the defaults as that is
- // not available in all manifests where this needs to be built.
- sdk_version: "core_current",
- },
-
- // The API elements are the ones annotated with
- // libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE)
- droiddoc_options: [
- "--show-single-annotation libcore.api.CorePlatformApi\\(status=libcore.api.CorePlatformApi.Status.STABLE\\)",
- ],
-
- // Temporarily disable compatibility with previous released APIs.
- // TODO - remove once prototype has stabilized
- // running "m update-api" will give instructions on what to do next
- unsafe_ignore_missing_latest_api: true,
-
- // This cannot be accessed by apps using <uses-library> in their manifest.
- shared_library: false,
- // TODO(b/188773212): force dex compilation for inclusion in bootclasspath_fragment.
- compile_dex: true,
-
- srcs: [
- "service/java/com/android/server/art/ArtManagerLocal.java",
- ],
-
- libs: [
- "art.module.api.annotations.for.system.modules",
- ],
-
- plugins: ["java_api_finder"],
- dist_group: "android",
-}
-
-art_cc_defaults {
- name: "art_libartservice_tests_defaults",
- srcs: [
- "service/native/service_test.cc",
- ],
- shared_libs: [
- "libbase",
- "libartservice",
- ],
-}
-
-// Version of ART gtest `art_libartservice_tests` bundled with the ART APEX on target.
-// TODO(b/192274705): Remove this module when the migration to standalone ART gtests is complete.
-art_cc_test {
- name: "art_libartservice_tests",
- defaults: [
- "art_gtest_defaults",
- "art_libartservice_tests_defaults",
- ],
-}
-
-// Standalone version of ART gtest `art_libartservice_tests`, not bundled with the ART APEX on
-// target.
-art_cc_test {
- name: "art_standalone_libartservice_tests",
- defaults: [
- "art_standalone_gtest_defaults",
- "art_libartservice_tests_defaults",
- ],
-}
diff --git a/libartservice/api/current.txt b/libartservice/api/current.txt
deleted file mode 100644
index c7844e0..0000000
--- a/libartservice/api/current.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-// Signature format: 2.0
-package com.android.server.art {
-
- public final class ArtManagerLocal {
- ctor public ArtManagerLocal();
- }
-
-}
-
diff --git a/libartservice/service/Android.bp b/libartservice/service/Android.bp
new file mode 100644
index 0000000..97f928e
--- /dev/null
+++ b/libartservice/service/Android.bp
@@ -0,0 +1,137 @@
+// Copyright (C) 2021 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// This native library contains JNI support code for the ART Service Java
+// Language library.
+cc_defaults {
+ name: "libartservice_defaults",
+ defaults: ["art_defaults"],
+ host_supported: true,
+ srcs: [
+ "native/service.cc",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+}
+
+cc_library {
+ name: "libartservice",
+ defaults: ["libartservice_defaults"],
+ apex_available: [
+ "com.android.art",
+ "com.android.art.debug",
+ ],
+ shared_libs: [
+ ],
+}
+
+cc_library {
+ name: "libartserviced",
+ defaults: [
+ "libartservice_defaults",
+ "art_debug_defaults",
+ ],
+ apex_available: [
+ "com.android.art",
+ "com.android.art.debug",
+ ],
+ shared_libs: [
+ ],
+}
+
+// Provides the API and implementation of the ART Service class that will be
+// loaded by the System Server.
+java_sdk_library {
+ // This target is named 'service-art' to conform to the naming conventions
+ // for JAR files in the System Server.
+ name: "service-art",
+ defaults: ["framework-system-server-module-defaults"],
+ permitted_packages: ["com.android.server.art"],
+ visibility: [
+ "//art:__subpackages__",
+ "//frameworks/base/services/core",
+ ],
+ apex_available: [
+ "com.android.art",
+ "com.android.art.debug",
+ ],
+ sdk_version: "system_server_current",
+ min_sdk_version: "31",
+ srcs: [
+ "java/**/*.java",
+ ],
+ static_libs: [
+ ],
+ jarjar_rules: "jarjar-rules.txt",
+}
+
+art_cc_defaults {
+ name: "art_libartservice_tests_defaults",
+ defaults: ["libartservice_defaults"],
+ srcs: [
+ "native/service_test.cc",
+ ],
+}
+
+// Version of ART gtest `art_libartservice_tests` bundled with the ART APEX on target.
+// TODO(b/192274705): Remove this module when the migration to standalone ART gtests is complete.
+art_cc_test {
+ name: "art_libartservice_tests",
+ defaults: [
+ "art_gtest_defaults",
+ "art_libartservice_tests_defaults",
+ ],
+}
+
+// Standalone version of ART gtest `art_libartservice_tests`, not bundled with the ART APEX on
+// target.
+art_cc_test {
+ name: "art_standalone_libartservice_tests",
+ defaults: [
+ "art_standalone_gtest_defaults",
+ "art_libartservice_tests_defaults",
+ ],
+}
+
+android_test {
+ name: "ArtServiceTests",
+
+ // Include all test java files.
+ srcs: [
+ "javatests/**/*.java",
+ ],
+
+ static_libs: [
+ "androidx.test.ext.junit",
+ "androidx.test.ext.truth",
+ "androidx.test.runner",
+ "mockito-target-minus-junit4",
+ "service-art.impl",
+ ],
+
+ sdk_version: "system_server_current",
+ min_sdk_version: "31",
+
+ test_suites: ["general-tests"],
+}
diff --git a/libartservice/service/AndroidManifest.xml b/libartservice/service/AndroidManifest.xml
new file mode 100644
index 0000000..921bde9
--- /dev/null
+++ b/libartservice/service/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.art.tests">
+
+ <application android:label="ArtServiceTests">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.art.tests"
+ android:label="Tests for ART Serices" />
+</manifest>
diff --git a/libartservice/api/removed.txt b/libartservice/service/api/current.txt
similarity index 100%
copy from libartservice/api/removed.txt
copy to libartservice/service/api/current.txt
diff --git a/libartservice/api/removed.txt b/libartservice/service/api/removed.txt
similarity index 100%
rename from libartservice/api/removed.txt
rename to libartservice/service/api/removed.txt
diff --git a/libartservice/api/system-server-current.txt b/libartservice/service/api/system-server-current.txt
similarity index 100%
rename from libartservice/api/system-server-current.txt
rename to libartservice/service/api/system-server-current.txt
diff --git a/libartservice/api/system-server-removed.txt b/libartservice/service/api/system-server-removed.txt
similarity index 100%
rename from libartservice/api/system-server-removed.txt
rename to libartservice/service/api/system-server-removed.txt
diff --git a/libartservice/service/jarjar-rules.txt b/libartservice/service/jarjar-rules.txt
new file mode 100644
index 0000000..c7d39e6
--- /dev/null
+++ b/libartservice/service/jarjar-rules.txt
@@ -0,0 +1,2 @@
+# Repackages static libraries to make them private to ART Services.
+rule com.android.modules.utils.** com.android.server.art.jarjar.@0
diff --git a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
index aac4b25..64aec7b 100644
--- a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
+++ b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
@@ -16,15 +16,16 @@
package com.android.server.art;
-import libcore.api.CorePlatformApi;
+import android.annotation.SystemApi;
/**
- * This class provides a system API for functionality provided by the ART
- * module.
+ * This class provides a system API for functionality provided by the ART module.
+ *
+ * @hide
*/
-@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
public final class ArtManagerLocal {
- static final String LOG_TAG = "ArtService";
+ private static final String TAG = "ArtService";
public ArtManagerLocal() {}
}
diff --git a/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
new file mode 100644
index 0000000..a27dfa5
--- /dev/null
+++ b/libartservice/service/javatests/com/android/server/art/ArtManagerLocalTest.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.art;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.art.ArtManagerLocal;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@SmallTest
+@RunWith(MockitoJUnitRunner.class)
+public class ArtManagerLocalTest {
+ private ArtManagerLocal mArtManagerLocal;
+
+ @Before
+ public void setUp() {
+ mArtManagerLocal = new ArtManagerLocal();
+ }
+
+ @Test
+ public void testScaffolding() {
+ assertThat(true).isTrue();
+ }
+}
diff --git a/libartservice/tests/Android.bp b/libartservice/tests/Android.bp
deleted file mode 100644
index dc110a1..0000000
--- a/libartservice/tests/Android.bp
+++ /dev/null
@@ -1,44 +0,0 @@
-//
-// Copyright (C) 2021 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 ArtServiceTests package
-//########################################################################
-
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "art_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["art_license"],
-}
-
-java_test {
- name: "ArtServiceTests",
-
- // Include all test java files.
- srcs: [
- "src/**/*.java",
- ],
-
- static_libs: [
- "androidx.test.runner",
- "service-art.impl",
- ],
-
- test_suites: ["general-tests"],
-}
diff --git a/libartservice/tests/src/com/android/server/art/ArtManagerLocalTests.java b/libartservice/tests/src/com/android/server/art/ArtManagerLocalTests.java
deleted file mode 100644
index 515849b..0000000
--- a/libartservice/tests/src/com/android/server/art/ArtManagerLocalTests.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.art;
-
-import static org.junit.Assert.assertTrue;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.server.art.ArtManagerLocal;
-
-import junit.framework.TestCase;
-
-public class ArtManagerLocalTests extends TestCase {
- private ArtManagerLocal mArtManagerLocal;
-
- public void setup() {
- mArtManagerLocal = new ArtManagerLocal();
- }
-
- public void testScaffolding() {
- assertTrue(true);
- }
-}
\ No newline at end of file
diff --git a/libdexfile/Android.bp b/libdexfile/Android.bp
index fdc57d0..9d9d8ce 100644
--- a/libdexfile/Android.bp
+++ b/libdexfile/Android.bp
@@ -288,14 +288,18 @@
":art-gtest-jars-GetMethodSignature",
":art-gtest-jars-Lookup",
":art-gtest-jars-Main",
+ ":art-gtest-jars-MainEmptyUncompressed",
":art-gtest-jars-MultiDex",
":art-gtest-jars-Nested",
],
header_libs: ["jni_headers"],
- shared_libs: [
- "libbacktrace",
+ static_libs: [
"libziparchive",
],
+ shared_libs: [
+ "libunwindstack",
+ "libz", // libziparchive dependency; must be repeated here since it's a static lib.
+ ],
}
// Version of ART gtest `art_libdexfile_tests` bundled with the ART APEX on target.
@@ -398,19 +402,6 @@
"art_libdexfile_external_tests_defaults",
],
- // Support multilib variants (using different suffix per sub-architecture), which is needed on
- // build targets with secondary architectures, as the CTS test suite packaging logic flattens
- // all test artifacts into a single `testcases` directory.
- compile_multilib: "both",
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "64",
- },
- },
-
test_config_template: ":art-gtests-target-standalone-cts-template",
test_suites: ["cts"], // For backed-by API coverage.
}
@@ -495,20 +486,6 @@
"art_standalone_test_defaults",
"art_libdexfile_support_tests_defaults",
],
-
- // Support multilib variants (using different suffix per sub-architecture), which is needed on
- // build targets with secondary architectures, as the MTS test suite packaging logic flattens
- // all test artifacts into a single `testcases` directory.
- compile_multilib: "both",
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "64",
- },
- },
-
test_suites: [
"mts-art",
],
diff --git a/libdexfile/art_standalone_libdexfile_tests.xml b/libdexfile/art_standalone_libdexfile_tests.xml
index 2c0e591..8e3ef2d 100644
--- a/libdexfile/art_standalone_libdexfile_tests.xml
+++ b/libdexfile/art_standalone_libdexfile_tests.xml
@@ -14,6 +14,7 @@
limitations under the License.
-->
<configuration description="Runs art_standalone_libdexfile_tests.">
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.art.apex" />
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="art_standalone_libdexfile_tests->/data/local/tmp/art_standalone_libdexfile_tests/art_standalone_libdexfile_tests" />
@@ -25,6 +26,7 @@
<option name="push" value="art-gtest-jars-GetMethodSignature.jar->/data/local/tmp/art_standalone_libdexfile_tests/art-gtest-jars-GetMethodSignature.jar" />
<option name="push" value="art-gtest-jars-Lookup.jar->/data/local/tmp/art_standalone_libdexfile_tests/art-gtest-jars-Lookup.jar" />
<option name="push" value="art-gtest-jars-Main.jar->/data/local/tmp/art_standalone_libdexfile_tests/art-gtest-jars-Main.jar" />
+ <option name="push" value="art-gtest-jars-MainEmptyUncompressed.jar->/data/local/tmp/art_standalone_libdexfile_tests/art-gtest-jars-MainEmptyUncompressed.jar" />
<option name="push" value="art-gtest-jars-MultiDex.jar->/data/local/tmp/art_standalone_libdexfile_tests/art-gtest-jars-MultiDex.jar" />
<option name="push" value="art-gtest-jars-Nested.jar->/data/local/tmp/art_standalone_libdexfile_tests/art-gtest-jars-Nested.jar" />
</target_preparer>
diff --git a/libdexfile/dex/art_dex_file_loader.cc b/libdexfile/dex/art_dex_file_loader.cc
index a7dd13e..1057bbc 100644
--- a/libdexfile/dex/art_dex_file_loader.cc
+++ b/libdexfile/dex/art_dex_file_loader.cc
@@ -19,9 +19,9 @@
#include <sys/stat.h>
#include "android-base/stringprintf.h"
-
#include "base/file_magic.h"
#include "base/file_utils.h"
+#include "base/logging.h"
#include "base/mem_map.h"
#include "base/mman.h" // For the PROT_* and MAP_* constants.
#include "base/stl_util.h"
@@ -114,18 +114,22 @@
return false;
}
+ if (zip_file_only_contains_uncompressed_dex != nullptr) {
+ // Start by assuming everything is uncompressed.
+ *zip_file_only_contains_uncompressed_dex = true;
+ }
+
uint32_t idx = 0;
std::string zip_entry_name = GetMultiDexClassesDexName(idx);
std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name.c_str(), error_msg));
if (zip_entry.get() == nullptr) {
- *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", filename,
- zip_entry_name.c_str(), error_msg->c_str());
- return false;
- }
-
- if (zip_file_only_contains_uncompressed_dex != nullptr) {
- // Start by assuming everything is uncompressed.
- *zip_file_only_contains_uncompressed_dex = true;
+ // A zip file with no dex code should be accepted. It's likely a config split APK, which we
+ // are currently passing from higher levels.
+ VLOG(dex) << StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)",
+ filename,
+ zip_entry_name.c_str(),
+ error_msg->c_str());
+ return true;
}
do {
diff --git a/libdexfile/dex/art_dex_file_loader_test.cc b/libdexfile/dex/art_dex_file_loader_test.cc
index 1863c1b..bf1d306 100644
--- a/libdexfile/dex/art_dex_file_loader_test.cc
+++ b/libdexfile/dex/art_dex_file_loader_test.cc
@@ -103,6 +103,20 @@
EXPECT_EQ(dexes[1]->GetLocationChecksum(), checksums[1]);
}
+TEST_F(ArtDexFileLoaderTest, GetMultiDexChecksumsEmptyZip) {
+ std::string error_msg;
+ std::vector<uint32_t> checksums;
+ std::vector<std::string> dex_locations;
+ std::string multidex_file = GetTestDexFileName("MainEmptyUncompressed");
+ const ArtDexFileLoader dex_file_loader;
+ EXPECT_TRUE(dex_file_loader.GetMultiDexChecksums(
+ multidex_file.c_str(), &checksums, &dex_locations, &error_msg))
+ << error_msg;
+
+ EXPECT_EQ(dex_locations.size(), 0);
+ EXPECT_EQ(checksums.size(), 0);
+}
+
TEST_F(ArtDexFileLoaderTest, ClassDefs) {
std::unique_ptr<const DexFile> raw(OpenTestDexFile("Nested"));
ASSERT_TRUE(raw.get() != nullptr);
diff --git a/libdexfile/dex/compact_dex_file.cc b/libdexfile/dex/compact_dex_file.cc
index a5044aa..205b829 100644
--- a/libdexfile/dex/compact_dex_file.cc
+++ b/libdexfile/dex/compact_dex_file.cc
@@ -22,9 +22,6 @@
namespace art {
-constexpr uint8_t CompactDexFile::kDexMagic[kDexMagicSize];
-constexpr uint8_t CompactDexFile::kDexMagicVersion[];
-
void CompactDexFile::WriteMagic(uint8_t* magic) {
std::copy_n(kDexMagic, kDexMagicSize, magic);
}
diff --git a/libdexfile/dex/compact_offset_table.cc b/libdexfile/dex/compact_offset_table.cc
index 8601b19..deec124 100644
--- a/libdexfile/dex/compact_offset_table.cc
+++ b/libdexfile/dex/compact_offset_table.cc
@@ -21,8 +21,6 @@
namespace art {
-constexpr size_t CompactOffsetTable::kElementsPerIndex;
-
CompactOffsetTable::Accessor::Accessor(const uint8_t* data_begin,
uint32_t minimum_offset,
uint32_t table_offset)
diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc
index dc5f27e..c9088ed 100644
--- a/libdexfile/dex/dex_file.cc
+++ b/libdexfile/dex/dex_file.cc
@@ -132,6 +132,11 @@
// any of the sections via a pointer.
CHECK_ALIGNED(begin_, alignof(Header));
+ if (DataSize() < sizeof(Header)) {
+ // Don't go further if the data doesn't even contain a header.
+ return;
+ }
+
InitializeSectionsFromMapList();
}
@@ -174,11 +179,13 @@
}
void DexFile::InitializeSectionsFromMapList() {
- const MapList* map_list = reinterpret_cast<const MapList*>(DataBegin() + header_->map_off_);
- if (header_->map_off_ == 0 || header_->map_off_ > DataSize()) {
+ static_assert(sizeof(MapList) <= sizeof(Header));
+ DCHECK_GE(DataSize(), sizeof(MapList));
+ if (header_->map_off_ == 0 || header_->map_off_ > DataSize() - sizeof(MapList)) {
// Bad offset. The dex file verifier runs after this method and will reject the file.
return;
}
+ const MapList* map_list = reinterpret_cast<const MapList*>(DataBegin() + header_->map_off_);
const size_t count = map_list->size_;
size_t map_limit = header_->map_off_ + count * sizeof(MapItem);
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index cc5d2fd..37a601d 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -570,7 +570,7 @@
};
// Callback for "new locals table entry".
- typedef void (*DexDebugNewLocalCb)(void* context, const LocalInfo& entry);
+ using DexDebugNewLocalCb = void (*)(void* context, const LocalInfo& entry);
const dex::AnnotationsDirectoryItem* GetAnnotationsDirectory(const dex::ClassDef& class_def)
const {
diff --git a/libdexfile/dex/dex_file_types.h b/libdexfile/dex/dex_file_types.h
index ecc0482..bf67187 100644
--- a/libdexfile/dex/dex_file_types.h
+++ b/libdexfile/dex/dex_file_types.h
@@ -17,6 +17,7 @@
#ifndef ART_LIBDEXFILE_DEX_DEX_FILE_TYPES_H_
#define ART_LIBDEXFILE_DEX_DEX_FILE_TYPES_H_
+#include <functional>
#include <iosfwd>
#include <limits>
#include <utility>
diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc
index f72528f..ec415ac 100644
--- a/libdexfile/dex/dex_file_verifier.cc
+++ b/libdexfile/dex/dex_file_verifier.cc
@@ -3470,7 +3470,7 @@
return false;
}
- // Flags allowed on fields, in general. Other lower-16-bit flags are to be ignored.
+ // Flags allowed on methods, in general. Other lower-16-bit flags are to be ignored.
constexpr uint32_t kMethodAccessFlags = kAccPublic |
kAccPrivate |
kAccProtected |
diff --git a/libdexfile/dex/dex_instruction.h b/libdexfile/dex/dex_instruction.h
index 7e43f75..ff6fcf7 100644
--- a/libdexfile/dex/dex_instruction.h
+++ b/libdexfile/dex/dex_instruction.h
@@ -22,8 +22,8 @@
#include "base/globals.h"
#include "base/macros.h"
-typedef uint8_t uint4_t;
-typedef int8_t int4_t;
+using uint4_t = uint8_t;
+using int4_t = int8_t;
namespace art {
diff --git a/libdexfile/dex/modifiers.h b/libdexfile/dex/modifiers.h
index 72949b0..a17545c 100644
--- a/libdexfile/dex/modifiers.h
+++ b/libdexfile/dex/modifiers.h
@@ -56,6 +56,9 @@
// Used by a class to denote that this class and any objects with this as a
// declaring-class/super-class are to be considered obsolete, meaning they should not be used by.
static constexpr uint32_t kAccObsoleteObject = 0x00200000; // class (runtime)
+// Set during boot image compilation to indicate that the class is
+// not initialized at compile tile and not in the list of preloaded classes.
+static constexpr uint32_t kAccInBootImageAndNotInPreloadedClasses = 0x00400000; // class (runtime)
// This is set by the class linker during LinkInterfaceMethods. It is used by a method
// to represent that it was copied from its declaring class into another class.
// We need copies of the original method because the method may end up in different
diff --git a/libdexfile/dex/utf.cc b/libdexfile/dex/utf.cc
index bfc704d..9692a26 100644
--- a/libdexfile/dex/utf.cc
+++ b/libdexfile/dex/utf.cc
@@ -133,45 +133,11 @@
}
// String contains non-ASCII characters.
- while (char_count--) {
- const uint16_t ch = *utf16_in++;
- if (ch > 0 && ch <= 0x7f) {
- *utf8_out++ = ch;
- } else {
- // Char_count == 0 here implies we've encountered an unpaired
- // surrogate and we have no choice but to encode it as 3-byte UTF
- // sequence. Note that unpaired surrogates can occur as a part of
- // "normal" operation.
- if ((ch >= 0xd800 && ch <= 0xdbff) && (char_count > 0)) {
- const uint16_t ch2 = *utf16_in;
-
- // Check if the other half of the pair is within the expected
- // range. If it isn't, we will have to emit both "halves" as
- // separate 3 byte sequences.
- if (ch2 >= 0xdc00 && ch2 <= 0xdfff) {
- utf16_in++;
- char_count--;
- const uint32_t code_point = (ch << 10) + ch2 - 0x035fdc00;
- *utf8_out++ = (code_point >> 18) | 0xf0;
- *utf8_out++ = ((code_point >> 12) & 0x3f) | 0x80;
- *utf8_out++ = ((code_point >> 6) & 0x3f) | 0x80;
- *utf8_out++ = (code_point & 0x3f) | 0x80;
- continue;
- }
- }
-
- if (ch > 0x07ff) {
- // Three byte encoding.
- *utf8_out++ = (ch >> 12) | 0xe0;
- *utf8_out++ = ((ch >> 6) & 0x3f) | 0x80;
- *utf8_out++ = (ch & 0x3f) | 0x80;
- } else /*(ch > 0x7f || ch == 0)*/ {
- // Two byte encoding.
- *utf8_out++ = (ch >> 6) | 0xc0;
- *utf8_out++ = (ch & 0x3f) | 0x80;
- }
- }
- }
+ // FIXME: We should not emit 4-byte sequences. Bug: 192935764
+ auto append = [&](char c) { *utf8_out++ = c; };
+ ConvertUtf16ToUtf8</*kUseShortZero=*/ false,
+ /*kUse4ByteSequence=*/ true,
+ /*kReplaceBadSurrogates=*/ false>(utf16_in, char_count, append);
}
int32_t ComputeUtf16HashFromModifiedUtf8(const char* utf8, size_t utf16_length) {
@@ -240,34 +206,13 @@
}
}
-size_t CountUtf8Bytes(const uint16_t* chars, size_t char_count) {
+size_t CountModifiedUtf8BytesInUtf16(const uint16_t* chars, size_t char_count) {
+ // FIXME: We should not emit 4-byte sequences. Bug: 192935764
size_t result = 0;
- const uint16_t *end = chars + char_count;
- while (chars < end) {
- const uint16_t ch = *chars++;
- if (LIKELY(ch != 0 && ch < 0x80)) {
- result++;
- continue;
- }
- if (ch < 0x800) {
- result += 2;
- continue;
- }
- if (ch >= 0xd800 && ch < 0xdc00) {
- if (chars < end) {
- const uint16_t ch2 = *chars;
- // If we find a properly paired surrogate, we emit it as a 4 byte
- // UTF sequence. If we find an unpaired leading or trailing surrogate,
- // we emit it as a 3 byte sequence like would have done earlier.
- if (ch2 >= 0xdc00 && ch2 < 0xe000) {
- chars++;
- result += 4;
- continue;
- }
- }
- }
- result += 3;
- }
+ auto append = [&](char c ATTRIBUTE_UNUSED) { ++result; };
+ ConvertUtf16ToUtf8</*kUseShortZero=*/ false,
+ /*kUse4ByteSequence=*/ true,
+ /*kReplaceBadSurrogates=*/ false>(chars, char_count, append);
return result;
}
diff --git a/libdexfile/dex/utf.h b/libdexfile/dex/utf.h
index 35cbf78..d372bff 100644
--- a/libdexfile/dex/utf.h
+++ b/libdexfile/dex/utf.h
@@ -41,12 +41,6 @@
size_t CountModifiedUtf8Chars(const char* utf8, size_t byte_count);
/*
- * Returns the number of modified UTF-8 bytes needed to represent the given
- * UTF-16 string.
- */
-size_t CountUtf8Bytes(const uint16_t* chars, size_t char_count);
-
-/*
* Convert from Modified UTF-8 to UTF-16.
*/
void ConvertModifiedUtf8ToUtf16(uint16_t* utf16_out, const char* utf8_in);
@@ -85,8 +79,14 @@
void ConvertUtf16ToUtf8(const uint16_t* utf16, size_t char_count, Append&& append);
/*
+ * Returns the number of modified UTF-8 bytes needed to represent the given
+ * UTF-16 string.
+ */
+size_t CountModifiedUtf8BytesInUtf16(const uint16_t* chars, size_t char_count);
+
+/*
* Convert from UTF-16 to Modified UTF-8. Note that the output is _not_
- * NUL-terminated. You probably need to call CountUtf8Bytes before calling
+ * NUL-terminated. You probably need to call CountModifiedUtf8BytesInUtf16 before calling
* this anyway, so if you want a NUL-terminated string, you know where to
* put the NUL byte.
*/
diff --git a/libdexfile/dex/utf_test.cc b/libdexfile/dex/utf_test.cc
index 919259e..85c74d2 100644
--- a/libdexfile/dex/utf_test.cc
+++ b/libdexfile/dex/utf_test.cc
@@ -117,7 +117,7 @@
static void AssertConversion(const std::vector<uint16_t>& input,
const std::vector<uint8_t>& expected) {
- ASSERT_EQ(expected.size(), CountUtf8Bytes(&input[0], input.size()));
+ ASSERT_EQ(expected.size(), CountModifiedUtf8BytesInUtf16(&input[0], input.size()));
std::vector<uint8_t> output(expected.size());
ConvertUtf16ToModifiedUtf8(reinterpret_cast<char*>(&output[0]), expected.size(),
@@ -229,7 +229,7 @@
return len;
}
-static size_t CountUtf8Bytes_reference(const uint16_t* chars, size_t char_count) {
+static size_t CountModifiedUtf8BytesInUtf16_reference(const uint16_t* chars, size_t char_count) {
size_t result = 0;
while (char_count--) {
const uint16_t ch = *chars++;
@@ -320,8 +320,8 @@
int char_count_test, char_count_reference;
// Calculate the number of utf-8 bytes for the utf-16 chars.
- byte_count_reference = CountUtf8Bytes_reference(buf, char_count);
- byte_count_test = CountUtf8Bytes(buf, char_count);
+ byte_count_reference = CountModifiedUtf8BytesInUtf16_reference(buf, char_count);
+ byte_count_test = CountModifiedUtf8BytesInUtf16(buf, char_count);
EXPECT_EQ(byte_count_reference, byte_count_test);
// Convert the utf-16 string to utf-8 bytes.
diff --git a/libdexfile/external/include/art_api/dex_file_external.h b/libdexfile/external/include/art_api/dex_file_external.h
index 360be92..d9db200 100644
--- a/libdexfile/external/include/art_api/dex_file_external.h
+++ b/libdexfile/external/include/art_api/dex_file_external.h
@@ -28,10 +28,10 @@
// may only be added here. C++ users should use dex_file_support.h instead.
struct ADexFile;
-typedef struct ADexFile ADexFile;
+typedef struct ADexFile ADexFile; // NOLINT
struct ADexFile_Method;
-typedef struct ADexFile_Method ADexFile_Method;
+typedef struct ADexFile_Method ADexFile_Method; // NOLINT
enum ADexFile_Error : uint32_t {
ADEXFILE_ERROR_OK = 0,
@@ -39,10 +39,11 @@
ADEXFILE_ERROR_INVALID_HEADER = 2,
ADEXFILE_ERROR_NOT_ENOUGH_DATA = 3,
};
-typedef enum ADexFile_Error ADexFile_Error;
+typedef enum ADexFile_Error ADexFile_Error; // NOLINT
// Callback used to return information about a dex method.
// The method information is valid only during the callback.
+// NOLINTNEXTLINE
typedef void ADexFile_MethodCallback(void* _Nullable callback_data,
const ADexFile_Method* _Nonnull method);
diff --git a/libelffile/dwarf/register.h b/libelffile/dwarf/register.h
index 7742ec4..33c6795 100644
--- a/libelffile/dwarf/register.h
+++ b/libelffile/dwarf/register.h
@@ -40,6 +40,8 @@
static Reg ArmDp(int num) { return Reg(256 + num); } // D0–D31.
static Reg Arm64Core(int num) { return Reg(num); } // X0-X31.
static Reg Arm64Fp(int num) { return Reg(64 + num); } // V0-V31.
+ static Reg Riscv64Core(int num) { return Reg(num); } // X0-X31
+ static Reg Riscv64Fp(int num) { return Reg(32 + num); } // F0-F31
static Reg X86Core(int num) { return Reg(num); }
static Reg X86Fp(int num) { return Reg(21 + num); }
static Reg X86_64Core(int num) {
diff --git a/libelffile/elf/elf_builder.h b/libelffile/elf/elf_builder.h
index 086bd41..6720569 100644
--- a/libelffile/elf/elf_builder.h
+++ b/libelffile/elf/elf_builder.h
@@ -814,6 +814,8 @@
return InstructionSet::kThumb2;
case EM_AARCH64:
return InstructionSet::kArm64;
+ case EM_RISCV:
+ return InstructionSet::kRiscv64;
case EM_386:
return InstructionSet::kX86;
case EM_X86_64:
@@ -839,6 +841,11 @@
elf_header.e_flags = 0;
break;
}
+ case InstructionSet::kRiscv64: {
+ elf_header.e_machine = EM_RISCV;
+ elf_header.e_flags = EF_RISCV_RVC | EF_RISCV_FLOAT_ABI_DOUBLE;
+ break;
+ }
case InstructionSet::kX86: {
elf_header.e_machine = EM_386;
elf_header.e_flags = 0;
diff --git a/libelffile/elf/elf_debug_reader.h b/libelffile/elf/elf_debug_reader.h
index 266c638..6e01989 100644
--- a/libelffile/elf/elf_debug_reader.h
+++ b/libelffile/elf/elf_debug_reader.h
@@ -35,11 +35,11 @@
class ElfDebugReader {
public:
// Note that the input buffer might be misaligned.
- typedef typename ElfTypes::Ehdr ALIGNED(1) Elf_Ehdr;
- typedef typename ElfTypes::Phdr ALIGNED(1) Elf_Phdr;
- typedef typename ElfTypes::Shdr ALIGNED(1) Elf_Shdr;
- typedef typename ElfTypes::Sym ALIGNED(1) Elf_Sym;
- typedef typename ElfTypes::Addr ALIGNED(1) Elf_Addr;
+ using Elf_Ehdr ALIGNED(1) = typename ElfTypes::Ehdr;
+ using Elf_Phdr ALIGNED(1) = typename ElfTypes::Phdr;
+ using Elf_Shdr ALIGNED(1) = typename ElfTypes::Shdr;
+ using Elf_Sym ALIGNED(1) = typename ElfTypes::Sym;
+ using Elf_Addr ALIGNED(1) = typename ElfTypes::Addr;
// Call Frame Information.
struct CFI {
diff --git a/libelffile/elf/elf_utils.h b/libelffile/elf/elf_utils.h
index 9c4f0d81..46b25b0 100644
--- a/libelffile/elf/elf_utils.h
+++ b/libelffile/elf/elf_utils.h
@@ -65,10 +65,21 @@
#define EI_ABIVERSION 8
#define EM_ARM 40
+#if !defined(STV_DEFAULT)
#define STV_DEFAULT 0
+#endif
#define EM_AARCH64 183
+#ifndef EM_RISCV
+#define EM_RISCV 243
+#endif
+
+#ifndef EF_RISCV_RVC
+#define EF_RISCV_RVC 0x1
+#define EF_RISCV_FLOAT_ABI_DOUBLE 0x4
+#endif
+
#define DT_BIND_NOW 24
#define DT_INIT_ARRAY 25
#define DT_FINI_ARRAY 26
diff --git a/libelffile/stream/error_delaying_output_stream.h b/libelffile/stream/error_delaying_output_stream.h
index b37ff4e..7553de6 100644
--- a/libelffile/stream/error_delaying_output_stream.h
+++ b/libelffile/stream/error_delaying_output_stream.h
@@ -71,8 +71,9 @@
PLOG(ERROR) << "Failed to seek in " << GetLocation() << ". Offset=" << offset
<< " whence=" << whence << " new_offset=" << new_offset;
output_good_ = false;
+ } else {
+ DCHECK_EQ(actual_offset, new_offset);
}
- DCHECK_EQ(actual_offset, new_offset);
}
output_offset_ = new_offset;
return new_offset;
diff --git a/libnativebridge/OWNERS b/libnativebridge/OWNERS
index daf87f4..71f76a6 100644
--- a/libnativebridge/OWNERS
+++ b/libnativebridge/OWNERS
@@ -1,3 +1,5 @@
+# Bug component: 86431
+mast@google.com
dimitry@google.com
eaeltsin@google.com
ngeoffray@google.com
diff --git a/libnativebridge/include/nativebridge/native_bridge.h b/libnativebridge/include/nativebridge/native_bridge.h
index 2199bab..5904c0f 100644
--- a/libnativebridge/include/nativebridge/native_bridge.h
+++ b/libnativebridge/include/nativebridge/native_bridge.h
@@ -40,7 +40,7 @@
// Function pointer type for sigaction. This is mostly the signature of a signal handler, except
// for the return type. The runtime needs to know whether the signal was handled or should be given
// to the chain.
-typedef bool (*NativeBridgeSignalHandlerFn)(int, siginfo_t*, void*);
+typedef bool (*NativeBridgeSignalHandlerFn)(int, siginfo_t*, void*); // NOLINT
// Open the native bridge, if any. Should be called by Runtime::Init(). A null library filename
// signals that we do not want to load a native bridge.
diff --git a/libnativebridge/libnativebridge.map.txt b/libnativebridge/libnativebridge.map.txt
index 8b51ab8..52d06eb 100644
--- a/libnativebridge/libnativebridge.map.txt
+++ b/libnativebridge/libnativebridge.map.txt
@@ -20,12 +20,12 @@
# that defines the exported interface. Please keep in sync with this list.
LIBNATIVEBRIDGE_1 {
global:
- NativeBridgeGetError;
- NativeBridgeInitialized;
- NativeBridgeGetTrampoline;
- PreInitializeNativeBridge;
- NativeBridgeAvailable;
- NeedsNativeBridge;
+ NativeBridgeGetError; # apex
+ NativeBridgeInitialized; # apex
+ NativeBridgeGetTrampoline; # apex
+ PreInitializeNativeBridge; # apex
+ NativeBridgeAvailable; # apex
+ NeedsNativeBridge; # apex
local:
*;
};
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
index 57d26c0..603f97a 100644
--- a/libnativebridge/tests/Android.bp
+++ b/libnativebridge/tests/Android.bp
@@ -180,20 +180,6 @@
],
header_libs: ["libbase_headers"],
- // Support multilib variants (using different suffix per sub-architecture),
- // which is needed on build targets with secondary architectures, as the CTS
- // test suite packaging logic flattens all test artifacts into a single
- // `testcases` directory.
- compile_multilib: "both",
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "64",
- },
- },
-
test_config_template: ":art-gtests-target-standalone-cts-template",
test_suites: [
"cts",
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index e14aea2..f1ef32d 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -64,7 +64,7 @@
"libdl_android",
],
static_libs: [
- "PlatformProperties",
+ "libPlatformProperties",
],
},
},
@@ -164,19 +164,6 @@
"libnativeloader",
],
- // Support multilib variants (using different suffix per sub-architecture), which is needed on
- // build targets with secondary architectures, as the CTS test suite packaging logic flattens
- // all test artifacts into a single `testcases` directory.
- compile_multilib: "both",
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "64",
- },
- },
-
// Added to CTS for API coverage of libnativeloader which is backed by the
// ART module.
test_config_template: ":art-gtests-target-standalone-cts-template",
diff --git a/libnativeloader/OWNERS b/libnativeloader/OWNERS
index f735653..87f9142 100644
--- a/libnativeloader/OWNERS
+++ b/libnativeloader/OWNERS
@@ -1,6 +1,6 @@
-dimitry@google.com
+# Bug component: 86431
+mast@google.com
jiyong@google.com
ngeoffray@google.com
oth@google.com
-mast@google.com
rpl@google.com
diff --git a/libnativeloader/README.md b/libnativeloader/README.md
index c5ace61..919feff 100644
--- a/libnativeloader/README.md
+++ b/libnativeloader/README.md
@@ -10,11 +10,11 @@
The most typical use case of this library is calling `System.loadLibrary(name)`.
When the method is called, the ART runtime delegates the call to this library
-along with the reference to the classloader where the call was made. Then this
-library finds the linker namespace (named `classloader-namespace`) that is
-associated with the given classloader, and tries to load the requested library
-from the namespace. The actual searching, loading, and linking of the library
-is performed by the dynamic linker.
+along with the reference to the classloader where the call was made. Then this
+library finds the linker namespace (typically with the name `clns-` followed by
+a number to make it unique) that is associated with the given classloader, and
+tries to load the requested library from that namespace. The actual searching,
+loading, and linking of the library is performed by the dynamic linker.
The linker namespace is created when an APK is loaded into the process, and is
associated with the classloader that loaded the APK. The linker namespace is
diff --git a/libnativeloader/libnativeloader.map.txt b/libnativeloader/libnativeloader.map.txt
index 59f457c..8c0fbdd 100644
--- a/libnativeloader/libnativeloader.map.txt
+++ b/libnativeloader/libnativeloader.map.txt
@@ -20,13 +20,13 @@
# that defines the exported interface. Please keep in sync with this list.
LIBNATIVELOADER_1 {
global:
- OpenNativeLibrary;
- CloseNativeLibrary;
- OpenNativeLibraryInNamespace;
- FindNamespaceByClassLoader;
- FindNativeLoaderNamespaceByClassLoader;
- CreateClassLoaderNamespace;
- NativeLoaderFreeErrorMessage;
+ OpenNativeLibrary; # apex
+ CloseNativeLibrary; # apex
+ OpenNativeLibraryInNamespace; # apex
+ FindNamespaceByClassLoader; # apex
+ FindNativeLoaderNamespaceByClassLoader; # apex
+ CreateClassLoaderNamespace; # apex
+ NativeLoaderFreeErrorMessage; # apex
local:
*;
};
diff --git a/libnativeloader/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp
index f31c430..9aeebf3 100644
--- a/libnativeloader/library_namespaces.cpp
+++ b/libnativeloader/library_namespaces.cpp
@@ -30,6 +30,7 @@
#include <android-base/macros.h>
#include <android-base/result.h>
#include <android-base/strings.h>
+#include <android-base/stringprintf.h>
#include <nativehelper/scoped_utf_chars.h>
#include "nativeloader/dlext_namespaces.h"
@@ -55,24 +56,26 @@
// vndk_product namespace for unbundled product apps
constexpr const char* kVndkProductNamespaceName = "vndk_product";
-// classloader-namespace is a linker namespace that is created for the loaded
-// app. To be specific, it is created for the app classloader. When
-// System.load() is called from a Java class that is loaded from the
-// classloader, the classloader-namespace namespace associated with that
-// classloader is selected for dlopen. The namespace is configured so that its
-// search path is set to the app-local JNI directory and it is linked to the
-// system namespace with the names of libs listed in the public.libraries.txt.
-// This way an app can only load its own JNI libraries along with the public libs.
-constexpr const char* kClassloaderNamespaceName = "classloader-namespace";
-// Same thing for vendor APKs.
-constexpr const char* kVendorClassloaderNamespaceName = "vendor-classloader-namespace";
-// If the namespace is shared then add this suffix to form
-// "classloader-namespace-shared" or "vendor-classloader-namespace-shared",
-// respectively. A shared namespace (cf. ANDROID_NAMESPACE_TYPE_SHARED) has
+// clns-XX is a linker namespace that is created for normal apps installed in
+// the data partition. To be specific, it is created for the app classloader.
+// When System.load() is called from a Java class that is loaded from the
+// classloader, the clns namespace associated with that classloader is selected
+// for dlopen. The namespace is configured so that its search path is set to the
+// app-local JNI directory and it is linked to the system namespace with the
+// names of libs listed in the public.libraries.txt and other public libraries.
+// This way an app can only load its own JNI libraries along with the public
+// libs.
+constexpr const char* kClassloaderNamespaceName = "clns";
+// Same thing for unbundled APKs in the vendor partition.
+constexpr const char* kVendorClassloaderNamespaceName = "vendor-clns";
+// Same thing for unbundled APKs in the product partition.
+constexpr const char* kProductClassloaderNamespaceName = "product-clns";
+// If the namespace is shared then add this suffix to help identify it in debug
+// messages. A shared namespace (cf. ANDROID_NAMESPACE_TYPE_SHARED) has
// inherited all the libraries of the parent classloader namespace, or the
-// system namespace for the main app classloader. It is used to give full
-// access to the platform libraries for apps bundled in the system image,
-// including their later updates installed in /data.
+// system namespace for the main app classloader. It is used to give full access
+// to the platform libraries for apps bundled in the system image, including
+// their later updates installed in /data.
constexpr const char* kSharedNamespaceSuffix = "-shared";
// (http://b/27588281) This is a workaround for apps using custom classloaders and calling
@@ -81,16 +84,19 @@
constexpr const char* kAlwaysPermittedDirectories = "/data:/mnt/expand";
constexpr const char* kVendorLibPath = "/vendor/" LIB;
+// TODO(mast): It's unlikely that both paths are necessary for kProductLibPath
+// below, because they can't be two separate directories - either one has to be
+// a symlink to the other.
constexpr const char* kProductLibPath = "/product/" LIB ":/system/product/" LIB;
-const std::regex kVendorDexPathRegex("(^|:)/vendor/");
+const std::regex kVendorDexPathRegex("(^|:)(/system)?/vendor/");
const std::regex kProductDexPathRegex("(^|:)(/system)?/product/");
-// Define origin of APK if it is from vendor partition or product partition
+// Define origin partition of APK
using ApkOrigin = enum {
APK_ORIGIN_DEFAULT = 0,
- APK_ORIGIN_VENDOR = 1,
- APK_ORIGIN_PRODUCT = 2,
+ APK_ORIGIN_VENDOR = 1, // Includes both /vendor and /system/vendor
+ APK_ORIGIN_PRODUCT = 2, // Includes both /product and /system/product
};
jobject GetParentClassLoader(JNIEnv* env, jobject class_loader) {
@@ -231,51 +237,38 @@
std::string system_exposed_libraries = default_public_libraries();
std::string namespace_name = kClassloaderNamespaceName;
ApkOrigin unbundled_app_origin = APK_ORIGIN_DEFAULT;
- if ((apk_origin == APK_ORIGIN_VENDOR ||
- (apk_origin == APK_ORIGIN_PRODUCT &&
- is_product_vndk_version_defined())) &&
- !is_shared) {
- unbundled_app_origin = apk_origin;
- // For vendor / product apks, give access to the vendor / product lib even though
- // they are treated as unbundled; the libs and apks are still bundled
- // together in the vendor / product partition.
- const char* origin_partition;
- const char* origin_lib_path;
- const char* llndk_libraries;
+ const char* apk_origin_msg = "other apk"; // Only for debug logging.
- switch (apk_origin) {
- case APK_ORIGIN_VENDOR:
- origin_partition = "vendor";
- origin_lib_path = kVendorLibPath;
- llndk_libraries = llndk_libraries_vendor().c_str();
- break;
- case APK_ORIGIN_PRODUCT:
- origin_partition = "product";
- origin_lib_path = kProductLibPath;
- llndk_libraries = llndk_libraries_product().c_str();
- break;
- default:
- origin_partition = "unknown";
- origin_lib_path = "";
- llndk_libraries = "";
- }
- library_path = library_path + ":" + origin_lib_path;
- permitted_path = permitted_path + ":" + origin_lib_path;
+ if (!is_shared) {
+ if (apk_origin == APK_ORIGIN_VENDOR) {
+ unbundled_app_origin = APK_ORIGIN_VENDOR;
+ apk_origin_msg = "unbundled vendor apk";
- // Also give access to LLNDK libraries since they are available to vendor or product
- system_exposed_libraries = system_exposed_libraries + ":" + llndk_libraries;
+ // For vendor apks, give access to the vendor libs even though they are
+ // treated as unbundled; the libs and apks are still bundled together in the
+ // vendor partition.
+ library_path = library_path + ':' + kVendorLibPath;
+ permitted_path = permitted_path + ':' + kVendorLibPath;
- // Different name is useful for debugging
- namespace_name = kVendorClassloaderNamespaceName;
- ALOGD("classloader namespace configured for unbundled %s apk. library_path=%s",
- origin_partition, library_path.c_str());
- } else {
- auto libs = filter_public_libraries(target_sdk_version, uses_libraries,
- extended_public_libraries());
- // extended public libraries are NOT available to vendor apks, otherwise it
- // would be system->vendor violation.
- if (!libs.empty()) {
- system_exposed_libraries = system_exposed_libraries + ':' + libs;
+ // Also give access to LLNDK libraries since they are available to vendor.
+ system_exposed_libraries = system_exposed_libraries + ':' + llndk_libraries_vendor();
+
+ // Different name is useful for debugging
+ namespace_name = kVendorClassloaderNamespaceName;
+ } else if (apk_origin == APK_ORIGIN_PRODUCT && is_product_vndk_version_defined()) {
+ unbundled_app_origin = APK_ORIGIN_PRODUCT;
+ apk_origin_msg = "unbundled product apk";
+
+ // Like for vendor apks, give access to the product libs since they are
+ // bundled together in the same partition.
+ library_path = library_path + ':' + kProductLibPath;
+ permitted_path = permitted_path + ':' + kProductLibPath;
+
+ // Also give access to LLNDK libraries since they are available to product.
+ system_exposed_libraries = system_exposed_libraries + ':' + llndk_libraries_product();
+
+ // Different name is useful for debugging
+ namespace_name = kProductClassloaderNamespaceName;
}
}
@@ -285,6 +278,36 @@
namespace_name = namespace_name + kSharedNamespaceSuffix;
}
+ // Append a unique number to the namespace name, to tell them apart when
+ // debugging linker issues, e.g. with debug.ld.all set to "dlopen,dlerror".
+ static int clns_count = 0;
+ namespace_name = android::base::StringPrintf("%s-%d", namespace_name.c_str(), ++clns_count);
+
+ ALOGD(
+ "Configuring %s for %s %s. target_sdk_version=%u, uses_libraries=%s, library_path=%s, "
+ "permitted_path=%s",
+ namespace_name.c_str(),
+ apk_origin_msg,
+ dex_path.c_str(),
+ static_cast<unsigned>(target_sdk_version),
+ android::base::Join(uses_libraries, ':').c_str(),
+ library_path.c_str(),
+ permitted_path.c_str());
+
+ if (unbundled_app_origin != APK_ORIGIN_VENDOR) {
+ // Extended public libraries are NOT available to unbundled vendor apks, but
+ // they are to other apps, including those in system, system_ext, and
+ // product partitions. The reason is that when GSI is used, the system
+ // partition may get replaced, and then vendor apps may fail. It's fine for
+ // product apps, because that partition isn't mounted in GSI tests.
+ auto libs =
+ filter_public_libraries(target_sdk_version, uses_libraries, extended_public_libraries());
+ if (!libs.empty()) {
+ ALOGD("Extending system_exposed_libraries: %s", libs.c_str());
+ system_exposed_libraries = system_exposed_libraries + ':' + libs;
+ }
+ }
+
// Create the app namespace
NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
// Heuristic: the first classloader with non-empty library_path is assumed to
diff --git a/libnativeloader/native_loader_test.cpp b/libnativeloader/native_loader_test.cpp
index 1dc778a..6c0c8b1 100644
--- a/libnativeloader/native_loader_test.cpp
+++ b/libnativeloader/native_loader_test.cpp
@@ -31,10 +31,10 @@
namespace nativeloader {
using ::testing::Eq;
-using ::testing::MatchesRegex;
using ::testing::NotNull;
+using ::testing::StartsWith;
using ::testing::StrEq;
-using internal::ConfigEntry;
+using internal::ConfigEntry; // NOLINT - ConfigEntry is actually used
using internal::ParseApexLibrariesConfig;
using internal::ParseConfig;
@@ -69,7 +69,7 @@
void SetExpectations() {
std::vector<std::string> default_public_libs =
android::base::Split(preloadable_public_libraries(), ":");
- for (auto l : default_public_libs) {
+ for (const std::string& l : default_public_libs) {
EXPECT_CALL(*mock,
mock_dlopen_ext(false, StrEq(l.c_str()), RTLD_NOW | RTLD_NODELETE, NotNull()))
.WillOnce(Return(any_nonnull));
@@ -168,13 +168,16 @@
/////////////////////////////////////////////////////////////////
-std::string default_public_and_extended_libraries() {
- std::string public_libs = default_public_libraries();
- std::string ext_libs = extended_public_libraries();
+std::string append_extended_libraries(const std::string& libs) {
+ const std::string& ext_libs = extended_public_libraries();
if (!ext_libs.empty()) {
- public_libs = public_libs + ":" + ext_libs;
+ return libs + ":" + ext_libs;
}
- return public_libs;
+ return libs;
+}
+
+std::string default_public_and_extended_libraries() {
+ return append_extended_libraries(default_public_libraries());
}
class NativeLoaderTest_Create : public NativeLoaderTest {
@@ -189,7 +192,7 @@
std::string permitted_path = "/data/app/foo/" LIB_DIR;
// expected output (.. for the default test inputs)
- std::string expected_namespace_name = "classloader-namespace";
+ std::string expected_namespace_prefix = "clns";
uint64_t expected_namespace_flags =
ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_ALSO_USED_AS_ANONYMOUS;
std::string expected_library_path = library_path;
@@ -225,7 +228,7 @@
EXPECT_CALL(*mock, NativeBridgeInitialized()).Times(testing::AnyNumber());
EXPECT_CALL(*mock, mock_create_namespace(
- Eq(IsBridged()), MatchesRegex(expected_namespace_name), nullptr,
+ Eq(IsBridged()), StartsWith(expected_namespace_prefix + "-"), nullptr,
StrEq(expected_library_path), expected_namespace_flags,
StrEq(expected_permitted_path), NsEq(expected_parent_namespace.c_str())))
.WillOnce(Return(TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE(dex_path.c_str()))));
@@ -320,7 +323,7 @@
dex_path = "/system/app/foo/foo.apk";
is_shared = true;
- expected_namespace_name = "classloader-namespace-shared";
+ expected_namespace_prefix = "clns-shared";
expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
SetExpectations();
RunTest();
@@ -330,7 +333,7 @@
dex_path = "/vendor/app/foo/foo.apk";
is_shared = true;
- expected_namespace_name = "classloader-namespace-shared";
+ expected_namespace_prefix = "clns-shared";
expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
SetExpectations();
RunTest();
@@ -340,12 +343,12 @@
dex_path = "/vendor/app/foo/foo.apk";
is_shared = false;
- expected_namespace_name = "vendor-classloader-namespace";
+ expected_namespace_prefix = "vendor-clns";
expected_library_path = expected_library_path + ":/vendor/" LIB_DIR;
expected_permitted_path = expected_permitted_path + ":/vendor/" LIB_DIR;
expected_shared_libs_to_platform_ns =
default_public_libraries() + ":" + llndk_libraries_vendor();
- expected_link_with_vndk_ns = !get_vndk_version(/*is_product_vndk=*/false).empty();
+ expected_link_with_vndk_ns = true;
SetExpectations();
RunTest();
}
@@ -354,7 +357,7 @@
dex_path = "/product/app/foo/foo.apk";
is_shared = true;
- expected_namespace_name = "classloader-namespace-shared";
+ expected_namespace_prefix = "clns-shared";
expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
SetExpectations();
RunTest();
@@ -364,7 +367,7 @@
dex_path = "/system/framework/services.jar:/apex/com.android.conscrypt/javalib/service-foo.jar";
is_shared = true;
- expected_namespace_name = "classloader-namespace-shared";
+ expected_namespace_prefix = "clns-shared";
expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED;
expected_link_with_conscrypt_ns = true;
SetExpectations();
@@ -376,28 +379,13 @@
is_shared = false;
if (is_product_vndk_version_defined()) {
- expected_namespace_name = "(vendor|product)-classloader-namespace";
+ expected_namespace_prefix = "product-clns";
expected_library_path = expected_library_path + ":/product/" LIB_DIR ":/system/product/" LIB_DIR;
expected_permitted_path =
expected_permitted_path + ":/product/" LIB_DIR ":/system/product/" LIB_DIR;
- expected_link_with_vndk_product_ns = true;
-
- // The handling of extended libraries for product apps changed in the
- // M-2022-10 release of the ART module (https://r.android.com/2194871).
- // Since this test is in CTS for T, we need to accept both new and old
- // behaviour, i.e. with and without the extended public libraries appended
- // at the end. Skip the EXPECT_CALL in
- // NativeLoaderTest_Create::SetExpectations and create a more lenient
- // variant of it here.
- expected_link_with_platform_ns = false;
expected_shared_libs_to_platform_ns =
- default_public_libraries() + ":" + llndk_libraries_product();
- EXPECT_CALL(*mock,
- mock_link_namespaces(Eq(IsBridged()),
- _,
- NsEq("system"),
- ::testing::StartsWith(expected_shared_libs_to_platform_ns)))
- .WillOnce(Return(true));
+ append_extended_libraries(default_public_libraries() + ":" + llndk_libraries_product());
+ expected_link_with_vndk_product_ns = true;
}
SetExpectations();
RunTest();
@@ -429,7 +417,7 @@
const std::string second_app_permitted_path = "/data/app/bar/" LIB_DIR;
const std::string expected_second_app_permitted_path =
std::string("/data:/mnt/expand:") + second_app_permitted_path;
- const std::string expected_second_app_parent_namespace = "classloader-namespace";
+ const std::string expected_second_app_parent_namespace = "clns";
// no ALSO_USED_AS_ANONYMOUS
const uint64_t expected_second_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED;
@@ -442,7 +430,7 @@
// namespace for the second app is created. Its parent is set to the namespace
// of the first app.
EXPECT_CALL(*mock, mock_create_namespace(
- Eq(IsBridged()), StrEq(expected_namespace_name), nullptr,
+ Eq(IsBridged()), StartsWith(expected_namespace_prefix + "-"), nullptr,
StrEq(second_app_library_path), expected_second_namespace_flags,
StrEq(expected_second_app_permitted_path), NsEq(dex_path.c_str())))
.WillOnce(Return(TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE(second_app_dex_path.c_str()))));
diff --git a/libnativeloader/native_loader_test.h b/libnativeloader/native_loader_test.h
index 5c51f00..30b2f84 100644
--- a/libnativeloader/native_loader_test.h
+++ b/libnativeloader/native_loader_test.h
@@ -42,7 +42,7 @@
// Instead of having two set of mock APIs for the two, define only one set with an additional
// argument 'bool bridged' to identify the context (i.e., called for libdl_android or
// libnativebridge).
- typedef char* mock_namespace_handle;
+ using mock_namespace_handle = char*;
virtual bool mock_init_anonymous_namespace(bool bridged, const char* sonames,
const char* search_paths) = 0;
virtual mock_namespace_handle mock_create_namespace(
diff --git a/libnativeloader/public_libraries.cpp b/libnativeloader/public_libraries.cpp
index 433a909..fae4d76 100644
--- a/libnativeloader/public_libraries.cpp
+++ b/libnativeloader/public_libraries.cpp
@@ -46,7 +46,6 @@
using internal::ConfigEntry;
using internal::ParseConfig;
using internal::ParseApexLibrariesConfig;
-using std::literals::string_literals::operator""s;
namespace {
@@ -117,7 +116,7 @@
if (android::base::ConsumePrefix(&fn, kExtendedPublicLibrariesFilePrefix) &&
android::base::ConsumeSuffix(&fn, kExtendedPublicLibrariesFileSuffix)) {
const std::string company_name(fn);
- const std::string config_file_path = dirname + "/"s + filename;
+ const std::string config_file_path = std::string(dirname) + std::string("/") + filename;
LOG_ALWAYS_FATAL_IF(
company_name.empty(),
"Error extracting company name from public native library list file path \"%s\"",
@@ -129,8 +128,11 @@
android::base::EndsWith(entry.soname, "." + company_name + ".so")) {
return true;
} else {
- return Errorf("Library name \"{}\" does not end with the company name {}.",
- entry.soname, company_name);
+ return Errorf(
+ "Library name \"{}\" does not start with \"lib\" and/or "
+ "does not end with the company name \"{}\".",
+ entry.soname,
+ company_name);
}
});
if (ret.ok()) {
@@ -161,28 +163,36 @@
}
// If this is for preloading libs, don't remove the libs from APEXes.
- if (for_preload) {
- return android::base::Join(*sonames, ':');
+ if (!for_preload) {
+ // Remove the public libs provided by apexes because these libs are available
+ // from apex namespaces.
+ for (const auto& p : apex_public_libraries()) {
+ auto public_libs = base::Split(p.second, ":");
+ sonames->erase(std::remove_if(sonames->begin(),
+ sonames->end(),
+ [&public_libs](const std::string& v) {
+ return std::find(public_libs.begin(), public_libs.end(), v) !=
+ public_libs.end();
+ }),
+ sonames->end());
+ }
}
- // Remove the public libs provided by apexes because these libs are available
- // from apex namespaces.
- for (const auto& p : apex_public_libraries()) {
- auto public_libs = base::Split(p.second, ":");
- sonames->erase(std::remove_if(sonames->begin(), sonames->end(), [&public_libs](const std::string& v) {
- return std::find(public_libs.begin(), public_libs.end(), v) != public_libs.end();
- }), sonames->end());
- }
- return android::base::Join(*sonames, ':');
+ std::string libs = android::base::Join(*sonames, ':');
+ ALOGD("InitDefaultPublicLibraries for_preload=%d: %s", for_preload, libs.c_str());
+ return libs;
}
static std::string InitVendorPublicLibraries() {
// This file is optional, quietly ignore if the file does not exist.
auto sonames = ReadConfig(kVendorPublicLibrariesFile, always_true);
if (!sonames.ok()) {
+ ALOGI("InitVendorPublicLibraries skipped: %s", sonames.error().message().c_str());
return "";
}
- return android::base::Join(*sonames, ':');
+ std::string libs = android::base::Join(*sonames, ':');
+ ALOGD("InitVendorPublicLibraries: %s", libs.c_str());
+ return libs;
}
// If ro.product.vndk.version is defined, /product/etc/public.libraries-<companyname>.txt contains
@@ -193,7 +203,9 @@
if (is_product_vndk_version_defined()) {
ReadExtensionLibraries("/product/etc", &sonames);
}
- return android::base::Join(sonames, ':');
+ std::string libs = android::base::Join(sonames, ':');
+ ALOGD("InitProductPublicLibraries: %s", libs.c_str());
+ return libs;
}
// read /system/etc/public.libraries-<companyname>.txt,
@@ -208,13 +220,12 @@
if (!is_product_vndk_version_defined()) {
ReadExtensionLibraries("/product/etc", &sonames);
}
- return android::base::Join(sonames, ':');
+ std::string libs = android::base::Join(sonames, ':');
+ ALOGD("InitExtendedPublicLibraries: %s", libs.c_str());
+ return libs;
}
static std::string InitLlndkLibrariesVendor() {
- if (get_vndk_version(/*is_product_vndk=*/false).empty()) {
- return "";
- }
std::string config_file = kLlndkLibrariesFile;
InsertVndkVersionStr(&config_file, false);
auto sonames = ReadConfig(config_file, always_true);
@@ -222,11 +233,14 @@
LOG_ALWAYS_FATAL("%s: %s", config_file.c_str(), sonames.error().message().c_str());
return "";
}
- return android::base::Join(*sonames, ':');
+ std::string libs = android::base::Join(*sonames, ':');
+ ALOGD("InitLlndkLibrariesVendor: %s", libs.c_str());
+ return libs;
}
static std::string InitLlndkLibrariesProduct() {
if (!is_product_vndk_version_defined()) {
+ ALOGD("InitLlndkLibrariesProduct: No product VNDK version defined");
return "";
}
std::string config_file = kLlndkLibrariesFile;
@@ -236,13 +250,12 @@
LOG_ALWAYS_FATAL("%s: %s", config_file.c_str(), sonames.error().message().c_str());
return "";
}
- return android::base::Join(*sonames, ':');
+ std::string libs = android::base::Join(*sonames, ':');
+ ALOGD("InitLlndkLibrariesProduct: %s", libs.c_str());
+ return libs;
}
static std::string InitVndkspLibrariesVendor() {
- if (get_vndk_version(/*is_product_vndk=*/false).empty()) {
- return "";
- }
std::string config_file = kVndkLibrariesFile;
InsertVndkVersionStr(&config_file, false);
auto sonames = ReadConfig(config_file, always_true);
@@ -250,11 +263,14 @@
LOG_ALWAYS_FATAL("%s", sonames.error().message().c_str());
return "";
}
- return android::base::Join(*sonames, ':');
+ std::string libs = android::base::Join(*sonames, ':');
+ ALOGD("InitVndkspLibrariesVendor: %s", libs.c_str());
+ return libs;
}
static std::string InitVndkspLibrariesProduct() {
if (!is_product_vndk_version_defined()) {
+ ALOGD("InitVndkspLibrariesProduct: No product VNDK version defined");
return "";
}
std::string config_file = kVndkLibrariesFile;
@@ -264,20 +280,33 @@
LOG_ALWAYS_FATAL("%s", sonames.error().message().c_str());
return "";
}
- return android::base::Join(*sonames, ':');
+ std::string libs = android::base::Join(*sonames, ':');
+ ALOGD("InitVndkspLibrariesProduct: %s", libs.c_str());
+ return libs;
}
static std::map<std::string, std::string> InitApexLibraries(const std::string& tag) {
std::string file_content;
if (!base::ReadFileToString(kApexLibrariesConfigFile, &file_content)) {
// config is optional
+ ALOGI("InitApexLibraries skipped: %s", strerror(errno));
return {};
}
- auto config = ParseApexLibrariesConfig(file_content, tag);
+ Result<std::map<std::string, std::string>> config = ParseApexLibrariesConfig(file_content, tag);
if (!config.ok()) {
LOG_ALWAYS_FATAL("%s: %s", kApexLibrariesConfigFile, config.error().message().c_str());
return {};
}
+ ALOGD("InitApexLibraries:\n %s",
+ [&config]() {
+ std::vector<std::string> lib_list;
+ lib_list.reserve(config->size());
+ for (std::pair<std::string, std::string> elem : *config) {
+ lib_list.emplace_back(elem.first + ": " + elem.second);
+ }
+ return android::base::Join(lib_list, "\n ");
+ }()
+ .c_str());
return *config;
}
diff --git a/libnativeloader/test/Android.bp b/libnativeloader/test/Android.bp
index fb9ae0d..95b9b88 100644
--- a/libnativeloader/test/Android.bp
+++ b/libnativeloader/test/Android.bp
@@ -23,58 +23,179 @@
default_applicable_licenses: ["art_license"],
}
+// A native library that goes into /system or /system_ext and that depends on
+// a non-public library that is linked from the system namespace.
cc_library {
- name: "libfoo.oem1",
- srcs: ["test.cpp"],
- cflags: ["-DLIBNAME=\"libfoo.oem1.so\""],
- shared_libs: [
- "libbase",
+ name: "libsystem_testlib",
+ min_sdk_version: "31",
+ stl: "libc++_static",
+ shared_libs: ["liblog"],
+ // It's difficult to add a shared_lib dependency on a non-public library
+ // here, so it dlopens one instead.
+ srcs: ["libsystem_testlib.cc"],
+}
+
+// A native library that goes into /product.
+cc_library {
+ name: "libproduct_testlib",
+ min_sdk_version: "31",
+ stl: "none",
+ srcs: [],
+}
+
+// A native library that goes into /vendor.
+cc_library {
+ name: "libvendor_testlib",
+ min_sdk_version: "31",
+ stl: "none",
+ srcs: [],
+}
+
+// This app is just an intermediate container to be able to include the .so
+// library in the host test. It's not actually installed or started.
+android_test_helper_app {
+ name: "library_container_app",
+ defaults: ["art_module_source_build_java_defaults"],
+ min_sdk_version: "31",
+ manifest: "library_container_app_manifest.xml",
+ compile_multilib: "both",
+ jni_libs: [
+ "libsystem_testlib",
+ "libproduct_testlib",
+ "libvendor_testlib",
],
}
-cc_library {
- name: "libbar.oem1",
- srcs: ["test.cpp"],
- cflags: ["-DLIBNAME=\"libbar.oem1.so\""],
- shared_libs: [
- "libbase",
+java_library {
+ name: "loadlibrarytest_test_utils",
+ sdk_version: "31",
+ static_libs: [
+ "androidx.test.ext.junit",
+ "androidx.test.ext.truth",
],
+ srcs: ["src/android/test/lib/TestUtils.java"],
}
-cc_library {
- name: "libfoo.oem2",
- srcs: ["test.cpp"],
- cflags: ["-DLIBNAME=\"libfoo.oem2.so\""],
- shared_libs: [
- "libbase",
- ],
+// Test fixture that represents a shared library in /system/framework.
+java_library {
+ name: "libnativeloader_system_shared_lib",
+ sdk_version: "31",
+ installable: true,
+ srcs: ["src/android/test/systemsharedlib/SystemSharedLib.java"],
}
-cc_library {
- name: "libbar.oem2",
- srcs: ["test.cpp"],
- cflags: ["-DLIBNAME=\"libbar.oem2.so\""],
- shared_libs: [
- "libbase",
- ],
+// Test fixture that represents a shared library in /system_ext/framework.
+java_library {
+ name: "libnativeloader_system_ext_shared_lib",
+ sdk_version: "31",
+ installable: true,
+ srcs: ["src/android/test/systemextsharedlib/SystemExtSharedLib.java"],
}
-cc_library {
- name: "libfoo.product1",
- srcs: ["test.cpp"],
- cflags: ["-DLIBNAME=\"libfoo.product1.so\""],
+// Test fixture that represents a shared library in /product/framework.
+java_library {
+ name: "libnativeloader_product_shared_lib",
product_specific: true,
- shared_libs: [
- "libbase",
+ sdk_version: "31",
+ installable: true,
+ srcs: ["src/android/test/productsharedlib/ProductSharedLib.java"],
+}
+
+// Test fixture that represents a shared library in /vendor/framework.
+java_library {
+ name: "libnativeloader_vendor_shared_lib",
+ vendor: true,
+ sdk_version: "31",
+ installable: true,
+ srcs: ["src/android/test/vendorsharedlib/VendorSharedLib.java"],
+}
+
+java_defaults {
+ name: "loadlibrarytest_app_defaults",
+ defaults: ["art_module_source_build_java_defaults"],
+ sdk_version: "31",
+ static_libs: [
+ "androidx.test.ext.junit",
+ "androidx.test.rules",
+ "loadlibrarytest_test_utils",
+ ],
+ libs: [
+ "libnativeloader_system_shared_lib",
+ "libnativeloader_system_ext_shared_lib",
+ "libnativeloader_product_shared_lib",
+ "libnativeloader_vendor_shared_lib",
],
}
-cc_library {
- name: "libbar.product1",
- srcs: ["test.cpp"],
- cflags: ["-DLIBNAME=\"libbar.product1.so\""],
+android_test_helper_app {
+ name: "loadlibrarytest_system_priv_app",
+ defaults: ["loadlibrarytest_app_defaults"],
+ manifest: "loadlibrarytest_system_priv_app_manifest.xml",
+ // /system/priv-app currently reuses the same test as /system/app.
+ srcs: ["src/android/test/app/SystemAppTest.java"],
+}
+
+android_test_helper_app {
+ name: "loadlibrarytest_system_app",
+ defaults: ["loadlibrarytest_app_defaults"],
+ manifest: "loadlibrarytest_system_app_manifest.xml",
+ srcs: ["src/android/test/app/SystemAppTest.java"],
+}
+
+android_test_helper_app {
+ name: "loadlibrarytest_system_ext_app",
+ defaults: ["loadlibrarytest_app_defaults"],
+ system_ext_specific: true,
+ manifest: "loadlibrarytest_system_ext_app_manifest.xml",
+ // /system_ext should behave the same as /system, so use the same test class there.
+ srcs: ["src/android/test/app/SystemAppTest.java"],
+}
+
+android_test_helper_app {
+ name: "loadlibrarytest_product_app",
+ defaults: ["loadlibrarytest_app_defaults"],
product_specific: true,
- shared_libs: [
- "libbase",
+ manifest: "loadlibrarytest_product_app_manifest.xml",
+ srcs: ["src/android/test/app/ProductAppTest.java"],
+}
+
+android_test_helper_app {
+ name: "loadlibrarytest_vendor_app",
+ defaults: ["loadlibrarytest_app_defaults"],
+ vendor: true,
+ manifest: "loadlibrarytest_vendor_app_manifest.xml",
+ srcs: ["src/android/test/app/VendorAppTest.java"],
+}
+
+// A normal app installed in /data.
+android_test_helper_app {
+ name: "loadlibrarytest_data_app",
+ defaults: ["loadlibrarytest_app_defaults"],
+ manifest: "loadlibrarytest_data_app_manifest.xml",
+ srcs: ["src/android/test/app/DataAppTest.java"],
+}
+
+java_test_host {
+ name: "libnativeloader_e2e_tests",
+ defaults: ["art_module_source_build_java_defaults"],
+ srcs: ["src/android/test/hostside/*.java"],
+ libs: [
+ "compatibility-tradefed",
+ "tradefed",
],
+ data: [
+ ":library_container_app",
+ ":libnativeloader_system_shared_lib",
+ ":libnativeloader_system_ext_shared_lib",
+ ":libnativeloader_product_shared_lib",
+ ":libnativeloader_vendor_shared_lib",
+ ":loadlibrarytest_system_priv_app",
+ ":loadlibrarytest_system_app",
+ ":loadlibrarytest_system_ext_app",
+ ":loadlibrarytest_product_app",
+ ":loadlibrarytest_vendor_app",
+ ":loadlibrarytest_data_app",
+ ],
+ test_config: "libnativeloader_e2e_tests.xml",
+ test_suites: ["general-tests"],
}
diff --git a/libnativeloader/test/Android.mk b/libnativeloader/test/Android.mk
deleted file mode 100644
index 95fa68a..0000000
--- a/libnativeloader/test/Android.mk
+++ /dev/null
@@ -1,72 +0,0 @@
-#
-# 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.
-#
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := public.libraries-oem1.txt
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-LOCAL_SRC_FILES:= $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := public.libraries-oem2.txt
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-LOCAL_SRC_FILES:= $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := public.libraries-product1.txt
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-LOCAL_SRC_FILES:= $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_ETC)
-include $(BUILD_PREBUILT)
-
-include $(CLEAR_VARS)
-LOCAL_PACKAGE_NAME := oemlibrarytest-system
-LOCAL_MODULE_TAGS := tests
-LOCAL_MANIFEST_FILE := system/AndroidManifest.xml
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := current
-LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_MODULE_PATH := $(TARGET_OUT_APPS)
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-include $(BUILD_PACKAGE)
-
-include $(CLEAR_VARS)
-LOCAL_PACKAGE_NAME := oemlibrarytest-vendor
-LOCAL_MODULE_TAGS := tests
-LOCAL_MANIFEST_FILE := vendor/AndroidManifest.xml
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := current
-LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_APPS)
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-include $(BUILD_PACKAGE)
diff --git a/libnativeloader/test/libnativeloader_e2e_tests.xml b/libnativeloader/test/libnativeloader_e2e_tests.xml
new file mode 100644
index 0000000..d4f6348
--- /dev/null
+++ b/libnativeloader/test/libnativeloader_e2e_tests.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 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.
+-->
+<configuration description="Config for libnativeloader e2e test cases">
+ <option name="test-suite-tag" value="libnativeloader_e2e_tests" />
+ <option name="test-suite-tag" value="apct" />
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.art.apex" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <!-- We push all files in LibnativeloaderTest.java, but use this
+ preparer to make partitions writable. That since remounting may
+ require a device reboot, and then it's important that the
+ `setenforce 0` from DisableSELinuxTargetPreparer occurs after that. -->
+ <option name="remount-system" value="true" />
+ <option name="remount-vendor" value="true" />
+ </target_preparer>
+
+ <!-- Vendor native libraries aren't accessible by any apps by sepolicy
+ rules. For that they need to be labelled same_process_hal_file in a
+ vendor specific file_contexts file (see
+ https://source.android.com/docs/core/permissions/namespaces_libraries#adding-additional-native-libraries).
+ To avoid setting that up to test loading libvendor_private*.so, disable
+ sepolicy checks while running the tests. It's libnativeloader logic we
+ want to test here. -->
+ <target_preparer class="com.android.tradefed.targetprep.DisableSELinuxTargetPreparer"/>
+
+ <test class="com.android.tradefed.testtype.HostTest" >
+ <option name="jar" value="libnativeloader_e2e_tests.jar" />
+ </test>
+
+ <!-- Only run tests if the device under test is SDK version 31 (Android 12) or above. -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" />
+</configuration>
diff --git a/libnativeloader/test/library_container_app_manifest.xml b/libnativeloader/test/library_container_app_manifest.xml
new file mode 100644
index 0000000..20030de
--- /dev/null
+++ b/libnativeloader/test/library_container_app_manifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2022 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.test.app.container">
+</manifest>
diff --git a/libnativeloader/test/libsystem_testlib.cc b/libnativeloader/test/libsystem_testlib.cc
new file mode 100644
index 0000000..97d3223
--- /dev/null
+++ b/libnativeloader/test/libsystem_testlib.cc
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 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 <dlfcn.h>
+
+#include "log/log.h"
+
+static void __attribute__((constructor)) ctor() {
+ // Load a library that should be available to system libraries through a
+ // linked namespace (i.e. is not directly in /system/${LIB}), and that is not
+ // in public.libraries.txt. We use a real one to avoid having to set up an
+ // APEX test fixture and rerun linkerconfig.
+ void* h = dlopen("libandroidicu.so", RTLD_NOW);
+ if (h == nullptr) {
+ LOG_ALWAYS_FATAL("Failed to load dependency: %s", dlerror());
+ }
+ dlclose(h);
+}
diff --git a/libnativeloader/test/loadlibrarytest_data_app_manifest.xml b/libnativeloader/test/loadlibrarytest_data_app_manifest.xml
new file mode 100644
index 0000000..4be78f8
--- /dev/null
+++ b/libnativeloader/test/loadlibrarytest_data_app_manifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2022 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.test.app.data">
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.test.app.data" />
+ <application>
+ <uses-library android:required="false" android:name="android.test.systemsharedlib" />
+ <uses-library android:required="false" android:name="android.test.systemextsharedlib" />
+ <uses-library android:required="false" android:name="android.test.productsharedlib" />
+ <uses-library android:required="false" android:name="android.test.vendorsharedlib" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub.oem1.so" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub.oem2.so" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub1.oem1.so" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub2.oem1.so" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub3.oem1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub1.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub2.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub3.product1.so" />
+ </application>
+</manifest>
+
diff --git a/libnativeloader/test/loadlibrarytest_product_app_manifest.xml b/libnativeloader/test/loadlibrarytest_product_app_manifest.xml
new file mode 100644
index 0000000..9061c39
--- /dev/null
+++ b/libnativeloader/test/loadlibrarytest_product_app_manifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.test.app.product">
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.test.app.product" />
+ <application>
+ <uses-library android:required="false" android:name="android.test.systemsharedlib" />
+ <uses-library android:required="false" android:name="android.test.systemextsharedlib" />
+ <uses-library android:required="false" android:name="android.test.productsharedlib" />
+ <uses-library android:required="false" android:name="android.test.vendorsharedlib" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub.oem1.so" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub.oem2.so" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub1.oem1.so" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub2.oem1.so" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub3.oem1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub1.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub2.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub3.product1.so" />
+ </application>
+</manifest>
+
diff --git a/libnativeloader/test/loadlibrarytest_system_app_manifest.xml b/libnativeloader/test/loadlibrarytest_system_app_manifest.xml
new file mode 100644
index 0000000..7ee7532
--- /dev/null
+++ b/libnativeloader/test/loadlibrarytest_system_app_manifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.test.app.system">
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.test.app.system" />
+ <application>
+ <uses-library android:required="false" android:name="android.test.systemsharedlib" />
+ <uses-library android:required="false" android:name="android.test.systemextsharedlib" />
+ <uses-library android:required="false" android:name="android.test.productsharedlib" />
+ <uses-library android:required="false" android:name="android.test.vendorsharedlib" />
+ <!-- System apps get a shared classloader namespace, so they don't need
+ uses-native-library entries for anything in /system or /system_ext. -->
+ <uses-native-library android:required="false" android:name="libproduct_extpub.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub1.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub2.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub3.product1.so" />
+ </application>
+</manifest>
+
diff --git a/libnativeloader/test/loadlibrarytest_system_ext_app_manifest.xml b/libnativeloader/test/loadlibrarytest_system_ext_app_manifest.xml
new file mode 100644
index 0000000..a2c1a2c
--- /dev/null
+++ b/libnativeloader/test/loadlibrarytest_system_ext_app_manifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.test.app.system_ext">
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.test.app.system_ext" />
+ <application>
+ <uses-library android:required="false" android:name="android.test.systemsharedlib" />
+ <uses-library android:required="false" android:name="android.test.systemextsharedlib" />
+ <uses-library android:required="false" android:name="android.test.productsharedlib" />
+ <uses-library android:required="false" android:name="android.test.vendorsharedlib" />
+ <!-- System apps get a shared classloader namespace, so they don't need
+ uses-native-library entries for anything in /system or /system_ext. -->
+ <uses-native-library android:required="false" android:name="libproduct_extpub.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub1.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub2.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub3.product1.so" />
+ </application>
+</manifest>
+
diff --git a/libnativeloader/test/loadlibrarytest_system_priv_app_manifest.xml b/libnativeloader/test/loadlibrarytest_system_priv_app_manifest.xml
new file mode 100644
index 0000000..87a30ce
--- /dev/null
+++ b/libnativeloader/test/loadlibrarytest_system_priv_app_manifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.test.app.system_priv">
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.test.app.system_priv" />
+ <application>
+ <uses-library android:required="false" android:name="android.test.systemsharedlib" />
+ <uses-library android:required="false" android:name="android.test.systemextsharedlib" />
+ <uses-library android:required="false" android:name="android.test.productsharedlib" />
+ <uses-library android:required="false" android:name="android.test.vendorsharedlib" />
+ <!-- System apps get a shared classloader namespace, so they don't need
+ uses-native-library entries for anything in /system or /system_ext. -->
+ <uses-native-library android:required="false" android:name="libproduct_extpub.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub1.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub2.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub3.product1.so" />
+ </application>
+</manifest>
+
diff --git a/libnativeloader/test/loadlibrarytest_vendor_app_manifest.xml b/libnativeloader/test/loadlibrarytest_vendor_app_manifest.xml
new file mode 100644
index 0000000..b3434fb
--- /dev/null
+++ b/libnativeloader/test/loadlibrarytest_vendor_app_manifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.test.app.vendor">
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.test.app.vendor" />
+ <application>
+ <uses-library android:required="false" android:name="android.test.systemsharedlib" />
+ <uses-library android:required="false" android:name="android.test.systemextsharedlib" />
+ <uses-library android:required="false" android:name="android.test.productsharedlib" />
+ <uses-library android:required="false" android:name="android.test.vendorsharedlib" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub.oem1.so" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub.oem2.so" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub1.oem1.so" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub2.oem1.so" />
+ <uses-native-library android:required="false" android:name="libsystem_extpub3.oem1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub1.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub2.product1.so" />
+ <uses-native-library android:required="false" android:name="libproduct_extpub3.product1.so" />
+ </application>
+</manifest>
+
diff --git a/libnativeloader/test/public.libraries-oem1.txt b/libnativeloader/test/public.libraries-oem1.txt
deleted file mode 100644
index f9433e2..0000000
--- a/libnativeloader/test/public.libraries-oem1.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-libfoo.oem1.so
-libbar.oem1.so
diff --git a/libnativeloader/test/public.libraries-oem2.txt b/libnativeloader/test/public.libraries-oem2.txt
deleted file mode 100644
index de6bdb0..0000000
--- a/libnativeloader/test/public.libraries-oem2.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-libfoo.oem2.so
-libbar.oem2.so
diff --git a/libnativeloader/test/public.libraries-product1.txt b/libnativeloader/test/public.libraries-product1.txt
deleted file mode 100644
index 358154c..0000000
--- a/libnativeloader/test/public.libraries-product1.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-libfoo.product1.so
-libbar.product1.so
diff --git a/libnativeloader/test/runtest.sh b/libnativeloader/test/runtest.sh
deleted file mode 100755
index 40beb5b..0000000
--- a/libnativeloader/test/runtest.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-adb root
-adb remount
-adb sync
-adb shell stop
-adb shell start
-sleep 5 # wait until device reboots
-adb logcat -c;
-adb shell am start -n android.test.app.system/android.test.app.TestActivity
-adb shell am start -n android.test.app.vendor/android.test.app.TestActivity
-adb logcat | grep android.test.app
diff --git a/libnativeloader/test/src/android/test/app/DataAppTest.java b/libnativeloader/test/src/android/test/app/DataAppTest.java
new file mode 100644
index 0000000..9403494
--- /dev/null
+++ b/libnativeloader/test/src/android/test/app/DataAppTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2022 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 android.test.app;
+
+import android.test.lib.TestUtils;
+import android.test.productsharedlib.ProductSharedLib;
+import android.test.systemextsharedlib.SystemExtSharedLib;
+import android.test.systemsharedlib.SystemSharedLib;
+import android.test.vendorsharedlib.VendorSharedLib;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class DataAppTest {
+ @Test
+ public void testLoadExtendedPublicLibraries() {
+ System.loadLibrary("system_extpub.oem1");
+ System.loadLibrary("system_extpub.oem2");
+ System.loadLibrary("system_extpub1.oem1");
+ TestUtils.assertLinkerNamespaceError( // Missing <uses-native-library>.
+ () -> System.loadLibrary("system_extpub_nouses.oem2"));
+ System.loadLibrary("product_extpub.product1");
+ System.loadLibrary("product_extpub1.product1");
+ }
+
+ @Test
+ public void testLoadPrivateLibraries() {
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("system_private1"));
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("systemext_private1"));
+ TestUtils.assertLibraryNotFound(() -> System.loadLibrary("product_private1"));
+ TestUtils.assertLibraryNotFound(() -> System.loadLibrary("vendor_private1"));
+ }
+
+ @Test
+ public void testLoadExtendedPublicLibrariesViaSystemSharedLib() {
+ SystemSharedLib.loadLibrary("system_extpub2.oem1");
+ SystemSharedLib.loadLibrary("product_extpub2.product1");
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaSystemSharedLib() {
+ // TODO(b/237577392): Loading a private native system library via a shared system library
+ // ought to work.
+ // SystemSharedLib.loadLibrary("system_private2");
+ // SystemSharedLib.loadLibrary("systemext_private2");
+ TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("product_private2"));
+ TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("vendor_private2"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaSystemExtSharedLib() {
+ // TODO(b/237577392): Loading a private native system library via a shared system library
+ // ought to work.
+ // SystemExtSharedLib.loadLibrary("system_private3");
+ // SystemExtSharedLib.loadLibrary("systemext_private3");
+ TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("product_private3"));
+ TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("vendor_private3"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaProductSharedLib() {
+ TestUtils.assertLinkerNamespaceError(() -> ProductSharedLib.loadLibrary("system_private4"));
+ TestUtils.assertLinkerNamespaceError(
+ () -> ProductSharedLib.loadLibrary("systemext_private4"));
+ ProductSharedLib.loadLibrary("product_private4");
+ TestUtils.assertLibraryNotFound(() -> ProductSharedLib.loadLibrary("vendor_private4"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaVendorSharedLib() {
+ TestUtils.assertLinkerNamespaceError(() -> VendorSharedLib.loadLibrary("system_private5"));
+ TestUtils.assertLinkerNamespaceError(
+ () -> VendorSharedLib.loadLibrary("systemext_private5"));
+ TestUtils.assertLibraryNotFound(() -> VendorSharedLib.loadLibrary("product_private5"));
+ VendorSharedLib.loadLibrary("vendor_private5");
+ }
+
+ @Test
+ public void testLoadExtendedPublicLibrariesWithAbsolutePaths() {
+ System.load(TestUtils.libPath("/system", "system_extpub3.oem1"));
+ System.load(TestUtils.libPath("/product", "product_extpub3.product1"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesWithAbsolutePaths() {
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/system", "system_private6")));
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/system_ext", "systemext_private6")));
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/product", "product_private6")));
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/vendor", "vendor_private6")));
+ }
+}
diff --git a/libnativeloader/test/src/android/test/app/ProductAppTest.java b/libnativeloader/test/src/android/test/app/ProductAppTest.java
new file mode 100644
index 0000000..7ec817b
--- /dev/null
+++ b/libnativeloader/test/src/android/test/app/ProductAppTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2022 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 android.test.app;
+
+import android.test.lib.TestUtils;
+import android.test.productsharedlib.ProductSharedLib;
+import android.test.systemextsharedlib.SystemExtSharedLib;
+import android.test.systemsharedlib.SystemSharedLib;
+import android.test.vendorsharedlib.VendorSharedLib;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class ProductAppTest {
+ @Test
+ public void testLoadExtendedPublicLibraries() {
+ System.loadLibrary("system_extpub.oem1");
+ System.loadLibrary("system_extpub.oem2");
+ System.loadLibrary("system_extpub1.oem1");
+ TestUtils.assertLinkerNamespaceError( // Missing <uses-native-library>.
+ () -> System.loadLibrary("system_extpub_nouses.oem2"));
+ System.loadLibrary("product_extpub.product1");
+ System.loadLibrary("product_extpub1.product1");
+ }
+
+ @Test
+ public void testLoadPrivateLibraries() {
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("system_private1"));
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("systemext_private1"));
+ System.loadLibrary("product_private1");
+ TestUtils.assertLibraryNotFound(() -> System.loadLibrary("vendor_private1"));
+ }
+
+ @Test
+ public void testLoadExtendedPublicLibrariesViaSystemSharedLib() {
+ SystemSharedLib.loadLibrary("system_extpub2.oem1");
+ SystemSharedLib.loadLibrary("product_extpub2.product1");
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaSystemSharedLib() {
+ // TODO(b/237577392): Loading a private native system library via a shared system library
+ // ought to work.
+ // SystemSharedLib.loadLibrary("system_private2");
+ // SystemSharedLib.loadLibrary("systemext_private2");
+ TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("product_private2"));
+ TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("vendor_private2"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaSystemExtSharedLib() {
+ // TODO(b/237577392): Loading a private native system library via a shared system library
+ // ought to work.
+ // SystemExtSharedLib.loadLibrary("system_private3");
+ // SystemExtSharedLib.loadLibrary("systemext_private3");
+ TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("product_private3"));
+ TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("vendor_private3"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaProductSharedLib() {
+ TestUtils.assertLinkerNamespaceError(() -> ProductSharedLib.loadLibrary("system_private4"));
+ TestUtils.assertLinkerNamespaceError(
+ () -> ProductSharedLib.loadLibrary("systemext_private4"));
+ ProductSharedLib.loadLibrary("product_private4");
+ TestUtils.assertLibraryNotFound(() -> ProductSharedLib.loadLibrary("vendor_private4"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaVendorSharedLib() {
+ TestUtils.assertLinkerNamespaceError(() -> VendorSharedLib.loadLibrary("system_private5"));
+ TestUtils.assertLinkerNamespaceError(
+ () -> VendorSharedLib.loadLibrary("systemext_private5"));
+ TestUtils.assertLibraryNotFound(() -> VendorSharedLib.loadLibrary("product_private5"));
+ VendorSharedLib.loadLibrary("vendor_private5");
+ }
+
+ @Test
+ public void testLoadExtendedPublicLibrariesWithAbsolutePaths() {
+ System.load(TestUtils.libPath("/system", "system_extpub3.oem1"));
+ System.load(TestUtils.libPath("/product", "product_extpub3.product1"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesWithAbsolutePaths() {
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/system", "system_private6")));
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/system_ext", "systemext_private6")));
+ System.load(TestUtils.libPath("/product", "product_private6"));
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/vendor", "vendor_private6")));
+ }
+}
diff --git a/libnativeloader/test/src/android/test/app/SystemAppTest.java b/libnativeloader/test/src/android/test/app/SystemAppTest.java
new file mode 100644
index 0000000..cabdfb7
--- /dev/null
+++ b/libnativeloader/test/src/android/test/app/SystemAppTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2022 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 android.test.app;
+
+import android.test.lib.TestUtils;
+import android.test.productsharedlib.ProductSharedLib;
+import android.test.systemextsharedlib.SystemExtSharedLib;
+import android.test.systemsharedlib.SystemSharedLib;
+import android.test.vendorsharedlib.VendorSharedLib;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+// These tests are run from /system/app, /system/priv-app, and /system_ext/app.
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class SystemAppTest {
+ @Test
+ public void testLoadExtendedPublicLibraries() {
+ System.loadLibrary("system_extpub.oem1");
+ System.loadLibrary("system_extpub.oem2");
+ System.loadLibrary("system_extpub1.oem1");
+ // Missing <uses-native-library> not relevant for system apps, which have shared classloader
+ // namespaces.
+ System.loadLibrary("system_extpub_nouses.oem2");
+ System.loadLibrary("product_extpub.product1");
+ System.loadLibrary("product_extpub1.product1");
+ }
+
+ @Test
+ public void testLoadPrivateLibraries() {
+ System.loadLibrary("system_private1");
+ System.loadLibrary("systemext_private1");
+ TestUtils.assertLibraryNotFound(() -> System.loadLibrary("product_private1"));
+ TestUtils.assertLibraryNotFound(() -> System.loadLibrary("vendor_private1"));
+ }
+
+ @Test
+ public void testLoadExtendedPublicLibrariesViaSystemSharedLib() {
+ SystemSharedLib.loadLibrary("system_extpub2.oem1");
+ SystemSharedLib.loadLibrary("product_extpub2.product1");
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaSystemSharedLib() {
+ SystemSharedLib.loadLibrary("system_private2");
+ SystemSharedLib.loadLibrary("systemext_private2");
+ TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("product_private2"));
+ TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("vendor_private2"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaSystemExtSharedLib() {
+ SystemExtSharedLib.loadLibrary("system_private3");
+ SystemExtSharedLib.loadLibrary("systemext_private3");
+ TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("product_private3"));
+ TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("vendor_private3"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaProductSharedLib() {
+ ProductSharedLib.loadLibrary("system_private4");
+ ProductSharedLib.loadLibrary("systemext_private4");
+ TestUtils.assertLibraryNotFound(() -> ProductSharedLib.loadLibrary("product_private4"));
+ TestUtils.assertLibraryNotFound(() -> ProductSharedLib.loadLibrary("vendor_private4"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaVendorSharedLib() {
+ VendorSharedLib.loadLibrary("system_private5");
+ VendorSharedLib.loadLibrary("systemext_private5");
+ TestUtils.assertLibraryNotFound(() -> VendorSharedLib.loadLibrary("product_private5"));
+ TestUtils.assertLibraryNotFound(() -> VendorSharedLib.loadLibrary("vendor_private5"));
+ }
+
+ @Test
+ public void testLoadExtendedPublicLibrariesWithAbsolutePaths() {
+ System.load(TestUtils.libPath("/system", "system_extpub3.oem1"));
+ System.load(TestUtils.libPath("/product", "product_extpub3.product1"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesWithAbsolutePaths() {
+ System.load(TestUtils.libPath("/system", "system_private6"));
+ System.load(TestUtils.libPath("/system_ext", "systemext_private6"));
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/product", "product_private6")));
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/vendor", "vendor_private6")));
+ }
+}
diff --git a/libnativeloader/test/src/android/test/app/TestActivity.java b/libnativeloader/test/src/android/test/app/TestActivity.java
deleted file mode 100644
index a7a455d..0000000
--- a/libnativeloader/test/src/android/test/app/TestActivity.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.
- */
-
-package android.test.app;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.util.Log;
-
-public class TestActivity extends Activity {
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- tryLoadingLib("foo.oem1");
- tryLoadingLib("bar.oem1");
- tryLoadingLib("foo.oem2");
- tryLoadingLib("bar.oem2");
- tryLoadingLib("foo.product1");
- tryLoadingLib("bar.product1");
- }
-
- private void tryLoadingLib(String name) {
- try {
- System.loadLibrary(name);
- Log.d(getPackageName(), "library " + name + " is successfully loaded");
- } catch (UnsatisfiedLinkError e) {
- Log.d(getPackageName(), "failed to load libarary " + name, e);
- }
- }
-}
diff --git a/libnativeloader/test/src/android/test/app/VendorAppTest.java b/libnativeloader/test/src/android/test/app/VendorAppTest.java
new file mode 100644
index 0000000..10d0ea0
--- /dev/null
+++ b/libnativeloader/test/src/android/test/app/VendorAppTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2022 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 android.test.app;
+
+import android.test.lib.TestUtils;
+import android.test.productsharedlib.ProductSharedLib;
+import android.test.systemextsharedlib.SystemExtSharedLib;
+import android.test.systemsharedlib.SystemSharedLib;
+import android.test.vendorsharedlib.VendorSharedLib;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class VendorAppTest {
+ @Test
+ public void testLoadExtendedPublicLibraries() {
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("system_extpub.oem1"));
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("system_extpub.oem2"));
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("system_extpub1.oem1"));
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("system_extpub_nouses.oem2"));
+ System.loadLibrary("product_extpub.product1");
+ System.loadLibrary("product_extpub1.product1");
+ }
+
+ @Test
+ public void testLoadPrivateLibraries() {
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("system_private1"));
+ TestUtils.assertLinkerNamespaceError(() -> System.loadLibrary("systemext_private1"));
+ TestUtils.assertLibraryNotFound(() -> System.loadLibrary("product_private1"));
+ System.loadLibrary("vendor_private1");
+ }
+
+ @Test
+ public void testLoadExtendedPublicLibrariesViaSystemSharedLib() {
+ SystemSharedLib.loadLibrary("system_extpub2.oem1");
+ SystemSharedLib.loadLibrary("product_extpub2.product1");
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaSystemSharedLib() {
+ // TODO(b/237577392): Loading a private native system library via a shared system library
+ // ought to work.
+ // SystemSharedLib.loadLibrary("system_private2");
+ // SystemSharedLib.loadLibrary("systemext_private2");
+ TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("product_private2"));
+ TestUtils.assertLibraryNotFound(() -> SystemSharedLib.loadLibrary("vendor_private2"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaSystemExtSharedLib() {
+ // TODO(b/237577392): Loading a private native system library via a shared system library
+ // ought to work.
+ // SystemExtSharedLib.loadLibrary("system_private3");
+ // SystemExtSharedLib.loadLibrary("systemext_private3");
+ TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("product_private3"));
+ TestUtils.assertLibraryNotFound(() -> SystemExtSharedLib.loadLibrary("vendor_private3"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaProductSharedLib() {
+ TestUtils.assertLinkerNamespaceError(() -> ProductSharedLib.loadLibrary("system_private4"));
+ TestUtils.assertLinkerNamespaceError(
+ () -> ProductSharedLib.loadLibrary("systemext_private4"));
+ ProductSharedLib.loadLibrary("product_private4");
+ TestUtils.assertLibraryNotFound(() -> ProductSharedLib.loadLibrary("vendor_private4"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesViaVendorSharedLib() {
+ TestUtils.assertLinkerNamespaceError(() -> VendorSharedLib.loadLibrary("system_private5"));
+ TestUtils.assertLinkerNamespaceError(
+ () -> VendorSharedLib.loadLibrary("systemext_private5"));
+ TestUtils.assertLibraryNotFound(() -> VendorSharedLib.loadLibrary("product_private5"));
+ VendorSharedLib.loadLibrary("vendor_private5");
+ }
+
+ @Test
+ public void testLoadExtendedPublicLibrariesWithAbsolutePaths() {
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/system", "system_extpub3.oem1")));
+ System.load(TestUtils.libPath("/product", "product_extpub3.product1"));
+ }
+
+ @Test
+ public void testLoadPrivateLibrariesWithAbsolutePaths() {
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/system", "system_private6")));
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/system_ext", "systemext_private6")));
+ TestUtils.assertLinkerNamespaceError(
+ () -> System.load(TestUtils.libPath("/product", "product_private6")));
+ System.load(TestUtils.libPath("/vendor", "vendor_private6"));
+ }
+}
diff --git a/libnativeloader/test/src/android/test/hostside/LibnativeloaderTest.java b/libnativeloader/test/src/android/test/hostside/LibnativeloaderTest.java
new file mode 100644
index 0000000..55a6dd2
--- /dev/null
+++ b/libnativeloader/test/src/android/test/hostside/LibnativeloaderTest.java
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2022 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 android.test.hostside;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.IInvocationContext;
+import com.android.tradefed.invoker.TestInformation;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.IAbi;
+import com.android.tradefed.testtype.junit4.AfterClassWithInfo;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
+import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
+import com.android.tradefed.util.CommandResult;
+
+import com.google.common.io.ByteStreams;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * Test libnativeloader behavior for apps and libs in various partitions by overlaying them over
+ * the system partitions. Requires root.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class LibnativeloaderTest extends BaseHostJUnit4Test {
+ private static final String TAG = "LibnativeloaderTest";
+ private static final String CLEANUP_PATHS_KEY = TAG + ":CLEANUP_PATHS";
+ private static final String LOG_FILE_NAME = "TestActivity.log";
+
+ @BeforeClassWithInfo
+ public static void beforeClassWithDevice(TestInformation testInfo) throws Exception {
+ DeviceContext ctx = new DeviceContext(testInfo);
+
+ // A soft reboot is slow, so do setup for all tests and reboot once.
+
+ File libContainerApk = ctx.mBuildHelper.getTestFile("library_container_app.apk");
+ try (ZipFile libApk = new ZipFile(libContainerApk)) {
+ ctx.pushExtendedPublicSystemOemLibs(libApk);
+ ctx.pushExtendedPublicProductLibs(libApk);
+ ctx.pushPrivateLibs(libApk);
+ }
+ ctx.pushSharedLib(
+ "/system", "android.test.systemsharedlib", "libnativeloader_system_shared_lib.jar");
+ ctx.pushSharedLib("/system_ext", "android.test.systemextsharedlib",
+ "libnativeloader_system_ext_shared_lib.jar");
+ ctx.pushSharedLib("/product", "android.test.productsharedlib",
+ "libnativeloader_product_shared_lib.jar");
+ ctx.pushSharedLib(
+ "/vendor", "android.test.vendorsharedlib", "libnativeloader_vendor_shared_lib.jar");
+
+ // "Install" apps in various partitions through plain adb push followed by a soft reboot. We
+ // need them in these locations to test library loading restrictions, so for all except
+ // loadlibrarytest_data_app we cannot use ITestDevice.installPackage for it since it only
+ // installs in /data.
+
+ // For testSystemPrivApp
+ ctx.pushApk("loadlibrarytest_system_priv_app", "/system/priv-app");
+
+ // For testSystemApp
+ ctx.pushApk("loadlibrarytest_system_app", "/system/app");
+
+ // For testSystemExtApp
+ ctx.pushApk("loadlibrarytest_system_ext_app", "/system_ext/app");
+
+ // For testProductApp
+ ctx.pushApk("loadlibrarytest_product_app", "/product/app");
+
+ // For testVendorApp
+ ctx.pushApk("loadlibrarytest_vendor_app", "/vendor/app");
+
+ ctx.softReboot();
+
+ // For testDataApp. Install this the normal way after the system server restart.
+ ctx.installPackage("loadlibrarytest_data_app");
+
+ testInfo.properties().put(CLEANUP_PATHS_KEY, ctx.mCleanup.getPathList());
+ }
+
+ @AfterClassWithInfo
+ public static void afterClassWithDevice(TestInformation testInfo) throws Exception {
+ DeviceContext ctx = new DeviceContext(testInfo);
+
+ // Uninstall loadlibrarytest_data_app.
+ ctx.mDevice.uninstallPackage("android.test.app.data");
+
+ String cleanupPathList = testInfo.properties().get(CLEANUP_PATHS_KEY);
+ CleanupPaths cleanup = new CleanupPaths(ctx.mDevice, cleanupPathList);
+ cleanup.cleanup();
+ }
+
+ @Test
+ public void testSystemPrivApp() throws Exception {
+ // There's currently no difference in the tests between /system/priv-app and /system/app, so
+ // let's reuse the same one.
+ runTests("android.test.app.system_priv", "android.test.app.SystemAppTest");
+ }
+
+ @Test
+ public void testSystemApp() throws Exception {
+ runTests("android.test.app.system", "android.test.app.SystemAppTest");
+ }
+
+ @Test
+ public void testSystemExtApp() throws Exception {
+ // /system_ext should behave the same as /system, so run the same test class there.
+ runTests("android.test.app.system_ext", "android.test.app.SystemAppTest");
+ }
+
+ @Test
+ public void testProductApp() throws Exception {
+ runTests("android.test.app.product", "android.test.app.ProductAppTest");
+ }
+
+ @Test
+ public void testVendorApp() throws Exception {
+ runTests("android.test.app.vendor", "android.test.app.VendorAppTest");
+ }
+
+ @Test
+ public void testDataApp() throws Exception {
+ runTests("android.test.app.data", "android.test.app.DataAppTest");
+ }
+
+ private void runTests(String pkgName, String testClassName) throws Exception {
+ DeviceContext ctx = new DeviceContext(getTestInformation());
+ var options = new DeviceTestRunOptions(pkgName)
+ .setTestClassName(testClassName)
+ .addInstrumentationArg("libDirName", ctx.libDirName());
+ runDeviceTests(options);
+ }
+
+ // Utility class that keeps track of a set of paths the need to be deleted after testing.
+ private static class CleanupPaths {
+ private ITestDevice mDevice;
+ private List<String> mCleanupPaths;
+
+ CleanupPaths(ITestDevice device) {
+ mDevice = device;
+ mCleanupPaths = new ArrayList<String>();
+ }
+
+ CleanupPaths(ITestDevice device, String pathList) {
+ mDevice = device;
+ mCleanupPaths = Arrays.asList(pathList.split(":"));
+ }
+
+ String getPathList() { return String.join(":", mCleanupPaths); }
+
+ // Adds the given path, or its topmost nonexisting parent directory, to the list of paths to
+ // clean up.
+ void addPath(String devicePath) throws DeviceNotAvailableException {
+ File path = new File(devicePath);
+ while (true) {
+ File parentPath = path.getParentFile();
+ if (parentPath == null || mDevice.doesFileExist(parentPath.toString())) {
+ break;
+ }
+ path = parentPath;
+ }
+ String nonExistingPath = path.toString();
+ if (!mCleanupPaths.contains(nonExistingPath)) {
+ mCleanupPaths.add(nonExistingPath);
+ }
+ }
+
+ void cleanup() throws DeviceNotAvailableException {
+ // Clean up in reverse order in case several pushed files were in the same nonexisting
+ // directory.
+ for (int i = mCleanupPaths.size() - 1; i >= 0; --i) {
+ mDevice.deleteFile(mCleanupPaths.get(i));
+ }
+ }
+ }
+
+ // Class for code that needs an ITestDevice. It may be instantiated both in tests and in
+ // (Before|After)ClassWithInfo.
+ private static class DeviceContext implements AutoCloseable {
+ IInvocationContext mContext;
+ ITestDevice mDevice;
+ CompatibilityBuildHelper mBuildHelper;
+ CleanupPaths mCleanup;
+ private String mTestArch;
+
+ DeviceContext(TestInformation testInfo) {
+ mContext = testInfo.getContext();
+ mDevice = testInfo.getDevice();
+ mBuildHelper = new CompatibilityBuildHelper(testInfo.getBuildInfo());
+ mCleanup = new CleanupPaths(mDevice);
+ }
+
+ public void close() throws DeviceNotAvailableException { mCleanup.cleanup(); }
+
+ // Helper class to both push a library to device and record it in a public.libraries-xxx.txt
+ // file.
+ class PublicLibs {
+ private ZipFile mLibApk;
+ private List<String> mPublicLibs = new ArrayList<String>();
+
+ PublicLibs(ZipFile libApk) {
+ mLibApk = libApk;
+ }
+
+ void addLib(String libName, String destDir, String destName) throws Exception {
+ pushNativeTestLib(mLibApk, libName, destDir + "/" + destName);
+ mPublicLibs.add(destName);
+ }
+
+ void pushPublicLibrariesFile(String path) throws DeviceNotAvailableException {
+ pushString(mPublicLibs.stream().collect(Collectors.joining("\n")) + "\n", path);
+ }
+ }
+
+ void pushExtendedPublicSystemOemLibs(ZipFile libApk) throws Exception {
+ var oem1Libs = new PublicLibs(libApk);
+ // Push libsystem_extpub<n>.oem1.so for each test. Since we cannot unload them, we need
+ // a fresh never-before-loaded library in each loadLibrary call.
+ for (int i = 1; i <= 3; ++i) {
+ oem1Libs.addLib("libsystem_testlib.so", "/system/${LIB}",
+ "libsystem_extpub" + i + ".oem1.so");
+ }
+ oem1Libs.addLib("libsystem_testlib.so", "/system/${LIB}", "libsystem_extpub.oem1.so");
+ oem1Libs.pushPublicLibrariesFile("/system/etc/public.libraries-oem1.txt");
+
+ var oem2Libs = new PublicLibs(libApk);
+ oem2Libs.addLib("libsystem_testlib.so", "/system/${LIB}", "libsystem_extpub.oem2.so");
+ // libextpub_nouses.oem2.so is a library that the test apps don't have
+ // <uses-native-library> dependencies for.
+ oem2Libs.addLib(
+ "libsystem_testlib.so", "/system/${LIB}", "libsystem_extpub_nouses.oem2.so");
+ oem2Libs.pushPublicLibrariesFile("/system/etc/public.libraries-oem2.txt");
+ }
+
+ void pushExtendedPublicProductLibs(ZipFile libApk) throws Exception {
+ var product1Libs = new PublicLibs(libApk);
+ // Push libproduct_extpub<n>.product1.so for each test. Since we cannot unload them, we
+ // need a fresh never-before-loaded library in each loadLibrary call.
+ for (int i = 1; i <= 3; ++i) {
+ product1Libs.addLib("libproduct_testlib.so", "/product/${LIB}",
+ "libproduct_extpub" + i + ".product1.so");
+ }
+ product1Libs.addLib(
+ "libproduct_testlib.so", "/product/${LIB}", "libproduct_extpub.product1.so");
+ product1Libs.pushPublicLibrariesFile("/product/etc/public.libraries-product1.txt");
+ }
+
+ void pushPrivateLibs(ZipFile libApk) throws Exception {
+ // Push the libraries once for each test. Since we cannot unload them, we need a fresh
+ // never-before-loaded library in each loadLibrary call.
+ for (int i = 1; i <= 6; ++i) {
+ pushNativeTestLib(libApk, "libsystem_testlib.so",
+ "/system/${LIB}/libsystem_private" + i + ".so");
+ pushNativeTestLib(libApk, "libsystem_testlib.so",
+ "/system_ext/${LIB}/libsystemext_private" + i + ".so");
+ pushNativeTestLib(libApk, "libproduct_testlib.so",
+ "/product/${LIB}/libproduct_private" + i + ".so");
+ pushNativeTestLib(libApk, "libvendor_testlib.so",
+ "/vendor/${LIB}/libvendor_private" + i + ".so");
+ }
+ }
+
+ void pushSharedLib(String partitionDir, String packageName, String buildJarName)
+ throws Exception {
+ String path = partitionDir + "/framework/" + packageName + ".jar";
+ pushFile(buildJarName, path);
+ // This permissions xml file is necessary to make it possible to depend on the shared
+ // library from the test app, even if it's in the same partition. It makes the library
+ // public to apps in other partitions as well, which is more than we need, but that
+ // being the case we test all shared libraries from all apps.
+ pushString("<permissions>\n"
+ + "<library name=\"" + packageName + "\" file=\"" + path + "\" />\n"
+ + "</permissions>\n",
+ partitionDir + "/etc/permissions/" + packageName + ".xml");
+ }
+
+ void softReboot() throws DeviceNotAvailableException {
+ assertCommandSucceeds("setprop dev.bootcomplete 0");
+ assertCommandSucceeds("stop");
+ assertCommandSucceeds("start");
+ mDevice.waitForDeviceAvailable();
+ }
+
+ String getTestArch() throws DeviceNotAvailableException {
+ if (mTestArch == null) {
+ IAbi abi = mContext.getConfigurationDescriptor().getAbi();
+ mTestArch = abi != null ? abi.getName()
+ : assertCommandSucceeds("getprop ro.product.cpu.abi");
+ }
+ return mTestArch;
+ }
+
+ String libDirName() throws DeviceNotAvailableException {
+ return getTestArch().contains("64") ? "lib64" : "lib";
+ }
+
+ // Pushes the given file contents to the device at the given destination path. destPath is
+ // assumed to have no risk of overlapping with existing files, and is deleted in tearDown(),
+ // along with any directory levels that had to be created.
+ void pushString(String fileContents, String destPath) throws DeviceNotAvailableException {
+ mCleanup.addPath(destPath);
+ assertThat(mDevice.pushString(fileContents, destPath)).isTrue();
+ }
+
+ // Like pushString, but pushes a data file included in the host test.
+ void pushFile(String fileName, String destPath) throws Exception {
+ mCleanup.addPath(destPath);
+ assertThat(mDevice.pushFile(mBuildHelper.getTestFile(fileName), destPath)).isTrue();
+ }
+
+ void pushApk(String apkBaseName, String destPath) throws Exception {
+ pushFile(apkBaseName + ".apk",
+ destPath + "/" + apkBaseName + "/" + apkBaseName + ".apk");
+ }
+
+ // Like pushString, but extracts libnativeloader_testlib.so from the library_container_app
+ // APK and pushes it to destPath. "${LIB}" is replaced with "lib" or "lib64" as appropriate.
+ void pushNativeTestLib(ZipFile libApk, String libName, String destPath) throws Exception {
+ String libApkPath = "lib/" + getTestArch() + "/" + libName;
+ ZipEntry entry = libApk.getEntry(libApkPath);
+ assertWithMessage("Failed to find " + libApkPath + " in library_container_app.apk")
+ .that(entry)
+ .isNotNull();
+
+ File libraryTempFile;
+ try (InputStream inStream = libApk.getInputStream(entry)) {
+ libraryTempFile = writeStreamToTempFile(libName, inStream);
+ }
+
+ destPath = destPath.replace("${LIB}", libDirName());
+
+ mCleanup.addPath(destPath);
+ assertThat(mDevice.pushFile(libraryTempFile, destPath)).isTrue();
+ }
+
+ void installPackage(String apkBaseName) throws Exception {
+ assertThat(mDevice.installPackage(mBuildHelper.getTestFile(apkBaseName + ".apk"),
+ false /* reinstall */))
+ .isNull();
+ }
+
+ String assertCommandSucceeds(String command) throws DeviceNotAvailableException {
+ CommandResult result = mDevice.executeShellV2Command(command);
+ assertWithMessage(result.toString()).that(result.getExitCode()).isEqualTo(0);
+ // Remove trailing \n's.
+ return result.getStdout().trim();
+ }
+ }
+
+ static private File writeStreamToTempFile(String tempFileBaseName, InputStream inStream)
+ throws Exception {
+ File hostTempFile = File.createTempFile(tempFileBaseName, null);
+ try (FileOutputStream outStream = new FileOutputStream(hostTempFile)) {
+ ByteStreams.copy(inStream, outStream);
+ }
+ return hostTempFile;
+ }
+}
diff --git a/libnativeloader/test/src/android/test/lib/TestUtils.java b/libnativeloader/test/src/android/test/lib/TestUtils.java
new file mode 100644
index 0000000..1dd917f
--- /dev/null
+++ b/libnativeloader/test/src/android/test/lib/TestUtils.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 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 android.test.lib;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import org.junit.function.ThrowingRunnable;
+
+public final class TestUtils {
+ public static void assertLibraryNotFound(ThrowingRunnable loadLibrary) {
+ Throwable t = assertThrows(UnsatisfiedLinkError.class, loadLibrary);
+ assertThat(t.getMessage()).containsMatch("dlopen failed: library .* not found");
+ }
+
+ public static void assertLinkerNamespaceError(ThrowingRunnable loadLibrary) {
+ Throwable t = assertThrows(UnsatisfiedLinkError.class, loadLibrary);
+ assertThat(t.getMessage())
+ .containsMatch("dlopen failed: .* is not accessible for the namespace");
+ }
+
+ public static String libPath(String dir, String libName) {
+ String libDirName = InstrumentationRegistry.getArguments().getString("libDirName");
+ return dir + "/" + libDirName + "/lib" + libName + ".so";
+ }
+}
diff --git a/libnativeloader/test/src/android/test/productsharedlib/ProductSharedLib.java b/libnativeloader/test/src/android/test/productsharedlib/ProductSharedLib.java
new file mode 100644
index 0000000..a500d2a
--- /dev/null
+++ b/libnativeloader/test/src/android/test/productsharedlib/ProductSharedLib.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 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 android.test.productsharedlib;
+
+public final class ProductSharedLib {
+ public static void loadLibrary(String name) { System.loadLibrary(name); }
+}
diff --git a/libnativeloader/test/src/android/test/systemextsharedlib/SystemExtSharedLib.java b/libnativeloader/test/src/android/test/systemextsharedlib/SystemExtSharedLib.java
new file mode 100644
index 0000000..1240e12
--- /dev/null
+++ b/libnativeloader/test/src/android/test/systemextsharedlib/SystemExtSharedLib.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 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 android.test.systemextsharedlib;
+
+public final class SystemExtSharedLib {
+ public static void loadLibrary(String name) { System.loadLibrary(name); }
+}
diff --git a/libnativeloader/test/src/android/test/systemsharedlib/SystemSharedLib.java b/libnativeloader/test/src/android/test/systemsharedlib/SystemSharedLib.java
new file mode 100644
index 0000000..8e2af9f
--- /dev/null
+++ b/libnativeloader/test/src/android/test/systemsharedlib/SystemSharedLib.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 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 android.test.systemsharedlib;
+
+public final class SystemSharedLib {
+ public static void loadLibrary(String name) { System.loadLibrary(name); }
+}
diff --git a/libnativeloader/test/src/android/test/vendorsharedlib/VendorSharedLib.java b/libnativeloader/test/src/android/test/vendorsharedlib/VendorSharedLib.java
new file mode 100644
index 0000000..8859b63
--- /dev/null
+++ b/libnativeloader/test/src/android/test/vendorsharedlib/VendorSharedLib.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 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 android.test.vendorsharedlib;
+
+public final class VendorSharedLib {
+ public static void loadLibrary(String name) { System.loadLibrary(name); }
+}
diff --git a/libnativeloader/test/system/AndroidManifest.xml b/libnativeloader/test/system/AndroidManifest.xml
deleted file mode 100644
index c304889..0000000
--- a/libnativeloader/test/system/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * 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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.test.app.system">
-
- <application>
- <activity android:name="android.test.app.TestActivity" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-
-</manifest>
-
diff --git a/libnativeloader/test/test.cpp b/libnativeloader/test/test.cpp
deleted file mode 100644
index b166928..0000000
--- a/libnativeloader/test/test.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * 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.
- */
-#define LOG_TAG "oemlib"
-#include <android-base/logging.h>
-
-static __attribute__((constructor)) void test_lib_init() {
- LOG(DEBUG) << LIBNAME << " loaded";
-}
diff --git a/libnativeloader/test/vendor/AndroidManifest.xml b/libnativeloader/test/vendor/AndroidManifest.xml
deleted file mode 100644
index c4c1a9c..0000000
--- a/libnativeloader/test/vendor/AndroidManifest.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * 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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.test.app.vendor">
-
- <application>
- <activity android:name="android.test.app.TestActivity" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-
-</manifest>
-
diff --git a/libprofile/Android.bp b/libprofile/Android.bp
index ecbcd0b..55bdcd1 100644
--- a/libprofile/Android.bp
+++ b/libprofile/Android.bp
@@ -106,11 +106,6 @@
"libprofile_defaults",
"libart_nativeunwind_defaults",
],
- shared_libs: [
- "libbase",
- "libziparchive",
- ],
- export_shared_lib_headers: ["libbase"],
target: {
android: {
shared_libs: [
@@ -147,10 +142,6 @@
"art_debug_defaults",
"libprofile_defaults",
],
- shared_libs: [
- "libbase",
- "libziparchive",
- ],
target: {
android: {
shared_libs: [
@@ -171,7 +162,6 @@
],
},
},
- export_shared_lib_headers: ["libbase"],
apex_available: [
"com.android.art.debug",
// TODO(b/183882457): This lib doesn't go into com.android.art, but
@@ -195,9 +185,12 @@
"profile/profile_boot_info_test.cc",
"profile/profile_compilation_info_test.cc",
],
- shared_libs: [
+ static_libs: [
"libziparchive",
],
+ shared_libs: [
+ "libz", // libziparchive dependency; must be repeated here since it's a static lib.
+ ],
}
// Version of ART gtest `art_libprofile_tests` bundled with the ART APEX on target.
diff --git a/libprofile/art_standalone_libprofile_tests.xml b/libprofile/art_standalone_libprofile_tests.xml
index 9ecd9a5..65264f4 100644
--- a/libprofile/art_standalone_libprofile_tests.xml
+++ b/libprofile/art_standalone_libprofile_tests.xml
@@ -14,6 +14,8 @@
limitations under the License.
-->
<configuration description="Runs art_standalone_libprofile_tests.">
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.art.apex" />
+
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="art_standalone_libprofile_tests->/data/local/tmp/art_standalone_libprofile_tests/art_standalone_libprofile_tests" />
diff --git a/libprofile/profile/profile_compilation_info.cc b/libprofile/profile/profile_compilation_info.cc
index f135805..bb48713 100644
--- a/libprofile/profile/profile_compilation_info.cc
+++ b/libprofile/profile/profile_compilation_info.cc
@@ -1710,14 +1710,14 @@
if (memcmp(header.GetVersion(), version_, kProfileVersionSize) != 0) {
*error = IsForBootImage() ? "Expected boot profile, got app profile."
: "Expected app profile, got boot profile.";
- return ProfileLoadStatus::kMergeError;
+ return ProfileLoadStatus::kVersionMismatch;
}
// Check if there are too many section infos.
uint32_t section_count = header.GetFileSectionCount();
uint32_t uncompressed_data_size = sizeof(FileHeader) + section_count * sizeof(FileSectionInfo);
if (uncompressed_data_size > GetSizeErrorThresholdBytes()) {
- LOG(ERROR) << "Profile data size exceeds " << GetSizeErrorThresholdBytes()
+ LOG(WARNING) << "Profile data size exceeds " << GetSizeErrorThresholdBytes()
<< " bytes. It has " << uncompressed_data_size << " bytes.";
return ProfileLoadStatus::kBadData;
}
@@ -1743,7 +1743,7 @@
// Allow large profiles for non target builds for the case where we are merging many profiles
// to generate a boot image profile.
if (uncompressed_data_size > GetSizeErrorThresholdBytes()) {
- LOG(ERROR) << "Profile data size exceeds "
+ LOG(WARNING) << "Profile data size exceeds "
<< GetSizeErrorThresholdBytes()
<< " bytes. It has " << uncompressed_data_size << " bytes.";
return ProfileLoadStatus::kBadData;
@@ -2209,7 +2209,8 @@
return vec;
};
for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
- const std::string& profile_key = dex_file->GetLocation();
+ const std::string& dex_location = dex_file->GetLocation();
+ std::string profile_key = info.GetProfileDexFileBaseKey(dex_location);
uint32_t checksum = dex_file->GetLocationChecksum();
uint32_t number_of_classes = dex_file->NumClassDefs();
@@ -2387,7 +2388,8 @@
}
bool ProfileCompilationInfo::UpdateProfileKeys(
- const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
+ const std::vector<std::unique_ptr<const DexFile>>& dex_files, /*out*/ bool* updated) {
+ *updated = false;
for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
for (const std::unique_ptr<DexFileData>& dex_data : info_) {
if (dex_data->checksum == dex_file->GetLocationChecksum() &&
@@ -2407,6 +2409,7 @@
// form the old key.
dex_data->profile_key = MigrateAnnotationInfo(new_profile_key, dex_data->profile_key);
profile_key_map_.Put(dex_data->profile_key, dex_data->profile_index);
+ *updated = true;
}
}
}
diff --git a/libprofile/profile/profile_compilation_info.h b/libprofile/profile/profile_compilation_info.h
index 4366078..76cbf9a 100644
--- a/libprofile/profile/profile_compilation_info.h
+++ b/libprofile/profile/profile_compilation_info.h
@@ -645,7 +645,10 @@
//
// If the new profile key would collide with an existing key (for a different dex)
// the method returns false. Otherwise it returns true.
- bool UpdateProfileKeys(const std::vector<std::unique_ptr<const DexFile>>& dex_files);
+ //
+ // `updated` is set to true if any profile key has been updated by this method.
+ bool UpdateProfileKeys(const std::vector<std::unique_ptr<const DexFile>>& dex_files,
+ /*out*/ bool* updated);
// Checks if the profile is empty.
bool IsEmpty() const;
diff --git a/libprofile/profile/profile_compilation_info_test.cc b/libprofile/profile/profile_compilation_info_test.cc
index 8c9d0df..2ee34f2 100644
--- a/libprofile/profile/profile_compilation_info_test.cc
+++ b/libprofile/profile/profile_compilation_info_test.cc
@@ -40,17 +40,23 @@
CommonArtTest::SetUp();
allocator_.reset(new ArenaAllocator(&pool_));
- dex1 = BuildDex("location1", /*checksum=*/ 1, "LUnique1;", /*num_method_ids=*/ 101);
- dex2 = BuildDex("location2", /*checksum=*/ 2, "LUnique2;", /*num_method_ids=*/ 102);
- dex3 = BuildDex("location3", /*checksum=*/ 3, "LUnique3;", /*num_method_ids=*/ 103);
- dex4 = BuildDex("location4", /*checksum=*/ 4, "LUnique4;", /*num_method_ids=*/ 104);
+ dex1 = BuildDex("location1", /*location_checksum=*/ 1, "LUnique1;", /*num_method_ids=*/ 101);
+ dex2 = BuildDex("location2", /*location_checksum=*/ 2, "LUnique2;", /*num_method_ids=*/ 102);
+ dex3 = BuildDex("location3", /*location_checksum=*/ 3, "LUnique3;", /*num_method_ids=*/ 103);
+ dex4 = BuildDex("location4", /*location_checksum=*/ 4, "LUnique4;", /*num_method_ids=*/ 104);
- dex1_checksum_missmatch =
- BuildDex("location1", /*checksum=*/ 12, "LUnique1;", /*num_method_ids=*/ 101);
- dex1_renamed =
- BuildDex("location1-renamed", /*checksum=*/ 1, "LUnique1;", /*num_method_ids=*/ 101);
- dex2_renamed =
- BuildDex("location2-renamed", /*checksum=*/ 2, "LUnique2;", /*num_method_ids=*/ 102);
+ dex1_checksum_missmatch = BuildDex("location1",
+ /*location_checksum=*/ 12,
+ "LUnique1;",
+ /*num_method_ids=*/ 101);
+ dex1_renamed = BuildDex("location1-renamed",
+ /*location_checksum=*/ 1,
+ "LUnique1;",
+ /*num_method_ids=*/ 101);
+ dex2_renamed = BuildDex("location2-renamed",
+ /*location_checksum=*/ 2,
+ "LUnique2;",
+ /*num_method_ids=*/ 102);
}
protected:
@@ -350,10 +356,16 @@
TEST_F(ProfileCompilationInfoTest, SaveMaxMethods) {
ScratchFile profile;
- const DexFile* dex_max1 = BuildDex(
- "location-max1", /*checksum=*/ 5, "LUniqueMax1;", kMaxMethodIds, kMaxClassIds);
- const DexFile* dex_max2 = BuildDex(
- "location-max2", /*checksum=*/ 6, "LUniqueMax2;", kMaxMethodIds, kMaxClassIds);
+ const DexFile* dex_max1 = BuildDex("location-max1",
+ /*location_checksum=*/ 5,
+ "LUniqueMax1;",
+ kMaxMethodIds,
+ kMaxClassIds);
+ const DexFile* dex_max2 = BuildDex("location-max2",
+ /*location_checksum=*/ 6,
+ "LUniqueMax2;",
+ kMaxMethodIds,
+ kMaxClassIds);
ProfileCompilationInfo saved_info;
@@ -733,11 +745,11 @@
// Save a few methods.
for (uint16_t i = 0; i < std::numeric_limits<ProfileIndexType>::max(); i++) {
std::string location = std::to_string(i);
- const DexFile* dex = BuildDex(location, /*checksum=*/ 1, "LC;", /*num_method_ids=*/ 1);
+ const DexFile* dex = BuildDex(location, /*location_checksum=*/ 1, "LC;", /*num_method_ids=*/ 1);
ASSERT_TRUE(AddMethod(&info, dex, /*method_idx=*/ 0));
}
// Add an extra dex file.
- const DexFile* dex = BuildDex("-1", /*checksum=*/ 1, "LC;", /*num_method_ids=*/ 1);
+ const DexFile* dex = BuildDex("-1", /*location_checksum=*/ 1, "LC;", /*num_method_ids=*/ 1);
ASSERT_FALSE(AddMethod(&info, dex, /*method_idx=*/ 0));
}
@@ -746,11 +758,11 @@
// Save a few methods.
for (uint16_t i = 0; i < std::numeric_limits<ProfileIndexType>::max(); i++) {
std::string location = std::to_string(i);
- const DexFile* dex = BuildDex(location, /*checksum=*/ 1, "LC;", /*num_method_ids=*/ 1);
+ const DexFile* dex = BuildDex(location, /*location_checksum=*/ 1, "LC;", /*num_method_ids=*/ 1);
ASSERT_TRUE(AddMethod(&info, dex, /*method_idx=*/ 0));
}
// Add an extra dex file.
- const DexFile* dex = BuildDex("-1", /*checksum=*/ 1, "LC;", /*num_method_ids=*/ 1);
+ const DexFile* dex = BuildDex("-1", /*location_checksum=*/ 1, "LC;", /*num_method_ids=*/ 1);
ASSERT_FALSE(AddMethod(&info, dex, /*method_idx=*/ 0));
}
@@ -944,7 +956,9 @@
AddMethod(&info, dex2, /*method_idx=*/ 0);
// Update the profile keys based on the original dex files
- ASSERT_TRUE(info.UpdateProfileKeys(dex_files));
+ bool updated = false;
+ ASSERT_TRUE(info.UpdateProfileKeys(dex_files, &updated));
+ ASSERT_TRUE(updated);
// Verify that we find the methods when searched with the original dex files.
for (const std::unique_ptr<const DexFile>& dex : dex_files) {
@@ -970,7 +984,9 @@
AddMethod(&info, dex2, /*method_idx=*/ 0, Hotness::kFlagHot, annotation);
// Update the profile keys based on the original dex files
- ASSERT_TRUE(info.UpdateProfileKeys(dex_files));
+ bool updated = false;
+ ASSERT_TRUE(info.UpdateProfileKeys(dex_files, &updated));
+ ASSERT_TRUE(updated);
// Verify that we find the methods when searched with the original dex files.
for (const std::unique_ptr<const DexFile>& dex : dex_files) {
@@ -993,7 +1009,9 @@
AddMethod(&info, dex2, /*method_idx=*/ 0);
// Update the profile keys based on the original dex files.
- ASSERT_TRUE(info.UpdateProfileKeys(dex_files));
+ bool updated = false;
+ ASSERT_TRUE(info.UpdateProfileKeys(dex_files, &updated));
+ ASSERT_FALSE(updated);
// Verify that we did not perform any update and that we cannot find anything with the new
// location.
@@ -1025,7 +1043,9 @@
// This will cause the rename to fail because an existing entry would already have that name.
AddMethod(&info, dex1_renamed, /*method_idx=*/ 0);
- ASSERT_FALSE(info.UpdateProfileKeys(dex_files));
+ bool updated = false;
+ ASSERT_FALSE(info.UpdateProfileKeys(dex_files, &updated));
+ ASSERT_FALSE(updated);
// Release the ownership as this is held by the test class;
for (std::unique_ptr<const DexFile>& dex : dex_files) {
@@ -1167,12 +1187,12 @@
ScratchFile profile;
const DexFile* dex1_1000 = BuildDex("location1_1000",
- /*checksum=*/ 7,
+ /*location_checksum=*/ 7,
"LC1_1000;",
/*num_method_ids=*/ 1u,
/*num_class_ids=*/ 1000u);
const DexFile* dex2_1000 = BuildDex("location2_1000",
- /*checksum=*/ 8,
+ /*location_checksum=*/ 8,
"LC2_1000;",
/*num_method_ids=*/ 1u,
/*num_class_ids=*/ 1000u);
diff --git a/oatdump/Android.bp b/oatdump/Android.bp
index 5724280..ba7ceb5 100644
--- a/oatdump/Android.bp
+++ b/oatdump/Android.bp
@@ -70,6 +70,9 @@
"libdexfile",
"libprofile",
],
+ static_libs: [
+ "libelffile",
+ ],
},
host: {
// Make the host binary static, except for system libraries.
@@ -124,6 +127,9 @@
"libdexfiled",
"libprofiled",
],
+ static_libs: [
+ "libelffiled",
+ ],
},
host: {
// Make the host binary static, except for system libraries.
diff --git a/oatdump/art_standalone_oatdump_tests.xml b/oatdump/art_standalone_oatdump_tests.xml
index bcd94ed..ab11b11 100644
--- a/oatdump/art_standalone_oatdump_tests.xml
+++ b/oatdump/art_standalone_oatdump_tests.xml
@@ -14,6 +14,8 @@
limitations under the License.
-->
<configuration description="Runs art_standalone_oatdump_tests.">
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.art.apex" />
+
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="art_standalone_oatdump_tests->/data/local/tmp/art_standalone_oatdump_tests/art_standalone_oatdump_tests" />
@@ -54,7 +56,8 @@
<!-- ART Mainline Module (external (AOSP) version). -->
<option name="mainline-module-package-name" value="com.android.art" />
</object>
-
+ <!-- Skip on HWASan. TODO(b/230394041): Re-enable -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.SkipHWASanModuleController" />
<!-- Only run tests if the device under test is SDK version 31 (Android 12) or above. -->
<object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" />
</configuration>
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index e69b32a..0d163bd 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -45,7 +45,6 @@
#include "class_linker-inl.h"
#include "class_linker.h"
#include "class_root-inl.h"
-#include "compiled_method.h"
#include "debug/debug_info.h"
#include "debug/elf_debug_writer.h"
#include "debug/method_debug_info.h"
@@ -183,17 +182,17 @@
builder_->WriteDynamicSection();
const OatHeader& oat_header = oat_file_->GetOatHeader();
- #define DO_TRAMPOLINE(fn_name) \
- if (oat_header.Get ## fn_name ## Offset() != 0) { \
- debug::MethodDebugInfo info = {}; \
- info.custom_name = #fn_name; \
- info.isa = oat_header.GetInstructionSet(); \
- info.is_code_address_text_relative = true; \
- size_t code_offset = oat_header.Get ## fn_name ## Offset(); \
- code_offset -= CompiledCode::CodeDelta(oat_header.GetInstructionSet()); \
- info.code_address = code_offset - oat_header.GetExecutableOffset(); \
- info.code_size = 0; /* The symbol lasts until the next symbol. */ \
- method_debug_infos_.push_back(std::move(info)); \
+ #define DO_TRAMPOLINE(fn_name) \
+ if (oat_header.Get ## fn_name ## Offset() != 0) { \
+ debug::MethodDebugInfo info = {}; \
+ info.custom_name = #fn_name; \
+ info.isa = oat_header.GetInstructionSet(); \
+ info.is_code_address_text_relative = true; \
+ size_t code_offset = oat_header.Get ## fn_name ## Offset(); \
+ code_offset -= GetInstructionSetEntryPointAdjustment(oat_header.GetInstructionSet()); \
+ info.code_address = code_offset - oat_header.GetExecutableOffset(); \
+ info.code_size = 0; /* The symbol lasts until the next symbol. */ \
+ method_debug_infos_.push_back(std::move(info)); \
}
DO_TRAMPOLINE(JniDlsymLookupTrampoline);
DO_TRAMPOLINE(JniDlsymLookupCriticalTrampoline);
diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h
index 3ec5b94..708befe 100644
--- a/oatdump/oatdump_test.h
+++ b/oatdump/oatdump_test.h
@@ -328,7 +328,9 @@
auto post_fork_fn = []() {
setpgid(0, 0); // Change process groups, so we don't get reaped by ProcessManager.
- return true; // Ignore setpgid failures.
+ // Ignore setpgid failures.
+ return setenv("ANDROID_LOG_TAGS", "*:e", 1) == 0; // We're only interested in errors and
+ // fatal logs.
};
ForkAndExecResult res = ForkAndExec(exec_argv, post_fork_fn, line_buf_fn);
diff --git a/odrefresh/Android.bp b/odrefresh/Android.bp
index 8fee83a..9b08940 100644
--- a/odrefresh/Android.bp
+++ b/odrefresh/Android.bp
@@ -47,7 +47,7 @@
],
static_libs: [
"libc++fs",
- "libtinyxml2",
+ "libmodules-utils-build",
],
tidy: true,
tidy_disabled_srcs: [":art-apex-cache-info"],
@@ -123,6 +123,7 @@
local_include_dirs: ["include"],
header_libs: ["libbase_headers"],
srcs: ["odrefresh_broken.cc"],
+ installable: false,
apex_available: ["test_jitzygote_com.android.art"],
}
@@ -155,6 +156,7 @@
host_supported: true,
export_include_dirs: ["include"],
local_include_dirs: ["include"],
+ header_libs: ["libart_headers"],
shared_libs: ["libartbase"],
target: {
android: {
diff --git a/odrefresh/odr_compilation_log.cc b/odrefresh/odr_compilation_log.cc
index 9c50817..0c8dda8 100644
--- a/odrefresh/odr_compilation_log.cc
+++ b/odrefresh/odr_compilation_log.cc
@@ -185,6 +185,12 @@
return true;
}
+ // The backoff time is for avoiding too many failed attempts. It should not be applied if the last
+ // compilation was successful.
+ if (entries_.back().exit_code == ExitCode::kCompilationSuccess) {
+ return true;
+ }
+
if (trigger == OdrMetrics::Trigger::kApexVersionMismatch ||
trigger == OdrMetrics::Trigger::kDexFilesChanged) {
// Things have changed since the last run.
diff --git a/odrefresh/odr_compilation_log_test.cc b/odrefresh/odr_compilation_log_test.cc
index 46cea79..f28d849 100644
--- a/odrefresh/odr_compilation_log_test.cc
+++ b/odrefresh/odr_compilation_log_test.cc
@@ -109,7 +109,7 @@
/*apex_version=*/1,
/*last_update_millis=*/762,
OdrMetrics::Trigger::kApexVersionMismatch,
- ExitCode::kCompilationSuccess);
+ ExitCode::kCompilationFailed);
ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kApexVersionMismatch));
ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kDexFilesChanged));
ASSERT_FALSE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown));
@@ -180,8 +180,8 @@
OdrMetrics::Trigger::kApexVersionMismatch,
start_time,
ExitCode::kCompilationSuccess);
- ASSERT_FALSE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time));
- ASSERT_FALSE(
+ ASSERT_TRUE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time));
+ ASSERT_TRUE(
ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay / 4));
ASSERT_TRUE(
ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time + kSecondsPerDay / 2));
@@ -382,7 +382,7 @@
kLastUpdateMillis,
OdrMetrics::Trigger::kApexVersionMismatch,
start_time,
- ExitCode::kCompilationSuccess);
+ ExitCode::kCompilationFailed);
ASSERT_FALSE(ocl.ShouldAttemptCompile(OdrMetrics::Trigger::kUnknown, start_time));
}
}
diff --git a/odrefresh/odr_config.h b/odrefresh/odr_config.h
index 0475466..76375ac 100644
--- a/odrefresh/odr_config.h
+++ b/odrefresh/odr_config.h
@@ -21,6 +21,7 @@
#include <optional>
#include <string>
#include <unordered_map>
+#include <unordered_set>
#include <vector>
#include "android-base/file.h"
@@ -40,6 +41,22 @@
// everything if any property matching a prefix changes.
constexpr const char* kCheckedSystemPropertyPrefixes[]{"dalvik.vm.", "ro.dalvik.vm."};
+// System property for the phenotype flag to override the device or default-configured
+// system server compiler filter setting.
+static constexpr char kSystemPropertySystemServerCompilerFilterOverride[] =
+ "persist.device_config.runtime_native_boot.systemservercompilerfilter_override";
+
+// The list of system properties that odrefresh ignores. They don't affect compilation results.
+const std::unordered_set<std::string> kIgnoredSystemProperties{
+ "dalvik.vm.dex2oat-cpu-set",
+ "dalvik.vm.dex2oat-threads",
+ "dalvik.vm.boot-dex2oat-cpu-set",
+ "dalvik.vm.boot-dex2oat-threads",
+ "dalvik.vm.restore-dex2oat-cpu-set",
+ "dalvik.vm.restore-dex2oat-threads",
+ "dalvik.vm.background-dex2oat-cpu-set",
+ "dalvik.vm.background-dex2oat-threads"};
+
struct SystemPropertyConfig {
const char* name;
const char* default_value;
@@ -55,7 +72,10 @@
// requirement (go/platform-experiments-flags#pre-requisites).
const android::base::NoDestructor<std::vector<SystemPropertyConfig>> kSystemProperties{
{SystemPropertyConfig{.name = "persist.device_config.runtime_native_boot.enable_uffd_gc",
- .default_value = "false"}}};
+ .default_value = "false"},
+ SystemPropertyConfig{.name = kPhDisableCompactDex, .default_value = "false"},
+ SystemPropertyConfig{.name = kSystemPropertySystemServerCompilerFilterOverride,
+ .default_value = ""}}};
// An enumeration of the possible zygote configurations on Android.
enum class ZygoteKind : uint8_t {
@@ -83,6 +103,7 @@
InstructionSet isa_;
std::string program_name_;
std::string system_server_classpath_;
+ std::string boot_image_compiler_filter_;
std::string system_server_compiler_filter_;
ZygoteKind zygote_kind_;
std::string boot_classpath_;
@@ -168,6 +189,9 @@
const std::string& GetSystemServerClasspath() const {
return system_server_classpath_;
}
+ const std::string& GetBootImageCompilerFilter() const {
+ return boot_image_compiler_filter_;
+ }
const std::string& GetSystemServerCompilerFilter() const {
return system_server_compiler_filter_;
}
@@ -204,6 +228,9 @@
system_server_classpath_ = classpath;
}
+ void SetBootImageCompilerFilter(const std::string& filter) {
+ boot_image_compiler_filter_ = filter;
+ }
void SetSystemServerCompilerFilter(const std::string& filter) {
system_server_compiler_filter_ = filter;
}
@@ -248,6 +275,7 @@
case art::InstructionSet::kX86:
case art::InstructionSet::kX86_64:
return std::make_pair(art::InstructionSet::kX86, art::InstructionSet::kX86_64);
+ case art::InstructionSet::kRiscv64:
case art::InstructionSet::kThumb2:
case art::InstructionSet::kNone:
LOG(FATAL) << "Invalid instruction set " << isa_;
diff --git a/odrefresh/odr_fs_utils.cc b/odrefresh/odr_fs_utils.cc
index 22cc1b6..3ed8021 100644
--- a/odrefresh/odr_fs_utils.cc
+++ b/odrefresh/odr_fs_utils.cc
@@ -68,9 +68,12 @@
}
if (rmdir(dir_path.c_str()) != 0) {
- LOG(ERROR) << "Failed to delete '" << dir_path << "'";
- return false;
+ // It's possible that we are not able to remove the directory itself. For example, when
+ // odrefresh is running in CompOS, the staging dir is prepared beforehand passed to the VM as an
+ // FD. In this case, just log and ignore the error. It's okay to keep the directory.
+ LOG(WARNING) << "Failed to delete '" << dir_path << "'";
}
+
return true;
}
diff --git a/odrefresh/odr_metrics.cc b/odrefresh/odr_metrics.cc
index 4bddb17..296dce6 100644
--- a/odrefresh/odr_metrics.cc
+++ b/odrefresh/odr_metrics.cc
@@ -36,7 +36,7 @@
namespace odrefresh {
OdrMetrics::OdrMetrics(const std::string& cache_directory, const std::string& metrics_file)
- : cache_directory_(cache_directory), metrics_file_(metrics_file), status_(Status::kOK) {
+ : cache_directory_(cache_directory), metrics_file_(metrics_file) {
DCHECK(StartsWith(metrics_file_, "/"));
// Remove existing metrics file if it exists.
@@ -56,36 +56,54 @@
}
OdrMetrics::~OdrMetrics() {
- cache_space_free_end_mib_ = GetFreeSpaceMiB(cache_directory_);
+ CaptureSpaceFreeEnd();
- // Log metrics only if odrefresh detected a reason to compile.
- if (trigger_.has_value()) {
+ // Log metrics only if this is explicitly enabled (typically when compilation was done or an error
+ // occurred).
+ if (enabled_) {
WriteToFile(metrics_file_, this);
}
}
-void OdrMetrics::SetCompilationTime(int32_t seconds) {
+void OdrMetrics::CaptureSpaceFreeEnd() {
+ cache_space_free_end_mib_ = GetFreeSpaceMiB(cache_directory_);
+}
+
+void OdrMetrics::SetCompilationTime(int32_t millis) {
switch (stage_) {
case Stage::kPrimaryBootClasspath:
- primary_bcp_compilation_seconds_ = seconds;
+ primary_bcp_compilation_millis_ = millis;
break;
case Stage::kSecondaryBootClasspath:
- secondary_bcp_compilation_seconds_ = seconds;
+ secondary_bcp_compilation_millis_ = millis;
break;
case Stage::kSystemServerClasspath:
- system_server_compilation_seconds_ = seconds;
+ system_server_compilation_millis_ = millis;
break;
case Stage::kCheck:
case Stage::kComplete:
case Stage::kPreparation:
case Stage::kUnknown:
- break;
+ LOG(FATAL) << "Unexpected stage " << stage_ << " when setting compilation time";
}
}
-void OdrMetrics::SetStage(Stage stage) {
- if (status_ == Status::kOK) {
- stage_ = stage;
+void OdrMetrics::SetDex2OatResult(const ExecResult& dex2oat_result) {
+ switch (stage_) {
+ case Stage::kPrimaryBootClasspath:
+ primary_bcp_dex2oat_result_ = dex2oat_result;
+ break;
+ case Stage::kSecondaryBootClasspath:
+ secondary_bcp_dex2oat_result_ = dex2oat_result;
+ break;
+ case Stage::kSystemServerClasspath:
+ system_server_dex2oat_result_ = dex2oat_result;
+ break;
+ case Stage::kCheck:
+ case Stage::kComplete:
+ case Stage::kPreparation:
+ case Stage::kUnknown:
+ LOG(FATAL) << "Unexpected stage " << stage_ << " when setting dex2oat result";
}
}
@@ -115,32 +133,41 @@
return static_cast<int32_t>(free_space_mib);
}
-bool OdrMetrics::ToRecord(/*out*/OdrMetricsRecord* record) const {
- if (!trigger_.has_value()) {
- return false;
+OdrMetricsRecord OdrMetrics::ToRecord() const {
+ return {
+ .odrefresh_metrics_version = kOdrefreshMetricsVersion,
+ .art_apex_version = art_apex_version_,
+ .trigger = static_cast<int32_t>(trigger_),
+ .stage_reached = static_cast<int32_t>(stage_),
+ .status = static_cast<int32_t>(status_),
+ .cache_space_free_start_mib = cache_space_free_start_mib_,
+ .cache_space_free_end_mib = cache_space_free_end_mib_,
+ .primary_bcp_compilation_millis = primary_bcp_compilation_millis_,
+ .secondary_bcp_compilation_millis = secondary_bcp_compilation_millis_,
+ .system_server_compilation_millis = system_server_compilation_millis_,
+ .primary_bcp_dex2oat_result = ConvertExecResult(primary_bcp_dex2oat_result_),
+ .secondary_bcp_dex2oat_result = ConvertExecResult(secondary_bcp_dex2oat_result_),
+ .system_server_dex2oat_result = ConvertExecResult(system_server_dex2oat_result_),
+ };
+}
+
+OdrMetricsRecord::Dex2OatExecResult OdrMetrics::ConvertExecResult(
+ const std::optional<ExecResult>& result) {
+ if (result.has_value()) {
+ return OdrMetricsRecord::Dex2OatExecResult(result.value());
+ } else {
+ return {};
}
- record->art_apex_version = art_apex_version_;
- record->trigger = static_cast<uint32_t>(trigger_.value());
- record->stage_reached = static_cast<uint32_t>(stage_);
- record->status = static_cast<uint32_t>(status_);
- record->primary_bcp_compilation_seconds = primary_bcp_compilation_seconds_;
- record->secondary_bcp_compilation_seconds = secondary_bcp_compilation_seconds_;
- record->system_server_compilation_seconds = system_server_compilation_seconds_;
- record->cache_space_free_start_mib = cache_space_free_start_mib_;
- record->cache_space_free_end_mib = cache_space_free_end_mib_;
- return true;
}
void OdrMetrics::WriteToFile(const std::string& path, const OdrMetrics* metrics) {
- OdrMetricsRecord record;
- if (!metrics->ToRecord(&record)) {
- LOG(ERROR) << "Attempting to report metrics without a compilation trigger.";
- return;
- }
+ OdrMetricsRecord record = metrics->ToRecord();
- // Preserve order from frameworks/proto_logging/stats/atoms.proto in metrics file written.
- std::ofstream ofs(path);
- ofs << record;
+ const android::base::Result<void>& result = record.WriteToFile(path);
+ if (!result.ok()) {
+ LOG(ERROR) << "Failed to report metrics to file: " << path
+ << ", error: " << result.error().message();
+ }
}
} // namespace odrefresh
diff --git a/odrefresh/odr_metrics.h b/odrefresh/odr_metrics.h
index cd80bef..75e75b0 100644
--- a/odrefresh/odr_metrics.h
+++ b/odrefresh/odr_metrics.h
@@ -24,6 +24,7 @@
#include <string>
#include "base/macros.h"
+#include "exec_utils.h"
#include "odr_metrics_record.h"
namespace art {
@@ -54,9 +55,12 @@
kNoSpace = 2,
kIoError = 3,
kDex2OatError = 4,
- kTimeLimitExceeded = 5,
+ // Value 5 was kTimeLimitExceeded, but has been removed in favour of
+ // reporting the exit code for Dex2Oat (set to ExecResult::kTimedOut)
kStagingFailed = 6,
kInstallFailed = 7,
+ // Failed to access the dalvik-cache directory due to lack of permission.
+ kDalvikCachePermissionDenied = 8,
};
// Enumeration describing the cause of compilation (if any) in odrefresh.
@@ -74,20 +78,17 @@
const std::string& metrics_file = kOdrefreshMetricsFile);
~OdrMetrics();
+ // Enables/disables metrics writing.
+ void SetEnabled(bool value) { enabled_ = value; }
+
// Gets the ART APEX that metrics are being collected on behalf of.
- int64_t GetArtApexVersion() const {
- return art_apex_version_;
- }
+ int64_t GetArtApexVersion() const { return art_apex_version_; }
// Sets the ART APEX that metrics are being collected on behalf of.
- void SetArtApexVersion(int64_t version) {
- art_apex_version_ = version;
- }
+ void SetArtApexVersion(int64_t version) { art_apex_version_ = version; }
// Gets the ART APEX last update time in milliseconds.
- int64_t GetArtApexLastUpdateMillis() const {
- return art_apex_last_update_millis_;
- }
+ int64_t GetArtApexLastUpdateMillis() const { return art_apex_last_update_millis_; }
// Sets the ART APEX last update time in milliseconds.
void SetArtApexLastUpdateMillis(int64_t last_update_millis) {
@@ -96,28 +97,27 @@
// Gets the trigger for metrics collection. The trigger is the reason why odrefresh considers
// compilation necessary.
- Trigger GetTrigger() const {
- return trigger_.has_value() ? trigger_.value() : Trigger::kUnknown;
- }
+ Trigger GetTrigger() const { return trigger_; }
// Sets the trigger for metrics collection. The trigger is the reason why odrefresh considers
// compilation necessary. Only call this method if compilation is necessary as the presence
// of a trigger means we will try to record and upload metrics.
- void SetTrigger(const Trigger trigger) {
- trigger_ = trigger;
- }
+ void SetTrigger(const Trigger trigger) { trigger_ = trigger; }
// Sets the execution status of the current odrefresh processing stage.
- void SetStatus(const Status status) {
- status_ = status;
- }
+ void SetStatus(const Status status) { status_ = status; }
// Sets the current odrefresh processing stage.
- void SetStage(Stage stage);
+ void SetStage(Stage stage) { stage_ = stage; }
- // Record metrics into an OdrMetricsRecord.
- // returns true on success, false if instance is not valid (because the trigger value is not set).
- bool ToRecord(/*out*/OdrMetricsRecord* record) const;
+ // Sets the result of the current dex2oat invocation.
+ void SetDex2OatResult(const ExecResult& dex2oat_result);
+
+ // Captures the current free space as the end free space.
+ void CaptureSpaceFreeEnd();
+
+ // Records metrics into an OdrMetricsRecord.
+ OdrMetricsRecord ToRecord() const;
private:
OdrMetrics(const OdrMetrics&) = delete;
@@ -126,23 +126,45 @@
static int32_t GetFreeSpaceMiB(const std::string& path);
static void WriteToFile(const std::string& path, const OdrMetrics* metrics);
- void SetCompilationTime(int32_t seconds);
+ void SetCompilationTime(int32_t millis);
+ static OdrMetricsRecord::Dex2OatExecResult
+ ConvertExecResult(const std::optional<ExecResult>& result);
const std::string cache_directory_;
const std::string metrics_file_;
+ bool enabled_ = false;
+
int64_t art_apex_version_ = 0;
int64_t art_apex_last_update_millis_ = 0;
- std::optional<Trigger> trigger_ = {}; // metrics are only logged if compilation is triggered.
+ Trigger trigger_ = Trigger::kUnknown;
Stage stage_ = Stage::kUnknown;
Status status_ = Status::kUnknown;
- int32_t primary_bcp_compilation_seconds_ = 0;
- int32_t secondary_bcp_compilation_seconds_ = 0;
- int32_t system_server_compilation_seconds_ = 0;
int32_t cache_space_free_start_mib_ = 0;
int32_t cache_space_free_end_mib_ = 0;
+ // The total time spent on compiling primary BCP.
+ int32_t primary_bcp_compilation_millis_ = 0;
+
+ // The result of the dex2oat invocation for compiling primary BCP, or `std::nullopt` if dex2oat is
+ // not invoked.
+ std::optional<ExecResult> primary_bcp_dex2oat_result_;
+
+ // The total time spent on compiling secondary BCP.
+ int32_t secondary_bcp_compilation_millis_ = 0;
+
+ // The result of the dex2oat invocation for compiling secondary BCP, or `std::nullopt` if dex2oat
+ // is not invoked.
+ std::optional<ExecResult> secondary_bcp_dex2oat_result_;
+
+ // The total time spent on compiling system server.
+ int32_t system_server_compilation_millis_ = 0;
+
+ // The result of the last dex2oat invocation for compiling system server, or `std::nullopt` if
+ // dex2oat is not invoked.
+ std::optional<ExecResult> system_server_dex2oat_result_;
+
friend class ScopedOdrCompilationTimer;
};
@@ -155,8 +177,8 @@
~ScopedOdrCompilationTimer() {
auto elapsed_time = std::chrono::steady_clock::now() - start_;
- auto elapsed_seconds = std::chrono::duration_cast<std::chrono::seconds>(elapsed_time);
- metrics_.SetCompilationTime(static_cast<int32_t>(elapsed_seconds.count()));
+ auto elapsed_millis = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time);
+ metrics_.SetCompilationTime(static_cast<int32_t>(elapsed_millis.count()));
}
private:
diff --git a/odrefresh/odr_metrics_record.cc b/odrefresh/odr_metrics_record.cc
index fc135d3..14ccb0c 100644
--- a/odrefresh/odr_metrics_record.cc
+++ b/odrefresh/odr_metrics_record.cc
@@ -17,56 +17,160 @@
#include "odr_metrics_record.h"
#include <iosfwd>
-#include <istream>
-#include <ostream>
-#include <streambuf>
#include <string>
+#include "android-base/logging.h"
+#include "tinyxml2.h"
+
namespace art {
namespace odrefresh {
-std::istream& operator>>(std::istream& is, OdrMetricsRecord& record) {
- // Block I/O related exceptions
- auto saved_exceptions = is.exceptions();
- is.exceptions(std::ios_base::iostate {});
+namespace {
+android::base::Result<int64_t> ReadInt64(tinyxml2::XMLElement* parent, const char* name) {
+ tinyxml2::XMLElement* element = parent->FirstChildElement(name);
+ if (element == nullptr) {
+ return Errorf("Expected Odrefresh metric {} not found", name);
+ }
- // The order here matches the field order of MetricsRecord.
- is >> record.art_apex_version >> std::ws;
- is >> record.trigger >> std::ws;
- is >> record.stage_reached >> std::ws;
- is >> record.status >> std::ws;
- is >> record.primary_bcp_compilation_seconds >> std::ws;
- is >> record.secondary_bcp_compilation_seconds >> std::ws;
- is >> record.system_server_compilation_seconds >> std::ws;
- is >> record.cache_space_free_start_mib >> std::ws;
- is >> record.cache_space_free_end_mib >> std::ws;
-
- // Restore I/O related exceptions
- is.exceptions(saved_exceptions);
- return is;
+ int64_t metric;
+ tinyxml2::XMLError result = element->QueryInt64Text(&metric);
+ if (result == tinyxml2::XML_SUCCESS) {
+ return metric;
+ } else {
+ return Errorf("Odrefresh metric {} is not an int64", name);
+ }
}
-std::ostream& operator<<(std::ostream& os, const OdrMetricsRecord& record) {
- static const char kSpace = ' ';
+android::base::Result<int32_t> ReadInt32(tinyxml2::XMLElement* parent, const char* name) {
+ tinyxml2::XMLElement* element = parent->FirstChildElement(name);
+ if (element == nullptr) {
+ return Errorf("Expected Odrefresh metric {} not found", name);
+ }
- // Block I/O related exceptions
- auto saved_exceptions = os.exceptions();
- os.exceptions(std::ios_base::iostate {});
+ int32_t metric;
+ tinyxml2::XMLError result = element->QueryIntText(&metric);
+ if (result == tinyxml2::XML_SUCCESS) {
+ return metric;
+ } else {
+ return Errorf("Odrefresh metric {} is not an int32", name);
+ }
+}
+
+android::base::Result<int32_t> ReadInt32Attribute(tinyxml2::XMLElement* element,
+ const char* element_name,
+ const char* attribute_name,
+ int min_value,
+ int max_value) {
+ int32_t value;
+ tinyxml2::XMLError result = element->QueryAttribute(attribute_name, &value);
+ if (result != tinyxml2::XML_SUCCESS) {
+ return Errorf("Expected Odrefresh metric {}.{} is not an int32", element_name, attribute_name);
+ }
+
+ if (value < min_value || value > max_value) {
+ return Errorf(
+ "Odrefresh metric {}.{} has a value ({}) outside of the expected range ([{}, {}])",
+ element_name,
+ attribute_name,
+ value,
+ min_value,
+ max_value);
+ }
+
+ return value;
+}
+
+android::base::Result<OdrMetricsRecord::Dex2OatExecResult> ReadExecResult(
+ tinyxml2::XMLElement* parent, const char* nodeName) {
+ tinyxml2::XMLElement* element = parent->FirstChildElement(nodeName);
+ if (element == nullptr) {
+ return Errorf("Expected Odrefresh metric {} not found", nodeName);
+ }
+
+ return OdrMetricsRecord::Dex2OatExecResult(
+ OR_RETURN(ReadInt32Attribute(element, nodeName, "status", 0, kExecResultNotRun)),
+ OR_RETURN(ReadInt32Attribute(element, nodeName, "exit-code", -1, 255)),
+ OR_RETURN(ReadInt32Attribute(element, nodeName, "signal", 0, SIGRTMAX)));
+}
+
+template <typename T>
+void AddMetric(tinyxml2::XMLElement* parent, const char* name, const T& value) {
+ parent->InsertNewChildElement(name)->SetText(value);
+}
+
+void AddResult(tinyxml2::XMLElement* parent,
+ const char* name,
+ const OdrMetricsRecord::Dex2OatExecResult& execResult) {
+ tinyxml2::XMLElement* result = parent->InsertNewChildElement(name);
+ result->SetAttribute("status", execResult.status);
+ result->SetAttribute("exit-code", execResult.exit_code);
+ result->SetAttribute("signal", execResult.signal);
+}
+} // namespace
+
+android::base::Result<void> OdrMetricsRecord::ReadFromFile(const std::string& filename) {
+ tinyxml2::XMLDocument xml_document;
+ tinyxml2::XMLError result = xml_document.LoadFile(filename.data());
+ if (result != tinyxml2::XML_SUCCESS) {
+ return android::base::Error() << xml_document.ErrorStr();
+ }
+
+ tinyxml2::XMLElement* metrics = xml_document.FirstChildElement("odrefresh_metrics");
+ if (metrics == nullptr) {
+ return Errorf("odrefresh_metrics element not found in {}", filename);
+ }
+
+ odrefresh_metrics_version = OR_RETURN(ReadInt32(metrics, "odrefresh_metrics_version"));
+ if (odrefresh_metrics_version != kOdrefreshMetricsVersion) {
+ return Errorf("odrefresh_metrics_version {} is different than expected ({})",
+ odrefresh_metrics_version,
+ kOdrefreshMetricsVersion);
+ }
+
+ art_apex_version = OR_RETURN(ReadInt64(metrics, "art_apex_version"));
+ trigger = OR_RETURN(ReadInt32(metrics, "trigger"));
+ stage_reached = OR_RETURN(ReadInt32(metrics, "stage_reached"));
+ status = OR_RETURN(ReadInt32(metrics, "status"));
+ cache_space_free_start_mib = OR_RETURN(ReadInt32(metrics, "cache_space_free_start_mib"));
+ cache_space_free_end_mib = OR_RETURN(ReadInt32(metrics, "cache_space_free_end_mib"));
+ primary_bcp_compilation_millis = OR_RETURN(ReadInt32(metrics, "primary_bcp_compilation_millis"));
+ secondary_bcp_compilation_millis =
+ OR_RETURN(ReadInt32(metrics, "secondary_bcp_compilation_millis"));
+ system_server_compilation_millis =
+ OR_RETURN(ReadInt32(metrics, "system_server_compilation_millis"));
+ primary_bcp_dex2oat_result = OR_RETURN(ReadExecResult(metrics, "primary_bcp_dex2oat_result"));
+ secondary_bcp_dex2oat_result = OR_RETURN(ReadExecResult(metrics, "secondary_bcp_dex2oat_result"));
+ system_server_dex2oat_result = OR_RETURN(ReadExecResult(metrics, "system_server_dex2oat_result"));
+
+ return {};
+}
+
+android::base::Result<void> OdrMetricsRecord::WriteToFile(const std::string& filename) const {
+ tinyxml2::XMLDocument xml_document;
+ tinyxml2::XMLElement* metrics = xml_document.NewElement("odrefresh_metrics");
+ xml_document.InsertEndChild(metrics);
// The order here matches the field order of MetricsRecord.
- os << record.art_apex_version << kSpace;
- os << record.trigger << kSpace;
- os << record.stage_reached << kSpace;
- os << record.status << kSpace;
- os << record.primary_bcp_compilation_seconds << kSpace;
- os << record.secondary_bcp_compilation_seconds << kSpace;
- os << record.system_server_compilation_seconds << kSpace;
- os << record.cache_space_free_start_mib << kSpace;
- os << record.cache_space_free_end_mib << std::endl;
+ AddMetric(metrics, "odrefresh_metrics_version", odrefresh_metrics_version);
+ AddMetric(metrics, "art_apex_version", art_apex_version);
+ AddMetric(metrics, "trigger", trigger);
+ AddMetric(metrics, "stage_reached", stage_reached);
+ AddMetric(metrics, "status", status);
+ AddMetric(metrics, "cache_space_free_start_mib", cache_space_free_start_mib);
+ AddMetric(metrics, "cache_space_free_end_mib", cache_space_free_end_mib);
+ AddMetric(metrics, "primary_bcp_compilation_millis", primary_bcp_compilation_millis);
+ AddMetric(metrics, "secondary_bcp_compilation_millis", secondary_bcp_compilation_millis);
+ AddMetric(metrics, "system_server_compilation_millis", system_server_compilation_millis);
+ AddResult(metrics, "primary_bcp_dex2oat_result", primary_bcp_dex2oat_result);
+ AddResult(metrics, "secondary_bcp_dex2oat_result", secondary_bcp_dex2oat_result);
+ AddResult(metrics, "system_server_dex2oat_result", system_server_dex2oat_result);
- // Restore I/O related exceptions
- os.exceptions(saved_exceptions);
- return os;
+ tinyxml2::XMLError result = xml_document.SaveFile(filename.data(), /*compact=*/true);
+ if (result == tinyxml2::XML_SUCCESS) {
+ return {};
+ } else {
+ return android::base::Error() << xml_document.ErrorStr();
+ }
}
} // namespace odrefresh
diff --git a/odrefresh/odr_metrics_record.h b/odrefresh/odr_metrics_record.h
index 9dd51a6..eb3ef9e 100644
--- a/odrefresh/odr_metrics_record.h
+++ b/odrefresh/odr_metrics_record.h
@@ -20,43 +20,68 @@
#include <cstdint>
#include <iosfwd> // For forward-declaration of std::string.
+#include "android-base/result.h"
+#include "exec_utils.h"
+#include "tinyxml2.h"
+
namespace art {
namespace odrefresh {
// Default location for storing metrics from odrefresh.
-constexpr const char* kOdrefreshMetricsFile = "/data/misc/odrefresh/odrefresh-metrics.txt";
+constexpr const char* kOdrefreshMetricsFile = "/data/misc/odrefresh/odrefresh-metrics.xml";
+
+// Initial OdrefreshMetrics version
+static constexpr int32_t kOdrefreshMetricsVersion = 3;
+
+// Constant value used in ExecResult when the process was not run at all.
+// Mirrors EXEC_RESULT_STATUS_NOT_RUN contained in frameworks/proto_logging/atoms.proto.
+static constexpr int32_t kExecResultNotRun = 5;
+static_assert(kExecResultNotRun > ExecResult::Status::kLast,
+ "`art::odrefresh::kExecResultNotRun` value should not overlap with"
+ " values of enum `art::ExecResult::Status`");
+
// MetricsRecord is a simpler container for Odrefresh metric values reported to statsd. The order
// and types of fields here mirror definition of `OdrefreshReported` in
// frameworks/proto_logging/stats/atoms.proto.
struct OdrMetricsRecord {
+ struct Dex2OatExecResult {
+ int32_t status;
+ int32_t exit_code;
+ int32_t signal;
+
+ explicit Dex2OatExecResult(int32_t status, int32_t exit_code, int32_t signal)
+ : status(status), exit_code(exit_code), signal(signal) {}
+
+ explicit Dex2OatExecResult(const ExecResult& result)
+ : status(result.status), exit_code(result.exit_code), signal(result.signal) {}
+
+ Dex2OatExecResult() : status(kExecResultNotRun), exit_code(-1), signal(0) {}
+ };
+
+ int32_t odrefresh_metrics_version;
int64_t art_apex_version;
int32_t trigger;
int32_t stage_reached;
int32_t status;
- int32_t primary_bcp_compilation_seconds;
- int32_t secondary_bcp_compilation_seconds;
- int32_t system_server_compilation_seconds;
int32_t cache_space_free_start_mib;
int32_t cache_space_free_end_mib;
+ int32_t primary_bcp_compilation_millis;
+ int32_t secondary_bcp_compilation_millis;
+ int32_t system_server_compilation_millis;
+ Dex2OatExecResult primary_bcp_dex2oat_result;
+ Dex2OatExecResult secondary_bcp_dex2oat_result;
+ Dex2OatExecResult system_server_dex2oat_result;
+
+ // Reads a `MetricsRecord` from an XML file.
+ // Returns an error if the XML document was not found or parsed correctly.
+ android::base::Result<void> ReadFromFile(const std::string& filename);
+
+ // Writes a `MetricsRecord` to an XML file.
+ // Returns an error if the XML document was not saved correctly.
+ android::base::Result<void> WriteToFile(const std::string& filename) const;
};
-// Read a `MetricsRecord` from an `istream`.
-//
-// This method blocks istream related exceptions, the caller should check `is.fail()` is false after
-// calling.
-//
-// Returns `is`.
-std::istream& operator>>(std::istream& is, OdrMetricsRecord& record);
-
-// Write a `MetricsRecord` to an `ostream`.
-//
-// This method blocks ostream related exceptions, the caller should check `os.fail()` is false after
-// calling.
-//
-// Returns `os`
-std::ostream& operator<<(std::ostream& os, const OdrMetricsRecord& record);
-
} // namespace odrefresh
} // namespace art
diff --git a/odrefresh/odr_metrics_record_test.cc b/odrefresh/odr_metrics_record_test.cc
index dd739d6..a6bfb8b 100644
--- a/odrefresh/odr_metrics_record_test.cc
+++ b/odrefresh/odr_metrics_record_test.cc
@@ -20,93 +20,233 @@
#include <fstream>
+#include "android-base/result-gmock.h"
+#include "android-base/stringprintf.h"
#include "base/common_art_test.h"
namespace art {
namespace odrefresh {
-class OdrMetricsRecordTest : public CommonArtTest {};
+class OdrMetricsRecordTest : public CommonArtTest {
+ protected:
+ void WriteFile() {
+ std::ofstream ofs(file_path_);
-TEST_F(OdrMetricsRecordTest, HappyPath) {
- const OdrMetricsRecord expected {
- .art_apex_version = 0x01233456'789abcde,
- .trigger = 0x01020304,
- .stage_reached = 0x11121314,
- .status = 0x21222324,
- .primary_bcp_compilation_seconds = 0x31323334,
- .secondary_bcp_compilation_seconds = 0x41424344,
- .system_server_compilation_seconds = 0x51525354,
- .cache_space_free_start_mib = 0x61626364,
- .cache_space_free_end_mib = 0x71727374
- };
+ ofs << "<odrefresh_metrics>";
+ ofs << metrics_version_;
+ ofs << "<art_apex_version>81966764218039518</art_apex_version>";
+ ofs << "<trigger>16909060</trigger>";
+ ofs << "<stage_reached>286397204</stage_reached>";
+ ofs << status_;
+ ofs << "<cache_space_free_start_mib>1633837924</cache_space_free_start_mib>";
+ ofs << "<cache_space_free_end_mib>1903326068</cache_space_free_end_mib>";
+ ofs << "<primary_bcp_compilation_millis>825373492</primary_bcp_compilation_millis>";
+ ofs << "<secondary_bcp_compilation_millis>1094861636</secondary_bcp_compilation_millis>";
+ ofs << "<system_server_compilation_millis>1364349780</system_server_compilation_millis>";
+ ofs << primary_bcp_dex2oat_result_;
+ ofs << secondary_bcp_dex2oat_result_;
+ ofs << system_server_dex2oat_result_;
+ ofs << "</odrefresh_metrics>";
- ScratchDir dir(/*keep_files=*/false);
- std::string file_path = dir.GetPath() + "/metrics-record.txt";
-
- {
- std::ofstream ofs(file_path);
- ofs << expected;
- ASSERT_FALSE(ofs.fail());
ofs.close();
}
- OdrMetricsRecord actual {};
- {
- std::ifstream ifs(file_path);
- ifs >> actual;
- ASSERT_TRUE(ifs.eof());
+ void SetUp() override {
+ CommonArtTest::SetUp();
+ scratch_dir_ = std::make_unique<ScratchDir>(/*keep_files=*/false);
+ file_path_ = scratch_dir_->GetPath() + "/metrics-record.xml";
}
+ void TearDown() override { scratch_dir_.reset(); }
+
+ std::unique_ptr<ScratchDir> scratch_dir_;
+ std::string file_path_;
+ std::string metrics_version_ = android::base::StringPrintf(
+ "<odrefresh_metrics_version>%d</odrefresh_metrics_version>", kOdrefreshMetricsVersion);
+ std::string status_ = "<status>30</status>";
+ std::string primary_bcp_dex2oat_result_ =
+ R"(<primary_bcp_dex2oat_result status="1" exit-code="-1" signal="0" />)";
+ std::string secondary_bcp_dex2oat_result_ =
+ R"(<secondary_bcp_dex2oat_result status="2" exit-code="15" signal="0" />)";
+ std::string system_server_dex2oat_result_ =
+ R"(<system_server_dex2oat_result status="3" exit-code="-1" signal="9" />)";
+};
+
+using android::base::testing::HasError;
+using android::base::testing::Ok;
+using android::base::testing::WithMessage;
+
+TEST_F(OdrMetricsRecordTest, HappyPath) {
+ OdrMetricsRecord expected{};
+ expected.odrefresh_metrics_version = art::odrefresh::kOdrefreshMetricsVersion;
+ expected.art_apex_version = 0x01233456'789abcde;
+ expected.trigger = 0x01020304;
+ expected.stage_reached = 0x11121314;
+ expected.status = 0x21222324;
+ expected.cache_space_free_start_mib = 0x61626364;
+ expected.cache_space_free_end_mib = 0x71727374;
+ expected.primary_bcp_compilation_millis = 0x31323334;
+ expected.secondary_bcp_compilation_millis = 0x41424344;
+ expected.system_server_compilation_millis = 0x51525354;
+ expected.primary_bcp_dex2oat_result = OdrMetricsRecord::Dex2OatExecResult(1, -1, 0);
+ expected.secondary_bcp_dex2oat_result = OdrMetricsRecord::Dex2OatExecResult(2, 15, 0);
+ expected.system_server_dex2oat_result = OdrMetricsRecord::Dex2OatExecResult(3, -1, 9);
+
+ ASSERT_THAT(expected.WriteToFile(file_path_), Ok());
+
+ OdrMetricsRecord actual{};
+ ASSERT_THAT(actual.ReadFromFile(file_path_), Ok());
+
+ ASSERT_EQ(expected.odrefresh_metrics_version, actual.odrefresh_metrics_version);
ASSERT_EQ(expected.art_apex_version, actual.art_apex_version);
ASSERT_EQ(expected.trigger, actual.trigger);
ASSERT_EQ(expected.stage_reached, actual.stage_reached);
ASSERT_EQ(expected.status, actual.status);
- ASSERT_EQ(expected.primary_bcp_compilation_seconds, actual.primary_bcp_compilation_seconds);
- ASSERT_EQ(expected.secondary_bcp_compilation_seconds, actual.secondary_bcp_compilation_seconds);
- ASSERT_EQ(expected.system_server_compilation_seconds, actual.system_server_compilation_seconds);
ASSERT_EQ(expected.cache_space_free_start_mib, actual.cache_space_free_start_mib);
ASSERT_EQ(expected.cache_space_free_end_mib, actual.cache_space_free_end_mib);
+ ASSERT_EQ(expected.primary_bcp_compilation_millis, actual.primary_bcp_compilation_millis);
+ ASSERT_EQ(expected.secondary_bcp_compilation_millis, actual.secondary_bcp_compilation_millis);
+ ASSERT_EQ(expected.system_server_compilation_millis, actual.system_server_compilation_millis);
+ ASSERT_EQ(expected.primary_bcp_dex2oat_result.status, actual.primary_bcp_dex2oat_result.status);
+ ASSERT_EQ(expected.primary_bcp_dex2oat_result.exit_code,
+ actual.primary_bcp_dex2oat_result.exit_code);
+ ASSERT_EQ(expected.primary_bcp_dex2oat_result.signal, actual.primary_bcp_dex2oat_result.signal);
+ ASSERT_EQ(expected.secondary_bcp_dex2oat_result.status,
+ actual.secondary_bcp_dex2oat_result.status);
+ ASSERT_EQ(expected.secondary_bcp_dex2oat_result.exit_code,
+ actual.secondary_bcp_dex2oat_result.exit_code);
+ ASSERT_EQ(expected.secondary_bcp_dex2oat_result.signal,
+ actual.secondary_bcp_dex2oat_result.signal);
+ ASSERT_EQ(expected.system_server_dex2oat_result.status,
+ actual.system_server_dex2oat_result.status);
+ ASSERT_EQ(expected.system_server_dex2oat_result.exit_code,
+ actual.system_server_dex2oat_result.exit_code);
+ ASSERT_EQ(expected.system_server_dex2oat_result.signal,
+ actual.system_server_dex2oat_result.signal);
ASSERT_EQ(0, memcmp(&expected, &actual, sizeof(expected)));
}
TEST_F(OdrMetricsRecordTest, EmptyInput) {
- ScratchDir dir(/*keep_files=*/false);
- std::string file_path = dir.GetPath() + "/metrics-record.txt";
-
- std::ifstream ifs(file_path);
- OdrMetricsRecord record;
- ifs >> record;
-
- ASSERT_TRUE(ifs.fail());
- ASSERT_TRUE(!ifs);
+ OdrMetricsRecord record{};
+ ASSERT_THAT(record.ReadFromFile(file_path_), testing::Not(Ok()));
}
-TEST_F(OdrMetricsRecordTest, ClosedInput) {
- ScratchDir dir(/*keep_files=*/false);
- std::string file_path = dir.GetPath() + "/metrics-record.txt";
-
- std::ifstream ifs(file_path);
- ifs.close();
-
- OdrMetricsRecord record;
- ifs >> record;
-
- ASSERT_TRUE(ifs.fail());
- ASSERT_TRUE(!ifs);
-}
-
-TEST_F(OdrMetricsRecordTest, ClosedOutput) {
- ScratchDir dir(/*keep_files=*/false);
- std::string file_path = dir.GetPath() + "/metrics-record.txt";
-
- std::ofstream ofs(file_path);
+TEST_F(OdrMetricsRecordTest, UnexpectedInput) {
+ std::ofstream ofs(file_path_);
+ ofs << "<not_odrefresh_metrics></not_odrefresh_metrics>";
ofs.close();
- OdrMetricsRecord record {};
- ofs << record;
+ OdrMetricsRecord record{};
+ ASSERT_THAT(record.ReadFromFile(file_path_),
+ HasError(WithMessage("odrefresh_metrics element not found in " + file_path_)));
+}
- ASSERT_TRUE(ofs.fail());
- ASSERT_TRUE(!ofs.good());
+TEST_F(OdrMetricsRecordTest, ExpectedElementNotFound) {
+ metrics_version_ = "<not_valid_metric>25</not_valid_metric>";
+ WriteFile();
+
+ OdrMetricsRecord record{};
+ ASSERT_THAT(
+ record.ReadFromFile(file_path_),
+ HasError(WithMessage("Expected Odrefresh metric odrefresh_metrics_version not found")));
+}
+
+TEST_F(OdrMetricsRecordTest, ExpectedAttributeNotFound) {
+ // Missing "status".
+ primary_bcp_dex2oat_result_ = R"(<primary_bcp_dex2oat_result exit-code="17" signal="18" />)";
+ WriteFile();
+
+ OdrMetricsRecord record{};
+ ASSERT_THAT(record.ReadFromFile(file_path_),
+ HasError(WithMessage(
+ "Expected Odrefresh metric primary_bcp_dex2oat_result.status is not an int32")));
+}
+
+TEST_F(OdrMetricsRecordTest, UnexpectedOdrefreshMetricsVersion) {
+ metrics_version_ = "<odrefresh_metrics_version>0</odrefresh_metrics_version>";
+ WriteFile();
+
+ OdrMetricsRecord record{};
+ std::string expected_error = android::base::StringPrintf(
+ "odrefresh_metrics_version 0 is different than expected (%d)", kOdrefreshMetricsVersion);
+ ASSERT_THAT(record.ReadFromFile(file_path_), HasError(WithMessage(expected_error)));
+}
+
+TEST_F(OdrMetricsRecordTest, UnexpectedType) {
+ status_ = "<status>abcd</status>"; // It should be an int32.
+ WriteFile();
+
+ OdrMetricsRecord record{};
+ ASSERT_THAT(record.ReadFromFile(file_path_),
+ HasError(WithMessage("Odrefresh metric status is not an int32")));
+}
+
+TEST_F(OdrMetricsRecordTest, ResultStatusOutsideOfRange) {
+ // Status is valid between 0 and 5 (5 being NOT_RUN)
+ primary_bcp_dex2oat_result_ =
+ R"(<primary_bcp_dex2oat_result status="-1" exit-code="-1" signal="0" />)";
+ WriteFile();
+
+ OdrMetricsRecord record{};
+ ASSERT_THAT(
+ record.ReadFromFile(file_path_),
+ HasError(WithMessage("Odrefresh metric primary_bcp_dex2oat_result.status has a value (-1) "
+ "outside of the expected range ([0, 5])")));
+
+ primary_bcp_dex2oat_result_ =
+ R"(<primary_bcp_dex2oat_result status="9" exit-code="-1" signal="0" />)";
+ WriteFile();
+
+ ASSERT_THAT(
+ record.ReadFromFile(file_path_),
+ HasError(WithMessage("Odrefresh metric primary_bcp_dex2oat_result.status has a value (9) "
+ "outside of the expected range ([0, 5])")));
+}
+
+TEST_F(OdrMetricsRecordTest, ResultExitCodeOutsideOfRange) {
+ // Exit Code is valid between -1 and 255
+ secondary_bcp_dex2oat_result_ =
+ R"(<secondary_bcp_dex2oat_result status="2" exit-code="-2" signal="0" />)";
+ WriteFile();
+
+ OdrMetricsRecord record{};
+ ASSERT_THAT(record.ReadFromFile(file_path_),
+ HasError(WithMessage(
+ "Odrefresh metric secondary_bcp_dex2oat_result.exit-code has a value (-2) "
+ "outside of the expected range ([-1, 255])")));
+
+ secondary_bcp_dex2oat_result_ =
+ R"(<secondary_bcp_dex2oat_result status="2" exit-code="258" signal="0" />)";
+ WriteFile();
+
+ ASSERT_THAT(record.ReadFromFile(file_path_),
+ HasError(WithMessage(
+ "Odrefresh metric secondary_bcp_dex2oat_result.exit-code has a value (258) "
+ "outside of the expected range ([-1, 255])")));
+}
+
+TEST_F(OdrMetricsRecordTest, ResultSignalOutsideOfRange) {
+ // Signal is valid between 0 and SIGRTMAX
+ system_server_dex2oat_result_ =
+ R"(<system_server_dex2oat_result status="3" exit-code="0" signal="-6" />)";
+ WriteFile();
+
+ OdrMetricsRecord record{};
+ ASSERT_THAT(record.ReadFromFile(file_path_),
+ HasError(WithMessage(android::base::StringPrintf(
+ "Odrefresh metric system_server_dex2oat_result.signal has a value (-6) "
+ "outside of the expected range ([0, %d])",
+ SIGRTMAX))));
+
+ system_server_dex2oat_result_ =
+ R"(<system_server_dex2oat_result status="3" exit-code="0" signal="65" />)";
+ WriteFile();
+
+ ASSERT_THAT(record.ReadFromFile(file_path_),
+ HasError(WithMessage(android::base::StringPrintf(
+ "Odrefresh metric system_server_dex2oat_result.signal has a value (65) "
+ "outside of the expected range ([0, %d])",
+ SIGRTMAX))));
}
} // namespace odrefresh
diff --git a/odrefresh/odr_metrics_test.cc b/odrefresh/odr_metrics_test.cc
index 4519f00..b8446df 100644
--- a/odrefresh/odr_metrics_test.cc
+++ b/odrefresh/odr_metrics_test.cc
@@ -15,27 +15,31 @@
*/
#include "odr_metrics.h"
-#include "base/casts.h"
-#include "odr_metrics_record.h"
#include <unistd.h>
+#include <chrono>
#include <fstream>
#include <memory>
#include <string>
+#include <thread>
+#include "base/casts.h"
#include "base/common_art_test.h"
+#include "odr_metrics_record.h"
namespace art {
namespace odrefresh {
+using std::chrono_literals::operator""ms; // NOLINT
+
class OdrMetricsTest : public CommonArtTest {
public:
void SetUp() override {
CommonArtTest::SetUp();
scratch_dir_ = std::make_unique<ScratchDir>();
- metrics_file_path_ = scratch_dir_->GetPath() + "/metrics.txt";
+ metrics_file_path_ = scratch_dir_->GetPath() + "/metrics.xml";
cache_directory_ = scratch_dir_->GetPath() + "/dir";
mkdir(cache_directory_.c_str(), S_IRWXU);
}
@@ -49,14 +53,6 @@
return OS::FileExists(path);
}
- bool RemoveMetricsFile() const {
- const char* path = metrics_file_path_.c_str();
- if (OS::FileExists(path)) {
- return unlink(path) == 0;
- }
- return true;
- }
-
const std::string GetCacheDirectory() const { return cache_directory_; }
const std::string GetMetricsFilePath() const { return metrics_file_path_; }
@@ -66,59 +62,23 @@
std::string cache_directory_;
};
-TEST_F(OdrMetricsTest, ToRecordFailsIfNotTriggered) {
- {
- OdrMetrics metrics(GetCacheDirectory(), GetMetricsFilePath());
- OdrMetricsRecord record {};
- EXPECT_FALSE(metrics.ToRecord(&record));
- }
-
- {
- OdrMetrics metrics(GetCacheDirectory(), GetMetricsFilePath());
- metrics.SetArtApexVersion(99);
- metrics.SetStage(OdrMetrics::Stage::kCheck);
- metrics.SetStatus(OdrMetrics::Status::kNoSpace);
- OdrMetricsRecord record {};
- EXPECT_FALSE(metrics.ToRecord(&record));
- }
-}
-
-TEST_F(OdrMetricsTest, ToRecordSucceedsIfTriggered) {
- OdrMetrics metrics(GetCacheDirectory(), GetMetricsFilePath());
- metrics.SetArtApexVersion(99);
- metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
- metrics.SetStage(OdrMetrics::Stage::kCheck);
- metrics.SetStatus(OdrMetrics::Status::kNoSpace);
-
- OdrMetricsRecord record{};
- EXPECT_TRUE(metrics.ToRecord(&record));
-
- EXPECT_EQ(99, record.art_apex_version);
- EXPECT_EQ(OdrMetrics::Trigger::kApexVersionMismatch,
- enum_cast<OdrMetrics::Trigger>(record.trigger));
- EXPECT_EQ(OdrMetrics::Stage::kCheck, enum_cast<OdrMetrics::Stage>(record.stage_reached));
- EXPECT_EQ(OdrMetrics::Status::kNoSpace, enum_cast<OdrMetrics::Status>(record.status));
-}
-
-TEST_F(OdrMetricsTest, MetricsFileIsNotCreatedIfNotTriggered) {
- EXPECT_TRUE(RemoveMetricsFile());
-
+TEST_F(OdrMetricsTest, MetricsFileIsNotCreatedIfNotEnabled) {
// Metrics file is (potentially) written in OdrMetrics destructor.
{
OdrMetrics metrics(GetCacheDirectory(), GetMetricsFilePath());
metrics.SetArtApexVersion(99);
+ metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
metrics.SetStage(OdrMetrics::Stage::kCheck);
metrics.SetStatus(OdrMetrics::Status::kNoSpace);
}
EXPECT_FALSE(MetricsFileExists());
}
-TEST_F(OdrMetricsTest, NoMetricsFileIsCreatedIfTriggered) {
- EXPECT_TRUE(RemoveMetricsFile());
-
+TEST_F(OdrMetricsTest, MetricsFileIsCreatedIfEnabled) {
// Metrics file is (potentially) written in OdrMetrics destructor.
{
OdrMetrics metrics(GetCacheDirectory(), GetMetricsFilePath());
+ metrics.SetEnabled(true);
metrics.SetArtApexVersion(101);
metrics.SetTrigger(OdrMetrics::Trigger::kDexFilesChanged);
metrics.SetStage(OdrMetrics::Stage::kCheck);
@@ -127,20 +87,6 @@
EXPECT_TRUE(MetricsFileExists());
}
-TEST_F(OdrMetricsTest, StageDoesNotAdvancedAfterFailure) {
- OdrMetrics metrics(GetCacheDirectory(), GetMetricsFilePath());
- metrics.SetArtApexVersion(1999);
- metrics.SetTrigger(OdrMetrics::Trigger::kMissingArtifacts);
- metrics.SetStage(OdrMetrics::Stage::kCheck);
- metrics.SetStatus(OdrMetrics::Status::kNoSpace);
- metrics.SetStage(OdrMetrics::Stage::kComplete);
-
- OdrMetricsRecord record{};
- EXPECT_TRUE(metrics.ToRecord(&record));
-
- EXPECT_EQ(OdrMetrics::Stage::kCheck, enum_cast<OdrMetrics::Stage>(record.stage_reached));
-}
-
TEST_F(OdrMetricsTest, TimeValuesAreRecorded) {
OdrMetrics metrics(GetCacheDirectory(), GetMetricsFilePath());
metrics.SetArtApexVersion(1999);
@@ -149,71 +95,177 @@
metrics.SetStatus(OdrMetrics::Status::kOK);
// Primary boot classpath compilation time.
- OdrMetricsRecord record{};
{
metrics.SetStage(OdrMetrics::Stage::kPrimaryBootClasspath);
ScopedOdrCompilationTimer timer(metrics);
- sleep(2u);
+ std::this_thread::sleep_for(100ms);
}
- EXPECT_TRUE(metrics.ToRecord(&record));
- EXPECT_EQ(OdrMetrics::Stage::kPrimaryBootClasspath,
- enum_cast<OdrMetrics::Stage>(record.stage_reached));
- EXPECT_NE(0, record.primary_bcp_compilation_seconds);
- EXPECT_GT(10, record.primary_bcp_compilation_seconds);
- EXPECT_EQ(0, record.secondary_bcp_compilation_seconds);
- EXPECT_EQ(0, record.system_server_compilation_seconds);
+ OdrMetricsRecord record = metrics.ToRecord();
+ EXPECT_EQ(enum_cast<OdrMetrics::Stage>(record.stage_reached),
+ OdrMetrics::Stage::kPrimaryBootClasspath);
+ EXPECT_GT(record.primary_bcp_compilation_millis, 0);
+ EXPECT_LT(record.primary_bcp_compilation_millis, 300);
+ EXPECT_EQ(record.secondary_bcp_compilation_millis, 0);
+ EXPECT_EQ(record.system_server_compilation_millis, 0);
// Secondary boot classpath compilation time.
{
metrics.SetStage(OdrMetrics::Stage::kSecondaryBootClasspath);
ScopedOdrCompilationTimer timer(metrics);
- sleep(2u);
+ std::this_thread::sleep_for(100ms);
}
- EXPECT_TRUE(metrics.ToRecord(&record));
+ record = metrics.ToRecord();
EXPECT_EQ(OdrMetrics::Stage::kSecondaryBootClasspath,
enum_cast<OdrMetrics::Stage>(record.stage_reached));
- EXPECT_NE(0, record.primary_bcp_compilation_seconds);
- EXPECT_NE(0, record.secondary_bcp_compilation_seconds);
- EXPECT_GT(10, record.secondary_bcp_compilation_seconds);
- EXPECT_EQ(0, record.system_server_compilation_seconds);
+ EXPECT_GT(record.primary_bcp_compilation_millis, 0);
+ EXPECT_GT(record.secondary_bcp_compilation_millis, 0);
+ EXPECT_LT(record.secondary_bcp_compilation_millis, 300);
+ EXPECT_EQ(record.system_server_compilation_millis, 0);
// system_server classpath compilation time.
{
metrics.SetStage(OdrMetrics::Stage::kSystemServerClasspath);
ScopedOdrCompilationTimer timer(metrics);
- sleep(2u);
+ std::this_thread::sleep_for(100ms);
}
- EXPECT_TRUE(metrics.ToRecord(&record));
+ record = metrics.ToRecord();
EXPECT_EQ(OdrMetrics::Stage::kSystemServerClasspath,
enum_cast<OdrMetrics::Stage>(record.stage_reached));
- EXPECT_NE(0, record.primary_bcp_compilation_seconds);
- EXPECT_NE(0, record.secondary_bcp_compilation_seconds);
- EXPECT_NE(0, record.system_server_compilation_seconds);
- EXPECT_GT(10, record.system_server_compilation_seconds);
+ EXPECT_GT(record.primary_bcp_compilation_millis, 0);
+ EXPECT_GT(record.secondary_bcp_compilation_millis, 0);
+ EXPECT_GT(record.system_server_compilation_millis, 0);
+ EXPECT_LT(record.system_server_compilation_millis, 300);
}
TEST_F(OdrMetricsTest, CacheSpaceValuesAreUpdated) {
- OdrMetricsRecord snap {};
- snap.cache_space_free_start_mib = -1;
- snap.cache_space_free_end_mib = -1;
- {
- OdrMetrics metrics(GetCacheDirectory(), GetMetricsFilePath());
- metrics.SetArtApexVersion(1999);
- metrics.SetTrigger(OdrMetrics::Trigger::kMissingArtifacts);
- metrics.SetStage(OdrMetrics::Stage::kCheck);
- metrics.SetStatus(OdrMetrics::Status::kOK);
- EXPECT_TRUE(metrics.ToRecord(&snap));
- EXPECT_NE(0, snap.cache_space_free_start_mib);
- EXPECT_EQ(0, snap.cache_space_free_end_mib);
- }
+ OdrMetrics metrics(GetCacheDirectory(), GetMetricsFilePath());
+ metrics.CaptureSpaceFreeEnd();
+ OdrMetricsRecord record = metrics.ToRecord();
+ EXPECT_GT(record.cache_space_free_start_mib, 0);
+ EXPECT_GT(record.cache_space_free_end_mib, 0);
+}
- OdrMetricsRecord on_disk;
- std::ifstream ifs(GetMetricsFilePath());
- EXPECT_TRUE(ifs);
- ifs >> on_disk;
- EXPECT_TRUE(ifs);
- EXPECT_EQ(snap.cache_space_free_start_mib, on_disk.cache_space_free_start_mib);
- EXPECT_NE(0, on_disk.cache_space_free_end_mib);
+TEST_F(OdrMetricsTest, PrimaryBcpResultWithValue) {
+ OdrMetrics metrics(GetCacheDirectory(), GetMetricsFilePath());
+ metrics.SetStage(OdrMetrics::Stage::kPrimaryBootClasspath);
+ metrics.SetDex2OatResult({
+ .status = ExecResult::Status::kExited,
+ .exit_code = 0,
+ .signal = 0
+ });
+ OdrMetricsRecord record = metrics.ToRecord();
+ EXPECT_EQ(record.primary_bcp_dex2oat_result.status, ExecResult::Status::kExited);
+ EXPECT_EQ(record.primary_bcp_dex2oat_result.exit_code, 0);
+ EXPECT_EQ(record.primary_bcp_dex2oat_result.signal, 0);
+}
+
+TEST_F(OdrMetricsTest, PrimaryBcpResultWithoutValue) {
+ OdrMetrics metrics(GetCacheDirectory(), GetMetricsFilePath());
+
+ OdrMetricsRecord record = metrics.ToRecord();
+ EXPECT_EQ(record.primary_bcp_dex2oat_result.status, kExecResultNotRun);
+ EXPECT_EQ(record.primary_bcp_dex2oat_result.exit_code, -1);
+ EXPECT_EQ(record.primary_bcp_dex2oat_result.signal, 0);
+}
+
+TEST_F(OdrMetricsTest, SecondaryBcpResultWithValue) {
+ OdrMetrics metrics(GetCacheDirectory(), GetMetricsFilePath());
+ metrics.SetStage(OdrMetrics::Stage::kPrimaryBootClasspath);
+ metrics.SetDex2OatResult({
+ .status = ExecResult::Status::kExited,
+ .exit_code = 0,
+ .signal = 0
+ });
+ metrics.SetStage(OdrMetrics::Stage::kSecondaryBootClasspath);
+ metrics.SetDex2OatResult({
+ .status = ExecResult::Status::kTimedOut,
+ .exit_code = 3,
+ .signal = 0
+ });
+ OdrMetricsRecord record = metrics.ToRecord();
+ EXPECT_EQ(record.primary_bcp_dex2oat_result.status, ExecResult::Status::kExited);
+ EXPECT_EQ(record.primary_bcp_dex2oat_result.exit_code, 0);
+ EXPECT_EQ(record.primary_bcp_dex2oat_result.signal, 0);
+ EXPECT_EQ(record.secondary_bcp_dex2oat_result.status, ExecResult::Status::kTimedOut);
+ EXPECT_EQ(record.secondary_bcp_dex2oat_result.exit_code, 3);
+ EXPECT_EQ(record.secondary_bcp_dex2oat_result.signal, 0);
+}
+
+TEST_F(OdrMetricsTest, SecondaryBcpResultWithoutValue) {
+ OdrMetrics metrics(GetCacheDirectory(), GetMetricsFilePath());
+ metrics.SetStage(OdrMetrics::Stage::kPrimaryBootClasspath);
+ metrics.SetDex2OatResult({
+ .status = ExecResult::Status::kExited,
+ .exit_code = 0,
+ .signal = 0
+ });
+
+ OdrMetricsRecord record = metrics.ToRecord();
+ EXPECT_EQ(record.primary_bcp_dex2oat_result.status, ExecResult::Status::kExited);
+ EXPECT_EQ(record.primary_bcp_dex2oat_result.exit_code, 0);
+ EXPECT_EQ(record.primary_bcp_dex2oat_result.signal, 0);
+ EXPECT_EQ(record.secondary_bcp_dex2oat_result.status, kExecResultNotRun);
+ EXPECT_EQ(record.secondary_bcp_dex2oat_result.exit_code, -1);
+ EXPECT_EQ(record.secondary_bcp_dex2oat_result.signal, 0);
+}
+
+TEST_F(OdrMetricsTest, SystemServerResultWithValue) {
+ OdrMetrics metrics(GetCacheDirectory(), GetMetricsFilePath());
+ metrics.SetStage(OdrMetrics::Stage::kPrimaryBootClasspath);
+ metrics.SetDex2OatResult({
+ .status = ExecResult::Status::kExited,
+ .exit_code = 0,
+ .signal = 0
+ });
+ metrics.SetStage(OdrMetrics::Stage::kSecondaryBootClasspath);
+ metrics.SetDex2OatResult({
+ .status = ExecResult::Status::kTimedOut,
+ .exit_code = 3,
+ .signal = 0
+ });
+ metrics.SetStage(OdrMetrics::Stage::kSystemServerClasspath);
+ metrics.SetDex2OatResult({
+ .status = ExecResult::Status::kSignaled,
+ .exit_code = 2,
+ .signal = 9
+ });
+ OdrMetricsRecord record = metrics.ToRecord();
+ EXPECT_EQ(record.primary_bcp_dex2oat_result.status, ExecResult::Status::kExited);
+ EXPECT_EQ(record.primary_bcp_dex2oat_result.exit_code, 0);
+ EXPECT_EQ(record.primary_bcp_dex2oat_result.signal, 0);
+ EXPECT_EQ(record.secondary_bcp_dex2oat_result.status, ExecResult::Status::kTimedOut);
+ EXPECT_EQ(record.secondary_bcp_dex2oat_result.exit_code, 3);
+ EXPECT_EQ(record.secondary_bcp_dex2oat_result.signal, 0);
+ EXPECT_EQ(record.system_server_dex2oat_result.status, ExecResult::Status::kSignaled);
+ EXPECT_EQ(record.system_server_dex2oat_result.exit_code, 2);
+ EXPECT_EQ(record.system_server_dex2oat_result.signal, 9);
+}
+
+TEST_F(OdrMetricsTest, SystemServerResultWithoutValue) {
+ OdrMetrics metrics(GetCacheDirectory(), GetMetricsFilePath());
+ metrics.SetStage(OdrMetrics::Stage::kPrimaryBootClasspath);
+ metrics.SetDex2OatResult({
+ .status = ExecResult::Status::kExited,
+ .exit_code = 0,
+ .signal = 0
+ });
+ metrics.SetStage(OdrMetrics::Stage::kSecondaryBootClasspath);
+ metrics.SetDex2OatResult({
+ .status = ExecResult::Status::kTimedOut,
+ .exit_code = 3,
+ .signal = 0
+ });
+
+ OdrMetricsRecord record = metrics.ToRecord();
+ EXPECT_EQ(record.primary_bcp_dex2oat_result.status, ExecResult::Status::kExited);
+ EXPECT_EQ(record.primary_bcp_dex2oat_result.exit_code, 0);
+ EXPECT_EQ(record.primary_bcp_dex2oat_result.signal, 0);
+ EXPECT_EQ(record.secondary_bcp_dex2oat_result.status, ExecResult::Status::kTimedOut);
+ EXPECT_EQ(record.secondary_bcp_dex2oat_result.exit_code, 3);
+ EXPECT_EQ(record.secondary_bcp_dex2oat_result.signal, 0);
+ EXPECT_EQ(record.system_server_dex2oat_result.status, kExecResultNotRun);
+ EXPECT_EQ(record.system_server_dex2oat_result.exit_code, -1);
+ EXPECT_EQ(record.system_server_dex2oat_result.signal, 0);
}
} // namespace odrefresh
diff --git a/odrefresh/odr_statslog_android.cc b/odrefresh/odr_statslog_android.cc
index 7db348e..29fcf49 100644
--- a/odrefresh/odr_statslog_android.cc
+++ b/odrefresh/odr_statslog_android.cc
@@ -71,12 +71,12 @@
return metrics::statsd::ODREFRESH_REPORTED__STATUS__STATUS_IO_ERROR;
case OdrMetrics::Status::kDex2OatError:
return metrics::statsd::ODREFRESH_REPORTED__STATUS__STATUS_DEX2OAT_ERROR;
- case OdrMetrics::Status::kTimeLimitExceeded:
- return metrics::statsd::ODREFRESH_REPORTED__STATUS__STATUS_TIME_LIMIT_EXCEEDED;
case OdrMetrics::Status::kStagingFailed:
return metrics::statsd::ODREFRESH_REPORTED__STATUS__STATUS_STAGING_FAILED;
case OdrMetrics::Status::kInstallFailed:
return metrics::statsd::ODREFRESH_REPORTED__STATUS__STATUS_INSTALL_FAILED;
+ case OdrMetrics::Status::kDalvikCachePermissionDenied:
+ return metrics::statsd::ODREFRESH_REPORTED__STATUS__STATUS_DALVIK_CACHE_PERMISSION_DENIED;
}
LOG(ERROR) << "Unknown status value: " << art_metrics_status;
@@ -103,16 +103,11 @@
bool ReadValues(const char* metrics_file,
/*out*/ OdrMetricsRecord* record,
/*out*/ std::string* error_msg) {
- std::ifstream ifs(metrics_file);
- if (!ifs) {
- *error_msg = android::base::StringPrintf(
- "metrics file '%s' could not be opened: %s", metrics_file, strerror(errno));
- return false;
- }
-
- ifs >> *record;
- if (!ifs) {
- *error_msg = "file parsing error.";
+ const android::base::Result<void>& result = record->ReadFromFile(metrics_file);
+ if (!result.ok()) {
+ *error_msg = android::base::StringPrintf("Unable to open or parse metrics file %s (error: %s)",
+ metrics_file,
+ result.error().message().data());
return false;
}
@@ -151,16 +146,29 @@
// Write values to statsd. The order of values passed is the same as the order of the
// fields in OdrMetricsRecord.
- int bytes_written = art::metrics::statsd::stats_write(metrics::statsd::ODREFRESH_REPORTED,
- record.art_apex_version,
- record.trigger,
- record.stage_reached,
- record.status,
- record.primary_bcp_compilation_seconds,
- record.secondary_bcp_compilation_seconds,
- record.system_server_compilation_seconds,
- record.cache_space_free_start_mib,
- record.cache_space_free_end_mib);
+ int bytes_written = art::metrics::statsd::stats_write(
+ metrics::statsd::ODREFRESH_REPORTED,
+ record.art_apex_version,
+ record.trigger,
+ record.stage_reached,
+ record.status,
+ record.primary_bcp_compilation_millis / 1000,
+ record.secondary_bcp_compilation_millis / 1000,
+ record.system_server_compilation_millis / 1000,
+ record.cache_space_free_start_mib,
+ record.cache_space_free_end_mib,
+ record.primary_bcp_compilation_millis,
+ record.secondary_bcp_compilation_millis,
+ record.system_server_compilation_millis,
+ record.primary_bcp_dex2oat_result.status,
+ record.primary_bcp_dex2oat_result.exit_code,
+ record.primary_bcp_dex2oat_result.signal,
+ record.secondary_bcp_dex2oat_result.status,
+ record.secondary_bcp_dex2oat_result.exit_code,
+ record.secondary_bcp_dex2oat_result.signal,
+ record.system_server_dex2oat_result.status,
+ record.system_server_dex2oat_result.exit_code,
+ record.system_server_dex2oat_result.signal);
if (bytes_written <= 0) {
*error_msg = android::base::StringPrintf("stats_write returned %d", bytes_written);
return false;
diff --git a/odrefresh/odrefresh.cc b/odrefresh/odrefresh.cc
index f829bb8..c65c986 100644
--- a/odrefresh/odrefresh.cc
+++ b/odrefresh/odrefresh.cc
@@ -56,12 +56,14 @@
#include "android-base/file.h"
#include "android-base/logging.h"
#include "android-base/macros.h"
+#include "android-base/parsebool.h"
#include "android-base/parseint.h"
#include "android-base/properties.h"
#include "android-base/result.h"
#include "android-base/scopeguard.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
+#include "android-modules-utils/sdk_level.h"
#include "android/log.h"
#include "arch/instruction_set.h"
#include "base/file_utils.h"
@@ -76,6 +78,7 @@
#include "dex/art_dex_file_loader.h"
#include "dexoptanalyzer.h"
#include "exec_utils.h"
+#include "fmt/format.h"
#include "log/log.h"
#include "odr_artifacts.h"
#include "odr_common.h"
@@ -86,25 +89,31 @@
#include "odrefresh/odrefresh.h"
#include "palette/palette.h"
#include "palette/palette_types.h"
+#include "read_barrier_config.h"
namespace art {
namespace odrefresh {
+namespace {
+
namespace apex = com::android::apex;
namespace art_apex = com::android::art;
-using android::base::Result;
+using ::android::base::ParseBool;
+using ::android::base::ParseBoolResult;
+using ::android::base::Result;
+using ::android::modules::sdklevel::IsAtLeastU;
-namespace {
+using ::fmt::literals::operator""_format; // NOLINT
// Name of cache info file in the ART Apex artifact cache.
constexpr const char* kCacheInfoFile = "cache-info.xml";
// Maximum execution time for odrefresh from start to end.
-constexpr time_t kMaximumExecutionSeconds = 300;
+constexpr time_t kMaximumExecutionSeconds = 480;
// Maximum execution time for any child process spawned.
-constexpr time_t kMaxChildProcessSeconds = 90;
+constexpr time_t kMaxChildProcessSeconds = 120;
constexpr mode_t kFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
@@ -204,10 +213,12 @@
return module_info_list;
}
-// Returns a rewritten path based on ANDROID_ROOT if the path starts with "/system/".
-std::string AndroidRootRewrite(const std::string& path) {
+// Returns a rewritten path based on environment variables for interesting paths.
+std::string RewriteParentDirectoryIfNeeded(const std::string& path) {
if (StartsWith(path, "/system/")) {
return Concatenate({GetAndroidRoot(), path.substr(7)});
+ } else if (StartsWith(path, "/system_ext/")) {
+ return Concatenate({GetSystemExtRoot(), path.substr(11)});
} else {
return path;
}
@@ -281,7 +292,7 @@
ArtDexFileLoader loader;
for (const std::string& path : jars) {
- std::string actual_path = AndroidRootRewrite(path);
+ std::string actual_path = RewriteParentDirectoryIfNeeded(path);
struct stat sb;
if (stat(actual_path.c_str(), &sb) == -1) {
PLOG(ERROR) << "Failed to stat component: " << QuotePath(actual_path);
@@ -407,7 +418,8 @@
args.emplace_back(Concatenate({"--instruction-set=", isa_str}));
}
-void AddDex2OatProfileAndCompilerFilter(
+// Returns true if any profile has been added.
+bool AddDex2OatProfile(
/*inout*/ std::vector<std::string>& args,
/*inout*/ std::vector<std::unique_ptr<File>>& output_files,
const std::vector<std::string>& profile_paths) {
@@ -420,12 +432,7 @@
has_any_profile = true;
}
}
-
- if (has_any_profile) {
- args.emplace_back("--compiler-filter=speed-profile");
- } else {
- args.emplace_back("--compiler-filter=speed");
- }
+ return has_any_profile;
}
bool AddBootClasspathFds(/*inout*/ std::vector<std::string>& args,
@@ -439,7 +446,7 @@
if (StartsWith(jar, "/apex/")) {
bcp_fds.emplace_back("-1");
} else {
- std::string actual_path = AndroidRootRewrite(jar);
+ std::string actual_path = RewriteParentDirectoryIfNeeded(jar);
std::unique_ptr<File> jar_file(OS::OpenFileForReading(actual_path.c_str()));
if (!jar_file || !jar_file->IsValid()) {
LOG(ERROR) << "Failed to open a BCP jar " << actual_path;
@@ -555,6 +562,13 @@
std::string GetSystemBootImageDir() { return GetAndroidRoot() + "/framework"; }
+bool HasVettedDeviceSystemServerProfiles() {
+ // While system_server profiles were bundled on the device prior to U+, they were not used by
+ // default or rigorously tested, so we cannot vouch for their efficacy.
+ static const bool kDeviceIsAtLeastU = IsAtLeastU();
+ return kDeviceIsAtLeastU;
+}
+
} // namespace
OnDeviceRefresh::OnDeviceRefresh(const OdrConfig& config)
@@ -781,7 +795,7 @@
const std::string image_name = ReplaceFileExtension(jar_name, "art");
const char* isa_str = GetInstructionSetString(config_.GetSystemServerIsa());
// Typically "/system/framework/oat/<isa>/services.art".
- return Concatenate({GetAndroidRoot(), "/framework/oat/", isa_str, "/", image_name});
+ return Concatenate({android::base::Dirname(jar_path), "/oat/", isa_str, "/", image_name});
} else {
// Typically
// "/data/misc/apexdata/.../dalvik-cache/<isa>/system@framework@services.jar@classes.art".
@@ -907,17 +921,35 @@
return true;
}
+WARN_UNUSED bool OnDeviceRefresh::CheckBuildUserfaultFdGc() const {
+ auto it = config_.GetSystemProperties().find("ro.dalvik.vm.enable_uffd_gc");
+ bool build_enable_uffd_gc = it != config_.GetSystemProperties().end() ?
+ ParseBool(it->second) == ParseBoolResult::kTrue :
+ false;
+ if (build_enable_uffd_gc != gUseUserfaultfd) {
+ // Normally, this should not happen. If this happens, the system image was probably built with a
+ // wrong PRODUCT_ENABLE_UFFD_GC flag.
+ LOG(WARNING) << "Userfaultfd GC check failed (build-time: {}, runtime: {})."_format(
+ build_enable_uffd_gc, gUseUserfaultfd);
+ return false;
+ }
+ return true;
+}
+
WARN_UNUSED bool OnDeviceRefresh::BootClasspathArtifactsOnSystemUsable(
const apex::ApexInfo& art_apex_info) const {
if (!art_apex_info.getIsFactory()) {
+ LOG(INFO) << "Updated ART APEX mounted";
return false;
}
- LOG(INFO) << "Factory ART APEX mounted.";
if (!CheckSystemPropertiesAreDefault()) {
return false;
}
- LOG(INFO) << "System properties are set to default values.";
+
+ if (!CheckBuildUserfaultFdGc()) {
+ return false;
+ }
return true;
}
@@ -927,14 +959,17 @@
if (std::any_of(apex_info_list.begin(),
apex_info_list.end(),
[](const apex::ApexInfo& apex_info) { return !apex_info.getIsFactory(); })) {
+ LOG(INFO) << "Updated APEXes mounted";
return false;
}
- LOG(INFO) << "Factory APEXes mounted.";
if (!CheckSystemPropertiesAreDefault()) {
return false;
}
- LOG(INFO) << "System properties are set to default values.";
+
+ if (!CheckBuildUserfaultFdGc()) {
+ return false;
+ }
return true;
}
@@ -1245,7 +1280,7 @@
}
Result<void> OnDeviceRefresh::CleanupArtifactDirectory(
- const std::vector<std::string>& artifacts_to_keep) const {
+ OdrMetrics& metrics, const std::vector<std::string>& artifacts_to_keep) const {
const std::string& artifact_dir = config_.GetArtifactDirectory();
std::unordered_set<std::string> artifact_set{artifacts_to_keep.begin(), artifacts_to_keep.end()};
@@ -1263,7 +1298,9 @@
// undefined behavior;
entries.push_back(entry);
}
- if (ec) {
+ if (ec && ec.value() != ENOENT) {
+ metrics.SetStatus(ec.value() == EPERM ? OdrMetrics::Status::kDalvikCachePermissionDenied :
+ OdrMetrics::Status::kIoError);
return Errorf("Failed to iterate over entries in the artifact directory: {}", ec.message());
}
@@ -1273,6 +1310,7 @@
if (!ContainsElement(artifact_set, path)) {
LOG(INFO) << "Removing " << path;
if (unlink(path.c_str()) != 0) {
+ metrics.SetStatus(OdrMetrics::Status::kIoError);
return ErrnoErrorf("Failed to remove file {}", QuotePath(path));
}
}
@@ -1280,6 +1318,7 @@
// Neither a regular file nor a directory. Unexpected file type.
LOG(INFO) << "Removing " << path;
if (unlink(path.c_str()) != 0) {
+ metrics.SetStatus(OdrMetrics::Status::kIoError);
return ErrnoErrorf("Failed to remove file {}", QuotePath(path));
}
}
@@ -1340,7 +1379,11 @@
auto cleanup_and_compile_all = [&, this]() {
compilation_options->compile_boot_classpath_for_isas = config_.GetBootClasspathIsas();
compilation_options->system_server_jars_to_compile = AllSystemServerJars();
- return RemoveArtifactsDirectory() ? ExitCode::kCompilationRequired : ExitCode::kCleanupFailed;
+ if (!RemoveArtifactsDirectory()) {
+ metrics.SetStatus(OdrMetrics::Status::kIoError);
+ return ExitCode::kCleanupFailed;
+ }
+ return ExitCode::kCompilationRequired;
};
std::optional<std::vector<apex::ApexInfo>> apex_info_list = GetApexInfoList();
@@ -1414,7 +1457,7 @@
checked_artifacts.push_back(cache_info_filename_);
}
- Result<void> result = CleanupArtifactDirectory(checked_artifacts);
+ Result<void> result = CleanupArtifactDirectory(metrics, checked_artifacts);
if (!result.ok()) {
LOG(ERROR) << result.error();
return ExitCode::kCleanupFailed;
@@ -1444,8 +1487,18 @@
std::vector<std::unique_ptr<File>> readonly_files_raii;
const std::string art_boot_profile_file = GetArtRoot() + "/etc/boot-image.prof";
const std::string framework_boot_profile_file = GetAndroidRoot() + "/etc/boot-image.prof";
- AddDex2OatProfileAndCompilerFilter(args, readonly_files_raii,
- {art_boot_profile_file, framework_boot_profile_file});
+ bool has_any_profile = AddDex2OatProfile(
+ args, readonly_files_raii, {art_boot_profile_file, framework_boot_profile_file});
+ if (!has_any_profile) {
+ *error_msg = "Missing boot image profile";
+ return false;
+ }
+ const std::string& compiler_filter = config_.GetBootImageCompilerFilter();
+ if (!compiler_filter.empty()) {
+ args.emplace_back("--compiler-filter=" + compiler_filter);
+ } else {
+ args.emplace_back("--compiler-filter=speed-profile");
+ }
// Compile as a single image for fewer files and slightly less memory overhead.
args.emplace_back("--single-image");
@@ -1481,7 +1534,7 @@
}
for (const std::string& component : jars_to_compile) {
- std::string actual_path = AndroidRootRewrite(component);
+ std::string actual_path = RewriteParentDirectoryIfNeeded(component);
args.emplace_back("--dex-file=" + component);
std::unique_ptr<File> file(OS::OpenFileForReading(actual_path.c_str()));
args.emplace_back(android::base::StringPrintf("--dex-fd=%d", file->Fd()));
@@ -1491,6 +1544,7 @@
args.emplace_back("--runtime-arg");
args.emplace_back(Concatenate({"-Xbootclasspath:", android::base::Join(jars_to_compile, ":")}));
if (!AddBootClasspathFds(args, readonly_files_raii, jars_to_compile)) {
+ metrics.SetStatus(OdrMetrics::Status::kIoError);
return false;
}
@@ -1543,15 +1597,11 @@
return true;
}
- bool timed_out = false;
- int dex2oat_exit_code = exec_utils_->ExecAndReturnCode(args, timeout, &timed_out, error_msg);
+ ExecResult dex2oat_result = exec_utils_->ExecAndReturnResult(args, timeout, error_msg);
+ metrics.SetDex2OatResult(dex2oat_result);
- if (dex2oat_exit_code != 0) {
- if (timed_out) {
- metrics.SetStatus(OdrMetrics::Status::kTimeLimitExceeded);
- } else {
- metrics.SetStatus(OdrMetrics::Status::kDex2OatError);
- }
+ if (dex2oat_result.status != ExecResult::kExited || dex2oat_result.exit_code != 0) {
+ metrics.SetStatus(OdrMetrics::Status::kDex2OatError);
EraseFiles(staging_files);
return false;
}
@@ -1592,7 +1642,7 @@
args.emplace_back(dex2oat);
args.emplace_back("--dex-file=" + jar);
- std::string actual_jar_path = AndroidRootRewrite(jar);
+ std::string actual_jar_path = RewriteParentDirectoryIfNeeded(jar);
std::unique_ptr<File> dex_file(OS::OpenFileForReading(actual_jar_path.c_str()));
args.emplace_back(android::base::StringPrintf("--dex-fd=%d", dex_file->Fd()));
readonly_files_raii.push_back(std::move(dex_file));
@@ -1606,11 +1656,17 @@
const std::string jar_name(android::base::Basename(jar));
const std::string profile = Concatenate({GetAndroidRoot(), "/framework/", jar_name, ".prof"});
- std::string compiler_filter = config_.GetSystemServerCompilerFilter();
- if (compiler_filter == "speed-profile") {
- AddDex2OatProfileAndCompilerFilter(args, readonly_files_raii, {profile});
- } else {
+ const std::string& compiler_filter = config_.GetSystemServerCompilerFilter();
+ const bool maybe_add_profile =
+ !compiler_filter.empty() || HasVettedDeviceSystemServerProfiles();
+ const bool has_added_profile =
+ maybe_add_profile && AddDex2OatProfile(args, readonly_files_raii, {profile});
+ if (!compiler_filter.empty()) {
args.emplace_back("--compiler-filter=" + compiler_filter);
+ } else if (has_added_profile) {
+ args.emplace_back("--compiler-filter=speed-profile");
+ } else {
+ args.emplace_back("--compiler-filter=speed");
}
const std::string image_location = GetSystemServerImagePath(/*on_system=*/false, jar);
@@ -1649,6 +1705,7 @@
auto bcp_jars = android::base::Split(config_.GetBootClasspath(), ":");
if (!AddBootClasspathFds(args, readonly_files_raii, bcp_jars)) {
+ metrics.SetStatus(OdrMetrics::Status::kIoError);
return false;
}
std::string unused_error_msg;
@@ -1677,7 +1734,7 @@
if (!classloader_context.empty()) {
std::vector<int> fds;
for (const std::string& path : classloader_context) {
- std::string actual_path = AndroidRootRewrite(path);
+ std::string actual_path = RewriteParentDirectoryIfNeeded(path);
std::unique_ptr<File> file(OS::OpenFileForReading(actual_path.c_str()));
if (!file->IsValid()) {
PLOG(ERROR) << "Failed to open classloader context " << actual_path;
@@ -1699,15 +1756,11 @@
return true;
}
- bool timed_out = false;
- int dex2oat_exit_code = exec_utils_->ExecAndReturnCode(args, timeout, &timed_out, error_msg);
+ ExecResult dex2oat_result = exec_utils_->ExecAndReturnResult(args, timeout, error_msg);
+ metrics.SetDex2OatResult(dex2oat_result);
- if (dex2oat_exit_code != 0) {
- if (timed_out) {
- metrics.SetStatus(OdrMetrics::Status::kTimeLimitExceeded);
- } else {
- metrics.SetStatus(OdrMetrics::Status::kDex2OatError);
- }
+ if (dex2oat_result.status != ExecResult::kExited || dex2oat_result.exit_code != 0) {
+ metrics.SetStatus(OdrMetrics::Status::kDex2OatError);
EraseFiles(staging_files);
return false;
}
@@ -1728,10 +1781,18 @@
const char* staging_dir = nullptr;
metrics.SetStage(OdrMetrics::Stage::kPreparation);
+ if (!EnsureDirectoryExists(config_.GetArtifactDirectory())) {
+ LOG(ERROR) << "Failed to prepare artifact directory";
+ metrics.SetStatus(errno == EPERM ? OdrMetrics::Status::kDalvikCachePermissionDenied :
+ OdrMetrics::Status::kIoError);
+ return ExitCode::kCleanupFailed;
+ }
+
if (config_.GetRefresh()) {
Result<void> result = RefreshExistingArtifacts();
if (!result.ok()) {
LOG(ERROR) << "Failed to refresh existing artifacts: " << result.error();
+ metrics.SetStatus(OdrMetrics::Status::kIoError);
return ExitCode::kCleanupFailed;
}
}
@@ -1740,6 +1801,7 @@
Result<void> result = WriteCacheInfo();
if (!result.ok()) {
LOG(ERROR) << result.error();
+ metrics.SetStatus(OdrMetrics::Status::kIoError);
return ExitCode::kCleanupFailed;
}
@@ -1849,6 +1911,7 @@
}
metrics.SetStage(OdrMetrics::Stage::kComplete);
+ metrics.SetStatus(OdrMetrics::Status::kOK);
return ExitCode::kCompilationSuccess;
}
diff --git a/odrefresh/odrefresh.h b/odrefresh/odrefresh.h
index e48567f..ff3d660 100644
--- a/odrefresh/odrefresh.h
+++ b/odrefresh/odrefresh.h
@@ -112,7 +112,7 @@
// Removes files that are not in the list.
android::base::Result<void> CleanupArtifactDirectory(
- const std::vector<std::string>& artifacts_to_keep) const;
+ OdrMetrics& metrics, const std::vector<std::string>& artifacts_to_keep) const;
// Loads artifacts to memory and writes them back. This is a workaround for old versions of
// odsign, which encounters "file exists" error when it adds existing artifacts to fs-verity. This
@@ -150,6 +150,9 @@
WARN_UNUSED bool CheckSystemPropertiesHaveNotChanged(
const com::android::art::CacheInfo& cache_info) const;
+ // Returns true if the system image is built with the right userfaultfd GC flag.
+ WARN_UNUSED bool CheckBuildUserfaultFdGc() const;
+
// Returns true if boot classpath artifacts on /system are usable if they exist. Note that this
// function does not check file existence.
WARN_UNUSED bool BootClasspathArtifactsOnSystemUsable(
diff --git a/odrefresh/odrefresh_main.cc b/odrefresh/odrefresh_main.cc
index 58ef28f..98a75f3 100644
--- a/odrefresh/odrefresh_main.cc
+++ b/odrefresh/odrefresh_main.cc
@@ -26,6 +26,7 @@
#include "arch/instruction_set.h"
#include "base/file_utils.h"
#include "base/globals.h"
+#include "base/stl_util.h"
#include "odr_common.h"
#include "odr_compilation_log.h"
#include "odr_config.h"
@@ -40,7 +41,9 @@
using ::art::odrefresh::CompilationOptions;
using ::art::odrefresh::ExitCode;
using ::art::odrefresh::kCheckedSystemPropertyPrefixes;
+using ::art::odrefresh::kIgnoredSystemProperties;
using ::art::odrefresh::kSystemProperties;
+using ::art::odrefresh::kSystemPropertySystemServerCompilerFilterOverride;
using ::art::odrefresh::OdrCompilationLog;
using ::art::odrefresh::OdrConfig;
using ::art::odrefresh::OdrMetrics;
@@ -144,6 +147,8 @@
config->SetArtifactDirectory(GetApexDataDalvikCacheDirectory(art::InstructionSet::kNone));
} else if (ArgumentMatches(arg, "--zygote-arch=", &value)) {
zygote = value;
+ } else if (ArgumentMatches(arg, "--boot-image-compiler-filter=", &value)) {
+ config->SetBootImageCompilerFilter(value);
} else if (ArgumentMatches(arg, "--system-server-compiler-filter=", &value)) {
config->SetSystemServerCompilerFilter(value);
} else if (ArgumentMatches(arg, "--staging-dir=", &value)) {
@@ -172,7 +177,8 @@
config->SetZygoteKind(zygote_kind);
if (config->GetSystemServerCompilerFilter().empty()) {
- std::string filter = GetProperty("dalvik.vm.systemservercompilerfilter", "speed");
+ std::string filter = GetProperty("dalvik.vm.systemservercompilerfilter", "");
+ filter = GetProperty(kSystemPropertySystemServerCompilerFilterOverride, filter);
config->SetSystemServerCompilerFilter(filter);
}
@@ -195,7 +201,7 @@
return;
}
for (const char* prefix : kCheckedSystemPropertyPrefixes) {
- if (StartsWith(name, prefix)) {
+ if (StartsWith(name, prefix) && !art::ContainsElement(kIgnoredSystemProperties, name)) {
(*system_properties)[name] = value;
}
}
@@ -232,6 +238,9 @@
UsageMsg("--staging-dir=<DIR> Write temporary artifacts to <DIR> rather than");
UsageMsg(" .../staging");
UsageMsg("--zygote-arch=<STRING> Zygote kind that overrides ro.zygote");
+ UsageMsg("--boot-image-compiler-filter=<STRING>");
+ UsageMsg(" Compiler filter for the boot image. Default: ");
+ UsageMsg(" speed-profile");
UsageMsg("--system-server-compiler-filter=<STRING>");
UsageMsg(" Compiler filter that overrides");
UsageMsg(" dalvik.vm.systemservercompilerfilter");
@@ -248,16 +257,21 @@
// by others and prevents system_server from loading generated artifacts.
umask(S_IWGRP | S_IWOTH);
- // Explicitly initialize logging (b/201042799).
- android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
-
OdrConfig config(argv[0]);
int n = InitializeConfig(argc, argv, &config);
+
+ // Explicitly initialize logging (b/201042799).
+ // But not in CompOS mode - logd doesn't exist in Microdroid (b/265153235).
+ if (!config.GetCompilationOsMode()) {
+ android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
+ }
+
argv += n;
argc -= n;
if (argc != 1) {
ArgumentError("Expected 1 argument, but have %d.", argc);
}
+
GetSystemProperties(config.MutableSystemProperties());
OdrMetrics metrics(config.GetArtifactDirectory());
@@ -267,10 +281,17 @@
CompilationOptions compilation_options;
if (action == "--check") {
// Fast determination of whether artifacts are up to date.
- return odr.CheckArtifactsAreUpToDate(metrics, &compilation_options);
+ ExitCode exit_code = odr.CheckArtifactsAreUpToDate(metrics, &compilation_options);
+ // Normally, `--check` should not write metrics. If compilation is not required, there's no need
+ // to write metrics; if compilation is required, `--compile` will write metrics. Therefore,
+ // `--check` should only write metrics when things went wrong.
+ metrics.SetEnabled(exit_code != ExitCode::kOkay && exit_code != ExitCode::kCompilationRequired);
+ return exit_code;
} else if (action == "--compile") {
- const ExitCode exit_code = odr.CheckArtifactsAreUpToDate(metrics, &compilation_options);
+ ExitCode exit_code = odr.CheckArtifactsAreUpToDate(metrics, &compilation_options);
if (exit_code != ExitCode::kCompilationRequired) {
+ // No compilation required, so only write metrics when things went wrong.
+ metrics.SetEnabled(exit_code != ExitCode::kOkay);
return exit_code;
}
OdrCompilationLog compilation_log;
@@ -278,6 +299,8 @@
LOG(INFO) << "Compilation skipped because it was attempted recently";
return ExitCode::kOkay;
}
+ // Compilation required, so always write metrics.
+ metrics.SetEnabled(true);
ExitCode compile_result = odr.Compile(metrics, compilation_options);
compilation_log.Log(metrics.GetArtApexVersion(),
metrics.GetArtApexLastUpdateMillis(),
diff --git a/odrefresh/odrefresh_test.cc b/odrefresh/odrefresh_test.cc
index ae7cc78..31e03a2 100644
--- a/odrefresh/odrefresh_test.cc
+++ b/odrefresh/odrefresh_test.cc
@@ -29,6 +29,7 @@
#include "android-base/scopeguard.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
+#include "android-modules-utils/sdk_level.h"
#include "arch/instruction_set.h"
#include "base/common_art_test.h"
#include "base/file_utils.h"
@@ -71,14 +72,13 @@
public:
// A workaround to avoid MOCK_METHOD on a method with an `std::string*` parameter, which will lead
// to a conflict between gmock and android-base/logging.h (b/132668253).
- int ExecAndReturnCode(std::vector<std::string>& arg_vector,
- time_t,
- bool*,
- std::string*) const override {
- return DoExecAndReturnCode(arg_vector);
+ ExecResult ExecAndReturnResult(const std::vector<std::string>& arg_vector,
+ int,
+ std::string*) const override {
+ return {.status = ExecResult::kExited, .exit_code = DoExecAndReturnCode(arg_vector)};
}
- MOCK_METHOD(int, DoExecAndReturnCode, (std::vector<std::string> & arg_vector), (const));
+ MOCK_METHOD(int, DoExecAndReturnCode, (const std::vector<std::string>& arg_vector), (const));
};
// Matches a flag that starts with `flag` and is a colon-separated list that contains an element
@@ -202,7 +202,7 @@
config_.SetStandaloneSystemServerJars(Concatenate({services_foo_jar_, ":", services_bar_jar_}));
config_.SetIsa(InstructionSet::kX86_64);
config_.SetZygoteKind(ZygoteKind::kZygote64_32);
- config_.SetSystemServerCompilerFilter("speed"); // specify a default
+ config_.SetSystemServerCompilerFilter("");
config_.SetArtifactDirectory(dalvik_cache_dir_);
std::string staging_dir = dalvik_cache_dir_ + "/staging";
@@ -255,11 +255,41 @@
Contains(Concatenate({"--dex-file=", framework_jar_})),
Contains(FlagContains("--dex-fd=", FdOf(core_oj_jar_))),
Contains(FlagContains("--dex-fd=", FdOf(framework_jar_))),
- Contains(FlagContains("--profile-file-fd=", FdOf(art_profile_))),
- Contains(FlagContains("--profile-file-fd=", FdOf(framework_profile_))),
Contains(Concatenate({"--oat-location=", dalvik_cache_dir_, "/x86_64/boot.oat"})),
- Contains(HasSubstr("--base=")),
- Contains("--compiler-filter=speed-profile"))))
+ Contains(HasSubstr("--base=")))))
+ .WillOnce(Return(0));
+
+ EXPECT_EQ(odrefresh_->Compile(*metrics_,
+ CompilationOptions{
+ .compile_boot_classpath_for_isas = {InstructionSet::kX86_64},
+ }),
+ ExitCode::kCompilationSuccess);
+}
+
+TEST_F(OdRefreshTest, BootClasspathJarsWithExplicitCompilerFilter) {
+ config_.SetBootImageCompilerFilter("speed");
+
+ // Profiles should still be passed.
+ EXPECT_CALL(*mock_exec_utils_,
+ DoExecAndReturnCode(
+ AllOf(Contains(FlagContains("--profile-file-fd=", FdOf(art_profile_))),
+ Contains(FlagContains("--profile-file-fd=", FdOf(framework_profile_))),
+ Contains("--compiler-filter=speed"))))
+ .WillOnce(Return(0));
+
+ EXPECT_EQ(odrefresh_->Compile(*metrics_,
+ CompilationOptions{
+ .compile_boot_classpath_for_isas = {InstructionSet::kX86_64},
+ }),
+ ExitCode::kCompilationSuccess);
+}
+
+TEST_F(OdRefreshTest, BootClasspathJarsWithDefaultCompilerFilter) {
+ EXPECT_CALL(*mock_exec_utils_,
+ DoExecAndReturnCode(
+ AllOf(Contains(FlagContains("--profile-file-fd=", FdOf(art_profile_))),
+ Contains(FlagContains("--profile-file-fd=", FdOf(framework_profile_))),
+ Contains("--compiler-filter=speed-profile"))))
.WillOnce(Return(0));
EXPECT_EQ(odrefresh_->Compile(*metrics_,
@@ -362,9 +392,11 @@
ExitCode::kCompilationSuccess);
}
-TEST_F(OdRefreshTest, CompileSetsCompilerFilterToSpeed) {
- // Test setup: use "speed" compiler filter.
- config_.SetSystemServerCompilerFilter("speed");
+// Test setup: The compiler filter is explicitly set to "speed-profile". Use it regardless of
+// whether the profile exists or not. Dex2oat will fall back to "verify" if the profile doesn't
+// exist.
+TEST_F(OdRefreshTest, CompileSetsCompilerFilterWithExplicitValue) {
+ config_.SetSystemServerCompilerFilter("speed-profile");
// Uninteresting calls.
EXPECT_CALL(
@@ -376,13 +408,12 @@
*mock_exec_utils_,
DoExecAndReturnCode(AllOf(Contains(Concatenate({"--dex-file=", location_provider_jar_})),
Not(Contains(HasSubstr("--profile-file-fd="))),
- Contains("--compiler-filter=speed"))))
+ Contains("--compiler-filter=speed-profile"))))
.WillOnce(Return(0));
- EXPECT_CALL(
- *mock_exec_utils_,
- DoExecAndReturnCode(AllOf(Contains(Concatenate({"--dex-file=", services_jar_})),
- Not(Contains(HasSubstr("--profile-file-fd="))),
- Contains("--compiler-filter=speed"))))
+ EXPECT_CALL(*mock_exec_utils_,
+ DoExecAndReturnCode(AllOf(Contains(Concatenate({"--dex-file=", services_jar_})),
+ Contains(HasSubstr("--profile-file-fd=")),
+ Contains("--compiler-filter=speed-profile"))))
.WillOnce(Return(0));
EXPECT_EQ(
odrefresh_->Compile(*metrics_,
@@ -392,11 +423,9 @@
ExitCode::kCompilationSuccess);
}
-TEST_F(OdRefreshTest, CompileSetsCompilerFilterToSpeedProfile) {
- // Test setup: with "speed-profile" compiler filter in the request, only apply if there is a
- // profile, otherwise fallback to speed.
- config_.SetSystemServerCompilerFilter("speed-profile");
-
+// Test setup: The compiler filter is not explicitly set. Use "speed-profile" if there is a vetted
+// profile (on U+), otherwise fall back to "speed".
+TEST_F(OdRefreshTest, CompileSetsCompilerFilterWithDefaultValue) {
// Uninteresting calls.
EXPECT_CALL(
*mock_exec_utils_, DoExecAndReturnCode(_))
@@ -410,12 +439,20 @@
Not(Contains(HasSubstr("--profile-file-fd="))),
Contains("--compiler-filter=speed"))))
.WillOnce(Return(0));
- EXPECT_CALL(
- *mock_exec_utils_,
- DoExecAndReturnCode(AllOf(Contains(Concatenate({"--dex-file=", services_jar_})),
- Contains(HasSubstr("--profile-file-fd=")),
- Contains("--compiler-filter=speed-profile"))))
- .WillOnce(Return(0));
+ // Only on U+ should we use the profile by default if available.
+ if (android::modules::sdklevel::IsAtLeastU()) {
+ EXPECT_CALL(*mock_exec_utils_,
+ DoExecAndReturnCode(AllOf(Contains(Concatenate({"--dex-file=", services_jar_})),
+ Contains(HasSubstr("--profile-file-fd=")),
+ Contains("--compiler-filter=speed-profile"))))
+ .WillOnce(Return(0));
+ } else {
+ EXPECT_CALL(*mock_exec_utils_,
+ DoExecAndReturnCode(AllOf(Contains(Concatenate({"--dex-file=", services_jar_})),
+ Not(Contains(HasSubstr("--profile-file-fd="))),
+ Contains("--compiler-filter=speed"))))
+ .WillOnce(Return(0));
+ }
EXPECT_EQ(
odrefresh_->Compile(*metrics_,
CompilationOptions{
@@ -424,36 +461,6 @@
ExitCode::kCompilationSuccess);
}
-TEST_F(OdRefreshTest, CompileSetsCompilerFilterToVerify) {
- // Test setup: use "speed" compiler filter.
- config_.SetSystemServerCompilerFilter("verify");
-
- // Uninteresting calls.
- EXPECT_CALL(
- *mock_exec_utils_, DoExecAndReturnCode(_))
- .Times(odrefresh_->AllSystemServerJars().size() - 2)
- .WillRepeatedly(Return(0));
-
- EXPECT_CALL(
- *mock_exec_utils_,
- DoExecAndReturnCode(AllOf(Contains(Concatenate({"--dex-file=", location_provider_jar_})),
- Not(Contains(HasSubstr("--profile-file-fd="))),
- Contains("--compiler-filter=verify"))))
- .WillOnce(Return(0));
- EXPECT_CALL(
- *mock_exec_utils_,
- DoExecAndReturnCode(AllOf(Contains(Concatenate({"--dex-file=", services_jar_})),
- Not(Contains(HasSubstr("--profile-file-fd="))),
- Contains("--compiler-filter=verify"))))
- .WillOnce(Return(0));
- EXPECT_EQ(
- odrefresh_->Compile(*metrics_,
- CompilationOptions{
- .system_server_jars_to_compile = odrefresh_->AllSystemServerJars(),
- }),
- ExitCode::kCompilationSuccess);
-}
-
TEST_F(OdRefreshTest, OutputFilesAndIsa) {
EXPECT_CALL(
*mock_exec_utils_,
diff --git a/openjdkjvm/Android.bp b/openjdkjvm/Android.bp
index 2757d9d..eb379d8 100644
--- a/openjdkjvm/Android.bp
+++ b/openjdkjvm/Android.bp
@@ -35,10 +35,10 @@
name: "art_openjdkjvm_license",
visibility: [":__subpackages__"],
license_kinds: [
- "SPDX-license-identifier-GPL-with-classpath-exception",
+ "SPDX-license-identifier-GPL-2.0-with-classpath-exception",
],
license_text: [
- "NOTICE",
+ "LICENSE",
],
}
diff --git a/openjdkjvm/NOTICE b/openjdkjvm/LICENSE
similarity index 100%
rename from openjdkjvm/NOTICE
rename to openjdkjvm/LICENSE
diff --git a/openjdkjvm/OpenjdkJvm.cc b/openjdkjvm/OpenjdkJvm.cc
index 8c1f773..da6fc85 100644
--- a/openjdkjvm/OpenjdkJvm.cc
+++ b/openjdkjvm/OpenjdkJvm.cc
@@ -32,6 +32,8 @@
/*
* Services that OpenJDK expects the VM to provide.
*/
+
+#include <assert.h>
#include <dlfcn.h>
#include <limits.h>
#include <stdio.h>
diff --git a/openjdkjvmti/Android.bp b/openjdkjvmti/Android.bp
index d01357f..d2ac9b6 100644
--- a/openjdkjvmti/Android.bp
+++ b/openjdkjvmti/Android.bp
@@ -36,10 +36,10 @@
visibility: [":__subpackages__"],
license_kinds: [
"SPDX-license-identifier-Apache-2.0",
- "SPDX-license-identifier-GPL-with-classpath-exception",
+ "SPDX-license-identifier-GPL-2.0-with-classpath-exception",
],
license_text: [
- "NOTICE",
+ "LICENSE",
],
}
@@ -111,7 +111,6 @@
shared_libs: [
"libart",
"libart-compiler",
- "libart-dexlayout",
"libdexfile",
"libartbase",
],
@@ -130,7 +129,6 @@
shared_libs: [
"libartd",
"libartd-compiler",
- "libartd-dexlayout",
"libdexfiled",
"libartbased",
],
diff --git a/openjdkjvmti/NOTICE b/openjdkjvmti/LICENSE
similarity index 100%
rename from openjdkjvmti/NOTICE
rename to openjdkjvmti/LICENSE
diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc
index 09900e1..276b3a8 100644
--- a/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/openjdkjvmti/OpenjdkJvmTi.cc
@@ -89,12 +89,6 @@
} \
} while (false)
-// Returns whether we are able to use all jvmti features.
-static bool IsFullJvmtiAvailable() {
- art::Runtime* runtime = art::Runtime::Current();
- return runtime->GetInstrumentation()->IsForcedInterpretOnly() || runtime->IsJavaDebuggable();
-}
-
class JvmtiFunctions {
private:
static jvmtiError getEnvironmentError(jvmtiEnv* env) {
@@ -1474,19 +1468,21 @@
FieldUtil::Register(gEventHandler);
BreakpointUtil::Register(gEventHandler);
Transformer::Register(gEventHandler);
-
- {
- // Make sure we can deopt anything we need to.
- art::ScopedSuspendAll ssa(__FUNCTION__);
- gDeoptManager->FinishSetup();
- }
-
+ gDeoptManager->FinishSetup();
runtime->GetJavaVM()->AddEnvironmentHook(GetEnvHandler);
return true;
}
extern "C" bool ArtPlugin_Deinitialize() {
+ // When runtime is shutting down, it is not necessary to unregister callbacks or update
+ // instrumentation levels. Removing callbacks require a GC critical section in some cases and
+ // when runtime is shutting down we already stop GC and hence it is not safe to request to
+ // enter a GC critical section.
+ if (art::Runtime::Current()->IsShuttingDown(art::Thread::Current())) {
+ return true;
+ }
+
gEventHandler->Shutdown();
gDeoptManager->Shutdown();
PhaseUtil::Unregister();
diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h
index 083ba6d..bc965a2 100644
--- a/openjdkjvmti/art_jvmti.h
+++ b/openjdkjvmti/art_jvmti.h
@@ -47,9 +47,11 @@
#include "base/strlcpy.h"
#include "base/mutex.h"
#include "events.h"
+#include "instrumentation.h"
#include "jni/java_vm_ext.h"
#include "jni/jni_env_ext.h"
#include "jvmti.h"
+#include "runtime.h"
#include "ti_breakpoint.h"
namespace art {
@@ -69,6 +71,13 @@
// This is the value 0x70010200.
static constexpr jint kArtTiVersion = JVMTI_VERSION_1_2 | 0x40000000;
+// Returns whether we are able to use all jvmti features.
+static inline bool IsFullJvmtiAvailable() {
+ art::Runtime* runtime = art::Runtime::Current();
+ return runtime->GetInstrumentation()->IsForcedInterpretOnly() ||
+ runtime->IsJavaDebuggableAtInit();
+}
+
// A structure that is a jvmtiEnv with additional information for the runtime.
struct ArtJvmTiEnv : public jvmtiEnv {
art::JavaVMExt* art_vm;
@@ -141,7 +150,7 @@
explicit JvmtiDeleter(jvmtiEnv* env) : env_(env) {}
JvmtiDeleter(JvmtiDeleter&) = default;
- JvmtiDeleter(JvmtiDeleter&&) = default;
+ JvmtiDeleter(JvmtiDeleter&&) noexcept = default;
JvmtiDeleter& operator=(const JvmtiDeleter&) = default;
void operator()(T* ptr) const {
@@ -161,7 +170,7 @@
explicit JvmtiDeleter(jvmtiEnv* env) : env_(env) {}
JvmtiDeleter(JvmtiDeleter&) = default;
- JvmtiDeleter(JvmtiDeleter&&) = default;
+ JvmtiDeleter(JvmtiDeleter&&) noexcept = default;
JvmtiDeleter& operator=(const JvmtiDeleter&) = default;
template <typename U>
diff --git a/openjdkjvmti/deopt_manager.cc b/openjdkjvmti/deopt_manager.cc
index 312a797..129aa0f 100644
--- a/openjdkjvmti/deopt_manager.cc
+++ b/openjdkjvmti/deopt_manager.cc
@@ -47,10 +47,12 @@
#include "gc/scoped_gc_critical_section.h"
#include "instrumentation.h"
#include "jit/jit.h"
+#include "jit/jit_code_cache.h"
#include "jni/jni_internal.h"
#include "mirror/class-inl.h"
#include "mirror/object_array-inl.h"
#include "nativehelper/scoped_local_ref.h"
+#include "oat_file_manager.h"
#include "read_barrier_config.h"
#include "runtime_callbacks.h"
#include "scoped_thread_state_change-inl.h"
@@ -61,16 +63,15 @@
namespace openjdkjvmti {
-// TODO We should make this much more selective in the future so we only return true when we
+static constexpr const char* kInstrumentationKey = "JVMTI_DeoptRequester";
+
+// We could make this much more selective in the future so we only return true when we
// actually care about the method at this time (ie active frames had locals changed). For now we
-// just assume that if anything has changed any frame's locals we care about all methods. If nothing
-// has we only care about methods with active breakpoints on them. In the future we should probably
-// rewrite all of this to instead do this at the ShadowFrame or thread granularity.
-bool JvmtiMethodInspectionCallback::IsMethodBeingInspected(art::ArtMethod* method) {
- // In non-java-debuggable runtimes the breakpoint check would miss if we have breakpoints on
- // methods that are inlined. Since these features are best effort in non-java-debuggable
- // runtimes it is OK to be less precise. For debuggable runtimes, inlining is disabled.
- return manager_->HaveLocalsChanged() || manager_->MethodHasBreakpoints(method);
+// just assume that if anything has changed any frame's locals we care about all methods. This only
+// impacts whether we are able to OSR or not so maybe not really important to maintain frame
+// specific information.
+bool JvmtiMethodInspectionCallback::HaveLocalsChanged() {
+ return manager_->HaveLocalsChanged();
}
DeoptManager::DeoptManager()
@@ -94,14 +95,6 @@
callbacks->AddMethodInspectionCallback(&inspection_callback_);
}
-void DeoptManager::Shutdown() {
- art::ScopedThreadStateChange stsc(art::Thread::Current(),
- art::ThreadState::kWaitingForDebuggerToAttach);
- art::ScopedSuspendAll ssa("remove method Inspection Callback");
- art::RuntimeCallbacks* callbacks = art::Runtime::Current()->GetRuntimeCallbacks();
- callbacks->RemoveMethodInspectionCallback(&inspection_callback_);
-}
-
void DeoptManager::DumpDeoptInfo(art::Thread* self, std::ostream& stream) {
art::ScopedObjectAccess soa(self);
art::MutexLock mutll(self, *art::Locks::thread_list_lock_);
@@ -151,48 +144,59 @@
void DeoptManager::FinishSetup() {
art::Thread* self = art::Thread::Current();
- art::MutexLock mu(self, deoptimization_status_lock_);
-
art::Runtime* runtime = art::Runtime::Current();
- // See if we need to do anything.
- if (!runtime->IsJavaDebuggable()) {
- // See if we can enable all JVMTI functions. If this is false, only kArtTiVersion agents can be
- // retrieved and they will all be best-effort.
- if (PhaseUtil::GetPhaseUnchecked() == JVMTI_PHASE_ONLOAD) {
- // We are still early enough to change the compiler options and get full JVMTI support.
- LOG(INFO) << "Openjdkjvmti plugin loaded on a non-debuggable runtime. Changing runtime to "
- << "debuggable state. Please pass '--debuggable' to dex2oat and "
- << "'-Xcompiler-option --debuggable' to dalvikvm in the future.";
- DCHECK(runtime->GetJit() == nullptr) << "Jit should not be running yet!";
- runtime->AddCompilerOption("--debuggable");
- runtime->SetJavaDebuggable(true);
- } else {
- LOG(WARNING) << "Openjdkjvmti plugin was loaded on a non-debuggable Runtime. Plugin was "
- << "loaded too late to change runtime state to DEBUGGABLE. Only kArtTiVersion "
- << "(0x" << std::hex << kArtTiVersion << ") environments are available. Some "
- << "functionality might not work properly.";
- if (runtime->GetJit() == nullptr &&
- runtime->GetJITOptions()->UseJitCompilation() &&
- !runtime->GetInstrumentation()->IsForcedInterpretOnly()) {
- // If we don't have a jit we should try to start the jit for performance reasons. We only
- // need to do this for late attach on non-debuggable processes because for debuggable
- // processes we already rely on jit and we cannot force this jit to start if we are still in
- // OnLoad since the runtime hasn't started up sufficiently. This is only expected to happen
- // on userdebug/eng builds.
- LOG(INFO) << "Attempting to start jit for openjdkjvmti plugin.";
- // Note: use rwx allowed = true, because if this is the system server, we will not be
- // allowed to allocate any JIT code cache, anyways.
- runtime->CreateJitCodeCache(/*rwx_memory_allowed=*/true);
- runtime->CreateJit();
- if (runtime->GetJit() == nullptr) {
- LOG(WARNING) << "Could not start jit for openjdkjvmti plugin. This process might be "
- << "quite slow as it is running entirely in the interpreter. Try running "
- << "'setenforce 0' and restarting this process.";
- }
- }
- }
- runtime->DeoptimizeBootImage();
+ if (runtime->IsJavaDebuggable()) {
+ return;
}
+
+ // See if we can enable all JVMTI functions.
+ if (PhaseUtil::GetPhaseUnchecked() == JVMTI_PHASE_ONLOAD) {
+ // We are still early enough to change the compiler options and get full JVMTI support.
+ LOG(INFO) << "Openjdkjvmti plugin loaded on a non-debuggable runtime. Changing runtime to "
+ << "debuggable state. Please pass '--debuggable' to dex2oat and "
+ << "'-Xcompiler-option --debuggable' to dalvikvm in the future.";
+ DCHECK(runtime->GetJit() == nullptr) << "Jit should not be running yet!";
+ art::ScopedSuspendAll ssa(__FUNCTION__);
+ // TODO check if we need to hold deoptimization_status_lock_ here.
+ art::MutexLock mu(self, deoptimization_status_lock_);
+ runtime->AddCompilerOption("--debuggable");
+ runtime->SetRuntimeDebugState(art::Runtime::RuntimeDebugState::kJavaDebuggableAtInit);
+ runtime->DeoptimizeBootImage();
+ return;
+ }
+
+ // Runtime has already started in non-debuggable mode. Only kArtTiVersion agents can be
+ // retrieved and they will all be best-effort.
+ LOG(WARNING) << "Openjdkjvmti plugin was loaded on a non-debuggable Runtime. Plugin was "
+ << "loaded too late to change runtime state to support all capabilities. Only "
+ << "kArtTiVersion (0x" << std::hex << kArtTiVersion << ") environments are "
+ << "available. Some functionality might not work properly.";
+
+ // Transition the runtime to debuggable:
+ // 1. Wait for any background verification tasks to finish. We don't support
+ // background verification after moving to debuggable state.
+ runtime->GetOatFileManager().WaitForBackgroundVerificationTasksToFinish();
+
+ // Do the transition in ScopedJITSuspend, so we don't start any JIT compilations
+ // before the transition to debuggable is finished.
+ art::jit::ScopedJitSuspend suspend_jit;
+ art::ScopedSuspendAll ssa(__FUNCTION__);
+
+ // 2. Discard any JITed code that was generated before, since they would be
+ // compiled without debug support.
+ art::jit::Jit* jit = runtime->GetJit();
+ if (jit != nullptr) {
+ jit->GetCodeCache()->InvalidateAllCompiledCode();
+ jit->GetCodeCache()->TransitionToDebuggable();
+ jit->GetJitCompiler()->SetDebuggableCompilerOption(true);
+ }
+
+ // 3. Change the state to JavaDebuggable, so that debug features can be
+ // enabled from now on.
+ runtime->SetRuntimeDebugState(art::Runtime::RuntimeDebugState::kJavaDebuggable);
+
+ // 4. Update all entrypoints to avoid using any AOT code.
+ runtime->GetInstrumentation()->UpdateEntrypointsForDebuggable();
}
bool DeoptManager::MethodHasBreakpoints(art::ArtMethod* method) {
@@ -365,6 +369,39 @@
}
}
+void DeoptManager::Shutdown() {
+ art::Thread* self = art::Thread::Current();
+ art::Runtime* runtime = art::Runtime::Current();
+
+ // Do the transition in ScopedJITSuspend, so we don't start any JIT compilations
+ // before the transition to debuggable is finished.
+ art::jit::ScopedJitSuspend suspend_jit;
+
+ art::ScopedThreadStateChange sts(self, art::ThreadState::kSuspended);
+ deoptimization_status_lock_.ExclusiveLock(self);
+ ScopedDeoptimizationContext sdc(self, this);
+
+ art::jit::Jit* jit = runtime->GetJit();
+ if (jit != nullptr && !runtime->IsShuttingDown(self)) {
+ jit->GetCodeCache()->InvalidateAllCompiledCode();
+ jit->GetCodeCache()->TransitionToDebuggable();
+ jit->GetJitCompiler()->SetDebuggableCompilerOption(false);
+ }
+
+ art::RuntimeCallbacks* callbacks = runtime->GetRuntimeCallbacks();
+ callbacks->RemoveMethodInspectionCallback(&inspection_callback_);
+ if (!runtime->IsJavaDebuggableAtInit()) {
+ runtime->SetRuntimeDebugState(art::Runtime::RuntimeDebugState::kNonJavaDebuggable);
+ }
+ // TODO(mythria): DeoptManager should use only one key. Merge
+ // kInstrumentationKey and kDeoptManagerInstrumentationKey.
+ if (!runtime->IsShuttingDown(self)) {
+ art::Runtime::Current()->GetInstrumentation()->DisableDeoptimization(kInstrumentationKey);
+ art::Runtime::Current()->GetInstrumentation()->DisableDeoptimization(
+ kDeoptManagerInstrumentationKey);
+ }
+}
+
void DeoptManager::RemoveDeoptimizeAllMethodsLocked(art::Thread* self) {
DCHECK_GT(global_deopt_count_, 0u) << "Request to remove non-existent global deoptimization!";
global_deopt_count_--;
@@ -438,7 +475,6 @@
return OK;
}
-static constexpr const char* kInstrumentationKey = "JVMTI_DeoptRequester";
void DeoptManager::RemoveDeoptimizationRequester() {
art::Thread* self = art::Thread::Current();
diff --git a/openjdkjvmti/deopt_manager.h b/openjdkjvmti/deopt_manager.h
index c0a788b..e9b91de 100644
--- a/openjdkjvmti/deopt_manager.h
+++ b/openjdkjvmti/deopt_manager.h
@@ -57,8 +57,7 @@
public:
explicit JvmtiMethodInspectionCallback(DeoptManager* manager) : manager_(manager) {}
- bool IsMethodBeingInspected(art::ArtMethod* method)
- override REQUIRES_SHARED(art::Locks::mutator_lock_);
+ bool HaveLocalsChanged() override REQUIRES_SHARED(art::Locks::mutator_lock_);
private:
DeoptManager* manager_;
@@ -111,9 +110,7 @@
REQUIRES_SHARED(art::Locks::mutator_lock_);
void DeoptimizeAllThreads() REQUIRES_SHARED(art::Locks::mutator_lock_);
- void FinishSetup()
- REQUIRES(!deoptimization_status_lock_, !art::Roles::uninterruptible_)
- REQUIRES(art::Locks::mutator_lock_);
+ void FinishSetup() REQUIRES(!deoptimization_status_lock_, !art::Roles::uninterruptible_);
static DeoptManager* Get();
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc
index a6425af..55fe9ef 100644
--- a/openjdkjvmti/events.cc
+++ b/openjdkjvmti/events.cc
@@ -447,9 +447,8 @@
if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kMonitorWait)) {
art::Thread* self = art::Thread::Current();
art::JNIEnvExt* jnienv = self->GetJniEnv();
- art::ArtField* parkBlockerField = art::jni::DecodeArtField(
- art::WellKnownClasses::java_lang_Thread_parkBlocker);
- art::ObjPtr<art::mirror::Object> blocker_obj = parkBlockerField->GetObj(self->GetPeer());
+ art::ObjPtr<art::mirror::Object> blocker_obj =
+ art::WellKnownClasses::java_lang_Thread_parkBlocker->GetObj(self->GetPeer());
if (blocker_obj.IsNull()) {
blocker_obj = self->GetPeer();
}
@@ -505,9 +504,8 @@
if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kMonitorWaited)) {
art::Thread* self = art::Thread::Current();
art::JNIEnvExt* jnienv = self->GetJniEnv();
- art::ArtField* parkBlockerField = art::jni::DecodeArtField(
- art::WellKnownClasses::java_lang_Thread_parkBlocker);
- art::ObjPtr<art::mirror::Object> blocker_obj = parkBlockerField->GetObj(self->GetPeer());
+ art::ObjPtr<art::mirror::Object> blocker_obj =
+ art::WellKnownClasses::java_lang_Thread_parkBlocker->GetObj(self->GetPeer());
if (blocker_obj.IsNull()) {
blocker_obj = self->GetPeer();
}
@@ -741,7 +739,6 @@
// Call-back for when a method is popped due to an exception throw. A method will either cause a
// MethodExited call-back or a MethodUnwind call-back when its activation is removed.
void MethodUnwind(art::Thread* self,
- art::Handle<art::mirror::Object> this_object ATTRIBUTE_UNUSED,
art::ArtMethod* method,
uint32_t dex_pc ATTRIBUTE_UNUSED)
REQUIRES_SHARED(art::Locks::mutator_lock_) override {
@@ -1256,10 +1253,10 @@
}
for (auto& m : klass->GetMethods(art::kRuntimePointerSize)) {
const void* code = m.GetEntryPointFromQuickCompiledCode();
- if (m.IsNative() || m.IsProxyMethod()) {
+ if (m.IsNative() || m.IsProxyMethod() || !m.IsInvokable()) {
continue;
} else if (!runtime_->GetClassLinker()->IsQuickToInterpreterBridge(code) &&
- !runtime_->IsAsyncDeoptimizeable(reinterpret_cast<uintptr_t>(code))) {
+ !runtime_->IsAsyncDeoptimizeable(&m, reinterpret_cast<uintptr_t>(code))) {
runtime_->GetInstrumentation()->InitializeMethodsCode(&m, /*aot_code=*/ nullptr);
}
}
diff --git a/openjdkjvmti/jvmti_allocator.h b/openjdkjvmti/jvmti_allocator.h
index bd4c85b..4adf769 100644
--- a/openjdkjvmti/jvmti_allocator.h
+++ b/openjdkjvmti/jvmti_allocator.h
@@ -46,13 +46,13 @@
template <>
class JvmtiAllocator<void> {
public:
- typedef void value_type;
- typedef void* pointer;
- typedef const void* const_pointer;
+ using value_type = void;
+ using pointer = void*;
+ using const_pointer = const void*;
template <typename U>
struct rebind {
- typedef JvmtiAllocator<U> other;
+ using other = JvmtiAllocator<U>;
};
explicit JvmtiAllocator(jvmtiEnv* env) : env_(env) {}
@@ -79,17 +79,17 @@
template <typename T>
class JvmtiAllocator {
public:
- typedef T value_type;
- typedef T* pointer;
- typedef T& reference;
- typedef const T* const_pointer;
- typedef const T& const_reference;
- typedef size_t size_type;
- typedef ptrdiff_t difference_type;
+ using value_type = T;
+ using pointer = T*;
+ using reference = T&;
+ using const_pointer = const T*;
+ using const_reference = const T&;
+ using size_type = size_t;
+ using difference_type = ptrdiff_t;
template <typename U>
struct rebind {
- typedef JvmtiAllocator<U> other;
+ using other = JvmtiAllocator<U>;
};
explicit JvmtiAllocator(jvmtiEnv* env) : env_(env) {}
diff --git a/openjdkjvmti/jvmti_weak_table-inl.h b/openjdkjvmti/jvmti_weak_table-inl.h
index 5b28e45..c5663e5 100644
--- a/openjdkjvmti/jvmti_weak_table-inl.h
+++ b/openjdkjvmti/jvmti_weak_table-inl.h
@@ -114,7 +114,7 @@
return true;
}
- if (art::kUseReadBarrier && self->GetIsGcMarking() && !update_since_last_sweep_) {
+ if (art::gUseReadBarrier && self->GetIsGcMarking() && !update_since_last_sweep_) {
// Under concurrent GC, there is a window between moving objects and sweeping of system
// weaks in which mutators are active. We may receive a to-space object pointer in obj,
// but still have from-space pointers in the table. Explicitly update the table once.
@@ -156,7 +156,7 @@
return true;
}
- if (art::kUseReadBarrier && self->GetIsGcMarking() && !update_since_last_sweep_) {
+ if (art::gUseReadBarrier && self->GetIsGcMarking() && !update_since_last_sweep_) {
// Under concurrent GC, there is a window between moving objects and sweeping of system
// weaks in which mutators are active. We may receive a to-space object pointer in obj,
// but still have from-space pointers in the table. Explicitly update the table once.
@@ -210,13 +210,13 @@
template <typename T>
template <typename Updater, typename JvmtiWeakTable<T>::TableUpdateNullTarget kTargetNull>
ALWAYS_INLINE inline void JvmtiWeakTable<T>::UpdateTableWith(Updater& updater) {
- // We optimistically hope that elements will still be well-distributed when re-inserting them.
- // So play with the map mechanics, and postpone rehashing. This avoids the need of a side
- // vector and two passes.
- float original_max_load_factor = tagged_objects_.max_load_factor();
- tagged_objects_.max_load_factor(std::numeric_limits<float>::max());
- // For checking that a max load-factor actually does what we expect.
- size_t original_bucket_count = tagged_objects_.bucket_count();
+ // We can't emplace within the map as a to-space reference could be the same as some
+ // from-space object reference in the map, causing correctness issues. The problem
+ // doesn't arise if all updated <K,V> pairs are inserted after the loop as by then such
+ // from-space object references would also have been taken care of.
+
+ // Side vector to hold node handles of entries which are updated.
+ std::vector<typename TagMap::node_type> updated_node_handles;
for (auto it = tagged_objects_.begin(); it != tagged_objects_.end();) {
DCHECK(!it->first.IsNull());
@@ -226,22 +226,24 @@
if (kTargetNull == kIgnoreNull && target_obj == nullptr) {
// Ignore null target, don't do anything.
} else {
- T tag = it->second;
- it = tagged_objects_.erase(it);
+ auto nh = tagged_objects_.extract(it++);
+ DCHECK(!nh.empty());
if (target_obj != nullptr) {
- tagged_objects_.emplace(art::GcRoot<art::mirror::Object>(target_obj), tag);
- DCHECK_EQ(original_bucket_count, tagged_objects_.bucket_count());
+ nh.key() = art::GcRoot<art::mirror::Object>(target_obj);
+ updated_node_handles.push_back(std::move(nh));
} else if (kTargetNull == kCallHandleNull) {
- HandleNullSweep(tag);
+ HandleNullSweep(nh.mapped());
}
- continue; // Iterator was implicitly updated by erase.
+ continue; // Iterator already updated above.
}
}
it++;
}
-
- tagged_objects_.max_load_factor(original_max_load_factor);
- // TODO: consider rehash here.
+ while (!updated_node_handles.empty()) {
+ auto ret = tagged_objects_.insert(std::move(updated_node_handles.back()));
+ DCHECK(ret.inserted);
+ updated_node_handles.pop_back();
+ }
}
template <typename T>
diff --git a/openjdkjvmti/jvmti_weak_table.h b/openjdkjvmti/jvmti_weak_table.h
index ea0d023..674b2a3 100644
--- a/openjdkjvmti/jvmti_weak_table.h
+++ b/openjdkjvmti/jvmti_weak_table.h
@@ -152,7 +152,7 @@
// Performance optimization: To avoid multiple table updates, ensure that during GC we
// only update once. See the comment on the implementation of GetTagSlowPath.
- if (art::kUseReadBarrier &&
+ if (art::gUseReadBarrier &&
self != nullptr &&
self->GetIsGcMarking() &&
!update_since_last_sweep_) {
@@ -211,13 +211,13 @@
};
using TagAllocator = JvmtiAllocator<std::pair<const art::GcRoot<art::mirror::Object>, T>>;
- std::unordered_map<art::GcRoot<art::mirror::Object>,
- T,
- HashGcRoot,
- EqGcRoot,
- TagAllocator> tagged_objects_
- GUARDED_BY(allow_disallow_lock_)
- GUARDED_BY(art::Locks::mutator_lock_);
+ using TagMap = std::unordered_map<art::GcRoot<art::mirror::Object>,
+ T,
+ HashGcRoot,
+ EqGcRoot,
+ TagAllocator>;
+
+ TagMap tagged_objects_ GUARDED_BY(allow_disallow_lock_) GUARDED_BY(art::Locks::mutator_lock_);
// To avoid repeatedly scanning the whole table, remember if we did that since the last sweep.
bool update_since_last_sweep_;
};
diff --git a/openjdkjvmti/ti_class.cc b/openjdkjvmti/ti_class.cc
index 9752fb1..37ceb37 100644
--- a/openjdkjvmti/ti_class.cc
+++ b/openjdkjvmti/ti_class.cc
@@ -80,7 +80,7 @@
#include "ti_phase.h"
#include "ti_redefine.h"
#include "transform.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace openjdkjvmti {
@@ -927,40 +927,42 @@
} else if (count_ptr == nullptr || classes == nullptr) {
return ERR(NULL_POINTER);
}
- art::JNIEnvExt* jnienv = self->GetJniEnv();
- if (loader == nullptr ||
- jnienv->IsInstanceOf(loader, art::WellKnownClasses::java_lang_BootClassLoader)) {
+ std::vector<const art::DexFile*> dex_files_storage;
+ const std::vector<const art::DexFile*>* dex_files = nullptr;
+ if (loader == nullptr) {
// We can just get the dex files directly for the boot class path.
- return CopyClassDescriptors(env,
- art::Runtime::Current()->GetClassLinker()->GetBootClassPath(),
- count_ptr,
- classes);
+ dex_files = &art::Runtime::Current()->GetClassLinker()->GetBootClassPath();
+ } else {
+ art::ScopedObjectAccess soa(self);
+ art::StackHandleScope<1> hs(self);
+ art::Handle<art::mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<art::mirror::ClassLoader>(loader)));
+ if (class_loader->InstanceOf(art::WellKnownClasses::java_lang_BootClassLoader.Get())) {
+ // We can just get the dex files directly for the boot class path.
+ dex_files = &art::Runtime::Current()->GetClassLinker()->GetBootClassPath();
+ } else if (!class_loader->InstanceOf(art::WellKnownClasses::java_lang_ClassLoader.Get())) {
+ return ERR(ILLEGAL_ARGUMENT);
+ } else if (!class_loader->InstanceOf(
+ art::WellKnownClasses::dalvik_system_BaseDexClassLoader.Get())) {
+ JVMTI_LOG(ERROR, env) << "GetClassLoaderClassDescriptors is only implemented for "
+ << "BootClassPath and dalvik.system.BaseDexClassLoader class loaders";
+ // TODO Possibly return OK With no classes would be better since these ones cannot have any
+ // real classes associated with them.
+ return ERR(NOT_IMPLEMENTED);
+ } else {
+ art::VisitClassLoaderDexFiles(
+ self,
+ class_loader,
+ [&](const art::DexFile* dex_file) {
+ dex_files_storage.push_back(dex_file);
+ return true; // Continue with other dex files.
+ });
+ dex_files = &dex_files_storage;
+ }
}
- if (!jnienv->IsInstanceOf(loader, art::WellKnownClasses::java_lang_ClassLoader)) {
- return ERR(ILLEGAL_ARGUMENT);
- } else if (!jnienv->IsInstanceOf(loader,
- art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
- JVMTI_LOG(ERROR, env) << "GetClassLoaderClassDescriptors is only implemented for "
- << "BootClassPath and dalvik.system.BaseDexClassLoader class loaders";
- // TODO Possibly return OK With no classes would be better since these ones cannot have any
- // real classes associated with them.
- return ERR(NOT_IMPLEMENTED);
- }
-
- art::ScopedObjectAccess soa(self);
- art::StackHandleScope<1> hs(self);
- art::Handle<art::mirror::ClassLoader> class_loader(
- hs.NewHandle(soa.Decode<art::mirror::ClassLoader>(loader)));
- std::vector<const art::DexFile*> dex_files;
- art::VisitClassLoaderDexFiles(
- soa,
- class_loader,
- [&](const art::DexFile* dex_file) {
- dex_files.push_back(dex_file);
- return true; // Continue with other dex files.
- });
// We hold the loader so the dex files won't go away until after this call at worst.
- return CopyClassDescriptors(env, dex_files, count_ptr, classes);
+ DCHECK(dex_files != nullptr);
+ return CopyClassDescriptors(env, *dex_files, count_ptr, classes);
}
jvmtiError ClassUtil::GetClassLoaderClasses(jvmtiEnv* env,
@@ -973,19 +975,17 @@
return ERR(NULL_POINTER);
}
art::Thread* self = art::Thread::Current();
- if (!self->GetJniEnv()->IsInstanceOf(initiating_loader,
- art::WellKnownClasses::java_lang_ClassLoader)) {
- return ERR(ILLEGAL_ARGUMENT);
- }
- if (self->GetJniEnv()->IsInstanceOf(initiating_loader,
- art::WellKnownClasses::java_lang_BootClassLoader)) {
- // Need to use null for the BootClassLoader.
- initiating_loader = nullptr;
- }
-
art::ScopedObjectAccess soa(self);
art::ObjPtr<art::mirror::ClassLoader> class_loader =
soa.Decode<art::mirror::ClassLoader>(initiating_loader);
+ if (class_loader == nullptr) {
+ // Keep null, meaning the boot class loader.
+ } else if (!class_loader->InstanceOf(art::WellKnownClasses::java_lang_ClassLoader.Get())) {
+ return ERR(ILLEGAL_ARGUMENT);
+ } else if (class_loader->InstanceOf(art::WellKnownClasses::java_lang_BootClassLoader.Get())) {
+ // Need to use null for the BootClassLoader.
+ class_loader = nullptr;
+ }
art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker();
diff --git a/openjdkjvmti/ti_class_loader-inl.h b/openjdkjvmti/ti_class_loader-inl.h
index 29ea684..f6b0126 100644
--- a/openjdkjvmti/ti_class_loader-inl.h
+++ b/openjdkjvmti/ti_class_loader-inl.h
@@ -48,8 +48,8 @@
art::Handle<art::mirror::ClassLoader> loader,
const Visitor& visitor) {
art::StackHandleScope<1> hs(self);
- art::ArtField* element_dex_file_field = art::jni::DecodeArtField(
- art::WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
+ art::ArtField* element_dex_file_field =
+ art::WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
art::Handle<art::mirror::ObjectArray<art::mirror::Object>> dex_elements_list(
hs.NewHandle(GetDexElementList(self, loader)));
diff --git a/openjdkjvmti/ti_class_loader.cc b/openjdkjvmti/ti_class_loader.cc
index d0a6634..c117937 100644
--- a/openjdkjvmti/ti_class_loader.cc
+++ b/openjdkjvmti/ti_class_loader.cc
@@ -57,16 +57,17 @@
#include "object_lock.h"
#include "runtime.h"
#include "transform.h"
+#include "well_known_classes-inl.h"
namespace openjdkjvmti {
bool ClassLoaderHelper::AddToClassLoader(art::Thread* self,
art::Handle<art::mirror::ClassLoader> loader,
const art::DexFile* dex_file) {
- art::ScopedObjectAccessUnchecked soa(self);
art::StackHandleScope<3> hs(self);
- if (art::ClassLinker::IsBootClassLoader(soa, loader.Get())) {
- art::Runtime::Current()->GetClassLinker()->AppendToBootClassPath(self, dex_file);
+ if (art::ClassLinker::IsBootClassLoader(loader.Get())) {
+ art::Runtime::Current()->AppendToBootClassPath(
+ dex_file->GetLocation(), dex_file->GetLocation(), {dex_file});
return true;
}
art::Handle<art::mirror::Object> java_dex_file_obj(
@@ -139,15 +140,14 @@
art::Handle<art::mirror::ClassLoader> loader) {
art::StackHandleScope<4> hs(self);
- art::Handle<art::mirror::Class>
- base_dex_loader_class(hs.NewHandle(self->DecodeJObject(
- art::WellKnownClasses::dalvik_system_BaseDexClassLoader)->AsClass()));
+ art::Handle<art::mirror::Class> base_dex_loader_class =
+ hs.NewHandle(art::WellKnownClasses::dalvik_system_BaseDexClassLoader.Get());
// Get all the ArtFields so we can look in the BaseDexClassLoader
- art::ArtField* path_list_field = art::jni::DecodeArtField(
- art::WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList);
+ art::ArtField* path_list_field =
+ art::WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList;
art::ArtField* dex_path_list_element_field =
- art::jni::DecodeArtField(art::WellKnownClasses::dalvik_system_DexPathList_dexElements);
+ art::WellKnownClasses::dalvik_system_DexPathList_dexElements;
// Check if loader is a BaseDexClassLoader
art::Handle<art::mirror::Class> loader_class(hs.NewHandle(loader->GetClass()));
diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc
index 10ea43a..02dc9f1 100644
--- a/openjdkjvmti/ti_extension.cc
+++ b/openjdkjvmti/ti_extension.cc
@@ -398,8 +398,7 @@
// These require index-ids and debuggable to function
art::Runtime* runtime = art::Runtime::Current();
- if (runtime->GetJniIdType() == art::JniIdType::kIndices &&
- (runtime->GetInstrumentation()->IsForcedInterpretOnly() || runtime->IsJavaDebuggable())) {
+ if (runtime->GetJniIdType() == art::JniIdType::kIndices && IsFullJvmtiAvailable()) {
// IsStructurallyModifiableClass
error = add_extension(
reinterpret_cast<jvmtiExtensionFunction>(Redefiner::IsStructurallyModifiableClass),
@@ -703,8 +702,7 @@
return error;
}
art::Runtime* runtime = art::Runtime::Current();
- if (runtime->GetJniIdType() == art::JniIdType::kIndices &&
- (runtime->GetInstrumentation()->IsForcedInterpretOnly() || runtime->IsJavaDebuggable())) {
+ if (runtime->GetJniIdType() == art::JniIdType::kIndices && IsFullJvmtiAvailable()) {
error = add_extension(
ArtJvmtiEvent::kStructuralDexFileLoadHook,
"com.android.art.class.structural_dex_file_load_hook",
diff --git a/openjdkjvmti/ti_heap.cc b/openjdkjvmti/ti_heap.cc
index 2a1d442..01864cd 100644
--- a/openjdkjvmti/ti_heap.cc
+++ b/openjdkjvmti/ti_heap.cc
@@ -1851,7 +1851,9 @@
const ObjectMap& map_;
};
ReplaceWeaksVisitor rwv(map);
- art::Runtime::Current()->SweepSystemWeaks(&rwv);
+ art::Runtime* runtime = art::Runtime::Current();
+ runtime->SweepSystemWeaks(&rwv);
+ runtime->GetThreadList()->SweepInterpreterCaches(&rwv);
// Re-add the object tags. At this point all weak-references to the old_obj_ptr are gone.
event_handler->ForEachEnv(self, [&](ArtJvmTiEnv* env) {
// Cannot have REQUIRES(art::Locks::mutator_lock_) since ForEachEnv doesn't require it.
diff --git a/openjdkjvmti/ti_redefine.cc b/openjdkjvmti/ti_redefine.cc
index 15cb6de..d40964f 100644
--- a/openjdkjvmti/ti_redefine.cc
+++ b/openjdkjvmti/ti_redefine.cc
@@ -89,7 +89,7 @@
#include "jni/jni_id_manager.h"
#include "jvmti.h"
#include "jvmti_allocator.h"
-#include "linear_alloc.h"
+#include "linear_alloc-inl.h"
#include "mirror/array-alloc-inl.h"
#include "mirror/array.h"
#include "mirror/class-alloc-inl.h"
@@ -130,7 +130,7 @@
#include "transform.h"
#include "verifier/class_verifier.h"
#include "verifier/verifier_enums.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
#include "write_barrier.h"
namespace openjdkjvmti {
@@ -285,8 +285,7 @@
art::Thread* thread,
art::LinearAlloc* allocator,
const std::unordered_set<art::ArtMethod*>& obsoleted_methods,
- ObsoleteMap* obsolete_maps)
- REQUIRES(art::Locks::mutator_lock_) {
+ ObsoleteMap* obsolete_maps) REQUIRES(art::Locks::mutator_lock_) {
ObsoleteMethodStackVisitor visitor(thread,
allocator,
obsoleted_methods,
@@ -310,7 +309,9 @@
art::ClassLinker* cl = runtime->GetClassLinker();
auto ptr_size = cl->GetImagePointerSize();
const size_t method_size = art::ArtMethod::Size(ptr_size);
- auto* method_storage = allocator_->Alloc(art::Thread::Current(), method_size);
+ auto* method_storage = allocator_->Alloc(art::Thread::Current(),
+ method_size,
+ art::LinearAllocKind::kArtMethod);
CHECK(method_storage != nullptr) << "Unable to allocate storage for obsolete version of '"
<< old_method->PrettyMethod() << "'";
new_obsolete_method = new (method_storage) art::ArtMethod();
@@ -454,8 +455,7 @@
}
// Check Thread specifically since it's not a root but too many things reach into it with Unsafe
// too allow structural redefinition.
- if (klass->IsAssignableFrom(
- self->DecodeJObject(art::WellKnownClasses::java_lang_Thread)->AsClass())) {
+ if (klass->IsAssignableFrom(art::WellKnownClasses::java_lang_Thread.Get())) {
*error_msg =
"java.lang.Thread has fields accessed using sun.misc.unsafe directly. It is not "
"safe to structurally redefine it.";
@@ -512,8 +512,15 @@
art::MemMap Redefiner::MoveDataToMemMap(const std::string& original_location,
art::ArrayRef<const unsigned char> data,
std::string* error_msg) {
+ std::string modified_location = StringPrintf("%s-transformed", original_location.c_str());
+ // A dangling multi-dex location appended to bootclasspath can cause inaccuracy in oat file
+ // validation. For simplicity, just convert it to a normal location.
+ size_t pos = modified_location.find(art::DexFileLoader::kMultiDexSeparator);
+ if (pos != std::string::npos) {
+ modified_location[pos] = '-';
+ }
art::MemMap map = art::MemMap::MapAnonymous(
- StringPrintf("%s-transformed", original_location.c_str()).c_str(),
+ modified_location.c_str(),
data.size(),
PROT_READ|PROT_WRITE,
/*low_4gb=*/ false,
@@ -545,6 +552,13 @@
if (driver_ != nullptr && lock_acquired_) {
GetMirrorClass()->MonitorExit(driver_->self_);
}
+ if (art::kIsDebugBuild) {
+ if (dex_file_ != nullptr) {
+ art::Thread* self = art::Thread::Current();
+ art::ClassLinker* cl = art::Runtime::Current()->GetClassLinker();
+ CHECK(!cl->IsDexFileRegistered(self, *dex_file_));
+ }
+ }
}
template<RedefinitionType kType>
@@ -1217,6 +1231,8 @@
actually_structural_(redefinitions_->size(), false),
initial_structural_(redefinitions_->size(), false) {}
+ ~RedefinitionDataHolder() REQUIRES_SHARED(art::Locks::mutator_lock_);
+
bool IsNull() const REQUIRES_SHARED(art::Locks::mutator_lock_) {
return arr_.IsNull();
}
@@ -1423,8 +1439,9 @@
RedefinitionDataIter(const RedefinitionDataIter&) = default;
RedefinitionDataIter(RedefinitionDataIter&&) = default;
- RedefinitionDataIter& operator=(const RedefinitionDataIter&) = default;
- RedefinitionDataIter& operator=(RedefinitionDataIter&&) = default;
+ // Assignments are deleted because holder_ is a reference.
+ RedefinitionDataIter& operator=(const RedefinitionDataIter&) = delete;
+ RedefinitionDataIter& operator=(RedefinitionDataIter&&) = delete;
bool operator==(const RedefinitionDataIter& other) const
REQUIRES_SHARED(art::Locks::mutator_lock_) {
@@ -1613,6 +1630,24 @@
return RedefinitionDataIter(Length(), *this);
}
+RedefinitionDataHolder::~RedefinitionDataHolder() {
+ art::Thread* self = art::Thread::Current();
+ art::ClassLinker* cl = art::Runtime::Current()->GetClassLinker();
+ for (RedefinitionDataIter data = begin(); data != end(); ++data) {
+ art::ObjPtr<art::mirror::DexCache> dex_cache = data.GetNewDexCache();
+ // When redefinition fails, the dex file will be deleted in the
+ // `ClassRedefinition` destructor. To avoid having a heap `DexCache` pointing
+ // to a dangling pointer, we clear the entries of those dex caches that are
+ // not registered in the runtime.
+ if (dex_cache != nullptr &&
+ dex_cache->GetDexFile() != nullptr &&
+ !cl->IsDexFileRegistered(self, *dex_cache->GetDexFile())) {
+ dex_cache->ResetNativeArrays();
+ dex_cache->SetDexFile(nullptr);
+ }
+ }
+}
+
bool Redefiner::ClassRedefinition::CheckVerification(const RedefinitionDataIter& iter) {
DCHECK_EQ(dex_file_->NumClassDefs(), 1u);
art::StackHandleScope<3> hs(driver_->self_);
@@ -1651,10 +1686,12 @@
art::MutableHandle<art::mirror::LongArray> old_cookie(
hs.NewHandle<art::mirror::LongArray>(nullptr));
bool has_older_cookie = false;
- // See if we already have a cookie that a previous redefinition got from the same classloader.
+ // See if we already have a cookie that a previous redefinition got from the same classloader
+ // and the same JavaDex file.
for (auto old_data = cur_data->GetHolder().begin(); old_data != *cur_data; ++old_data) {
- if (old_data.GetSourceClassLoader() == source_class_loader.Get()) {
- // Since every instance of this classloader should have the same cookie associated with it we
+ if (old_data.GetSourceClassLoader() == source_class_loader.Get() &&
+ old_data.GetJavaDexFile() == dex_file_obj.Get()) {
+ // Since every instance of this JavaDex file should have the same cookie associated with it we
// can stop looking here.
has_older_cookie = true;
old_cookie.Assign(old_data.GetNewDexFileCookie());
@@ -1679,12 +1716,13 @@
// Save the cookie.
cur_data->SetNewDexFileCookie(new_cookie.Get());
- // If there are other copies of this same classloader we need to make sure that we all have the
- // same cookie.
+ // If there are other copies of the same classloader and the same JavaDex file we need to
+ // make sure that we all have the same cookie.
if (has_older_cookie) {
for (auto old_data = cur_data->GetHolder().begin(); old_data != *cur_data; ++old_data) {
// We will let the GC take care of the cookie we allocated for this one.
- if (old_data.GetSourceClassLoader() == source_class_loader.Get()) {
+ if (old_data.GetSourceClassLoader() == source_class_loader.Get() &&
+ old_data.GetJavaDexFile() == dex_file_obj.Get()) {
old_data.SetNewDexFileCookie(new_cookie.Get());
}
}
@@ -1802,13 +1840,12 @@
bool Redefiner::ClassRedefinition::FinishRemainingCommonAllocations(
/*out*/RedefinitionDataIter* cur_data) {
- art::ScopedObjectAccessUnchecked soa(driver_->self_);
art::StackHandleScope<2> hs(driver_->self_);
cur_data->SetMirrorClass(GetMirrorClass());
// This shouldn't allocate
art::Handle<art::mirror::ClassLoader> loader(hs.NewHandle(GetClassLoader()));
// The bootclasspath is handled specially so it doesn't have a j.l.DexFile.
- if (!art::ClassLinker::IsBootClassLoader(soa, loader.Get())) {
+ if (!art::ClassLinker::IsBootClassLoader(loader.Get())) {
cur_data->SetSourceClassLoader(loader.Get());
art::Handle<art::mirror::Object> dex_file_obj(hs.NewHandle(
ClassLoaderHelper::FindSourceDexFileObject(driver_->self_, loader)));
@@ -2218,6 +2255,11 @@
}
void Redefiner::ClassRedefinition::ReleaseDexFile() {
+ if (art::kIsDebugBuild) {
+ art::Thread* self = art::Thread::Current();
+ art::ClassLinker* cl = art::Runtime::Current()->GetClassLinker();
+ CHECK(cl->IsDexFileRegistered(self, *dex_file_));
+ }
dex_file_.release(); // NOLINT b/117926937
}
@@ -2477,7 +2519,9 @@
art::ClassLinker* cl = runtime_->GetClassLinker();
if (data.GetSourceClassLoader() == nullptr) {
// AppendToBootClassPath includes dex file registration.
- cl->AppendToBootClassPath(&data.GetRedefinition().GetDexFile(), data.GetNewDexCache());
+ const art::DexFile& dex_file = data.GetRedefinition().GetDexFile();
+ runtime_->AppendToBootClassPath(
+ dex_file.GetLocation(), dex_file.GetLocation(), {{&dex_file, data.GetNewDexCache()}});
} else {
cl->RegisterExistingDexCache(data.GetNewDexCache(), data.GetSourceClassLoader());
}
@@ -2910,6 +2954,27 @@
// be undone. This replaces the mirror::Class in 'holder' as well. It's magic!
HeapExtensions::ReplaceReferences(driver_->self_, map);
+ // Undo the replacement of old_class with new_class for the methods / fields on the old_class.
+ // It is hard to ensure that we don't replace the declaring class of the old class field / methods
+ // isn't impacted by ReplaceReferences. It is just simpler to undo the replacement here.
+ std::for_each(
+ old_classes_vec.cbegin(),
+ old_classes_vec.cend(),
+ [](art::ObjPtr<art::mirror::Class> orig) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ orig->VisitMethods(
+ [&](art::ArtMethod* method) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ if (method->IsCopied()) {
+ // Copied methods have interfaces as their declaring class.
+ return;
+ }
+ method->SetDeclaringClass(orig);
+ },
+ art::kRuntimePointerSize);
+ orig->VisitFields([&](art::ArtField* field) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ field->SetDeclaringClass(orig);
+ });
+ });
+
// Save the old class so that the JIT gc doesn't get confused by it being collected before the
// jit code. This is also needed to keep the dex-caches of any obsolete methods live.
for (auto [new_class, old_class] :
@@ -3081,10 +3146,14 @@
// First save the old values of the 2 arrays that make up the obsolete methods maps. Then
// allocate the 2 arrays that make up the obsolete methods map. Since the contents of the arrays
// are only modified when all threads (other than the modifying one) are suspended we don't need
- // to worry about missing the unsyncronized writes to the array. We do synchronize when setting
+ // to worry about missing the unsynchronized writes to the array. We do synchronize when setting
// it however, since that can happen at any time.
cur_data->SetOldObsoleteMethods(ext->GetObsoleteMethods());
cur_data->SetOldDexCaches(ext->GetObsoleteDexCaches());
+ // FIXME: The `ClassExt::ExtendObsoleteArrays()` is non-atomic and does not ensure proper
+ // memory visibility, so it can race with `ArtMethod::GetObsoleteDexCache()`.
+ // We should allocate the new arrays here but record it in the redefinition data and set the
+ // new arrays in `ClassExt` later with all other threads suspended.
if (!art::mirror::ClassExt::ExtendObsoleteArrays(
ext, driver_->self_, klass->GetDeclaredMethodsSlice(art::kRuntimePointerSize).size())) {
// OOM. Clear exception and return error.
diff --git a/openjdkjvmti/ti_redefine.h b/openjdkjvmti/ti_redefine.h
index 85b1070..cdd6627 100644
--- a/openjdkjvmti/ti_redefine.h
+++ b/openjdkjvmti/ti_redefine.h
@@ -126,7 +126,7 @@
~ClassRedefinition() NO_THREAD_SAFETY_ANALYSIS;
// Move assignment so we can sort these in a vector.
- ClassRedefinition& operator=(ClassRedefinition&& other) {
+ ClassRedefinition& operator=(ClassRedefinition&& other) noexcept {
driver_ = other.driver_;
klass_ = other.klass_;
dex_file_ = std::move(other.dex_file_);
@@ -138,7 +138,7 @@
}
// Move constructor so we can put these into a vector.
- ClassRedefinition(ClassRedefinition&& other)
+ ClassRedefinition(ClassRedefinition&& other) noexcept
: driver_(other.driver_),
klass_(other.klass_),
dex_file_(std::move(other.dex_file_)),
diff --git a/openjdkjvmti/ti_search.cc b/openjdkjvmti/ti_search.cc
index 526836e..4d96732 100644
--- a/openjdkjvmti/ti_search.cc
+++ b/openjdkjvmti/ti_search.cc
@@ -60,7 +60,7 @@
#include "thread_list.h"
#include "ti_logging.h"
#include "ti_phase.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace openjdkjvmti {
@@ -247,12 +247,8 @@
return ERR(ILLEGAL_ARGUMENT);
}
- art::ScopedObjectAccess soa(art::Thread::Current());
- for (std::unique_ptr<const art::DexFile>& dex_file : dex_files) {
- current->GetClassLinker()->AppendToBootClassPath(art::Thread::Current(), dex_file.release());
- }
-
- return ERR(NONE);
+ current->AddExtraBootDexFiles(segment, segment, std::move(dex_files));
+ return OK;
}
jvmtiError SearchUtil::AddToDexClassLoaderInMemory(jvmtiEnv* jvmti_env,
@@ -343,33 +339,33 @@
// exceptions are swallowed.
art::Thread* self = art::Thread::Current();
- JNIEnv* env = self->GetJniEnv();
- if (!env->IsInstanceOf(classloader, art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
+ art::ScopedObjectAccess soa(self);
+ art::StackHandleScope<2u> hs(self);
+ art::Handle<art::mirror::ClassLoader> class_loader =
+ hs.NewHandle(soa.Decode<art::mirror::ClassLoader>(classloader));
+ if (!class_loader->InstanceOf(art::WellKnownClasses::dalvik_system_BaseDexClassLoader.Get())) {
JVMTI_LOG(ERROR, jvmti_env) << "Unable to add " << segment << " to non BaseDexClassLoader!";
return ERR(CLASS_LOADER_UNSUPPORTED);
}
- jmethodID add_dex_path_id = env->GetMethodID(
- art::WellKnownClasses::dalvik_system_BaseDexClassLoader,
- "addDexPath",
- "(Ljava/lang/String;)V");
+ art::ArtMethod* add_dex_path_id =
+ art::WellKnownClasses::dalvik_system_BaseDexClassLoader->FindClassMethod(
+ "addDexPath", "(Ljava/lang/String;)V", art::kRuntimePointerSize);
if (add_dex_path_id == nullptr) {
return ERR(INTERNAL);
}
- ScopedLocalRef<jstring> dex_path(env, env->NewStringUTF(segment));
- if (dex_path.get() == nullptr) {
+ art::Handle<art::mirror::String> dex_path =
+ hs.NewHandle(art::mirror::String::AllocFromModifiedUtf8(self, segment));
+ if (dex_path == nullptr) {
return ERR(INTERNAL);
}
- env->CallVoidMethod(classloader, add_dex_path_id, dex_path.get());
- if (env->ExceptionCheck()) {
- {
- art::ScopedObjectAccess soa(self);
- JVMTI_LOG(ERROR, jvmti_env) << "Failed to add " << segment << " to classloader. Error was "
- << self->GetException()->Dump();
- }
- env->ExceptionClear();
+ add_dex_path_id->InvokeVirtual<'V', 'L'>(self, class_loader.Get(), dex_path.Get());
+ if (self->IsExceptionPending()) {
+ JVMTI_LOG(ERROR, jvmti_env) << "Failed to add " << segment << " to classloader. Error was "
+ << self->GetException()->Dump();
+ self->ClearException();
return ERR(ILLEGAL_ARGUMENT);
}
return OK;
@@ -396,10 +392,13 @@
return ERR(INTERNAL);
}
- art::Thread* self = art::Thread::Current();
- JNIEnv* env = self->GetJniEnv();
- if (!env->IsInstanceOf(loader, art::WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
- return ERR(INTERNAL);
+ {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ObjPtr<art::mirror::ClassLoader> class_loader =
+ soa.Decode<art::mirror::ClassLoader>(loader);
+ if (!class_loader->InstanceOf(art::WellKnownClasses::dalvik_system_BaseDexClassLoader.Get())) {
+ return ERR(INTERNAL);
+ }
}
return AddToDexClassLoader(jvmti_env, loader, segment);
diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc
index 38257f1..c3aab4c 100644
--- a/openjdkjvmti/ti_stack.cc
+++ b/openjdkjvmti/ti_stack.cc
@@ -82,7 +82,7 @@
#include "thread_list.h"
#include "thread_pool.h"
#include "ti_thread.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace openjdkjvmti {
@@ -578,10 +578,11 @@
if (thread_list[i] == nullptr) {
return ERR(INVALID_THREAD);
}
- if (!soa.Env()->IsInstanceOf(thread_list[i], art::WellKnownClasses::java_lang_Thread)) {
+ art::ObjPtr<art::mirror::Object> thread = soa.Decode<art::mirror::Object>(thread_list[i]);
+ if (!thread->InstanceOf(art::WellKnownClasses::java_lang_Thread.Get())) {
return ERR(INVALID_THREAD);
}
- data.handles.push_back(hs.NewHandle(soa.Decode<art::mirror::Object>(thread_list[i])));
+ data.handles.push_back(hs.NewHandle(thread));
}
RunCheckpointAndWait(&data, static_cast<size_t>(max_frame_count));
diff --git a/openjdkjvmti/ti_thread.cc b/openjdkjvmti/ti_thread.cc
index f31759e..b5bc35e 100644
--- a/openjdkjvmti/ti_thread.cc
+++ b/openjdkjvmti/ti_thread.cc
@@ -59,7 +59,7 @@
#include "thread-current-inl.h"
#include "thread_list.h"
#include "ti_phase.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace openjdkjvmti {
@@ -131,6 +131,7 @@
if (name != "JDWP" && name != "Signal Catcher" && name != "perfetto_hprof_listener" &&
name != art::metrics::MetricsReporter::kBackgroundThreadName &&
!android::base::StartsWith(name, "Jit thread pool") &&
+ !android::base::StartsWith(name, "Heap thread pool worker thread") &&
!android::base::StartsWith(name, "Runtime worker thread")) {
LOG(FATAL) << "Unexpected thread before start: " << name << " id: "
<< self->GetThreadId();
@@ -173,12 +174,7 @@
static void WaitForSystemDaemonStart(art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_) {
- {
- art::ScopedThreadStateChange strc(self, art::ThreadState::kNative);
- JNIEnv* jni = self->GetJniEnv();
- jni->CallStaticVoidMethod(art::WellKnownClasses::java_lang_Daemons,
- art::WellKnownClasses::java_lang_Daemons_waitForDaemonStart);
- }
+ art::WellKnownClasses::java_lang_Daemons_waitForDaemonStart->InvokeStatic<'V'>(self);
if (self->IsExceptionPending()) {
LOG(WARNING) << "Exception occurred when waiting for system daemons to start: "
<< self->GetException()->Dump();
@@ -191,8 +187,7 @@
gThreadCallback.started = true;
art::Thread* self = art::Thread::Current();
art::ScopedObjectAccess soa(self);
- art::ObjPtr<art::mirror::Class> thread_class =
- soa.Decode<art::mirror::Class>(art::WellKnownClasses::java_lang_Thread);
+ art::ObjPtr<art::mirror::Class> thread_class = art::WellKnownClasses::java_lang_Thread.Get();
CHECK(thread_class != nullptr);
context_class_loader_ = thread_class->FindDeclaredInstanceField("contextClassLoader",
"Ljava/lang/ClassLoader;");
@@ -235,7 +230,9 @@
if (thread == nullptr) {
*thr = art::Thread::Current();
return true;
- } else if (!soa.Env()->IsInstanceOf(thread, art::WellKnownClasses::java_lang_Thread)) {
+ }
+ art::ObjPtr<art::mirror::Object> othread = soa.Decode<art::mirror::Object>(thread);
+ if (!othread->InstanceOf(art::WellKnownClasses::java_lang_Thread.Get())) {
*err = ERR(INVALID_THREAD);
return false;
} else {
@@ -296,7 +293,7 @@
// ThreadGroup.
if (peer != nullptr) {
- art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_group);
+ art::ArtField* f = art::WellKnownClasses::java_lang_Thread_group;
CHECK(f != nullptr);
art::ObjPtr<art::mirror::Object> group = f->GetObject(peer);
info_ptr->thread_group = group == nullptr
@@ -321,7 +318,7 @@
// Name.
{
- art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_name);
+ art::ArtField* f = art::WellKnownClasses::java_lang_Thread_name;
CHECK(f != nullptr);
art::ObjPtr<art::mirror::Object> name = f->GetObject(peer);
std::string name_cpp;
@@ -342,21 +339,21 @@
// Priority.
{
- art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_priority);
+ art::ArtField* f = art::WellKnownClasses::java_lang_Thread_priority;
CHECK(f != nullptr);
info_ptr->priority = static_cast<jint>(f->GetInt(peer));
}
// Daemon.
{
- art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_daemon);
+ art::ArtField* f = art::WellKnownClasses::java_lang_Thread_daemon;
CHECK(f != nullptr);
info_ptr->is_daemon = f->GetBoolean(peer) == 0 ? JNI_FALSE : JNI_TRUE;
}
// ThreadGroup.
{
- art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_group);
+ art::ArtField* f = art::WellKnownClasses::java_lang_Thread_group;
CHECK(f != nullptr);
art::ObjPtr<art::mirror::Object> group = f->GetObject(peer);
info_ptr->thread_group = group == nullptr
@@ -616,8 +613,7 @@
// Need to read the Java "started" field to know whether this is starting or terminated.
art::Handle<art::mirror::Object> peer(hs.NewHandle(soa.Decode<art::mirror::Object>(thread)));
- art::ObjPtr<art::mirror::Class> thread_klass =
- soa.Decode<art::mirror::Class>(art::WellKnownClasses::java_lang_Thread);
+ art::ObjPtr<art::mirror::Class> thread_klass = art::WellKnownClasses::java_lang_Thread.Get();
if (!thread_klass->IsAssignableFrom(peer->GetClass())) {
return ERR(INVALID_THREAD);
}
@@ -814,46 +810,52 @@
if (priority < JVMTI_THREAD_MIN_PRIORITY || priority > JVMTI_THREAD_MAX_PRIORITY) {
return ERR(INVALID_PRIORITY);
}
- JNIEnv* env = art::Thread::Current()->GetJniEnv();
- if (thread == nullptr || !env->IsInstanceOf(thread, art::WellKnownClasses::java_lang_Thread)) {
+ if (thread == nullptr) {
return ERR(INVALID_THREAD);
}
- if (proc == nullptr) {
- return ERR(NULL_POINTER);
- }
-
+ art::Runtime* runtime = art::Runtime::Current();
+ art::Thread* self = art::Thread::Current();
+ std::unique_ptr<AgentData> data;
{
- art::Runtime* runtime = art::Runtime::Current();
- art::MutexLock mu(art::Thread::Current(), *art::Locks::runtime_shutdown_lock_);
- if (runtime->IsShuttingDownLocked()) {
- // The runtime is shutting down so we cannot create new threads.
- // TODO It's not fully clear from the spec what we should do here. We aren't yet in
- // JVMTI_PHASE_DEAD so we cannot return ERR(WRONG_PHASE) but creating new threads is now
- // impossible. Existing agents don't seem to generally do anything with this return value so
- // it doesn't matter too much. We could do something like sending a fake ThreadStart event
- // even though code is never actually run.
- return ERR(INTERNAL);
+ art::ScopedObjectAccess soa(self);
+ art::ObjPtr<art::mirror::Object> othread = soa.Decode<art::mirror::Object>(thread);
+ if (!othread->InstanceOf(art::WellKnownClasses::java_lang_Thread.Get())) {
+ return ERR(INVALID_THREAD);
}
- runtime->StartThreadBirth();
- }
+ if (proc == nullptr) {
+ return ERR(NULL_POINTER);
+ }
- std::unique_ptr<AgentData> data(new AgentData);
- data->arg = arg;
- data->proc = proc;
- // We need a global ref for Java objects, as local refs will be invalid.
- data->thread = env->NewGlobalRef(thread);
- data->java_vm = art::Runtime::Current()->GetJavaVM();
- data->jvmti_env = jvmti_env;
- data->priority = priority;
- ScopedLocalRef<jstring> s(
- env,
- reinterpret_cast<jstring>(
- env->GetObjectField(thread, art::WellKnownClasses::java_lang_Thread_name)));
- if (s == nullptr) {
- data->name = "JVMTI Agent Thread";
- } else {
- ScopedUtfChars name(env, s.get());
- data->name = name.c_str();
+ {
+ art::MutexLock mu(soa.Self(), *art::Locks::runtime_shutdown_lock_);
+ if (runtime->IsShuttingDownLocked()) {
+ // The runtime is shutting down so we cannot create new threads.
+ // TODO It's not fully clear from the spec what we should do here. We aren't yet in
+ // JVMTI_PHASE_DEAD so we cannot return ERR(WRONG_PHASE) but creating new threads is now
+ // impossible. Existing agents don't seem to generally do anything with this return value so
+ // it doesn't matter too much. We could do something like sending a fake ThreadStart event
+ // even though code is never actually run.
+ return ERR(INTERNAL);
+ }
+ runtime->StartThreadBirth();
+ }
+
+ data.reset(new AgentData);
+ data->arg = arg;
+ data->proc = proc;
+ // We need a global ref for Java objects, as local refs will be invalid.
+ data->thread = runtime->GetJavaVM()->AddGlobalRef(soa.Self(), othread);
+ data->java_vm = runtime->GetJavaVM();
+ data->jvmti_env = jvmti_env;
+ data->priority = priority;
+ art::ObjPtr<art::mirror::Object> name =
+ art::WellKnownClasses::java_lang_Thread_name->GetObject(
+ soa.Decode<art::mirror::Object>(thread));
+ if (name == nullptr) {
+ data->name = "JVMTI Agent Thread";
+ } else {
+ data->name = name->AsString()->ToModifiedUtf8();
+ }
}
pthread_t pthread;
@@ -863,8 +865,7 @@
reinterpret_cast<void*>(data.get()));
if (pthread_create_result != 0) {
// If the create succeeded the other thread will call EndThreadBirth.
- art::Runtime* runtime = art::Runtime::Current();
- art::MutexLock mu(art::Thread::Current(), *art::Locks::runtime_shutdown_lock_);
+ art::MutexLock mu(self, *art::Locks::runtime_shutdown_lock_);
runtime->EndThreadBirth();
return ERR(INTERNAL);
}
diff --git a/openjdkjvmti/ti_threadgroup.cc b/openjdkjvmti/ti_threadgroup.cc
index bc912cf..120024e 100644
--- a/openjdkjvmti/ti_threadgroup.cc
+++ b/openjdkjvmti/ti_threadgroup.cc
@@ -47,7 +47,7 @@
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
#include "thread_list.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace openjdkjvmti {
@@ -95,22 +95,21 @@
}
art::ScopedObjectAccess soa(art::Thread::Current());
- if (soa.Env()->IsInstanceOf(group, art::WellKnownClasses::java_lang_ThreadGroup) == JNI_FALSE) {
+ art::StackHandleScope<2> hs(soa.Self());
+ art::Handle<art::mirror::Class> tg_class =
+ hs.NewHandle(art::WellKnownClasses::java_lang_ThreadGroup.Get());
+ art::Handle<art::mirror::Object> thread_group =
+ hs.NewHandle(soa.Decode<art::mirror::Object>(group));
+ if (!thread_group->InstanceOf(tg_class.Get())) {
return ERR(INVALID_THREAD_GROUP);
}
- art::StackHandleScope<2> hs(soa.Self());
- art::Handle<art::mirror::Class> tg_class(
- hs.NewHandle(soa.Decode<art::mirror::Class>(art::WellKnownClasses::java_lang_ThreadGroup)));
- art::Handle<art::mirror::Object> obj(hs.NewHandle(soa.Decode<art::mirror::Object>(group)));
-
// Do the name first. It's the only thing that can fail.
{
- art::ArtField* name_field =
- art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_name);
+ art::ArtField* name_field = art::WellKnownClasses::java_lang_ThreadGroup_name;
CHECK(name_field != nullptr);
art::ObjPtr<art::mirror::String> name_obj =
- art::ObjPtr<art::mirror::String>::DownCast(name_field->GetObject(obj.Get()));
+ art::ObjPtr<art::mirror::String>::DownCast(name_field->GetObject(thread_group.Get()));
std::string tmp_str;
const char* tmp_cstr;
if (name_obj == nullptr) {
@@ -129,10 +128,9 @@
// Parent.
{
- art::ArtField* parent_field =
- art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_parent);
+ art::ArtField* parent_field = art::WellKnownClasses::java_lang_ThreadGroup_parent;
CHECK(parent_field != nullptr);
- art::ObjPtr<art::mirror::Object> parent_group = parent_field->GetObject(obj.Get());
+ art::ObjPtr<art::mirror::Object> parent_group = parent_field->GetObject(thread_group.Get());
info_ptr->parent = parent_group == nullptr
? nullptr
: soa.AddLocalReference<jthreadGroup>(parent_group);
@@ -142,14 +140,14 @@
{
art::ArtField* prio_field = tg_class->FindDeclaredInstanceField("maxPriority", "I");
CHECK(prio_field != nullptr);
- info_ptr->max_priority = static_cast<jint>(prio_field->GetInt(obj.Get()));
+ info_ptr->max_priority = static_cast<jint>(prio_field->GetInt(thread_group.Get()));
}
// Daemon.
{
art::ArtField* daemon_field = tg_class->FindDeclaredInstanceField("daemon", "Z");
CHECK(daemon_field != nullptr);
- info_ptr->is_daemon = daemon_field->GetBoolean(obj.Get()) == 0 ? JNI_FALSE : JNI_TRUE;
+ info_ptr->is_daemon = daemon_field->GetBoolean(thread_group.Get()) == 0 ? JNI_FALSE : JNI_TRUE;
}
return ERR(NONE);
@@ -161,8 +159,7 @@
REQUIRES_SHARED(art::Locks::mutator_lock_) {
CHECK(desired_thread_group != nullptr);
- art::ArtField* thread_group_field =
- art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_group);
+ art::ArtField* thread_group_field = art::WellKnownClasses::java_lang_Thread_group;
DCHECK(thread_group_field != nullptr);
art::ObjPtr<art::mirror::Object> group = thread_group_field->GetObject(peer);
return (group == desired_thread_group.Get());
@@ -194,8 +191,7 @@
CHECK(thread_group != nullptr);
// Get the ThreadGroup[] "groups" out of this thread group...
- art::ArtField* groups_field =
- art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_groups);
+ art::ArtField* groups_field = art::WellKnownClasses::java_lang_ThreadGroup_groups;
art::ObjPtr<art::mirror::Object> groups_array = groups_field->GetObject(thread_group.Get());
if (groups_array == nullptr) {
@@ -225,14 +221,13 @@
}
art::ScopedObjectAccess soa(art::Thread::Current());
-
- if (!soa.Env()->IsInstanceOf(group, art::WellKnownClasses::java_lang_ThreadGroup)) {
+ art::StackHandleScope<1> hs(soa.Self());
+ art::Handle<art::mirror::Object> thread_group =
+ hs.NewHandle(soa.Decode<art::mirror::Object>(group));
+ if (!thread_group->InstanceOf(art::WellKnownClasses::java_lang_ThreadGroup.Get())) {
return ERR(INVALID_THREAD_GROUP);
}
- art::StackHandleScope<1> hs(soa.Self());
- art::Handle<art::mirror::Object> thread_group = hs.NewHandle(
- soa.Decode<art::mirror::Object>(group));
art::ObjectLock<art::mirror::Object> thread_group_lock(soa.Self(), thread_group);
diff --git a/perfetto_hprof/Android.bp b/perfetto_hprof/Android.bp
index a81a4fa..f5ae042 100644
--- a/perfetto_hprof/Android.bp
+++ b/perfetto_hprof/Android.bp
@@ -50,6 +50,7 @@
compile_multilib: "both",
shared_libs: [
+ "libartpalette",
"libbase",
"liblog",
],
@@ -68,6 +69,12 @@
header_libs: [
"libnativehelper_header_only",
],
+ // FIXME: Workaround LTO build breakage
+ // http://b/241700157
+ lto: {
+ never: true,
+ },
+
}
art_cc_library {
diff --git a/perfetto_hprof/perfetto_hprof.cc b/perfetto_hprof/perfetto_hprof.cc
index 669fb0c..b13e5e1 100644
--- a/perfetto_hprof/perfetto_hprof.cc
+++ b/perfetto_hprof/perfetto_hprof.cc
@@ -18,8 +18,6 @@
#include "perfetto_hprof.h"
-#include <android-base/logging.h>
-#include <base/fast_exit.h>
#include <fcntl.h>
#include <inttypes.h>
#include <sched.h>
@@ -36,6 +34,10 @@
#include <optional>
#include <type_traits>
+#include "android-base/logging.h"
+#include "android-base/properties.h"
+#include "base/fast_exit.h"
+#include "base/systrace.h"
#include "gc/heap-visit-objects-inl.h"
#include "gc/heap.h"
#include "gc/scoped_gc_critical_section.h"
@@ -86,6 +88,7 @@
static int requested_tracing_session_id = 0;
static State g_state = State::kUninitialized;
+static bool g_oome_triggered = false;
// Pipe to signal from the signal handler into a worker thread that handles the
// dump requests.
@@ -151,19 +154,32 @@
return false;
}
+uint64_t GetCurrentBootClockNs() {
+ struct timespec ts = {};
+ if (clock_gettime(CLOCK_BOOTTIME, &ts) != 0) {
+ LOG(FATAL) << "Failed to get boottime.";
+ }
+ return ts.tv_sec * 1000000000LL + ts.tv_nsec;
+}
+
class JavaHprofDataSource : public perfetto::DataSource<JavaHprofDataSource> {
public:
constexpr static perfetto::BufferExhaustedPolicy kBufferExhaustedPolicy =
perfetto::BufferExhaustedPolicy::kStall;
+
+ explicit JavaHprofDataSource(bool verify_session_id) : verify_session_id_(verify_session_id) {}
+
void OnSetup(const SetupArgs& args) override {
- uint64_t normalized_cfg_tracing_session_id =
- args.config->tracing_session_id() % std::numeric_limits<int32_t>::max();
- if (requested_tracing_session_id < 0) {
- LOG(ERROR) << "invalid requested tracing session id " << requested_tracing_session_id;
- return;
- }
- if (static_cast<uint64_t>(requested_tracing_session_id) != normalized_cfg_tracing_session_id) {
- return;
+ if (verify_session_id_) {
+ uint64_t normalized_tracing_session_id =
+ args.config->tracing_session_id() % std::numeric_limits<int32_t>::max();
+ if (requested_tracing_session_id < 0) {
+ LOG(ERROR) << "invalid requested tracing session id " << requested_tracing_session_id;
+ return;
+ }
+ if (static_cast<uint64_t>(requested_tracing_session_id) != normalized_tracing_session_id) {
+ return;
+ }
}
// This is on the heap as it triggers -Wframe-larger-than.
@@ -232,10 +248,10 @@
}
private:
+ bool verify_session_id_ = false;
bool enabled_ = false;
bool dump_smaps_ = false;
std::vector<std::string> ignored_types_;
- static art::Thread* self_;
art::Mutex finish_mutex_{"perfetto_hprof_ds_mutex", art::LockLevel::kGenericBottomLock};
bool is_finished_ = false;
@@ -243,27 +259,40 @@
std::function<void()> async_stop_;
};
-art::Thread* JavaHprofDataSource::self_ = nullptr;
-
-
-void WaitForDataSource(art::Thread* self) {
+void SetupDataSource(const std::string& ds_name, bool verify_session_id) {
perfetto::TracingInitArgs args;
args.backends = perfetto::BackendType::kSystemBackend;
perfetto::Tracing::Initialize(args);
perfetto::DataSourceDescriptor dsd;
- dsd.set_name("android.java_hprof");
+ dsd.set_name(ds_name);
dsd.set_will_notify_on_stop(true);
- JavaHprofDataSource::Register(dsd);
+ JavaHprofDataSource::Register(dsd, verify_session_id);
+ LOG(INFO) << "registered data source " << ds_name;
+}
- LOG(INFO) << "waiting for data source";
-
+// Waits for the data source OnStart
+void WaitForDataSource(art::Thread* self) {
art::MutexLock lk(self, GetStateMutex());
while (g_state != State::kStart) {
GetStateCV().Wait(self);
}
}
+// Waits for the data source OnStart with a timeout. Returns false on timeout.
+bool TimedWaitForDataSource(art::Thread* self, int64_t timeout_ms) {
+ const uint64_t cutoff_ns = GetCurrentBootClockNs() + timeout_ms * 1000000;
+ art::MutexLock lk(self, GetStateMutex());
+ while (g_state != State::kStart) {
+ const uint64_t current_ns = GetCurrentBootClockNs();
+ if (current_ns >= cutoff_ns) {
+ return false;
+ }
+ GetStateCV().TimedWait(self, (cutoff_ns - current_ns) / 1000000, 0);
+ }
+ return true;
+}
+
// Helper class to write Java heap dumps to `ctx`. The whole heap dump can be
// split into more perfetto.protos.HeapGraph messages, to avoid making each
// message too big.
@@ -831,10 +860,45 @@
uint64_t prev_object_id_ = 0;
};
-void DumpPerfetto(art::Thread* self) {
+// waitpid with a timeout implemented by ~busy-waiting
+// See b/181031512 for rationale.
+void BusyWaitpid(pid_t pid, uint32_t timeout_ms) {
+ for (size_t i = 0;; ++i) {
+ if (i == timeout_ms) {
+ // The child hasn't exited.
+ // Give up and SIGKILL it. The next waitpid should succeed.
+ LOG(ERROR) << "perfetto_hprof child timed out. Sending SIGKILL.";
+ kill(pid, SIGKILL);
+ }
+ int stat_loc;
+ pid_t wait_result = waitpid(pid, &stat_loc, WNOHANG);
+ if (wait_result == -1 && errno != EINTR) {
+ if (errno != ECHILD) {
+ // This hopefully never happens (should only be EINVAL).
+ PLOG(FATAL_WITHOUT_ABORT) << "waitpid";
+ }
+ // If we get ECHILD, the parent process was handling SIGCHLD, or did a wildcard wait.
+ // The child is no longer here either way, so that's good enough for us.
+ break;
+ } else if (wait_result > 0) {
+ break;
+ } else { // wait_result == 0 || errno == EINTR.
+ usleep(1000);
+ }
+ }
+}
+
+enum class ResumeParentPolicy {
+ IMMEDIATELY,
+ DEFERRED
+};
+
+void ForkAndRun(art::Thread* self,
+ ResumeParentPolicy resume_parent_policy,
+ const std::function<void(pid_t child)>& parent_runnable,
+ const std::function<void(pid_t parent, uint64_t timestamp)>& child_runnable) {
pid_t parent_pid = getpid();
LOG(INFO) << "preparing to dump heap for " << parent_pid;
-
// Need to take a heap dump while GC isn't running. See the comment in
// Heap::VisitObjects(). Also we need the critical section to avoid visiting
// the same object twice. See b/34967844.
@@ -859,41 +923,20 @@
}
if (pid != 0) {
// Parent
- // Stop the thread suspension as soon as possible to allow the rest of the application to
- // continue while we waitpid here.
- ssa.reset();
- gcs.reset();
- for (size_t i = 0;; ++i) {
- if (i == 1000) {
- // The child hasn't exited for 1 second (and all it was supposed to do was fork itself).
- // Give up and SIGKILL it. The next waitpid should succeed.
- LOG(ERROR) << "perfetto_hprof child timed out. Sending SIGKILL.";
- kill(pid, SIGKILL);
- }
- // Busy waiting here will introduce some extra latency, but that is okay because we have
- // already unsuspended all other threads. This runs on the perfetto_hprof_listener, which
- // is not needed for progress of the app itself.
- int stat_loc;
- pid_t wait_result = waitpid(pid, &stat_loc, WNOHANG);
- if (wait_result == -1 && errno != EINTR) {
- if (errno != ECHILD) {
- // This hopefully never happens (should only be EINVAL).
- PLOG(FATAL_WITHOUT_ABORT) << "waitpid";
- }
- // If we get ECHILD, the parent process was handling SIGCHLD, or did a wildcard wait.
- // The child is no longer here either way, so that's good enough for us.
- break;
- } else if (wait_result > 0) {
- break;
- } else { // wait_result == 0 || errno == EINTR.
- usleep(1000);
- }
+ if (resume_parent_policy == ResumeParentPolicy::IMMEDIATELY) {
+ // Stop the thread suspension as soon as possible to allow the rest of the application to
+ // continue while we waitpid here.
+ ssa.reset();
+ gcs.reset();
+ }
+ parent_runnable(pid);
+ if (resume_parent_policy != ResumeParentPolicy::IMMEDIATELY) {
+ ssa.reset();
+ gcs.reset();
}
return;
}
-
// The following code is only executed by the child of the original process.
-
// Uninstall signal handler, so we don't trigger a profile on it.
if (sigaction(kJavaHeapprofdSignal, &g_orig_act, nullptr) != 0) {
close(g_signal_pipe_fds[0]);
@@ -902,25 +945,14 @@
return;
}
- // Daemon creates a new process that is the grand-child of the original process, and exits.
- if (daemon(0, 0) == -1) {
- PLOG(FATAL) << "daemon";
- }
+ uint64_t ts = GetCurrentBootClockNs();
+ child_runnable(parent_pid, ts);
+ // Prevent the `atexit` handlers from running. We do not want to call cleanup
+ // functions the parent process has registered.
+ art::FastExit(0);
+}
- // The following code is only executed by the grand-child of the original process.
-
- // Make sure that this is the first thing we do after forking, so if anything
- // below hangs, the fork will go away from the watchdog.
- ArmWatchdogOrDie();
-
- struct timespec ts = {};
- if (clock_gettime(CLOCK_BOOTTIME, &ts) != 0) {
- LOG(FATAL) << "Failed to get boottime.";
- }
- uint64_t timestamp = ts.tv_sec * 1000000000LL + ts.tv_nsec;
-
- WaitForDataSource(self);
-
+void WriteHeapPackets(pid_t parent_pid, uint64_t timestamp) {
JavaHprofDataSource::Trace(
[parent_pid, timestamp](JavaHprofDataSource::TraceContext ctx)
NO_THREAD_SAFETY_ANALYSIS {
@@ -968,11 +1000,96 @@
}
}
});
+}
- LOG(INFO) << "finished dumping heap for " << parent_pid;
- // Prevent the `atexit` handlers from running. We do not want to call cleanup
- // functions the parent process has registered.
- art::FastExit(0);
+void DumpPerfetto(art::Thread* self) {
+ ForkAndRun(
+ self,
+ ResumeParentPolicy::IMMEDIATELY,
+ // parent thread
+ [](pid_t child) {
+ // Busy waiting here will introduce some extra latency, but that is okay because we have
+ // already unsuspended all other threads. This runs on the perfetto_hprof_listener, which
+ // is not needed for progress of the app itself.
+ // We daemonize the child process, so effectively we only need to wait
+ // for it to fork and exit.
+ BusyWaitpid(child, 1000);
+ },
+ // child thread
+ [self](pid_t dumped_pid, uint64_t timestamp) {
+ // Daemon creates a new process that is the grand-child of the original process, and exits.
+ if (daemon(0, 0) == -1) {
+ PLOG(FATAL) << "daemon";
+ }
+ // The following code is only executed by the grand-child of the original process.
+
+ // Make sure that this is the first thing we do after forking, so if anything
+ // below hangs, the fork will go away from the watchdog.
+ ArmWatchdogOrDie();
+ SetupDataSource("android.java_hprof", true);
+ WaitForDataSource(self);
+ WriteHeapPackets(dumped_pid, timestamp);
+ LOG(INFO) << "finished dumping heap for " << dumped_pid;
+ });
+}
+
+void DumpPerfettoOutOfMemory() {
+ art::Thread* self = art::Thread::Current();
+ if (!self) {
+ LOG(FATAL_WITHOUT_ABORT) << "no thread in DumpPerfettoOutOfMemory";
+ return;
+ }
+ {
+ // OutOfMemoryErrors are reentrant, make sure we do not fork and process
+ // more than once.
+ art::MutexLock lk(self, GetStateMutex());
+ if (g_oome_triggered) {
+ return;
+ }
+ g_oome_triggered = true;
+ }
+ // If we fork & resume the original process execution it will most likely exit
+ // ~immediately due to the OOME error thrown. When the system detects that
+ // that, it will cleanup by killing all processes in the cgroup (including
+ // the process we just forked).
+ // We need to avoid the race between the heap dump and the process group
+ // cleanup, and the only way to do this is to avoid resuming the original
+ // process until the heap dump is complete.
+ // Given we are already about to crash anyway, the diagnostic data we get
+ // outweighs the cost of introducing some latency.
+ ForkAndRun(
+ self,
+ ResumeParentPolicy::DEFERRED,
+ // parent process
+ [](pid_t child) {
+ // waitpid to reap the zombie
+ // we are explicitly waiting for the child to exit
+ // The reason for the timeout on top of the watchdog is that it is
+ // possible (albeit unlikely) that even the watchdog will fail to be
+ // activated in the case of an atfork handler.
+ BusyWaitpid(child, kWatchdogTimeoutSec * 1000);
+ },
+ // child process
+ [self](pid_t dumped_pid, uint64_t timestamp) {
+ ArmWatchdogOrDie();
+ art::ScopedTrace trace("perfetto_hprof oome");
+ SetupDataSource("android.java_hprof.oom", false);
+ perfetto::Tracing::ActivateTriggers({"com.android.telemetry.art-outofmemory"}, 500);
+
+ // A pre-armed tracing session might not exist, so we should wait for a
+ // limited amount of time before we decide to let the execution continue.
+ if (!TimedWaitForDataSource(self, 500)) {
+ LOG(INFO) << "timeout waiting for data source start (no active session?)";
+ return;
+ }
+ WriteHeapPackets(dumped_pid, timestamp);
+ LOG(INFO) << "finished dumping heap for OOME " << dumped_pid;
+ });
+}
+
+bool CanProfile() {
+ std::string build_type = android::base::GetProperty("ro.build.type", "");
+ return !build_type.empty() && build_type != "user";
}
// The plugin initialization function.
@@ -1062,10 +1179,19 @@
});
th.detach();
+ // Register the OOM error handler.
+ if (CanProfile()) {
+ art::Runtime::Current()->SetOutOfMemoryErrorHook(perfetto_hprof::DumpPerfettoOutOfMemory);
+ }
+
return true;
}
extern "C" bool ArtPlugin_Deinitialize() {
+ if (CanProfile()) {
+ art::Runtime::Current()->SetOutOfMemoryErrorHook(nullptr);
+ }
+
if (sigaction(kJavaHeapprofdSignal, &g_orig_act, nullptr) != 0) {
PLOG(ERROR) << "failed to reset signal handler";
// We cannot close the pipe if the signal handler wasn't unregistered,
diff --git a/profman/Android.bp b/profman/Android.bp
index b231499..8e76bc2 100644
--- a/profman/Android.bp
+++ b/profman/Android.bp
@@ -32,6 +32,7 @@
"profman.cc",
"profile_assistant.cc",
],
+ header_libs: ["profman_headers"],
target: {
android: {
@@ -160,11 +161,31 @@
},
}
+cc_library_headers {
+ name: "profman_headers",
+ defaults: ["art_defaults"],
+ export_include_dirs: ["include"],
+ host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ windows: {
+ enabled: true,
+ },
+ },
+ apex_available: [
+ "com.android.art",
+ "com.android.art.debug",
+ ],
+}
+
art_cc_defaults {
name: "art_profman_tests_defaults",
data: [
":art-gtest-jars-ProfileTestMultiDex",
],
+ header_libs: ["profman_headers"],
tidy_timeout_srcs: ["profile_assistant_test.cc"],
srcs: ["profile_assistant_test.cc"],
}
diff --git a/profman/include/profman/profman_result.h b/profman/include/profman/profman_result.h
new file mode 100644
index 0000000..4d2b733
--- /dev/null
+++ b/profman/include/profman/profman_result.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_PROFMAN_INCLUDE_PROFMAN_PROFMAN_RESULT_H_
+#define ART_PROFMAN_INCLUDE_PROFMAN_PROFMAN_RESULT_H_
+
+namespace art {
+
+class ProfmanResult {
+ public:
+ static constexpr int kErrorUsage = 100;
+
+ // The return codes of processing profiles (running profman in normal mode).
+ //
+ // On a successful run:
+ // - If `--force-merge` is specified, the return code can only be `kSuccess`.
+ // - If no `--profile-file(-fd)` is specified, the return code can only be
+ // `kSkipCompilationSmallDelta` or `kSkipCompilationEmptyProfiles`.
+ // - Otherwise, the return code can only be `kCompile`, `kSkipCompilationSmallDelta`, or
+ // `kSkipCompilationEmptyProfiles`.
+ //
+ // Note that installd consumes the returns codes with its own copy of these values
+ // (frameworks/native/cmds/installd/dexopt.cpp).
+ enum ProcessingResult {
+ // The success code for `--force-merge`.
+ // This is also the generic success code for non-analysis runs.
+ kSuccess = 0,
+ // A merge has been performed, meaning the reference profile has been changed.
+ kCompile = 1,
+ // `--profile-file(-fd)` is not specified, or the specified profiles are outdated (i.e., APK
+ // filename or checksum mismatch), empty, or don't contain enough number of new classes and
+ // methods that meets the threshold to trigger a merge.
+ kSkipCompilationSmallDelta = 2,
+ // All the input profiles (including the reference profile) are either outdated (i.e., APK
+ // filename or checksum mismatch) or empty.
+ kSkipCompilationEmptyProfiles = 7,
+ // Errors.
+ kErrorBadProfiles = 3,
+ kErrorIO = 4,
+ kErrorCannotLock = 5,
+ kErrorDifferentVersions = 6,
+ };
+
+ // The return codes of running profman with `--copy-and-update-profile-key`.
+ enum CopyAndUpdateResult {
+ kCopyAndUpdateSuccess = 0,
+ kCopyAndUpdateNoUpdate = 21,
+ kCopyAndUpdateErrorFailedToUpdateProfile = 22,
+ kCopyAndUpdateErrorFailedToSaveProfile = 23,
+ kCopyAndUpdateErrorFailedToLoadProfile = 24,
+ };
+};
+
+} // namespace art
+
+#endif // ART_PROFMAN_INCLUDE_PROFMAN_PROFMAN_RESULT_H_
diff --git a/profman/profile_assistant.cc b/profman/profile_assistant.cc
index d098738..abbde2d 100644
--- a/profman/profile_assistant.cc
+++ b/profman/profile_assistant.cc
@@ -18,6 +18,7 @@
#include "base/os.h"
#include "base/unix_file/fd_file.h"
+#include "profman/profman_result.h"
namespace art {
@@ -26,25 +27,22 @@
static constexpr const uint32_t kMinNewMethodsForCompilation = 100;
static constexpr const uint32_t kMinNewClassesForCompilation = 50;
-
-ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfilesInternal(
- const std::vector<ScopedFlock>& profile_files,
- const ScopedFlock& reference_profile_file,
- const ProfileCompilationInfo::ProfileLoadFilterFn& filter_fn,
- const Options& options) {
- DCHECK(!profile_files.empty());
-
+ProfmanResult::ProcessingResult ProfileAssistant::ProcessProfilesInternal(
+ const std::vector<ScopedFlock>& profile_files,
+ const ScopedFlock& reference_profile_file,
+ const ProfileCompilationInfo::ProfileLoadFilterFn& filter_fn,
+ const Options& options) {
ProfileCompilationInfo info(options.IsBootImageMerge());
// Load the reference profile.
if (!info.Load(reference_profile_file->Fd(), /*merge_classes=*/ true, filter_fn)) {
LOG(WARNING) << "Could not load reference profile file";
- return kErrorBadProfiles;
+ return ProfmanResult::kErrorBadProfiles;
}
if (options.IsBootImageMerge() && !info.IsForBootImage()) {
LOG(WARNING) << "Requested merge for boot image profile but the reference profile is regular.";
- return kErrorBadProfiles;
+ return ProfmanResult::kErrorBadProfiles;
}
// Store the current state of the reference profile before merging with the current profiles.
@@ -65,21 +63,21 @@
// TODO: Do we really need to use a different error code for version mismatch?
ProfileCompilationInfo wrong_info(!options.IsBootImageMerge());
if (wrong_info.Load(profile_files[i]->Fd(), /*merge_classes=*/ true, filter_fn)) {
- return kErrorDifferentVersions;
+ return ProfmanResult::kErrorDifferentVersions;
}
- return kErrorBadProfiles;
+ return ProfmanResult::kErrorBadProfiles;
}
if (!info.MergeWith(cur_info)) {
LOG(WARNING) << "Could not merge profile file at index " << i;
- return kErrorBadProfiles;
+ return ProfmanResult::kErrorBadProfiles;
}
}
// If we perform a forced merge do not analyze the difference between profiles.
if (!options.IsForceMerge()) {
if (info.IsEmpty()) {
- return kSkipCompilationEmptyProfiles;
+ return ProfmanResult::kSkipCompilationEmptyProfiles;
}
uint32_t min_change_in_methods_for_compilation = std::max(
(options.GetMinNewMethodsPercentChangeForCompilation() * number_of_methods) / 100,
@@ -91,21 +89,21 @@
if (((info.GetNumberOfMethods() - number_of_methods) < min_change_in_methods_for_compilation) &&
((info.GetNumberOfResolvedClasses() - number_of_classes)
< min_change_in_classes_for_compilation)) {
- return kSkipCompilationSmallDelta;
+ return ProfmanResult::kSkipCompilationSmallDelta;
}
}
// We were successful in merging all profile information. Update the reference profile.
if (!reference_profile_file->ClearContent()) {
PLOG(WARNING) << "Could not clear reference profile file";
- return kErrorIO;
+ return ProfmanResult::kErrorIO;
}
if (!info.Save(reference_profile_file->Fd())) {
LOG(WARNING) << "Could not save reference profile file";
- return kErrorIO;
+ return ProfmanResult::kErrorIO;
}
- return options.IsForceMerge() ? kSuccess : kCompile;
+ return options.IsForceMerge() ? ProfmanResult::kSuccess : ProfmanResult::kCompile;
}
class ScopedFlockList {
@@ -144,7 +142,7 @@
std::vector<ScopedFlock> flocks_;
};
-ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfiles(
+ProfmanResult::ProcessingResult ProfileAssistant::ProcessProfiles(
const std::vector<int>& profile_files_fd,
int reference_profile_file_fd,
const ProfileCompilationInfo::ProfileLoadFilterFn& filter_fn,
@@ -155,7 +153,7 @@
ScopedFlockList profile_files(profile_files_fd.size());
if (!profile_files.Init(profile_files_fd, &error)) {
LOG(WARNING) << "Could not lock profile files: " << error;
- return kErrorCannotLock;
+ return ProfmanResult::kErrorCannotLock;
}
// The reference_profile_file is opened in read/write mode because it's
@@ -166,7 +164,7 @@
&error);
if (reference_profile_file.get() == nullptr) {
LOG(WARNING) << "Could not lock reference profiled files: " << error;
- return kErrorCannotLock;
+ return ProfmanResult::kErrorCannotLock;
}
return ProcessProfilesInternal(profile_files.Get(),
@@ -175,7 +173,7 @@
options);
}
-ProfileAssistant::ProcessingResult ProfileAssistant::ProcessProfiles(
+ProfmanResult::ProcessingResult ProfileAssistant::ProcessProfiles(
const std::vector<std::string>& profile_files,
const std::string& reference_profile_file,
const ProfileCompilationInfo::ProfileLoadFilterFn& filter_fn,
@@ -185,14 +183,14 @@
ScopedFlockList profile_files_list(profile_files.size());
if (!profile_files_list.Init(profile_files, &error)) {
LOG(WARNING) << "Could not lock profile files: " << error;
- return kErrorCannotLock;
+ return ProfmanResult::kErrorCannotLock;
}
ScopedFlock locked_reference_profile_file = LockedFile::Open(
reference_profile_file.c_str(), O_RDWR, /* block= */ true, &error);
if (locked_reference_profile_file.get() == nullptr) {
LOG(WARNING) << "Could not lock reference profile files: " << error;
- return kErrorCannotLock;
+ return ProfmanResult::kErrorCannotLock;
}
return ProcessProfilesInternal(profile_files_list.Get(),
diff --git a/profman/profile_assistant.h b/profman/profile_assistant.h
index 0ef4f88..6b7a7a6 100644
--- a/profman/profile_assistant.h
+++ b/profman/profile_assistant.h
@@ -22,24 +22,12 @@
#include "base/scoped_flock.h"
#include "profile/profile_compilation_info.h"
+#include "profman/profman_result.h"
namespace art {
class ProfileAssistant {
public:
- // These also serve as return codes of profman and are processed by installd
- // (frameworks/native/cmds/installd/commands.cpp)
- enum ProcessingResult {
- kSuccess = 0, // Generic success code for non-analysis runs.
- kCompile = 1,
- kSkipCompilationSmallDelta = 2,
- kErrorBadProfiles = 3,
- kErrorIO = 4,
- kErrorCannotLock = 5,
- kErrorDifferentVersions = 6,
- kSkipCompilationEmptyProfiles = 7,
- };
-
class Options {
public:
static constexpr bool kForceMergeDefault = false;
@@ -101,14 +89,14 @@
// this case no file will be updated. A variation of this code is
// kSkipCompilationEmptyProfiles which indicates that all the profiles are empty.
// This allow the caller to make fine grain decisions on the compilation strategy.
- static ProcessingResult ProcessProfiles(
+ static ProfmanResult::ProcessingResult ProcessProfiles(
const std::vector<std::string>& profile_files,
const std::string& reference_profile_file,
const ProfileCompilationInfo::ProfileLoadFilterFn& filter_fn
= ProfileCompilationInfo::ProfileFilterFnAcceptAll,
const Options& options = Options());
- static ProcessingResult ProcessProfiles(
+ static ProfmanResult::ProcessingResult ProcessProfiles(
const std::vector<int>& profile_files_fd_,
int reference_profile_file_fd,
const ProfileCompilationInfo::ProfileLoadFilterFn& filter_fn
@@ -116,7 +104,7 @@
const Options& options = Options());
private:
- static ProcessingResult ProcessProfilesInternal(
+ static ProfmanResult::ProcessingResult ProcessProfilesInternal(
const std::vector<ScopedFlock>& profile_files,
const ScopedFlock& reference_profile_file,
const ProfileCompilationInfo::ProfileLoadFilterFn& filter_fn,
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index f446c09..4fc8143 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-#include <gtest/gtest.h>
+#include "profile_assistant.h"
+
#include <sstream>
#include <string>
@@ -31,12 +32,13 @@
#include "dex/dex_instruction_iterator.h"
#include "dex/type_reference.h"
#include "exec_utils.h"
+#include "gtest/gtest.h"
#include "linear_alloc.h"
#include "mirror/class-inl.h"
#include "obj_ptr-inl.h"
#include "profile/profile_compilation_info.h"
#include "profile/profile_test_helper.h"
-#include "profile_assistant.h"
+#include "profman/profman_result.h"
#include "scoped_thread_state_change-inl.h"
namespace art {
@@ -50,13 +52,13 @@
void PostRuntimeCreate() override {
allocator_.reset(new ArenaAllocator(Runtime::Current()->GetArenaPool()));
- dex1 = BuildDex("location1", /*checksum=*/ 1, "LUnique1;", /*num_method_ids=*/ 10001);
- dex2 = BuildDex("location2", /*checksum=*/ 2, "LUnique2;", /*num_method_ids=*/ 10002);
- dex3 = BuildDex("location3", /*checksum=*/ 3, "LUnique3;", /*num_method_ids=*/ 10003);
- dex4 = BuildDex("location4", /*checksum=*/ 4, "LUnique4;", /*num_method_ids=*/ 10004);
+ dex1 = BuildDex("location1", /*location_checksum=*/ 1, "LUnique1;", /*num_method_ids=*/ 10001);
+ dex2 = BuildDex("location2", /*location_checksum=*/ 2, "LUnique2;", /*num_method_ids=*/ 10002);
+ dex3 = BuildDex("location3", /*location_checksum=*/ 3, "LUnique3;", /*num_method_ids=*/ 10003);
+ dex4 = BuildDex("location4", /*location_checksum=*/ 4, "LUnique4;", /*num_method_ids=*/ 10004);
dex1_checksum_missmatch =
- BuildDex("location1", /*checksum=*/ 12, "LUnique1;", /*num_method_ids=*/ 10001);
+ BuildDex("location1", /*location_checksum=*/ 12, "LUnique1;", /*num_method_ids=*/ 10001);
}
protected:
@@ -267,7 +269,7 @@
bool CreateAndDump(const std::string& input_file_contents,
std::string* output_file_contents,
- std::optional<const std::string> target = std::nullopt) {
+ const std::optional<const std::string>& target = std::nullopt) {
ScratchFile profile_file;
EXPECT_TRUE(CreateProfile(input_file_contents,
profile_file.GetFilename(),
@@ -442,10 +444,16 @@
const std::vector<const std::string>& extra_args =
std::vector<const std::string>()) {
uint16_t max_classes = std::max(classes_in_cur_profile, classes_in_ref_profile);
- const DexFile* dex1_x = BuildDex(
- "location1_x", /*checksum=*/ 0x101, "LUnique1_x;", /*num_method_ids=*/ 0, max_classes);
- const DexFile* dex2_x = BuildDex(
- "location2_x", /*checksum=*/ 0x102, "LUnique2_x;", /*num_method_ids=*/ 0, max_classes);
+ const DexFile* dex1_x = BuildDex("location1_x",
+ /*location_checksum=*/ 0x101,
+ "LUnique1_x;",
+ /*num_method_ids=*/ 0,
+ max_classes);
+ const DexFile* dex2_x = BuildDex("location2_x",
+ /*location_checksum=*/ 0x102,
+ "LUnique2_x;",
+ /*num_method_ids=*/ 0,
+ max_classes);
ScratchFile profile;
ScratchFile reference_profile;
@@ -486,8 +494,7 @@
SetupProfile(dex3, dex4, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
// We should advise compilation.
- ASSERT_EQ(ProfileAssistant::kCompile,
- ProcessProfiles(profile_fds, reference_profile_fd));
+ ASSERT_EQ(ProfmanResult::kCompile, ProcessProfiles(profile_fds, reference_profile_fd));
// The resulting compilation info must be equal to the merge of the inputs.
ProfileCompilationInfo result;
ASSERT_TRUE(result.Load(reference_profile_fd));
@@ -506,15 +513,15 @@
TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferencesBecauseOfClasses) {
const uint16_t kNumberOfClassesToEnableCompilation = 100;
const DexFile* dex1_100 = BuildDex("location1_100",
- /*checksum=*/ 101,
+ /*location_checksum=*/ 101,
"LUnique1_100;",
/*num_method_ids=*/ 0,
- /*num_type_ids=*/ 100);
+ /*num_class_ids=*/ 100);
const DexFile* dex2_100 = BuildDex("location2_100",
- /*checksum=*/ 102,
+ /*location_checksum=*/ 102,
"LUnique2_100;",
/*num_method_ids=*/ 0,
- /*num_type_ids=*/ 100);
+ /*num_class_ids=*/ 100);
ScratchFile profile1;
ScratchFile reference_profile;
@@ -527,8 +534,7 @@
SetupProfile(dex1_100, dex2_100, 0, kNumberOfClassesToEnableCompilation, profile1, &info1);
// We should advise compilation.
- ASSERT_EQ(ProfileAssistant::kCompile,
- ProcessProfiles(profile_fds, reference_profile_fd));
+ ASSERT_EQ(ProfmanResult::kCompile, ProcessProfiles(profile_fds, reference_profile_fd));
// The resulting compilation info must be equal to the merge of the inputs.
ProfileCompilationInfo result;
ASSERT_TRUE(result.Load(reference_profile_fd));
@@ -566,8 +572,7 @@
&reference_info, kNumberOfMethodsToEnableCompilation / 2);
// We should advise compilation.
- ASSERT_EQ(ProfileAssistant::kCompile,
- ProcessProfiles(profile_fds, reference_profile_fd));
+ ASSERT_EQ(ProfmanResult::kCompile, ProcessProfiles(profile_fds, reference_profile_fd));
// The resulting compilation info must be equal to the merge of the inputs
ProfileCompilationInfo result;
@@ -600,7 +605,7 @@
SetupProfile(dex3, dex4, /*number_of_methods=*/ 0, /*number_of_classes*/ 0, profile2, &info2);
// We should not advise compilation.
- ASSERT_EQ(ProfileAssistant::kSkipCompilationEmptyProfiles,
+ ASSERT_EQ(ProfmanResult::kSkipCompilationEmptyProfiles,
ProcessProfiles(profile_fds, reference_profile_fd));
// The information from profiles must remain the same.
@@ -637,7 +642,7 @@
SetupProfile(dex3, dex4, kNumberOfMethodsToSkipCompilation, 0, profile2, &info2);
// We should not advise compilation.
- ASSERT_EQ(ProfileAssistant::kSkipCompilationSmallDelta,
+ ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
ProcessProfiles(profile_fds, reference_profile_fd));
// The information from profiles must remain the same.
@@ -663,10 +668,9 @@
std::vector<const std::string> extra_args({"--min-new-methods-percent-change=2"});
// We should not advise compilation.
- ASSERT_EQ(ProfileAssistant::kSkipCompilationSmallDelta,
- CheckCompilationMethodPercentChange(kNumberOfMethodsInCurProfile,
- kNumberOfMethodsInRefProfile,
- extra_args));
+ ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
+ CheckCompilationMethodPercentChange(
+ kNumberOfMethodsInCurProfile, kNumberOfMethodsInRefProfile, extra_args));
}
TEST_F(ProfileAssistantTest, ShouldAdviseCompilationMethodPercentage) {
@@ -675,10 +679,9 @@
std::vector<const std::string> extra_args({"--min-new-methods-percent-change=2"});
// We should advise compilation.
- ASSERT_EQ(ProfileAssistant::kCompile,
- CheckCompilationMethodPercentChange(kNumberOfMethodsInCurProfile,
- kNumberOfMethodsInRefProfile,
- extra_args));
+ ASSERT_EQ(ProfmanResult::kCompile,
+ CheckCompilationMethodPercentChange(
+ kNumberOfMethodsInCurProfile, kNumberOfMethodsInRefProfile, extra_args));
}
TEST_F(ProfileAssistantTest, DoNotAdviseCompilationMethodPercentageWithNewMin) {
@@ -686,7 +689,7 @@
const uint16_t kNumberOfMethodsInCurProfile = 6200; // Threshold is 20%.
// We should not advise compilation.
- ASSERT_EQ(ProfileAssistant::kSkipCompilationSmallDelta,
+ ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
CheckCompilationMethodPercentChange(kNumberOfMethodsInCurProfile,
kNumberOfMethodsInRefProfile));
}
@@ -697,10 +700,9 @@
std::vector<const std::string> extra_args({"--min-new-classes-percent-change=2"});
// We should not advise compilation.
- ASSERT_EQ(ProfileAssistant::kSkipCompilationSmallDelta,
- CheckCompilationClassPercentChange(kNumberOfClassesInCurProfile,
- kNumberOfClassesInRefProfile,
- extra_args));
+ ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
+ CheckCompilationClassPercentChange(
+ kNumberOfClassesInCurProfile, kNumberOfClassesInRefProfile, extra_args));
}
TEST_F(ProfileAssistantTest, ShouldAdviseCompilationClassPercentage) {
@@ -709,10 +711,9 @@
std::vector<const std::string> extra_args({"--min-new-classes-percent-change=2"});
// We should advise compilation.
- ASSERT_EQ(ProfileAssistant::kCompile,
- CheckCompilationClassPercentChange(kNumberOfClassesInCurProfile,
- kNumberOfClassesInRefProfile,
- extra_args));
+ ASSERT_EQ(ProfmanResult::kCompile,
+ CheckCompilationClassPercentChange(
+ kNumberOfClassesInCurProfile, kNumberOfClassesInRefProfile, extra_args));
}
TEST_F(ProfileAssistantTest, DoNotAdviseCompilationClassPercentageWithNewMin) {
@@ -720,7 +721,7 @@
const uint16_t kNumberOfClassesInCurProfile = 6200; // Threshold is 20%.
// We should not advise compilation.
- ASSERT_EQ(ProfileAssistant::kSkipCompilationSmallDelta,
+ ASSERT_EQ(ProfmanResult::kSkipCompilationSmallDelta,
CheckCompilationClassPercentChange(kNumberOfClassesInCurProfile,
kNumberOfClassesInRefProfile));
}
@@ -744,8 +745,7 @@
dex1_checksum_missmatch, dex2, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
// We should fail processing.
- ASSERT_EQ(ProfileAssistant::kErrorBadProfiles,
- ProcessProfiles(profile_fds, reference_profile_fd));
+ ASSERT_EQ(ProfmanResult::kErrorBadProfiles, ProcessProfiles(profile_fds, reference_profile_fd));
// The information from profiles must remain the same.
CheckProfileInfo(profile1, info1);
@@ -776,8 +776,7 @@
&reference_info);
// We should not advise compilation.
- ASSERT_EQ(ProfileAssistant::kErrorBadProfiles,
- ProcessProfiles(profile_fds, reference_profile_fd));
+ ASSERT_EQ(ProfmanResult::kErrorBadProfiles, ProcessProfiles(profile_fds, reference_profile_fd));
// The information from profiles must remain the same.
CheckProfileInfo(profile1, info1);
@@ -971,7 +970,7 @@
/*for_boot_image=*/ true));
ProfileCompilationInfo bootProfile(/*for_boot_image=*/ true);
- bootProfile.Load(profile.GetFilename(), /*clear_if_invalid=*/ true);
+ EXPECT_TRUE(bootProfile.Load(profile.GetFilename(), /*clear_if_invalid=*/ false));
// Generate the boot profile.
ScratchFile out_profile;
@@ -1065,10 +1064,10 @@
core_dex,
/*for_boot_image=*/ true));
- ProfileCompilationInfo boot_profile1;
- ProfileCompilationInfo boot_profile2;
- boot_profile1.Load(profile1.GetFilename(), /*for_boot_image=*/ true);
- boot_profile2.Load(profile2.GetFilename(), /*for_boot_image=*/ true);
+ ProfileCompilationInfo boot_profile1(/*for_boot_image=*/ true);
+ ProfileCompilationInfo boot_profile2(/*for_boot_image=*/ true);
+ EXPECT_TRUE(boot_profile1.Load(profile1.GetFilename(), /*clear_if_invalid=*/ false));
+ EXPECT_TRUE(boot_profile2.Load(profile2.GetFilename(), /*clear_if_invalid=*/ false));
// Generate the boot profile.
ScratchFile out_profile;
@@ -1525,8 +1524,7 @@
&reference_info, kNumberOfMethodsToEnableCompilation / 2, /*reverse_dex_write_order=*/true);
// We should advise compilation.
- ASSERT_EQ(ProfileAssistant::kCompile,
- ProcessProfiles(profile_fds, reference_profile_fd));
+ ASSERT_EQ(ProfmanResult::kCompile, ProcessProfiles(profile_fds, reference_profile_fd));
// The resulting compilation info must be equal to the merge of the inputs.
ProfileCompilationInfo result;
@@ -1787,7 +1785,7 @@
argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
std::string error;
- EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfileAssistant::kCompile) << error;
+ EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kCompile) << error;
// Verify that we can load the result.
@@ -1820,6 +1818,169 @@
ASSERT_TRUE(expected.Equals(result));
}
+TEST_F(ProfileAssistantTest, MergeProfilesNoProfile) {
+ ScratchFile reference_profile;
+
+ // Use a real dex file to generate profile test data.
+ std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("ProfileTestMultiDex");
+ const DexFile& d1 = *dex_files[0];
+ const DexFile& d2 = *dex_files[0];
+
+ // The reference profile info will contain the methods with indices 0-100.
+ ProfileCompilationInfo reference_info;
+ SetupProfile(&d1,
+ &d2,
+ /*number_of_methods=*/ 100,
+ /*number_of_classes=*/ 0,
+ reference_profile,
+ &reference_info);
+
+ std::string content_before;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_before));
+
+ // Run profman and pass the dex file with --apk-fd.
+ android::base::unique_fd apk_fd(
+ // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
+ open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
+ ASSERT_GE(apk_fd.get(), 0);
+
+ std::string profman_cmd = GetProfmanCmd();
+ std::vector<std::string> argv_str;
+ argv_str.push_back(profman_cmd);
+ argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
+ argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
+
+ // Must return kSkipCompilationSmallDelta.
+ std::string error;
+ EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSkipCompilationSmallDelta)
+ << error;
+
+ // Verify that the content has not changed.
+ std::string content_after;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_after));
+ EXPECT_EQ(content_before, content_after);
+}
+
+TEST_F(ProfileAssistantTest, MergeProfilesNoProfilePassByFilename) {
+ ScratchFile reference_profile;
+
+ // Use a real dex file to generate profile test data.
+ std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("ProfileTestMultiDex");
+ const DexFile& d1 = *dex_files[0];
+ const DexFile& d2 = *dex_files[0];
+
+ // The reference profile info will contain the methods with indices 0-100.
+ ProfileCompilationInfo reference_info;
+ SetupProfile(&d1,
+ &d2,
+ /*number_of_methods=*/100,
+ /*number_of_classes=*/0,
+ reference_profile,
+ &reference_info);
+
+ std::string content_before;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_before));
+
+ // Run profman and pass the dex file with --apk-fd.
+ android::base::unique_fd apk_fd(
+ // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
+ open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
+ ASSERT_GE(apk_fd.get(), 0);
+
+ std::string profman_cmd = GetProfmanCmd();
+ std::vector<std::string> argv_str;
+ argv_str.push_back(profman_cmd);
+ argv_str.push_back("--reference-profile-file=" + reference_profile.GetFilename());
+ argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
+
+ // Must return kSkipCompilationSmallDelta.
+ std::string error;
+ EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSkipCompilationSmallDelta)
+ << error;
+
+ // Verify that the content has not changed.
+ std::string content_after;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_after));
+ EXPECT_EQ(content_before, content_after);
+}
+
+TEST_F(ProfileAssistantTest, MergeProfilesNoProfileEmptyReferenceProfile) {
+ ScratchFile reference_profile;
+
+ // The reference profile info will only contain the header.
+ ProfileCompilationInfo reference_info;
+ SetupProfile(/*dex_file1=*/ nullptr,
+ /*dex_file2=*/ nullptr,
+ /*number_of_methods=*/ 0,
+ /*number_of_classes=*/ 0,
+ reference_profile,
+ &reference_info);
+
+ std::string content_before;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_before));
+
+ // Run profman and pass the dex file with --apk-fd.
+ android::base::unique_fd apk_fd(
+ // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
+ open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
+ ASSERT_GE(apk_fd.get(), 0);
+
+ std::string profman_cmd = GetProfmanCmd();
+ std::vector<std::string> argv_str;
+ argv_str.push_back(profman_cmd);
+ argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
+ argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
+
+ // Must return kSkipCompilationEmptyProfiles.
+ std::string error;
+ EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSkipCompilationEmptyProfiles)
+ << error;
+
+ // Verify that the content has not changed.
+ std::string content_after;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_after));
+ EXPECT_EQ(content_before, content_after);
+}
+
+TEST_F(ProfileAssistantTest, MergeProfilesNoProfileEmptyReferenceProfileAfterFiltering) {
+ ScratchFile reference_profile;
+
+ // Use fake dex files to generate profile test data.
+ // All the methods will be filtered out during the profman invocation.
+ ProfileCompilationInfo reference_info;
+ SetupProfile(dex1,
+ dex2,
+ /*number_of_methods=*/ 100,
+ /*number_of_classes=*/ 0,
+ reference_profile,
+ &reference_info);
+
+ std::string content_before;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_before));
+
+ // Run profman and pass the real dex file with --apk-fd.
+ android::base::unique_fd apk_fd(
+ // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
+ open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
+ ASSERT_GE(apk_fd.get(), 0);
+
+ std::string profman_cmd = GetProfmanCmd();
+ std::vector<std::string> argv_str;
+ argv_str.push_back(profman_cmd);
+ argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
+ argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
+
+ // Must return kSkipCompilationEmptyProfiles.
+ std::string error;
+ EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSkipCompilationEmptyProfiles)
+ << error;
+
+ // Verify that the content has not changed.
+ std::string content_after;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &content_after));
+ EXPECT_EQ(content_before, content_after);
+}
+
TEST_F(ProfileAssistantTest, CopyAndUpdateProfileKey) {
ScratchFile profile1;
ScratchFile reference_profile;
@@ -1846,7 +2007,8 @@
// Run profman and pass the dex file with --apk-fd.
android::base::unique_fd apk_fd(
- open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY)); // NOLINT
+ // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
+ open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
ASSERT_GE(apk_fd.get(), 0);
std::string profman_cmd = GetProfmanCmd();
@@ -1858,7 +2020,8 @@
argv_str.push_back("--copy-and-update-profile-key");
std::string error;
- ASSERT_EQ(ExecAndReturnCode(argv_str, &error), 0) << error;
+ // Must return kCopyAndUpdateSuccess.
+ ASSERT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kCopyAndUpdateSuccess) << error;
// Verify that we can load the result.
ProfileCompilationInfo result;
@@ -1874,6 +2037,46 @@
}
}
+TEST_F(ProfileAssistantTest, CopyAndUpdateProfileKeyNoUpdate) {
+ ScratchFile profile1;
+ ScratchFile reference_profile;
+
+ // Use fake dex files to generate profile test data.
+ ProfileCompilationInfo info1;
+ SetupProfile(dex1,
+ dex2,
+ /*number_of_methods=*/ 100,
+ /*number_of_classes=*/ 0,
+ profile1,
+ &info1);
+
+ std::string input_content;
+ ASSERT_TRUE(android::base::ReadFileToString(profile1.GetFilename(), &input_content));
+
+ // Run profman and pass the real dex file with --apk-fd. It won't match any entry in the profile.
+ android::base::unique_fd apk_fd(
+ // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
+ open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
+ ASSERT_GE(apk_fd.get(), 0);
+
+ std::string profman_cmd = GetProfmanCmd();
+ std::vector<std::string> argv_str;
+ argv_str.push_back(profman_cmd);
+ argv_str.push_back("--profile-file-fd=" + std::to_string(profile1.GetFd()));
+ argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile.GetFd()));
+ argv_str.push_back("--apk-fd=" + std::to_string(apk_fd.get()));
+ argv_str.push_back("--copy-and-update-profile-key");
+ std::string error;
+
+ // Must return kCopyAndUpdateNoUpdate.
+ ASSERT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kCopyAndUpdateNoUpdate) << error;
+
+ // Verify that the content is the same.
+ std::string output_content;
+ ASSERT_TRUE(android::base::ReadFileToString(reference_profile.GetFilename(), &output_content));
+ EXPECT_EQ(input_content, output_content);
+}
+
TEST_F(ProfileAssistantTest, BootImageMerge) {
ScratchFile profile;
ScratchFile reference_profile;
@@ -1900,7 +2103,7 @@
int return_code = ProcessProfiles(profile_fds, reference_profile_fd, extra_args);
- ASSERT_EQ(return_code, ProfileAssistant::kSuccess);
+ ASSERT_EQ(return_code, ProfmanResult::kSuccess);
// Verify the result: it should be equal to info2 since info1 is a regular profile
// and should be ignored.
@@ -1918,15 +2121,15 @@
const uint16_t kNumberOfClassesInCurProfile = 6110; // Threshold is 2%.
const DexFile* dex1_7000 = BuildDex("location1_7000",
- /*checksum=*/ 7001,
+ /*location_checksum=*/ 7001,
"LUnique1_7000;",
/*num_method_ids=*/ 0,
- /*num_type_ids=*/ 7000);
+ /*num_class_ids=*/ 7000);
const DexFile* dex2_7000 = BuildDex("location2_7000",
- /*checksum=*/ 7002,
+ /*location_checksum=*/ 7002,
"LUnique2_7000;",
/*num_method_ids=*/ 0,
- /*num_type_ids=*/ 7000);
+ /*num_class_ids=*/ 7000);
ScratchFile profile;
ScratchFile reference_profile;
@@ -1942,7 +2145,7 @@
std::vector<const std::string> extra_args({"--force-merge"});
int return_code = ProcessProfiles(profile_fds, reference_profile_fd, extra_args);
- ASSERT_EQ(return_code, ProfileAssistant::kSuccess);
+ ASSERT_EQ(return_code, ProfmanResult::kSuccess);
// Check that the result is the aggregation.
ProfileCompilationInfo result;
@@ -1974,7 +2177,8 @@
// Run profman and pass the dex file with --apk-fd.
android::base::unique_fd apk_fd(
- open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY)); // NOLINT
+ // NOLINTNEXTLINE - Profman needs file to be opened after fork() and exec()
+ open(GetTestDexFileName("ProfileTestMultiDex").c_str(), O_RDONLY));
ASSERT_GE(apk_fd.get(), 0);
std::string profman_cmd = GetProfmanCmd();
@@ -1987,7 +2191,7 @@
argv_str.push_back("--boot-image-merge");
std::string error;
- EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfileAssistant::kSuccess) << error;
+ EXPECT_EQ(ExecAndReturnCode(argv_str, &error), ProfmanResult::kSuccess) << error;
// Verify that we can load the result and that it equals to what we saved.
ProfileCompilationInfo result(/*for_boot_image=*/ true);
@@ -2009,17 +2213,16 @@
int reference_profile_fd = GetFd(profile2);
std::vector<const std::string> boot_image_args({"--boot-image-merge"});
ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, boot_image_args),
- ProfileAssistant::kErrorDifferentVersions);
- ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd),
- ProfileAssistant::kErrorBadProfiles);
+ ProfmanResult::kErrorDifferentVersions);
+ ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd), ProfmanResult::kErrorBadProfiles);
// Reverse the order of the profiles to verify we get the same behaviour.
profile_fds[0] = GetFd(profile2);
reference_profile_fd = GetFd(profile1);
ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, boot_image_args),
- ProfileAssistant::kErrorBadProfiles);
+ ProfmanResult::kErrorBadProfiles);
ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd),
- ProfileAssistant::kErrorDifferentVersions);
+ ProfmanResult::kErrorDifferentVersions);
}
// Under default behaviour we will abort if we cannot load a profile during a merge
@@ -2042,7 +2245,7 @@
// With force-merge we should merge successfully.
std::vector<const std::string> extra_args({"--force-merge", "--boot-image-merge"});
ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, extra_args),
- ProfileAssistant::kSuccess);
+ ProfmanResult::kSuccess);
ProfileCompilationInfo result(/*for_boot_image=*/ true);
ASSERT_TRUE(result.Load(reference_profile_fd));
@@ -2051,7 +2254,7 @@
// Without force-merge we should fail.
std::vector<const std::string> extra_args2({"--boot-image-merge"});
ASSERT_EQ(ProcessProfiles(profile_fds, reference_profile_fd, extra_args2),
- ProfileAssistant::kErrorBadProfiles);
+ ProfmanResult::kErrorBadProfiles);
}
} // namespace art
diff --git a/profman/profman.cc b/profman/profman.cc
index 1968468..c2b98a2 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -36,7 +36,6 @@
#include "android-base/parsebool.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
-
#include "base/array_ref.h"
#include "base/dumpable.h"
#include "base/logging.h" // For InitLogging.
@@ -64,6 +63,7 @@
#include "profile/profile_boot_info.h"
#include "profile/profile_compilation_info.h"
#include "profile_assistant.h"
+#include "profman/profman_result.h"
namespace art {
@@ -118,7 +118,10 @@
UsageError("");
UsageError(" --profile-file=<filename>: specify profiler output file to use for compilation.");
UsageError(" Can be specified multiple time, in which case the data from the different");
- UsageError(" profiles will be aggregated.");
+ UsageError(" profiles will be aggregated. Can also be specified zero times, in which case");
+ UsageError(" profman will still analyze the reference profile against the given --apk and");
+ UsageError(" return exit code based on whether the reference profile is empty and whether");
+ UsageError(" an error occurs, but no merge will happen.");
UsageError("");
UsageError(" --profile-file-fd=<number>: same as --profile-file but accepts a file descriptor.");
UsageError(" Cannot be used together with --profile-file.");
@@ -126,8 +129,8 @@
UsageError(" --reference-profile-file=<filename>: specify a reference profile.");
UsageError(" The data in this file will be compared with the data obtained by merging");
UsageError(" all the files specified with --profile-file or --profile-file-fd.");
- UsageError(" If the exit code is EXIT_COMPILE then all --profile-file will be merged into");
- UsageError(" --reference-profile-file. ");
+ UsageError(" If the exit code is ProfmanResult::kCompile then all --profile-file will be");
+ UsageError(" merged into --reference-profile-file. ");
UsageError("");
UsageError(" --reference-profile-file-fd=<number>: same as --reference-profile-file but");
UsageError(" accepts a file descriptor. Cannot be used together with");
@@ -194,7 +197,7 @@
UsageError(" the min percent of new classes to trigger a compilation.");
UsageError("");
- exit(EXIT_FAILURE);
+ exit(ProfmanResult::kErrorUsage);
}
// Note: make sure you update the Usage if you change these values.
@@ -501,11 +504,10 @@
}
};
- ProfileAssistant::ProcessingResult ProcessProfiles() {
- // Validate that at least one profile file was passed, as well as a reference profile.
- if (profile_files_.empty() && profile_files_fd_.empty()) {
- Usage("No profile files specified.");
- }
+ ProfmanResult::ProcessingResult ProcessProfiles() {
+ // Validate that a reference profile was passed, at the very least. It's okay that profiles are
+ // missing, in which case profman will still analyze the reference profile (to check whether
+ // it's empty), but no merge will happen.
if (reference_profile_file_.empty() && !FdIsValid(reference_profile_file_fd_)) {
Usage("No reference profile file specified.");
}
@@ -518,7 +520,7 @@
// Check if we have any apks which we should use to filter the profile data.
std::set<ProfileFilterKey> profile_filter_keys;
if (!GetProfileFilterKeyFromApks(&profile_filter_keys)) {
- return ProfileAssistant::kErrorIO;
+ return ProfmanResult::kErrorIO;
}
// Build the profile filter function. If the set of keys is empty it means we
@@ -536,9 +538,9 @@
}
};
- ProfileAssistant::ProcessingResult result;
+ ProfmanResult::ProcessingResult result;
- if (profile_files_.empty()) {
+ if (reference_profile_file_.empty()) {
// The file doesn't need to be flushed here (ProcessProfiles will do it)
// so don't check the usage.
File file(reference_profile_file_fd_, false);
@@ -1205,8 +1207,23 @@
for (std::string_view t :
SplitString(ic_line.substr(1), kProfileParsingInlineChacheTargetSep)) {
InlineCacheSegment out;
- DCHECK_EQ(t[0], 'L') << "Target is not a class? " << t;
- size_t recv_end = t.find_first_of(';');
+ // The target may be an array for methods defined in `j.l.Object`, such as `clone()`.
+ size_t recv_end;
+ if (UNLIKELY(t[0] == '[')) {
+ recv_end = t.find_first_not_of('[', 1u);
+ DCHECK_NE(recv_end, std::string_view::npos);
+ if (t[recv_end] == 'L') {
+ recv_end = t.find_first_of(';', recv_end + 1u);
+ DCHECK_NE(recv_end, std::string_view::npos);
+ } else {
+ // Primitive array.
+ DCHECK_NE(Primitive::GetType(t[recv_end]), Primitive::kPrimNot);
+ }
+ } else {
+ DCHECK_EQ(t[0], 'L') << "Target is not a class? " << t;
+ recv_end = t.find_first_of(';', 1u);
+ DCHECK_NE(recv_end, std::string_view::npos);
+ }
out.receiver_ = t.substr(0, recv_end + 1);
Split(t.substr(recv_end + 1), kProfileParsingTypeSep, &out.inline_caches_);
res->push_back(out);
@@ -1854,7 +1871,7 @@
return copy_and_update_profile_key_;
}
- int32_t CopyAndUpdateProfileKey() {
+ ProfmanResult::CopyAndUpdateResult CopyAndUpdateProfileKey() {
// Validate that at least one profile file was passed, as well as a reference profile.
if (!(profile_files_.size() == 1 ^ profile_files_fd_.size() == 1)) {
Usage("Only one profile file should be specified.");
@@ -1867,10 +1884,6 @@
Usage("No apk files specified");
}
- static constexpr int32_t kErrorFailedToUpdateProfile = -1;
- static constexpr int32_t kErrorFailedToSaveProfile = -2;
- static constexpr int32_t kErrorFailedToLoadProfile = -3;
-
bool use_fds = profile_files_fd_.size() == 1;
ProfileCompilationInfo profile;
@@ -1882,15 +1895,19 @@
// Open the dex files to look up classes and methods.
std::vector<std::unique_ptr<const DexFile>> dex_files;
OpenApkFilesFromLocations(&dex_files);
- if (!profile.UpdateProfileKeys(dex_files)) {
- return kErrorFailedToUpdateProfile;
+ bool updated = false;
+ if (!profile.UpdateProfileKeys(dex_files, &updated)) {
+ return ProfmanResult::kCopyAndUpdateErrorFailedToUpdateProfile;
}
bool result = use_fds
? profile.Save(reference_profile_file_fd_)
: profile.Save(reference_profile_file_, /*bytes_written=*/ nullptr);
- return result ? 0 : kErrorFailedToSaveProfile;
+ if (!result) {
+ return ProfmanResult::kCopyAndUpdateErrorFailedToSaveProfile;
+ }
+ return updated ? ProfmanResult::kCopyAndUpdateSuccess : ProfmanResult::kCopyAndUpdateNoUpdate;
} else {
- return kErrorFailedToLoadProfile;
+ return ProfmanResult::kCopyAndUpdateErrorFailedToLoadProfile;
}
}
@@ -1950,7 +1967,7 @@
return ics.Dump(os);
}
-// See ProfileAssistant::ProcessingResult for return codes.
+// See ProfmanResult for return codes.
static int profman(int argc, char** argv) {
ProfMan profman;
diff --git a/runtime/Android.bp b/runtime/Android.bp
index ba7f6cc..f162807 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -37,6 +37,12 @@
libart_cc_defaults {
name: "libart_nativeunwind_defaults",
target: {
+ host: {
+ cflags: [
+ "-fsanitize-address-use-after-return=never",
+ "-Wno-unused-command-line-argument",
+ ],
+ },
android_arm: {
// Arm 32 bit does not produce complete exidx unwind information
// so keep the .debug_frame which is relatively small and does
@@ -110,6 +116,7 @@
"art_method.cc",
"backtrace_helper.cc",
"barrier.cc",
+ "base/gc_visited_arena_pool.cc",
"base/locks.cc",
"base/mem_map_arena_pool.cc",
"base/mutex.cc",
@@ -130,7 +137,7 @@
"exec_utils.cc",
"fault_handler.cc",
"gc/allocation_record.cc",
- "gc/allocator/dlmalloc.cc",
+ "gc/allocator/art-dlmalloc.cc",
"gc/allocator/rosalloc.cc",
"gc/accounting/bitmap.cc",
"gc/accounting/card_table.cc",
@@ -142,6 +149,7 @@
"gc/collector/garbage_collector.cc",
"gc/collector/immune_region.cc",
"gc/collector/immune_spaces.cc",
+ "gc/collector/mark_compact.cc",
"gc/collector/mark_sweep.cc",
"gc/collector/partial_mark_sweep.cc",
"gc/collector/semi_space.cc",
@@ -173,7 +181,6 @@
"interpreter/interpreter.cc",
"interpreter/interpreter_cache.cc",
"interpreter/interpreter_common.cc",
- "interpreter/interpreter_intrinsics.cc",
"interpreter/interpreter_switch_impl0.cc",
"interpreter/interpreter_switch_impl1.cc",
"interpreter/interpreter_switch_impl2.cc",
@@ -194,7 +201,7 @@
"jni/jni_env_ext.cc",
"jni/jni_id_manager.cc",
"jni/jni_internal.cc",
- "linear_alloc.cc",
+ "jni/local_reference_table.cc",
"method_handles.cc",
"metrics/reporter.cc",
"mirror/array.cc",
@@ -209,6 +216,7 @@
"mirror/method_handles_lookup.cc",
"mirror/method_type.cc",
"mirror/object.cc",
+ "mirror/stack_frame_info.cc",
"mirror/stack_trace_element.cc",
"mirror/string.cc",
"mirror/throwable.cc",
@@ -225,6 +233,7 @@
"native/dalvik_system_ZygoteHooks.cc",
"native/java_lang_Class.cc",
"native/java_lang_Object.cc",
+ "native/java_lang_StackStreamFactory.cc",
"native/java_lang_String.cc",
"native/java_lang_StringFactory.cc",
"native/java_lang_System.cc",
@@ -254,6 +263,7 @@
"oat.cc",
"oat_file.cc",
"oat_file_assistant.cc",
+ "oat_file_assistant_context.cc",
"oat_file_manager.cc",
"oat_quick_method_header.cc",
"object_lock.cc",
@@ -269,6 +279,7 @@
"runtime.cc",
"runtime_callbacks.cc",
"runtime_common.cc",
+ "runtime_image.cc",
"runtime_intrinsics.cc",
"runtime_options.cc",
"scoped_thread_state_change.cc",
@@ -415,7 +426,6 @@
],
static_libs: [
"libstatslog_art",
- "libtinyxml2",
],
generated_sources: [
"apex-info-list-tinyxml",
@@ -439,6 +449,10 @@
"runtime_linux.cc",
"thread_linux.cc",
],
+ cflags: [
+ "-fsanitize-address-use-after-return=never",
+ "-Wno-unused-command-line-argument",
+ ],
shared_libs: [
"libz", // For adler32.
],
@@ -456,17 +470,20 @@
header_libs: [
"art_cmdlineparser_headers",
"cpp-define-generator-definitions",
+ "dlmalloc",
"jni_platform_headers",
"libart_headers",
"libnativehelper_header_only",
],
- export_header_lib_headers: ["libart_headers"],
+ export_header_lib_headers: [
+ "dlmalloc",
+ "libart_headers",
+ ],
whole_static_libs: [
"libcpu_features",
],
shared_libs: [
"libartpalette",
- "libbacktrace",
"libbase", // For common macros.
"liblog",
"liblz4",
@@ -496,7 +513,6 @@
name: "libart_static_base_defaults",
whole_static_libs: [
"libartpalette",
- "libbacktrace",
"libbase",
"liblog",
"liblz4",
@@ -509,6 +525,12 @@
"libz",
],
target: {
+ host: {
+ cflags: [
+ "-fsanitize-address-use-after-return=never",
+ "-Wno-unused-command-line-argument",
+ ],
+ },
bionic: {
whole_static_libs: [
"libasync_safe", // libunwindstack dependency on Bionic.
@@ -561,6 +583,7 @@
"gc/allocator/rosalloc.h",
"gc/collector_type.h",
"gc/collector/gc_type.h",
+ "gc/collector/mark_compact.h",
"gc/space/region_space.h",
"gc/space/space.h",
"gc/weak_root_state.h",
@@ -569,6 +592,7 @@
"indirect_reference_table.h",
"jdwp_provider.h",
"jni_id_type.h",
+ "linear_alloc.h",
"lock_word.h",
"oat_file.h",
"process_state.h",
@@ -670,6 +694,14 @@
art_cc_defaults {
name: "libart-runtime-gtest-defaults",
+ target: {
+ host: {
+ cflags: [
+ "-fsanitize-address-use-after-return=never",
+ "-Wno-unused-command-line-argument",
+ ],
+ },
+ },
tidy_timeout_srcs: [
"common_runtime_test.cc",
],
@@ -679,9 +711,11 @@
],
shared_libs: [
"libbase",
+ "libz", // libziparchive dependency; must be repeated here since it's a static lib.
],
static_libs: [
"libprocinfo",
+ "libziparchive",
],
header_libs: [
"libnativehelper_header_only",
@@ -714,6 +748,14 @@
art_cc_defaults {
name: "art_runtime_tests_defaults",
+ target: {
+ host: {
+ cflags: [
+ "-fsanitize-address-use-after-return=never",
+ "-Wno-unused-command-line-argument",
+ ],
+ },
+ },
data: [
":art-gtest-jars-AllFields",
":art-gtest-jars-ErroneousA",
@@ -741,6 +783,7 @@
":art-gtest-jars-MyClass",
":art-gtest-jars-MyClassNatives",
":art-gtest-jars-Nested",
+ ":art-gtest-jars-NonStaticLeafMethods",
":art-gtest-jars-Packages",
":art-gtest-jars-ProfileTestMultiDex",
":art-gtest-jars-ProtoCompare",
@@ -773,13 +816,14 @@
srcs: [
"app_info_test.cc",
"arch/arch_test.cc",
+ "arch/arm/instruction_set_features_arm_test.cc",
+ "arch/arm64/instruction_set_features_arm64_test.cc",
"arch/instruction_set_features_test.cc",
"arch/memcmp16_test.cc",
"arch/stub_test.cc",
- "arch/arm/instruction_set_features_arm_test.cc",
- "arch/arm64/instruction_set_features_arm64_test.cc",
"arch/x86/instruction_set_features_x86_test.cc",
"arch/x86_64/instruction_set_features_x86_64_test.cc",
+ "art_method_test.cc",
"barrier_test.cc",
"base/message_queue_test.cc",
"base/mutex_test.cc",
@@ -799,12 +843,12 @@
"gc/heap_test.cc",
"gc/heap_verification_test.cc",
"gc/reference_queue_test.cc",
- "gc/space/dlmalloc_space_static_test.cc",
"gc/space/dlmalloc_space_random_test.cc",
+ "gc/space/dlmalloc_space_static_test.cc",
"gc/space/image_space_test.cc",
"gc/space/large_object_space_test.cc",
- "gc/space/rosalloc_space_static_test.cc",
"gc/space/rosalloc_space_random_test.cc",
+ "gc/space/rosalloc_space_static_test.cc",
"gc/space/space_create_test.cc",
"gc/system_weak_test.cc",
"gc/task_processor_test.cc",
@@ -817,11 +861,13 @@
"intern_table_test.cc",
"interpreter/safe_math_test.cc",
"interpreter/unstarted_runtime_test.cc",
+ "jit/jit_load_test.cc",
"jit/jit_memory_region_test.cc",
"jit/profile_saver_test.cc",
"jit/profiling_info_test.cc",
"jni/java_vm_ext_test.cc",
"jni/jni_internal_test.cc",
+ "jni/local_reference_table_test.cc",
"method_handles_test.cc",
"metrics/reporter_test.cc",
"mirror/dex_cache_test.cc",
@@ -830,12 +876,14 @@
"mirror/var_handle_test.cc",
"monitor_pool_test.cc",
"monitor_test.cc",
- "oat_file_test.cc",
+ "native_stack_dump_test.cc",
"oat_file_assistant_test.cc",
+ "oat_file_test.cc",
"parsed_options_test.cc",
"prebuilt_tools_test.cc",
"proxy_test.cc",
"reference_table_test.cc",
+ "reflection_test.cc",
"runtime_callbacks_test.cc",
"runtime_test.cc",
"subtype_check_info_test.cc",
@@ -848,7 +896,12 @@
"verifier/reg_type_test.cc",
],
shared_libs: [
- "libbacktrace",
+ "libunwindstack",
+ "libz", // libziparchive dependency; must be repeated here since it's a static lib.
+ ],
+ static_libs: [
+ "libgmock",
+ "libziparchive",
],
header_libs: [
"art_cmdlineparser_headers", // For parsed_options_test.
@@ -890,59 +943,6 @@
test_config: "art_standalone_runtime_tests.xml",
}
-art_cc_defaults {
- name: "art_runtime_compiler_tests_defaults",
- tidy_timeout_srcs: [
- "reflection_test.cc",
- ],
- srcs: [
- "reflection_test.cc",
- ],
- data: [
- ":art-gtest-jars-Main",
- ":art-gtest-jars-NonStaticLeafMethods",
- ":art-gtest-jars-StaticLeafMethods",
- ],
-}
-
-// Version of ART gtest `art_runtime_compiler_tests` bundled with the ART APEX on target.
-// TODO(b/192274705): Remove this module when the migration to standalone ART gtests is complete.
-art_cc_test {
- name: "art_runtime_compiler_tests",
- defaults: [
- "art_gtest_defaults",
- "art_runtime_compiler_tests_defaults",
- ],
- shared_libs: [
- "libartd-compiler",
- ],
- target: {
- host: {
- required: ["dex2oatd"],
- },
- },
-}
-
-// Standalone version of ART gtest `art_runtime_compiler_tests`, not bundled with the ART APEX on
-// target.
-art_cc_test {
- name: "art_standalone_runtime_compiler_tests",
- defaults: [
- "art_standalone_gtest_defaults",
- "art_runtime_compiler_tests_defaults",
- ],
- data: [":generate-boot-image"],
- shared_libs: [
- "libart-compiler",
- ],
- target: {
- host: {
- required: ["dex2oat"],
- },
- },
- test_config: "art_standalone_runtime_compiler_tests.xml",
-}
-
genrule {
name: "libart_mterp.x86ng",
out: ["mterp_x86ng.S"],
diff --git a/runtime/alloc_instrumentation.md b/runtime/alloc_instrumentation.md
index 513bbe3..66e9a6a 100644
--- a/runtime/alloc_instrumentation.md
+++ b/runtime/alloc_instrumentation.md
@@ -17,7 +17,7 @@
- These in turn are called by `SetStatsEnabled()`, `SetAllocationListener()`, et al, which
require the mutator lock is not held.
-- With a started runtime, `SetEntrypointsInstrumented()` calls `ScopedSupendAll(`) before updating
+- With a started runtime, `SetEntrypointsInstrumented()` calls `ScopedSuspendAll(`) before updating
the function table.
Mutual exclusion in the dispatch table is thus ensured by the fact that it is only updated while
diff --git a/runtime/app_info.cc b/runtime/app_info.cc
index c72951e..2dbbbf6 100644
--- a/runtime/app_info.cc
+++ b/runtime/app_info.cc
@@ -93,6 +93,12 @@
<< "\nodex_status=" << odex_status;
}
+bool AppInfo::HasRegisteredAppInfo() {
+ MutexLock mu(Thread::Current(), update_mutex_);
+
+ return package_name_.has_value();
+}
+
void AppInfo::GetPrimaryApkOptimizationStatus(
std::string* out_compiler_filter,
std::string* out_compilation_reason) {
@@ -110,6 +116,13 @@
*out_compilation_reason = kUnknownValue;
}
+AppInfo::CodeType AppInfo::GetRegisteredCodeType(const std::string& code_path) {
+ MutexLock mu(Thread::Current(), update_mutex_);
+
+ const auto it = registered_code_locations_.find(code_path);
+ return it != registered_code_locations_.end() ? it->second.code_type : CodeType::kUnknown;
+}
+
std::ostream& operator<<(std::ostream& os, AppInfo& rhs) {
MutexLock mu(Thread::Current(), rhs.update_mutex_);
diff --git a/runtime/app_info.h b/runtime/app_info.h
index 68f2c58..43e2ef3 100644
--- a/runtime/app_info.h
+++ b/runtime/app_info.h
@@ -77,6 +77,13 @@
void GetPrimaryApkOptimizationStatus(std::string* out_compiler_filter,
std::string* out_compilation_reason);
+ // Whether we've received a call to RegisterAppInfo.
+ bool HasRegisteredAppInfo();
+
+ // The registered code type for a given code path. Note that this will
+ // be kUnknown until an explicit registration for that path has been made.
+ CodeType GetRegisteredCodeType(const std::string& code_path);
+
private:
// Encapsulates optimization information about a particular code location.
struct CodeLocationInfo {
diff --git a/runtime/app_info_test.cc b/runtime/app_info_test.cc
index 4a365de..51dd42f 100644
--- a/runtime/app_info_test.cc
+++ b/runtime/app_info_test.cc
@@ -24,12 +24,17 @@
TEST(AppInfoTest, RegisterAppInfo) {
AppInfo app_info;
+ EXPECT_FALSE(app_info.HasRegisteredAppInfo());
+ EXPECT_EQ(app_info.GetRegisteredCodeType("code_location"), AppInfo::CodeType::kUnknown);
+
app_info.RegisterAppInfo(
"package_name",
std::vector<std::string>({"code_location"}),
"",
"",
AppInfo::CodeType::kPrimaryApk);
+ EXPECT_TRUE(app_info.HasRegisteredAppInfo());
+ EXPECT_EQ(app_info.GetRegisteredCodeType("code_location"), AppInfo::CodeType::kPrimaryApk);
std::string filter;
std::string reason;
@@ -48,11 +53,13 @@
"",
"",
AppInfo::CodeType::kPrimaryApk);
+ EXPECT_EQ(app_info.GetRegisteredCodeType("code_location"), AppInfo::CodeType::kPrimaryApk);
app_info.RegisterOdexStatus(
"code_location",
"filter",
"reason",
"odex_status");
+ EXPECT_EQ(app_info.GetRegisteredCodeType("code_location"), AppInfo::CodeType::kPrimaryApk);
std::string filter;
std::string reason;
@@ -69,17 +76,22 @@
"filter",
"reason",
"odex_status");
+ EXPECT_FALSE(app_info.HasRegisteredAppInfo());
app_info.RegisterOdexStatus(
"code_location2",
"filter2",
"reason2",
"odex_status");
+ EXPECT_FALSE(app_info.HasRegisteredAppInfo());
app_info.RegisterAppInfo(
"package_name",
std::vector<std::string>({"code_location"}),
"",
"",
AppInfo::CodeType::kPrimaryApk);
+ EXPECT_TRUE(app_info.HasRegisteredAppInfo());
+ EXPECT_EQ(app_info.GetRegisteredCodeType("code_location"), AppInfo::CodeType::kPrimaryApk);
+ EXPECT_EQ(app_info.GetRegisteredCodeType("code_location2"), AppInfo::CodeType::kUnknown);
std::string filter;
std::string reason;
@@ -110,7 +122,7 @@
"filter",
"reason",
"odex_status");
-
+ EXPECT_EQ(app_info.GetRegisteredCodeType("code_location"), AppInfo::CodeType::kSplitApk);
// The optimization status is unknown since we don't have primary apks.
app_info.GetPrimaryApkOptimizationStatus(&filter, &reason);
diff --git a/runtime/arch/arch_test.cc b/runtime/arch/arch_test.cc
index 23213d9..83fd986 100644
--- a/runtime/arch/arch_test.cc
+++ b/runtime/arch/arch_test.cc
@@ -18,27 +18,13 @@
#include "art_method-inl.h"
#include "base/callee_save_type.h"
+#include "base/common_art_test.h"
#include "entrypoints/quick/callee_save_frame.h"
-#include "common_runtime_test.h"
#include "quick/quick_method_frame_info.h"
namespace art {
-class ArchTest : public CommonRuntimeTest {
- protected:
- void SetUpRuntimeOptions(RuntimeOptions *options) override {
- // Use 64-bit ISA for runtime setup to make method size potentially larger
- // than necessary (rather than smaller) during CreateCalleeSaveMethod
- options->push_back(std::make_pair("imageinstructionset", "x86_64"));
- }
-
- // Do not do any of the finalization. We don't want to run any code, we don't need the heap
- // prepared, it actually will be a problem with setting the instruction set to x86_64 in
- // SetUpRuntimeOptions.
- void FinalizeSetup() override {
- ASSERT_EQ(InstructionSet::kX86_64, Runtime::Current()->GetInstructionSet());
- }
-};
+class ArchTest : public CommonArtTest {};
// Grab architecture specific constants.
namespace arm {
diff --git a/runtime/arch/arm/asm_support_arm.S b/runtime/arch/arm/asm_support_arm.S
index 23d82ba..7f1f470 100644
--- a/runtime/arch/arm/asm_support_arm.S
+++ b/runtime/arch/arm/asm_support_arm.S
@@ -27,10 +27,8 @@
// Register holding Thread::Current().
#define rSELF r9
-#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
+#ifdef RESERVE_MARKING_REGISTER
// Marking Register, holding Thread::Current()->GetIsGcMarking().
-// Only used with the Concurrent Copying (CC) garbage
-// collector, with the Baker read barrier configuration.
#define rMR r8
#endif
@@ -160,7 +158,7 @@
// entrypoints that possibly (directly or indirectly) perform a
// suspend check (before they return).
.macro REFRESH_MARKING_REGISTER
-#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
+#ifdef RESERVE_MARKING_REGISTER
ldr rMR, [rSELF, #THREAD_IS_GC_MARKING_OFFSET]
#endif
.endm
@@ -315,10 +313,6 @@
DELIVER_PENDING_EXCEPTION
.endm
-.macro RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
- RETURN_OR_DELIVER_PENDING_EXCEPTION_REG r1
-.endm
-
.macro RETURN_OR_DELIVER_PENDING_EXCEPTION
ldr ip, [rSELF, #THREAD_EXCEPTION_OFFSET] @ Get exception field.
cmp ip, #0
diff --git a/runtime/arch/arm/asm_support_arm.h b/runtime/arch/arm/asm_support_arm.h
index aff055e..a3d46c2 100644
--- a/runtime/arch/arm/asm_support_arm.h
+++ b/runtime/arch/arm/asm_support_arm.h
@@ -25,6 +25,8 @@
#define FRAME_SIZE_SAVE_EVERYTHING 192
#define FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT FRAME_SIZE_SAVE_EVERYTHING
#define FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK FRAME_SIZE_SAVE_EVERYTHING
+#define SAVE_EVERYTHING_FRAME_R0_OFFSET \
+ (FRAME_SIZE_SAVE_EVERYTHING - CALLEE_SAVE_EVERYTHING_NUM_CORE_SPILLS * POINTER_SIZE)
// The offset from the art_quick_read_barrier_mark_introspection (used for field
// loads with 32-bit LDR) to the entrypoint for field loads with 16-bit LDR,
@@ -43,22 +45,22 @@
// The offset of the reference load LDR from the return address in LR for field loads.
#ifdef USE_HEAP_POISONING
-#define BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET -8
-#define BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET -4
+#define BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET (-8)
+#define BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET (-4)
#else
-#define BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET -4
-#define BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET -2
+#define BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET (-4)
+#define BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET (-2)
#endif
// The offset of the reference load LDR from the return address in LR for array loads.
#ifdef USE_HEAP_POISONING
-#define BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET -8
+#define BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET (-8)
#else
-#define BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET -4
+#define BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET (-4)
#endif
// The offset of the reference load LDR from the return address in LR for GC root loads.
-#define BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_OFFSET -8
-#define BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET -6
+#define BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_OFFSET (-8)
+#define BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET (-6)
// The offset of the MOV from the return address in LR for intrinsic CAS.
-#define BAKER_MARK_INTROSPECTION_INTRINSIC_CAS_MOV_OFFSET -8
+#define BAKER_MARK_INTROSPECTION_INTRINSIC_CAS_MOV_OFFSET (-8)
#endif // ART_RUNTIME_ARCH_ARM_ASM_SUPPORT_ARM_H_
diff --git a/runtime/arch/arm/entrypoints_init_arm.cc b/runtime/arch/arm/entrypoints_init_arm.cc
index b0b0064..555babe 100644
--- a/runtime/arch/arm/entrypoints_init_arm.cc
+++ b/runtime/arch/arm/entrypoints_init_arm.cc
@@ -91,7 +91,7 @@
qpoints->SetReadBarrierMarkReg10(is_active ? art_quick_read_barrier_mark_reg10 : nullptr);
qpoints->SetReadBarrierMarkReg11(is_active ? art_quick_read_barrier_mark_reg11 : nullptr);
- if (kUseReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
// For the alignment check, strip the Thumb mode bit.
DCHECK_ALIGNED(reinterpret_cast<intptr_t>(art_quick_read_barrier_mark_introspection) - 1u,
256u);
diff --git a/runtime/arch/arm/fault_handler_arm.cc b/runtime/arch/arm/fault_handler_arm.cc
index 7bd402f..974e056 100644
--- a/runtime/arch/arm/fault_handler_arm.cc
+++ b/runtime/arch/arm/fault_handler_arm.cc
@@ -45,76 +45,59 @@
return instr_size;
}
-void FaultManager::GetMethodAndReturnPcAndSp(siginfo_t* siginfo ATTRIBUTE_UNUSED,
- void* context,
- ArtMethod** out_method,
- uintptr_t* out_return_pc,
- uintptr_t* out_sp,
- bool* out_is_stack_overflow) {
+uintptr_t FaultManager::GetFaultPc(siginfo_t* siginfo ATTRIBUTE_UNUSED, void* context) {
struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
- struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
- *out_sp = static_cast<uintptr_t>(sc->arm_sp);
- VLOG(signals) << "sp: " << std::hex << *out_sp;
- if (*out_sp == 0) {
- return;
+ struct sigcontext* sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
+ if (sc->arm_sp == 0) {
+ VLOG(signals) << "Missing SP";
+ return 0u;
}
+ return sc->arm_pc;
+}
- // In the case of a stack overflow, the stack is not valid and we can't
- // get the method from the top of the stack. However it's in r0.
- uintptr_t* fault_addr = reinterpret_cast<uintptr_t*>(sc->fault_address);
- uintptr_t* overflow_addr = reinterpret_cast<uintptr_t*>(
- reinterpret_cast<uint8_t*>(*out_sp) - GetStackOverflowReservedBytes(InstructionSet::kArm));
- if (overflow_addr == fault_addr) {
- *out_method = reinterpret_cast<ArtMethod*>(sc->arm_r0);
- *out_is_stack_overflow = true;
- } else {
- // The method is at the top of the stack.
- *out_method = reinterpret_cast<ArtMethod*>(reinterpret_cast<uintptr_t*>(*out_sp)[0]);
- *out_is_stack_overflow = false;
- }
-
- // Work out the return PC. This will be the address of the instruction
- // following the faulting ldr/str instruction. This is in thumb mode so
- // the instruction might be a 16 or 32 bit one. Also, the GC map always
- // has the bottom bit of the PC set so we also need to set that.
-
- // Need to work out the size of the instruction that caused the exception.
- uint8_t* ptr = reinterpret_cast<uint8_t*>(sc->arm_pc);
- VLOG(signals) << "pc: " << std::hex << static_cast<void*>(ptr);
-
- if (ptr == nullptr) {
- // Somebody jumped to 0x0. Definitely not ours, and will definitely segfault below.
- *out_method = nullptr;
- return;
- }
-
- uint32_t instr_size = GetInstructionSize(ptr);
-
- *out_return_pc = (sc->arm_pc + instr_size) | 1;
+uintptr_t FaultManager::GetFaultSp(void* context) {
+ struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
+ struct sigcontext* sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
+ return sc->arm_sp;
}
bool NullPointerHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info, void* context) {
- if (!IsValidImplicitCheck(info)) {
+ uintptr_t fault_address = reinterpret_cast<uintptr_t>(info->si_addr);
+ if (!IsValidFaultAddress(fault_address)) {
return false;
}
- // The code that looks for the catch location needs to know the value of the
- // ARM PC at the point of call. For Null checks we insert a GC map that is immediately after
- // the load/store instruction that might cause the fault. However the mapping table has
- // the low bits set for thumb mode so we need to set the bottom bit for the LR
- // register in order to find the mapping.
+
+ struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
+ struct sigcontext* sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
+ ArtMethod** sp = reinterpret_cast<ArtMethod**>(sc->arm_sp);
+ if (!IsValidMethod(*sp)) {
+ return false;
+ }
+
+ // For null checks in compiled code we insert a stack map that is immediately
+ // after the load/store instruction that might cause the fault and we need to
+ // pass the return PC to the handler. For null checks in Nterp, we similarly
+ // need the return PC to recognize that this was a null check in Nterp, so
+ // that the handler can get the needed data from the Nterp frame.
+
+ // Note: Currently, Nterp is compiled to the A32 instruction set and managed
+ // code is compiled to the T32 instruction set.
+ // To find the stack map for compiled code, we need to set the bottom bit in
+ // the return PC indicating T32 just like we would if we were going to return
+ // to that PC (though we're going to jump to the exception handler instead).
// Need to work out the size of the instruction that caused the exception.
- struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
- struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
uint8_t* ptr = reinterpret_cast<uint8_t*>(sc->arm_pc);
bool in_thumb_mode = sc->arm_cpsr & (1 << 5);
uint32_t instr_size = in_thumb_mode ? GetInstructionSize(ptr) : 4;
- uintptr_t gc_map_location = (sc->arm_pc + instr_size) | (in_thumb_mode ? 1 : 0);
+ uintptr_t return_pc = (sc->arm_pc + instr_size) | (in_thumb_mode ? 1 : 0);
- // Push the gc map location to the stack and pass the fault address in LR.
+ // Push the return PC to the stack and pass the fault address in LR.
sc->arm_sp -= sizeof(uintptr_t);
- *reinterpret_cast<uintptr_t*>(sc->arm_sp) = gc_map_location;
- sc->arm_lr = reinterpret_cast<uintptr_t>(info->si_addr);
+ *reinterpret_cast<uintptr_t*>(sc->arm_sp) = return_pc;
+ sc->arm_lr = fault_address;
+
+ // Arrange for the signal handler to return to the NPE entrypoint.
sc->arm_pc = reinterpret_cast<uintptr_t>(art_quick_throw_null_pointer_exception_from_signal);
// Make sure the thumb bit is set as the handler is in thumb mode.
sc->arm_cpsr = sc->arm_cpsr | (1 << 5);
diff --git a/runtime/arch/arm/jni_entrypoints_arm.S b/runtime/arch/arm/jni_entrypoints_arm.S
index fc57df7..d91882c 100644
--- a/runtime/arch/arm/jni_entrypoints_arm.S
+++ b/runtime/arch/arm/jni_entrypoints_arm.S
@@ -99,7 +99,7 @@
// Call artFindNativeMethod() for normal native and artFindNativeMethodRunnable()
// for @FastNative or @CriticalNative.
ldr ip, [r0, #THREAD_TOP_QUICK_FRAME_OFFSET] // uintptr_t tagged_quick_frame
- bic ip, #1 // ArtMethod** sp
+ bic ip, #TAGGED_JNI_SP_MASK // ArtMethod** sp
ldr ip, [ip] // ArtMethod* method
ldr ip, [ip, #ART_METHOD_ACCESS_FLAGS_OFFSET] // uint32_t access_flags
tst ip, #(ACCESS_FLAGS_METHOD_IS_FAST_NATIVE | ACCESS_FLAGS_METHOD_IS_CRITICAL_NATIVE)
@@ -327,6 +327,11 @@
JNI_SAVE_MANAGED_ARGS_TRAMPOLINE art_jni_method_start, artJniMethodStart, rSELF
/*
+ * Trampoline to `artJniMethodEntryHook()` that preserves all managed arguments.
+ */
+JNI_SAVE_MANAGED_ARGS_TRAMPOLINE art_jni_method_entry_hook, artJniMethodEntryHook, rSELF
+
+ /*
* Trampoline to `artJniMonitoredMethodStart()` that preserves all managed arguments.
*/
JNI_SAVE_MANAGED_ARGS_TRAMPOLINE art_jni_monitored_method_start, artJniMonitoredMethodStart, rSELF
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index d6f129b..2656146 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -134,16 +134,50 @@
.cfi_adjust_cfa_offset -52
.endm
-.macro RETURN_IF_RESULT_IS_ZERO
- cbnz r0, 1f @ result non-zero branch over
- bx lr @ return
+.macro RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ ldr r1, [rSELF, # THREAD_EXCEPTION_OFFSET] // Get exception field.
+ cbnz r1, 1f
+ DEOPT_OR_RETURN r1 // Check if deopt is required
1:
+ DELIVER_PENDING_EXCEPTION
.endm
-.macro RETURN_IF_RESULT_IS_NON_ZERO
- cbz r0, 1f @ result zero branch over
- bx lr @ return
-1:
+.macro DEOPT_OR_RETURN temp, is_ref = 0
+ ldr \temp, [rSELF, #THREAD_DEOPT_CHECK_REQUIRED_OFFSET]
+ cbnz \temp, 2f
+ bx lr
+2:
+ SETUP_SAVE_EVERYTHING_FRAME \temp
+ mov r2, \is_ref // pass if result is a reference
+ mov r1, r0 // pass the result
+ mov r0, rSELF // Thread::Current
+ bl artDeoptimizeIfNeeded
+ .cfi_remember_state
+ RESTORE_SAVE_EVERYTHING_FRAME
+ REFRESH_MARKING_REGISTER
+ bx lr
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
+.endm
+
+.macro DEOPT_OR_RESTORE_SAVE_EVERYTHING_FRAME_AND_RETURN_R0 temp, is_ref
+ ldr \temp, [rSELF, #THREAD_DEOPT_CHECK_REQUIRED_OFFSET]
+ cbnz \temp, 2f
+ .cfi_remember_state
+ RESTORE_SAVE_EVERYTHING_FRAME_KEEP_R0
+ REFRESH_MARKING_REGISTER
+ bx lr
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
+2:
+ str r0, [sp, SAVE_EVERYTHING_FRAME_R0_OFFSET] // update result in the frame
+ mov r2, \is_ref // pass if result is a reference
+ mov r1, r0 // pass the result
+ mov r0, rSELF // Thread::Current
+ bl artDeoptimizeIfNeeded
+ .cfi_remember_state
+ RESTORE_SAVE_EVERYTHING_FRAME
+ REFRESH_MARKING_REGISTER
+ bx lr
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
.endm
.macro NO_ARG_RUNTIME_EXCEPTION c_name, cxx_name
@@ -183,12 +217,16 @@
.endm
.macro RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
- RETURN_IF_RESULT_IS_ZERO
+ cbnz r0, 1f @ result non-zero branch over
+ DEOPT_OR_RETURN r1
+1:
DELIVER_PENDING_EXCEPTION
.endm
-.macro RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
- RETURN_IF_RESULT_IS_NON_ZERO
+.macro RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
+ cbz r0, 1f @ result zero branch over
+ DEOPT_OR_RETURN r1, /*is_ref=*/1
+1:
DELIVER_PENDING_EXCEPTION
.endm
@@ -517,8 +555,7 @@
bl artLockObjectFromCode @ (Object* obj, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- RETURN_IF_RESULT_IS_ZERO
- DELIVER_PENDING_EXCEPTION
+ RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
END art_quick_lock_object_no_inline
/*
@@ -548,8 +585,7 @@
bl artUnlockObjectFromCode @ (Object* obj, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- RETURN_IF_RESULT_IS_ZERO
- DELIVER_PENDING_EXCEPTION
+ RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
END art_quick_unlock_object_no_inline
/*
@@ -601,13 +637,33 @@
.cfi_rel_offset \rReg, \offset
.endm
- /*
- * Macro to insert read barrier, only used in art_quick_aput_obj.
- * rObj and rDest are registers, offset is a defined literal such as MIRROR_OBJECT_CLASS_OFFSET.
- * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path.
- */
-.macro READ_BARRIER rDest, rObj, offset
+ // Helper macros for `art_quick_aput_obj`.
#ifdef USE_READ_BARRIER
+#ifdef USE_BAKER_READ_BARRIER
+.macro BAKER_RB_CHECK_GRAY_BIT_AND_LOAD rDest, rObj, offset, gray_slow_path_label
+ ldr ip, [\rObj, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
+ tst ip, #LOCK_WORD_READ_BARRIER_STATE_MASK_SHIFTED
+ bne \gray_slow_path_label
+ // False dependency to avoid needing load/load fence.
+ add \rObj, \rObj, ip, lsr #32
+ ldr \rDest, [\rObj, #\offset]
+ UNPOISON_HEAP_REF \rDest
+.endm
+
+.macro BAKER_RB_LOAD_AND_MARK rDest, rObj, offset, mark_function
+ ldr \rDest, [\rObj, #\offset]
+ UNPOISON_HEAP_REF \rDest
+ str lr, [sp, #-8]! @ Save LR with correct stack alignment.
+ .cfi_rel_offset lr, 0
+ .cfi_adjust_cfa_offset 8
+ bl \mark_function
+ ldr lr, [sp], #8 @ Restore LR.
+ .cfi_restore lr
+ .cfi_adjust_cfa_offset -8
+.endm
+#else // USE_BAKER_READ_BARRIER
+ .extern artReadBarrierSlow
+.macro READ_BARRIER_SLOW rDest, rObj, offset
push {r0-r3, ip, lr} @ 6 words for saved registers (used in art_quick_aput_obj)
.cfi_adjust_cfa_offset 24
.cfi_rel_offset r0, 0
@@ -640,30 +696,36 @@
pop {lr} @ restore lr
.cfi_adjust_cfa_offset -4
.cfi_restore lr
-#else
- ldr \rDest, [\rObj, #\offset]
- UNPOISON_HEAP_REF \rDest
-#endif // USE_READ_BARRIER
.endm
+#endif // USE_BAKER_READ_BARRIER
+#endif // USE_READ_BARRIER
-#ifdef USE_READ_BARRIER
- .extern artReadBarrierSlow
-#endif
.hidden art_quick_aput_obj
ENTRY art_quick_aput_obj
-#ifdef USE_READ_BARRIER
- @ The offset to .Ldo_aput_null is too large to use cbz due to expansion from READ_BARRIER macro.
+#if defined(USE_READ_BARRIER) && !defined(USE_BAKER_READ_BARRIER)
+ @ The offset to .Ldo_aput_null is too large to use cbz due to expansion from `READ_BARRIER_SLOW`.
tst r2, r2
- beq .Ldo_aput_null
-#else
- cbz r2, .Ldo_aput_null
+ beq .Laput_obj_null
+ READ_BARRIER_SLOW r3, r0, MIRROR_OBJECT_CLASS_OFFSET
+ READ_BARRIER_SLOW r3, r3, MIRROR_CLASS_COMPONENT_TYPE_OFFSET
+ READ_BARRIER_SLOW r4, r2, MIRROR_OBJECT_CLASS_OFFSET
+#else // !defined(USE_READ_BARRIER) || defined(USE_BAKER_READ_BARRIER)
+ cbz r2, .Laput_obj_null
+#ifdef USE_READ_BARRIER
+ cmp rMR, #0
+ bne .Laput_obj_gc_marking
#endif // USE_READ_BARRIER
- READ_BARRIER r3, r0, MIRROR_OBJECT_CLASS_OFFSET
- READ_BARRIER ip, r2, MIRROR_OBJECT_CLASS_OFFSET
- READ_BARRIER r3, r3, MIRROR_CLASS_COMPONENT_TYPE_OFFSET
- cmp r3, ip @ value's type == array's component type - trivial assignability
- bne .Lcheck_assignability
-.Ldo_aput:
+ ldr r3, [r0, #MIRROR_OBJECT_CLASS_OFFSET]
+ UNPOISON_HEAP_REF r3
+ // R4 is a scratch register in managed ARM ABI.
+ ldr r4, [r2, #MIRROR_OBJECT_CLASS_OFFSET]
+ UNPOISON_HEAP_REF r4
+ ldr r3, [r3, #MIRROR_CLASS_COMPONENT_TYPE_OFFSET]
+ UNPOISON_HEAP_REF r3
+#endif // !defined(USE_READ_BARRIER) || defined(USE_BAKER_READ_BARRIER)
+ cmp r3, r4 @ value's type == array's component type - trivial assignability
+ bne .Laput_obj_check_assignability
+.Laput_obj_store:
add r3, r0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
POISON_HEAP_REF r2
str r2, [r3, r1, lsl #2]
@@ -671,26 +733,22 @@
lsr r0, r0, #CARD_TABLE_CARD_SHIFT
strb r3, [r3, r0]
blx lr
-.Ldo_aput_null:
+
+.Laput_obj_null:
add r3, r0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
str r2, [r3, r1, lsl #2]
blx lr
-.Lcheck_assignability:
+
+.Laput_obj_check_assignability:
push {r0-r2, lr} @ save arguments
.cfi_adjust_cfa_offset 16
- .cfi_rel_offset r0, 0
- .cfi_rel_offset r1, 4
- .cfi_rel_offset r2, 8
.cfi_rel_offset lr, 12
- mov r1, ip
+ mov r1, r4
mov r0, r3
bl artIsAssignableFromCode
cbz r0, .Lthrow_array_store_exception
.cfi_remember_state
pop {r0-r2, lr}
- .cfi_restore r0
- .cfi_restore r1
- .cfi_restore r2
.cfi_restore lr
.cfi_adjust_cfa_offset -16
add r3, r0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
@@ -700,19 +758,52 @@
lsr r0, r0, #CARD_TABLE_CARD_SHIFT
strb r3, [r3, r0]
blx lr
+
.Lthrow_array_store_exception:
CFI_RESTORE_STATE_AND_DEF_CFA sp, 16
pop {r0-r2, lr}
- .cfi_restore r0
- .cfi_restore r1
- .cfi_restore r2
.cfi_restore lr
.cfi_adjust_cfa_offset -16
+#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
+ .cfi_remember_state
+#endif // defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
SETUP_SAVE_ALL_CALLEE_SAVES_FRAME r3
mov r1, r2
- mov r2, rSELF @ pass Thread::Current
+ mov r2, rSELF @ Pass Thread::Current.
bl artThrowArrayStoreException @ (Class*, Class*, Thread*)
- bkpt @ unreached
+ bkpt @ Unreachable.
+
+#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, 0
+.Laput_obj_gc_marking:
+ BAKER_RB_CHECK_GRAY_BIT_AND_LOAD \
+ r3, r0, MIRROR_OBJECT_CLASS_OFFSET, .Laput_obj_mark_array_class
+.Laput_obj_mark_array_class_continue:
+ BAKER_RB_CHECK_GRAY_BIT_AND_LOAD \
+ r3, r3, MIRROR_CLASS_COMPONENT_TYPE_OFFSET, .Laput_obj_mark_array_element
+.Laput_obj_mark_array_element_continue:
+ BAKER_RB_CHECK_GRAY_BIT_AND_LOAD \
+ r4, r2, MIRROR_OBJECT_CLASS_OFFSET, .Laput_obj_mark_object_class
+.Laput_obj_mark_object_class_continue:
+
+ cmp r3, r4 @ value's type == array's component type - trivial assignability
+ // All registers are set up for correctly `.Laput_obj_check_assignability`.
+ bne .Laput_obj_check_assignability
+ b .Laput_obj_store
+
+.Laput_obj_mark_array_class:
+ BAKER_RB_LOAD_AND_MARK r3, r0, MIRROR_OBJECT_CLASS_OFFSET, art_quick_read_barrier_mark_reg03
+ b .Laput_obj_mark_array_class_continue
+
+.Laput_obj_mark_array_element:
+ BAKER_RB_LOAD_AND_MARK \
+ r3, r3, MIRROR_CLASS_COMPONENT_TYPE_OFFSET, art_quick_read_barrier_mark_reg03
+ b .Laput_obj_mark_array_element_continue
+
+.Laput_obj_mark_object_class:
+ BAKER_RB_LOAD_AND_MARK r4, r2, MIRROR_OBJECT_CLASS_OFFSET, art_quick_read_barrier_mark_reg04
+ b .Laput_obj_mark_object_class_continue
+#endif // defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
END art_quick_aput_obj
// Macro to facilitate adding new allocation entrypoints.
@@ -782,11 +873,8 @@
mov r1, rSELF @ pass Thread::Current
bl \entrypoint @ (uint32_t index, Thread*)
cbz r0, 1f @ If result is null, deliver the OOME.
- .cfi_remember_state
- RESTORE_SAVE_EVERYTHING_FRAME_KEEP_R0
- REFRESH_MARKING_REGISTER
- bx lr
- CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
+ str r0, [sp, #136] @ store result in the frame
+ DEOPT_OR_RESTORE_SAVE_EVERYTHING_FRAME_AND_RETURN_R0 r1, /* is_ref= */ 1
1:
DELIVER_PENDING_EXCEPTION_FRAME_READY
END \name
@@ -809,12 +897,12 @@
/*
* Called by managed code to resolve a static field and load a non-wide value.
*/
-ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
-ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
-ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
-ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
-ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
-ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
/*
* Called by managed code to resolve a static field and load a 64-bit primitive value.
*/
@@ -827,7 +915,7 @@
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
cbnz r2, 1f @ success if no exception pending
- bx lr @ return on success
+ DEOPT_OR_RETURN r2 @ check if deopt is required or return
1:
DELIVER_PENDING_EXCEPTION
END art_quick_get64_static
@@ -835,12 +923,12 @@
/*
* Called by managed code to resolve an instance field and load a non-wide value.
*/
-TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
-TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
-TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
-TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
-TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
-TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_R1
+TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
/*
* Called by managed code to resolve an instance field and load a 64-bit primitive value.
*/
@@ -853,7 +941,7 @@
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
cbnz r2, 1f @ success if no exception pending
- bx lr @ return on success
+ DEOPT_OR_RETURN r2 @ check if deopt is required or return
1:
DELIVER_PENDING_EXCEPTION
END art_quick_get64_instance
@@ -888,8 +976,7 @@
.cfi_adjust_cfa_offset -16
RESTORE_SAVE_REFS_ONLY_FRAME @ TODO: we can clearly save an add here
REFRESH_MARKING_REGISTER
- RETURN_IF_RESULT_IS_ZERO
- DELIVER_PENDING_EXCEPTION
+ RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
END art_quick_set64_instance
.extern artSet64StaticFromCompiledCode
@@ -903,8 +990,7 @@
.cfi_adjust_cfa_offset -16
RESTORE_SAVE_REFS_ONLY_FRAME @ TODO: we can clearly save an add here
REFRESH_MARKING_REGISTER
- RETURN_IF_RESULT_IS_ZERO
- DELIVER_PENDING_EXCEPTION
+ RETURN_IF_RESULT_IS_ZERO_OR_DELIVER
END art_quick_set64_static
// Generate the allocation entrypoints for each allocator.
@@ -1037,7 +1123,7 @@
bl \cxx_name @ (mirror::Class* cls, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
END \c_name
.endm
@@ -1096,10 +1182,6 @@
str r1, [rSELF, #THREAD_LOCAL_OBJECTS_OFFSET]
POISON_HEAP_REF r0
str r0, [r2, #MIRROR_OBJECT_CLASS_OFFSET] // Store the class pointer.
- // Fence. This is "ish" not "ishst" so
- // that the code after this allocation
- // site will see the right values in
- // the fields of the class.
mov r0, r2
// No barrier. The class is already observably initialized (otherwise the fast
// path size check above would fail) and new-instance allocations are protected
@@ -1122,7 +1204,7 @@
bl \entrypoint // (mirror::Class* klass, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
END \name
.endm
@@ -1162,10 +1244,6 @@
POISON_HEAP_REF r0
str r0, [r3, #MIRROR_OBJECT_CLASS_OFFSET] // Store the class pointer.
str r1, [r3, #MIRROR_ARRAY_LENGTH_OFFSET] // Store the array length.
- // Fence. This is "ish" not "ishst" so
- // that the code after this allocation
- // site will see the right values in
- // the fields of the class.
mov r0, r3
// new-array is special. The class is loaded and immediately goes to the Initialized state
// before it is published. Therefore the only fence needed is for the publication of the object.
@@ -1195,7 +1273,7 @@
bl \entrypoint
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
END \name
.endm
@@ -1448,16 +1526,27 @@
.cfi_remember_state
.cfi_def_cfa_register sp
+ // store into fpr, for when it's a fpr return...
+ vmov d0, r0, r1
+
+ LOAD_RUNTIME_INSTANCE r2
+ ldr r2, [r2, #INSTRUMENTATION_STUBS_INSTALLED_OFFSET_FROM_RUNTIME_INSTANCE]
+ cbnz r2, .Lcall_method_exit_hook
+.Lcall_method_exit_hook_done:
+
// Tear down the callee-save frame. Skip arg registers.
add sp, #FRAME_SIZE_SAVE_REFS_AND_ARGS-FRAME_SIZE_SAVE_REFS_ONLY
.cfi_adjust_cfa_offset -(FRAME_SIZE_SAVE_REFS_AND_ARGS-FRAME_SIZE_SAVE_REFS_ONLY)
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- // store into fpr, for when it's a fpr return...
- vmov d0, r0, r1
bx lr // ret
+.Lcall_method_exit_hook:
+ mov r2, #FRAME_SIZE_SAVE_REFS_AND_ARGS
+ bl art_quick_method_exit_hook
+ b .Lcall_method_exit_hook_done
+
// Undo the unwinding information from above since it doesn't apply below.
CFI_RESTORE_STATE_AND_DEF_CFA r10, FRAME_SIZE_SAVE_REFS_AND_ARGS
.Lexception_in_native:
@@ -1883,7 +1972,7 @@
bl artStringBuilderAppend @ (uint32_t, const unit32_t*, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
END art_quick_string_builder_append
/*
@@ -1895,13 +1984,9 @@
*
* If `reg` is different from `r0`, the generated function follows a
* non-standard runtime calling convention:
- * - register `reg` is used to pass the (sole) argument of this
- * function (instead of R0);
- * - register `reg` is used to return the result of this function
- * (instead of R0);
- * - R0 is treated like a normal (non-argument) caller-save register;
- * - everything else is the same as in the standard runtime calling
- * convention (e.g. standard callee-save registers are preserved).
+ * - register `reg` (which may be different from R0) is used to pass the (sole) argument,
+ * - register `reg` (which may be different from R0) is used to return the result,
+ * - all other registers are callee-save (the values they hold are preserved).
*/
.macro READ_BARRIER_MARK_REG name, reg
ENTRY \name
@@ -2481,29 +2566,27 @@
SETUP_SAVE_EVERYTHING_FRAME r0
ldr r0, [sp, FRAME_SIZE_SAVE_EVERYTHING] @ pass ArtMethod
mov r1, rSELF @ pass Thread::Current
- bl artMethodEntryHook @ (ArtMethod*, Thread*)
+ mov r2, sp @ pass SP
+ bl artMethodEntryHook @ (ArtMethod*, Thread*, SP)
RESTORE_SAVE_EVERYTHING_FRAME
REFRESH_MARKING_REGISTER
blx lr
END art_quick_method_entry_hook
ENTRY art_quick_method_exit_hook
- SETUP_SAVE_EVERYTHING_FRAME r2
+ SETUP_SAVE_EVERYTHING_FRAME r5
- add r3, sp, #8 @ store fpr_res pointer, in kSaveEverything frame
- add r2, sp, #136 @ store gpr_res pointer, in kSaveEverything frame
- ldr r1, [sp, #FRAME_SIZE_SAVE_EVERYTHING] @ pass ArtMethod*
+ sub sp, #4 @ align stack
+ push {r2} @ pass frame_size stack
+ add r3, sp, #(8 + 8) @ store fpr_res pointer, in kSaveEverything frame
+ add r2, sp, #(136 + 8) @ store gpr_res pointer, in kSaveEverything frame
+ add r1, sp, #(FRAME_SIZE_SAVE_EVERYTHING + 8) @ pass ArtMethod**
mov r0, rSELF @ pass Thread::Current
- blx artMethodExitHook @ (Thread*, ArtMethod*, gpr_res*, fpr_res*)
+ blx artMethodExitHook @ (Thread*, ArtMethod**, gpr_res*, fpr_res*,
+ @ frame_size)
- .cfi_remember_state
- cbnz r0, .Ldo_deliver_instrumentation_exception_exit @ Deliver exception
-
- // Normal return.
+ add sp, #8 @ pop arguments on stack
RESTORE_SAVE_EVERYTHING_FRAME
REFRESH_MARKING_REGISTER
blx lr
-.Ldo_deliver_instrumentation_exception_exit:
- CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
- DELIVER_PENDING_EXCEPTION_FRAME_READY
END art_quick_method_exit_hook
diff --git a/runtime/arch/arm/quick_entrypoints_cc_arm.cc b/runtime/arch/arm/quick_entrypoints_cc_arm.cc
index 987b459..d7fef6f 100644
--- a/runtime/arch/arm/quick_entrypoints_cc_arm.cc
+++ b/runtime/arch/arm/quick_entrypoints_cc_arm.cc
@@ -25,6 +25,7 @@
uint32_t*);
template <bool kIsStatic>
+NO_STACK_PROTECTOR
static void quick_invoke_reg_setup(ArtMethod* method, uint32_t* args, uint32_t args_size,
Thread* self, JValue* result, const char* shorty) {
// Note: We do not follow aapcs ABI in quick code for both softfp and hardfp.
@@ -96,6 +97,7 @@
// Called by art::ArtMethod::Invoke to do entry into a non-static method.
// TODO: migrate into an assembly implementation as with ARM64.
+NO_STACK_PROTECTOR
extern "C" void art_quick_invoke_stub(ArtMethod* method, uint32_t* args, uint32_t args_size,
Thread* self, JValue* result, const char* shorty) {
quick_invoke_reg_setup<false>(method, args, args_size, self, result, shorty);
@@ -103,6 +105,7 @@
// Called by art::ArtMethod::Invoke to do entry into a static method.
// TODO: migrate into an assembly implementation as with ARM64.
+NO_STACK_PROTECTOR
extern "C" void art_quick_invoke_static_stub(ArtMethod* method, uint32_t* args,
uint32_t args_size, Thread* self, JValue* result,
const char* shorty) {
diff --git a/runtime/arch/arm64/asm_support_arm64.S b/runtime/arch/arm64/asm_support_arm64.S
index ca6b6fd..7210262 100644
--- a/runtime/arch/arm64/asm_support_arm64.S
+++ b/runtime/arch/arm64/asm_support_arm64.S
@@ -34,10 +34,8 @@
#define xIP1 x17
#define wIP1 w17
-#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
+#ifdef RESERVE_MARKING_REGISTER
// Marking Register, holding Thread::Current()->GetIsGcMarking().
-// Only used with the Concurrent Copying (CC) garbage
-// collector, with the Baker read barrier configuration.
#define wMR w20
#endif
@@ -180,7 +178,7 @@
// entrypoints that possibly (directly or indirectly) perform a
// suspend check (before they return).
.macro REFRESH_MARKING_REGISTER
-#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
+#ifdef RESERVE_MARKING_REGISTER
ldr wMR, [xSELF, #THREAD_IS_GC_MARKING_OFFSET]
#endif
.endm
diff --git a/runtime/arch/arm64/asm_support_arm64.h b/runtime/arch/arm64/asm_support_arm64.h
index 887ee02..983fe3a 100644
--- a/runtime/arch/arm64/asm_support_arm64.h
+++ b/runtime/arch/arm64/asm_support_arm64.h
@@ -19,6 +19,7 @@
#include "asm_support.h"
+// TODO(mythria): Change these to use constants from callee_save_frame_arm64.h
#define CALLEE_SAVES_SIZE (12 * 8 + 8 * 8)
// +8 for the ArtMethod, +8 for alignment.
#define FRAME_SIZE_SAVE_ALL_CALLEE_SAVES (CALLEE_SAVES_SIZE + 16)
@@ -27,6 +28,8 @@
#define FRAME_SIZE_SAVE_EVERYTHING 512
#define FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT FRAME_SIZE_SAVE_EVERYTHING
#define FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK FRAME_SIZE_SAVE_EVERYTHING
+#define SAVE_EVERYTHING_FRAME_X0_OFFSET \
+ (FRAME_SIZE_SAVE_EVERYTHING - CALLEE_SAVE_EVERYTHING_NUM_CORE_SPILLS * POINTER_SIZE)
// The offset from art_quick_read_barrier_mark_introspection to the array switch cases,
// i.e. art_quick_read_barrier_mark_introspection_arrays.
@@ -37,17 +40,17 @@
// The offset of the reference load LDR from the return address in LR for field loads.
#ifdef USE_HEAP_POISONING
-#define BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET -8
+#define BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET (-8)
#else
-#define BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET -4
+#define BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET (-4)
#endif
// The offset of the reference load LDR from the return address in LR for array loads.
#ifdef USE_HEAP_POISONING
-#define BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET -8
+#define BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET (-8)
#else
-#define BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET -4
+#define BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET (-4)
#endif
// The offset of the reference load LDR from the return address in LR for GC root loads.
-#define BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_OFFSET -8
+#define BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_OFFSET (-8)
#endif // ART_RUNTIME_ARCH_ARM64_ASM_SUPPORT_ARM64_H_
diff --git a/runtime/arch/arm64/context_arm64.cc b/runtime/arch/arm64/context_arm64.cc
index 16f4792..569457a 100644
--- a/runtime/arch/arm64/context_arm64.cc
+++ b/runtime/arch/arm64/context_arm64.cc
@@ -29,6 +29,10 @@
#define __hwasan_handle_longjmp(sp)
#endif
+#if defined(__aarch64__) && defined(__BIONIC__)
+#include <bionic/malloc.h>
+#endif
+
namespace art {
namespace arm64 {
@@ -130,7 +134,21 @@
extern "C" NO_RETURN void art_quick_do_long_jump(uint64_t*, uint64_t*);
-void Arm64Context::DoLongJump() {
+#if defined(__aarch64__) && defined(__BIONIC__) && defined(M_MEMTAG_STACK_IS_ON)
+static inline __attribute__((no_sanitize("memtag"))) void untag_memory(void* from, void* to) {
+ __asm__ __volatile__(
+ ".arch_extension mte\n"
+ "1:\n"
+ "stg %[Ptr], [%[Ptr]], #16\n"
+ "cmp %[Ptr], %[End]\n"
+ "b.lt 1b\n"
+ : [Ptr] "+&r"(from)
+ : [End] "r"(to)
+ : "memory");
+}
+#endif
+
+__attribute__((no_sanitize("memtag"))) void Arm64Context::DoLongJump() {
uint64_t gprs[arraysize(gprs_)];
uint64_t fprs[kNumberOfDRegisters];
@@ -145,6 +163,13 @@
}
// Ensure the Thread Register contains the address of the current thread.
DCHECK_EQ(reinterpret_cast<uintptr_t>(Thread::Current()), gprs[TR]);
+#if defined(__aarch64__) && defined(__BIONIC__) && defined(M_MEMTAG_STACK_IS_ON)
+ bool memtag_stack;
+ // This works fine because versions of Android that did not support M_MEMTAG_STACK_ON did not
+ // support stack tagging either.
+ if (android_mallopt(M_MEMTAG_STACK_IS_ON, &memtag_stack, sizeof(memtag_stack)) && memtag_stack)
+ untag_memory(__builtin_frame_address(0), reinterpret_cast<void*>(gprs[SP]));
+#endif
// Tell HWASan about the new stack top.
__hwasan_handle_longjmp(reinterpret_cast<void*>(gprs[SP]));
// The Marking Register will be updated by art_quick_do_long_jump.
diff --git a/runtime/arch/arm64/fault_handler_arm64.cc b/runtime/arch/arm64/fault_handler_arm64.cc
index a5becf6..9634492 100644
--- a/runtime/arch/arm64/fault_handler_arm64.cc
+++ b/runtime/arch/arm64/fault_handler_arm64.cc
@@ -38,66 +38,56 @@
namespace art {
-void FaultManager::GetMethodAndReturnPcAndSp(siginfo_t* siginfo,
- void* context,
- ArtMethod** out_method,
- uintptr_t* out_return_pc,
- uintptr_t* out_sp,
- bool* out_is_stack_overflow) {
- struct ucontext *uc = reinterpret_cast<struct ucontext *>(context);
- struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
-
+uintptr_t FaultManager::GetFaultPc(siginfo_t* siginfo, void* context) {
// SEGV_MTEAERR (Async MTE fault) is delivered at an arbitrary point after the actual fault.
// Register contents, including PC and SP, are unrelated to the fault and can only confuse ART
// signal handlers.
if (siginfo->si_signo == SIGSEGV && siginfo->si_code == SEGV_MTEAERR) {
- return;
+ VLOG(signals) << "Async MTE fault";
+ return 0u;
}
- *out_sp = static_cast<uintptr_t>(sc->sp);
- VLOG(signals) << "sp: " << *out_sp;
- if (*out_sp == 0) {
- return;
+ struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
+ struct sigcontext* sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
+ if (sc->sp == 0) {
+ VLOG(signals) << "Missing SP";
+ return 0u;
}
+ return sc->pc;
+}
- // In the case of a stack overflow, the stack is not valid and we can't
- // get the method from the top of the stack. However it's in x0.
- uintptr_t* fault_addr = reinterpret_cast<uintptr_t*>(sc->fault_address);
- uintptr_t* overflow_addr = reinterpret_cast<uintptr_t*>(
- reinterpret_cast<uint8_t*>(*out_sp) - GetStackOverflowReservedBytes(InstructionSet::kArm64));
- if (overflow_addr == fault_addr) {
- *out_method = reinterpret_cast<ArtMethod*>(sc->regs[0]);
- *out_is_stack_overflow = true;
- } else {
- // The method is at the top of the stack.
- *out_method = *reinterpret_cast<ArtMethod**>(*out_sp);
- *out_is_stack_overflow = false;
- }
-
- // Work out the return PC. This will be the address of the instruction
- // following the faulting ldr/str instruction.
- VLOG(signals) << "pc: " << std::hex
- << static_cast<void*>(reinterpret_cast<uint8_t*>(sc->pc));
-
- *out_return_pc = sc->pc + 4;
+uintptr_t FaultManager::GetFaultSp(void* context) {
+ struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
+ struct sigcontext* sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
+ return sc->sp;
}
bool NullPointerHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info, void* context) {
- if (!IsValidImplicitCheck(info)) {
+ uintptr_t fault_address = reinterpret_cast<uintptr_t>(info->si_addr);
+ if (!IsValidFaultAddress(fault_address)) {
return false;
}
- // The code that looks for the catch location needs to know the value of the
- // PC at the point of call. For Null checks we insert a GC map that is immediately after
- // the load/store instruction that might cause the fault.
- struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
- struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
+ // For null checks in compiled code we insert a stack map that is immediately
+ // after the load/store instruction that might cause the fault and we need to
+ // pass the return PC to the handler. For null checks in Nterp, we similarly
+ // need the return PC to recognize that this was a null check in Nterp, so
+ // that the handler can get the needed data from the Nterp frame.
- // Push the gc map location to the stack and pass the fault address in LR.
+ struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
+ struct sigcontext* sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
+ ArtMethod** sp = reinterpret_cast<ArtMethod**>(sc->sp);
+ uintptr_t return_pc = sc->pc + 4u;
+ if (!IsValidMethod(*sp) || !IsValidReturnPc(sp, return_pc)) {
+ return false;
+ }
+
+ // Push the return PC to the stack and pass the fault address in LR.
sc->sp -= sizeof(uintptr_t);
- *reinterpret_cast<uintptr_t*>(sc->sp) = sc->pc + 4;
- sc->regs[30] = reinterpret_cast<uintptr_t>(info->si_addr);
+ *reinterpret_cast<uintptr_t*>(sc->sp) = return_pc;
+ sc->regs[30] = fault_address;
+ // Arrange for the signal handler to return to the NPE entrypoint.
sc->pc = reinterpret_cast<uintptr_t>(art_quick_throw_null_pointer_exception_from_signal);
VLOG(signals) << "Generating null pointer exception";
return true;
@@ -112,12 +102,11 @@
constexpr uint32_t checkinst =
0xf9400000 | (kSuspendCheckRegister << 5) | (kSuspendCheckRegister << 0);
- struct ucontext *uc = reinterpret_cast<struct ucontext *>(context);
- struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
- VLOG(signals) << "checking suspend";
+ struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
+ struct sigcontext* sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
uint32_t inst = *reinterpret_cast<uint32_t*>(sc->pc);
- VLOG(signals) << "inst: " << std::hex << inst << " checkinst: " << checkinst;
+ VLOG(signals) << "checking suspend; inst: " << std::hex << inst << " checkinst: " << checkinst;
if (inst != checkinst) {
// The instruction is not good, not ours.
return false;
@@ -141,8 +130,8 @@
bool StackOverflowHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* info ATTRIBUTE_UNUSED,
void* context) {
- struct ucontext *uc = reinterpret_cast<struct ucontext *>(context);
- struct sigcontext *sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
+ struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
+ struct sigcontext* sc = reinterpret_cast<struct sigcontext*>(&uc->uc_mcontext);
VLOG(signals) << "stack overflow handler with sp at " << std::hex << &uc;
VLOG(signals) << "sigcontext: " << std::hex << sc;
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc
index ad082ae..93400d9 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64.cc
@@ -171,6 +171,18 @@
has_sve));
}
+Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::IntersectWithHwcap() const {
+ Arm64FeaturesUniquePtr hwcaps = Arm64InstructionSetFeatures::FromHwcap();
+ return Arm64FeaturesUniquePtr(new Arm64InstructionSetFeatures(
+ fix_cortex_a53_835769_,
+ fix_cortex_a53_843419_,
+ has_crc_ && hwcaps->has_crc_,
+ has_lse_ && hwcaps->has_lse_,
+ has_fp16_ && hwcaps->has_fp16_,
+ has_dotprod_ && hwcaps->has_dotprod_,
+ has_sve_ && hwcaps->has_sve_));
+}
+
Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromBitmap(uint32_t bitmap) {
bool is_a53 = (bitmap & kA53Bitfield) != 0;
bool has_crc = (bitmap & kCRCBitField) != 0;
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.h b/runtime/arch/arm64/instruction_set_features_arm64.h
index eb98c01..8f0013a 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.h
+++ b/runtime/arch/arm64/instruction_set_features_arm64.h
@@ -53,6 +53,10 @@
// Use external cpu_features library.
static Arm64FeaturesUniquePtr FromCpuFeatures();
+ // Return a new set of instruction set features, intersecting `this` features
+ // with hardware capabilities.
+ Arm64FeaturesUniquePtr IntersectWithHwcap() const;
+
bool Equals(const InstructionSetFeatures* other) const override;
// Note that newer CPUs do not have a53 erratum 835769 and 843419,
diff --git a/runtime/arch/arm64/jni_entrypoints_arm64.S b/runtime/arch/arm64/jni_entrypoints_arm64.S
index 463767c..9612a7b 100644
--- a/runtime/arch/arm64/jni_entrypoints_arm64.S
+++ b/runtime/arch/arm64/jni_entrypoints_arm64.S
@@ -103,7 +103,7 @@
// Call artFindNativeMethod() for normal native and artFindNativeMethodRunnable()
// for @FastNative or @CriticalNative.
ldr xIP0, [x0, #THREAD_TOP_QUICK_FRAME_OFFSET] // uintptr_t tagged_quick_frame
- bic xIP0, xIP0, #1 // ArtMethod** sp
+ bic xIP0, xIP0, #TAGGED_JNI_SP_MASK // ArtMethod** sp
ldr xIP0, [xIP0] // ArtMethod* method
ldr xIP0, [xIP0, #ART_METHOD_ACCESS_FLAGS_OFFSET] // uint32_t access_flags
mov xIP1, #(ACCESS_FLAGS_METHOD_IS_FAST_NATIVE | ACCESS_FLAGS_METHOD_IS_CRITICAL_NATIVE)
@@ -366,6 +366,11 @@
JNI_SAVE_MANAGED_ARGS_TRAMPOLINE art_jni_method_start, artJniMethodStart, xSELF
/*
+ * Trampoline to `artJniMethodEntryHook` that preserves all managed arguments.
+ */
+JNI_SAVE_MANAGED_ARGS_TRAMPOLINE art_jni_method_entry_hook, artJniMethodEntryHook, xSELF
+
+ /*
* Trampoline to `artJniMonitoredMethodStart()` that preserves all managed arguments.
*/
JNI_SAVE_MANAGED_ARGS_TRAMPOLINE art_jni_monitored_method_start, artJniMonitoredMethodStart, xSELF
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index d8c91e1..1df24c1 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -194,26 +194,56 @@
RESTORE_SAVE_EVERYTHING_FRAME_KEEP_X0
.endm
-.macro RETURN_IF_RESULT_IS_ZERO
- cbnz x0, 1f // result non-zero branch over
- ret // return
-1:
+.macro RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ ldr x1, [xSELF, # THREAD_EXCEPTION_OFFSET] // Get exception field.
+ cbnz x1, 1f
+ DEOPT_OR_RETURN x1 // Check if deopt is required
+1: // deliver exception on current thread
+ DELIVER_PENDING_EXCEPTION
.endm
-.macro RETURN_IF_RESULT_IS_NON_ZERO
- cbz x0, 1f // result zero branch over
- ret // return
-1:
+.macro DEOPT_OR_RETURN temp, is_ref = 0
+ ldr \temp, [xSELF, #THREAD_DEOPT_CHECK_REQUIRED_OFFSET]
+ cbnz \temp, 2f
+ ret
+2:
+ SETUP_SAVE_EVERYTHING_FRAME
+ mov x2, \is_ref // pass if result is a reference
+ mov x1, x0 // pass the result
+ mov x0, xSELF // Thread::Current
+ bl artDeoptimizeIfNeeded
+ .cfi_remember_state
+ RESTORE_SAVE_EVERYTHING_FRAME
+ REFRESH_MARKING_REGISTER
+ ret
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
.endm
-// Same as above with x1. This is helpful in stubs that want to avoid clobbering another register.
-.macro RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
- RETURN_OR_DELIVER_PENDING_EXCEPTION_REG x1
+.macro DEOPT_OR_RESTORE_SAVE_EVERYTHING_FRAME_AND_RETURN_X0 temp, is_ref
+ ldr \temp, [xSELF, #THREAD_DEOPT_CHECK_REQUIRED_OFFSET]
+ cbnz \temp, 2f
+ .cfi_remember_state
+ RESTORE_SAVE_EVERYTHING_FRAME_KEEP_X0
+ REFRESH_MARKING_REGISTER
+ ret
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
+2:
+ str x0, [sp, #SAVE_EVERYTHING_FRAME_X0_OFFSET] // update result in the frame
+ mov x2, \is_ref // pass if result is a reference
+ mov x1, x0 // pass the result
+ mov x0, xSELF // Thread::Current
+ bl artDeoptimizeIfNeeded
+ .cfi_remember_state
+ RESTORE_SAVE_EVERYTHING_FRAME
+ REFRESH_MARKING_REGISTER
+ ret
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
.endm
+
.macro RETURN_IF_W0_IS_ZERO_OR_DELIVER
cbnz w0, 1f // result non-zero branch over
- ret // return
+ DEOPT_OR_RETURN x1
1:
DELIVER_PENDING_EXCEPTION
.endm
@@ -999,25 +1029,30 @@
.cfi_restore \xReg2
.endm
- /*
- * Macro to insert read barrier, only used in art_quick_aput_obj.
- * xDest, wDest and xObj are registers, offset is a defined literal such as
- * MIRROR_OBJECT_CLASS_OFFSET. Dest needs both x and w versions of the same register to handle
- * name mismatch between instructions. This macro uses the lower 32b of register when possible.
- * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path.
- */
-.macro READ_BARRIER xDest, wDest, xObj, xTemp, wTemp, offset, number
+ // Helper macros for `art_quick_aput_obj`.
#ifdef USE_READ_BARRIER
-# ifdef USE_BAKER_READ_BARRIER
- ldr \wTemp, [\xObj, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
- tbnz \wTemp, #LOCK_WORD_READ_BARRIER_STATE_SHIFT, .Lrb_slowpath\number
+#ifdef USE_BAKER_READ_BARRIER
+.macro BAKER_RB_CHECK_GRAY_BIT_AND_LOAD wDest, xObj, offset, gray_slow_path_label
+ ldr wIP0, [\xObj, #MIRROR_OBJECT_LOCK_WORD_OFFSET]
+ tbnz wIP0, #LOCK_WORD_READ_BARRIER_STATE_SHIFT, \gray_slow_path_label
// False dependency to avoid needing load/load fence.
- add \xObj, \xObj, \xTemp, lsr #32
- ldr \wDest, [\xObj, #\offset] // Heap reference = 32b. This also zero-extends to \xDest.
+ add \xObj, \xObj, xIP0, lsr #32
+ ldr \wDest, [\xObj, #\offset] // Heap reference = 32b; zero-extends to xN.
UNPOISON_HEAP_REF \wDest
- b .Lrb_exit\number
-# endif // USE_BAKER_READ_BARRIER
-.Lrb_slowpath\number:
+.endm
+
+.macro BAKER_RB_LOAD_AND_MARK wDest, xObj, offset, mark_function
+ ldr \wDest, [\xObj, #\offset] // Heap reference = 32b; zero-extends to xN.
+ UNPOISON_HEAP_REF \wDest
+ // Save LR in a register preserved by `art_quick_read_barrier_mark_regNN`
+ // and unused by the `art_quick_aput_obj`.
+ mov x5, lr
+ bl \mark_function
+ mov lr, x5 // Restore LR.
+.endm
+#else // USE_BAKER_READ_BARRIER
+ .extern artReadBarrierSlow
+.macro READ_BARRIER_SLOW xDest, wDest, xObj, offset
// Store registers used in art_quick_aput_obj (x0-x4, LR), stack is 16B aligned.
SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 48
SAVE_TWO_REGS x2, x3, 16
@@ -1042,41 +1077,44 @@
POP_REG_NE x4, 32, \xDest
RESTORE_REG xLR, 40
DECREASE_FRAME 48
-.Lrb_exit\number:
-#else
- ldr \wDest, [\xObj, #\offset] // Heap reference = 32b. This also zero-extends to \xDest.
- UNPOISON_HEAP_REF \wDest
-#endif // USE_READ_BARRIER
.endm
+#endif // USE_BAKER_READ_BARRIER
+#endif // USE_READ_BARRIER
-#ifdef USE_READ_BARRIER
- .extern artReadBarrierSlow
-#endif
ENTRY art_quick_aput_obj
- cbz x2, .Ldo_aput_null
- READ_BARRIER x3, w3, x0, x3, w3, MIRROR_OBJECT_CLASS_OFFSET, 0 // Heap reference = 32b
- // This also zero-extends to x3
- READ_BARRIER x3, w3, x3, x4, w4, MIRROR_CLASS_COMPONENT_TYPE_OFFSET, 1 // Heap reference = 32b
- // This also zero-extends to x3
- READ_BARRIER x4, w4, x2, x4, w4, MIRROR_OBJECT_CLASS_OFFSET, 2 // Heap reference = 32b
- // This also zero-extends to x4
+ cbz x2, .Laput_obj_null
+#if defined(USE_READ_BARRIER) && !defined(USE_BAKER_READ_BARRIER)
+ READ_BARRIER_SLOW x3, w3, x0, MIRROR_OBJECT_CLASS_OFFSET
+ READ_BARRIER_SLOW x3, w3, x3, MIRROR_CLASS_COMPONENT_TYPE_OFFSET
+ READ_BARRIER_SLOW x4, w4, x2, MIRROR_OBJECT_CLASS_OFFSET
+#else // !defined(USE_READ_BARRIER) || defined(USE_BAKER_READ_BARRIER)
+#ifdef USE_READ_BARRIER
+ cbnz wMR, .Laput_obj_gc_marking
+#endif // USE_READ_BARRIER
+ ldr w3, [x0, #MIRROR_OBJECT_CLASS_OFFSET] // Heap reference = 32b; zero-extends to x3.
+ UNPOISON_HEAP_REF w3
+ ldr w3, [x3, #MIRROR_CLASS_COMPONENT_TYPE_OFFSET] // Heap reference = 32b; zero-extends to x3.
+ UNPOISON_HEAP_REF w3
+ ldr w4, [x2, #MIRROR_OBJECT_CLASS_OFFSET] // Heap reference = 32b; zero-extends to x4.
+ UNPOISON_HEAP_REF w4
+#endif // !defined(USE_READ_BARRIER) || defined(USE_BAKER_READ_BARRIER)
cmp w3, w4 // value's type == array's component type - trivial assignability
- bne .Lcheck_assignability
-.Ldo_aput:
+ bne .Laput_obj_check_assignability
+.Laput_obj_store:
add x3, x0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
- // "Compress" = do nothing
POISON_HEAP_REF w2
- str w2, [x3, x1, lsl #2] // Heap reference = 32b
+ str w2, [x3, x1, lsl #2] // Heap reference = 32b.
ldr x3, [xSELF, #THREAD_CARD_TABLE_OFFSET]
lsr x0, x0, #CARD_TABLE_CARD_SHIFT
strb w3, [x3, x0]
ret
-.Ldo_aput_null:
+
+.Laput_obj_null:
add x3, x0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
- // "Compress" = do nothing
- str w2, [x3, x1, lsl #2] // Heap reference = 32b
+ str w2, [x3, x1, lsl #2] // Heap reference = 32b.
ret
-.Lcheck_assignability:
+
+.Laput_obj_check_assignability:
// Store arguments and link register
SAVE_TWO_REGS_INCREASE_FRAME x0, x1, 32
SAVE_TWO_REGS x2, xLR, 16
@@ -1087,7 +1125,7 @@
bl artIsAssignableFromCode
// Check for exception
- cbz x0, .Lthrow_array_store_exception
+ cbz x0, .Laput_obj_throw_array_store_exception
// Restore
.cfi_remember_state
@@ -1095,23 +1133,56 @@
RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, 32
add x3, x0, #MIRROR_OBJECT_ARRAY_DATA_OFFSET
- // "Compress" = do nothing
POISON_HEAP_REF w2
- str w2, [x3, x1, lsl #2] // Heap reference = 32b
+ str w2, [x3, x1, lsl #2] // Heap reference = 32b.
ldr x3, [xSELF, #THREAD_CARD_TABLE_OFFSET]
lsr x0, x0, #CARD_TABLE_CARD_SHIFT
strb w3, [x3, x0]
ret
CFI_RESTORE_STATE_AND_DEF_CFA sp, 32
-.Lthrow_array_store_exception:
+
+.Laput_obj_throw_array_store_exception:
RESTORE_TWO_REGS x2, xLR, 16
RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, 32
+#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
+ .cfi_remember_state
+#endif // defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
mov x1, x2 // Pass value.
mov x2, xSELF // Pass Thread::Current.
bl artThrowArrayStoreException // (Object*, Object*, Thread*).
- brk 0 // Unreached.
+ brk 0 // Unreachable.
+
+#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
+ CFI_RESTORE_STATE_AND_DEF_CFA sp, 0
+.Laput_obj_gc_marking:
+ BAKER_RB_CHECK_GRAY_BIT_AND_LOAD \
+ w3, x0, MIRROR_OBJECT_CLASS_OFFSET, .Laput_obj_mark_array_class
+.Laput_obj_mark_array_class_continue:
+ BAKER_RB_CHECK_GRAY_BIT_AND_LOAD \
+ w3, x3, MIRROR_CLASS_COMPONENT_TYPE_OFFSET, .Laput_obj_mark_array_element
+.Laput_obj_mark_array_element_continue:
+ BAKER_RB_CHECK_GRAY_BIT_AND_LOAD \
+ w4, x2, MIRROR_OBJECT_CLASS_OFFSET, .Laput_obj_mark_object_class
+.Laput_obj_mark_object_class_continue:
+ cmp w3, w4 // value's type == array's component type - trivial assignability
+ bne .Laput_obj_check_assignability
+ b .Laput_obj_store
+
+.Laput_obj_mark_array_class:
+ BAKER_RB_LOAD_AND_MARK w3, x0, MIRROR_OBJECT_CLASS_OFFSET, art_quick_read_barrier_mark_reg03
+ b .Laput_obj_mark_array_class_continue
+
+.Laput_obj_mark_array_element:
+ BAKER_RB_LOAD_AND_MARK \
+ w3, x3, MIRROR_CLASS_COMPONENT_TYPE_OFFSET, art_quick_read_barrier_mark_reg03
+ b .Laput_obj_mark_array_element_continue
+
+.Laput_obj_mark_object_class:
+ BAKER_RB_LOAD_AND_MARK w4, x2, MIRROR_OBJECT_CLASS_OFFSET, art_quick_read_barrier_mark_reg04
+ b .Laput_obj_mark_object_class_continue
+#endif // defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
END art_quick_aput_obj
// Macro to facilitate adding new allocation entrypoints.
@@ -1214,11 +1285,7 @@
mov x1, xSELF // pass Thread::Current
bl \entrypoint // (int32_t index, Thread* self)
cbz w0, 1f // If result is null, deliver the OOME.
- .cfi_remember_state
- RESTORE_SAVE_EVERYTHING_FRAME_KEEP_X0
- REFRESH_MARKING_REGISTER
- ret // return
- CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
+ DEOPT_OR_RESTORE_SAVE_EVERYTHING_FRAME_AND_RETURN_X0 x1, /* is_ref= */ 1
1:
DELIVER_PENDING_EXCEPTION_FRAME_READY
END \name
@@ -1228,13 +1295,14 @@
ONE_ARG_SAVE_EVERYTHING_DOWNCALL \name, \entrypoint, RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET
.endm
-.macro RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
- cbz w0, 1f // result zero branch over
- ret // return
+.macro RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
+ cbz w0, 1f // result zero branch over
+ DEOPT_OR_RETURN x1, /*is_ref=*/1 // check for deopt or return
1:
DELIVER_PENDING_EXCEPTION
.endm
+
/*
* Entry from managed code that calls artHandleFillArrayDataFromCode and delivers exception on
* failure.
@@ -1256,21 +1324,21 @@
// Note: Functions `art{Get,Set}<Kind>{Static,Instance}FromCompiledCode` are
// defined with a macro in runtime/entrypoints/quick/quick_field_entrypoints.cc.
-ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-ONE_ARG_REF_DOWNCALL art_quick_get64_static, artGet64StaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
+ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get64_static, artGet64StaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-TWO_ARG_REF_DOWNCALL art_quick_get64_instance, artGet64InstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
-TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION_X1
+TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get64_instance, artGet64InstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
TWO_ARG_REF_DOWNCALL art_quick_set8_static, artSet8StaticFromCompiledCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
TWO_ARG_REF_DOWNCALL art_quick_set16_static, artSet16StaticFromCompiledCode, RETURN_IF_W0_IS_ZERO_OR_DELIVER
@@ -1410,7 +1478,7 @@
bl \cxx_name
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
END \c_name
.endm
@@ -1439,10 +1507,6 @@
str x5, [xSELF, #THREAD_LOCAL_OBJECTS_OFFSET]
POISON_HEAP_REF w0
str w0, [x4, #MIRROR_OBJECT_CLASS_OFFSET] // Store the class pointer.
- // Fence. This is "ish" not "ishst" so
- // that the code after this allocation
- // site will see the right values in
- // the fields of the class.
mov x0, x4
// No barrier. The class is already observably initialized (otherwise the fast
// path size check above would fail) and new-instance allocations are protected
@@ -1465,7 +1529,7 @@
bl \entrypoint // (mirror::Class*, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
END \name
.endm
@@ -1510,7 +1574,6 @@
POISON_HEAP_REF \wClass
str \wClass, [x0, #MIRROR_OBJECT_CLASS_OFFSET] // Store the class pointer.
str \wCount, [x0, #MIRROR_ARRAY_LENGTH_OFFSET] // Store the array length.
- // Fence.
// new-array is special. The class is loaded and immediately goes to the Initialized state
// before it is published. Therefore the only fence needed is for the publication of the object.
// See ClassLinker::CreateArrayClass() for more details.
@@ -1539,7 +1602,7 @@
bl \entrypoint
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
END \name
.endm
@@ -1837,6 +1900,11 @@
.cfi_remember_state
.cfi_def_cfa_register sp
+ LOAD_RUNTIME_INSTANCE x1
+ ldrb w1, [x1, #INSTRUMENTATION_STUBS_INSTALLED_OFFSET_FROM_RUNTIME_INSTANCE]
+ cbnz w1, .Lcall_method_exit_hook
+.Lcall_method_exit_hook_done:
+
// Tear down the callee-save frame.
RESTORE_SAVE_REFS_AND_ARGS_FRAME
REFRESH_MARKING_REGISTER
@@ -1845,6 +1913,12 @@
fmov d0, x0
ret
+.Lcall_method_exit_hook:
+ fmov d0, x0
+ mov x4, FRAME_SIZE_SAVE_REFS_AND_ARGS
+ bl art_quick_method_exit_hook
+ b .Lcall_method_exit_hook_done
+
// Undo the unwinding information from above since it doesn't apply below.
CFI_RESTORE_STATE_AND_DEF_CFA x28, FRAME_SIZE_SAVE_REFS_AND_ARGS
.Lexception_in_native:
@@ -2102,7 +2176,7 @@
bl artStringBuilderAppend // (uint32_t, const unit32_t*, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
END art_quick_string_builder_append
/*
@@ -2111,15 +2185,10 @@
* `wreg` (corresponding to X register `xreg`), saving and restoring
* all caller-save registers.
*
- * If `wreg` is different from `w0`, the generated function follows a
- * non-standard runtime calling convention:
- * - register `wreg` is used to pass the (sole) argument of this
- * function (instead of W0);
- * - register `wreg` is used to return the result of this function
- * (instead of W0);
- * - W0 is treated like a normal (non-argument) caller-save register;
- * - everything else is the same as in the standard runtime calling
- * convention (e.g. standard callee-save registers are preserved).
+ * The generated function follows a non-standard runtime calling convention:
+ * - register `reg` (which may be different from W0) is used to pass the (sole) argument,
+ * - register `reg` (which may be different from W0) is used to return the result,
+ * - all other registers are callee-save (the values they hold are preserved).
*/
.macro READ_BARRIER_MARK_REG name, wreg, xreg
ENTRY \name
@@ -2598,7 +2667,8 @@
ldr x0, [sp, #FRAME_SIZE_SAVE_EVERYTHING] // pass ArtMethod*
mov x1, xSELF // pass Thread::Current
- bl artMethodEntryHook // (ArtMethod*, Thread*)
+ mov x2, sp // pass SP
+ bl artMethodEntryHook // (ArtMethod*, Thread*, SP)
RESTORE_SAVE_EVERYTHING_FRAME // Note: will restore xSELF
REFRESH_MARKING_REGISTER
@@ -2609,21 +2679,16 @@
ENTRY art_quick_method_exit_hook
SETUP_SAVE_EVERYTHING_FRAME
+ // frame_size is passed from JITed code in x4
add x3, sp, #16 // floating-point result ptr in kSaveEverything frame
add x2, sp, #272 // integer result ptr in kSaveEverything frame
- ldr x1, [sp, #FRAME_SIZE_SAVE_EVERYTHING] // ArtMethod*
+ add x1, sp, #FRAME_SIZE_SAVE_EVERYTHING // ArtMethod**
mov x0, xSELF // Thread::Current
- bl artMethodExitHook // (Thread*, ArtMethod*, gpr_res*, fpr_res*)
-
- .cfi_remember_state
- cbnz x0, .Ldo_deliver_instrumentation_exception_exit // Handle exception
+ bl artMethodExitHook // (Thread*, ArtMethod**, gpr_res*, fpr_res*,
+ // frame_size)
// Normal return.
RESTORE_SAVE_EVERYTHING_FRAME
REFRESH_MARKING_REGISTER
ret
-.Ldo_deliver_instrumentation_exception_exit:
- CFI_RESTORE_STATE_AND_DEF_CFA sp, FRAME_SIZE_SAVE_EVERYTHING
- DELIVER_PENDING_EXCEPTION_FRAME_READY
END art_quick_method_exit_hook
-
diff --git a/runtime/arch/instruction_set_features.cc b/runtime/arch/instruction_set_features.cc
index ec1e340..7a1e6b0 100644
--- a/runtime/arch/instruction_set_features.cc
+++ b/runtime/arch/instruction_set_features.cc
@@ -53,6 +53,33 @@
UNREACHABLE();
}
+std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::FromVariantAndHwcap(
+ InstructionSet isa, const std::string& variant, std::string* error_msg) {
+ auto variant_features = FromVariant(isa, variant, error_msg);
+ if (variant_features == nullptr) {
+ return nullptr;
+ }
+ // Pixel3a is wrongly reporting itself as cortex-a75, so validate the features
+ // with hwcaps.
+ // Note that when cross-compiling on device (using dex2oat32 for compiling
+ // arm64), the hwcaps will report that no feature is supported. This is
+ // currently our best approach to be safe/correct. Maybe using the
+ // cpu_features library could fix this issue.
+ if (isa == InstructionSet::kArm64) {
+ auto new_features = down_cast<const Arm64InstructionSetFeatures*>(variant_features.get())
+ ->IntersectWithHwcap();
+ if (!variant_features->Equals(new_features.get())) {
+ LOG(WARNING) << "Mismatch between instruction set variant of device ("
+ << *variant_features
+ << ") and features returned by the hardware (" << *new_features << ")";
+ }
+ return new_features;
+ } else {
+ // TODO: Implement this validation on all architectures.
+ return variant_features;
+ }
+}
+
std::unique_ptr<const InstructionSetFeatures> InstructionSetFeatures::FromBitmap(InstructionSet isa,
uint32_t bitmap) {
std::unique_ptr<const InstructionSetFeatures> result;
diff --git a/runtime/arch/instruction_set_features.h b/runtime/arch/instruction_set_features.h
index b80d36f..cee8c5d 100644
--- a/runtime/arch/instruction_set_features.h
+++ b/runtime/arch/instruction_set_features.h
@@ -39,6 +39,12 @@
const std::string& variant,
std::string* error_msg);
+ // Process a CPU variant string for the given ISA and make sure the features advertised
+ // are supported by the hardware. This is needed for Pixel3a which wrongly
+ // reports itself as cortex-a75.
+ static std::unique_ptr<const InstructionSetFeatures> FromVariantAndHwcap(
+ InstructionSet isa, const std::string& variant, std::string* error_msg);
+
// Parse a bitmap for the given isa and create an InstructionSetFeatures.
static std::unique_ptr<const InstructionSetFeatures> FromBitmap(InstructionSet isa,
uint32_t bitmap);
diff --git a/runtime/arch/quick_alloc_entrypoints.S b/runtime/arch/quick_alloc_entrypoints.S
index 32888ed..5d4b24b 100644
--- a/runtime/arch/quick_alloc_entrypoints.S
+++ b/runtime/arch/quick_alloc_entrypoints.S
@@ -16,27 +16,27 @@
.macro GENERATE_ALLOC_ENTRYPOINTS c_suffix, cxx_suffix
// Called by managed code to allocate an object of a resolved class.
-ONE_ARG_DOWNCALL art_quick_alloc_object_resolved\c_suffix, artAllocObjectFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_DOWNCALL art_quick_alloc_object_resolved\c_suffix, artAllocObjectFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
// Called by managed code to allocate an object of an initialized class.
-ONE_ARG_DOWNCALL art_quick_alloc_object_initialized\c_suffix, artAllocObjectFromCodeInitialized\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_DOWNCALL art_quick_alloc_object_initialized\c_suffix, artAllocObjectFromCodeInitialized\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
// Called by managed code to allocate an object when the caller doesn't know whether it has access
// to the created type.
-ONE_ARG_DOWNCALL art_quick_alloc_object_with_checks\c_suffix, artAllocObjectFromCodeWithChecks\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_DOWNCALL art_quick_alloc_object_with_checks\c_suffix, artAllocObjectFromCodeWithChecks\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
// Called by managed code to allocate a string if it could not be removed by any optimizations
-ONE_ARG_DOWNCALL art_quick_alloc_string_object\c_suffix, artAllocStringObject\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_DOWNCALL art_quick_alloc_string_object\c_suffix, artAllocStringObject\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
// Called by managed code to allocate an array of a resolve class.
-TWO_ARG_DOWNCALL art_quick_alloc_array_resolved\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+TWO_ARG_DOWNCALL art_quick_alloc_array_resolved\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
// Called by managed code to allocate a string from bytes
-FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes\c_suffix, artAllocStringFromBytesFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes\c_suffix, artAllocStringFromBytesFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
// Called by managed code to allocate a string from chars
-THREE_ARG_DOWNCALL art_quick_alloc_string_from_chars\c_suffix, artAllocStringFromCharsFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+THREE_ARG_DOWNCALL art_quick_alloc_string_from_chars\c_suffix, artAllocStringFromCharsFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
// Called by managed code to allocate a string from string
-ONE_ARG_DOWNCALL art_quick_alloc_string_from_string\c_suffix, artAllocStringFromStringFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ONE_ARG_DOWNCALL art_quick_alloc_string_from_string\c_suffix, artAllocStringFromStringFromCode\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
-TWO_ARG_DOWNCALL art_quick_alloc_array_resolved8\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-TWO_ARG_DOWNCALL art_quick_alloc_array_resolved16\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-TWO_ARG_DOWNCALL art_quick_alloc_array_resolved32\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
-TWO_ARG_DOWNCALL art_quick_alloc_array_resolved64\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+TWO_ARG_DOWNCALL art_quick_alloc_array_resolved8\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
+TWO_ARG_DOWNCALL art_quick_alloc_array_resolved16\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
+TWO_ARG_DOWNCALL art_quick_alloc_array_resolved32\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
+TWO_ARG_DOWNCALL art_quick_alloc_array_resolved64\c_suffix, artAllocArrayFromCodeResolved\cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
.endm
.macro GENERATE_ALL_ALLOC_ENTRYPOINTS
@@ -58,29 +58,29 @@
// GENERATE_ALL_ALLOC_ENTRYPOINTS for selectively implementing allocation fast paths in
// hand-written assembly.
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(c_suffix, cxx_suffix) \
- ONE_ARG_DOWNCALL art_quick_alloc_object_resolved ## c_suffix, artAllocObjectFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ ONE_ARG_DOWNCALL art_quick_alloc_object_resolved ## c_suffix, artAllocObjectFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_INITIALIZED(c_suffix, cxx_suffix) \
- ONE_ARG_DOWNCALL art_quick_alloc_object_initialized ## c_suffix, artAllocObjectFromCodeInitialized ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ ONE_ARG_DOWNCALL art_quick_alloc_object_initialized ## c_suffix, artAllocObjectFromCodeInitialized ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_WITH_ACCESS_CHECK(c_suffix, cxx_suffix) \
- ONE_ARG_DOWNCALL art_quick_alloc_object_with_checks ## c_suffix, artAllocObjectFromCodeWithChecks ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ ONE_ARG_DOWNCALL art_quick_alloc_object_with_checks ## c_suffix, artAllocObjectFromCodeWithChecks ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_OBJECT(c_suffix, cxx_suffix) \
- ONE_ARG_DOWNCALL art_quick_alloc_string_object ## c_suffix, artAllocStringObject ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ ONE_ARG_DOWNCALL art_quick_alloc_string_object ## c_suffix, artAllocStringObject ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_BYTES(c_suffix, cxx_suffix) \
- FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes ## c_suffix, artAllocStringFromBytesFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ FOUR_ARG_DOWNCALL art_quick_alloc_string_from_bytes ## c_suffix, artAllocStringFromBytesFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_CHARS(c_suffix, cxx_suffix) \
- THREE_ARG_DOWNCALL art_quick_alloc_string_from_chars ## c_suffix, artAllocStringFromCharsFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ THREE_ARG_DOWNCALL art_quick_alloc_string_from_chars ## c_suffix, artAllocStringFromCharsFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_STRING_FROM_STRING(c_suffix, cxx_suffix) \
- ONE_ARG_DOWNCALL art_quick_alloc_string_from_string ## c_suffix, artAllocStringFromStringFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ ONE_ARG_DOWNCALL art_quick_alloc_string_from_string ## c_suffix, artAllocStringFromStringFromCode ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED(c_suffix, cxx_suffix) \
- TWO_ARG_DOWNCALL art_quick_alloc_array_resolved ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ TWO_ARG_DOWNCALL art_quick_alloc_array_resolved ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED8(c_suffix, cxx_suffix) \
- TWO_ARG_DOWNCALL art_quick_alloc_array_resolved8 ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ TWO_ARG_DOWNCALL art_quick_alloc_array_resolved8 ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED16(c_suffix, cxx_suffix) \
- TWO_ARG_DOWNCALL art_quick_alloc_array_resolved16 ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ TWO_ARG_DOWNCALL art_quick_alloc_array_resolved16 ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED32(c_suffix, cxx_suffix) \
- TWO_ARG_DOWNCALL art_quick_alloc_array_resolved32 ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ TWO_ARG_DOWNCALL art_quick_alloc_array_resolved32 ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
#define GENERATE_ALLOC_ENTRYPOINTS_ALLOC_ARRAY_RESOLVED64(c_suffix, cxx_suffix) \
- TWO_ARG_DOWNCALL art_quick_alloc_array_resolved64 ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER
+ TWO_ARG_DOWNCALL art_quick_alloc_array_resolved64 ## c_suffix, artAllocArrayFromCodeResolved ## cxx_suffix, RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER
.macro GENERATE_ALLOC_ENTRYPOINTS_FOR_REGION_TLAB_ALLOCATOR
// This is to be separately defined for each architecture to allow a hand-written assembly fast path.
diff --git a/runtime/arch/stub_test.cc b/runtime/arch/stub_test.cc
index 772681d..759cd9a 100644
--- a/runtime/arch/stub_test.cc
+++ b/runtime/arch/stub_test.cc
@@ -26,7 +26,7 @@
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "imt_conflict_table.h"
#include "jni/jni_internal.h"
-#include "linear_alloc.h"
+#include "linear_alloc-inl.h"
#include "mirror/class-alloc-inl.h"
#include "mirror/string-inl.h"
#include "mirror/object_array-alloc-inl.h"
@@ -961,7 +961,6 @@
TEST_F(StubTest, StringCompareTo) {
- TEST_DISABLED_FOR_STRING_COMPRESSION();
// There is no StringCompareTo runtime entrypoint for __arm__ or __aarch64__.
#if defined(__i386__) || (defined(__x86_64__) && !defined(__APPLE__))
// TODO: Check the "Unresolved" allocation stubs
@@ -1777,7 +1776,8 @@
Runtime::Current()->GetClassLinker()->CreateImtConflictTable(/*count=*/0u, linear_alloc);
void* data = linear_alloc->Alloc(
self,
- ImtConflictTable::ComputeSizeWithOneMoreEntry(empty_conflict_table, kRuntimePointerSize));
+ ImtConflictTable::ComputeSizeWithOneMoreEntry(empty_conflict_table, kRuntimePointerSize),
+ LinearAllocKind::kNoGCRoots);
ImtConflictTable* new_table = new (data) ImtConflictTable(
empty_conflict_table, inf_contains, contains_amethod, kRuntimePointerSize);
conflict_method->SetImtConflictTable(new_table, kRuntimePointerSize);
@@ -1918,74 +1918,74 @@
// TODO: Exercise the ReadBarrierMarkRegX entry points.
TEST_F(StubTest, ReadBarrier) {
-#if defined(ART_USE_READ_BARRIER) && (defined(__i386__) || defined(__arm__) || \
- defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)))
- Thread* self = Thread::Current();
+#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) ||\
+ (defined(__x86_64__) && !defined(__APPLE__))
+ if (gUseReadBarrier) {
+ Thread* self = Thread::Current();
- const uintptr_t readBarrierSlow = StubTest::GetEntrypoint(self, kQuickReadBarrierSlow);
+ const uintptr_t readBarrierSlow = StubTest::GetEntrypoint(self, kQuickReadBarrierSlow);
- // Create an object
- ScopedObjectAccess soa(self);
- // garbage is created during ClassLinker::Init
+ // Create an object
+ ScopedObjectAccess soa(self);
+ // garbage is created during ClassLinker::Init
- StackHandleScope<2> hs(soa.Self());
- Handle<mirror::Class> c(
- hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::Class> c(
+ hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;")));
- // Build an object instance
- Handle<mirror::Object> obj(hs.NewHandle(c->AllocObject(soa.Self())));
+ // Build an object instance
+ Handle<mirror::Object> obj(hs.NewHandle(c->AllocObject(soa.Self())));
- EXPECT_FALSE(self->IsExceptionPending());
+ EXPECT_FALSE(self->IsExceptionPending());
- size_t result = Invoke3(0U, reinterpret_cast<size_t>(obj.Get()),
- mirror::Object::ClassOffset().SizeValue(), readBarrierSlow, self);
+ size_t result = Invoke3(0U, reinterpret_cast<size_t>(obj.Get()),
+ mirror::Object::ClassOffset().SizeValue(), readBarrierSlow, self);
- EXPECT_FALSE(self->IsExceptionPending());
- EXPECT_NE(reinterpret_cast<size_t>(nullptr), result);
- mirror::Class* klass = reinterpret_cast<mirror::Class*>(result);
- EXPECT_OBJ_PTR_EQ(klass, obj->GetClass());
-
- // Tests done.
-#else
+ EXPECT_FALSE(self->IsExceptionPending());
+ EXPECT_NE(reinterpret_cast<size_t>(nullptr), result);
+ mirror::Class* klass = reinterpret_cast<mirror::Class*>(result);
+ EXPECT_OBJ_PTR_EQ(klass, obj->GetClass());
+ return;
+ }
+#endif
LOG(INFO) << "Skipping read_barrier_slow";
// Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping read_barrier_slow" << std::endl;
-#endif
}
TEST_F(StubTest, ReadBarrierForRoot) {
-#if defined(ART_USE_READ_BARRIER) && (defined(__i386__) || defined(__arm__) || \
- defined(__aarch64__) || (defined(__x86_64__) && !defined(__APPLE__)))
- Thread* self = Thread::Current();
+#if defined(__i386__) || defined(__arm__) || defined(__aarch64__) ||\
+ (defined(__x86_64__) && !defined(__APPLE__))
+ if (gUseReadBarrier) {
+ Thread* self = Thread::Current();
- const uintptr_t readBarrierForRootSlow =
- StubTest::GetEntrypoint(self, kQuickReadBarrierForRootSlow);
+ const uintptr_t readBarrierForRootSlow =
+ StubTest::GetEntrypoint(self, kQuickReadBarrierForRootSlow);
- // Create an object
- ScopedObjectAccess soa(self);
- // garbage is created during ClassLinker::Init
+ // Create an object
+ ScopedObjectAccess soa(self);
+ // garbage is created during ClassLinker::Init
- StackHandleScope<1> hs(soa.Self());
+ StackHandleScope<1> hs(soa.Self());
- Handle<mirror::String> obj(
- hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!")));
+ Handle<mirror::String> obj(
+ hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "hello, world!")));
- EXPECT_FALSE(self->IsExceptionPending());
+ EXPECT_FALSE(self->IsExceptionPending());
- GcRoot<mirror::Class> root(GetClassRoot<mirror::String>());
- size_t result = Invoke3(reinterpret_cast<size_t>(&root), 0U, 0U, readBarrierForRootSlow, self);
+ GcRoot<mirror::Class> root(GetClassRoot<mirror::String>());
+ size_t result = Invoke3(reinterpret_cast<size_t>(&root), 0U, 0U, readBarrierForRootSlow, self);
- EXPECT_FALSE(self->IsExceptionPending());
- EXPECT_NE(reinterpret_cast<size_t>(nullptr), result);
- mirror::Class* klass = reinterpret_cast<mirror::Class*>(result);
- EXPECT_OBJ_PTR_EQ(klass, obj->GetClass());
-
- // Tests done.
-#else
+ EXPECT_FALSE(self->IsExceptionPending());
+ EXPECT_NE(reinterpret_cast<size_t>(nullptr), result);
+ mirror::Class* klass = reinterpret_cast<mirror::Class*>(result);
+ EXPECT_OBJ_PTR_EQ(klass, obj->GetClass());
+ return;
+ }
+#endif
LOG(INFO) << "Skipping read_barrier_for_root_slow";
// Force-print to std::cout so it's also outside the logcat.
std::cout << "Skipping read_barrier_for_root_slow" << std::endl;
-#endif
}
} // namespace art
diff --git a/runtime/arch/x86/asm_support_x86.h b/runtime/arch/x86/asm_support_x86.h
index 737d736..f688933 100644
--- a/runtime/arch/x86/asm_support_x86.h
+++ b/runtime/arch/x86/asm_support_x86.h
@@ -25,5 +25,7 @@
#define FRAME_SIZE_SAVE_EVERYTHING (48 + 64)
#define FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT FRAME_SIZE_SAVE_EVERYTHING
#define FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK FRAME_SIZE_SAVE_EVERYTHING
+#define SAVE_EVERYTHING_FRAME_EAX_OFFSET \
+ (FRAME_SIZE_SAVE_EVERYTHING - CALLEE_SAVE_EVERYTHING_NUM_CORE_SPILLS * POINTER_SIZE)
#endif // ART_RUNTIME_ARCH_X86_ASM_SUPPORT_X86_H_
diff --git a/runtime/arch/x86/fault_handler_x86.cc b/runtime/arch/x86/fault_handler_x86.cc
index 3a08ec5..c485f0d 100644
--- a/runtime/arch/x86/fault_handler_x86.cc
+++ b/runtime/arch/x86/fault_handler_x86.cc
@@ -25,6 +25,7 @@
#include "base/logging.h" // For VLOG.
#include "base/macros.h"
#include "base/safe_copy.h"
+#include "oat_quick_method_header.h"
#include "runtime_globals.h"
#include "thread-current-inl.h"
@@ -77,30 +78,18 @@
// Get the size of an instruction in bytes.
// Return 0 if the instruction is not handled.
-static uint32_t GetInstructionSize(const uint8_t* pc) {
- // Don't segfault if pc points to garbage.
- char buf[15]; // x86/x86-64 have a maximum instruction length of 15 bytes.
- ssize_t bytes = SafeCopy(buf, pc, sizeof(buf));
-
- if (bytes == 0) {
- // Nothing was readable.
- return 0;
- }
-
- if (bytes == -1) {
- // SafeCopy not supported, assume that the entire range is readable.
- bytes = 16;
- } else {
- pc = reinterpret_cast<uint8_t*>(buf);
- }
-
-#define INCREMENT_PC() \
- do { \
- pc++; \
- if (pc - startpc > bytes) { \
- return 0; \
- } \
+static uint32_t GetInstructionSize(const uint8_t* pc, size_t bytes) {
+#define FETCH_OR_SKIP_BYTE(assignment) \
+ do { \
+ if (bytes == 0u) { \
+ return 0u; \
+ } \
+ (assignment); \
+ ++pc; \
+ --bytes; \
} while (0)
+#define FETCH_BYTE(var) FETCH_OR_SKIP_BYTE((var) = *pc)
+#define SKIP_BYTE() FETCH_OR_SKIP_BYTE((void)0)
#if defined(__x86_64)
const bool x86_64 = true;
@@ -110,8 +99,8 @@
const uint8_t* startpc = pc;
- uint8_t opcode = *pc;
- INCREMENT_PC();
+ uint8_t opcode;
+ FETCH_BYTE(opcode);
uint8_t modrm;
bool has_modrm = false;
bool two_byte = false;
@@ -143,8 +132,7 @@
// Group 4
case 0x67:
- opcode = *pc;
- INCREMENT_PC();
+ FETCH_BYTE(opcode);
prefix_present = true;
break;
}
@@ -154,15 +142,13 @@
}
if (x86_64 && opcode >= 0x40 && opcode <= 0x4f) {
- opcode = *pc;
- INCREMENT_PC();
+ FETCH_BYTE(opcode);
}
if (opcode == 0x0f) {
// Two byte opcode
two_byte = true;
- opcode = *pc;
- INCREMENT_PC();
+ FETCH_BYTE(opcode);
}
bool unhandled_instruction = false;
@@ -175,8 +161,7 @@
case 0xb7:
case 0xbe: // movsx
case 0xbf:
- modrm = *pc;
- INCREMENT_PC();
+ FETCH_BYTE(modrm);
has_modrm = true;
break;
default:
@@ -195,32 +180,28 @@
case 0x3c:
case 0x3d:
case 0x85: // test.
- modrm = *pc;
- INCREMENT_PC();
+ FETCH_BYTE(modrm);
has_modrm = true;
break;
case 0x80: // group 1, byte immediate.
case 0x83:
case 0xc6:
- modrm = *pc;
- INCREMENT_PC();
+ FETCH_BYTE(modrm);
has_modrm = true;
immediate_size = 1;
break;
case 0x81: // group 1, word immediate.
case 0xc7: // mov
- modrm = *pc;
- INCREMENT_PC();
+ FETCH_BYTE(modrm);
has_modrm = true;
immediate_size = operand_size_prefix ? 2 : 4;
break;
case 0xf6:
case 0xf7:
- modrm = *pc;
- INCREMENT_PC();
+ FETCH_BYTE(modrm);
has_modrm = true;
switch ((modrm >> 3) & 7) { // Extract "reg/opcode" from "modr/m".
case 0: // test
@@ -255,7 +236,7 @@
// Check for SIB.
if (mod != 3U /* 0b11 */ && (modrm & 7U /* 0b111 */) == 4) {
- INCREMENT_PC(); // SIB
+ SKIP_BYTE(); // SIB
}
switch (mod) {
@@ -271,86 +252,79 @@
pc += displacement_size + immediate_size;
VLOG(signals) << "x86 instruction length calculated as " << (pc - startpc);
- if (pc - startpc > bytes) {
- return 0;
- }
return pc - startpc;
+
+#undef SKIP_BYTE
+#undef FETCH_BYTE
+#undef FETCH_OR_SKIP_BYTE
}
-void FaultManager::GetMethodAndReturnPcAndSp(siginfo_t* siginfo, void* context,
- ArtMethod** out_method,
- uintptr_t* out_return_pc,
- uintptr_t* out_sp,
- bool* out_is_stack_overflow) {
+uintptr_t FaultManager::GetFaultPc(siginfo_t* siginfo ATTRIBUTE_UNUSED, void* context) {
struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
- *out_sp = static_cast<uintptr_t>(uc->CTX_ESP);
- VLOG(signals) << "sp: " << std::hex << *out_sp;
- if (*out_sp == 0) {
- return;
+ if (uc->CTX_ESP == 0) {
+ VLOG(signals) << "Missing SP";
+ return 0u;
}
+ return uc->CTX_EIP;
+}
- // In the case of a stack overflow, the stack is not valid and we can't
- // get the method from the top of the stack. However it's in EAX(x86)/RDI(x86_64).
- uintptr_t* fault_addr = reinterpret_cast<uintptr_t*>(siginfo->si_addr);
- uintptr_t* overflow_addr = reinterpret_cast<uintptr_t*>(
-#if defined(__x86_64__)
- reinterpret_cast<uint8_t*>(*out_sp) - GetStackOverflowReservedBytes(InstructionSet::kX86_64));
-#else
- reinterpret_cast<uint8_t*>(*out_sp) - GetStackOverflowReservedBytes(InstructionSet::kX86));
-#endif
- if (overflow_addr == fault_addr) {
- *out_method = reinterpret_cast<ArtMethod*>(uc->CTX_METHOD);
- *out_is_stack_overflow = true;
- } else {
- // The method is at the top of the stack.
- *out_method = *reinterpret_cast<ArtMethod**>(*out_sp);
- *out_is_stack_overflow = false;
- }
-
- uint8_t* pc = reinterpret_cast<uint8_t*>(uc->CTX_EIP);
- VLOG(signals) << HexDump(pc, 32, true, "PC ");
-
- if (pc == nullptr) {
- // Somebody jumped to 0x0. Definitely not ours, and will definitely segfault below.
- *out_method = nullptr;
- return;
- }
-
- uint32_t instr_size = GetInstructionSize(pc);
- if (instr_size == 0) {
- // Unknown instruction, tell caller it's not ours.
- *out_method = nullptr;
- return;
- }
- *out_return_pc = reinterpret_cast<uintptr_t>(pc + instr_size);
+uintptr_t FaultManager::GetFaultSp(void* context) {
+ struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
+ return uc->CTX_ESP;
}
bool NullPointerHandler::Action(int, siginfo_t* sig, void* context) {
- if (!IsValidImplicitCheck(sig)) {
- return false;
- }
- struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
- uint8_t* pc = reinterpret_cast<uint8_t*>(uc->CTX_EIP);
- uint8_t* sp = reinterpret_cast<uint8_t*>(uc->CTX_ESP);
-
- uint32_t instr_size = GetInstructionSize(pc);
- if (instr_size == 0) {
- // Unknown instruction, can't really happen.
+ uintptr_t fault_address = reinterpret_cast<uintptr_t>(sig->si_addr);
+ if (!IsValidFaultAddress(fault_address)) {
return false;
}
- // We need to arrange for the signal handler to return to the null pointer
- // exception generator. The return address must be the address of the
- // next instruction (this instruction + instruction size). The return address
- // is on the stack at the top address of the current frame.
+ struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
+ ArtMethod** sp = reinterpret_cast<ArtMethod**>(uc->CTX_ESP);
+ ArtMethod* method = *sp;
+ if (!IsValidMethod(method)) {
+ return false;
+ }
- // Push the return address and fault address onto the stack.
- uintptr_t retaddr = reinterpret_cast<uintptr_t>(pc + instr_size);
- uintptr_t* next_sp = reinterpret_cast<uintptr_t*>(sp - 2 * sizeof(uintptr_t));
- next_sp[1] = retaddr;
- next_sp[0] = reinterpret_cast<uintptr_t>(sig->si_addr);
+ // For null checks in compiled code we insert a stack map that is immediately
+ // after the load/store instruction that might cause the fault and we need to
+ // pass the return PC to the handler. For null checks in Nterp, we similarly
+ // need the return PC to recognize that this was a null check in Nterp, so
+ // that the handler can get the needed data from the Nterp frame.
+
+ // Note: Allowing nested faults if `IsValidMethod()` returned a false positive.
+ // Note: The `ArtMethod::GetOatQuickMethodHeader()` can acquire locks, which is
+ // essentially unsafe in a signal handler, but we allow that here just like in
+ // `NullPointerHandler::IsValidReturnPc()`. For more details see comments there.
+ uintptr_t pc = uc->CTX_EIP;
+ const OatQuickMethodHeader* method_header = method->GetOatQuickMethodHeader(pc);
+ if (method_header == nullptr) {
+ VLOG(signals) << "No method header.";
+ return false;
+ }
+ const uint8_t* pc_ptr = reinterpret_cast<const uint8_t*>(pc);
+ size_t offset = pc_ptr - method_header->GetCode();
+ size_t code_size = method_header->GetCodeSize();
+ CHECK_LT(offset, code_size);
+ size_t max_instr_size = code_size - offset;
+ uint32_t instr_size = GetInstructionSize(pc_ptr, max_instr_size);
+ if (instr_size == 0u) {
+ // Unknown instruction (can't really happen) or not enough bytes until end of method code.
+ return false;
+ }
+
+ uintptr_t return_pc = reinterpret_cast<uintptr_t>(pc + instr_size);
+ if (!IsValidReturnPc(sp, return_pc)) {
+ return false;
+ }
+
+ // Push the return PC and fault address onto the stack.
+ uintptr_t* next_sp = reinterpret_cast<uintptr_t*>(sp) - 2;
+ next_sp[1] = return_pc;
+ next_sp[0] = fault_address;
uc->CTX_ESP = reinterpret_cast<uintptr_t>(next_sp);
+ // Arrange for the signal handler to return to the NPE entrypoint.
uc->CTX_EIP = reinterpret_cast<uintptr_t>(
art_quick_throw_null_pointer_exception_from_signal);
VLOG(signals) << "Generating null pointer exception";
@@ -385,7 +359,7 @@
#endif
uint8_t checkinst2[] = {0x85, 0x00};
- struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
+ struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
uint8_t* pc = reinterpret_cast<uint8_t*>(uc->CTX_EIP);
uint8_t* sp = reinterpret_cast<uint8_t*>(uc->CTX_ESP);
@@ -441,7 +415,7 @@
// address for the previous method is on the stack at ESP.
bool StackOverflowHandler::Action(int, siginfo_t* info, void* context) {
- struct ucontext *uc = reinterpret_cast<struct ucontext*>(context);
+ struct ucontext* uc = reinterpret_cast<struct ucontext*>(context);
uintptr_t sp = static_cast<uintptr_t>(uc->CTX_ESP);
uintptr_t fault_addr = reinterpret_cast<uintptr_t>(info->si_addr);
diff --git a/runtime/arch/x86/instruction_set_features_x86.cc b/runtime/arch/x86/instruction_set_features_x86.cc
index 325f47f..f11aca9 100644
--- a/runtime/arch/x86/instruction_set_features_x86.cc
+++ b/runtime/arch/x86/instruction_set_features_x86.cc
@@ -44,6 +44,9 @@
"atom",
"sandybridge",
"silvermont",
+ "goldmont",
+ "goldmont-plus",
+ "tremont",
"kabylake",
"default",
};
@@ -52,24 +55,36 @@
"atom",
"sandybridge",
"silvermont",
+ "goldmont",
+ "goldmont-plus",
+ "tremont",
"kabylake",
};
static constexpr const char* x86_variants_with_sse4_1[] = {
"sandybridge",
"silvermont",
+ "goldmont",
+ "goldmont-plus",
+ "tremont",
"kabylake",
};
static constexpr const char* x86_variants_with_sse4_2[] = {
"sandybridge",
"silvermont",
+ "goldmont",
+ "goldmont-plus",
+ "tremont",
"kabylake",
};
static constexpr const char* x86_variants_with_popcnt[] = {
"sandybridge",
"silvermont",
+ "goldmont",
+ "goldmont-plus",
+ "tremont",
"kabylake",
};
static constexpr const char* x86_variants_with_avx[] = {
diff --git a/runtime/arch/x86/instruction_set_features_x86_test.cc b/runtime/arch/x86/instruction_set_features_x86_test.cc
index ce8e9f4..c50360a 100644
--- a/runtime/arch/x86/instruction_set_features_x86_test.cc
+++ b/runtime/arch/x86/instruction_set_features_x86_test.cc
@@ -110,6 +110,81 @@
EXPECT_FALSE(x86_64_features->Equals(x86_features.get()));
}
+TEST(X86InstructionSetFeaturesTest, X86FeaturesFromGoldmontVariant) {
+ // Build features for a 32-bit x86 goldmont processor.
+ std::string error_msg;
+ std::unique_ptr<const InstructionSetFeatures> x86_features(
+ InstructionSetFeatures::FromVariant(InstructionSet::kX86, "goldmont", &error_msg));
+ ASSERT_TRUE(x86_features.get() != nullptr) << error_msg;
+ EXPECT_EQ(x86_features->GetInstructionSet(), InstructionSet::kX86);
+ EXPECT_TRUE(x86_features->Equals(x86_features.get()));
+ EXPECT_STREQ("ssse3,sse4.1,sse4.2,-avx,-avx2,popcnt",
+ x86_features->GetFeatureString().c_str());
+ EXPECT_EQ(x86_features->AsBitmap(), 39U);
+
+ // Build features for a 64-bit x86-64 goldmont processor.
+ std::unique_ptr<const InstructionSetFeatures> x86_64_features(
+ InstructionSetFeatures::FromVariant(InstructionSet::kX86_64, "goldmont", &error_msg));
+ ASSERT_TRUE(x86_64_features.get() != nullptr) << error_msg;
+ EXPECT_EQ(x86_64_features->GetInstructionSet(), InstructionSet::kX86_64);
+ EXPECT_TRUE(x86_64_features->Equals(x86_64_features.get()));
+ EXPECT_STREQ("ssse3,sse4.1,sse4.2,-avx,-avx2,popcnt",
+ x86_64_features->GetFeatureString().c_str());
+ EXPECT_EQ(x86_64_features->AsBitmap(), 39U);
+
+ EXPECT_FALSE(x86_64_features->Equals(x86_features.get()));
+}
+
+TEST(X86InstructionSetFeaturesTest, X86FeaturesFromGoldmontPlusVariant) {
+ // Build features for a 32-bit x86 goldmont-plus processor.
+ std::string error_msg;
+ std::unique_ptr<const InstructionSetFeatures> x86_features(
+ InstructionSetFeatures::FromVariant(InstructionSet::kX86, "goldmont-plus", &error_msg));
+ ASSERT_TRUE(x86_features.get() != nullptr) << error_msg;
+ EXPECT_EQ(x86_features->GetInstructionSet(), InstructionSet::kX86);
+ EXPECT_TRUE(x86_features->Equals(x86_features.get()));
+ EXPECT_STREQ("ssse3,sse4.1,sse4.2,-avx,-avx2,popcnt",
+ x86_features->GetFeatureString().c_str());
+ EXPECT_EQ(x86_features->AsBitmap(), 39U);
+
+ // Build features for a 64-bit x86-64 goldmont-plus processor.
+ std::unique_ptr<const InstructionSetFeatures> x86_64_features(
+ InstructionSetFeatures::FromVariant(InstructionSet::kX86_64, "goldmont-plus", &error_msg));
+ ASSERT_TRUE(x86_64_features.get() != nullptr) << error_msg;
+ EXPECT_EQ(x86_64_features->GetInstructionSet(), InstructionSet::kX86_64);
+ EXPECT_TRUE(x86_64_features->Equals(x86_64_features.get()));
+ EXPECT_STREQ("ssse3,sse4.1,sse4.2,-avx,-avx2,popcnt",
+ x86_64_features->GetFeatureString().c_str());
+ EXPECT_EQ(x86_64_features->AsBitmap(), 39U);
+
+ EXPECT_FALSE(x86_64_features->Equals(x86_features.get()));
+}
+
+TEST(X86InstructionSetFeaturesTest, X86FeaturesFromTremontVariant) {
+ // Build features for a 32-bit x86 tremont processor.
+ std::string error_msg;
+ std::unique_ptr<const InstructionSetFeatures> x86_features(
+ InstructionSetFeatures::FromVariant(InstructionSet::kX86, "tremont", &error_msg));
+ ASSERT_TRUE(x86_features.get() != nullptr) << error_msg;
+ EXPECT_EQ(x86_features->GetInstructionSet(), InstructionSet::kX86);
+ EXPECT_TRUE(x86_features->Equals(x86_features.get()));
+ EXPECT_STREQ("ssse3,sse4.1,sse4.2,-avx,-avx2,popcnt",
+ x86_features->GetFeatureString().c_str());
+ EXPECT_EQ(x86_features->AsBitmap(), 39U);
+
+ // Build features for a 64-bit x86-64 tremont processor.
+ std::unique_ptr<const InstructionSetFeatures> x86_64_features(
+ InstructionSetFeatures::FromVariant(InstructionSet::kX86_64, "tremont", &error_msg));
+ ASSERT_TRUE(x86_64_features.get() != nullptr) << error_msg;
+ EXPECT_EQ(x86_64_features->GetInstructionSet(), InstructionSet::kX86_64);
+ EXPECT_TRUE(x86_64_features->Equals(x86_64_features.get()));
+ EXPECT_STREQ("ssse3,sse4.1,sse4.2,-avx,-avx2,popcnt",
+ x86_64_features->GetFeatureString().c_str());
+ EXPECT_EQ(x86_64_features->AsBitmap(), 39U);
+
+ EXPECT_FALSE(x86_64_features->Equals(x86_features.get()));
+}
+
TEST(X86InstructionSetFeaturesTest, X86FeaturesFromKabylakeVariant) {
// Build features for a 32-bit kabylake x86 processor.
std::string error_msg;
diff --git a/runtime/arch/x86/jni_entrypoints_x86.S b/runtime/arch/x86/jni_entrypoints_x86.S
index d827509..c7cf856 100644
--- a/runtime/arch/x86/jni_entrypoints_x86.S
+++ b/runtime/arch/x86/jni_entrypoints_x86.S
@@ -98,7 +98,7 @@
// for @FastNative or @CriticalNative.
movl (%esp), %eax // Thread* self
movl THREAD_TOP_QUICK_FRAME_OFFSET(%eax), %eax // uintptr_t tagged_quick_frame
- andl LITERAL(0xfffffffe), %eax // ArtMethod** sp
+ andl LITERAL(TAGGED_JNI_SP_MASK_TOGGLED32), %eax // ArtMethod** sp
movl (%eax), %eax // ArtMethod* method
testl LITERAL(ACCESS_FLAGS_METHOD_IS_FAST_NATIVE | ACCESS_FLAGS_METHOD_IS_CRITICAL_NATIVE), \
ART_METHOD_ACCESS_FLAGS_OFFSET(%eax)
@@ -286,6 +286,12 @@
JNI_SAVE_MANAGED_ARGS_TRAMPOLINE art_jni_method_start, artJniMethodStart, fs:THREAD_SELF_OFFSET
/*
+ * Trampoline to `artJniMethodEntryHook` that preserves all managed arguments.
+ */
+JNI_SAVE_MANAGED_ARGS_TRAMPOLINE \
+ art_jni_method_entry_hook, artJniMethodEntryHook, fs:THREAD_SELF_OFFSET
+
+ /*
* Trampoline to `artJniMonitoredMethodStart()` that preserves all managed arguments.
*/
JNI_SAVE_MANAGED_ARGS_TRAMPOLINE \
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index 7f1311c..c768229 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -794,12 +794,9 @@
call CALLVAR(cxx_name) // cxx_name(arg1, Thread*)
addl MACRO_LITERAL(16), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-16)
- testl %eax, %eax // If result is null, deliver the OOME.
+ testl %eax, %eax // If result is null deliver pending exception
jz 1f
- CFI_REMEMBER_STATE
- RESTORE_SAVE_EVERYTHING_FRAME_KEEP_EAX // restore frame up to return address
- ret // return
- CFI_RESTORE_STATE_AND_DEF_CFA esp, FRAME_SIZE_SAVE_EVERYTHING
+ DEOPT_OR_RESTORE_SAVE_EVERYTHING_FRAME_AND_RETURN_EAX ebx, /* is_ref= */1 // Check for deopt
1:
DELIVER_PENDING_EXCEPTION_FRAME_READY
END_FUNCTION VAR(c_name)
@@ -809,18 +806,72 @@
ONE_ARG_SAVE_EVERYTHING_DOWNCALL \c_name, \cxx_name, RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET
END_MACRO
-MACRO0(RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER)
- testl %eax, %eax // eax == 0 ?
- jz 1f // if eax == 0 goto 1
- ret // return
-1: // deliver exception on current thread
+MACRO0(RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER)
+ testl %eax, %eax // eax == 0 ?
+ jz 1f // if eax == 0 goto 1
+ DEOPT_OR_RETURN ebx, /*is_ref=*/1 // check if deopt is required
+1: // deliver exception on current thread
DELIVER_PENDING_EXCEPTION
END_MACRO
+MACRO0(RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION)
+ cmpl MACRO_LITERAL(0),%fs:THREAD_EXCEPTION_OFFSET // exception field == 0 ?
+ jne 1f // if exception field != 0 goto 1
+ DEOPT_OR_RETURN ebx // check if deopt is required
+1: // deliver exception on current thread
+ DELIVER_PENDING_EXCEPTION
+END_MACRO
+
+MACRO2(DEOPT_OR_RETURN, temp, is_ref = 0)
+ cmpl LITERAL(0), %fs:THREAD_DEOPT_CHECK_REQUIRED_OFFSET
+ jne 2f
+ ret
+2:
+ SETUP_SAVE_EVERYTHING_FRAME \temp
+ subl MACRO_LITERAL(4), %esp // alignment padding
+ CFI_ADJUST_CFA_OFFSET(4)
+ pushl MACRO_LITERAL(\is_ref) // is_ref
+ CFI_ADJUST_CFA_OFFSET(4)
+ PUSH_ARG eax // result
+ pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current
+ CFI_ADJUST_CFA_OFFSET(4)
+ call SYMBOL(artDeoptimizeIfNeeded)
+ addl LITERAL(16), %esp // pop arguments
+ CFI_REMEMBER_STATE
+ RESTORE_SAVE_EVERYTHING_FRAME
+ ret
+ CFI_RESTORE_STATE_AND_DEF_CFA esp, FRAME_SIZE_SAVE_EVERYTHING
+END_MACRO
+
+MACRO2(DEOPT_OR_RESTORE_SAVE_EVERYTHING_FRAME_AND_RETURN_EAX, temp, is_ref = 0)
+ cmpl LITERAL(0), %fs:THREAD_DEOPT_CHECK_REQUIRED_OFFSET
+ jne 2f
+ CFI_REMEMBER_STATE
+ RESTORE_SAVE_EVERYTHING_FRAME_KEEP_EAX
+ ret
+ CFI_RESTORE_STATE_AND_DEF_CFA esp, FRAME_SIZE_SAVE_EVERYTHING
+2:
+ movl %eax, SAVE_EVERYTHING_FRAME_EAX_OFFSET(%esp) // update eax in the frame
+ subl MACRO_LITERAL(4), %esp // alignment padding
+ CFI_ADJUST_CFA_OFFSET(4)
+ pushl MACRO_LITERAL(\is_ref) // is_ref
+ CFI_ADJUST_CFA_OFFSET(4)
+ PUSH_ARG eax // result
+ pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current
+ CFI_ADJUST_CFA_OFFSET(4)
+ call SYMBOL(artDeoptimizeIfNeeded)
+ addl LITERAL(16), %esp // pop arguments
+ CFI_REMEMBER_STATE
+ RESTORE_SAVE_EVERYTHING_FRAME
+ ret
+ CFI_RESTORE_STATE_AND_DEF_CFA esp, FRAME_SIZE_SAVE_EVERYTHING
+END_MACRO
+
+
MACRO0(RETURN_IF_EAX_ZERO)
testl %eax, %eax // eax == 0 ?
jnz 1f // if eax != 0 goto 1
- ret // return
+ DEOPT_OR_RETURN ebx // check if deopt is needed
1: // deliver exception on current thread
DELIVER_PENDING_EXCEPTION
END_MACRO
@@ -927,7 +978,7 @@
addl LITERAL(16), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-16)
RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER // return or deliver exception
END_FUNCTION VAR(c_name)
END_MACRO
@@ -974,7 +1025,7 @@
addl LITERAL(16), %esp
CFI_ADJUST_CFA_OFFSET(-16)
RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER // return or deliver exception
END_MACRO
MACRO2(ART_QUICK_ALLOC_OBJECT_TLAB, c_name, cxx_name)
@@ -1107,7 +1158,7 @@
addl LITERAL(16), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-16)
RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER // return or deliver exception
END_FUNCTION VAR(c_entrypoint)
END_MACRO
@@ -1254,126 +1305,102 @@
.endif
END_MACRO
- /*
- * Macro to insert read barrier, only used in art_quick_aput_obj.
- * obj_reg and dest_reg are registers, offset is a defined literal such as
- * MIRROR_OBJECT_CLASS_OFFSET.
- * pop_eax is a boolean flag, indicating if eax is popped after the call.
- * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path.
- */
-MACRO4(READ_BARRIER, obj_reg, offset, dest_reg, pop_eax)
-#ifdef USE_READ_BARRIER
- PUSH eax // save registers used in art_quick_aput_obj
- PUSH ebx
- PUSH edx
- PUSH ecx
- // Outgoing argument set up
- pushl MACRO_LITERAL((RAW_VAR(offset))) // pass offset, double parentheses are necessary
- CFI_ADJUST_CFA_OFFSET(4)
- PUSH RAW_VAR(obj_reg) // pass obj_reg
- PUSH eax // pass ref, just pass eax for now since parameter ref is unused
- call SYMBOL(artReadBarrierSlow) // artReadBarrierSlow(ref, obj_reg, offset)
- // No need to unpoison return value in eax, artReadBarrierSlow() would do the unpoisoning.
- .ifnc RAW_VAR(dest_reg), eax
- movl %eax, REG_VAR(dest_reg) // save loaded ref in dest_reg
- .endif
- addl MACRO_LITERAL(12), %esp // pop arguments
- CFI_ADJUST_CFA_OFFSET(-12)
- POP_REG_NE ecx, RAW_VAR(dest_reg) // Restore args except dest_reg
- POP_REG_NE edx, RAW_VAR(dest_reg)
- POP_REG_NE ebx, RAW_VAR(dest_reg)
- .ifc RAW_VAR(pop_eax), true
- POP_REG_NE eax, RAW_VAR(dest_reg)
- .endif
-#else
- movl RAW_VAR(offset)(REG_VAR(obj_reg)), REG_VAR(dest_reg)
- UNPOISON_HEAP_REF RAW_VAR(dest_reg)
-#endif // USE_READ_BARRIER
-END_MACRO
-
DEFINE_FUNCTION art_quick_aput_obj
test %edx, %edx // store of null
- jz .Ldo_aput_null
- READ_BARRIER eax, MIRROR_OBJECT_CLASS_OFFSET, ebx, true
- READ_BARRIER ebx, MIRROR_CLASS_COMPONENT_TYPE_OFFSET, ebx, true
- // value's type == array's component type - trivial assignability
-#if defined(USE_READ_BARRIER)
- READ_BARRIER edx, MIRROR_OBJECT_CLASS_OFFSET, eax, false
- cmpl %eax, %ebx
- POP eax // restore eax from the push in the beginning of READ_BARRIER macro
- // This asymmetric push/pop saves a push of eax and maintains stack alignment.
-#elif defined(USE_HEAP_POISONING)
- PUSH eax // save eax
- movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %eax
- UNPOISON_HEAP_REF eax
- cmpl %eax, %ebx
- POP eax // restore eax
-#else
- cmpl MIRROR_OBJECT_CLASS_OFFSET(%edx), %ebx
-#endif
- jne .Lcheck_assignability
-.Ldo_aput:
+ jz .Laput_obj_null
+ movl MIRROR_OBJECT_CLASS_OFFSET(%eax), %ebx
+ UNPOISON_HEAP_REF ebx
+#ifdef USE_READ_BARRIER
+ cmpl LITERAL(0), %fs:THREAD_IS_GC_MARKING_OFFSET
+ jnz .Laput_obj_gc_marking
+#endif // USE_READ_BARRIER
+ movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%ebx), %ebx
+ cmpl MIRROR_OBJECT_CLASS_OFFSET(%edx), %ebx // Both poisoned if heap poisoning is enabled.
+ jne .Laput_obj_check_assignability
+.Laput_obj_store:
POISON_HEAP_REF edx
movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4)
movl %fs:THREAD_CARD_TABLE_OFFSET, %edx
shrl LITERAL(CARD_TABLE_CARD_SHIFT), %eax
movb %dl, (%edx, %eax)
ret
-.Ldo_aput_null:
+
+.Laput_obj_null:
movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4)
ret
-.Lcheck_assignability:
- PUSH eax // save arguments
- PUSH ecx
- PUSH edx
-#if defined(USE_READ_BARRIER)
- subl LITERAL(4), %esp // alignment padding
- CFI_ADJUST_CFA_OFFSET(4)
- READ_BARRIER edx, MIRROR_OBJECT_CLASS_OFFSET, eax, true
- subl LITERAL(4), %esp // alignment padding
- CFI_ADJUST_CFA_OFFSET(4)
- PUSH eax // pass arg2 - type of the value to be stored
-#elif defined(USE_HEAP_POISONING)
- subl LITERAL(8), %esp // alignment padding
- CFI_ADJUST_CFA_OFFSET(8)
+
+.Laput_obj_check_assignability:
+ UNPOISON_HEAP_REF ebx // Unpoison array component type if poisoning is enabled.
+ PUSH_ARG eax // Save `art_quick_aput_obj()` arguments.
+ PUSH_ARG ecx
+ PUSH_ARG edx
+ INCREASE_FRAME 8 // Alignment padding.
+ // Pass arg2 - type of the value to be stored.
+#if defined(USE_HEAP_POISONING)
movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %eax
UNPOISON_HEAP_REF eax
- PUSH eax // pass arg2 - type of the value to be stored
+ PUSH_ARG eax
#else
- subl LITERAL(8), %esp // alignment padding
- CFI_ADJUST_CFA_OFFSET(8)
- pushl MIRROR_OBJECT_CLASS_OFFSET(%edx) // pass arg2 - type of the value to be stored
+ pushl MIRROR_OBJECT_CLASS_OFFSET(%edx)
CFI_ADJUST_CFA_OFFSET(4)
#endif
- PUSH ebx // pass arg1 - component type of the array
+.Laput_obj_check_assignability_call:
+ PUSH_ARG ebx // Pass arg1 - component type of the array.
call SYMBOL(artIsAssignableFromCode) // (Class* a, Class* b)
- addl LITERAL(16), %esp // pop arguments
- CFI_ADJUST_CFA_OFFSET(-16)
+ DECREASE_FRAME 16 // Pop `artIsAssignableFromCode()` arguments
testl %eax, %eax
+ POP_ARG edx // Pop `art_quick_aput_obj()` arguments; flags unaffected.
+ POP_ARG ecx
+ POP_ARG eax
jz .Lthrow_array_store_exception
- POP edx
- POP ecx
- POP eax
POISON_HEAP_REF edx
- movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4) // do the aput
+ movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%eax, %ecx, 4) // Do the aput.
movl %fs:THREAD_CARD_TABLE_OFFSET, %edx
shrl LITERAL(CARD_TABLE_CARD_SHIFT), %eax
movb %dl, (%edx, %eax)
ret
- CFI_ADJUST_CFA_OFFSET(12) // 3 POP after the jz for unwinding.
+
.Lthrow_array_store_exception:
- POP edx
- POP ecx
- POP eax
- SETUP_SAVE_ALL_CALLEE_SAVES_FRAME ebx // save all registers as basis for long jump context
- // Outgoing argument set up
- PUSH eax // alignment padding
- pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
- CFI_ADJUST_CFA_OFFSET(4)
- PUSH edx // pass arg2 - value
- PUSH eax // pass arg1 - array
+#ifdef USE_READ_BARRIER
+ CFI_REMEMBER_STATE
+#endif // USE_READ_BARRIER
+ SETUP_SAVE_ALL_CALLEE_SAVES_FRAME ebx // Save all registers as basis for long jump context.
+ // Outgoing argument set up.
+ PUSH_ARG eax // Alignment padding.
+ PUSH_ARG fs:THREAD_SELF_OFFSET // Pass Thread::Current()
+ PUSH_ARG edx // Pass arg2 - value.
+ PUSH_ARG eax // Pass arg1 - array.
call SYMBOL(artThrowArrayStoreException) // (array, value, Thread*)
UNREACHABLE
+
+#ifdef USE_READ_BARRIER
+ CFI_RESTORE_STATE_AND_DEF_CFA esp, 4
+.Laput_obj_gc_marking:
+ PUSH_ARG eax // Save `art_quick_aput_obj()` arguments.
+ PUSH_ARG ecx // We need to align stack for `art_quick_read_barrier_mark_regNN`
+ PUSH_ARG edx // and use a register (EAX) as a temporary for the object class.
+ call SYMBOL(art_quick_read_barrier_mark_reg03) // Mark EBX.
+ movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%ebx), %ebx
+ UNPOISON_HEAP_REF ebx
+ call SYMBOL(art_quick_read_barrier_mark_reg03) // Mark EBX.
+ movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %eax
+ UNPOISON_HEAP_REF eax
+ call SYMBOL(art_quick_read_barrier_mark_reg00) // Mark EAX.
+ cmpl %eax, %ebx
+ jne .Laput_obj_check_assignability_gc_marking
+ POP_ARG edx // Restore `art_quick_aput_obj()` arguments.
+ POP_ARG ecx
+ POP_ARG eax
+ jmp .Laput_obj_store
+
+.Laput_obj_check_assignability_gc_marking:
+ // Prepare arguments in line with `.Laput_obj_check_assignability_call` and jump there.
+ // (EAX, ECX and EDX were already saved in the right stack slots.)
+ INCREASE_FRAME 8 // Alignment padding.
+ PUSH_ARG eax // Pass arg2 - type of the value to be stored.
+ // The arg1 shall be pushed at `.Laput_obj_check_assignability_call`.
+ jmp .Laput_obj_check_assignability_call
+#endif // USE_READ_BARRIER
END_FUNCTION art_quick_aput_obj
DEFINE_FUNCTION art_quick_memcpy
@@ -1501,21 +1528,21 @@
// Note: Functions `art{Get,Set}<Kind>{Static,Instance}FromCompiledCode` are
// defined with a macro in runtime/entrypoints/quick/quick_field_entrypoints.cc.
-ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-ONE_ARG_REF_DOWNCALL art_quick_get64_static, artGet64StaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get64_static, artGet64StaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get64_instance, artGet64InstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get64_instance, artGet64InstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
TWO_ARG_REF_DOWNCALL art_quick_set8_static, artSet8StaticFromCompiledCode, RETURN_IF_EAX_ZERO
TWO_ARG_REF_DOWNCALL art_quick_set16_static, artSet16StaticFromCompiledCode, RETURN_IF_EAX_ZERO
@@ -1616,7 +1643,7 @@
movl %eax, %edi // remember code pointer in EDI
addl LITERAL(16), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-16)
- test %eax, %eax // if code pointer is null goto deliver pending exception
+ test %eax, %eax // if code pointer is null goto deliver the OOME.
jz 1f
RESTORE_SAVE_REFS_AND_ARGS_FRAME_AND_JUMP
1:
@@ -1686,6 +1713,16 @@
CFI_REMEMBER_STATE
CFI_DEF_CFA_REGISTER(esp)
+ // Quick expects the return value to be in xmm0.
+ movd %eax, %xmm0
+ movd %edx, %xmm1
+ punpckldq %xmm1, %xmm0
+
+ LOAD_RUNTIME_INSTANCE ebx
+ cmpb MACRO_LITERAL(0), INSTRUMENTATION_STUBS_INSTALLED_OFFSET_FROM_RUNTIME_INSTANCE(%ebx)
+ jne .Lcall_method_exit_hook
+.Lcall_method_exit_hook_done:
+
// Tear down the callee-save frame.
// Remove space for FPR args and EAX
addl LITERAL(4 + 4 * 8), %esp
@@ -1698,12 +1735,13 @@
POP ebp // Restore callee saves
POP esi
POP edi
- // Quick expects the return value to be in xmm0.
- movd %eax, %xmm0
- movd %edx, %xmm1
- punpckldq %xmm1, %xmm0
ret
+.Lcall_method_exit_hook:
+ movl LITERAL(FRAME_SIZE_SAVE_REFS_AND_ARGS), %ebx
+ call art_quick_method_exit_hook
+ jmp .Lcall_method_exit_hook_done
+
// Undo the unwinding information from above since it doesn't apply below.
CFI_RESTORE_STATE_AND_DEF_CFA ebp, 64
.Lexception_in_native:
@@ -1974,34 +2012,29 @@
SETUP_SAVE_REFS_ONLY_FRAME ebx // save ref containing registers for GC
// Outgoing argument set up
leal FRAME_SIZE_SAVE_REFS_ONLY + __SIZEOF_POINTER__(%esp), %edi // prepare args
- push %eax // push padding
+ push %eax // push padding
CFI_ADJUST_CFA_OFFSET(4)
- pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
+ pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current()
CFI_ADJUST_CFA_OFFSET(4)
- push %edi // pass args
+ push %edi // pass args
CFI_ADJUST_CFA_OFFSET(4)
- push %eax // pass format
+ push %eax // pass format
CFI_ADJUST_CFA_OFFSET(4)
- call SYMBOL(artStringBuilderAppend) // (uint32_t, const unit32_t*, Thread*)
- addl MACRO_LITERAL(16), %esp // pop arguments
+ call SYMBOL(artStringBuilderAppend) // (uint32_t, const unit32_t*, Thread*)
+ addl MACRO_LITERAL(16), %esp // pop arguments
CFI_ADJUST_CFA_OFFSET(-16)
- RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception
+ RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER // return or deliver exception
END_FUNCTION art_quick_string_builder_append
// Create a function `name` calling the ReadBarrier::Mark routine,
// getting its argument and returning its result through register
// `reg`, saving and restoring all caller-save registers.
//
-// If `reg` is different from `eax`, the generated function follows a
-// non-standard runtime calling convention:
-// - register `reg` is used to pass the (sole) argument of this function
-// (instead of EAX);
-// - register `reg` is used to return the result of this function
-// (instead of EAX);
-// - EAX is treated like a normal (non-argument) caller-save register;
-// - everything else is the same as in the standard runtime calling
-// convention (e.g. standard callee-save registers are preserved).
+// The generated function follows a non-standard runtime calling convention:
+// - register `reg` (which may differ from EAX) is used to pass the (sole) argument,
+// - register `reg` (which may differ from EAX) is used to return the result,
+// - all other registers are callee-save (the values they hold are preserved).
MACRO2(READ_BARRIER_MARK_REG, name, reg)
DEFINE_FUNCTION VAR(name)
// Null check so that we can load the lock word.
@@ -2313,15 +2346,18 @@
DEFINE_FUNCTION art_quick_method_entry_hook
SETUP_SAVE_EVERYTHING_FRAME edx
mov FRAME_SIZE_SAVE_EVERYTHING(%esp), %eax // Fetch ArtMethod
- subl LITERAL(8), %esp
- CFI_ADJUST_CFA_OFFSET(8)
+ mov %esp, %edx // Store esp before pushing anything on stack.
+ subl LITERAL(4), %esp
+ CFI_ADJUST_CFA_OFFSET(4)
+ push %edx // Pass SP
+ CFI_ADJUST_CFA_OFFSET(4)
pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current().
CFI_ADJUST_CFA_OFFSET(4)
pushl %eax // Pass Method*.
CFI_ADJUST_CFA_OFFSET(4)
- call SYMBOL(artMethodEntryHook) // (Method*, Thread*)
+ call SYMBOL(artMethodEntryHook) // (Method*, Thread*, SP)
addl LITERAL(16), %esp // Pop arguments.
CFI_ADJUST_CFA_OFFSET(-16)
@@ -2331,40 +2367,34 @@
END_FUNCTION art_quick_method_entry_hook
DEFINE_FUNCTION art_quick_method_exit_hook
- SETUP_SAVE_EVERYTHING_FRAME ebx
+ PUSH edi
+ SETUP_SAVE_EVERYTHING_FRAME_EDI_SAVED edi
- mov FRAME_SIZE_SAVE_EVERYTHING(%esp), %ebx // Remember ArtMethod*
- subl LITERAL(8), %esp // Align stack.
- CFI_ADJUST_CFA_OFFSET(8)
+ leal FRAME_SIZE_SAVE_EVERYTHING(%esp), %edi // Remember ArtMethod**
+ subl LITERAL(4), %esp // Align stack.
+ CFI_ADJUST_CFA_OFFSET(4)
+
PUSH_ARG edx // Save gpr return value. edx and eax need to be together
// which isn't the case in kSaveEverything frame.
PUSH_ARG eax
movl %esp, %edx // Get pointer to gpr_result
- leal 32(%esp), %eax // Get pointer to fpr_result, in kSaveEverything frame
+ leal 28(%esp), %eax // Get pointer to fpr_result, in kSaveEverything frame
+ PUSH_ARG ebx // push frame_size
PUSH_ARG eax // Pass fpr_result
PUSH_ARG edx // Pass gpr_result
- PUSH_ARG ebx // Pass ArtMethod*
+ PUSH_ARG edi // Pass ArtMethod**
pushl %fs:THREAD_SELF_OFFSET // Pass Thread::Current.
CFI_ADJUST_CFA_OFFSET(4)
- call SYMBOL(artMethodExitHook) // (Thread*, ArtMethod*, gpr_result*, fpr_result*)
+ call SYMBOL(artMethodExitHook) // (Thread*, ArtMethod**, gpr_result*, fpr_result*,
+ // frame_size)
// Return result could have been changed if it's a reference.
- movl 16(%esp), %ecx
+ movl 20(%esp), %ecx
movl %ecx, (80+32)(%esp)
addl LITERAL(32), %esp // Pop arguments and grp_result.
CFI_ADJUST_CFA_OFFSET(-32)
- cmpl LITERAL(1), %eax // Check if we returned error.
- CFI_REMEMBER_STATE
- je .Ldo_deliver_instrumentation_exception_exit
-
// Normal return.
RESTORE_SAVE_EVERYTHING_FRAME
ret
-.Ldo_deliver_instrumentation_exception_exit:
- CFI_RESTORE_STATE_AND_DEF_CFA esp, FRAME_SIZE_SAVE_EVERYTHING
- DELIVER_PENDING_EXCEPTION_FRAME_READY
END_FUNCTION art_quick_method_exit_hook
-
-
-
diff --git a/runtime/arch/x86_64/asm_support_x86_64.S b/runtime/arch/x86_64/asm_support_x86_64.S
index bfec8c0..a2aa686 100644
--- a/runtime/arch/x86_64/asm_support_x86_64.S
+++ b/runtime/arch/x86_64/asm_support_x86_64.S
@@ -485,11 +485,10 @@
END_MACRO
MACRO0(RETURN_OR_DELIVER_PENDING_EXCEPTION)
- movq %gs:THREAD_EXCEPTION_OFFSET, %rcx // get exception field
- testq %rcx, %rcx // rcx == 0 ?
- jnz 1f // if rcx != 0 goto 1
- ret // return
-1: // deliver exception on current thread
+ cmpq MACRO_LITERAL(0), %gs:THREAD_EXCEPTION_OFFSET // compare exception field with 0
+ jne 1f // if exception != 0 goto 1
+ ret // return
+1: // deliver exception on current thread
DELIVER_PENDING_EXCEPTION
END_MACRO
diff --git a/runtime/arch/x86_64/asm_support_x86_64.h b/runtime/arch/x86_64/asm_support_x86_64.h
index 51befbe..e389c78 100644
--- a/runtime/arch/x86_64/asm_support_x86_64.h
+++ b/runtime/arch/x86_64/asm_support_x86_64.h
@@ -25,5 +25,7 @@
#define FRAME_SIZE_SAVE_EVERYTHING (144 + 16*8)
#define FRAME_SIZE_SAVE_EVERYTHING_FOR_CLINIT FRAME_SIZE_SAVE_EVERYTHING
#define FRAME_SIZE_SAVE_EVERYTHING_FOR_SUSPEND_CHECK FRAME_SIZE_SAVE_EVERYTHING
+#define SAVE_EVERYTHING_FRAME_RAX_OFFSET \
+ (FRAME_SIZE_SAVE_EVERYTHING - CALLEE_SAVE_EVERYTHING_NUM_CORE_SPILLS * POINTER_SIZE)
#endif // ART_RUNTIME_ARCH_X86_64_ASM_SUPPORT_X86_64_H_
diff --git a/runtime/arch/x86_64/jni_entrypoints_x86_64.S b/runtime/arch/x86_64/jni_entrypoints_x86_64.S
index 0d5fa3f..55f01b7 100644
--- a/runtime/arch/x86_64/jni_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/jni_entrypoints_x86_64.S
@@ -118,7 +118,7 @@
// Call artFindNativeMethod() for normal native and artFindNativeMethodRunnable()
// for @FastNative or @CriticalNative.
movq THREAD_TOP_QUICK_FRAME_OFFSET(%rdi), %rax // uintptr_t tagged_quick_frame
- andq LITERAL(0xfffffffffffffffe), %rax // ArtMethod** sp
+ andq LITERAL(TAGGED_JNI_SP_MASK_TOGGLED64), %rax // ArtMethod** sp
movq (%rax), %rax // ArtMethod* method
testl LITERAL(ACCESS_FLAGS_METHOD_IS_FAST_NATIVE | ACCESS_FLAGS_METHOD_IS_CRITICAL_NATIVE), \
ART_METHOD_ACCESS_FLAGS_OFFSET(%rax)
@@ -400,6 +400,12 @@
JNI_SAVE_MANAGED_ARGS_TRAMPOLINE art_jni_method_start, artJniMethodStart, gs:THREAD_SELF_OFFSET
/*
+ * Trampoline to `artJniMethodEntryHook` that preserves all managed arguments.
+ */
+JNI_SAVE_MANAGED_ARGS_TRAMPOLINE \
+ art_jni_method_entry_hook, artJniMethodEntryHook, gs:THREAD_SELF_OFFSET
+
+ /*
* Trampoline to `artJniMonitoredMethodStart()` that preserves all managed arguments.
*/
JNI_SAVE_MANAGED_ARGS_TRAMPOLINE \
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 673696c..babd1bc 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -731,12 +731,9 @@
movl %eax, %edi // pass the index of the constant as arg0
movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current()
call CALLVAR(cxx_name) // cxx_name(arg0, Thread*)
- testl %eax, %eax // If result is null, deliver the OOME.
+ testl %eax, %eax // If result is null, deliver pending exception.
jz 1f
- CFI_REMEMBER_STATE
- RESTORE_SAVE_EVERYTHING_FRAME_KEEP_RAX // restore frame up to return address
- ret
- CFI_RESTORE_STATE_AND_DEF_CFA rsp, FRAME_SIZE_SAVE_EVERYTHING
+ DEOPT_OR_RESTORE_SAVE_EVERYTHING_FRAME_AND_RETURN_RAX /*is_ref=*/1
1:
DELIVER_PENDING_EXCEPTION_FRAME_READY
END_FUNCTION VAR(c_name)
@@ -746,18 +743,65 @@
ONE_ARG_SAVE_EVERYTHING_DOWNCALL \c_name, \cxx_name, RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET
END_MACRO
-MACRO0(RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER)
+MACRO0(RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER)
testq %rax, %rax // rax == 0 ?
jz 1f // if rax == 0 goto 1
- ret // return
+ DEOPT_OR_RETURN /*is_ref=*/1 // Check if deopt is required
1: // deliver exception on current thread
DELIVER_PENDING_EXCEPTION
END_MACRO
+
+MACRO0(RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION)
+ movq %gs:THREAD_EXCEPTION_OFFSET, %rcx // get exception field
+ testq %rcx, %rcx // rcx == 0 ?
+ jnz 1f // if rcx != 0 goto 1
+ DEOPT_OR_RETURN // Check if deopt is required
+1: // deliver exception on current thread
+ DELIVER_PENDING_EXCEPTION
+END_MACRO
+
+MACRO1(DEOPT_OR_RETURN, is_ref = 0)
+ cmpl LITERAL(0), %gs:THREAD_DEOPT_CHECK_REQUIRED_OFFSET
+ jne 2f
+ ret
+2:
+ SETUP_SAVE_EVERYTHING_FRAME
+ movq LITERAL(\is_ref), %rdx // pass if result is a reference
+ movq %rax, %rsi // pass the result
+ movq %gs:THREAD_SELF_OFFSET, %rdi // pass Thread::Current
+ call SYMBOL(artDeoptimizeIfNeeded)
+ CFI_REMEMBER_STATE
+ RESTORE_SAVE_EVERYTHING_FRAME
+ ret
+ CFI_RESTORE_STATE_AND_DEF_CFA rsp, FRAME_SIZE_SAVE_EVERYTHING
+END_MACRO
+
+MACRO1(DEOPT_OR_RESTORE_SAVE_EVERYTHING_FRAME_AND_RETURN_RAX, is_ref = 0)
+ cmpl LITERAL(0), %gs:THREAD_DEOPT_CHECK_REQUIRED_OFFSET
+ jne 2f
+ CFI_REMEMBER_STATE
+ RESTORE_SAVE_EVERYTHING_FRAME_KEEP_RAX
+ ret
+ CFI_RESTORE_STATE_AND_DEF_CFA rsp, FRAME_SIZE_SAVE_EVERYTHING
+2:
+ movq %rax, SAVE_EVERYTHING_FRAME_RAX_OFFSET(%rsp) // update result in the frame
+ movq LITERAL(\is_ref), %rdx // pass if result is a reference
+ movq %rax, %rsi // pass the result
+ movq %gs:THREAD_SELF_OFFSET, %rdi // pass Thread::Current
+ call SYMBOL(artDeoptimizeIfNeeded)
+ CFI_REMEMBER_STATE
+ RESTORE_SAVE_EVERYTHING_FRAME
+ ret
+ CFI_RESTORE_STATE_AND_DEF_CFA rsp, FRAME_SIZE_SAVE_EVERYTHING
+END_MACRO
+
+
+
MACRO0(RETURN_IF_EAX_ZERO)
testl %eax, %eax // eax == 0 ?
jnz 1f // if eax != 0 goto 1
- ret // return
+ DEOPT_OR_RETURN // Check if we need a deopt
1: // deliver exception on current thread
DELIVER_PENDING_EXCEPTION
END_MACRO
@@ -859,7 +903,7 @@
movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current()
call CALLVAR(cxx_name) // cxx_name(arg0, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER // return or deliver exception
END_FUNCTION VAR(c_name)
END_MACRO
@@ -931,7 +975,7 @@
movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current()
call CALLVAR(cxx_name) // cxx_name(arg0, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER // return or deliver exception
END_MACRO
// A hand-written override for GENERATE_ALLOC_ENTRYPOINTS_ALLOC_OBJECT_RESOLVED(_tlab, TLAB). May be
@@ -1019,7 +1063,7 @@
movq %gs:THREAD_SELF_OFFSET, %rdx // pass Thread::Current()
call CALLVAR(cxx_name) // cxx_name(arg0, arg1, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER // return or deliver exception
END_FUNCTION VAR(c_entrypoint)
END_MACRO
@@ -1163,134 +1207,89 @@
.endif
END_MACRO
- /*
- * Macro to insert read barrier, used in art_quick_aput_obj.
- * obj_reg and dest_reg{32|64} are registers, offset is a defined literal such as
- * MIRROR_OBJECT_CLASS_OFFSET. dest_reg needs two versions to handle the mismatch between
- * 64b PUSH/POP and 32b argument.
- * TODO: When read barrier has a fast path, add heap unpoisoning support for the fast path.
- *
- * As with art_quick_aput_obj function, the 64b versions are in comments.
- */
-MACRO4(READ_BARRIER, obj_reg, offset, dest_reg32, dest_reg64)
-#ifdef USE_READ_BARRIER
- PUSH rax // save registers that might be used
- PUSH rdi
- PUSH rsi
- PUSH rdx
- PUSH rcx
- SETUP_FP_CALLEE_SAVE_FRAME
- // Outgoing argument set up
- // movl REG_VAR(ref_reg32), %edi // pass ref, no-op for now since parameter ref is unused
- // // movq REG_VAR(ref_reg64), %rdi
- movl REG_VAR(obj_reg), %esi // pass obj_reg
- // movq REG_VAR(obj_reg), %rsi
- movl MACRO_LITERAL((RAW_VAR(offset))), %edx // pass offset, double parentheses are necessary
- // movq MACRO_LITERAL((RAW_VAR(offset))), %rdx
- call SYMBOL(artReadBarrierSlow) // artReadBarrierSlow(ref, obj_reg, offset)
- // No need to unpoison return value in rax, artReadBarrierSlow() would do the unpoisoning.
- .ifnc RAW_VAR(dest_reg32), eax
- // .ifnc RAW_VAR(dest_reg64), rax
- movl %eax, REG_VAR(dest_reg32) // save loaded ref in dest_reg
- // movq %rax, REG_VAR(dest_reg64)
- .endif
- RESTORE_FP_CALLEE_SAVE_FRAME
- POP_REG_NE rcx, RAW_VAR(dest_reg64) // Restore registers except dest_reg
- POP_REG_NE rdx, RAW_VAR(dest_reg64)
- POP_REG_NE rsi, RAW_VAR(dest_reg64)
- POP_REG_NE rdi, RAW_VAR(dest_reg64)
- POP_REG_NE rax, RAW_VAR(dest_reg64)
-#else
- movl RAW_VAR(offset)(REG_VAR(obj_reg)), REG_VAR(dest_reg32)
- // movq RAW_VAR(offset)(REG_VAR(obj_reg)), REG_VAR(dest_reg64)
- UNPOISON_HEAP_REF RAW_VAR(dest_reg32) // UNPOISON_HEAP_REF only takes a 32b register
-#endif // USE_READ_BARRIER
-END_MACRO
-
DEFINE_FUNCTION art_quick_aput_obj
- testl %edx, %edx // store of null
-// test %rdx, %rdx
- jz .Ldo_aput_null
- READ_BARRIER edi, MIRROR_OBJECT_CLASS_OFFSET, ecx, rcx
- // READ_BARRIER rdi, MIRROR_OBJECT_CLASS_OFFSET, ecx, rcx
- READ_BARRIER ecx, MIRROR_CLASS_COMPONENT_TYPE_OFFSET, ecx, rcx
- // READ_BARRIER rcx, MIRROR_CLASS_COMPONENT_TYPE_OFFSET, ecx, rcx
-#if defined(USE_HEAP_POISONING) || defined(USE_READ_BARRIER)
- READ_BARRIER edx, MIRROR_OBJECT_CLASS_OFFSET, eax, rax // rax is free.
- // READ_BARRIER rdx, MIRROR_OBJECT_CLASS_OFFSET, eax, rax
- cmpl %eax, %ecx // value's type == array's component type - trivial assignability
-#else
- cmpl MIRROR_OBJECT_CLASS_OFFSET(%edx), %ecx // value's type == array's component type - trivial assignability
-// cmpq MIRROR_CLASS_OFFSET(%rdx), %rcx
-#endif
- jne .Lcheck_assignability
-.Ldo_aput:
+ test %edx, %edx // store of null
+ jz .Laput_obj_null
+ movl MIRROR_OBJECT_CLASS_OFFSET(%rdi), %ecx
+ UNPOISON_HEAP_REF ecx
+#ifdef USE_READ_BARRIER
+ cmpl LITERAL(0), %gs:THREAD_IS_GC_MARKING_OFFSET
+ jnz .Laput_obj_gc_marking
+#endif // USE_READ_BARRIER
+ movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%rcx), %ecx
+ cmpl MIRROR_OBJECT_CLASS_OFFSET(%rdx), %ecx // Both poisoned if heap poisoning is enabled.
+ jne .Laput_obj_check_assignability
+.Laput_obj_store:
POISON_HEAP_REF edx
- movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%edi, %esi, 4)
-// movq %rdx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4)
+ movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4)
movq %gs:THREAD_CARD_TABLE_OFFSET, %rdx
shrl LITERAL(CARD_TABLE_CARD_SHIFT), %edi
-// shrl LITERAL(CARD_TABLE_CARD_SHIFT), %rdi
- movb %dl, (%rdx, %rdi) // Note: this assumes that top 32b of %rdi are zero
+ movb %dl, (%rdx, %rdi)
ret
-.Ldo_aput_null:
- movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%edi, %esi, 4)
-// movq %rdx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4)
+
+.Laput_obj_null:
+ movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4)
ret
-.Lcheck_assignability:
- // Save arguments.
- PUSH rdi
- PUSH rsi
- PUSH rdx
+
+.Laput_obj_check_assignability:
+ UNPOISON_HEAP_REF ecx // Unpoison array component type if poisoning is enabled.
+ PUSH_ARG rdi // Save arguments.
+ PUSH_ARG rsi
+ PUSH_ARG rdx
+ movl MIRROR_OBJECT_CLASS_OFFSET(%rdx), %esi // Pass arg2 = value's class.
+ UNPOISON_HEAP_REF esi
+.Laput_obj_check_assignability_call:
+ movl %ecx, %edi // Pass arg1 = array's component type.
SETUP_FP_CALLEE_SAVE_FRAME
-
-#if defined(USE_HEAP_POISONING) || defined(USE_READ_BARRIER)
- // The load of MIRROR_OBJECT_CLASS_OFFSET(%edx) is redundant, eax still holds the value.
- movl %eax, %esi // Pass arg2 = value's class.
- // movq %rax, %rsi
-#else
- // "Uncompress" = do nothing, as already zero-extended on load.
- movl MIRROR_OBJECT_CLASS_OFFSET(%edx), %esi // Pass arg2 = value's class.
-#endif
- movq %rcx, %rdi // Pass arg1 = array's component type.
-
call SYMBOL(artIsAssignableFromCode) // (Class* a, Class* b)
-
- // Exception?
- testq %rax, %rax
- jz .Lthrow_array_store_exception
-
- RESTORE_FP_CALLEE_SAVE_FRAME
- // Restore arguments.
- POP rdx
- POP rsi
- POP rdi
-
+ RESTORE_FP_CALLEE_SAVE_FRAME // Resore FP registers.
+ POP_ARG rdx // Restore arguments.
+ POP_ARG rsi
+ POP_ARG rdi
+ testq %rax, %rax // Check for exception.
+ jz .Laput_obj_throw_array_store_exception
POISON_HEAP_REF edx
- movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%edi, %esi, 4)
-// movq %rdx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4)
+ movl %edx, MIRROR_OBJECT_ARRAY_DATA_OFFSET(%rdi, %rsi, 4)
movq %gs:THREAD_CARD_TABLE_OFFSET, %rdx
shrl LITERAL(CARD_TABLE_CARD_SHIFT), %edi
-// shrl LITERAL(CARD_TABLE_CARD_SHIFT), %rdi
- movb %dl, (%rdx, %rdi) // Note: this assumes that top 32b of %rdi are zero
-// movb %dl, (%rdx, %rdi)
+ movb %dl, (%rdx, %rdi)
ret
- CFI_ADJUST_CFA_OFFSET(24 + 4 * 8) // Reset unwind info so following code unwinds.
-.Lthrow_array_store_exception:
- RESTORE_FP_CALLEE_SAVE_FRAME
- // Restore arguments.
- POP rdx
- POP rsi
- POP rdi
+.Laput_obj_throw_array_store_exception:
+#ifdef USE_READ_BARRIER
+ CFI_REMEMBER_STATE
+#endif // USE_READ_BARRIER
SETUP_SAVE_ALL_CALLEE_SAVES_FRAME // Save all registers as basis for long jump context.
-
// Outgoing argument set up.
movq %rdx, %rsi // Pass arg 2 = value.
movq %gs:THREAD_SELF_OFFSET, %rdx // Pass arg 3 = Thread::Current().
// Pass arg 1 = array.
call SYMBOL(artThrowArrayStoreException) // (array, value, Thread*)
UNREACHABLE
+
+#ifdef USE_READ_BARRIER
+ CFI_RESTORE_STATE_AND_DEF_CFA rsp, 4
+.Laput_obj_gc_marking:
+ // We need to align stack for `art_quick_read_barrier_mark_regNN`.
+ INCREASE_FRAME 8 // Stack alignment.
+ call SYMBOL(art_quick_read_barrier_mark_reg01) // Mark ECX
+ movl MIRROR_CLASS_COMPONENT_TYPE_OFFSET(%rcx), %ecx
+ UNPOISON_HEAP_REF ecx
+ call SYMBOL(art_quick_read_barrier_mark_reg01) // Mark ECX
+ movl MIRROR_OBJECT_CLASS_OFFSET(%rdx), %eax
+ UNPOISON_HEAP_REF eax
+ call SYMBOL(art_quick_read_barrier_mark_reg00) // Mark EAX
+ DECREASE_FRAME 8 // Remove stack alignment.
+ cmpl %eax, %ecx
+ je .Laput_obj_store
+ // Prepare arguments in line with `.Laput_obj_check_assignability_call` and jump there.
+ PUSH_ARG rdi // Save arguments.
+ PUSH_ARG rsi
+ PUSH_ARG rdx
+ movl %eax, %esi // Pass arg2 - type of the value to be stored.
+ // The arg1 shall be moved at `.Ldo_assignability_check_call`.
+ jmp .Laput_obj_check_assignability_call
+#endif // USE_READ_BARRIER
END_FUNCTION art_quick_aput_obj
// TODO: This is quite silly on X86_64 now.
@@ -1324,27 +1323,27 @@
THREE_ARG_REF_DOWNCALL art_quick_set64_instance, artSet64InstanceFromCompiledCode, RETURN_IF_EAX_ZERO
THREE_ARG_REF_DOWNCALL art_quick_set_obj_instance, artSetObjInstanceFromCompiledCode, RETURN_IF_EAX_ZERO
-TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get64_instance, artGet64InstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_byte_instance, artGetByteInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_boolean_instance, artGetBooleanInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_short_instance, artGetShortInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_char_instance, artGetCharInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get32_instance, artGet32InstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get64_instance, artGet64InstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_get_obj_instance, artGetObjInstanceFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
TWO_ARG_REF_DOWNCALL art_quick_set8_static, artSet8StaticFromCompiledCode, RETURN_IF_EAX_ZERO
TWO_ARG_REF_DOWNCALL art_quick_set16_static, artSet16StaticFromCompiledCode, RETURN_IF_EAX_ZERO
TWO_ARG_REF_DOWNCALL art_quick_set32_static, artSet32StaticFromCompiledCode, RETURN_IF_EAX_ZERO
-TWO_ARG_REF_DOWNCALL art_quick_set64_static, artSet64StaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+TWO_ARG_REF_DOWNCALL art_quick_set64_static, artSet64StaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
TWO_ARG_REF_DOWNCALL art_quick_set_obj_static, artSetObjStaticFromCompiledCode, RETURN_IF_EAX_ZERO
-ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-ONE_ARG_REF_DOWNCALL art_quick_get64_static, artGet64StaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
-ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCompiledCode, RETURN_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_byte_static, artGetByteStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_boolean_static, artGetBooleanStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_short_static, artGetShortStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_char_static, artGetCharStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get32_static, artGet32StaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get64_static, artGet64StaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
+ONE_ARG_REF_DOWNCALL art_quick_get_obj_static, artGetObjStaticFromCompiledCode, RETURN_OR_DEOPT_OR_DELIVER_PENDING_EXCEPTION
DEFINE_FUNCTION art_quick_proxy_invoke_handler
SETUP_SAVE_REFS_AND_ARGS_FRAME_WITH_METHOD_IN_RDI
@@ -1575,6 +1574,14 @@
CFI_REMEMBER_STATE
CFI_DEF_CFA_REGISTER(rsp)
+ // store into fpr, for when it's a fpr return...
+ movq %rax, %xmm0
+
+ LOAD_RUNTIME_INSTANCE rcx
+ cmpb MACRO_LITERAL(0), INSTRUMENTATION_STUBS_INSTALLED_OFFSET_FROM_RUNTIME_INSTANCE(%rcx)
+ jne .Lcall_method_exit_hook
+.Lcall_method_exit_hook_done:
+
// Tear down the callee-save frame.
// Load FPRs.
// movq %xmm0, 16(%rsp) // doesn't make sense!!!
@@ -1604,10 +1611,13 @@
POP r13 // Callee save.
POP r14 // Callee save.
POP r15 // Callee save.
- // store into fpr, for when it's a fpr return...
- movq %rax, %xmm0
ret
+.Lcall_method_exit_hook:
+ movq LITERAL(FRAME_SIZE_SAVE_REFS_AND_ARGS), %r8
+ call art_quick_method_exit_hook
+ jmp .Lcall_method_exit_hook_done
+
// Undo the unwinding information from above since it doesn't apply below.
CFI_RESTORE_STATE_AND_DEF_CFA rbp, 208
.Lexception_in_native:
@@ -1846,7 +1856,7 @@
movq %gs:THREAD_SELF_OFFSET, %rdx // pass Thread::Current()
call artStringBuilderAppend // (uint32_t, const unit32_t*, Thread*)
RESTORE_SAVE_REFS_ONLY_FRAME // restore frame up to return address
- RETURN_IF_RESULT_IS_NON_ZERO_OR_DELIVER // return or deliver exception
+ RETURN_IF_RESULT_IS_NON_ZERO_OR_DEOPT_OR_DELIVER // return or deopt or deliver exception
END_FUNCTION art_quick_string_builder_append
// Create a function `name` calling the ReadBarrier::Mark routine,
@@ -1855,16 +1865,9 @@
//
// The generated function follows a non-standard runtime calling
// convention:
-// - register `reg` (which may be different from RDI) is used to pass
-// the (sole) argument of this function;
-// - register `reg` (which may be different from RAX) is used to return
-// the result of this function (instead of RAX);
-// - if `reg` is different from `rdi`, RDI is treated like a normal
-// (non-argument) caller-save register;
-// - if `reg` is different from `rax`, RAX is treated like a normal
-// (non-result) caller-save register;
-// - everything else is the same as in the standard runtime calling
-// convention (e.g. standard callee-save registers are preserved).
+// - register `reg` (which may be different from RDI) is used to pass the (sole) argument,
+// - register `reg` (which may be different from RAX) is used to return the result,
+// - all other registers are callee-save (the values they hold are preserved).
MACRO2(READ_BARRIER_MARK_REG, name, reg)
DEFINE_FUNCTION VAR(name)
// Null check so that we can load the lock word.
@@ -2164,33 +2167,28 @@
movq FRAME_SIZE_SAVE_EVERYTHING(%rsp), %rdi // pass ArtMethod
movq %gs:THREAD_SELF_OFFSET, %rsi // pass Thread::Current()
+ movq %rsp, %rdx // SP
- call SYMBOL(artMethodEntryHook) // (ArtMethod*, Thread*)
+ call SYMBOL(artMethodEntryHook) // (ArtMethod*, Thread*, sp)
RESTORE_SAVE_EVERYTHING_FRAME
ret
END_FUNCTION art_quick_method_entry_hook
// On entry, method is at the bottom of the stack.
-// and r8 has should_deopt_frame value.
DEFINE_FUNCTION art_quick_method_exit_hook
SETUP_SAVE_EVERYTHING_FRAME
+ // R8 passed from JITed code contains frame_size
leaq 16(%rsp), %rcx // floating-point result pointer in kSaveEverything
// frame
leaq 144(%rsp), %rdx // integer result pointer in kSaveEverything frame
- movq FRAME_SIZE_SAVE_EVERYTHING(%rsp), %rsi // ArtMethod
+ leaq FRAME_SIZE_SAVE_EVERYTHING(%rsp), %rsi // ArtMethod**
movq %gs:THREAD_SELF_OFFSET, %rdi // Thread::Current
- call SYMBOL(artMethodExitHook) // (Thread*, SP, gpr_res*, fpr_res*)
-
- cmpq LITERAL(1), %rax
- CFI_REMEMBER_STATE
- je .Ldo_deliver_instrumentation_exception_exit
+ call SYMBOL(artMethodExitHook) // (Thread*, ArtMethod**, gpr_res*, fpr_res*,
+ // frame_size)
// Normal return.
RESTORE_SAVE_EVERYTHING_FRAME
ret
-.Ldo_deliver_instrumentation_exception_exit:
- CFI_RESTORE_STATE_AND_DEF_CFA rsp, FRAME_SIZE_SAVE_EVERYTHING
- DELIVER_PENDING_EXCEPTION_FRAME_READY
-END_FUNCTION art_quick_method_entry_hook
+END_FUNCTION art_quick_method_exit_hook
diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h
index 5f23f1e..f6a99ac 100644
--- a/runtime/art_field-inl.h
+++ b/runtime/art_field-inl.h
@@ -40,15 +40,11 @@
return GetDeclaringClass<kWithoutReadBarrier>()->IsProxyClass<kVerifyNone>();
}
-// We are only ever allowed to set our own final fields. We do need to be careful since if a
-// structural redefinition occurs during <clinit> we can end up trying to set the non-obsolete
-// class's fields from the obsolete class. This is something we want to allow. This is tested by
-// run-test 2002-virtual-structural-initializing.
+// We are only ever allowed to set our own final fields
inline bool ArtField::CanBeChangedBy(ArtMethod* method) {
ObjPtr<mirror::Class> declaring_class(GetDeclaringClass());
ObjPtr<mirror::Class> referring_class(method->GetDeclaringClass());
- return !IsFinal() || (declaring_class == referring_class) ||
- UNLIKELY(referring_class->IsObsoleteVersionOf(declaring_class));
+ return !IsFinal() || (declaring_class == referring_class);
}
template<ReadBarrierOption kReadBarrierOption>
@@ -64,6 +60,32 @@
declaring_class_ = GcRoot<mirror::Class>(new_declaring_class);
}
+template<typename RootVisitorType>
+void ArtField::VisitArrayRoots(RootVisitorType& visitor,
+ uint8_t* start_boundary,
+ uint8_t* end_boundary,
+ LengthPrefixedArray<ArtField>* array) {
+ DCHECK_LE(start_boundary, end_boundary);
+ DCHECK_NE(array->size(), 0u);
+ ArtField* first_field = &array->At(0);
+ DCHECK_LE(static_cast<void*>(end_boundary), static_cast<void*>(first_field + array->size()));
+ static constexpr size_t kFieldSize = sizeof(ArtField);
+ // Confirm the assumption that ArtField size is power of two. It's important
+ // as we assume so below (RoundUp).
+ static_assert(IsPowerOfTwo(kFieldSize));
+ uint8_t* declaring_class =
+ reinterpret_cast<uint8_t*>(first_field) + DeclaringClassOffset().Int32Value();
+ // Jump to the first class to visit.
+ if (declaring_class < start_boundary) {
+ declaring_class += RoundUp(start_boundary - declaring_class, kFieldSize);
+ }
+ while (declaring_class < end_boundary) {
+ visitor.VisitRoot(
+ reinterpret_cast<mirror::CompressedReference<mirror::Object>*>(declaring_class));
+ declaring_class += kFieldSize;
+ }
+}
+
inline MemberOffset ArtField::GetOffsetDuringLinking() {
DCHECK(GetDeclaringClass()->IsLoaded() || GetDeclaringClass()->IsErroneous());
return MemberOffset(offset_);
diff --git a/runtime/art_field.h b/runtime/art_field.h
index 4e77e7f..c205920 100644
--- a/runtime/art_field.h
+++ b/runtime/art_field.h
@@ -27,6 +27,7 @@
namespace art {
class DexFile;
+template<typename T> class LengthPrefixedArray;
class ScopedObjectAccessAlreadyRunnable;
namespace mirror {
@@ -39,6 +40,15 @@
class ArtField final {
public:
+ // Visit declaring classes of all the art-fields in 'array' that reside
+ // in [start_boundary, end_boundary).
+ template<typename RootVisitorType>
+ static void VisitArrayRoots(RootVisitorType& visitor,
+ uint8_t* start_boundary,
+ uint8_t* end_boundary,
+ LengthPrefixedArray<ArtField>* array)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
ObjPtr<mirror::Class> GetDeclaringClass() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/art_method-alloc-inl.h b/runtime/art_method-alloc-inl.h
new file mode 100644
index 0000000..13051b1
--- /dev/null
+++ b/runtime/art_method-alloc-inl.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_ART_METHOD_ALLOC_INL_H_
+#define ART_RUNTIME_ART_METHOD_ALLOC_INL_H_
+
+#include "art_method-inl.h"
+
+#include "handle.h"
+#include "handle_scope.h"
+#include "mirror/class-alloc-inl.h"
+
+namespace art {
+
+namespace detail {
+
+template <char Shorty>
+struct HandleShortyTraits {
+ using Type = typename ShortyTraits<Shorty>::Type;
+ static Type Extract(Type value) ALWAYS_INLINE { return value; }
+};
+
+template <> struct HandleShortyTraits<'L'> {
+ using Type = Handle<mirror::Object>;
+ static typename ShortyTraits<'L'>::Type Extract(Type value)
+ REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE {
+ return value.Get();
+ }
+};
+
+} // namespace detail
+
+template <char... ArgType, typename HandleScopeType>
+inline Handle<mirror::Object> ArtMethod::NewObject(
+ HandleScopeType& hs,
+ Thread* self,
+ typename detail::HandleShortyTraits<ArgType>::Type... args) {
+ DCHECK(!GetDeclaringClass()->IsInterface());
+ DCHECK(GetDeclaringClass()->IsInitialized());
+ DCHECK(IsConstructor());
+ DCHECK(!IsStatic());
+ MutableHandle<mirror::Object> new_object = hs.NewHandle(GetDeclaringClass()->AllocObject(self));
+ DCHECK_EQ(new_object == nullptr, self->IsExceptionPending());
+ if (LIKELY(new_object != nullptr)) {
+ InvokeInstance<'V', ArgType...>(
+ self, new_object.Get(), detail::HandleShortyTraits<ArgType>::Extract(args)...);
+ if (UNLIKELY(self->IsExceptionPending())) {
+ new_object.Assign(nullptr);
+ }
+ }
+ return new_object;
+}
+
+template <char... ArgType>
+inline ObjPtr<mirror::Object> ArtMethod::NewObject(
+ Thread* self, typename detail::HandleShortyTraits<ArgType>::Type... args) {
+ StackHandleScope<1u> hs(self);
+ return NewObject<ArgType...>(hs, self, args...).Get();
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_ART_METHOD_ALLOC_INL_H_
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 844a0ff..3ea5130 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -48,6 +48,201 @@
namespace art {
+namespace detail {
+
+template <> struct ShortyTraits<'V'> {
+ using Type = void;
+ static Type Get(const JValue& value ATTRIBUTE_UNUSED) {}
+ // `kVRegCount` and `Set()` are not defined.
+};
+
+template <> struct ShortyTraits<'Z'> {
+ // Despite using `uint8_t` for `boolean` in `JValue`, we shall use `bool` here.
+ using Type = bool;
+ static Type Get(const JValue& value) { return value.GetZ() != 0u; }
+ static constexpr size_t kVRegCount = 1u;
+ static void Set(uint32_t* args, Type value) { args[0] = static_cast<uint32_t>(value ? 1u : 0u); }
+};
+
+template <> struct ShortyTraits<'B'> {
+ using Type = int8_t;
+ static Type Get(const JValue& value) { return value.GetB(); }
+ static constexpr size_t kVRegCount = 1u;
+ static void Set(uint32_t* args, Type value) { args[0] = static_cast<uint32_t>(value); }
+};
+
+template <> struct ShortyTraits<'C'> {
+ using Type = uint16_t;
+ static Type Get(const JValue& value) { return value.GetC(); }
+ static constexpr size_t kVRegCount = 1u;
+ static void Set(uint32_t* args, Type value) { args[0] = static_cast<uint32_t>(value); }
+};
+
+template <> struct ShortyTraits<'S'> {
+ using Type = int16_t;
+ static Type Get(const JValue& value) { return value.GetS(); }
+ static constexpr size_t kVRegCount = 1u;
+ static void Set(uint32_t* args, Type value) { args[0] = static_cast<uint32_t>(value); }
+};
+
+template <> struct ShortyTraits<'I'> {
+ using Type = int32_t;
+ static Type Get(const JValue& value) { return value.GetI(); }
+ static constexpr size_t kVRegCount = 1u;
+ static void Set(uint32_t* args, Type value) { args[0] = static_cast<uint32_t>(value); }
+};
+
+template <> struct ShortyTraits<'J'> {
+ using Type = int64_t;
+ static Type Get(const JValue& value) { return value.GetJ(); }
+ static constexpr size_t kVRegCount = 2u;
+ static void Set(uint32_t* args, Type value) {
+ // Little-endian representation.
+ args[0] = static_cast<uint32_t>(value);
+ args[1] = static_cast<uint32_t>(static_cast<uint64_t>(value) >> 32);
+ }
+};
+
+template <> struct ShortyTraits<'F'> {
+ using Type = float;
+ static Type Get(const JValue& value) { return value.GetF(); }
+ static constexpr size_t kVRegCount = 1u;
+ static void Set(uint32_t* args, Type value) { args[0] = bit_cast<uint32_t>(value); }
+};
+
+template <> struct ShortyTraits<'D'> {
+ using Type = double;
+ static Type Get(const JValue& value) { return value.GetD(); }
+ static constexpr size_t kVRegCount = 2u;
+ static void Set(uint32_t* args, Type value) {
+ // Little-endian representation.
+ uint64_t v = bit_cast<uint64_t>(value);
+ args[0] = static_cast<uint32_t>(v);
+ args[1] = static_cast<uint32_t>(v >> 32);
+ }
+};
+
+template <> struct ShortyTraits<'L'> {
+ using Type = ObjPtr<mirror::Object>;
+ static Type Get(const JValue& value) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return value.GetL();
+ }
+ static constexpr size_t kVRegCount = 1u;
+ static void Set(uint32_t* args, Type value) REQUIRES_SHARED(Locks::mutator_lock_) {
+ args[0] = StackReference<mirror::Object>::FromMirrorPtr(value.Ptr()).AsVRegValue();
+ }
+};
+
+template <char... Shorty>
+constexpr auto MaterializeShorty() {
+ constexpr size_t kSize = std::size({Shorty...}) + 1u;
+ return std::array<char, kSize>{Shorty..., '\0'};
+}
+
+template <char... ArgType>
+constexpr size_t NumberOfVRegs() {
+ constexpr size_t kArgVRegCount[] = {
+ ShortyTraits<ArgType>::kVRegCount...
+ };
+ size_t sum = 0u;
+ for (size_t count : kArgVRegCount) {
+ sum += count;
+ }
+ return sum;
+}
+
+template <char... ArgType>
+inline ALWAYS_INLINE void FillVRegs(uint32_t* vregs ATTRIBUTE_UNUSED,
+ typename ShortyTraits<ArgType>::Type... args ATTRIBUTE_UNUSED)
+ REQUIRES_SHARED(Locks::mutator_lock_) {}
+
+template <char FirstArgType, char... ArgType>
+inline ALWAYS_INLINE void FillVRegs(uint32_t* vregs,
+ typename ShortyTraits<FirstArgType>::Type first_arg,
+ typename ShortyTraits<ArgType>::Type... args)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ShortyTraits<FirstArgType>::Set(vregs, first_arg);
+ FillVRegs<ArgType...>(vregs + ShortyTraits<FirstArgType>::kVRegCount, args...);
+}
+
+template <char... ArgType>
+inline ALWAYS_INLINE auto MaterializeVRegs(typename ShortyTraits<ArgType>::Type... args)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ constexpr size_t kNumVRegs = NumberOfVRegs<ArgType...>();
+ std::array<uint32_t, kNumVRegs> vregs;
+ FillVRegs<ArgType...>(vregs.data(), args...);
+ return vregs;
+}
+
+} // namespace detail
+
+template <char ReturnType, char... ArgType>
+inline typename detail::ShortyTraits<ReturnType>::Type
+ArtMethod::InvokeStatic(Thread* self, typename detail::ShortyTraits<ArgType>::Type... args) {
+ DCHECK(IsStatic());
+ DCHECK(GetDeclaringClass()->IsInitialized()); // Used only for initialized well-known classes.
+ JValue result;
+ constexpr auto shorty = detail::MaterializeShorty<ReturnType, ArgType...>();
+ auto vregs = detail::MaterializeVRegs<ArgType...>(args...);
+ Invoke(self, vregs.data(), sizeof(vregs), &result, shorty.data());
+ return detail::ShortyTraits<ReturnType>::Get(result);
+}
+
+template <char ReturnType, char... ArgType>
+typename detail::ShortyTraits<ReturnType>::Type
+ArtMethod::InvokeInstance(Thread* self,
+ ObjPtr<mirror::Object> receiver,
+ typename detail::ShortyTraits<ArgType>::Type... args) {
+ DCHECK(!GetDeclaringClass()->IsInterface());
+ DCHECK(!IsStatic());
+ JValue result;
+ constexpr auto shorty = detail::MaterializeShorty<ReturnType, ArgType...>();
+ auto vregs = detail::MaterializeVRegs<'L', ArgType...>(receiver, args...);
+ Invoke(self, vregs.data(), sizeof(vregs), &result, shorty.data());
+ return detail::ShortyTraits<ReturnType>::Get(result);
+}
+
+template <char ReturnType, char... ArgType>
+typename detail::ShortyTraits<ReturnType>::Type
+ArtMethod::InvokeFinal(Thread* self,
+ ObjPtr<mirror::Object> receiver,
+ typename detail::ShortyTraits<ArgType>::Type... args) {
+ DCHECK(!GetDeclaringClass()->IsInterface());
+ DCHECK(!IsStatic());
+ DCHECK(IsFinal() || GetDeclaringClass()->IsFinal());
+ DCHECK(receiver != nullptr);
+ return InvokeInstance<ReturnType, ArgType...>(self, receiver, args...);
+}
+
+template <char ReturnType, char... ArgType>
+typename detail::ShortyTraits<ReturnType>::Type
+ArtMethod::InvokeVirtual(Thread* self,
+ ObjPtr<mirror::Object> receiver,
+ typename detail::ShortyTraits<ArgType>::Type... args) {
+ DCHECK(!GetDeclaringClass()->IsInterface());
+ DCHECK(!IsStatic());
+ DCHECK(!IsFinal());
+ DCHECK(receiver != nullptr);
+ ArtMethod* target_method =
+ receiver->GetClass()->FindVirtualMethodForVirtual(this, kRuntimePointerSize);
+ DCHECK(target_method != nullptr);
+ return target_method->InvokeInstance<ReturnType, ArgType...>(self, receiver, args...);
+}
+
+template <char ReturnType, char... ArgType>
+typename detail::ShortyTraits<ReturnType>::Type
+ArtMethod::InvokeInterface(Thread* self,
+ ObjPtr<mirror::Object> receiver,
+ typename detail::ShortyTraits<ArgType>::Type... args) {
+ DCHECK(GetDeclaringClass()->IsInterface());
+ DCHECK(!IsStatic());
+ DCHECK(receiver != nullptr);
+ ArtMethod* target_method =
+ receiver->GetClass()->FindVirtualMethodForInterface(this, kRuntimePointerSize);
+ DCHECK(target_method != nullptr);
+ return target_method->InvokeInstance<ReturnType, ArgType...>(self, receiver, args...);
+}
+
template <ReadBarrierOption kReadBarrierOption>
inline ObjPtr<mirror::Class> ArtMethod::GetDeclaringClassUnchecked() {
GcRootSource gc_root_source(this);
@@ -102,6 +297,15 @@
return type;
}
+inline bool ArtMethod::IsStringConstructor() {
+ uint32_t access_flags = GetAccessFlags();
+ DCHECK(!IsClassInitializer(access_flags));
+ return IsConstructor(access_flags) &&
+ // No read barrier needed for reading a constant reference only to read
+ // a constant string class flag. See `ReadBarrierOption`.
+ GetDeclaringClass<kWithoutReadBarrier>()->IsStringClass();
+}
+
inline bool ArtMethod::IsOverridableByDefaultMethod() {
// It is safe to avoid the read barrier here since the constant interface flag
// in the `Class` object is stored before creating the `ArtMethod` and storing
@@ -332,7 +536,7 @@
return klass->GetDexCache<kDefaultVerifyFlags, kReadBarrierOption>();
} else {
DCHECK(!IsProxyMethod());
- return GetObsoleteDexCache();
+ return GetObsoleteDexCache<kReadBarrierOption>();
}
}
@@ -378,9 +582,10 @@
return ResolveClassFromTypeIndex(GetReturnTypeIndex());
}
-template <ReadBarrierOption kReadBarrierOption>
inline bool ArtMethod::HasSingleImplementation() {
- if (IsFinal() || GetDeclaringClass<kReadBarrierOption>()->IsFinal()) {
+ // No read barrier needed for reading a constant reference only to read
+ // a constant final class flag. See `ReadBarrierOption`.
+ if (IsFinal() || GetDeclaringClass<kWithoutReadBarrier>()->IsFinal()) {
// We don't set kAccSingleImplementation for these cases since intrinsic
// can use the flag also.
return true;
@@ -388,21 +593,66 @@
return (GetAccessFlags() & kAccSingleImplementation) != 0;
}
-template<ReadBarrierOption kReadBarrierOption, typename RootVisitorType>
+template<ReadBarrierOption kReadBarrierOption, bool kVisitProxyMethod, typename RootVisitorType>
void ArtMethod::VisitRoots(RootVisitorType& visitor, PointerSize pointer_size) {
if (LIKELY(!declaring_class_.IsNull())) {
visitor.VisitRoot(declaring_class_.AddressWithoutBarrier());
- ObjPtr<mirror::Class> klass = declaring_class_.Read<kReadBarrierOption>();
- if (UNLIKELY(klass->IsProxyClass())) {
- // For normal methods, dex cache shortcuts will be visited through the declaring class.
- // However, for proxies we need to keep the interface method alive, so we visit its roots.
- ArtMethod* interface_method = GetInterfaceMethodForProxyUnchecked(pointer_size);
- DCHECK(interface_method != nullptr);
- interface_method->VisitRoots<kReadBarrierOption>(visitor, pointer_size);
+ if (kVisitProxyMethod) {
+ ObjPtr<mirror::Class> klass = declaring_class_.Read<kReadBarrierOption>();
+ if (UNLIKELY(klass->IsProxyClass())) {
+ // For normal methods, dex cache shortcuts will be visited through the declaring class.
+ // However, for proxies we need to keep the interface method alive, so we visit its roots.
+ ArtMethod* interface_method = GetInterfaceMethodForProxyUnchecked(pointer_size);
+ DCHECK(interface_method != nullptr);
+ interface_method->VisitRoots<kReadBarrierOption, kVisitProxyMethod>(visitor, pointer_size);
+ }
}
}
}
+template<typename RootVisitorType>
+void ArtMethod::VisitRoots(RootVisitorType& visitor,
+ uint8_t* start_boundary,
+ uint8_t* end_boundary,
+ ArtMethod* method) {
+ mirror::CompressedReference<mirror::Object>* cls_ptr =
+ reinterpret_cast<mirror::CompressedReference<mirror::Object>*>(
+ reinterpret_cast<uint8_t*>(method) + DeclaringClassOffset().Int32Value());
+ if (reinterpret_cast<uint8_t*>(cls_ptr) >= start_boundary
+ && reinterpret_cast<uint8_t*>(cls_ptr) < end_boundary) {
+ visitor.VisitRootIfNonNull(cls_ptr);
+ }
+}
+
+template<PointerSize kPointerSize, typename RootVisitorType>
+void ArtMethod::VisitArrayRoots(RootVisitorType& visitor,
+ uint8_t* start_boundary,
+ uint8_t* end_boundary,
+ LengthPrefixedArray<ArtMethod>* array) {
+ DCHECK_LE(start_boundary, end_boundary);
+ DCHECK_NE(array->size(), 0u);
+ static constexpr size_t kMethodSize = ArtMethod::Size(kPointerSize);
+ ArtMethod* first_method = &array->At(0, kMethodSize, ArtMethod::Alignment(kPointerSize));
+ DCHECK_LE(static_cast<void*>(end_boundary),
+ static_cast<void*>(reinterpret_cast<uint8_t*>(first_method)
+ + array->size() * kMethodSize));
+ uint8_t* declaring_class =
+ reinterpret_cast<uint8_t*>(first_method) + DeclaringClassOffset().Int32Value();
+ // Jump to the first class to visit.
+ if (declaring_class < start_boundary) {
+ size_t remainder = (start_boundary - declaring_class) % kMethodSize;
+ declaring_class = start_boundary;
+ if (remainder > 0) {
+ declaring_class += kMethodSize - remainder;
+ }
+ }
+ while (declaring_class < end_boundary) {
+ visitor.VisitRootIfNonNull(
+ reinterpret_cast<mirror::CompressedReference<mirror::Object>*>(declaring_class));
+ declaring_class += kMethodSize;
+ }
+}
+
template <typename Visitor>
inline void ArtMethod::UpdateEntrypoints(const Visitor& visitor, PointerSize pointer_size) {
if (IsNative()) {
@@ -419,6 +669,43 @@
}
}
+template <ReadBarrierOption kReadBarrierOption>
+inline bool ArtMethod::StillNeedsClinitCheck() {
+ if (!NeedsClinitCheckBeforeCall()) {
+ return false;
+ }
+ ObjPtr<mirror::Class> klass = GetDeclaringClass<kReadBarrierOption>();
+ return !klass->IsVisiblyInitialized();
+}
+
+inline bool ArtMethod::StillNeedsClinitCheckMayBeDead() {
+ if (!NeedsClinitCheckBeforeCall()) {
+ return false;
+ }
+ ObjPtr<mirror::Class> klass = GetDeclaringClassMayBeDead();
+ return !klass->IsVisiblyInitialized();
+}
+
+inline bool ArtMethod::IsDeclaringClassVerifiedMayBeDead() {
+ ObjPtr<mirror::Class> klass = GetDeclaringClassMayBeDead();
+ return klass->IsVerified();
+}
+
+inline ObjPtr<mirror::Class> ArtMethod::GetDeclaringClassMayBeDead() {
+ // Helper method for checking the status of the declaring class which may be dead.
+ //
+ // To avoid resurrecting an unreachable object, or crashing the GC in some GC phases,
+ // we must not use a full read barrier. Therefore we read the declaring class without
+ // a read barrier and check if it's already marked. If yes, we check the status of the
+ // to-space class object as intended. Otherwise, there is no to-space object and the
+ // from-space class object contains the most recent value of the status field; even if
+ // this races with another thread doing a read barrier and updating the status, that's
+ // no different from a race with a thread that just updates the status.
+ ObjPtr<mirror::Class> klass = GetDeclaringClass<kWithoutReadBarrier>();
+ ObjPtr<mirror::Class> marked = ReadBarrier::IsMarked(klass.Ptr());
+ return (marked != nullptr) ? marked : klass;
+}
+
inline CodeItemInstructionAccessor ArtMethod::DexInstructions() {
return CodeItemInstructionAccessor(*GetDexFile(), GetCodeItem());
}
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index f6f8b5f..2ff145d 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -111,35 +111,63 @@
return executable->GetArtMethod();
}
+template <ReadBarrierOption kReadBarrierOption>
ObjPtr<mirror::DexCache> ArtMethod::GetObsoleteDexCache() {
+ // Note: The class redefinition happens with GC disabled, so at the point where we
+ // create obsolete methods, the `ClassExt` and its obsolete methods and dex caches
+ // members are reachable without a read barrier. If we start a GC later, and we
+ // look at these objects without read barriers (`kWithoutReadBarrier`), the method
+ // pointers shall be the same in from-space array as in to-space array (if these
+ // arrays are different) and the dex cache array entry can point to from-space or
+ // to-space `DexCache` but either is a valid result for `kWithoutReadBarrier`.
+ ScopedAssertNoThreadSuspension ants(__FUNCTION__);
+ std::optional<ScopedDebugDisallowReadBarriers> sddrb(std::nullopt);
+ if (kIsDebugBuild && kReadBarrierOption == kWithoutReadBarrier) {
+ sddrb.emplace(Thread::Current());
+ }
PointerSize pointer_size = kRuntimePointerSize;
DCHECK(!Runtime::Current()->IsAotCompiler()) << PrettyMethod();
DCHECK(IsObsolete());
- ObjPtr<mirror::ClassExt> ext(GetDeclaringClass()->GetExtData());
- ObjPtr<mirror::PointerArray> obsolete_methods(ext.IsNull() ? nullptr : ext->GetObsoleteMethods());
- int32_t len = (obsolete_methods.IsNull() ? 0 : obsolete_methods->GetLength());
- DCHECK(len == 0 || len == ext->GetObsoleteDexCaches()->GetLength())
- << "len=" << len << " ext->GetObsoleteDexCaches()=" << ext->GetObsoleteDexCaches();
+ ObjPtr<mirror::Class> declaring_class = GetDeclaringClass<kReadBarrierOption>();
+ ObjPtr<mirror::ClassExt> ext =
+ declaring_class->GetExtData<kDefaultVerifyFlags, kReadBarrierOption>();
+ ObjPtr<mirror::PointerArray> obsolete_methods(
+ ext.IsNull() ? nullptr : ext->GetObsoleteMethods<kDefaultVerifyFlags, kReadBarrierOption>());
+ int32_t len = 0;
+ ObjPtr<mirror::ObjectArray<mirror::DexCache>> obsolete_dex_caches = nullptr;
+ if (!obsolete_methods.IsNull()) {
+ len = obsolete_methods->GetLength();
+ obsolete_dex_caches = ext->GetObsoleteDexCaches<kDefaultVerifyFlags, kReadBarrierOption>();
+ // FIXME: `ClassExt::SetObsoleteArrays()` is not atomic, so one of the arrays we see here
+ // could be extended for a new class redefinition while the other may be shorter.
+ // Furthermore, there is no synchronization to ensure that copied contents of an old
+ // obsolete array are visible to a thread reading the new array.
+ DCHECK_EQ(len, obsolete_dex_caches->GetLength())
+ << " ext->GetObsoleteDexCaches()=" << obsolete_dex_caches;
+ }
// Using kRuntimePointerSize (instead of using the image's pointer size) is fine since images
// should never have obsolete methods in them so they should always be the same.
DCHECK_EQ(pointer_size, Runtime::Current()->GetClassLinker()->GetImagePointerSize());
for (int32_t i = 0; i < len; i++) {
if (this == obsolete_methods->GetElementPtrSize<ArtMethod*>(i, pointer_size)) {
- return ext->GetObsoleteDexCaches()->Get(i);
+ return obsolete_dex_caches->GetWithoutChecks<kDefaultVerifyFlags, kReadBarrierOption>(i);
}
}
- CHECK(GetDeclaringClass()->IsObsoleteObject())
+ CHECK(declaring_class->IsObsoleteObject())
<< "This non-structurally obsolete method does not appear in the obsolete map of its class: "
- << GetDeclaringClass()->PrettyClass() << " Searched " << len << " caches.";
+ << declaring_class->PrettyClass() << " Searched " << len << " caches.";
CHECK_EQ(this,
std::clamp(this,
- &(*GetDeclaringClass()->GetMethods(pointer_size).begin()),
- &(*GetDeclaringClass()->GetMethods(pointer_size).end())))
+ &(*declaring_class->GetMethods(pointer_size).begin()),
+ &(*declaring_class->GetMethods(pointer_size).end())))
<< "class is marked as structurally obsolete method but not found in normal obsolete-map "
<< "despite not being the original method pointer for " << GetDeclaringClass()->PrettyClass();
- return GetDeclaringClass()->GetDexCache();
+ return declaring_class->template GetDexCache<kDefaultVerifyFlags, kReadBarrierOption>();
}
+template ObjPtr<mirror::DexCache> ArtMethod::GetObsoleteDexCache<kWithReadBarrier>();
+template ObjPtr<mirror::DexCache> ArtMethod::GetObsoleteDexCache<kWithoutReadBarrier>();
+
uint16_t ArtMethod::FindObsoleteDexClassDefIndex() {
DCHECK(!Runtime::Current()->IsAotCompiler()) << PrettyMethod();
DCHECK(IsObsolete());
@@ -150,10 +178,34 @@
return dex_file->GetIndexForClassDef(*class_def);
}
-void ArtMethod::ThrowInvocationTimeError() {
+void ArtMethod::ThrowInvocationTimeError(ObjPtr<mirror::Object> receiver) {
DCHECK(!IsInvokable());
if (IsDefaultConflicting()) {
ThrowIncompatibleClassChangeErrorForMethodConflict(this);
+ } else if (GetDeclaringClass()->IsInterface() && receiver != nullptr) {
+ // If this was an interface call, check whether there is a method in the
+ // superclass chain that isn't public. In this situation, we should throw an
+ // IllegalAccessError.
+ DCHECK(IsAbstract());
+ ObjPtr<mirror::Class> current = receiver->GetClass();
+ while (current != nullptr) {
+ for (ArtMethod& method : current->GetDeclaredMethodsSlice(kRuntimePointerSize)) {
+ ArtMethod* np_method = method.GetInterfaceMethodIfProxy(kRuntimePointerSize);
+ if (!np_method->IsStatic() &&
+ np_method->GetNameView() == GetNameView() &&
+ np_method->GetSignature() == GetSignature()) {
+ if (!np_method->IsPublic()) {
+ ThrowIllegalAccessErrorForImplementingMethod(receiver->GetClass(), np_method, this);
+ return;
+ } else if (np_method->IsAbstract()) {
+ ThrowAbstractMethodError(this);
+ return;
+ }
+ }
+ }
+ current = current->GetSuperClass();
+ }
+ ThrowAbstractMethodError(this);
} else {
DCHECK(IsAbstract());
ThrowAbstractMethodError(this);
@@ -310,6 +362,7 @@
return found_dex_pc;
}
+NO_STACK_PROTECTOR
void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result,
const char* shorty) {
if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) {
@@ -551,6 +604,12 @@
return nullptr;
}
+ // We should not reach here with a pc of 0. pc can be 0 for downcalls when walking the stack.
+ // For native methods this case is handled by the caller by checking the quick frame tag. See
+ // StackVisitor::WalkStack for more details. For non-native methods pc can be 0 only for runtime
+ // methods or proxy invoke handlers which are handled earlier.
+ DCHECK_NE(pc, 0u) << "PC 0 for " << PrettyMethod();
+
// Check whether the current entry point contains this pc.
if (!class_linker->IsQuickGenericJniStub(existing_entry_point) &&
!class_linker->IsQuickResolutionStub(existing_entry_point) &&
@@ -592,21 +651,17 @@
OatFile::OatMethod oat_method =
FindOatMethodFor(this, class_linker->GetImagePointerSize(), &found);
if (!found) {
- if (IsNative()) {
- // We are running the GenericJNI stub. The entrypoint may point
- // to different entrypoints or to a JIT-compiled JNI stub.
- DCHECK(class_linker->IsQuickGenericJniStub(existing_entry_point) ||
- class_linker->IsQuickResolutionStub(existing_entry_point) ||
- existing_entry_point == GetQuickInstrumentationEntryPoint() ||
- (jit != nullptr && jit->GetCodeCache()->ContainsPc(existing_entry_point)))
- << " entrypoint: " << existing_entry_point
- << " size: " << OatQuickMethodHeader::FromEntryPoint(existing_entry_point)->GetCodeSize()
- << " pc: " << reinterpret_cast<const void*>(pc);
- return nullptr;
- }
- // Only for unit tests.
- // TODO(ngeoffray): Update these tests to pass the right pc?
- return OatQuickMethodHeader::FromEntryPoint(existing_entry_point);
+ CHECK(IsNative());
+ // We are running the GenericJNI stub. The entrypoint may point
+ // to different entrypoints or to a JIT-compiled JNI stub.
+ DCHECK(class_linker->IsQuickGenericJniStub(existing_entry_point) ||
+ class_linker->IsQuickResolutionStub(existing_entry_point) ||
+ existing_entry_point == GetQuickInstrumentationEntryPoint() ||
+ (jit != nullptr && jit->GetCodeCache()->ContainsPc(existing_entry_point)))
+ << " entrypoint: " << existing_entry_point
+ << " size: " << OatQuickMethodHeader::FromEntryPoint(existing_entry_point)->GetCodeSize()
+ << " pc: " << reinterpret_cast<const void*>(pc);
+ return nullptr;
}
const void* oat_entry_point = oat_method.GetQuickCode();
if (oat_entry_point == nullptr || class_linker->IsQuickGenericJniStub(oat_entry_point)) {
@@ -615,10 +670,13 @@
}
OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromEntryPoint(oat_entry_point);
- if (pc == 0) {
- // This is a downcall, it can only happen for a native method.
- DCHECK(IsNative());
- return method_header;
+ // We could have existing Oat code for native methods but we may not use it if the runtime is java
+ // debuggable or when profiling boot class path. There is no easy way to check if the pc
+ // corresponds to QuickGenericJniStub. Since we have eliminated all the other cases, if the pc
+ // doesn't correspond to the AOT code then we must be running QuickGenericJniStub.
+ if (IsNative() && !method_header->Contains(pc)) {
+ DCHECK_NE(pc, 0u) << "PC 0 for " << PrettyMethod();
+ return nullptr;
}
DCHECK(method_header->Contains(pc))
@@ -728,16 +786,16 @@
// the entry point to the JIT code, but this would require taking the JIT code cache
// lock to notify it, which we do not want at this level.
Runtime* runtime = Runtime::Current();
+ const void* entry_point = GetEntryPointFromQuickCompiledCodePtrSize(image_pointer_size);
if (runtime->UseJitCompilation()) {
- if (runtime->GetJit()->GetCodeCache()->ContainsPc(GetEntryPointFromQuickCompiledCode())) {
+ if (runtime->GetJit()->GetCodeCache()->ContainsPc(entry_point)) {
SetEntryPointFromQuickCompiledCodePtrSize(
src->IsNative() ? GetQuickGenericJniStub() : GetQuickToInterpreterBridge(),
image_pointer_size);
}
}
- if (interpreter::IsNterpSupported() &&
- (GetEntryPointFromQuickCompiledCodePtrSize(image_pointer_size) ==
- interpreter::GetNterpEntryPoint())) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ if (interpreter::IsNterpSupported() && class_linker->IsNterpEntryPoint(entry_point)) {
// If the entrypoint is nterp, it's too early to check if the new method
// will support it. So for simplicity, use the interpreter bridge.
SetEntryPointFromQuickCompiledCodePtrSize(GetQuickToInterpreterBridge(), image_pointer_size);
diff --git a/runtime/art_method.h b/runtime/art_method.h
index c2de718..7841e3a 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -49,6 +49,7 @@
class ImtConflictTable;
enum InvokeType : uint32_t;
union JValue;
+template<typename T> class LengthPrefixedArray;
class OatQuickMethodHeader;
class ProfilingInfo;
class ScopedObjectAccessAlreadyRunnable;
@@ -67,6 +68,22 @@
class String;
} // namespace mirror
+namespace detail {
+template <char Shorty> struct ShortyTraits;
+template <> struct ShortyTraits<'V'>;
+template <> struct ShortyTraits<'Z'>;
+template <> struct ShortyTraits<'B'>;
+template <> struct ShortyTraits<'C'>;
+template <> struct ShortyTraits<'S'>;
+template <> struct ShortyTraits<'I'>;
+template <> struct ShortyTraits<'J'>;
+template <> struct ShortyTraits<'F'>;
+template <> struct ShortyTraits<'D'>;
+template <> struct ShortyTraits<'L'>;
+template <char Shorty> struct HandleShortyTraits;
+template <> struct HandleShortyTraits<'L'>;
+} // namespace detail
+
class ArtMethod final {
public:
// Should the class state be checked on sensitive operations?
@@ -87,6 +104,23 @@
jobject jlr_method)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Visit the declaring class in 'method' if it is within [start_boundary, end_boundary).
+ template<typename RootVisitorType>
+ static void VisitRoots(RootVisitorType& visitor,
+ uint8_t* start_boundary,
+ uint8_t* end_boundary,
+ ArtMethod* method)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Visit declaring classes of all the art-methods in 'array' that reside
+ // in [start_boundary, end_boundary).
+ template<PointerSize kPointerSize, typename RootVisitorType>
+ static void VisitArrayRoots(RootVisitorType& visitor,
+ uint8_t* start_boundary,
+ uint8_t* end_boundary,
+ LengthPrefixedArray<ArtMethod>* array)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
ALWAYS_INLINE ObjPtr<mirror::Class> GetDeclaringClass() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -131,27 +165,47 @@
// Returns true if the method is declared public.
bool IsPublic() const {
- return (GetAccessFlags() & kAccPublic) != 0;
+ return IsPublic(GetAccessFlags());
+ }
+
+ static bool IsPublic(uint32_t access_flags) {
+ return (access_flags & kAccPublic) != 0;
}
// Returns true if the method is declared private.
bool IsPrivate() const {
- return (GetAccessFlags() & kAccPrivate) != 0;
+ return IsPrivate(GetAccessFlags());
+ }
+
+ static bool IsPrivate(uint32_t access_flags) {
+ return (access_flags & kAccPrivate) != 0;
}
// Returns true if the method is declared static.
bool IsStatic() const {
- return (GetAccessFlags() & kAccStatic) != 0;
+ return IsStatic(GetAccessFlags());
+ }
+
+ static bool IsStatic(uint32_t access_flags) {
+ return (access_flags & kAccStatic) != 0;
}
// Returns true if the method is a constructor according to access flags.
bool IsConstructor() const {
- return (GetAccessFlags() & kAccConstructor) != 0;
+ return IsConstructor(GetAccessFlags());
+ }
+
+ static bool IsConstructor(uint32_t access_flags) {
+ return (access_flags & kAccConstructor) != 0;
}
// Returns true if the method is a class initializer according to access flags.
bool IsClassInitializer() const {
- return IsConstructor() && IsStatic();
+ return IsClassInitializer(GetAccessFlags());
+ }
+
+ static bool IsClassInitializer(uint32_t access_flags) {
+ return IsConstructor(access_flags) && IsStatic(access_flags);
}
// Returns true if the method is static, private, or a constructor.
@@ -166,16 +220,30 @@
// Returns true if the method is declared synchronized.
bool IsSynchronized() const {
+ return IsSynchronized(GetAccessFlags());
+ }
+
+ static bool IsSynchronized(uint32_t access_flags) {
constexpr uint32_t synchonized = kAccSynchronized | kAccDeclaredSynchronized;
- return (GetAccessFlags() & synchonized) != 0;
+ return (access_flags & synchonized) != 0;
}
+ // Returns true if the method is declared final.
bool IsFinal() const {
- return (GetAccessFlags() & kAccFinal) != 0;
+ return IsFinal(GetAccessFlags());
}
+ static bool IsFinal(uint32_t access_flags) {
+ return (access_flags & kAccFinal) != 0;
+ }
+
+ // Returns true if the method is an intrinsic.
bool IsIntrinsic() const {
- return (GetAccessFlags() & kAccIntrinsic) != 0;
+ return IsIntrinsic(GetAccessFlags());
+ }
+
+ static bool IsIntrinsic(uint32_t access_flags) {
+ return (access_flags & kAccIntrinsic) != 0;
}
ALWAYS_INLINE void SetIntrinsic(uint32_t intrinsic) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -192,53 +260,76 @@
void SetNotIntrinsic() REQUIRES_SHARED(Locks::mutator_lock_);
+ // Returns true if the method is a copied method.
bool IsCopied() const {
+ return IsCopied(GetAccessFlags());
+ }
+
+ static bool IsCopied(uint32_t access_flags) {
// We do not have intrinsics for any default methods and therefore intrinsics are never copied.
// So we are using a flag from the intrinsic flags range and need to check `kAccIntrinsic` too.
static_assert((kAccCopied & kAccIntrinsicBits) != 0,
"kAccCopied deliberately overlaps intrinsic bits");
- const bool copied = (GetAccessFlags() & (kAccIntrinsic | kAccCopied)) == kAccCopied;
+ const bool copied = (access_flags & (kAccIntrinsic | kAccCopied)) == kAccCopied;
// (IsMiranda() || IsDefaultConflicting()) implies copied
- DCHECK(!(IsMiranda() || IsDefaultConflicting()) || copied)
+ DCHECK(!(IsMiranda(access_flags) || IsDefaultConflicting(access_flags)) || copied)
<< "Miranda or default-conflict methods must always be copied.";
return copied;
}
bool IsMiranda() const {
+ return IsMiranda(GetAccessFlags());
+ }
+
+ static bool IsMiranda(uint32_t access_flags) {
// Miranda methods are marked as copied and abstract but not default.
// We need to check the kAccIntrinsic too, see `IsCopied()`.
static constexpr uint32_t kMask = kAccIntrinsic | kAccCopied | kAccAbstract | kAccDefault;
static constexpr uint32_t kValue = kAccCopied | kAccAbstract;
- return (GetAccessFlags() & kMask) == kValue;
+ return (access_flags & kMask) == kValue;
}
// 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() const {
+ return IsDefaultConflicting(GetAccessFlags());
+ }
+
+ static bool IsDefaultConflicting(uint32_t access_flags) {
// Default conflct methods are marked as copied, abstract and default.
// We need to check the kAccIntrinsic too, see `IsCopied()`.
static constexpr uint32_t kMask = kAccIntrinsic | kAccCopied | kAccAbstract | kAccDefault;
static constexpr uint32_t kValue = kAccCopied | kAccAbstract | kAccDefault;
- return (GetAccessFlags() & kMask) == kValue;
+ return (access_flags & kMask) == kValue;
}
// Returns true if invoking this method will not throw an AbstractMethodError or
// IncompatibleClassChangeError.
bool IsInvokable() const {
- // Default conflicting methods are marked with `kAccAbstract` (as well as `kAccCopied`
- // and `kAccDefault`) but they are not considered abstract, see `IsAbstract()`.
- DCHECK_EQ((GetAccessFlags() & kAccAbstract) == 0, !IsDefaultConflicting() && !IsAbstract());
- return (GetAccessFlags() & kAccAbstract) == 0;
+ return IsInvokable(GetAccessFlags());
}
+ static bool IsInvokable(uint32_t access_flags) {
+ // Default conflicting methods are marked with `kAccAbstract` (as well as `kAccCopied`
+ // and `kAccDefault`) but they are not considered abstract, see `IsAbstract()`.
+ DCHECK_EQ((access_flags & kAccAbstract) == 0,
+ !IsDefaultConflicting(access_flags) && !IsAbstract(access_flags));
+ return (access_flags & kAccAbstract) == 0;
+ }
+
+ // Returns true if the method is marked as pre-compiled.
bool IsPreCompiled() const {
+ return IsPreCompiled(GetAccessFlags());
+ }
+
+ static bool IsPreCompiled(uint32_t access_flags) {
// kAccCompileDontBother and kAccPreCompiled overlap with kAccIntrinsicBits.
static_assert((kAccCompileDontBother & kAccIntrinsicBits) != 0);
static_assert((kAccPreCompiled & kAccIntrinsicBits) != 0);
static constexpr uint32_t kMask = kAccIntrinsic | kAccCompileDontBother | kAccPreCompiled;
static constexpr uint32_t kValue = kAccCompileDontBother | kAccPreCompiled;
- return (GetAccessFlags() & kMask) == kValue;
+ return (access_flags & kMask) == kValue;
}
void SetPreCompiled() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -254,41 +345,51 @@
AddAccessFlags(kAccPreCompiled | kAccCompileDontBother);
}
+ void ClearPreCompiled() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ClearAccessFlags(kAccPreCompiled | kAccCompileDontBother);
+ }
+
+ // Returns true if the method resides in shared memory.
bool IsMemorySharedMethod() {
- return (GetAccessFlags() & kAccMemorySharedMethod) != 0;
+ return IsMemorySharedMethod(GetAccessFlags());
+ }
+
+ static bool IsMemorySharedMethod(uint32_t access_flags) {
+ return (access_flags & kAccMemorySharedMethod) != 0;
}
void SetMemorySharedMethod() REQUIRES_SHARED(Locks::mutator_lock_) {
- // Disable until we make sure critical code is AOTed.
- static constexpr bool kEnabledMemorySharedMethod = false;
- if (kEnabledMemorySharedMethod && !IsIntrinsic() && !IsAbstract()) {
+ uint32_t access_flags = GetAccessFlags();
+ if (!IsIntrinsic(access_flags) && !IsAbstract(access_flags)) {
AddAccessFlags(kAccMemorySharedMethod);
SetHotCounter();
}
}
void ClearMemorySharedMethod() REQUIRES_SHARED(Locks::mutator_lock_) {
- if (IsIntrinsic() || IsAbstract()) {
+ uint32_t access_flags = GetAccessFlags();
+ if (IsIntrinsic(access_flags) || IsAbstract(access_flags)) {
return;
}
- if (IsMemorySharedMethod()) {
+ if (IsMemorySharedMethod(access_flags)) {
ClearAccessFlags(kAccMemorySharedMethod);
}
}
- void ClearPreCompiled() REQUIRES_SHARED(Locks::mutator_lock_) {
- ClearAccessFlags(kAccPreCompiled | kAccCompileDontBother);
+ // Returns true if the method can be compiled.
+ bool IsCompilable() const {
+ return IsCompilable(GetAccessFlags());
}
- bool IsCompilable() const {
- if (IsIntrinsic()) {
+ static bool IsCompilable(uint32_t access_flags) {
+ if (IsIntrinsic(access_flags)) {
// kAccCompileDontBother overlaps with kAccIntrinsicBits.
return true;
}
- if (IsPreCompiled()) {
+ if (IsPreCompiled(access_flags)) {
return true;
}
- return (GetAccessFlags() & kAccCompileDontBother) == 0;
+ return (access_flags & kAccCompileDontBother) == 0;
}
void ClearDontCompile() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -303,52 +404,107 @@
// This is set by the class linker.
bool IsDefault() const {
- static_assert((kAccDefault & (kAccIntrinsic | kAccIntrinsicBits)) == 0,
- "kAccDefault conflicts with intrinsic modifier");
- return (GetAccessFlags() & kAccDefault) != 0;
+ return IsDefault(GetAccessFlags());
}
+ static bool IsDefault(uint32_t access_flags) {
+ static_assert((kAccDefault & (kAccIntrinsic | kAccIntrinsicBits)) == 0,
+ "kAccDefault conflicts with intrinsic modifier");
+ return (access_flags & kAccDefault) != 0;
+ }
+
+ // Returns true if the method is obsolete.
bool IsObsolete() const {
- return (GetAccessFlags() & kAccObsoleteMethod) != 0;
+ return IsObsolete(GetAccessFlags());
+ }
+
+ static bool IsObsolete(uint32_t access_flags) {
+ return (access_flags & kAccObsoleteMethod) != 0;
}
void SetIsObsolete() REQUIRES_SHARED(Locks::mutator_lock_) {
AddAccessFlags(kAccObsoleteMethod);
}
+ // Returns true if the method is native.
bool IsNative() const {
- return (GetAccessFlags() & kAccNative) != 0;
+ return IsNative(GetAccessFlags());
+ }
+
+ static bool IsNative(uint32_t access_flags) {
+ return (access_flags & kAccNative) != 0;
}
// Checks to see if the method was annotated with @dalvik.annotation.optimization.FastNative.
bool IsFastNative() const {
+ return IsFastNative(GetAccessFlags());
+ }
+
+ static bool IsFastNative(uint32_t access_flags) {
// 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.
constexpr uint32_t mask = kAccFastNative | kAccNative;
- return (GetAccessFlags() & mask) == mask;
+ return (access_flags & mask) == mask;
}
// Checks to see if the method was annotated with @dalvik.annotation.optimization.CriticalNative.
bool IsCriticalNative() const {
+ return IsCriticalNative(GetAccessFlags());
+ }
+
+ static bool IsCriticalNative(uint32_t access_flags) {
// 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.
constexpr uint32_t mask = kAccCriticalNative | kAccNative;
- return (GetAccessFlags() & mask) == mask;
+ return (access_flags & mask) == mask;
}
+ // Returns true if the method is managed (not native).
+ bool IsManaged() const {
+ return IsManaged(GetAccessFlags());
+ }
+
+ static bool IsManaged(uint32_t access_flags) {
+ return !IsNative(access_flags);
+ }
+
+ // Returns true if the method is managed (not native) and invokable.
+ bool IsManagedAndInvokable() const {
+ return IsManagedAndInvokable(GetAccessFlags());
+ }
+
+ static bool IsManagedAndInvokable(uint32_t access_flags) {
+ return IsManaged(access_flags) && IsInvokable(access_flags);
+ }
+
+ // Returns true if the method is abstract.
bool IsAbstract() const {
+ return IsAbstract(GetAccessFlags());
+ }
+
+ static bool IsAbstract(uint32_t access_flags) {
// Default confliciting methods have `kAccAbstract` set but they are not actually abstract.
- return (GetAccessFlags() & kAccAbstract) != 0 && !IsDefaultConflicting();
+ return (access_flags & kAccAbstract) != 0 && !IsDefaultConflicting(access_flags);
}
+ // Returns true if the method is declared synthetic.
bool IsSynthetic() const {
- return (GetAccessFlags() & kAccSynthetic) != 0;
+ return IsSynthetic(GetAccessFlags());
}
+ static bool IsSynthetic(uint32_t access_flags) {
+ return (access_flags & kAccSynthetic) != 0;
+ }
+
+ // Returns true if the method is declared varargs.
bool IsVarargs() const {
- return (GetAccessFlags() & kAccVarargs) != 0;
+ return IsVarargs(GetAccessFlags());
+ }
+
+ static bool IsVarargs(uint32_t access_flags) {
+ return (access_flags & kAccVarargs) != 0;
}
bool IsProxyMethod() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -372,10 +528,15 @@
ClearAccessFlags(kAccSkipAccessChecks);
}
+ // Returns true if the method has previously been warm.
bool PreviouslyWarm() const {
+ return PreviouslyWarm(GetAccessFlags());
+ }
+
+ static bool PreviouslyWarm(uint32_t access_flags) {
// kAccPreviouslyWarm overlaps with kAccIntrinsicBits. Return true for intrinsics.
constexpr uint32_t mask = kAccPreviouslyWarm | kAccIntrinsic;
- return (GetAccessFlags() & mask) != 0u;
+ return (access_flags & mask) != 0u;
}
void SetPreviouslyWarm() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -389,10 +550,14 @@
// Should this method be run in the interpreter and count locks (e.g., failed structured-
// locking verification)?
bool MustCountLocks() const {
- if (IsIntrinsic()) {
+ return MustCountLocks(GetAccessFlags());
+ }
+
+ static bool MustCountLocks(uint32_t access_flags) {
+ if (IsIntrinsic(access_flags)) {
return false;
}
- return (GetAccessFlags() & kAccMustCountLocks) != 0;
+ return (access_flags & kAccMustCountLocks) != 0;
}
void ClearMustCountLocks() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -404,9 +569,14 @@
ClearAccessFlags(kAccSkipAccessChecks);
}
+ // Returns true if the method is using the nterp entrypoint fast path.
bool HasNterpEntryPointFastPathFlag() const {
+ return HasNterpEntryPointFastPathFlag(GetAccessFlags());
+ }
+
+ static bool HasNterpEntryPointFastPathFlag(uint32_t access_flags) {
constexpr uint32_t mask = kAccNative | kAccNterpEntryPointFastPathFlag;
- return (GetAccessFlags() & mask) == kAccNterpEntryPointFastPathFlag;
+ return (access_flags & mask) == kAccNterpEntryPointFastPathFlag;
}
void SetNterpEntryPointFastPathFlag() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -418,14 +588,21 @@
AddAccessFlags(kAccNterpInvokeFastPathFlag);
}
+ // Returns whether the method is a string constructor. The method must not
+ // be a class initializer. (Class initializers are called from a different
+ // context where we do not need to check for string constructors.)
+ bool IsStringConstructor() REQUIRES_SHARED(Locks::mutator_lock_);
+
// Returns true if this method could be overridden by a default method.
bool IsOverridableByDefaultMethod() REQUIRES_SHARED(Locks::mutator_lock_);
bool CheckIncompatibleClassChange(InvokeType type) REQUIRES_SHARED(Locks::mutator_lock_);
// Throws the error that would result from trying to invoke this method (i.e.
- // IncompatibleClassChangeError or AbstractMethodError). Only call if !IsInvokable();
- void ThrowInvocationTimeError() REQUIRES_SHARED(Locks::mutator_lock_);
+ // IncompatibleClassChangeError, AbstractMethodError, or IllegalAccessError).
+ // Only call if !IsInvokable();
+ void ThrowInvocationTimeError(ObjPtr<mirror::Object> receiver)
+ REQUIRES_SHARED(Locks::mutator_lock_);
uint16_t GetMethodIndex() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -490,6 +667,80 @@
void Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result, const char* shorty)
REQUIRES_SHARED(Locks::mutator_lock_);
+ template <char ReturnType, char... ArgType>
+ typename detail::ShortyTraits<ReturnType>::Type
+ InvokeStatic(Thread* self, typename detail::ShortyTraits<ArgType>::Type... args)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ template <char ReturnType, char... ArgType>
+ typename detail::ShortyTraits<ReturnType>::Type
+ InvokeInstance(Thread* self,
+ ObjPtr<mirror::Object> receiver,
+ typename detail::ShortyTraits<ArgType>::Type... args)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ template <char ReturnType, char... ArgType>
+ typename detail::ShortyTraits<ReturnType>::Type
+ InvokeFinal(Thread* self,
+ ObjPtr<mirror::Object> receiver,
+ typename detail::ShortyTraits<ArgType>::Type... args)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ template <char ReturnType, char... ArgType>
+ typename detail::ShortyTraits<ReturnType>::Type
+ InvokeVirtual(Thread* self,
+ ObjPtr<mirror::Object> receiver,
+ typename detail::ShortyTraits<ArgType>::Type... args)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ template <char ReturnType, char... ArgType>
+ typename detail::ShortyTraits<ReturnType>::Type
+ InvokeInterface(Thread* self,
+ ObjPtr<mirror::Object> receiver,
+ typename detail::ShortyTraits<ArgType>::Type... args)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ template <char... ArgType, typename HandleScopeType>
+ Handle<mirror::Object> NewObject(HandleScopeType& hs,
+ Thread* self,
+ typename detail::HandleShortyTraits<ArgType>::Type... args)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ template <char... ArgType>
+ ObjPtr<mirror::Object> NewObject(Thread* self,
+ typename detail::HandleShortyTraits<ArgType>::Type... args)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Returns true if the method needs a class initialization check according to access flags.
+ // Only static methods other than the class initializer need this check.
+ // The caller is responsible for performing the actual check.
+ bool NeedsClinitCheckBeforeCall() const {
+ return NeedsClinitCheckBeforeCall(GetAccessFlags());
+ }
+
+ static bool NeedsClinitCheckBeforeCall(uint32_t access_flags) {
+ // The class initializer is special as it is invoked during initialization
+ // and does not need the check.
+ return IsStatic(access_flags) && !IsConstructor(access_flags);
+ }
+
+ // Check if the method needs a class initialization check before call
+ // and its declaring class is not yet visibly initialized.
+ // (The class needs to be visibly initialized before we can use entrypoints
+ // to compiled code for static methods. See b/18161648 .)
+ template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+ bool StillNeedsClinitCheck() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Similar to `StillNeedsClinitCheck()` but the method's declaring class may
+ // be dead but not yet reclaimed by the GC, so we cannot do a full read barrier
+ // but we still want to check the class status in the to-space class if any.
+ // Note: JIT can hold and use such methods during managed heap GC.
+ bool StillNeedsClinitCheckMayBeDead() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Check if the declaring class has been verified and look at the to-space
+ // class object, if any, as in `StillNeedsClinitCheckMayBeDead()`.
+ bool IsDeclaringClassVerifiedMayBeDead() REQUIRES_SHARED(Locks::mutator_lock_);
+
const void* GetEntryPointFromQuickCompiledCode() const {
return GetEntryPointFromQuickCompiledCodePtrSize(kRuntimePointerSize);
}
@@ -538,7 +789,6 @@
SetDataPtrSize(table, pointer_size);
}
- template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
ALWAYS_INLINE bool HasSingleImplementation() REQUIRES_SHARED(Locks::mutator_lock_);
ALWAYS_INLINE void SetHasSingleImplementation(bool single_impl)
@@ -612,7 +862,11 @@
}
bool HasCodeItem() REQUIRES_SHARED(Locks::mutator_lock_) {
- return !IsRuntimeMethod() && !IsNative() && !IsProxyMethod() && !IsAbstract();
+ uint32_t access_flags = GetAccessFlags();
+ return !IsNative(access_flags) &&
+ !IsAbstract(access_flags) &&
+ !IsRuntimeMethod() &&
+ !IsProxyMethod();
}
// We need to explicitly indicate whether the code item is obtained from the compact dex file,
@@ -635,7 +889,9 @@
REQUIRES_SHARED(Locks::mutator_lock_);
// NO_THREAD_SAFETY_ANALYSIS since we don't know what the callback requires.
- template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename RootVisitorType>
+ template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier,
+ bool kVisitProxyMethod = true,
+ typename RootVisitorType>
void VisitRoots(RootVisitorType& visitor, PointerSize pointer_size) NO_THREAD_SAFETY_ANALYSIS;
const DexFile* GetDexFile() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -691,6 +947,7 @@
template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
ObjPtr<mirror::DexCache> GetDexCache() REQUIRES_SHARED(Locks::mutator_lock_);
+ template <ReadBarrierOption kReadBarrierOption>
ObjPtr<mirror::DexCache> GetObsoleteDexCache() REQUIRES_SHARED(Locks::mutator_lock_);
ALWAYS_INLINE ArtMethod* GetInterfaceMethodForProxyUnchecked(PointerSize pointer_size)
@@ -824,7 +1081,7 @@
// Entry within a dispatch table for this method. For static/direct methods the index is into
// the declaringClass.directMethods, for virtual methods the vtable and for interface methods the
- // ifTable.
+ // interface's method array in `IfTable`s of implementing classes.
uint16_t method_index_;
union {
@@ -918,6 +1175,10 @@
access_flags_.fetch_and(~flag, std::memory_order_relaxed);
}
+ // Helper method for checking the class status of a possibly dead declaring class.
+ // See `StillNeedsClinitCheckMayBeDead()` and `IsDeclaringClassVerifierMayBeDead()`.
+ ObjPtr<mirror::Class> GetDeclaringClassMayBeDead() REQUIRES_SHARED(Locks::mutator_lock_);
+
// Used by GetName and GetNameView to share common code.
const char* GetRuntimeMethodName() REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/art_method_test.cc b/runtime/art_method_test.cc
new file mode 100644
index 0000000..b71f8a3
--- /dev/null
+++ b/runtime/art_method_test.cc
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2022 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 <type_traits>
+
+#include "art_method-alloc-inl.h"
+
+#include "base/casts.h"
+#include "common_runtime_test.h"
+#include "mirror/class-alloc-inl.h"
+#include "well_known_classes.h"
+
+namespace art {
+
+namespace {
+// Helper function to avoid `ASSERT_EQ` with floating point types.
+int32_t ToIntegralType(float value) { return bit_cast<int32_t>(value); }
+int64_t ToIntegralType(double value) { return bit_cast<int64_t>(value); }
+template <typename T> T ToIntegralType(T value) { return value; }
+}
+
+class ArtMethodTest : public CommonRuntimeTest {
+ protected:
+ ArtMethodTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+
+ // Test primitive type boxing and unboxing.
+ //
+ // This provides basic checks that the translation of the compile-time shorty
+ // to argument types and return type are correct and that values are passed
+ // correctly for these single-argument calls (`ArtMethod::InvokeStatic()` with
+ // primitive args and `ArtMethod::InvokeInstance()` with a reference arg).
+ template <typename Type, char kPrimitive>
+ void TestBoxUnbox(ArtMethod* value_of, const char* unbox_name, Type value) {
+ Thread* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ ASSERT_STREQ(value_of->GetName(), "valueOf");
+ std::string unbox_signature = std::string("()") + kPrimitive;
+ ArtMethod* unbox_method = value_of->GetDeclaringClass()->FindClassMethod(
+ unbox_name, unbox_signature.c_str(), kRuntimePointerSize);
+ ASSERT_TRUE(unbox_method != nullptr);
+ ASSERT_FALSE(unbox_method->IsStatic());
+ ASSERT_TRUE(value_of->GetDeclaringClass()->IsFinal());
+
+ static_assert(std::is_same_v<ObjPtr<mirror::Object> (ArtMethod::*)(Thread*, Type),
+ decltype(&ArtMethod::InvokeStatic<'L', kPrimitive>)>);
+ StackHandleScope<1u> hs(self);
+ Handle<mirror::Object> boxed =
+ hs.NewHandle(value_of->InvokeStatic<'L', kPrimitive>(self, value));
+ ASSERT_TRUE(boxed != nullptr);
+ ASSERT_OBJ_PTR_EQ(boxed->GetClass(), value_of->GetDeclaringClass());
+ static_assert(std::is_same_v<Type (ArtMethod::*)(Thread*, ObjPtr<mirror::Object>),
+ decltype(&ArtMethod::InvokeInstance<kPrimitive>)>);
+ // Exercise both `InvokeInstance()` and `InvokeFinal()` (boxing classes are final).
+ Type unboxed1 = unbox_method->InvokeInstance<kPrimitive>(self, boxed.Get());
+ ASSERT_EQ(ToIntegralType(value), ToIntegralType(unboxed1));
+ Type unboxed2 = unbox_method->InvokeFinal<kPrimitive>(self, boxed.Get());
+ ASSERT_EQ(ToIntegralType(value), ToIntegralType(unboxed2));
+ }
+};
+
+TEST_F(ArtMethodTest, BoxUnboxBoolean) {
+ TestBoxUnbox<bool, 'Z'>(WellKnownClasses::java_lang_Boolean_valueOf, "booleanValue", true);
+}
+
+TEST_F(ArtMethodTest, BoxUnboxByte) {
+ TestBoxUnbox<int8_t, 'B'>(WellKnownClasses::java_lang_Byte_valueOf, "byteValue", -12);
+}
+
+TEST_F(ArtMethodTest, BoxUnboxChar) {
+ TestBoxUnbox<uint16_t, 'C'>(WellKnownClasses::java_lang_Character_valueOf, "charValue", 0xffaa);
+}
+
+TEST_F(ArtMethodTest, BoxUnboxShort) {
+ TestBoxUnbox<int16_t, 'S'>(WellKnownClasses::java_lang_Short_valueOf, "shortValue", -0x1234);
+}
+
+TEST_F(ArtMethodTest, BoxUnboxInt) {
+ TestBoxUnbox<int32_t, 'I'>(WellKnownClasses::java_lang_Integer_valueOf, "intValue", -0x12345678);
+}
+
+TEST_F(ArtMethodTest, BoxUnboxLong) {
+ TestBoxUnbox<int64_t, 'J'>(
+ WellKnownClasses::java_lang_Long_valueOf, "longValue", UINT64_C(-0x1234567887654321));
+}
+
+TEST_F(ArtMethodTest, BoxUnboxFloat) {
+ TestBoxUnbox<float, 'F'>(WellKnownClasses::java_lang_Float_valueOf, "floatValue", -2.0f);
+}
+
+TEST_F(ArtMethodTest, BoxUnboxDouble) {
+ TestBoxUnbox<double, 'D'>(WellKnownClasses::java_lang_Double_valueOf, "doubleValue", 8.0);
+}
+
+TEST_F(ArtMethodTest, ArrayList) {
+ Thread* self = Thread::Current();
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ScopedObjectAccess soa(self);
+ StackHandleScope<4u> hs(self);
+ Handle<mirror::Class> list_class =
+ hs.NewHandle(class_linker->FindSystemClass(self, "Ljava/util/List;"));
+ ASSERT_TRUE(list_class != nullptr);
+ Handle<mirror::Class> abstract_list_class =
+ hs.NewHandle(class_linker->FindSystemClass(self, "Ljava/util/AbstractList;"));
+ ASSERT_TRUE(abstract_list_class != nullptr);
+ Handle<mirror::Class> array_list_class =
+ hs.NewHandle(class_linker->FindSystemClass(self, "Ljava/util/ArrayList;"));
+ ASSERT_TRUE(array_list_class != nullptr);
+ ASSERT_TRUE(abstract_list_class->Implements(list_class.Get()));
+ ASSERT_TRUE(array_list_class->IsSubClass(abstract_list_class.Get()));
+
+ ArtMethod* init = array_list_class->FindClassMethod("<init>", "()V", kRuntimePointerSize);
+ ASSERT_TRUE(init != nullptr);
+ ArtMethod* array_list_size_method =
+ array_list_class->FindClassMethod("size", "()I", kRuntimePointerSize);
+ DCHECK(array_list_size_method != nullptr);
+ ArtMethod* abstract_list_size_method =
+ abstract_list_class->FindClassMethod("size", "()I", kRuntimePointerSize);
+ DCHECK(abstract_list_size_method != nullptr);
+ ArtMethod* list_size_method =
+ list_class->FindInterfaceMethod("size", "()I", kRuntimePointerSize);
+ DCHECK(list_size_method != nullptr);
+
+ Handle<mirror::Object> array_list = init->NewObject<>(hs, self);
+ ASSERT_FALSE(self->IsExceptionPending());
+ ASSERT_TRUE(array_list != nullptr);
+
+ // Invoke `ArrayList.size()` directly, with virtual dispatch from
+ // `AbstractList.size()` and with interface dispatch from `List.size()`.
+ int32_t size = array_list_size_method->InvokeInstance<'I'>(self, array_list.Get());
+ ASSERT_FALSE(self->IsExceptionPending());
+ ASSERT_EQ(0, size);
+ size = abstract_list_size_method->InvokeVirtual<'I'>(self, array_list.Get());
+ ASSERT_FALSE(self->IsExceptionPending());
+ ASSERT_EQ(0, size);
+ size = list_size_method->InvokeInterface<'I'>(self, array_list.Get());
+ ASSERT_FALSE(self->IsExceptionPending());
+ ASSERT_EQ(0, size);
+
+ // Try to invoke abstract method `AbstractList.size()` directly.
+ abstract_list_size_method->InvokeInstance<'I'>(self, array_list.Get());
+ ASSERT_TRUE(self->IsExceptionPending());
+ self->ClearException();
+}
+
+} // namespace art
diff --git a/runtime/art_standalone_runtime_compiler_tests.xml b/runtime/art_standalone_runtime_compiler_tests.xml
deleted file mode 100644
index 3ae3a82..0000000
--- a/runtime/art_standalone_runtime_compiler_tests.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 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.
--->
-<configuration description="Runs art_standalone_runtime_compiler_tests.">
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="cleanup" value="true" />
- <option name="push" value="art_standalone_runtime_compiler_tests->/data/local/tmp/art_standalone_runtime_compiler_tests/art_standalone_runtime_compiler_tests" />
- <option name="append-bitness" value="true" />
- </target_preparer>
-
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="cleanup" value="true" />
- <option name="push" value="art-gtest-jars-Main.jar->/data/local/tmp/art_standalone_runtime_compiler_tests/art-gtest-jars-Main.jar" />
- <option name="push" value="art-gtest-jars-NonStaticLeafMethods.jar->/data/local/tmp/art_standalone_runtime_compiler_tests/art-gtest-jars-NonStaticLeafMethods.jar" />
- <option name="push" value="art-gtest-jars-StaticLeafMethods.jar->/data/local/tmp/art_standalone_runtime_compiler_tests/art-gtest-jars-StaticLeafMethods.jar" />
- </target_preparer>
-
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
- <option name="cleanup" value="true" />
- <option name="append-bitness" value="true" />
- <option name="push-file" key="generate-boot-image" value="/data/local/tmp/art_standalone_runtime_compiler_tests/generate-boot-image" />
- </target_preparer>
-
- <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="throw-if-cmd-fail" value="true" />
- <option name="run-command" value="mkdir -p /data/local/tmp/art_standalone_runtime_compiler_tests/art_boot_images" />
- <option name="run-command" value="/data/local/tmp/art_standalone_runtime_compiler_tests/generate-boot-image --output-dir=/data/local/tmp/art_standalone_runtime_compiler_tests/art_boot_images" />
- <option name="teardown-command" value="rm -rf /data/local/tmp/art_standalone_runtime_compiler_tests/art_boot_images" />
- </target_preparer>
-
- <test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/local/tmp/art_standalone_runtime_compiler_tests" />
- <option name="module-name" value="art_standalone_runtime_compiler_tests" />
- <option name="ld-library-path-32" value="/apex/com.android.art/lib" />
- <option name="ld-library-path-64" value="/apex/com.android.art/lib64" />
- </test>
-
- <!-- When this test is run in a Mainline context (e.g. with `mts-tradefed`), only enable it if
- one of the Mainline modules below is present on the device used for testing. -->
- <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
- <!-- ART Mainline Module (internal version). -->
- <option name="mainline-module-package-name" value="com.google.android.art" />
- <!-- ART Mainline Module (external (AOSP) version). -->
- <option name="mainline-module-package-name" value="com.android.art" />
- </object>
-
- <!-- Only run tests if the device under test is SDK version 31 (Android 12) or above. -->
- <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" />
-</configuration>
diff --git a/runtime/art_standalone_runtime_tests.xml b/runtime/art_standalone_runtime_tests.xml
index 5200529..b210f5e 100644
--- a/runtime/art_standalone_runtime_tests.xml
+++ b/runtime/art_standalone_runtime_tests.xml
@@ -14,6 +14,8 @@
limitations under the License.
-->
<configuration description="Runs art_standalone_runtime_tests.">
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.art.apex" />
+
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="art_standalone_runtime_tests->/data/local/tmp/art_standalone_runtime_tests/art_standalone_runtime_tests" />
@@ -48,6 +50,7 @@
<option name="push" value="art-gtest-jars-MyClass.jar->/data/local/tmp/art_standalone_runtime_tests/art-gtest-jars-MyClass.jar" />
<option name="push" value="art-gtest-jars-MyClassNatives.jar->/data/local/tmp/art_standalone_runtime_tests/art-gtest-jars-MyClassNatives.jar" />
<option name="push" value="art-gtest-jars-Nested.jar->/data/local/tmp/art_standalone_runtime_tests/art-gtest-jars-Nested.jar" />
+ <option name="push" value="art-gtest-jars-NonStaticLeafMethods.jar->/data/local/tmp/art_standalone_runtime_tests/art-gtest-jars-NonStaticLeafMethods.jar" />
<option name="push" value="art-gtest-jars-Packages.jar->/data/local/tmp/art_standalone_runtime_tests/art-gtest-jars-Packages.jar" />
<option name="push" value="art-gtest-jars-ProfileTestMultiDex.jar->/data/local/tmp/art_standalone_runtime_tests/art-gtest-jars-ProfileTestMultiDex.jar" />
<option name="push" value="art-gtest-jars-ProtoCompare.jar->/data/local/tmp/art_standalone_runtime_tests/art-gtest-jars-ProtoCompare.jar" />
@@ -93,7 +96,7 @@
<option name="exclude-filter" value="HiddenApiTest.DexDomain_SystemSystemExtFrameworkDir" />
<option name="exclude-filter" value="HiddenApiTest.DexDomain_SystemSystemExtFrameworkDir_MultiDex" />
<option name="exclude-filter" value="JniInternalTest.CallVarArgMethodBadPrimitive" />
- <option name="exclude-filter" value="OatFileAssistantTest.SystemFrameworkDir" />
+ <option name="exclude-filter" value="OatFileAssistantBaseTest.SystemFrameworkDir" />
<option name="exclude-filter" value="StubTest.Fields16" />
<option name="exclude-filter" value="StubTest.Fields32" />
<option name="exclude-filter" value="StubTest.Fields64" />
@@ -107,10 +110,11 @@
them fail to dynamically link to the expected (64-bit) libraries.
TODO(b/204649079): Investigate these failures and re-enable these tests. -->
- <option name="exclude-filter" value="ExecUtilsTest.EnvSnapshotDeletionsAreNotVisible" />
- <option name="exclude-filter" value="ExecUtilsTest.ExecNoTimeout" />
- <option name="exclude-filter" value="ExecUtilsTest.ExecSuccess" />
- <option name="exclude-filter" value="ExecUtilsTest.ExecTimeout" />
+ <option name="exclude-filter" value="*ExecUtilsTest.EnvSnapshotDeletionsAreNotVisible*" />
+ <option name="exclude-filter" value="*ExecUtilsTest.ExecNoTimeout*" />
+ <option name="exclude-filter" value="*ExecUtilsTest.ExecStatFailed*" />
+ <option name="exclude-filter" value="*ExecUtilsTest.ExecSuccess*" />
+ <option name="exclude-filter" value="*ExecUtilsTest.ExecTimeout*" />
</test>
<!-- When this test is run in a Mainline context (e.g. with `mts-tradefed`), only enable it if
diff --git a/runtime/backtrace_helper.h b/runtime/backtrace_helper.h
index a74d0e0..9be2550 100644
--- a/runtime/backtrace_helper.h
+++ b/runtime/backtrace_helper.h
@@ -26,7 +26,7 @@
namespace art {
-// Using libbacktrace
+// Using libunwindstack
class BacktraceCollector {
public:
BacktraceCollector(uintptr_t* out_frames, size_t max_depth, size_t skip_count)
diff --git a/runtime/barrier.cc b/runtime/barrier.cc
index d144591..a6cc9ba 100644
--- a/runtime/barrier.cc
+++ b/runtime/barrier.cc
@@ -40,6 +40,11 @@
SetCountLocked(self, count_ - 1);
}
+void Barrier::IncrementNoWait(Thread* self) {
+ MutexLock mu(self, *GetLock());
+ SetCountLocked(self, count_ + 1);
+}
+
void Barrier::Wait(Thread* self) {
Increment(self, -1);
}
diff --git a/runtime/barrier.h b/runtime/barrier.h
index 432df76..4c94a14 100644
--- a/runtime/barrier.h
+++ b/runtime/barrier.h
@@ -51,6 +51,9 @@
// Pass through the barrier, decrement the count but do not block.
void Pass(Thread* self) REQUIRES(!GetLock());
+ // Increment the barrier but do not block. The caller should ensure that it
+ // decrements/passes it eventually.
+ void IncrementNoWait(Thread* self) REQUIRES(!GetLock());
// Decrement the count, then wait until the count is zero.
void Wait(Thread* self) REQUIRES(!GetLock());
diff --git a/runtime/barrier_test.cc b/runtime/barrier_test.cc
index 5ec24bc..52959bd 100644
--- a/runtime/barrier_test.cc
+++ b/runtime/barrier_test.cc
@@ -52,6 +52,10 @@
class BarrierTest : public CommonRuntimeTest {
public:
+ BarrierTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+
static int32_t num_threads;
};
diff --git a/runtime/base/gc_visited_arena_pool.cc b/runtime/base/gc_visited_arena_pool.cc
new file mode 100644
index 0000000..6bf52ce
--- /dev/null
+++ b/runtime/base/gc_visited_arena_pool.cc
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2022 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 "base/gc_visited_arena_pool.h"
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/arena_allocator-inl.h"
+#include "base/memfd.h"
+#include "base/utils.h"
+#include "gc/collector/mark_compact-inl.h"
+
+namespace art {
+
+TrackedArena::TrackedArena(uint8_t* start, size_t size, bool pre_zygote_fork)
+ : Arena(), first_obj_array_(nullptr), pre_zygote_fork_(pre_zygote_fork) {
+ static_assert(ArenaAllocator::kArenaAlignment <= kPageSize,
+ "Arena should not need stronger alignment than kPageSize.");
+ DCHECK_ALIGNED(size, kPageSize);
+ DCHECK_ALIGNED(start, kPageSize);
+ memory_ = start;
+ size_ = size;
+ size_t arr_size = size / kPageSize;
+ first_obj_array_.reset(new uint8_t*[arr_size]);
+ std::fill_n(first_obj_array_.get(), arr_size, nullptr);
+}
+
+void TrackedArena::Release() {
+ if (bytes_allocated_ > 0) {
+ // Userfaultfd GC uses MAP_SHARED mappings for linear-alloc and therefore
+ // MADV_DONTNEED will not free the pages from page cache. Therefore use
+ // MADV_REMOVE instead, which is meant for this purpose.
+ // Arenas allocated pre-zygote fork are private anonymous and hence must be
+ // released using MADV_DONTNEED.
+ if (!gUseUserfaultfd || pre_zygote_fork_ ||
+ (madvise(Begin(), Size(), MADV_REMOVE) == -1 && errno == EINVAL)) {
+ // MADV_REMOVE fails if invoked on anonymous mapping, which could happen
+ // if the arena is released before userfaultfd-GC starts using memfd. So
+ // use MADV_DONTNEED.
+ ZeroAndReleasePages(Begin(), Size());
+ }
+ std::fill_n(first_obj_array_.get(), Size() / kPageSize, nullptr);
+ bytes_allocated_ = 0;
+ }
+}
+
+void TrackedArena::SetFirstObject(uint8_t* obj_begin, uint8_t* obj_end) {
+ DCHECK_LE(static_cast<void*>(Begin()), static_cast<void*>(obj_end));
+ DCHECK_LT(static_cast<void*>(obj_begin), static_cast<void*>(obj_end));
+ size_t idx = static_cast<size_t>(obj_begin - Begin()) / kPageSize;
+ size_t last_byte_idx = static_cast<size_t>(obj_end - 1 - Begin()) / kPageSize;
+ // If the addr is at the beginning of a page, then we set it for that page too.
+ if (IsAligned<kPageSize>(obj_begin)) {
+ first_obj_array_[idx] = obj_begin;
+ }
+ while (idx < last_byte_idx) {
+ first_obj_array_[++idx] = obj_begin;
+ }
+}
+
+uint8_t* GcVisitedArenaPool::AddMap(size_t min_size) {
+ size_t size = std::max(min_size, kLinearAllocPoolSize);
+#if defined(__LP64__)
+ // This is true only when we are running a 64-bit dex2oat to compile a 32-bit image.
+ if (low_4gb_) {
+ size = std::max(min_size, kLow4GBLinearAllocPoolSize);
+ }
+#endif
+ size_t alignment = BestPageTableAlignment(size);
+ DCHECK_GE(size, kPMDSize);
+ std::string err_msg;
+ maps_.emplace_back(MemMap::MapAnonymousAligned(
+ name_, size, PROT_READ | PROT_WRITE, low_4gb_, alignment, &err_msg));
+ MemMap& map = maps_.back();
+ if (!map.IsValid()) {
+ LOG(FATAL) << "Failed to allocate " << name_ << ": " << err_msg;
+ UNREACHABLE();
+ }
+
+ if (gUseUserfaultfd) {
+ // Create a shadow-map for the map being added for userfaultfd GC
+ gc::collector::MarkCompact* mark_compact =
+ Runtime::Current()->GetHeap()->MarkCompactCollector();
+ DCHECK_NE(mark_compact, nullptr);
+ mark_compact->AddLinearAllocSpaceData(map.Begin(), map.Size());
+ }
+ Chunk* chunk = new Chunk(map.Begin(), map.Size());
+ best_fit_allocs_.insert(chunk);
+ free_chunks_.insert(chunk);
+ return map.Begin();
+}
+
+GcVisitedArenaPool::GcVisitedArenaPool(bool low_4gb, bool is_zygote, const char* name)
+ : bytes_allocated_(0), name_(name), low_4gb_(low_4gb), pre_zygote_fork_(is_zygote) {}
+
+GcVisitedArenaPool::~GcVisitedArenaPool() {
+ for (Chunk* chunk : free_chunks_) {
+ delete chunk;
+ }
+ // Must not delete chunks from best_fit_allocs_ as they are shared with
+ // free_chunks_.
+}
+
+size_t GcVisitedArenaPool::GetBytesAllocated() const {
+ std::lock_guard<std::mutex> lock(lock_);
+ return bytes_allocated_;
+}
+
+uint8_t* GcVisitedArenaPool::AddPreZygoteForkMap(size_t size) {
+ DCHECK(pre_zygote_fork_);
+ DCHECK(Runtime::Current()->IsZygote());
+ std::string pre_fork_name = "Pre-zygote-";
+ pre_fork_name += name_;
+ std::string err_msg;
+ maps_.emplace_back(MemMap::MapAnonymous(
+ pre_fork_name.c_str(), size, PROT_READ | PROT_WRITE, low_4gb_, &err_msg));
+ MemMap& map = maps_.back();
+ if (!map.IsValid()) {
+ LOG(FATAL) << "Failed to allocate " << pre_fork_name << ": " << err_msg;
+ UNREACHABLE();
+ }
+ return map.Begin();
+}
+
+Arena* GcVisitedArenaPool::AllocArena(size_t size) {
+ // Return only page aligned sizes so that madvise can be leveraged.
+ size = RoundUp(size, kPageSize);
+ std::lock_guard<std::mutex> lock(lock_);
+
+ if (pre_zygote_fork_) {
+ // The first fork out of zygote hasn't happened yet. Allocate arena in a
+ // private-anonymous mapping to retain clean pages across fork.
+ DCHECK(Runtime::Current()->IsZygote());
+ uint8_t* addr = AddPreZygoteForkMap(size);
+ auto emplace_result = allocated_arenas_.emplace(addr, size, /*pre_zygote_fork=*/true);
+ return const_cast<TrackedArena*>(&(*emplace_result.first));
+ }
+
+ Chunk temp_chunk(nullptr, size);
+ auto best_fit_iter = best_fit_allocs_.lower_bound(&temp_chunk);
+ if (UNLIKELY(best_fit_iter == best_fit_allocs_.end())) {
+ AddMap(size);
+ best_fit_iter = best_fit_allocs_.lower_bound(&temp_chunk);
+ CHECK(best_fit_iter != best_fit_allocs_.end());
+ }
+ auto free_chunks_iter = free_chunks_.find(*best_fit_iter);
+ DCHECK(free_chunks_iter != free_chunks_.end());
+ Chunk* chunk = *best_fit_iter;
+ DCHECK_EQ(chunk, *free_chunks_iter);
+ // if the best-fit chunk < 2x the requested size, then give the whole chunk.
+ if (chunk->size_ < 2 * size) {
+ DCHECK_GE(chunk->size_, size);
+ auto emplace_result = allocated_arenas_.emplace(chunk->addr_,
+ chunk->size_,
+ /*pre_zygote_fork=*/false);
+ DCHECK(emplace_result.second);
+ free_chunks_.erase(free_chunks_iter);
+ best_fit_allocs_.erase(best_fit_iter);
+ delete chunk;
+ return const_cast<TrackedArena*>(&(*emplace_result.first));
+ } else {
+ auto emplace_result = allocated_arenas_.emplace(chunk->addr_,
+ size,
+ /*pre_zygote_fork=*/false);
+ DCHECK(emplace_result.second);
+ // Compute next iterators for faster insert later.
+ auto next_best_fit_iter = best_fit_iter;
+ next_best_fit_iter++;
+ auto next_free_chunks_iter = free_chunks_iter;
+ next_free_chunks_iter++;
+ auto best_fit_nh = best_fit_allocs_.extract(best_fit_iter);
+ auto free_chunks_nh = free_chunks_.extract(free_chunks_iter);
+ best_fit_nh.value()->addr_ += size;
+ best_fit_nh.value()->size_ -= size;
+ DCHECK_EQ(free_chunks_nh.value()->addr_, chunk->addr_);
+ best_fit_allocs_.insert(next_best_fit_iter, std::move(best_fit_nh));
+ free_chunks_.insert(next_free_chunks_iter, std::move(free_chunks_nh));
+ return const_cast<TrackedArena*>(&(*emplace_result.first));
+ }
+}
+
+void GcVisitedArenaPool::FreeRangeLocked(uint8_t* range_begin, size_t range_size) {
+ Chunk temp_chunk(range_begin, range_size);
+ bool merge_with_next = false;
+ bool merge_with_prev = false;
+ auto next_iter = free_chunks_.lower_bound(&temp_chunk);
+ auto iter_for_extract = free_chunks_.end();
+ // Can we merge with the previous chunk?
+ if (next_iter != free_chunks_.begin()) {
+ auto prev_iter = next_iter;
+ prev_iter--;
+ merge_with_prev = (*prev_iter)->addr_ + (*prev_iter)->size_ == range_begin;
+ if (merge_with_prev) {
+ range_begin = (*prev_iter)->addr_;
+ range_size += (*prev_iter)->size_;
+ // Hold on to the iterator for faster extract later
+ iter_for_extract = prev_iter;
+ }
+ }
+ // Can we merge with the next chunk?
+ if (next_iter != free_chunks_.end()) {
+ merge_with_next = range_begin + range_size == (*next_iter)->addr_;
+ if (merge_with_next) {
+ range_size += (*next_iter)->size_;
+ if (merge_with_prev) {
+ auto iter = next_iter;
+ next_iter++;
+ // Keep only one of the two chunks to be expanded.
+ Chunk* chunk = *iter;
+ size_t erase_res = best_fit_allocs_.erase(chunk);
+ DCHECK_EQ(erase_res, 1u);
+ free_chunks_.erase(iter);
+ delete chunk;
+ } else {
+ iter_for_extract = next_iter;
+ next_iter++;
+ }
+ }
+ }
+
+ // Extract-insert avoids 2/4 destroys and 2/2 creations
+ // as compared to erase-insert, so use that when merging.
+ if (merge_with_prev || merge_with_next) {
+ auto free_chunks_nh = free_chunks_.extract(iter_for_extract);
+ auto best_fit_allocs_nh = best_fit_allocs_.extract(*iter_for_extract);
+
+ free_chunks_nh.value()->addr_ = range_begin;
+ DCHECK_EQ(best_fit_allocs_nh.value()->addr_, range_begin);
+ free_chunks_nh.value()->size_ = range_size;
+ DCHECK_EQ(best_fit_allocs_nh.value()->size_, range_size);
+
+ free_chunks_.insert(next_iter, std::move(free_chunks_nh));
+ // Since the chunk's size has expanded, the hint won't be useful
+ // for best-fit set.
+ best_fit_allocs_.insert(std::move(best_fit_allocs_nh));
+ } else {
+ DCHECK(iter_for_extract == free_chunks_.end());
+ Chunk* chunk = new Chunk(range_begin, range_size);
+ free_chunks_.insert(next_iter, chunk);
+ best_fit_allocs_.insert(chunk);
+ }
+}
+
+void GcVisitedArenaPool::FreeArenaChain(Arena* first) {
+ if (kRunningOnMemoryTool) {
+ for (Arena* arena = first; arena != nullptr; arena = arena->Next()) {
+ MEMORY_TOOL_MAKE_UNDEFINED(arena->Begin(), arena->GetBytesAllocated());
+ }
+ }
+
+ // TODO: Handle the case when arena_allocator::kArenaAllocatorPreciseTracking
+ // is true. See MemMapArenaPool::FreeArenaChain() for example.
+ CHECK(!arena_allocator::kArenaAllocatorPreciseTracking);
+
+ // madvise the arenas before acquiring lock for scalability
+ for (Arena* temp = first; temp != nullptr; temp = temp->Next()) {
+ temp->Release();
+ }
+
+ std::lock_guard<std::mutex> lock(lock_);
+ while (first != nullptr) {
+ FreeRangeLocked(first->Begin(), first->Size());
+ // In other implementations of ArenaPool this is calculated when asked for,
+ // thanks to the list of free arenas that is kept around. But in this case,
+ // we release the freed arena back to the pool and therefore need to
+ // calculate here.
+ bytes_allocated_ += first->GetBytesAllocated();
+ TrackedArena* temp = down_cast<TrackedArena*>(first);
+ // TODO: Add logic to unmap the maps corresponding to pre-zygote-fork
+ // arenas, which are expected to be released only during shutdown.
+ first = first->Next();
+ size_t erase_count = allocated_arenas_.erase(*temp);
+ DCHECK_EQ(erase_count, 1u);
+ }
+}
+
+} // namespace art
diff --git a/runtime/base/gc_visited_arena_pool.h b/runtime/base/gc_visited_arena_pool.h
new file mode 100644
index 0000000..4f176ef
--- /dev/null
+++ b/runtime/base/gc_visited_arena_pool.h
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_BASE_GC_VISITED_ARENA_POOL_H_
+#define ART_RUNTIME_BASE_GC_VISITED_ARENA_POOL_H_
+
+#include "base/casts.h"
+#include "base/arena_allocator.h"
+#include "base/locks.h"
+#include "base/mem_map.h"
+
+#include <set>
+
+namespace art {
+
+// GcVisitedArenaPool can be used for tracking allocations so that they can
+// be visited during GC to update the GC-roots inside them.
+
+// An Arena which tracks its allocations.
+class TrackedArena final : public Arena {
+ public:
+ // Used for searching in maps. Only arena's starting address is relevant.
+ explicit TrackedArena(uint8_t* addr) : pre_zygote_fork_(false) { memory_ = addr; }
+ TrackedArena(uint8_t* start, size_t size, bool pre_zygote_fork);
+
+ template <typename PageVisitor>
+ void VisitRoots(PageVisitor& visitor) const REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK_ALIGNED(Size(), kPageSize);
+ DCHECK_ALIGNED(Begin(), kPageSize);
+ int nr_pages = Size() / kPageSize;
+ uint8_t* page_begin = Begin();
+ for (int i = 0; i < nr_pages && first_obj_array_[i] != nullptr; i++, page_begin += kPageSize) {
+ visitor(page_begin, first_obj_array_[i]);
+ }
+ }
+
+ // Return the page addr of the first page with first_obj set to nullptr.
+ uint8_t* GetLastUsedByte() const REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK_ALIGNED(Begin(), kPageSize);
+ DCHECK_ALIGNED(End(), kPageSize);
+ // Jump past bytes-allocated for arenas which are not currently being used
+ // by arena-allocator. This helps in reducing loop iterations below.
+ uint8_t* last_byte = AlignUp(Begin() + GetBytesAllocated(), kPageSize);
+ DCHECK_LE(last_byte, End());
+ for (size_t i = (last_byte - Begin()) / kPageSize;
+ last_byte < End() && first_obj_array_[i] != nullptr;
+ last_byte += kPageSize, i++) {
+ // No body.
+ }
+ return last_byte;
+ }
+
+ uint8_t* GetFirstObject(uint8_t* addr) const REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK_LE(Begin(), addr);
+ DCHECK_GT(End(), addr);
+ return first_obj_array_[(addr - Begin()) / kPageSize];
+ }
+
+ // Set 'obj_begin' in first_obj_array_ in every element for which it's the
+ // first object.
+ void SetFirstObject(uint8_t* obj_begin, uint8_t* obj_end);
+
+ void Release() override;
+ bool IsPreZygoteForkArena() const { return pre_zygote_fork_; }
+
+ private:
+ // first_obj_array_[i] is the object that overlaps with the ith page's
+ // beginning, i.e. first_obj_array_[i] <= ith page_begin.
+ std::unique_ptr<uint8_t*[]> first_obj_array_;
+ const bool pre_zygote_fork_;
+};
+
+// An arena-pool wherein allocations can be tracked so that the GC can visit all
+// the GC roots. All the arenas are allocated in one sufficiently large memory
+// range to avoid multiple calls to mremapped/mprotected syscalls.
+class GcVisitedArenaPool final : public ArenaPool {
+ public:
+#if defined(__LP64__)
+ // Use a size in multiples of 1GB as that can utilize the optimized mremap
+ // page-table move.
+ static constexpr size_t kLinearAllocPoolSize = 1 * GB;
+ static constexpr size_t kLow4GBLinearAllocPoolSize = 32 * MB;
+#else
+ static constexpr size_t kLinearAllocPoolSize = 32 * MB;
+#endif
+
+ explicit GcVisitedArenaPool(bool low_4gb = false,
+ bool is_zygote = false,
+ const char* name = "LinearAlloc");
+ virtual ~GcVisitedArenaPool();
+ Arena* AllocArena(size_t size) override;
+ void FreeArenaChain(Arena* first) override;
+ size_t GetBytesAllocated() const override;
+ void ReclaimMemory() override {}
+ void LockReclaimMemory() override {}
+ void TrimMaps() override {}
+
+ bool Contains(void* ptr) {
+ std::lock_guard<std::mutex> lock(lock_);
+ for (auto& map : maps_) {
+ if (map.HasAddress(ptr)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ template <typename PageVisitor>
+ void VisitRoots(PageVisitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_) {
+ std::lock_guard<std::mutex> lock(lock_);
+ for (auto& arena : allocated_arenas_) {
+ arena.VisitRoots(visitor);
+ }
+ }
+
+ template <typename Callback>
+ void ForEachAllocatedArena(Callback cb) REQUIRES_SHARED(Locks::mutator_lock_) {
+ std::lock_guard<std::mutex> lock(lock_);
+ for (auto& arena : allocated_arenas_) {
+ cb(arena);
+ }
+ }
+
+ // Called in Heap::PreZygoteFork(). All allocations after this are done in
+ // arena-pool which is visited by userfaultfd.
+ void SetupPostZygoteMode() {
+ std::lock_guard<std::mutex> lock(lock_);
+ DCHECK(pre_zygote_fork_);
+ pre_zygote_fork_ = false;
+ }
+
+ private:
+ void FreeRangeLocked(uint8_t* range_begin, size_t range_size) REQUIRES(lock_);
+ // Add a map (to be visited by userfaultfd) to the pool of at least min_size
+ // and return its address.
+ uint8_t* AddMap(size_t min_size) REQUIRES(lock_);
+ // Add a private anonymous map prior to zygote fork to the pool and return its
+ // address.
+ uint8_t* AddPreZygoteForkMap(size_t size) REQUIRES(lock_);
+
+ class Chunk {
+ public:
+ Chunk(uint8_t* addr, size_t size) : addr_(addr), size_(size) {}
+ uint8_t* addr_;
+ size_t size_;
+ };
+
+ class LessByChunkAddr {
+ public:
+ bool operator()(const Chunk* a, const Chunk* b) const {
+ return std::less<uint8_t*>{}(a->addr_, b->addr_);
+ }
+ };
+
+ class LessByChunkSize {
+ public:
+ // Since two chunks could have the same size, use addr when that happens.
+ bool operator()(const Chunk* a, const Chunk* b) const {
+ return a->size_ < b->size_ ||
+ (a->size_ == b->size_ && std::less<uint8_t*>{}(a->addr_, b->addr_));
+ }
+ };
+
+ class LessByArenaAddr {
+ public:
+ bool operator()(const TrackedArena& a, const TrackedArena& b) const {
+ return std::less<uint8_t*>{}(a.Begin(), b.Begin());
+ }
+ };
+
+ // Use a std::mutex here as Arenas are second-from-the-bottom when using MemMaps, and MemMap
+ // itself uses std::mutex scoped to within an allocate/free only.
+ mutable std::mutex lock_;
+ std::vector<MemMap> maps_ GUARDED_BY(lock_);
+ std::set<Chunk*, LessByChunkSize> best_fit_allocs_ GUARDED_BY(lock_);
+ std::set<Chunk*, LessByChunkAddr> free_chunks_ GUARDED_BY(lock_);
+ // Set of allocated arenas. It's required to be able to find the arena
+ // corresponding to a given address.
+ // TODO: consider using HashSet, which is more memory efficient.
+ std::set<TrackedArena, LessByArenaAddr> allocated_arenas_ GUARDED_BY(lock_);
+ // Number of bytes allocated so far.
+ size_t bytes_allocated_ GUARDED_BY(lock_);
+ const char* name_;
+ const bool low_4gb_;
+ // Set to true in zygote process so that all linear-alloc allocations are in
+ // private-anonymous mappings and not on userfaultfd visited pages. At
+ // first zygote fork, it's set to false, after which all allocations are done
+ // in userfaultfd visited space.
+ bool pre_zygote_fork_ GUARDED_BY(lock_);
+
+ DISALLOW_COPY_AND_ASSIGN(GcVisitedArenaPool);
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_BASE_GC_VISITED_ARENA_POOL_H_
diff --git a/runtime/base/locks.h b/runtime/base/locks.h
index 829adff..c15e5de 100644
--- a/runtime/base/locks.h
+++ b/runtime/base/locks.h
@@ -68,12 +68,12 @@
// Can be held while GC related work is done, and thus must be above kMarkSweepMarkStackLock
kThreadWaitLock,
kCHALock,
- kJitCodeCacheLock,
kRosAllocGlobalLock,
kRosAllocBracketLock,
kRosAllocBulkFreeLock,
kAllocSpaceLock,
kTaggingLockLevel,
+ kJitCodeCacheLock,
kTransactionLogLock,
kCustomTlsLock,
kJniFunctionTableLock,
diff --git a/runtime/base/message_queue_test.cc b/runtime/base/message_queue_test.cc
index 7a788a9..09dbc32 100644
--- a/runtime/base/message_queue_test.cc
+++ b/runtime/base/message_queue_test.cc
@@ -20,10 +20,16 @@
#include "common_runtime_test.h"
#include "thread-current-inl.h"
+#include "runtime.h"
namespace art {
-class MessageQueueTest : public CommonRuntimeTest {};
+class MessageQueueTest : public CommonRuntimeTest {
+ protected:
+ MessageQueueTest() {
+ this->use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+};
namespace {
@@ -81,6 +87,8 @@
}
TEST_F(MessageQueueTest, TwoWayMessaging) {
+ CHECK(Runtime::Current() != nullptr); // Runtime is needed by Mutex.
+
TestMessageQueue queue1;
TestMessageQueue queue2;
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index 5709333..728dc84 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -28,6 +28,7 @@
#include "base/systrace.h"
#include "base/time_utils.h"
#include "base/value_object.h"
+#include "monitor.h"
#include "mutex-inl.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-inl.h"
@@ -59,18 +60,19 @@
};
#if ART_USE_FUTEXES
+// Compute a relative timespec as *result_ts = lhs - rhs.
+// Return false (and produce an invalid *result_ts) if lhs < rhs.
static bool ComputeRelativeTimeSpec(timespec* result_ts, const timespec& lhs, const timespec& rhs) {
const int32_t one_sec = 1000 * 1000 * 1000; // one second in nanoseconds.
+ static_assert(std::is_signed<decltype(result_ts->tv_sec)>::value); // Signed on Linux.
result_ts->tv_sec = lhs.tv_sec - rhs.tv_sec;
result_ts->tv_nsec = lhs.tv_nsec - rhs.tv_nsec;
if (result_ts->tv_nsec < 0) {
result_ts->tv_sec--;
result_ts->tv_nsec += one_sec;
- } else if (result_ts->tv_nsec > one_sec) {
- result_ts->tv_sec++;
- result_ts->tv_nsec -= one_sec;
}
- return result_ts->tv_sec < 0;
+ DCHECK(result_ts->tv_nsec >= 0 && result_ts->tv_nsec < one_sec);
+ return result_ts->tv_sec >= 0;
}
#endif
@@ -462,7 +464,10 @@
do {
timespec timeout_ts;
timeout_ts.tv_sec = 0;
- timeout_ts.tv_nsec = Runtime::Current()->GetMonitorTimeoutNs();
+ // NB: Some tests use the mutex without the runtime.
+ timeout_ts.tv_nsec = Runtime::Current() != nullptr
+ ? Runtime::Current()->GetMonitorTimeoutNs()
+ : Monitor::kDefaultMonitorTimeoutMs;
if (futex(state_and_contenders_.Address(), FUTEX_WAIT_PRIVATE, cur_state,
enable_monitor_timeout_ ? &timeout_ts : nullptr , nullptr, 0) != 0) {
// We only went to sleep after incrementing and contenders and checking that the
@@ -512,6 +517,7 @@
Locks::thread_list_lock_->ExclusiveLock(self);
std::string owner_stack_dump;
pid_t owner_tid = GetExclusiveOwnerTid();
+ CHECK(Runtime::Current() != nullptr);
Thread *owner = Runtime::Current()->GetThreadList()->FindThreadByTid(owner_tid);
if (owner != nullptr) {
if (IsDumpFrequent(owner, try_times)) {
@@ -852,7 +858,7 @@
timespec now_abs_ts;
InitTimeSpec(true, CLOCK_MONOTONIC, 0, 0, &now_abs_ts);
timespec rel_ts;
- if (ComputeRelativeTimeSpec(&rel_ts, end_abs_ts, now_abs_ts)) {
+ if (!ComputeRelativeTimeSpec(&rel_ts, end_abs_ts, now_abs_ts)) {
return false; // Timed out.
}
ScopedContentionRecorder scr(this, SafeGetTid(self), GetExclusiveOwnerTid());
@@ -869,6 +875,7 @@
// EAGAIN and EINTR both indicate a spurious failure,
// recompute the relative time out from now and try again.
// We don't use TEMP_FAILURE_RETRY so we can recompute rel_ts;
+ num_contenders_.fetch_sub(1); // Unlikely to matter.
PLOG(FATAL) << "timed futex wait failed for " << name_;
}
}
diff --git a/runtime/base/mutex_test.cc b/runtime/base/mutex_test.cc
index 7eba50b..f1b4e49 100644
--- a/runtime/base/mutex_test.cc
+++ b/runtime/base/mutex_test.cc
@@ -21,7 +21,12 @@
namespace art {
-class MutexTest : public CommonRuntimeTest {};
+class MutexTest : public CommonRuntimeTest {
+ protected:
+ MutexTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+};
struct MutexTester {
static void AssertDepth(Mutex& mu, uint32_t expected_depth) {
@@ -37,6 +42,9 @@
};
TEST_F(MutexTest, LockUnlock) {
+ // TODO: Remove `Mutex` dependency on `Runtime` or at least make sure it works
+ // without a `Runtime` with reasonable defaults (and without dumping stack for timeout).
+ ASSERT_TRUE(Runtime::Current() != nullptr);
Mutex mu("test mutex");
MutexTester::AssertDepth(mu, 0U);
mu.Lock(Thread::Current());
diff --git a/runtime/base/timing_logger.cc b/runtime/base/timing_logger.cc
index abf4f58..c39b44e 100644
--- a/runtime/base/timing_logger.cc
+++ b/runtime/base/timing_logger.cc
@@ -33,8 +33,6 @@
namespace art {
-constexpr size_t TimingLogger::kIndexNotFound;
-
CumulativeLogger::CumulativeLogger(const std::string& name)
: name_(name),
lock_name_("CumulativeLoggerLock" + name),
diff --git a/runtime/base/timing_logger_test.cc b/runtime/base/timing_logger_test.cc
index 6f8d8cd..38ae9a5 100644
--- a/runtime/base/timing_logger_test.cc
+++ b/runtime/base/timing_logger_test.cc
@@ -16,11 +16,11 @@
#include "timing_logger.h"
-#include "common_runtime_test.h"
+#include "base/common_art_test.h"
namespace art {
-class TimingLoggerTest : public CommonRuntimeTest {};
+class TimingLoggerTest : public CommonArtTest {};
// TODO: Negative test cases (improper pairing of EndSplit, etc.)
diff --git a/runtime/cha.cc b/runtime/cha.cc
index d19d4f6..a40afb4 100644
--- a/runtime/cha.cc
+++ b/runtime/cha.cc
@@ -144,14 +144,14 @@
ArtMethod* super_method = super_it->
GetVTableEntry<kDefaultVerifyFlags, kWithoutReadBarrier>(vtbl_index, pointer_size);
if (super_method->IsAbstract() &&
- super_method->HasSingleImplementation<kWithoutReadBarrier>() &&
+ super_method->HasSingleImplementation() &&
super_method->GetSingleImplementation(pointer_size) == method) {
// Do like there was no single implementation defined previously
// for this method of the superclass.
super_method->SetSingleImplementation(nullptr, pointer_size);
} else {
// No related SingleImplementations could possibly be found any further.
- DCHECK(!super_method->HasSingleImplementation<kWithoutReadBarrier>());
+ DCHECK(!super_method->HasSingleImplementation());
break;
}
}
@@ -168,7 +168,7 @@
j < count;
++j) {
ArtMethod* method = interface->GetVirtualMethod(j, pointer_size);
- if (method->HasSingleImplementation<kWithoutReadBarrier>() &&
+ if (method->HasSingleImplementation() &&
alloc->ContainsUnsafe(method->GetSingleImplementation(pointer_size)) &&
!method->IsDefault()) {
// Do like there was no single implementation defined previously for this method.
@@ -692,8 +692,9 @@
}
}
-void ClassHierarchyAnalysis::RemoveDependenciesForLinearAlloc(const LinearAlloc* linear_alloc) {
- MutexLock mu(Thread::Current(), *Locks::cha_lock_);
+void ClassHierarchyAnalysis::RemoveDependenciesForLinearAlloc(Thread* self,
+ const LinearAlloc* linear_alloc) {
+ MutexLock mu(self, *Locks::cha_lock_);
for (auto it = cha_dependency_map_.begin(); it != cha_dependency_map_.end(); ) {
// Use unsafe to avoid locking since the allocator is going to be deleted.
if (linear_alloc->ContainsUnsafe(it->first)) {
diff --git a/runtime/cha.h b/runtime/cha.h
index 14af43e..5a403ec 100644
--- a/runtime/cha.h
+++ b/runtime/cha.h
@@ -126,7 +126,7 @@
// Remove all of the dependencies for a linear allocator. This is called when dex cache unloading
// occurs.
- void RemoveDependenciesForLinearAlloc(const LinearAlloc* linear_alloc)
+ void RemoveDependenciesForLinearAlloc(Thread* self, const LinearAlloc* linear_alloc)
REQUIRES(!Locks::cha_lock_);
private:
diff --git a/runtime/cha_test.cc b/runtime/cha_test.cc
index c60720f..48fd06d 100644
--- a/runtime/cha_test.cc
+++ b/runtime/cha_test.cc
@@ -16,11 +16,12 @@
#include "cha.h"
-#include "common_runtime_test.h"
+#include "base/common_art_test.h"
+#include "thread-current-inl.h"
namespace art {
-class CHATest : public CommonRuntimeTest {};
+class CHATest : public CommonArtTest {};
// Mocks some methods.
#define METHOD1 (reinterpret_cast<ArtMethod*>(8u))
diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h
index a7c3e45..d03139b 100644
--- a/runtime/check_reference_map_visitor.h
+++ b/runtime/check_reference_map_visitor.h
@@ -91,7 +91,7 @@
CodeItemDataAccessor accessor(m->DexInstructionData());
uint16_t number_of_dex_registers = accessor.RegistersSize();
- if (!Runtime::Current()->IsAsyncDeoptimizeable(GetCurrentQuickFramePc())) {
+ if (!Runtime::Current()->IsAsyncDeoptimizeable(GetOuterMethod(), GetCurrentQuickFramePc())) {
// We can only guarantee dex register info presence for debuggable methods.
return;
}
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 02b2778..8f3692d 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -24,6 +24,7 @@
#include "art_method-inl.h"
#include "base/mutex.h"
#include "class_linker.h"
+#include "class_table-inl.h"
#include "dex/dex_file.h"
#include "dex/dex_file_structs.h"
#include "gc_root-inl.h"
@@ -475,6 +476,11 @@
DCHECK(Thread::Current()->IsExceptionPending());
return nullptr;
}
+ // Look for the method again in case the type resolution updated the cache.
+ resolved = dex_cache->GetResolvedMethod(method_idx);
+ if (kResolveMode == ResolveMode::kNoChecks && resolved != nullptr) {
+ return resolved;
+ }
}
// Check if the invoke type matches the class type.
@@ -583,6 +589,12 @@
return nullptr;
}
+ // Look for the field again in case the type resolution updated the cache.
+ resolved = dex_cache->GetResolvedField(field_idx);
+ if (resolved != nullptr) {
+ return resolved;
+ }
+
resolved = FindResolvedField(klass, dex_cache.Get(), class_loader.Get(), field_idx, is_static);
if (resolved == nullptr) {
const char* name = dex_file.GetFieldName(field_id);
@@ -592,6 +604,11 @@
return resolved;
}
+template <typename Visitor>
+inline void ClassLinker::VisitBootClasses(Visitor* visitor) {
+ boot_class_table_->Visit(*visitor);
+}
+
template <class Visitor>
inline void ClassLinker::VisitClassTables(const Visitor& visitor) {
Thread* const self = Thread::Current();
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index f6f98e6..f5a4ce6 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -37,12 +37,14 @@
#include "art_method-inl.h"
#include "barrier.h"
#include "base/arena_allocator.h"
+#include "base/arena_bit_vector.h"
#include "base/casts.h"
#include "base/file_utils.h"
#include "base/hash_map.h"
#include "base/hash_set.h"
#include "base/leb128.h"
#include "base/logging.h"
+#include "base/mem_map_arena_pool.h"
#include "base/metrics/metrics.h"
#include "base/mutex-inl.h"
#include "base/os.h"
@@ -96,7 +98,7 @@
#include "jit/jit_code_cache.h"
#include "jni/java_vm_ext.h"
#include "jni/jni_internal.h"
-#include "linear_alloc.h"
+#include "linear_alloc-inl.h"
#include "mirror/array-alloc-inl.h"
#include "mirror/array-inl.h"
#include "mirror/call_site.h"
@@ -270,7 +272,6 @@
}
void Run(Thread* self) override {
- self->ClearMakeVisiblyInitializedCounter();
AdjustThreadVisibilityCounter(self, -1);
}
@@ -298,7 +299,13 @@
}
}
- static constexpr size_t kMaxClasses = 16;
+ // Making classes initialized in bigger batches helps with app startup for
+ // apps that initialize a lot of classes by running fewer checkpoints.
+ // (On the other hand, bigger batches make class initialization checks more
+ // likely to take a slow path but that is mitigated by making partially
+ // filled buffers visibly initialized if we take the slow path many times.
+ // See `Thread::kMakeVisiblyInitializedCounterTriggerCount`.)
+ static constexpr size_t kMaxClasses = 48;
ClassLinker* const class_linker_;
size_t num_classes_;
@@ -321,6 +328,7 @@
}
std::optional<Barrier> maybe_barrier; // Avoid constructing the Barrier for `wait == false`.
if (wait) {
+ Locks::mutator_lock_->AssertNotHeld(self);
maybe_barrier.emplace(0);
}
int wait_count = 0;
@@ -534,10 +542,9 @@
static void WrapExceptionInInitializer(Handle<mirror::Class> klass)
REQUIRES_SHARED(Locks::mutator_lock_) {
Thread* self = Thread::Current();
- JNIEnv* env = self->GetJniEnv();
- ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred());
- CHECK(cause.get() != nullptr);
+ ObjPtr<mirror::Throwable> cause = self->GetException();
+ CHECK(cause != nullptr);
// Boot classpath classes should not fail initialization. This is a consistency debug check.
// This cannot in general be guaranteed, but in all likelihood leads to breakage down the line.
@@ -552,12 +559,8 @@
<< self->GetException()->Dump();
}
- env->ExceptionClear();
- bool is_error = env->IsInstanceOf(cause.get(), WellKnownClasses::java_lang_Error);
- env->Throw(cause.get());
-
// We only wrap non-Error exceptions; an Error can just be used as-is.
- if (!is_error) {
+ if (!cause->IsError()) {
self->ThrowNewWrappedException("Ljava/lang/ExceptionInInitializerError;", nullptr);
}
VlogClassInitializationFailure(klass);
@@ -1084,22 +1087,121 @@
VLOG(startup) << "ClassLinker::FinishInit exiting";
}
-void ClassLinker::RunRootClinits(Thread* self) {
- for (size_t i = 0; i < static_cast<size_t>(ClassRoot::kMax); ++i) {
- ObjPtr<mirror::Class> c = GetClassRoot(ClassRoot(i), this);
- if (!c->IsArrayClass() && !c->IsPrimitive()) {
- StackHandleScope<1> hs(self);
- Handle<mirror::Class> h_class(hs.NewHandle(c));
- if (!EnsureInitialized(self, h_class, true, true)) {
- LOG(FATAL) << "Exception when initializing " << h_class->PrettyClass()
- << ": " << self->GetException()->Dump();
- }
- } else {
- DCHECK(c->IsInitialized());
+static void EnsureRootInitialized(ClassLinker* class_linker,
+ Thread* self,
+ ObjPtr<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!klass->IsVisiblyInitialized()) {
+ DCHECK(!klass->IsArrayClass());
+ DCHECK(!klass->IsPrimitive());
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Class> h_class(hs.NewHandle(klass));
+ if (!class_linker->EnsureInitialized(
+ self, h_class, /*can_init_fields=*/ true, /*can_init_parents=*/ true)) {
+ LOG(FATAL) << "Exception when initializing " << h_class->PrettyClass()
+ << ": " << self->GetException()->Dump();
}
}
}
+void ClassLinker::RunEarlyRootClinits(Thread* self) {
+ StackHandleScope<1u> hs(self);
+ Handle<mirror::ObjectArray<mirror::Class>> class_roots = hs.NewHandle(GetClassRoots());
+ EnsureRootInitialized(this, self, GetClassRoot<mirror::Class>(class_roots.Get()));
+ EnsureRootInitialized(this, self, GetClassRoot<mirror::String>(class_roots.Get()));
+ // `Field` class is needed for register_java_net_InetAddress in libcore, b/28153851.
+ EnsureRootInitialized(this, self, GetClassRoot<mirror::Field>(class_roots.Get()));
+
+ WellKnownClasses::Init(self->GetJniEnv());
+
+ // `FinalizerReference` class is needed for initialization of `java.net.InetAddress`.
+ // (Indirectly by constructing a `ObjectStreamField` which uses a `StringBuilder`
+ // and, when resizing, initializes the `System` class for `System.arraycopy()`
+ // and `System.<clinit> creates a finalizable object.)
+ EnsureRootInitialized(
+ this, self, WellKnownClasses::java_lang_ref_FinalizerReference_add->GetDeclaringClass());
+}
+
+void ClassLinker::RunRootClinits(Thread* self) {
+ StackHandleScope<1u> hs(self);
+ Handle<mirror::ObjectArray<mirror::Class>> class_roots = hs.NewHandle(GetClassRoots());
+ for (size_t i = 0; i < static_cast<size_t>(ClassRoot::kMax); ++i) {
+ EnsureRootInitialized(this, self, GetClassRoot(ClassRoot(i), class_roots.Get()));
+ }
+
+ // Make sure certain well-known classes are initialized. Note that well-known
+ // classes are always in the boot image, so this code is primarily intended
+ // for running without boot image but may be needed for boot image if the
+ // AOT-initialization fails due to introduction of new code to `<clinit>`.
+ ArtMethod* static_methods_of_classes_to_initialize[] = {
+ // Initialize primitive boxing classes (avoid check at runtime).
+ WellKnownClasses::java_lang_Boolean_valueOf,
+ WellKnownClasses::java_lang_Byte_valueOf,
+ WellKnownClasses::java_lang_Character_valueOf,
+ WellKnownClasses::java_lang_Double_valueOf,
+ WellKnownClasses::java_lang_Float_valueOf,
+ WellKnownClasses::java_lang_Integer_valueOf,
+ WellKnownClasses::java_lang_Long_valueOf,
+ WellKnownClasses::java_lang_Short_valueOf,
+ // Initialize `StackOverflowError`.
+ WellKnownClasses::java_lang_StackOverflowError_init,
+ // Ensure class loader classes are initialized (avoid check at runtime).
+ // Superclass `ClassLoader` is a class root and already initialized above.
+ // Superclass `BaseDexClassLoader` is initialized implicitly.
+ WellKnownClasses::dalvik_system_DelegateLastClassLoader_init,
+ WellKnownClasses::dalvik_system_DexClassLoader_init,
+ WellKnownClasses::dalvik_system_InMemoryDexClassLoader_init,
+ WellKnownClasses::dalvik_system_PathClassLoader_init,
+ WellKnownClasses::java_lang_BootClassLoader_init,
+ // Ensure `Daemons` class is initialized (avoid check at runtime).
+ WellKnownClasses::java_lang_Daemons_start,
+ // Ensure `Thread` and `ThreadGroup` classes are initialized (avoid check at runtime).
+ WellKnownClasses::java_lang_Thread_init,
+ WellKnownClasses::java_lang_ThreadGroup_add,
+ // Ensure reference classes are initialized (avoid check at runtime).
+ // The `FinalizerReference` class was initialized in `RunEarlyRootClinits()`.
+ WellKnownClasses::java_lang_ref_ReferenceQueue_add,
+ // Ensure `InvocationTargetException` class is initialized (avoid check at runtime).
+ WellKnownClasses::java_lang_reflect_InvocationTargetException_init,
+ // Ensure `Parameter` class is initialized (avoid check at runtime).
+ WellKnownClasses::java_lang_reflect_Parameter_init,
+ // Ensure `MethodHandles` class is initialized (avoid check at runtime).
+ WellKnownClasses::java_lang_invoke_MethodHandles_lookup,
+ // Ensure `DirectByteBuffer` class is initialized (avoid check at runtime).
+ WellKnownClasses::java_nio_DirectByteBuffer_init,
+ // Ensure `FloatingDecimal` class is initialized (avoid check at runtime).
+ WellKnownClasses::jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_D,
+ // Ensure reflection annotation classes are initialized (avoid check at runtime).
+ WellKnownClasses::libcore_reflect_AnnotationFactory_createAnnotation,
+ WellKnownClasses::libcore_reflect_AnnotationMember_init,
+ // We're suppressing exceptions from `DdmServer` and we do not want to repeatedly
+ // suppress class initialization error (say, due to OOM), so initialize it early.
+ WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch,
+ };
+ for (ArtMethod* method : static_methods_of_classes_to_initialize) {
+ EnsureRootInitialized(this, self, method->GetDeclaringClass());
+ }
+ ArtField* static_fields_of_classes_to_initialize[] = {
+ // Ensure classes used by class loaders are initialized (avoid check at runtime).
+ WellKnownClasses::dalvik_system_DexFile_cookie,
+ WellKnownClasses::dalvik_system_DexPathList_dexElements,
+ WellKnownClasses::dalvik_system_DexPathList__Element_dexFile,
+ // Ensure `VMRuntime` is initialized (avoid check at runtime).
+ WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer,
+ // Initialize empty arrays needed by `StackOverflowError`.
+ WellKnownClasses::java_util_Collections_EMPTY_LIST,
+ WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT,
+ };
+ for (ArtField* field : static_fields_of_classes_to_initialize) {
+ EnsureRootInitialized(this, self, field->GetDeclaringClass());
+ }
+
+ // This invariant is important since otherwise we will have the entire proxy invoke system
+ // confused.
+ DCHECK_NE(WellKnownClasses::java_lang_reflect_Proxy_init->GetEntryPointFromQuickCompiledCode(),
+ GetQuickInstrumentationEntryPoint());
+}
+
ALWAYS_INLINE
static uint32_t ComputeMethodHash(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(!method->IsRuntimeMethod());
@@ -1338,11 +1440,9 @@
}
}
-bool ClassLinker::IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
- ObjPtr<mirror::ClassLoader> class_loader) {
+bool ClassLinker::IsBootClassLoader(ObjPtr<mirror::Object> class_loader) {
return class_loader == nullptr ||
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader) ==
- class_loader->GetClass();
+ WellKnownClasses::java_lang_BootClassLoader == class_loader->GetClass();
}
class CHAOnDeleteUpdateClassVisitor {
@@ -1732,112 +1832,113 @@
// Helper class for ArtMethod checks when adding an image. Keeps all required functionality
// together and caches some intermediate results.
+template <PointerSize kPointerSize>
class ImageChecker final {
public:
- static void CheckObjects(gc::Heap* heap, ClassLinker* class_linker)
+ static void CheckObjects(gc::Heap* heap, gc::space::ImageSpace* space)
REQUIRES_SHARED(Locks::mutator_lock_) {
- ImageChecker ic(heap, class_linker);
+ // There can be no GC during boot image initialization, so we do not need read barriers.
+ ScopedDebugDisallowReadBarriers sddrb(Thread::Current());
+
+ CHECK_EQ(kPointerSize, space->GetImageHeader().GetPointerSize());
+ const ImageSection& objects_section = space->GetImageHeader().GetObjectsSection();
+ uintptr_t space_begin = reinterpret_cast<uintptr_t>(space->Begin());
+ uintptr_t objects_begin = space_begin + objects_section.Offset();
+ uintptr_t objects_end = objects_begin + objects_section.Size();
+ ImageChecker ic(heap);
auto visitor = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(obj != nullptr);
- CHECK(obj->GetClass() != nullptr) << "Null class in object " << obj;
- CHECK(obj->GetClass()->GetClass() != nullptr) << "Null class class " << obj;
- if (obj->IsClass()) {
+ mirror::Class* obj_klass = obj->GetClass<kDefaultVerifyFlags, kWithoutReadBarrier>();
+ CHECK(obj_klass != nullptr) << "Null class in object " << obj;
+ mirror::Class* class_class = obj_klass->GetClass<kDefaultVerifyFlags, kWithoutReadBarrier>();
+ CHECK(class_class != nullptr) << "Null class class " << obj;
+ if (obj_klass == class_class) {
auto klass = obj->AsClass();
for (ArtField& field : klass->GetIFields()) {
- CHECK_EQ(field.GetDeclaringClass(), klass);
+ CHECK_EQ(field.GetDeclaringClass<kWithoutReadBarrier>(), klass);
}
for (ArtField& field : klass->GetSFields()) {
- CHECK_EQ(field.GetDeclaringClass(), klass);
+ CHECK_EQ(field.GetDeclaringClass<kWithoutReadBarrier>(), klass);
}
- const PointerSize pointer_size = ic.pointer_size_;
- for (ArtMethod& m : klass->GetMethods(pointer_size)) {
+ for (ArtMethod& m : klass->GetMethods(kPointerSize)) {
ic.CheckArtMethod(&m, klass);
}
- ObjPtr<mirror::PointerArray> vtable = klass->GetVTable();
+ ObjPtr<mirror::PointerArray> vtable =
+ klass->GetVTable<kDefaultVerifyFlags, kWithoutReadBarrier>();
if (vtable != nullptr) {
- ic.CheckArtMethodPointerArray(vtable, nullptr);
+ ic.CheckArtMethodPointerArray(vtable);
}
if (klass->ShouldHaveImt()) {
- ImTable* imt = klass->GetImt(pointer_size);
+ ImTable* imt = klass->GetImt(kPointerSize);
for (size_t i = 0; i < ImTable::kSize; ++i) {
- ic.CheckArtMethod(imt->Get(i, pointer_size), nullptr);
+ ic.CheckArtMethod(imt->Get(i, kPointerSize), /*expected_class=*/ nullptr);
}
}
if (klass->ShouldHaveEmbeddedVTable()) {
for (int32_t i = 0; i < klass->GetEmbeddedVTableLength(); ++i) {
- ic.CheckArtMethod(klass->GetEmbeddedVTableEntry(i, pointer_size), nullptr);
+ ic.CheckArtMethod(klass->GetEmbeddedVTableEntry(i, kPointerSize),
+ /*expected_class=*/ nullptr);
}
}
- ObjPtr<mirror::IfTable> iftable = klass->GetIfTable();
- for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) {
- if (iftable->GetMethodArrayCount(i) > 0) {
- ic.CheckArtMethodPointerArray(iftable->GetMethodArray(i), nullptr);
+ ObjPtr<mirror::IfTable> iftable =
+ klass->GetIfTable<kDefaultVerifyFlags, kWithoutReadBarrier>();
+ int32_t iftable_count = (iftable != nullptr) ? iftable->Count() : 0;
+ for (int32_t i = 0; i < iftable_count; ++i) {
+ ObjPtr<mirror::PointerArray> method_array =
+ iftable->GetMethodArrayOrNull<kDefaultVerifyFlags, kWithoutReadBarrier>(i);
+ if (method_array != nullptr) {
+ ic.CheckArtMethodPointerArray(method_array);
}
}
}
};
- heap->VisitObjects(visitor);
+ space->GetLiveBitmap()->VisitMarkedRange(objects_begin, objects_end, visitor);
}
private:
- ImageChecker(gc::Heap* heap, ClassLinker* class_linker)
- : spaces_(heap->GetBootImageSpaces()),
- pointer_size_(class_linker->GetImagePointerSize()) {
- space_begin_.reserve(spaces_.size());
- method_sections_.reserve(spaces_.size());
- runtime_method_sections_.reserve(spaces_.size());
- for (gc::space::ImageSpace* space : spaces_) {
+ explicit ImageChecker(gc::Heap* heap) {
+ ArrayRef<gc::space::ImageSpace* const> spaces(heap->GetBootImageSpaces());
+ space_begin_.reserve(spaces.size());
+ for (gc::space::ImageSpace* space : spaces) {
+ CHECK_EQ(static_cast<const void*>(space->Begin()), &space->GetImageHeader());
space_begin_.push_back(space->Begin());
- auto& header = space->GetImageHeader();
- method_sections_.push_back(&header.GetMethodsSection());
- runtime_method_sections_.push_back(&header.GetRuntimeMethodsSection());
}
}
void CheckArtMethod(ArtMethod* m, ObjPtr<mirror::Class> expected_class)
REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Class> declaring_class = m->GetDeclaringClassUnchecked<kWithoutReadBarrier>();
if (m->IsRuntimeMethod()) {
- ObjPtr<mirror::Class> declaring_class = m->GetDeclaringClassUnchecked();
CHECK(declaring_class == nullptr) << declaring_class << " " << m->PrettyMethod();
} else if (m->IsCopied()) {
- CHECK(m->GetDeclaringClass() != nullptr) << m->PrettyMethod();
+ CHECK(declaring_class != nullptr) << m->PrettyMethod();
} else if (expected_class != nullptr) {
- CHECK_EQ(m->GetDeclaringClassUnchecked(), expected_class) << m->PrettyMethod();
+ CHECK_EQ(declaring_class, expected_class) << m->PrettyMethod();
}
- if (!spaces_.empty()) {
- bool contains = false;
- for (size_t i = 0; !contains && i != space_begin_.size(); ++i) {
- const size_t offset = reinterpret_cast<uint8_t*>(m) - space_begin_[i];
- contains = method_sections_[i]->Contains(offset) ||
- runtime_method_sections_[i]->Contains(offset);
+ bool contains = false;
+ for (const uint8_t* begin : space_begin_) {
+ const size_t offset = reinterpret_cast<uint8_t*>(m) - begin;
+ const ImageHeader* header = reinterpret_cast<const ImageHeader*>(begin);
+ if (header->GetMethodsSection().Contains(offset) ||
+ header->GetRuntimeMethodsSection().Contains(offset)) {
+ contains = true;
+ break;
}
- CHECK(contains) << m << " not found";
}
+ CHECK(contains) << m << " not found";
}
- void CheckArtMethodPointerArray(ObjPtr<mirror::PointerArray> arr,
- ObjPtr<mirror::Class> expected_class)
+ void CheckArtMethodPointerArray(ObjPtr<mirror::PointerArray> arr)
REQUIRES_SHARED(Locks::mutator_lock_) {
CHECK(arr != nullptr);
for (int32_t j = 0; j < arr->GetLength(); ++j) {
- auto* method = arr->GetElementPtrSize<ArtMethod*>(j, pointer_size_);
- // expected_class == null means we are a dex cache.
- if (expected_class != nullptr) {
- CHECK(method != nullptr);
- }
- if (method != nullptr) {
- CheckArtMethod(method, expected_class);
- }
+ auto* method = arr->GetElementPtrSize<ArtMethod*>(j, kPointerSize);
+ CHECK(method != nullptr);
+ CheckArtMethod(method, /*expected_class=*/ nullptr);
}
}
- const std::vector<gc::space::ImageSpace*>& spaces_;
- const PointerSize pointer_size_;
-
- // Cached sections from the spaces.
std::vector<const uint8_t*> space_begin_;
- std::vector<const ImageSection*> method_sections_;
- std::vector<const ImageSection*> runtime_method_sections_;
};
static void VerifyAppImage(const ImageHeader& header,
@@ -1906,9 +2007,8 @@
hs.NewHandle(dex_caches_object->AsObjectArray<mirror::DexCache>()));
Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle(
header.GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray<mirror::Class>()));
- MutableHandle<mirror::ClassLoader> image_class_loader(hs.NewHandle(
- app_image ? header.GetImageRoot(ImageHeader::kAppImageClassLoader)->AsClassLoader()
- : nullptr));
+ MutableHandle<mirror::Object> special_root(hs.NewHandle(
+ app_image ? header.GetImageRoot(ImageHeader::kSpecialRoots) : nullptr));
DCHECK(class_roots != nullptr);
if (class_roots->GetLength() != static_cast<int32_t>(ClassRoot::kMax)) {
*error_msg = StringPrintf("Expected %d class roots but got %d",
@@ -1954,17 +2054,41 @@
}
if (app_image) {
- ScopedObjectAccessUnchecked soa(Thread::Current());
- ScopedAssertNoThreadSuspension sants("Checking app image", soa.Self());
- if (IsBootClassLoader(soa, image_class_loader.Get())) {
+ ScopedAssertNoThreadSuspension sants("Checking app image");
+ if (special_root == nullptr) {
+ *error_msg = "Unexpected null special root in app image";
+ return false;
+ } else if (special_root->IsIntArray()) {
+ size_t count = special_root->AsIntArray()->GetLength();
+ if (oat_file->GetVdexFile()->GetNumberOfDexFiles() != count) {
+ *error_msg = "Cheksums count does not match";
+ return false;
+ }
+ static_assert(sizeof(VdexFile::VdexChecksum) == sizeof(int32_t));
+ const VdexFile::VdexChecksum* art_checksums =
+ reinterpret_cast<VdexFile::VdexChecksum*>(special_root->AsIntArray()->GetData());
+ const VdexFile::VdexChecksum* vdex_checksums =
+ oat_file->GetVdexFile()->GetDexChecksumsArray();
+ if (memcmp(art_checksums, vdex_checksums, sizeof(VdexFile::VdexChecksum) * count) != 0) {
+ *error_msg = "Image and vdex cheksums did not match";
+ return false;
+ }
+ } else if (IsBootClassLoader(special_root.Get())) {
*error_msg = "Unexpected BootClassLoader in app image";
return false;
+ } else if (!special_root->IsClassLoader()) {
+ *error_msg = "Unexpected special root in app image";
+ return false;
}
}
if (kCheckImageObjects) {
if (!app_image) {
- ImageChecker::CheckObjects(heap, this);
+ if (image_pointer_size_ == PointerSize::k64) {
+ ImageChecker<PointerSize::k64>::CheckObjects(heap, space);
+ } else {
+ ImageChecker<PointerSize::k32>::CheckObjects(heap, space);
+ }
}
}
@@ -2012,8 +2136,7 @@
// Set image methods' entry point that point to the nterp trampoline to the
// nterp entry point. This allows taking the fast path when doing a
// nterp->nterp call.
- DCHECK_IMPLIES(NeedsClinitCheckBeforeCall(&method),
- method.GetDeclaringClass()->IsVisiblyInitialized());
+ DCHECK(!method.StillNeedsClinitCheck());
method.SetEntryPointFromQuickCompiledCode(interpreter::GetNterpEntryPoint());
} else {
method.SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge());
@@ -2024,7 +2147,7 @@
if (runtime->IsVerificationSoftFail()) {
header.VisitPackedArtMethods([&](ArtMethod& method) REQUIRES_SHARED(Locks::mutator_lock_) {
- if (!method.IsNative() && method.IsInvokable()) {
+ if (method.IsManagedAndInvokable()) {
method.ClearSkipAccessChecks();
}
}, space->Begin(), image_pointer_size_);
@@ -2115,7 +2238,7 @@
const bool tracing_enabled = Trace::IsTracingEnabled();
Thread* const self = Thread::Current();
WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
- if (kUseReadBarrier) {
+ if (gUseReadBarrier) {
// We do not track new roots for CC.
DCHECK_EQ(0, flags & (kVisitRootFlagNewRoots |
kVisitRootFlagClearRootLog |
@@ -2146,12 +2269,21 @@
boot_class_table_->VisitRoots(root_visitor);
// If tracing is enabled, then mark all the class loaders to prevent unloading.
if ((flags & kVisitRootFlagClassLoader) != 0 || tracing_enabled) {
- for (const ClassLoaderData& data : class_loaders_) {
- GcRoot<mirror::Object> root(GcRoot<mirror::Object>(self->DecodeJObject(data.weak_root)));
- root.VisitRoot(visitor, RootInfo(kRootVMInternal));
+ gc::Heap* const heap = Runtime::Current()->GetHeap();
+ // Don't visit class-loaders if compacting with userfaultfd GC as these
+ // weaks are updated using Runtime::SweepSystemWeaks() and the GC doesn't
+ // tolerate double updates.
+ if (!gUseUserfaultfd
+ || !heap->MarkCompactCollector()->IsCompacting(self)) {
+ for (const ClassLoaderData& data : class_loaders_) {
+ GcRoot<mirror::Object> root(GcRoot<mirror::Object>(self->DecodeJObject(data.weak_root)));
+ root.VisitRoot(visitor, RootInfo(kRootVMInternal));
+ }
+ } else {
+ DCHECK_EQ(heap->CurrentCollectorType(), gc::CollectorType::kCollectorTypeCMC);
}
}
- } else if (!kUseReadBarrier && (flags & kVisitRootFlagNewRoots) != 0) {
+ } else if (!gUseReadBarrier && (flags & kVisitRootFlagNewRoots) != 0) {
for (auto& root : new_class_roots_) {
ObjPtr<mirror::Class> old_ref = root.Read<kWithoutReadBarrier>();
root.VisitRoot(visitor, RootInfo(kRootStickyClass));
@@ -2172,13 +2304,13 @@
}
}
}
- if (!kUseReadBarrier && (flags & kVisitRootFlagClearRootLog) != 0) {
+ if (!gUseReadBarrier && (flags & kVisitRootFlagClearRootLog) != 0) {
new_class_roots_.clear();
new_bss_roots_boot_oat_files_.clear();
}
- if (!kUseReadBarrier && (flags & kVisitRootFlagStartLoggingNewRoots) != 0) {
+ if (!gUseReadBarrier && (flags & kVisitRootFlagStartLoggingNewRoots) != 0) {
log_new_roots_ = true;
- } else if (!kUseReadBarrier && (flags & kVisitRootFlagStopLoggingNewRoots) != 0) {
+ } else if (!gUseReadBarrier && (flags & kVisitRootFlagStopLoggingNewRoots) != 0) {
log_new_roots_ = false;
}
// We deliberately ignore the class roots in the image since we
@@ -2366,7 +2498,7 @@
}
} else if (cha_ != nullptr) {
// If we don't have a JIT, we need to manually remove the CHA dependencies manually.
- cha_->RemoveDependenciesForLinearAlloc(data.allocator);
+ cha_->RemoveDependenciesForLinearAlloc(self, data.allocator);
}
// Cleanup references to single implementation ArtMethods that will be deleted.
if (cleanup_cha) {
@@ -2652,19 +2784,16 @@
} \
} while (0)
-bool ClassLinker::FindClassInSharedLibraries(ScopedObjectAccessAlreadyRunnable& soa,
- Thread* self,
+bool ClassLinker::FindClassInSharedLibraries(Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
/*out*/ ObjPtr<mirror::Class>* result) {
- ArtField* field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
- return FindClassInSharedLibrariesHelper(soa, self, descriptor, hash, class_loader, field, result);
+ ArtField* field = WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
+ return FindClassInSharedLibrariesHelper(self, descriptor, hash, class_loader, field, result);
}
-bool ClassLinker::FindClassInSharedLibrariesHelper(ScopedObjectAccessAlreadyRunnable& soa,
- Thread* self,
+bool ClassLinker::FindClassInSharedLibrariesHelper(Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
@@ -2682,38 +2811,35 @@
for (auto loader : shared_libraries.Iterate<mirror::ClassLoader>()) {
temp_loader.Assign(loader);
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
- FindClassInBaseDexClassLoader(soa, self, descriptor, hash, temp_loader, result),
+ FindClassInBaseDexClassLoader(self, descriptor, hash, temp_loader, result),
*result,
self);
}
return true;
}
-bool ClassLinker::FindClassInSharedLibrariesAfter(ScopedObjectAccessAlreadyRunnable& soa,
- Thread* self,
+bool ClassLinker::FindClassInSharedLibrariesAfter(Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
/*out*/ ObjPtr<mirror::Class>* result) {
- ArtField* field = jni::DecodeArtField(
- WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter);
- return FindClassInSharedLibrariesHelper(soa, self, descriptor, hash, class_loader, field, result);
+ ArtField* field = WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter;
+ return FindClassInSharedLibrariesHelper(self, descriptor, hash, class_loader, field, result);
}
-bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
- Thread* self,
+bool ClassLinker::FindClassInBaseDexClassLoader(Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
/*out*/ ObjPtr<mirror::Class>* result) {
// Termination case: boot class loader.
- if (IsBootClassLoader(soa, class_loader.Get())) {
+ if (IsBootClassLoader(class_loader.Get())) {
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
FindClassInBootClassLoaderClassPath(self, descriptor, hash, result), *result, self);
return true;
}
- if (IsPathOrDexClassLoader(soa, class_loader) || IsInMemoryDexClassLoader(soa, class_loader)) {
+ if (IsPathOrDexClassLoader(class_loader) || IsInMemoryDexClassLoader(class_loader)) {
// For regular path or dex class loader the search order is:
// - parent
// - shared libraries
@@ -2723,19 +2849,19 @@
StackHandleScope<1> hs(self);
Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent()));
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
- FindClassInBaseDexClassLoader(soa, self, descriptor, hash, h_parent, result),
+ FindClassInBaseDexClassLoader(self, descriptor, hash, h_parent, result),
*result,
self);
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
- FindClassInSharedLibraries(soa, self, descriptor, hash, class_loader, result),
+ FindClassInSharedLibraries(self, descriptor, hash, class_loader, result),
*result,
self);
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
- FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader, result),
+ FindClassInBaseDexClassLoaderClassPath(self, descriptor, hash, class_loader, result),
*result,
self);
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
- FindClassInSharedLibrariesAfter(soa, self, descriptor, hash, class_loader, result),
+ FindClassInSharedLibrariesAfter(self, descriptor, hash, class_loader, result),
*result,
self);
// We did not find a class, but the class loader chain was recognized, so we
@@ -2743,7 +2869,7 @@
return true;
}
- if (IsDelegateLastClassLoader(soa, class_loader)) {
+ if (IsDelegateLastClassLoader(class_loader)) {
// For delegate last, the search order is:
// - boot class path
// - shared libraries
@@ -2752,15 +2878,15 @@
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
FindClassInBootClassLoaderClassPath(self, descriptor, hash, result), *result, self);
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
- FindClassInSharedLibraries(soa, self, descriptor, hash, class_loader, result),
+ FindClassInSharedLibraries(self, descriptor, hash, class_loader, result),
*result,
self);
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
- FindClassInBaseDexClassLoaderClassPath(soa, descriptor, hash, class_loader, result),
+ FindClassInBaseDexClassLoaderClassPath(self, descriptor, hash, class_loader, result),
*result,
self);
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
- FindClassInSharedLibrariesAfter(soa, self, descriptor, hash, class_loader, result),
+ FindClassInSharedLibrariesAfter(self, descriptor, hash, class_loader, result),
*result,
self);
@@ -2768,7 +2894,7 @@
StackHandleScope<1> hs(self);
Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent()));
RETURN_IF_UNRECOGNIZED_OR_FOUND_OR_EXCEPTION(
- FindClassInBaseDexClassLoader(soa, self, descriptor, hash, h_parent, result),
+ FindClassInBaseDexClassLoader(self, descriptor, hash, h_parent, result),
*result,
self);
// We did not find a class, but the class loader chain was recognized, so we
@@ -2837,14 +2963,14 @@
}
bool ClassLinker::FindClassInBaseDexClassLoaderClassPath(
- ScopedObjectAccessAlreadyRunnable& soa,
+ Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
/*out*/ ObjPtr<mirror::Class>* result) {
- DCHECK(IsPathOrDexClassLoader(soa, class_loader) ||
- IsInMemoryDexClassLoader(soa, class_loader) ||
- IsDelegateLastClassLoader(soa, class_loader))
+ DCHECK(IsPathOrDexClassLoader(class_loader) ||
+ IsInMemoryDexClassLoader(class_loader) ||
+ IsDelegateLastClassLoader(class_loader))
<< "Unexpected class loader for descriptor " << descriptor;
const DexFile* dex_file = nullptr;
@@ -2859,15 +2985,15 @@
}
return true; // Continue with the next DexFile.
};
- VisitClassLoaderDexFiles(soa, class_loader, find_class_def);
+ VisitClassLoaderDexFiles(self, class_loader, find_class_def);
if (class_def != nullptr) {
- *result = DefineClass(soa.Self(), descriptor, hash, class_loader, *dex_file, *class_def);
+ *result = DefineClass(self, descriptor, hash, class_loader, *dex_file, *class_def);
if (UNLIKELY(*result == nullptr)) {
- CHECK(soa.Self()->IsExceptionPending()) << descriptor;
- FilterDexFileCaughtExceptions(soa.Self(), this);
+ CHECK(self->IsExceptionPending()) << descriptor;
+ FilterDexFileCaughtExceptions(self, this);
} else {
- DCHECK(!soa.Self()->IsExceptionPending());
+ DCHECK(!self->IsExceptionPending());
}
}
// A BaseDexClassLoader is always a known lookup.
@@ -2923,7 +3049,7 @@
} else {
ScopedObjectAccessUnchecked soa(self);
bool known_hierarchy =
- FindClassInBaseDexClassLoader(soa, self, descriptor, hash, class_loader, &result_ptr);
+ FindClassInBaseDexClassLoader(self, descriptor, hash, class_loader, &result_ptr);
if (result_ptr != nullptr) {
// The chain was understood and we found the class. We still need to add the class to
// the class table to protect from racy programs that can try and redefine the path list
@@ -2978,29 +3104,23 @@
"%s",
class_name_string.c_str());
} else {
- ScopedLocalRef<jobject> class_loader_object(
- soa.Env(), soa.AddLocalReference<jobject>(class_loader.Get()));
- ScopedLocalRef<jobject> result(soa.Env(), nullptr);
- {
- ScopedThreadStateChange tsc(self, ThreadState::kNative);
- ScopedLocalRef<jobject> class_name_object(
- soa.Env(), soa.Env()->NewStringUTF(class_name_string.c_str()));
- if (class_name_object.get() == nullptr) {
- DCHECK(self->IsExceptionPending()); // OOME.
- return nullptr;
- }
- CHECK(class_loader_object.get() != nullptr);
- result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(),
- WellKnownClasses::java_lang_ClassLoader_loadClass,
- class_name_object.get()));
+ StackHandleScope<1u> hs(self);
+ Handle<mirror::String> class_name_object = hs.NewHandle(
+ mirror::String::AllocFromModifiedUtf8(self, class_name_string.c_str()));
+ if (class_name_object == nullptr) {
+ DCHECK(self->IsExceptionPending()); // OOME.
+ return nullptr;
}
- if (result.get() == nullptr && !self->IsExceptionPending()) {
+ DCHECK(class_loader != nullptr);
+ result_ptr = ObjPtr<mirror::Class>::DownCast(
+ WellKnownClasses::java_lang_ClassLoader_loadClass->InvokeVirtual<'L', 'L'>(
+ self, class_loader.Get(), class_name_object.Get()));
+ if (result_ptr == nullptr && !self->IsExceptionPending()) {
// broken loader - throw NPE to be compatible with Dalvik
ThrowNullPointerException(StringPrintf("ClassLoader.loadClass returned null for %s",
class_name_string.c_str()).c_str());
return nullptr;
}
- result_ptr = soa.Decode<mirror::Class>(result.get());
// Check the name of the returned class.
descriptor_equals = (result_ptr != nullptr) && result_ptr->DescriptorEquals(descriptor);
}
@@ -3114,6 +3234,7 @@
ScopedDefiningClass sdc(self);
StackHandleScope<3> hs(self);
metrics::AutoTimer timer{GetMetrics()->ClassLoadingTotalTime()};
+ metrics::AutoTimer timeDelta{GetMetrics()->ClassLoadingTotalTimeDelta()};
auto klass = hs.NewHandle<mirror::Class>(nullptr);
// Load the class from the dex file.
@@ -3389,14 +3510,11 @@
}
instrumentation::Instrumentation* instrumentation = runtime->GetInstrumentation();
- // Link the code of methods skipped by LinkCode.
for (size_t method_index = 0; method_index < num_direct_methods; ++method_index) {
ArtMethod* method = klass->GetDirectMethod(method_index, pointer_size);
- if (!method->IsStatic()) {
- // Only update static methods.
- continue;
+ if (method->NeedsClinitCheckBeforeCall()) {
+ instrumentation->UpdateMethodsCode(method, instrumentation->GetCodeForInvoke(method));
}
- instrumentation->UpdateMethodsCode(method, instrumentation->GetCodeForInvoke(method));
}
// Ignore virtual methods on the iterator.
}
@@ -3424,7 +3542,7 @@
}
// Method shouldn't have already been linked.
- DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr);
+ DCHECK_EQ(method->GetEntryPointFromQuickCompiledCode(), nullptr);
DCHECK(!method->GetDeclaringClass()->IsVisiblyInitialized()); // Actually ClassStatus::Idx.
if (!method->IsInvokable()) {
@@ -3480,7 +3598,7 @@
// If the ArtField alignment changes, review all uses of LengthPrefixedArray<ArtField>.
static_assert(alignof(ArtField) == 4, "ArtField alignment is expected to be 4.");
size_t storage_size = LengthPrefixedArray<ArtField>::ComputeSize(length);
- void* array_storage = allocator->Alloc(self, storage_size);
+ void* array_storage = allocator->Alloc(self, storage_size, LinearAllocKind::kArtFieldArray);
auto* ret = new(array_storage) LengthPrefixedArray<ArtField>(length);
CHECK(ret != nullptr);
std::uninitialized_fill_n(&ret->At(0), length, ArtField());
@@ -3497,7 +3615,7 @@
const size_t method_size = ArtMethod::Size(image_pointer_size_);
const size_t storage_size =
LengthPrefixedArray<ArtMethod>::ComputeSize(length, method_size, method_alignment);
- void* array_storage = allocator->Alloc(self, storage_size);
+ void* array_storage = allocator->Alloc(self, storage_size, LinearAllocKind::kArtMethodArray);
auto* ret = new (array_storage) LengthPrefixedArray<ArtMethod>(length);
CHECK(ret != nullptr);
for (size_t i = 0; i < length; ++i) {
@@ -3810,7 +3928,8 @@
std::string dex_file_location = dex_file.GetLocation();
// The following paths checks don't work on preopt when using boot dex files, where the dex
// cache location is the one on device, and the dex_file's location is the one on host.
- if (!(Runtime::Current()->IsAotCompiler() && class_loader == nullptr && !kIsTargetBuild)) {
+ Runtime* runtime = Runtime::Current();
+ if (!(runtime->IsAotCompiler() && class_loader == nullptr && !kIsTargetBuild)) {
CHECK_GE(dex_file_location.length(), dex_cache_length)
<< dex_cache_location << " " << dex_file.GetLocation();
const std::string dex_file_suffix = dex_file_location.substr(
@@ -3820,34 +3939,37 @@
// dex file location is /system/priv-app/SettingsProvider/SettingsProvider.apk
CHECK_EQ(dex_cache_location, dex_file_suffix);
}
+
+ // Check if we need to initialize OatFile data (.data.bimg.rel.ro and .bss
+ // sections) needed for code execution and register the oat code range.
const OatFile* oat_file =
(dex_file.GetOatDexFile() != nullptr) ? dex_file.GetOatDexFile()->GetOatFile() : nullptr;
- // Clean up pass to remove null dex caches; null dex caches can occur due to class unloading
- // and we are lazily removing null entries. Also check if we need to initialize OatFile data
- // (.data.bimg.rel.ro and .bss sections) needed for code execution.
bool initialize_oat_file_data = (oat_file != nullptr) && oat_file->IsExecutable();
- JavaVMExt* const vm = self->GetJniEnv()->GetVm();
- for (auto it = dex_caches_.begin(); it != dex_caches_.end(); ) {
- const DexCacheData& data = it->second;
- if (self->IsJWeakCleared(data.weak_root)) {
- vm->DeleteWeakGlobalRef(self, data.weak_root);
- it = dex_caches_.erase(it);
- } else {
- if (initialize_oat_file_data &&
- it->first->GetOatDexFile() != nullptr &&
- it->first->GetOatDexFile()->GetOatFile() == oat_file) {
+ if (initialize_oat_file_data) {
+ for (const auto& entry : dex_caches_) {
+ if (!self->IsJWeakCleared(entry.second.weak_root) &&
+ entry.first->GetOatDexFile() != nullptr &&
+ entry.first->GetOatDexFile()->GetOatFile() == oat_file) {
initialize_oat_file_data = false; // Already initialized.
+ break;
}
- ++it;
}
}
if (initialize_oat_file_data) {
oat_file->InitializeRelocations();
+ // Notify the fault handler about the new executable code range if needed.
+ size_t exec_offset = oat_file->GetOatHeader().GetExecutableOffset();
+ DCHECK_LE(exec_offset, oat_file->Size());
+ size_t exec_size = oat_file->Size() - exec_offset;
+ if (exec_size != 0u) {
+ runtime->AddGeneratedCodeRange(oat_file->Begin() + exec_offset, exec_size);
+ }
}
+
// Let hiddenapi assign a domain to the newly registered dex file.
hiddenapi::InitializeDexFileDomain(dex_file, class_loader);
- jweak dex_cache_jweak = vm->AddWeakGlobalRef(self, dex_cache);
+ jweak dex_cache_jweak = self->GetJniEnv()->GetVm()->AddWeakGlobalRef(self, dex_cache);
DexCacheData data;
data.weak_root = dex_cache_jweak;
data.class_table = ClassTableForClassLoader(class_loader);
@@ -4144,10 +4266,11 @@
class_loader)));
if (component_type == nullptr) {
DCHECK(self->IsExceptionPending());
- // We need to accept erroneous classes as component types.
+ // We need to accept erroneous classes as component types. Under AOT, we
+ // don't accept them as we cannot encode the erroneous class in an image.
const size_t component_hash = ComputeModifiedUtf8Hash(descriptor + 1);
component_type.Assign(LookupClass(self, descriptor + 1, component_hash, class_loader.Get()));
- if (component_type == nullptr) {
+ if (component_type == nullptr || Runtime::Current()->IsAotCompiler()) {
DCHECK(self->IsExceptionPending());
return nullptr;
} else {
@@ -4766,6 +4889,12 @@
return; // nothing to process
}
const uint8_t* handlers_ptr = accessor.GetCatchHandlerData(0);
+ CHECK(method->GetDexFile()->IsInDataSection(handlers_ptr))
+ << method->PrettyMethod()
+ << "@" << method->GetDexFile()->GetLocation()
+ << "@" << reinterpret_cast<const void*>(handlers_ptr)
+ << " is_compact_dex=" << method->GetDexFile()->IsCompactDexFile();
+
uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
for (uint32_t idx = 0; idx < handlers_size; idx++) {
CatchHandlerIterator iterator(handlers_ptr);
@@ -5022,8 +5151,7 @@
// Find the <init>(InvocationHandler)V method. The exact method offset varies depending
// on which front-end compiler was used to build the libcore DEX files.
- ArtMethod* proxy_constructor =
- jni::DecodeArtMethod(WellKnownClasses::java_lang_reflect_Proxy_init);
+ ArtMethod* proxy_constructor = WellKnownClasses::java_lang_reflect_Proxy_init;
DCHECK(proxy_constructor != nullptr)
<< "Could not find <init> method in java.lang.reflect.Proxy";
@@ -5088,11 +5216,19 @@
CHECK_EQ(prototype, method->GetInterfaceMethodIfProxy(image_pointer_size_));
}
-bool ClassLinker::CanWeInitializeClass(ObjPtr<mirror::Class> klass, bool can_init_statics,
+bool ClassLinker::CanWeInitializeClass(ObjPtr<mirror::Class> klass,
+ bool can_init_statics,
bool can_init_parents) {
if (can_init_statics && can_init_parents) {
return true;
}
+ DCHECK(Runtime::Current()->IsAotCompiler());
+
+ // We currently don't support initializing at AOT time classes that need access
+ // checks.
+ if (klass->IsVerifiedNeedsAccessChecks()) {
+ return false;
+ }
if (!can_init_statics) {
// Check if there's a class initializer.
ArtMethod* clinit = klass->FindClassInitializer(image_pointer_size_);
@@ -5902,7 +6038,9 @@
if (imt == nullptr) {
LinearAlloc* allocator = GetAllocatorForClassLoader(klass->GetClassLoader());
imt = reinterpret_cast<ImTable*>(
- allocator->Alloc(self, ImTable::SizeInBytes(image_pointer_size_)));
+ allocator->Alloc(self,
+ ImTable::SizeInBytes(image_pointer_size_),
+ LinearAllocKind::kNoGCRoots));
if (imt == nullptr) {
return false;
}
@@ -6185,8 +6323,9 @@
// Allocate a new table. Note that we will leak this table at the next conflict,
// but that's a tradeoff compared to making the table fixed size.
void* data = linear_alloc->Alloc(
- Thread::Current(), ImtConflictTable::ComputeSizeWithOneMoreEntry(current_table,
- image_pointer_size_));
+ Thread::Current(),
+ ImtConflictTable::ComputeSizeWithOneMoreEntry(current_table, image_pointer_size_),
+ LinearAllocKind::kNoGCRoots);
if (data == nullptr) {
LOG(ERROR) << "Failed to allocate conflict table";
return conflict_method;
@@ -6300,8 +6439,8 @@
LinearAlloc* linear_alloc,
PointerSize image_pointer_size) {
void* data = linear_alloc->Alloc(Thread::Current(),
- ImtConflictTable::ComputeSize(count,
- image_pointer_size));
+ ImtConflictTable::ComputeSize(count, image_pointer_size),
+ LinearAllocKind::kNoGCRoots);
return (data != nullptr) ? new (data) ImtConflictTable(count, image_pointer_size) : nullptr;
}
@@ -6917,7 +7056,7 @@
klass_(klass),
self_(self),
runtime_(runtime),
- stack_(runtime->GetLinearAlloc()->GetArenaPool()),
+ stack_(runtime->GetArenaPool()),
allocator_(&stack_),
copied_method_records_(copied_method_records_initial_buffer_,
kCopiedMethodRecordInitialBufferSize,
@@ -6997,6 +7136,10 @@
kMethodSize,
kMethodAlignment);
memset(old_methods, 0xFEu, old_size);
+ // Set size to 0 to avoid visiting declaring classes.
+ if (gUseUserfaultfd) {
+ old_methods->SetSize(0);
+ }
}
}
}
@@ -7599,16 +7742,25 @@
const size_t old_methods_ptr_size = (old_methods != nullptr) ? old_size : 0;
auto* methods = reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(
class_linker_->GetAllocatorForClassLoader(klass->GetClassLoader())->Realloc(
- self_, old_methods, old_methods_ptr_size, new_size));
+ self_, old_methods, old_methods_ptr_size, new_size, LinearAllocKind::kArtMethodArray));
CHECK(methods != nullptr); // Native allocation failure aborts.
if (methods != old_methods) {
- StrideIterator<ArtMethod> out = methods->begin(kMethodSize, kMethodAlignment);
- // Copy over the old methods. The `ArtMethod::CopyFrom()` is only necessary to not miss
- // read barriers since `LinearAlloc::Realloc()` won't do read barriers when it copies.
- for (auto& m : klass->GetMethods(kPointerSize)) {
- out->CopyFrom(&m, kPointerSize);
- ++out;
+ if (gUseReadBarrier) {
+ StrideIterator<ArtMethod> out = methods->begin(kMethodSize, kMethodAlignment);
+ // Copy over the old methods. The `ArtMethod::CopyFrom()` is only necessary to not miss
+ // read barriers since `LinearAlloc::Realloc()` won't do read barriers when it copies.
+ for (auto& m : klass->GetMethods(kPointerSize)) {
+ out->CopyFrom(&m, kPointerSize);
+ ++out;
+ }
+ } else if (gUseUserfaultfd) {
+ // Clear the declaring class of the old dangling method array so that GC doesn't
+ // try to update them, which could cause crashes in userfaultfd GC due to
+ // checks in post-compact address computation.
+ for (auto& m : klass->GetMethods(kPointerSize)) {
+ m.SetDeclaringClass(nullptr);
+ }
}
}
@@ -7859,20 +8011,6 @@
return true;
}
-NO_INLINE
-static void ThrowIllegalAccessErrorForImplementingMethod(ObjPtr<mirror::Class> klass,
- ArtMethod* vtable_method,
- ArtMethod* interface_method)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- DCHECK(!vtable_method->IsAbstract());
- DCHECK(!vtable_method->IsPublic());
- ThrowIllegalAccessError(
- klass,
- "Method '%s' implementing interface method '%s' is not public",
- vtable_method->PrettyMethod().c_str(),
- interface_method->PrettyMethod().c_str());
-}
-
template <PointerSize kPointerSize>
ObjPtr<mirror::PointerArray> ClassLinker::LinkMethodsHelper<kPointerSize>::AllocPointerArray(
Thread* self, size_t length) {
@@ -7961,55 +8099,17 @@
static constexpr double kMinLoadFactor = 0.3;
static constexpr double kMaxLoadFactor = 0.5;
static constexpr size_t kMaxStackBuferSize = 256;
- const size_t super_vtable_buffer_size = super_vtable_length * 3;
const size_t declared_virtuals_buffer_size = num_virtual_methods * 3;
- const size_t total_buffer_size = super_vtable_buffer_size + declared_virtuals_buffer_size;
- uint32_t* super_vtable_buffer_ptr = (total_buffer_size <= kMaxStackBuferSize)
- ? reinterpret_cast<uint32_t*>(alloca(total_buffer_size * sizeof(uint32_t)))
- : allocator_.AllocArray<uint32_t>(total_buffer_size);
- uint32_t* declared_virtuals_buffer_ptr = super_vtable_buffer_ptr + super_vtable_buffer_size;
- VTableSignatureSet super_vtable_signatures(
- kMinLoadFactor,
- kMaxLoadFactor,
- VTableSignatureHash(super_vtable_accessor),
- VTableSignatureEqual(super_vtable_accessor),
- super_vtable_buffer_ptr,
- super_vtable_buffer_size,
- allocator_.Adapter());
- ArrayRef<uint32_t> same_signature_vtable_lists;
- // Insert the first `mirror::Object::kVTableLength` indexes with pre-calculated hashes.
- DCHECK_GE(super_vtable_length, mirror::Object::kVTableLength);
- for (uint32_t i = 0; i != mirror::Object::kVTableLength; ++i) {
- size_t hash = class_linker_->object_virtual_method_hashes_[i];
- // There are no duplicate signatures in `java.lang.Object`, so use `HashSet<>::PutWithHash()`.
- // This avoids equality comparison for the three `java.lang.Object.wait()` overloads.
- super_vtable_signatures.PutWithHash(i, hash);
- }
- // Insert the remaining indexes, check for duplicate signatures.
- if (super_vtable_length > mirror::Object::kVTableLength) {
- for (size_t i = mirror::Object::kVTableLength; i < super_vtable_length; ++i) {
- // Use `super_vtable_accessor` for getting the method for hash calculation.
- // Letting `HashSet<>::insert()` use the internal accessor copy in the hash
- // function prevents the compiler from optimizing this properly because the
- // compiler cannot prove that the accessor copy is immutable.
- size_t hash = ComputeMethodHash(super_vtable_accessor.GetVTableEntry(i));
- auto [it, inserted] = super_vtable_signatures.InsertWithHash(i, hash);
- if (UNLIKELY(!inserted)) {
- if (same_signature_vtable_lists.empty()) {
- same_signature_vtable_lists = ArrayRef<uint32_t>(
- allocator_.AllocArray<uint32_t>(super_vtable_length), super_vtable_length);
- std::fill_n(same_signature_vtable_lists.data(), super_vtable_length, dex::kDexNoIndex);
- same_signature_vtable_lists_ = same_signature_vtable_lists;
- }
- DCHECK_LT(*it, i);
- same_signature_vtable_lists[i] = *it;
- *it = i;
- }
- }
- }
+ const size_t super_vtable_buffer_size = super_vtable_length * 3;
+ const size_t bit_vector_size = BitVector::BitsToWords(num_virtual_methods);
+ const size_t total_size =
+ declared_virtuals_buffer_size + super_vtable_buffer_size + bit_vector_size;
- // For each declared virtual method, look for a superclass virtual method
- // to override and assign a new vtable index if no method was overridden.
+ uint32_t* declared_virtuals_buffer_ptr = (total_size <= kMaxStackBuferSize)
+ ? reinterpret_cast<uint32_t*>(alloca(total_size * sizeof(uint32_t)))
+ : allocator_.AllocArray<uint32_t>(total_size);
+ uint32_t* bit_vector_buffer_ptr = declared_virtuals_buffer_ptr + declared_virtuals_buffer_size;
+
DeclaredVirtualSignatureSet declared_virtual_signatures(
kMinLoadFactor,
kMaxLoadFactor,
@@ -8018,8 +8118,24 @@
declared_virtuals_buffer_ptr,
declared_virtuals_buffer_size,
allocator_.Adapter());
+
+ ArrayRef<uint32_t> same_signature_vtable_lists;
const bool is_proxy_class = klass->IsProxyClass();
size_t vtable_length = super_vtable_length;
+
+ // Record which declared methods are overriding a super method.
+ BitVector initialized_methods(/* expandable= */ false,
+ Allocator::GetNoopAllocator(),
+ bit_vector_size,
+ bit_vector_buffer_ptr);
+
+ // Note: our sets hash on the method name, and therefore we pay a high
+ // performance price when a class has many overloads.
+ //
+ // We populate a set of declared signatures instead of signatures from the
+ // super vtable (which is only lazy populated in case of interface overriding,
+ // see below). This makes sure that we pay the performance price only on that
+ // class, and not on its subclasses (except in the case of interface overriding, see below).
for (size_t i = 0; i != num_virtual_methods; ++i) {
ArtMethod* virtual_method = klass->GetVirtualMethodDuringLinking(i, kPointerSize);
DCHECK(!virtual_method->IsStatic()) << virtual_method->PrettyMethod();
@@ -8028,59 +8144,79 @@
: virtual_method;
size_t hash = ComputeMethodHash(signature_method);
declared_virtual_signatures.PutWithHash(i, hash);
- auto it = super_vtable_signatures.FindWithHash(signature_method, hash);
- if (it != super_vtable_signatures.end()) {
- size_t super_index = *it;
- DCHECK_LT(super_index, super_vtable_length);
- ArtMethod* super_method = super_vtable_accessor.GetVTableEntry(super_index);
- // Historical note: Before Android 4.1, an inaccessible package-private
- // superclass method would have been incorrectly overridden.
- bool overrides = klass->CanAccessMember(super_method->GetDeclaringClass(),
- super_method->GetAccessFlags());
- if (overrides && super_method->IsFinal()) {
- sants.reset();
- ThrowLinkageError(klass, "Method %s overrides final method in class %s",
- virtual_method->PrettyMethod().c_str(),
- super_method->GetDeclaringClassDescriptor());
- return 0u;
- }
- if (UNLIKELY(!same_signature_vtable_lists.empty())) {
- // We may override more than one method according to JLS, see b/211854716 .
- // We record the highest overridden vtable index here so that we can walk
- // the list to find other overridden methods when constructing the vtable.
- // However, we walk all the methods to check for final method overriding.
- size_t current_index = super_index;
- while (same_signature_vtable_lists[current_index] != dex::kDexNoIndex) {
- DCHECK_LT(same_signature_vtable_lists[current_index], current_index);
- current_index = same_signature_vtable_lists[current_index];
- ArtMethod* current_method = super_vtable_accessor.GetVTableEntry(current_index);
- if (klass->CanAccessMember(current_method->GetDeclaringClass(),
- current_method->GetAccessFlags())) {
- if (current_method->IsFinal()) {
- sants.reset();
- ThrowLinkageError(klass, "Method %s overrides final method in class %s",
- virtual_method->PrettyMethod().c_str(),
- current_method->GetDeclaringClassDescriptor());
- return 0u;
- }
- if (!overrides) {
- overrides = true;
- super_index = current_index;
- super_method = current_method;
- }
- }
- }
- }
- if (overrides) {
- virtual_method->SetMethodIndex(super_index);
- continue;
- }
- }
- // The method does not override any method from superclass, so it needs a new vtable index.
- virtual_method->SetMethodIndex(vtable_length);
- ++vtable_length;
}
+ // Loop through each super vtable method and see if they are overridden by a method we added to
+ // the hash table.
+ for (size_t j = 0; j < super_vtable_length; ++j) {
+ // Search the hash table to see if we are overridden by any method.
+ ArtMethod* super_method = super_vtable_accessor.GetVTableEntry(j);
+ if (!klass->CanAccessMember(super_method->GetDeclaringClass(),
+ super_method->GetAccessFlags())) {
+ // Continue on to the next method since this one is package private and cannot be overridden.
+ // Before Android 4.1, the package-private method super_method might have been incorrectly
+ // overridden.
+ continue;
+ }
+ size_t hash = (j < mirror::Object::kVTableLength)
+ ? class_linker_->object_virtual_method_hashes_[j]
+ : ComputeMethodHash(super_method);
+ auto it = declared_virtual_signatures.FindWithHash(super_method, hash);
+ if (it == declared_virtual_signatures.end()) {
+ continue;
+ }
+ ArtMethod* virtual_method = klass->GetVirtualMethodDuringLinking(*it, kPointerSize);
+ if (super_method->IsFinal()) {
+ sants.reset();
+ ThrowLinkageError(klass, "Method %s overrides final method in class %s",
+ virtual_method->PrettyMethod().c_str(),
+ super_method->GetDeclaringClassDescriptor());
+ return 0u;
+ }
+ if (initialized_methods.IsBitSet(*it)) {
+ // The method is overriding more than one method.
+ // We record that information in a linked list to later set the method in the vtable
+ // locations that are not the method index.
+ if (same_signature_vtable_lists.empty()) {
+ same_signature_vtable_lists = ArrayRef<uint32_t>(
+ allocator_.AllocArray<uint32_t>(super_vtable_length), super_vtable_length);
+ std::fill_n(same_signature_vtable_lists.data(), super_vtable_length, dex::kDexNoIndex);
+ same_signature_vtable_lists_ = same_signature_vtable_lists;
+ }
+ same_signature_vtable_lists[j] = virtual_method->GetMethodIndexDuringLinking();
+ } else {
+ initialized_methods.SetBit(*it);
+ }
+
+ // We arbitrarily set to the largest index. This is also expected when
+ // iterating over the `same_signature_vtable_lists_`.
+ virtual_method->SetMethodIndex(j);
+ }
+
+ // Add the non-overridden methods at the end.
+ for (size_t i = 0; i < num_virtual_methods; ++i) {
+ if (!initialized_methods.IsBitSet(i)) {
+ ArtMethod* local_method = klass->GetVirtualMethodDuringLinking(i, kPointerSize);
+ local_method->SetMethodIndex(vtable_length);
+ vtable_length++;
+ }
+ }
+
+ // A lazily constructed super vtable set, which we only populate in the less
+ // common sittuation of a superclass implementing a method declared in an
+ // interface this class inherits.
+ // We still try to allocate the set on the stack as using the arena will have
+ // a larger cost.
+ uint32_t* super_vtable_buffer_ptr = bit_vector_buffer_ptr + bit_vector_size;
+ VTableSignatureSet super_vtable_signatures(
+ kMinLoadFactor,
+ kMaxLoadFactor,
+ VTableSignatureHash(super_vtable_accessor),
+ VTableSignatureEqual(super_vtable_accessor),
+ super_vtable_buffer_ptr,
+ super_vtable_buffer_size,
+ allocator_.Adapter());
+
// Assign vtable indexes for interface methods in new interfaces and store them
// in implementation method arrays. These shall be replaced by actual method
// pointers later. We do not need to do this for superclass interfaces as we can
@@ -8099,42 +8235,39 @@
ArtMethod* interface_method = iface->GetVirtualMethod(j, kPointerSize);
size_t hash = ComputeMethodHash(interface_method);
ArtMethod* vtable_method = nullptr;
- bool found = false;
auto it1 = declared_virtual_signatures.FindWithHash(interface_method, hash);
if (it1 != declared_virtual_signatures.end()) {
- vtable_method = klass->GetVirtualMethodDuringLinking(*it1, kPointerSize);
- found = true;
+ ArtMethod* found_method = klass->GetVirtualMethodDuringLinking(*it1, kPointerSize);
+ // For interface overriding, we only look at public methods.
+ if (found_method->IsPublic()) {
+ vtable_method = found_method;
+ }
} else {
+ // This situation should be rare (a superclass implements a method
+ // declared in an interface this class is inheriting). Only in this case
+ // do we lazily populate the super_vtable_signatures.
+ if (super_vtable_signatures.empty()) {
+ for (size_t k = 0; k < super_vtable_length; ++k) {
+ ArtMethod* super_method = super_vtable_accessor.GetVTableEntry(k);
+ if (!super_method->IsPublic()) {
+ // For interface overriding, we only look at public methods.
+ continue;
+ }
+ size_t super_hash = (k < mirror::Object::kVTableLength)
+ ? class_linker_->object_virtual_method_hashes_[k]
+ : ComputeMethodHash(super_method);
+ auto [it, inserted] = super_vtable_signatures.InsertWithHash(k, super_hash);
+ DCHECK(inserted || super_vtable_accessor.GetVTableEntry(*it) == super_method);
+ }
+ }
auto it2 = super_vtable_signatures.FindWithHash(interface_method, hash);
if (it2 != super_vtable_signatures.end()) {
- // If there are multiple vtable methods with the same signature, the one with
- // the highest vtable index is not nessarily the one in most-derived class.
- // Find the most-derived method. See b/211854716 .
vtable_method = super_vtable_accessor.GetVTableEntry(*it2);
- if (UNLIKELY(!same_signature_vtable_lists.empty())) {
- size_t current_index = *it2;
- while (same_signature_vtable_lists[current_index] != dex::kDexNoIndex) {
- DCHECK_LT(same_signature_vtable_lists[current_index], current_index);
- current_index = same_signature_vtable_lists[current_index];
- ArtMethod* current_method = super_vtable_accessor.GetVTableEntry(current_index);
- ObjPtr<mirror::Class> current_class = current_method->GetDeclaringClass();
- if (current_class->IsSubClass(vtable_method->GetDeclaringClass())) {
- vtable_method = current_method;
- }
- }
- }
- found = true;
}
}
+
uint32_t vtable_index = vtable_length;
- if (found) {
- DCHECK(vtable_method != nullptr);
- if (!vtable_method->IsAbstract() && !vtable_method->IsPublic()) {
- // FIXME: Delay the exception until we actually try to call the method. b/211854716
- sants.reset();
- ThrowIllegalAccessErrorForImplementingMethod(klass, vtable_method, interface_method);
- return 0u;
- }
+ if (vtable_method != nullptr) {
vtable_index = vtable_method->GetMethodIndexDuringLinking();
if (!vtable_method->IsOverridableByDefaultMethod()) {
method_array->SetElementPtrSize(j, vtable_index, kPointerSize);
@@ -8144,7 +8277,7 @@
auto [it, inserted] = copied_method_records_.InsertWithHash(
CopiedMethodRecord(interface_method, vtable_index), hash);
- if (found) {
+ if (vtable_method != nullptr) {
DCHECK_EQ(vtable_index, it->GetMethodIndex());
} else if (inserted) {
DCHECK_EQ(vtable_index, it->GetMethodIndex());
@@ -8310,26 +8443,29 @@
ThrowClassFormatError(klass.Get(), "Too many methods on interface: %zu", num_virtual_methods);
return false;
}
+ // Assign each method an interface table index and set the default flag.
bool has_defaults = false;
- // Assign each method an IMT index and set the default flag.
for (size_t i = 0; i < num_virtual_methods; ++i) {
ArtMethod* m = klass->GetVirtualMethodDuringLinking(i, kPointerSize);
m->SetMethodIndex(i);
- if (!m->IsAbstract()) {
+ uint32_t access_flags = m->GetAccessFlags();
+ DCHECK(!ArtMethod::IsDefault(access_flags));
+ DCHECK_EQ(!ArtMethod::IsAbstract(access_flags), ArtMethod::IsInvokable(access_flags));
+ if (ArtMethod::IsInvokable(access_flags)) {
// If the dex file does not support default methods, throw ClassFormatError.
// This check is necessary to protect from odd cases, such as native default
// methods, that the dex file verifier permits for old dex file versions. b/157170505
// FIXME: This should be `if (!m->GetDexFile()->SupportsDefaultMethods())` but we're
// currently running CTS tests for default methods with dex file version 035 which
// does not support default methods. So, we limit this to native methods. b/157718952
- if (m->IsNative()) {
+ if (ArtMethod::IsNative(access_flags)) {
DCHECK(!m->GetDexFile()->SupportsDefaultMethods());
ThrowClassFormatError(klass.Get(),
"Dex file does not support default method '%s'",
m->PrettyMethod().c_str());
return false;
}
- if (!m->IsPublic()) {
+ if (!ArtMethod::IsPublic(access_flags)) {
// The verifier should have caught the non-public method for dex version 37.
// Just warn and skip it since this is from before default-methods so we don't
// really need to care that it has code.
@@ -8337,7 +8473,7 @@
<< "This will be a fatal error in subsequent versions of android. "
<< "Continuing anyway.";
}
- m->SetAccessFlags(m->GetAccessFlags() | kAccDefault);
+ m->SetAccessFlags(access_flags | kAccDefault);
has_defaults = true;
}
}
@@ -8484,17 +8620,16 @@
uint32_t vtable_index = virtual_method.GetMethodIndexDuringLinking();
vtable->SetElementPtrSize(vtable_index, &virtual_method, kPointerSize);
if (UNLIKELY(vtable_index < same_signature_vtable_lists.size())) {
- // We may override more than one method according to JLS, see b/211854716 .
- // If we do, arbitrarily update the method index to the lowest overridden vtable index.
+ // We may override more than one method according to JLS, see b/211854716.
while (same_signature_vtable_lists[vtable_index] != dex::kDexNoIndex) {
DCHECK_LT(same_signature_vtable_lists[vtable_index], vtable_index);
vtable_index = same_signature_vtable_lists[vtable_index];
- ArtMethod* current_method = super_class->GetVTableEntry(vtable_index, kPointerSize);
- if (klass->CanAccessMember(current_method->GetDeclaringClass(),
- current_method->GetAccessFlags())) {
+ vtable->SetElementPtrSize(vtable_index, &virtual_method, kPointerSize);
+ if (kIsDebugBuild) {
+ ArtMethod* current_method = super_class->GetVTableEntry(vtable_index, kPointerSize);
+ DCHECK(klass->CanAccessMember(current_method->GetDeclaringClass(),
+ current_method->GetAccessFlags()));
DCHECK(!current_method->IsFinal());
- vtable->SetElementPtrSize(vtable_index, &virtual_method, kPointerSize);
- virtual_method.SetMethodIndex(vtable_index);
}
}
}
@@ -8606,7 +8741,8 @@
};
// We use the following order of field types for assigning offsets.
-// Some fields can be shuffled forward to fill gaps, see `ClassLinker::LinkFields()`.
+// Some fields can be shuffled forward to fill gaps, see
+// `ClassLinker::LinkFieldsHelper::LinkFields()`.
enum class ClassLinker::LinkFieldsHelper::FieldTypeOrder : uint16_t {
kReference = 0u,
kLong,
@@ -9504,6 +9640,9 @@
Handle<mirror::MethodType> type = hs.NewHandle(
mirror::MethodType::Create(self, return_type, method_params));
if (type != nullptr) {
+ // Ensure all stores for the newly created MethodType are visible, before we attempt to place
+ // it in the DexCache (b/224733324).
+ std::atomic_thread_fence(std::memory_order_release);
dex_cache->SetResolvedMethodType(proto_idx, type.Get());
}
@@ -10000,11 +10139,13 @@
Handle<mirror::ClassLoader> parent_loader,
Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries,
Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries_after) {
+ CHECK(loader_class.Get() == WellKnownClasses::dalvik_system_PathClassLoader ||
+ loader_class.Get() == WellKnownClasses::dalvik_system_DelegateLastClassLoader ||
+ loader_class.Get() == WellKnownClasses::dalvik_system_InMemoryDexClassLoader);
StackHandleScope<5> hs(self);
- ArtField* dex_elements_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements);
+ ArtField* dex_elements_field = WellKnownClasses::dalvik_system_DexPathList_dexElements;
Handle<mirror::Class> dex_elements_class(hs.NewHandle(dex_elements_field->ResolveType()));
DCHECK(dex_elements_class != nullptr);
@@ -10016,14 +10157,13 @@
Handle<mirror::Class> h_dex_element_class =
hs.NewHandle(dex_elements_class->GetComponentType());
- ArtField* element_file_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
+ ArtField* element_file_field = WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
DCHECK_EQ(h_dex_element_class.Get(), element_file_field->GetDeclaringClass());
- ArtField* cookie_field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
+ ArtField* cookie_field = WellKnownClasses::dalvik_system_DexFile_cookie;
DCHECK_EQ(cookie_field->GetDeclaringClass(), element_file_field->LookupResolvedType());
- ArtField* file_name_field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_fileName);
+ ArtField* file_name_field = WellKnownClasses::dalvik_system_DexFile_fileName;
DCHECK_EQ(file_name_field->GetDeclaringClass(), element_file_field->LookupResolvedType());
// Fill the elements array.
@@ -10095,88 +10235,45 @@
ObjPtr<mirror::ClassLoader>::DownCast(loader_class->AllocObject(self)));
DCHECK(h_class_loader != nullptr);
// Set DexPathList.
- ArtField* path_list_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList);
+ ArtField* path_list_field = WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList;
DCHECK(path_list_field != nullptr);
path_list_field->SetObject<false>(h_class_loader.Get(), h_dex_path_list.Get());
// Make a pretend boot-classpath.
// TODO: Should we scan the image?
- ArtField* const parent_field =
- jni::DecodeArtField(WellKnownClasses::java_lang_ClassLoader_parent);
+ ArtField* const parent_field = WellKnownClasses::java_lang_ClassLoader_parent;
DCHECK(parent_field != nullptr);
if (parent_loader.Get() == nullptr) {
- ScopedObjectAccessUnchecked soa(self);
- ObjPtr<mirror::Object> boot_loader(soa.Decode<mirror::Class>(
- WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self));
+ ObjPtr<mirror::Object> boot_loader(
+ WellKnownClasses::java_lang_BootClassLoader->AllocObject(self));
parent_field->SetObject<false>(h_class_loader.Get(), boot_loader);
} else {
parent_field->SetObject<false>(h_class_loader.Get(), parent_loader.Get());
}
ArtField* shared_libraries_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
+ WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
DCHECK(shared_libraries_field != nullptr);
shared_libraries_field->SetObject<false>(h_class_loader.Get(), shared_libraries.Get());
ArtField* shared_libraries_after_field =
- jni::DecodeArtField(
- WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter);
+ WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter;
DCHECK(shared_libraries_after_field != nullptr);
shared_libraries_after_field->SetObject<false>(h_class_loader.Get(),
shared_libraries_after.Get());
return h_class_loader.Get();
}
-jobject ClassLinker::CreateWellKnownClassLoader(Thread* self,
- const std::vector<const DexFile*>& dex_files,
- jclass loader_class,
- jobject parent_loader,
- jobject shared_libraries,
- jobject shared_libraries_after) {
- CHECK(self->GetJniEnv()->IsSameObject(loader_class,
- WellKnownClasses::dalvik_system_PathClassLoader) ||
- self->GetJniEnv()->IsSameObject(loader_class,
- WellKnownClasses::dalvik_system_DelegateLastClassLoader) ||
- self->GetJniEnv()->IsSameObject(loader_class,
- WellKnownClasses::dalvik_system_InMemoryDexClassLoader));
-
- // SOAAlreadyRunnable is protected, and we need something to add a global reference.
- // We could move the jobject to the callers, but all call-sites do this...
- ScopedObjectAccessUnchecked soa(self);
-
- // For now, create a libcore-level DexFile for each ART DexFile. This "explodes" multidex.
- StackHandleScope<5> hs(self);
-
- Handle<mirror::Class> h_loader_class =
- hs.NewHandle<mirror::Class>(soa.Decode<mirror::Class>(loader_class));
- Handle<mirror::ClassLoader> h_parent =
- hs.NewHandle<mirror::ClassLoader>(soa.Decode<mirror::ClassLoader>(parent_loader));
- Handle<mirror::ObjectArray<mirror::ClassLoader>> h_shared_libraries =
- hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::ClassLoader>>(shared_libraries));
- Handle<mirror::ObjectArray<mirror::ClassLoader>> h_shared_libraries_after =
- hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::ClassLoader>>(shared_libraries_after));
-
- ObjPtr<mirror::ClassLoader> loader = CreateWellKnownClassLoader(
- self,
- dex_files,
- h_loader_class,
- h_parent,
- h_shared_libraries,
- h_shared_libraries_after);
-
- // Make it a global ref and return.
- ScopedLocalRef<jobject> local_ref(
- soa.Env(), soa.Env()->AddLocalReference<jobject>(loader));
- return soa.Env()->NewGlobalRef(local_ref.get());
-}
-
jobject ClassLinker::CreatePathClassLoader(Thread* self,
const std::vector<const DexFile*>& dex_files) {
- return CreateWellKnownClassLoader(self,
- dex_files,
- WellKnownClasses::dalvik_system_PathClassLoader,
- nullptr);
+ StackHandleScope<3u> hs(self);
+ Handle<mirror::Class> d_s_pcl =
+ hs.NewHandle(WellKnownClasses::dalvik_system_PathClassLoader.Get());
+ auto null_parent = hs.NewHandle<mirror::ClassLoader>(nullptr);
+ auto null_libs = hs.NewHandle<mirror::ObjectArray<mirror::ClassLoader>>(nullptr);
+ ObjPtr<mirror::ClassLoader> class_loader =
+ CreateWellKnownClassLoader(self, dex_files, d_s_pcl, null_parent, null_libs, null_libs);
+ return Runtime::Current()->GetJavaVM()->AddGlobalRef(self, class_loader);
}
void ClassLinker::DropFindArrayClassCache() {
@@ -10196,6 +10293,18 @@
}
}
+void ClassLinker::VisitDexCaches(DexCacheVisitor* visitor) const {
+ Thread* const self = Thread::Current();
+ for (const auto& it : dex_caches_) {
+ // Need to use DecodeJObject so that we get null for cleared JNI weak globals.
+ ObjPtr<mirror::DexCache> dex_cache = ObjPtr<mirror::DexCache>::DownCast(
+ self->DecodeJObject(it.second.weak_root));
+ if (dex_cache != nullptr) {
+ visitor->Visit(dex_cache);
+ }
+ }
+}
+
void ClassLinker::VisitAllocators(AllocatorVisitor* visitor) const {
for (const ClassLoaderData& data : class_loaders_) {
LinearAlloc* alloc = data.allocator;
@@ -10221,27 +10330,65 @@
void ClassLinker::CleanupClassLoaders() {
Thread* const self = Thread::Current();
- std::vector<ClassLoaderData> to_delete;
+ std::list<ClassLoaderData> to_delete;
// Do the delete outside the lock to avoid lock violation in jit code cache.
{
WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
for (auto it = class_loaders_.begin(); it != class_loaders_.end(); ) {
- const ClassLoaderData& data = *it;
+ auto this_it = it;
+ ++it;
+ const ClassLoaderData& data = *this_it;
// Need to use DecodeJObject so that we get null for cleared JNI weak globals.
ObjPtr<mirror::ClassLoader> class_loader =
ObjPtr<mirror::ClassLoader>::DownCast(self->DecodeJObject(data.weak_root));
- if (class_loader != nullptr) {
- ++it;
- } else {
+ if (class_loader == nullptr) {
VLOG(class_linker) << "Freeing class loader";
- to_delete.push_back(data);
- it = class_loaders_.erase(it);
+ to_delete.splice(to_delete.end(), class_loaders_, this_it);
}
}
}
- for (ClassLoaderData& data : to_delete) {
- // CHA unloading analysis and SingleImplementaion cleanups are required.
- DeleteClassLoader(self, data, /*cleanup_cha=*/ true);
+ std::set<const OatFile*> unregistered_oat_files;
+ if (!to_delete.empty()) {
+ JavaVMExt* vm = self->GetJniEnv()->GetVm();
+ WriterMutexLock mu(self, *Locks::dex_lock_);
+ for (auto it = dex_caches_.begin(), end = dex_caches_.end(); it != end; ) {
+ const DexFile* dex_file = it->first;
+ const DexCacheData& data = it->second;
+ if (self->DecodeJObject(data.weak_root) == nullptr) {
+ DCHECK(to_delete.end() != std::find_if(
+ to_delete.begin(),
+ to_delete.end(),
+ [&](const ClassLoaderData& cld) { return cld.class_table == data.class_table; }));
+ if (dex_file->GetOatDexFile() != nullptr &&
+ dex_file->GetOatDexFile()->GetOatFile() != nullptr &&
+ dex_file->GetOatDexFile()->GetOatFile()->IsExecutable()) {
+ unregistered_oat_files.insert(dex_file->GetOatDexFile()->GetOatFile());
+ }
+ vm->DeleteWeakGlobalRef(self, data.weak_root);
+ it = dex_caches_.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+ {
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ for (ClassLoaderData& data : to_delete) {
+ // CHA unloading analysis and SingleImplementaion cleanups are required.
+ DeleteClassLoader(self, data, /*cleanup_cha=*/ true);
+ }
+ }
+ if (!unregistered_oat_files.empty()) {
+ for (const OatFile* oat_file : unregistered_oat_files) {
+ // Notify the fault handler about removal of the executable code range if needed.
+ DCHECK(oat_file->IsExecutable());
+ size_t exec_offset = oat_file->GetOatHeader().GetExecutableOffset();
+ DCHECK_LE(exec_offset, oat_file->Size());
+ size_t exec_size = oat_file->Size() - exec_offset;
+ if (exec_size != 0u) {
+ Runtime::Current()->RemoveGeneratedCodeRange(oat_file->Begin() + exec_offset, exec_size);
+ }
+ }
}
}
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index a3a1adf..b7a05ee 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -36,6 +36,7 @@
#include "dex/dex_file_types.h"
#include "gc_root.h"
#include "handle.h"
+#include "interpreter/mterp/nterp.h"
#include "jni.h"
#include "mirror/class.h"
#include "mirror/object.h"
@@ -127,6 +128,13 @@
REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) = 0;
};
+class DexCacheVisitor {
+ public:
+ virtual ~DexCacheVisitor() {}
+ virtual void Visit(ObjPtr<mirror::DexCache> dex_cache)
+ REQUIRES_SHARED(Locks::dex_lock_, Locks::mutator_lock_) = 0;
+};
+
template <typename Func>
class ClassLoaderFuncVisitor final : public ClassLoaderVisitor {
public:
@@ -168,6 +176,7 @@
// Add boot class path dex files that were not included in the boot image.
// ClassLinker takes ownership of these dex files.
+ // DO NOT use directly. Use `Runtime::AddExtraBootDexFiles`.
void AddExtraBootDexFiles(Thread* self,
std::vector<std::unique_ptr<const DexFile>>&& additional_dex_files)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -452,6 +461,12 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
+ // Initializes a few essential classes, namely `java.lang.Class`,
+ // `java.lang.Object` and `java.lang.reflect.Field`.
+ void RunEarlyRootClinits(Thread* self)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
+
// Initializes classes that have instances in the image but that have
// <clinit> methods so they could not be initialized by the compiler.
void RunRootClinits(Thread* self)
@@ -478,6 +493,11 @@
REQUIRES(!Locks::classlinker_classes_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Visits only the classes in the boot class path.
+ template <typename Visitor>
+ inline void VisitBootClasses(Visitor* visitor)
+ REQUIRES_SHARED(Locks::classlinker_classes_lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_);
// Less efficient variant of VisitClasses that copies the class_table_ into secondary storage
// so that it can visit individual classes without holding the doesn't hold the
// Locks::classlinker_classes_lock_. As the Locks::classlinker_classes_lock_ isn't held this code
@@ -608,6 +628,11 @@
return nterp_trampoline_ == entry_point;
}
+ bool IsNterpEntryPoint(const void* entry_point) const {
+ return entry_point == interpreter::GetNterpEntryPoint() ||
+ entry_point == interpreter::GetNterpWithClinitEntryPoint();
+ }
+
const void* GetQuickToInterpreterBridgeTrampoline() const {
return quick_to_interpreter_bridge_trampoline_;
}
@@ -645,31 +670,19 @@
REQUIRES(!Locks::classlinker_classes_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- // Creates a GlobalRef PathClassLoader or DelegateLastClassLoader (specified by loader_class)
- // that can be used to load classes from the given dex files. The parent of the class loader
- // will be set to `parent_loader`. If `parent_loader` is null the parent will be
- // the boot class loader.
- // If class_loader points to a different class than PathClassLoader or DelegateLastClassLoader
- // this method will abort.
- // Note: the objects are not completely set up. Do not use this outside of tests and the compiler.
- jobject CreateWellKnownClassLoader(Thread* self,
- const std::vector<const DexFile*>& dex_files,
- jclass loader_class,
- jobject parent_loader,
- jobject shared_libraries = nullptr,
- jobject shared_libraries_after = nullptr)
- REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Locks::dex_lock_);
-
- // Calls CreateWellKnownClassLoader(self,
- // dex_files,
- // WellKnownClasses::dalvik_system_PathClassLoader,
- // nullptr)
+ // Calls `CreateWellKnownClassLoader()` with `WellKnownClasses::dalvik_system_PathClassLoader`,
+ // and null parent and libraries. Wraps the result in a JNI global reference.
jobject CreatePathClassLoader(Thread* self, const std::vector<const DexFile*>& dex_files)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
- // Non-GlobalRef version of CreateWellKnownClassLoader
+ // Creates a `PathClassLoader`, `DelegateLastClassLoader` or `InMemoryDexClassLoader`
+ // (specified by loader_class) that can be used to load classes from the given dex files.
+ // The parent of the class loader will be set to `parent_loader`. If `parent_loader` is
+ // null the parent will be the boot class loader.
+ // If `loader_class` points to a different class than `PathClassLoader`,
+ // `DelegateLastClassLoader` or `InMemoryDexClassLoader` this method will abort.
+ // Note: the objects are not completely set up. Do not use this outside of tests and the compiler.
ObjPtr<mirror::ClassLoader> CreateWellKnownClassLoader(
Thread* self,
const std::vector<const DexFile*>& dex_files,
@@ -710,8 +723,7 @@
REQUIRES(!Locks::classlinker_classes_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- static bool IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
- ObjPtr<mirror::ClassLoader> class_loader)
+ static bool IsBootClassLoader(ObjPtr<mirror::Object> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_);
ArtMethod* AddMethodToConflictTable(ObjPtr<mirror::Class> klass,
@@ -762,10 +774,12 @@
REQUIRES_SHARED(Locks::mutator_lock_)
NO_THREAD_SAFETY_ANALYSIS;
+ // DO NOT use directly. Use `Runtime::AppendToBootClassPath`.
void AppendToBootClassPath(Thread* self, const DexFile* dex_file)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
+ // DO NOT use directly. Use `Runtime::AppendToBootClassPath`.
void AppendToBootClassPath(const DexFile* dex_file, ObjPtr<mirror::DexCache> dex_cache)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
@@ -774,6 +788,10 @@
void VisitClassLoaders(ClassLoaderVisitor* visitor) const
REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_);
+ // Visit all of the dex caches in the class linker.
+ void VisitDexCaches(DexCacheVisitor* visitor) const
+ REQUIRES_SHARED(Locks::dex_lock_, Locks::mutator_lock_);
+
// Checks that a class and its superclass from another class loader have the same virtual methods.
bool ValidateSuperClassDescriptors(Handle<mirror::Class> klass)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -782,7 +800,7 @@
return cha_.get();
}
- void MakeInitializedClassesVisiblyInitialized(Thread* self, bool wait);
+ void MakeInitializedClassesVisiblyInitialized(Thread* self, bool wait /* ==> no locks held */);
// Registers the native method and returns the new entry point. NB The returned entry point
// might be different from the native_method argument if some MethodCallback modifies it.
@@ -988,8 +1006,7 @@
// class-loader chain could be handled, false otherwise, i.e., a non-supported class-loader
// was encountered while walking the parent chain (currently only BootClassLoader and
// PathClassLoader are supported).
- bool FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
- Thread* self,
+ bool FindClassInBaseDexClassLoader(Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
@@ -997,8 +1014,7 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
- bool FindClassInSharedLibraries(ScopedObjectAccessAlreadyRunnable& soa,
- Thread* self,
+ bool FindClassInSharedLibraries(Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
@@ -1006,8 +1022,7 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
- bool FindClassInSharedLibrariesHelper(ScopedObjectAccessAlreadyRunnable& soa,
- Thread* self,
+ bool FindClassInSharedLibrariesHelper(Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
@@ -1016,8 +1031,7 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::dex_lock_);
- bool FindClassInSharedLibrariesAfter(ScopedObjectAccessAlreadyRunnable& soa,
- Thread* self,
+ bool FindClassInSharedLibrariesAfter(Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
@@ -1033,7 +1047,7 @@
// The method always returns true, to notify to the caller a
// BaseDexClassLoader has a known lookup.
bool FindClassInBaseDexClassLoaderClassPath(
- ScopedObjectAccessAlreadyRunnable& soa,
+ Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 010b384..05f31b2 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -52,6 +52,7 @@
#include "mirror/object_array-inl.h"
#include "mirror/proxy.h"
#include "mirror/reference.h"
+#include "mirror/stack_frame_info.h"
#include "mirror/stack_trace_element.h"
#include "mirror/string-inl.h"
#include "mirror/var_handle.h"
@@ -130,7 +131,14 @@
EXPECT_TRUE(JavaLangObject->GetSuperClass() == nullptr);
EXPECT_FALSE(JavaLangObject->HasSuperClass());
EXPECT_TRUE(JavaLangObject->GetClassLoader() == nullptr);
- class_linker_->MakeInitializedClassesVisiblyInitialized(Thread::Current(), /*wait=*/ true);
+ {
+ Thread* self = Thread::Current();
+ StackHandleScope<1> hs(self);
+ HandleWrapperObjPtr<mirror::Class> h(hs.NewHandleWrapper(&JavaLangObject));
+ ScopedThreadSuspension sts(self, ThreadState::kNative);
+ class_linker_->MakeInitializedClassesVisiblyInitialized(self, /*wait=*/ true);
+ // HandleWrapperObjPtr restores JavaLangObject here after becoming runnable again.
+ }
EXPECT_EQ(ClassStatus::kVisiblyInitialized, JavaLangObject->GetStatus());
EXPECT_FALSE(JavaLangObject->IsErroneous());
EXPECT_TRUE(JavaLangObject->IsLoaded());
@@ -598,6 +606,7 @@
struct ClassExtOffsets : public CheckOffsets<mirror::ClassExt> {
ClassExtOffsets() : CheckOffsets<mirror::ClassExt>(false, "Ldalvik/system/ClassExt;") {
+ addOffset(OFFSETOF_MEMBER(mirror::ClassExt, class_value_map_), "classValueMap");
addOffset(OFFSETOF_MEMBER(mirror::ClassExt, erroneous_state_error_), "erroneousStateError");
addOffset(OFFSETOF_MEMBER(mirror::ClassExt, instance_jfield_ids_), "instanceJfieldIDs");
addOffset(OFFSETOF_MEMBER(mirror::ClassExt, jmethod_ids_), "jmethodIDs");
@@ -640,6 +649,20 @@
}
};
+struct StackFrameInfoOffsets : public CheckOffsets<mirror::StackFrameInfo> {
+ StackFrameInfoOffsets() : CheckOffsets<mirror::StackFrameInfo>(
+ false, "Ljava/lang/StackFrameInfo;") {
+ addOffset(OFFSETOF_MEMBER(mirror::StackFrameInfo, bci_), "bci");
+ addOffset(OFFSETOF_MEMBER(mirror::StackFrameInfo, declaring_class_), "declaringClass");
+ addOffset(OFFSETOF_MEMBER(mirror::StackFrameInfo, file_name_), "fileName");
+ addOffset(OFFSETOF_MEMBER(mirror::StackFrameInfo, line_number_), "lineNumber");
+ addOffset(OFFSETOF_MEMBER(mirror::StackFrameInfo, method_name_), "methodName");
+ addOffset(OFFSETOF_MEMBER(mirror::StackFrameInfo, method_type_), "methodType");
+ addOffset(OFFSETOF_MEMBER(mirror::StackFrameInfo, retain_class_ref_), "retainClassRef");
+ addOffset(OFFSETOF_MEMBER(mirror::StackFrameInfo, ste_), "ste");
+ }
+};
+
struct ClassLoaderOffsets : public CheckOffsets<mirror::ClassLoader> {
ClassLoaderOffsets() : CheckOffsets<mirror::ClassLoader>(false, "Ljava/lang/ClassLoader;") {
addOffset(OFFSETOF_MEMBER(mirror::ClassLoader, allocator_), "allocator");
@@ -661,20 +684,18 @@
addOffset(OFFSETOF_MEMBER(mirror::DexCache, class_loader_), "classLoader");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, dex_file_), "dexFile");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, location_), "location");
- addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_preresolved_strings_), "numPreResolvedStrings");
- addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_call_sites_), "numResolvedCallSites");
- addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_fields_), "numResolvedFields");
- addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_method_types_), "numResolvedMethodTypes");
- addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_methods_), "numResolvedMethods");
- addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_resolved_types_), "numResolvedTypes");
- addOffset(OFFSETOF_MEMBER(mirror::DexCache, num_strings_), "numStrings");
- addOffset(OFFSETOF_MEMBER(mirror::DexCache, preresolved_strings_), "preResolvedStrings");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_call_sites_), "resolvedCallSites");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_fields_), "resolvedFields");
+ addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_fields_array_), "resolvedFieldsArray");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_method_types_), "resolvedMethodTypes");
+ addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_method_types_array_),
+ "resolvedMethodTypesArray");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_methods_), "resolvedMethods");
+ addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_methods_array_), "resolvedMethodsArray");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_types_), "resolvedTypes");
+ addOffset(OFFSETOF_MEMBER(mirror::DexCache, resolved_types_array_), "resolvedTypesArray");
addOffset(OFFSETOF_MEMBER(mirror::DexCache, strings_), "strings");
+ addOffset(OFFSETOF_MEMBER(mirror::DexCache, strings_array_), "stringsArray");
}
};
@@ -830,7 +851,7 @@
// C++ fields must exactly match the fields in the Java classes. If this fails,
// reorder the fields in the C++ class. Managed class fields are ordered by
-// ClassLinker::LinkFields.
+// ClassLinker::LinkFieldsHelper::LinkFields.
TEST_F(ClassLinkerTest, ValidateFieldOrderOfJavaCppUnionClasses) {
ScopedObjectAccess soa(Thread::Current());
EXPECT_TRUE(ObjectOffsets().Check());
@@ -858,6 +879,7 @@
EXPECT_TRUE(ArrayElementVarHandleOffsets().Check());
EXPECT_TRUE(ByteArrayViewVarHandleOffsets().Check());
EXPECT_TRUE(ByteBufferViewVarHandleOffsets().Check());
+ EXPECT_TRUE(StackFrameInfoOffsets().Check());
}
TEST_F(ClassLinkerTest, FindClassNonexistent) {
@@ -1689,7 +1711,7 @@
}
ASSERT_TRUE(klass == nullptr);
} else if (expected_class_loader_obj == nullptr) {
- ASSERT_TRUE(ClassLinker::IsBootClassLoader(soa, klass->GetClassLoader()));
+ ASSERT_TRUE(ClassLinker::IsBootClassLoader(klass->GetClassLoader()));
} else {
ASSERT_TRUE(klass != nullptr) << descriptor;
Handle<mirror::ClassLoader> expected_class_loader(
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index 2419b7b..722b8f7 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -18,9 +18,9 @@
#include <algorithm>
-#include <android-base/parseint.h>
-#include <android-base/strings.h>
-
+#include "android-base/file.h"
+#include "android-base/parseint.h"
+#include "android-base/strings.h"
#include "art_field-inl.h"
#include "base/casts.h"
#include "base/dchecked_vector.h"
@@ -44,7 +44,7 @@
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -778,14 +778,15 @@
}
// Returns the WellKnownClass for the given class loader type.
-static jclass GetClassLoaderClass(ClassLoaderContext::ClassLoaderType type) {
+static ObjPtr<mirror::Class> GetClassLoaderClass(ClassLoaderContext::ClassLoaderType type)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
switch (type) {
case ClassLoaderContext::kPathClassLoader:
- return WellKnownClasses::dalvik_system_PathClassLoader;
+ return WellKnownClasses::dalvik_system_PathClassLoader.Get();
case ClassLoaderContext::kDelegateLastClassLoader:
- return WellKnownClasses::dalvik_system_DelegateLastClassLoader;
+ return WellKnownClasses::dalvik_system_DelegateLastClassLoader.Get();
case ClassLoaderContext::kInMemoryDexClassLoader:
- return WellKnownClasses::dalvik_system_InMemoryDexClassLoader;
+ return WellKnownClasses::dalvik_system_InMemoryDexClassLoader.Get();
case ClassLoaderContext::kInvalidClassLoader: break; // will fail after the switch.
}
LOG(FATAL) << "Invalid class loader type " << type;
@@ -883,8 +884,7 @@
compilation_sources.begin(),
compilation_sources.end());
}
- Handle<mirror::Class> loader_class = hs.NewHandle<mirror::Class>(
- soa.Decode<mirror::Class>(GetClassLoaderClass(info.type)));
+ Handle<mirror::Class> loader_class = hs.NewHandle<mirror::Class>(GetClassLoaderClass(info.type));
ObjPtr<mirror::ClassLoader> loader =
Runtime::Current()->GetClassLinker()->CreateWellKnownClassLoader(
self,
@@ -950,12 +950,13 @@
return result;
}
-std::string ClassLoaderContext::FlattenDexPaths() const {
+std::vector<std::string> ClassLoaderContext::FlattenDexPaths() const {
+ std::vector<std::string> result;
+
if (class_loader_chain_ == nullptr) {
- return "";
+ return result;
}
- std::vector<std::string> result;
std::vector<ClassLoaderInfo*> work_list;
work_list.push_back(class_loader_chain_.get());
while (!work_list.empty()) {
@@ -966,7 +967,7 @@
}
AddToWorkList(info, work_list);
}
- return FlattenClasspath(result);
+ return result;
}
const char* ClassLoaderContext::GetClassLoaderTypeName(ClassLoaderType type) {
@@ -1019,21 +1020,18 @@
// Collects all the dex files loaded by the given class loader.
// Returns true for success or false if an unexpected state is discovered (e.g. a null dex cookie,
// a null list of dex elements or a null dex element).
-static bool CollectDexFilesFromSupportedClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+static bool CollectDexFilesFromSupportedClassLoader(Thread* self,
Handle<mirror::ClassLoader> class_loader,
std::vector<const DexFile*>* out_dex_files)
REQUIRES_SHARED(Locks::mutator_lock_) {
- CHECK(IsInstanceOfBaseDexClassLoader(soa, class_loader));
+ CHECK(IsInstanceOfBaseDexClassLoader(class_loader));
// All supported class loaders inherit from BaseDexClassLoader.
// We need to get the DexPathList and loop through it.
- ArtField* const cookie_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
- ArtField* const dex_file_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
+ ArtField* const cookie_field = WellKnownClasses::dalvik_system_DexFile_cookie;
+ ArtField* const dex_file_field = WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
ObjPtr<mirror::Object> dex_path_list =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)->
- GetObject(class_loader.Get());
+ WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList->GetObject(class_loader.Get());
CHECK(cookie_field != nullptr);
CHECK(dex_file_field != nullptr);
if (dex_path_list == nullptr) {
@@ -1043,8 +1041,7 @@
}
// DexPathList has an array dexElements of Elements[] which each contain a dex file.
ObjPtr<mirror::Object> dex_elements_obj =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
- GetObject(dex_path_list);
+ WellKnownClasses::dalvik_system_DexPathList_dexElements->GetObject(dex_path_list);
// Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
// at the mCookie which is a DexFile vector.
if (dex_elements_obj == nullptr) {
@@ -1052,7 +1049,7 @@
// and assume we have no elements.
return true;
} else {
- StackHandleScope<1> hs(soa.Self());
+ StackHandleScope<1> hs(self);
Handle<mirror::ObjectArray<mirror::Object>> dex_elements(
hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>()));
for (auto element : dex_elements.Iterate<mirror::Object>()) {
@@ -1075,19 +1072,15 @@
}
static bool GetDexFilesFromDexElementsArray(
- ScopedObjectAccessAlreadyRunnable& soa,
Handle<mirror::ObjectArray<mirror::Object>> dex_elements,
std::vector<const DexFile*>* out_dex_files) REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(dex_elements != nullptr);
- ArtField* const cookie_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
- ArtField* const dex_file_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
- const ObjPtr<mirror::Class> element_class = soa.Decode<mirror::Class>(
- WellKnownClasses::dalvik_system_DexPathList__Element);
- const ObjPtr<mirror::Class> dexfile_class = soa.Decode<mirror::Class>(
- WellKnownClasses::dalvik_system_DexFile);
+ ArtField* const cookie_field = WellKnownClasses::dalvik_system_DexFile_cookie;
+ ArtField* const dex_file_field = WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
+ const ObjPtr<mirror::Class> element_class =
+ WellKnownClasses::dalvik_system_DexPathList__Element.Get();
+ const ObjPtr<mirror::Class> dexfile_class = WellKnownClasses::dalvik_system_DexFile.Get();
for (auto element : dex_elements.Iterate<mirror::Object>()) {
// We can hit a null element here because this is invoked with a partially filled dex_elements
@@ -1131,17 +1124,17 @@
bool is_shared_library,
bool is_after)
REQUIRES_SHARED(Locks::mutator_lock_) {
- if (ClassLinker::IsBootClassLoader(soa, class_loader.Get())) {
+ if (ClassLinker::IsBootClassLoader(class_loader.Get())) {
// Nothing to do for the boot class loader as we don't add its dex files to the context.
return true;
}
ClassLoaderContext::ClassLoaderType type;
- if (IsPathOrDexClassLoader(soa, class_loader)) {
+ if (IsPathOrDexClassLoader(class_loader)) {
type = kPathClassLoader;
- } else if (IsDelegateLastClassLoader(soa, class_loader)) {
+ } else if (IsDelegateLastClassLoader(class_loader)) {
type = kDelegateLastClassLoader;
- } else if (IsInMemoryDexClassLoader(soa, class_loader)) {
+ } else if (IsInMemoryDexClassLoader(class_loader)) {
type = kInMemoryDexClassLoader;
} else {
LOG(WARNING) << "Unsupported class loader";
@@ -1150,7 +1143,7 @@
// Inspect the class loader for its dex files.
std::vector<const DexFile*> dex_files_loaded;
- CollectDexFilesFromSupportedClassLoader(soa, class_loader, &dex_files_loaded);
+ CollectDexFilesFromSupportedClassLoader(soa.Self(), class_loader, &dex_files_loaded);
// If we have a dex_elements array extract its dex elements now.
// This is used in two situations:
@@ -1162,7 +1155,7 @@
// BaseDexClassLoader, the paths already present in the class loader will be passed
// in the dex_elements array.
if (dex_elements != nullptr) {
- GetDexFilesFromDexElementsArray(soa, dex_elements, &dex_files_loaded);
+ GetDexFilesFromDexElementsArray(dex_elements, &dex_files_loaded);
}
ClassLoaderInfo* info = new ClassLoaderContext::ClassLoaderInfo(type);
@@ -1197,8 +1190,7 @@
// Add the shared libraries.
StackHandleScope<5> hs(Thread::Current());
- ArtField* field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
+ ArtField* field = WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader.Get());
if (raw_shared_libraries != nullptr) {
Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries =
@@ -1216,8 +1208,7 @@
}
}
}
- ArtField* field2 = jni::DecodeArtField(
- WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter);
+ ArtField* field2 = WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter;
ObjPtr<mirror::Object> raw_shared_libraries_after = field2->GetObject(class_loader.Get());
if (raw_shared_libraries_after != nullptr) {
Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries_after =
@@ -1287,12 +1278,12 @@
StackHandleScope<1> hs(soa.Self());
Handle<mirror::ClassLoader> h_class_loader =
hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader));
- if (!IsInstanceOfBaseDexClassLoader(soa, h_class_loader)) {
+ if (!IsInstanceOfBaseDexClassLoader(h_class_loader)) {
return std::map<std::string, std::string>{};
}
std::vector<const DexFile*> dex_files_loaded;
- CollectDexFilesFromSupportedClassLoader(soa, h_class_loader, &dex_files_loaded);
+ CollectDexFilesFromSupportedClassLoader(soa.Self(), h_class_loader, &dex_files_loaded);
std::map<std::string, std::string> results;
for (const DexFile* dex_file : dex_files_loaded) {
@@ -1345,6 +1336,28 @@
(std::string_view(path).substr(/*pos*/ path.size() - suffix.size()) == suffix);
}
+// Resolves symlinks and returns the canonicalized absolute path. Returns relative path as is.
+static std::string ResolveIfAbsolutePath(const std::string& path) {
+ if (!IsAbsoluteLocation(path)) {
+ return path;
+ }
+
+ std::string filename = path;
+ std::string multi_dex_suffix;
+ size_t pos = filename.find(DexFileLoader::kMultiDexSeparator);
+ if (pos != std::string::npos) {
+ multi_dex_suffix = filename.substr(pos);
+ filename.resize(pos);
+ }
+
+ std::string resolved_filename;
+ if (!android::base::Realpath(filename, &resolved_filename)) {
+ PLOG(ERROR) << "Unable to resolve path '" << path << "'";
+ return path;
+ }
+ return resolved_filename + multi_dex_suffix;
+}
+
// Returns true if the given dex names are mathing, false otherwise.
static bool AreDexNameMatching(const std::string& actual_dex_name,
const std::string& expected_dex_name) {
@@ -1355,28 +1368,30 @@
bool is_dex_name_absolute = IsAbsoluteLocation(actual_dex_name);
bool is_expected_dex_name_absolute = IsAbsoluteLocation(expected_dex_name);
bool dex_names_match = false;
+ std::string resolved_actual_dex_name = ResolveIfAbsolutePath(actual_dex_name);
+ std::string resolved_expected_dex_name = ResolveIfAbsolutePath(expected_dex_name);
if (is_dex_name_absolute == is_expected_dex_name_absolute) {
// If both locations are absolute or relative then compare them as they are.
// This is usually the case for: shared libraries and secondary dex files.
- dex_names_match = (actual_dex_name == expected_dex_name);
+ dex_names_match = (resolved_actual_dex_name == resolved_expected_dex_name);
} else if (is_dex_name_absolute) {
// The runtime name is absolute but the compiled name (the expected one) is relative.
// This is the case for split apks which depend on base or on other splits.
dex_names_match =
- AbsolutePathHasRelativeSuffix(actual_dex_name, expected_dex_name);
+ AbsolutePathHasRelativeSuffix(resolved_actual_dex_name, resolved_expected_dex_name);
} else if (is_expected_dex_name_absolute) {
// The runtime name is relative but the compiled name is absolute.
// There is no expected use case that would end up here as dex files are always loaded
// with their absolute location. However, be tolerant and do the best effort (in case
// there are unexpected new use case...).
dex_names_match =
- AbsolutePathHasRelativeSuffix(expected_dex_name, actual_dex_name);
+ AbsolutePathHasRelativeSuffix(resolved_expected_dex_name, resolved_actual_dex_name);
} else {
// Both locations are relative. In this case there's not much we can be sure about
// except that the names are the same. The checksum will ensure that the files are
// are same. This should not happen outside testing and manual invocations.
- dex_names_match = (actual_dex_name == expected_dex_name);
+ dex_names_match = (resolved_actual_dex_name == resolved_expected_dex_name);
}
return dex_names_match;
diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h
index eceea00..806ab5e 100644
--- a/runtime/class_loader_context.h
+++ b/runtime/class_loader_context.h
@@ -51,7 +51,10 @@
// Special encoding used to denote a foreign ClassLoader was found when trying to encode class
// loader contexts for each classpath element in a ClassLoader. See
- // EncodeClassPathContextsForClassLoader. Keep in sync with PackageDexUsage in the framework.
+ // EncodeClassPathContextsForClassLoader. Keep in sync with PackageDexUsage in the framework
+ // (frameworks/base/services/core/java/com/android/server/pm/dex/PackageDexUsage.java) and
+ // DexUseManager in ART Services
+ // (art/libartservice/service/java/com/android/server/art/DexUseManager.java).
static constexpr const char* kUnsupportedClassLoaderContextEncoding =
"=UnsupportedClassLoaderContext=";
@@ -155,9 +158,8 @@
// Should only be called if OpenDexFiles() returned true.
std::vector<const DexFile*> FlattenOpenedDexFiles() const;
- // Return a colon-separated list of dex file locations from this class loader
- // context after flattening.
- std::string FlattenDexPaths() const;
+ // Return a list of dex file locations from this class loader context after flattening.
+ std::vector<std::string> FlattenDexPaths() const;
// Verifies that the current context is identical to the context encoded as `context_spec`.
// Identical means:
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index 073b4b6..ce9780a 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -18,8 +18,13 @@
#include <gtest/gtest.h>
+#include <filesystem>
+#include <fstream>
+
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "art_field-inl.h"
+#include "art_method-alloc-inl.h"
#include "base/dchecked_vector.h"
#include "base/stl_util.h"
#include "class_linker.h"
@@ -36,12 +41,29 @@
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
class ClassLoaderContextTest : public CommonRuntimeTest {
public:
+ ClassLoaderContextTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+
+ void SetUp() override {
+ CommonRuntimeTest::SetUp();
+ scratch_dir_ = std::make_unique<ScratchDir>();
+ scratch_path_ = scratch_dir_->GetPath();
+ // Remove the trailing '/';
+ scratch_path_.resize(scratch_path_.length() - 1);
+ }
+
+ void TearDown() override {
+ scratch_dir_.reset();
+ CommonRuntimeTest::TearDown();
+ }
+
void VerifyContextSize(ClassLoaderContext* context, size_t expected_size) {
ASSERT_TRUE(context != nullptr);
ASSERT_EQ(expected_size, context->GetParentChainSize());
@@ -219,14 +241,14 @@
ASSERT_FALSE(context->owns_the_dex_files_);
}
- void VerifyClassLoaderDexFiles(ScopedObjectAccess& soa,
+ void VerifyClassLoaderDexFiles(Thread* self,
Handle<mirror::ClassLoader> class_loader,
- jclass type,
+ ObjPtr<mirror::Class> type,
std::vector<const DexFile*>& expected_dex_files)
REQUIRES_SHARED(Locks::mutator_lock_) {
- ASSERT_TRUE(class_loader->GetClass() == soa.Decode<mirror::Class>(type));
+ ASSERT_TRUE(class_loader->GetClass() == type);
- std::vector<const DexFile*> class_loader_dex_files = GetDexFiles(soa, class_loader);
+ std::vector<const DexFile*> class_loader_dex_files = GetDexFiles(self, class_loader);
ASSERT_EQ(expected_dex_files.size(), class_loader_dex_files.size());
for (size_t i = 0; i < expected_dex_files.size(); i++) {
@@ -340,6 +362,9 @@
return true;
}
+ std::unique_ptr<ScratchDir> scratch_dir_;
+ std::string scratch_path_;
+
private:
void VerifyClassLoaderInfo(ClassLoaderContext* context,
size_t index,
@@ -653,10 +678,9 @@
Handle<mirror::ClassLoader> class_loader = hs.NewHandle(
soa.Decode<mirror::ClassLoader>(jclass_loader));
- ASSERT_TRUE(class_loader->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader));
+ ASSERT_TRUE(class_loader->GetClass() == WellKnownClasses::dalvik_system_PathClassLoader);
ASSERT_TRUE(class_loader->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
// For the first class loader the class path dex files must come first and then the
// compilation sources.
@@ -665,9 +689,9 @@
expected_classpath.push_back(dex);
}
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
expected_classpath);
}
@@ -690,12 +714,12 @@
soa.Decode<mirror::ClassLoader>(jclass_loader));
// An empty context should create a single PathClassLoader with only the compilation sources.
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
compilation_sources_raw);
ASSERT_TRUE(class_loader->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
}
TEST_F(ClassLoaderContextTest, CreateClassLoaderWithComplexChain) {
@@ -741,31 +765,31 @@
for (auto& dex : compilation_sources_raw) {
class_loader_1_dex_files.push_back(dex);
}
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_1,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_1_dex_files);
// Verify the second class loader
Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(class_loader_1->GetParent());
std::vector<const DexFile*> class_loader_2_dex_files =
MakeNonOwningPointerVector(classpath_dex_c);
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_2,
- WellKnownClasses::dalvik_system_DelegateLastClassLoader,
+ WellKnownClasses::dalvik_system_DelegateLastClassLoader.Get(),
class_loader_2_dex_files);
// Verify the third class loader
Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(class_loader_2->GetParent());
std::vector<const DexFile*> class_loader_3_dex_files =
MakeNonOwningPointerVector(classpath_dex_d);
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_3,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_3_dex_files);
// The last class loader should have the BootClassLoader as a parent.
ASSERT_TRUE(class_loader_3->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
}
TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibraries) {
@@ -809,14 +833,13 @@
for (auto& dex : compilation_sources_raw) {
class_loader_1_dex_files.push_back(dex);
}
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_1,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_1_dex_files);
// Verify the shared libraries.
- ArtField* field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
+ ArtField* field = WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get());
ASSERT_TRUE(raw_shared_libraries != nullptr);
@@ -828,9 +851,9 @@
Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0));
std::vector<const DexFile*> class_loader_2_dex_files =
MakeNonOwningPointerVector(classpath_dex_c);
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_2,
- WellKnownClasses::dalvik_system_DelegateLastClassLoader,
+ WellKnownClasses::dalvik_system_DelegateLastClassLoader.Get(),
class_loader_2_dex_files);
raw_shared_libraries = field->GetObject(class_loader_2.Get());
ASSERT_TRUE(raw_shared_libraries == nullptr);
@@ -839,20 +862,20 @@
Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(shared_libraries->Get(1));
std::vector<const DexFile*> class_loader_3_dex_files =
MakeNonOwningPointerVector(classpath_dex_d);
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_3,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_3_dex_files);
raw_shared_libraries = field->GetObject(class_loader_3.Get());
ASSERT_TRUE(raw_shared_libraries == nullptr);
// All class loaders should have the BootClassLoader as a parent.
ASSERT_TRUE(class_loader_1->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
ASSERT_TRUE(class_loader_2->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
ASSERT_TRUE(class_loader_3->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
}
TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibrariesInParentToo) {
@@ -894,14 +917,13 @@
for (auto& dex : compilation_sources_raw) {
class_loader_1_dex_files.push_back(dex);
}
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_1,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_1_dex_files);
// Verify its shared library.
- ArtField* field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
+ ArtField* field = WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get());
ASSERT_TRUE(raw_shared_libraries != nullptr);
@@ -912,9 +934,9 @@
Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0));
std::vector<const DexFile*> class_loader_2_dex_files =
MakeNonOwningPointerVector(classpath_dex_b);
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_2,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_2_dex_files);
raw_shared_libraries = field->GetObject(class_loader_2.Get());
ASSERT_TRUE(raw_shared_libraries == nullptr);
@@ -923,9 +945,9 @@
Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(class_loader_1->GetParent());
std::vector<const DexFile*> class_loader_3_dex_files =
MakeNonOwningPointerVector(classpath_dex_c);
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_3,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_3_dex_files);
// Verify its shared library.
@@ -939,20 +961,20 @@
Handle<mirror::ClassLoader> class_loader_4 = hs.NewHandle(shared_libraries_2->Get(0));
std::vector<const DexFile*> class_loader_4_dex_files =
MakeNonOwningPointerVector(classpath_dex_d);
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_4,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_4_dex_files);
raw_shared_libraries = field->GetObject(class_loader_4.Get());
ASSERT_TRUE(raw_shared_libraries == nullptr);
// Class loaders should have the BootClassLoader as a parent.
ASSERT_TRUE(class_loader_2->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
ASSERT_TRUE(class_loader_3->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
ASSERT_TRUE(class_loader_4->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
}
TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibrariesDependencies) {
@@ -994,14 +1016,13 @@
for (auto& dex : compilation_sources_raw) {
class_loader_1_dex_files.push_back(dex);
}
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_1,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_1_dex_files);
// Verify its shared library.
- ArtField* field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
+ ArtField* field = WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get());
ASSERT_TRUE(raw_shared_libraries != nullptr);
@@ -1012,9 +1033,9 @@
Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0));
std::vector<const DexFile*> class_loader_2_dex_files =
MakeNonOwningPointerVector(classpath_dex_b);
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_2,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_2_dex_files);
// Verify the shared library dependency of the shared library.
@@ -1028,9 +1049,9 @@
Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(shared_libraries_2->Get(0));
std::vector<const DexFile*> class_loader_3_dex_files =
MakeNonOwningPointerVector(classpath_dex_c);
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_3,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_3_dex_files);
raw_shared_libraries = field->GetObject(class_loader_3.Get());
ASSERT_TRUE(raw_shared_libraries == nullptr);
@@ -1039,20 +1060,20 @@
Handle<mirror::ClassLoader> class_loader_4 = hs.NewHandle(class_loader_1->GetParent());
std::vector<const DexFile*> class_loader_4_dex_files =
MakeNonOwningPointerVector(classpath_dex_d);
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_4,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_4_dex_files);
raw_shared_libraries = field->GetObject(class_loader_4.Get());
ASSERT_TRUE(raw_shared_libraries == nullptr);
// Class loaders should have the BootClassLoader as a parent.
ASSERT_TRUE(class_loader_2->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
ASSERT_TRUE(class_loader_3->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
ASSERT_TRUE(class_loader_4->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
}
TEST_F(ClassLoaderContextTest, RemoveSourceLocations) {
@@ -1109,14 +1130,13 @@
for (auto& dex : compilation_sources_raw) {
class_loader_1_dex_files.push_back(dex);
}
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_1,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_1_dex_files);
// Verify its shared library.
- ArtField* field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
+ ArtField* field = WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get());
ASSERT_TRUE(raw_shared_libraries != nullptr);
@@ -1127,18 +1147,18 @@
Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0));
std::vector<const DexFile*> class_loader_2_dex_files =
MakeNonOwningPointerVector(classpath_dex_b);
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_2,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_2_dex_files);
// Verify the parent.
Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(class_loader_1->GetParent());
std::vector<const DexFile*> class_loader_3_dex_files =
MakeNonOwningPointerVector(classpath_dex_c);
- VerifyClassLoaderDexFiles(soa,
+ VerifyClassLoaderDexFiles(soa.Self(),
class_loader_3,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
class_loader_3_dex_files);
// Verify its shared library is the same as the child.
@@ -1151,9 +1171,9 @@
// Class loaders should have the BootClassLoader as a parent.
ASSERT_TRUE(class_loader_2->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
ASSERT_TRUE(class_loader_3->GetParent()->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+ WellKnownClasses::java_lang_BootClassLoader);
}
TEST_F(ClassLoaderContextTest, EncodeInOatFile) {
@@ -1343,13 +1363,14 @@
static jobject CreateForeignClassLoader() {
ScopedObjectAccess soa(Thread::Current());
- JNIEnv* env = soa.Env();
// We cannot instantiate a ClassLoader directly, so instead we allocate an Object to represent
// our foreign ClassLoader (this works because the runtime does proper instanceof checks before
// operating on this object.
- jmethodID ctor = env->GetMethodID(WellKnownClasses::java_lang_Object, "<init>", "()V");
- return env->NewObject(WellKnownClasses::java_lang_Object, ctor);
+ ArtMethod* ctor =
+ GetClassRoot<mirror::Object>()->FindClassMethod("<init>", "()V", kRuntimePointerSize);
+ CHECK(ctor != nullptr);
+ return soa.AddLocalReference<jobject>(ctor->NewObject<>(soa.Self()));
}
TEST_F(ClassLoaderContextTest, EncodeContextsForUnsupportedBase) {
@@ -1600,6 +1621,28 @@
ClassLoaderContext::VerificationResult::kVerifies);
}
+TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterResolvingSymlinks) {
+ {
+ std::ofstream ofs(scratch_path_ + "/foo.jar");
+ ASSERT_TRUE(ofs);
+ }
+ std::filesystem::create_directory_symlink(scratch_path_, scratch_path_ + "/bar");
+
+ std::string context_spec =
+ android::base::StringPrintf("PCL[%s/foo.jar*123:%s/foo.jar!classes2.dex*456]",
+ scratch_path_.c_str(),
+ scratch_path_.c_str());
+ std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec);
+ PretendContextOpenedDexFilesForChecksums(context.get());
+
+ std::string context_spec_with_symlinks =
+ android::base::StringPrintf("PCL[%s/bar/foo.jar*123:%s/bar/foo.jar!classes2.dex*456]",
+ scratch_path_.c_str(),
+ scratch_path_.c_str());
+ ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec_with_symlinks),
+ ClassLoaderContext::VerificationResult::kVerifies);
+}
+
TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) {
jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr);
jobject class_loader_b = LoadDexInDelegateLastClassLoader("ForClassLoaderB", class_loader_a);
diff --git a/runtime/class_loader_utils.h b/runtime/class_loader_utils.h
index 934c92b..6868b3f 100644
--- a/runtime/class_loader_utils.h
+++ b/runtime/class_loader_utils.h
@@ -26,46 +26,36 @@
#include "mirror/object.h"
#include "native/dalvik_system_DexFile.h"
#include "scoped_thread_state_change-inl.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
// Returns true if the given class loader derives from BaseDexClassLoader.
-inline bool IsInstanceOfBaseDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
- Handle<mirror::ClassLoader> class_loader)
+inline bool IsInstanceOfBaseDexClassLoader(Handle<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_) {
- return class_loader->InstanceOf(
- soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_BaseDexClassLoader));
+ return class_loader->InstanceOf(WellKnownClasses::dalvik_system_BaseDexClassLoader.Get());
}
// Returns true if the given class loader is either a PathClassLoader or a DexClassLoader.
// (they both have the same behaviour with respect to class lookup order)
-inline bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
- Handle<mirror::ClassLoader> class_loader)
+inline bool IsPathOrDexClassLoader(Handle<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_) {
ObjPtr<mirror::Class> class_loader_class = class_loader->GetClass();
- return
- (class_loader_class ==
- soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader)) ||
- (class_loader_class ==
- soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader));
+ return (class_loader_class == WellKnownClasses::dalvik_system_PathClassLoader) ||
+ (class_loader_class == WellKnownClasses::dalvik_system_DexClassLoader);
}
// Returns true if the given class loader is an InMemoryDexClassLoader.
-inline bool IsInMemoryDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
- Handle<mirror::ClassLoader> class_loader)
+inline bool IsInMemoryDexClassLoader(Handle<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_) {
ObjPtr<mirror::Class> class_loader_class = class_loader->GetClass();
- return (class_loader_class ==
- soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_InMemoryDexClassLoader));
+ return (class_loader_class == WellKnownClasses::dalvik_system_InMemoryDexClassLoader);
}
-inline bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
- Handle<mirror::ClassLoader> class_loader)
+inline bool IsDelegateLastClassLoader(Handle<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_) {
ObjPtr<mirror::Class> class_loader_class = class_loader->GetClass();
- return class_loader_class ==
- soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DelegateLastClassLoader);
+ return class_loader_class == WellKnownClasses::dalvik_system_DelegateLastClassLoader;
}
// Visit the DexPathList$Element instances in the given classloader with the given visitor.
@@ -75,20 +65,17 @@
// when the visitor ends the visit (by returning false).
// This function assumes that the given classloader is a subclass of BaseDexClassLoader!
template <typename Visitor, typename RetType>
-inline RetType VisitClassLoaderDexElements(ScopedObjectAccessAlreadyRunnable& soa,
+inline RetType VisitClassLoaderDexElements(Thread* self,
Handle<mirror::ClassLoader> class_loader,
Visitor fn,
RetType defaultReturn)
REQUIRES_SHARED(Locks::mutator_lock_) {
- Thread* self = soa.Self();
ObjPtr<mirror::Object> dex_path_list =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)->
- GetObject(class_loader.Get());
+ WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList->GetObject(class_loader.Get());
if (dex_path_list != nullptr) {
// DexPathList has an array dexElements of Elements[] which each contain a dex file.
ObjPtr<mirror::Object> dex_elements_obj =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
- GetObject(dex_path_list);
+ WellKnownClasses::dalvik_system_DexPathList_dexElements->GetObject(dex_path_list);
// Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
// at the mCookie which is a DexFile vector.
if (dex_elements_obj != nullptr) {
@@ -117,15 +104,13 @@
// when the visitor ends the visit (by returning false).
// This function assumes that the given classloader is a subclass of BaseDexClassLoader!
template <typename Visitor, typename RetType>
-inline RetType VisitClassLoaderDexFiles(ScopedObjectAccessAlreadyRunnable& soa,
+inline RetType VisitClassLoaderDexFiles(Thread* self,
Handle<mirror::ClassLoader> class_loader,
Visitor fn,
RetType defaultReturn)
REQUIRES_SHARED(Locks::mutator_lock_) {
- ArtField* const cookie_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
- ArtField* const dex_file_field =
- jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
+ ArtField* const cookie_field = WellKnownClasses::dalvik_system_DexFile_cookie;
+ ArtField* const dex_file_field = WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
if (dex_file_field == nullptr || cookie_field == nullptr) {
return defaultReturn;
}
@@ -133,7 +118,9 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
ObjPtr<mirror::Object> dex_file = dex_file_field->GetObject(element);
if (dex_file != nullptr) {
- ObjPtr<mirror::LongArray> long_array = cookie_field->GetObject(dex_file)->AsLongArray();
+ StackHandleScope<1> hs(self);
+ Handle<mirror::LongArray> long_array =
+ hs.NewHandle(cookie_field->GetObject(dex_file)->AsLongArray());
if (long_array == nullptr) {
// This should never happen so log a warning.
LOG(WARNING) << "Null DexFile::mCookie";
@@ -155,12 +142,12 @@
return true;
};
- return VisitClassLoaderDexElements(soa, class_loader, visit_dex_files, defaultReturn);
+ return VisitClassLoaderDexElements(self, class_loader, visit_dex_files, defaultReturn);
}
// Simplified version of the above, w/o out argument.
template <typename Visitor>
-inline void VisitClassLoaderDexFiles(ScopedObjectAccessAlreadyRunnable& soa,
+inline void VisitClassLoaderDexFiles(Thread* self,
Handle<mirror::ClassLoader> class_loader,
Visitor fn)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -174,10 +161,10 @@
return fn(dex_file);
};
- VisitClassLoaderDexFiles<decltype(helper), void*>(soa,
+ VisitClassLoaderDexFiles<decltype(helper), void*>(self,
class_loader,
helper,
- /* default= */ nullptr);
+ /* defaultReturn= */ nullptr);
}
} // namespace art
diff --git a/runtime/class_table-inl.h b/runtime/class_table-inl.h
index 071376c..67eeb55 100644
--- a/runtime/class_table-inl.h
+++ b/runtime/class_table-inl.h
@@ -104,6 +104,43 @@
}
}
+template <typename Visitor>
+class ClassTable::TableSlot::ClassAndRootVisitor {
+ public:
+ explicit ClassAndRootVisitor(Visitor& visitor) : visitor_(visitor) {}
+
+ void VisitRoot(mirror::CompressedReference<mirror::Object>* klass) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(!klass->IsNull());
+ // Visit roots in the klass object
+ visitor_(klass->AsMirrorPtr());
+ // Visit the GC-root holding klass' reference
+ visitor_.VisitRoot(klass);
+ }
+
+ private:
+ Visitor& visitor_;
+};
+
+template <typename Visitor>
+void ClassTable::VisitClassesAndRoots(Visitor& visitor) {
+ TableSlot::ClassAndRootVisitor class_visitor(visitor);
+ ReaderMutexLock mu(Thread::Current(), lock_);
+ for (ClassSet& class_set : classes_) {
+ for (TableSlot& table_slot : class_set) {
+ table_slot.VisitRoot(class_visitor);
+ }
+ }
+ for (GcRoot<mirror::Object>& root : strong_roots_) {
+ visitor.VisitRoot(root.AddressWithoutBarrier());
+ }
+ for (const OatFile* oat_file : oat_files_) {
+ for (GcRoot<mirror::Object>& root : oat_file->GetBssGcRoots()) {
+ visitor.VisitRootIfNonNull(root.AddressWithoutBarrier());
+ }
+ }
+}
+
template <ReadBarrierOption kReadBarrierOption, typename Visitor>
bool ClassTable::Visit(Visitor& visitor) {
ReaderMutexLock mu(Thread::Current(), lock_);
diff --git a/runtime/class_table.h b/runtime/class_table.h
index 212a7d6..123c069 100644
--- a/runtime/class_table.h
+++ b/runtime/class_table.h
@@ -85,6 +85,9 @@
template<typename Visitor>
void VisitRoot(const Visitor& visitor) const NO_THREAD_SAFETY_ANALYSIS;
+ template<typename Visitor>
+ class ClassAndRootVisitor;
+
private:
// Extract a raw pointer from an address.
static ObjPtr<mirror::Class> ExtractPtr(uint32_t data)
@@ -185,6 +188,12 @@
REQUIRES(!lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
+ template<class Visitor>
+ void VisitClassesAndRoots(Visitor& visitor)
+ NO_THREAD_SAFETY_ANALYSIS
+ REQUIRES(!lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// Stops visit if the visitor returns false.
template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor>
bool Visit(Visitor& visitor)
diff --git a/runtime/class_table_test.cc b/runtime/class_table_test.cc
index 7dbeba5..14c5d70 100644
--- a/runtime/class_table_test.cc
+++ b/runtime/class_table_test.cc
@@ -66,7 +66,12 @@
};
-class ClassTableTest : public CommonRuntimeTest {};
+class ClassTableTest : public CommonRuntimeTest {
+ protected:
+ ClassTableTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+};
TEST_F(ClassTableTest, ClassTable) {
ScopedObjectAccess soa(Thread::Current());
diff --git a/runtime/common_dex_operations.h b/runtime/common_dex_operations.h
index 882e3ce..0b9d3fb 100644
--- a/runtime/common_dex_operations.h
+++ b/runtime/common_dex_operations.h
@@ -26,6 +26,7 @@
#include "dex/code_item_accessors.h"
#include "dex/dex_file_structs.h"
#include "dex/primitive.h"
+#include "entrypoints/entrypoint_utils.h"
#include "handle_scope-inl.h"
#include "instrumentation.h"
#include "interpreter/interpreter.h"
@@ -55,8 +56,28 @@
ShadowFrame* shadow_frame,
uint16_t arg_offset,
JValue* result);
+
} // namespace interpreter
+inline bool EnsureInitialized(Thread* self, ShadowFrame* shadow_frame)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (LIKELY(!shadow_frame->GetMethod()->StillNeedsClinitCheck())) {
+ return true;
+ }
+
+ // Save the shadow frame.
+ ScopedStackedShadowFramePusher pusher(self, shadow_frame);
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Class> h_class = hs.NewHandle(shadow_frame->GetMethod()->GetDeclaringClass());
+ if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(
+ self, h_class, /*can_init_fields=*/ true, /*can_init_parents=*/ true))) {
+ DCHECK(self->IsExceptionPending());
+ return false;
+ }
+ DCHECK(h_class->IsInitializing());
+ return true;
+}
+
inline void PerformCall(Thread* self,
const CodeItemDataAccessor& accessor,
ArtMethod* caller_method,
@@ -65,15 +86,20 @@
JValue* result,
bool use_interpreter_entrypoint)
REQUIRES_SHARED(Locks::mutator_lock_) {
- if (LIKELY(Runtime::Current()->IsStarted())) {
- if (use_interpreter_entrypoint) {
- interpreter::ArtInterpreterToInterpreterBridge(self, accessor, callee_frame, result);
- } else {
- interpreter::ArtInterpreterToCompiledCodeBridge(
- self, caller_method, callee_frame, first_dest_reg, result);
- }
- } else {
+ if (UNLIKELY(!Runtime::Current()->IsStarted())) {
interpreter::UnstartedRuntime::Invoke(self, accessor, callee_frame, result, first_dest_reg);
+ return;
+ }
+
+ if (!EnsureInitialized(self, callee_frame)) {
+ return;
+ }
+
+ if (use_interpreter_entrypoint) {
+ interpreter::ArtInterpreterToInterpreterBridge(self, accessor, callee_frame, result);
+ } else {
+ interpreter::ArtInterpreterToCompiledCodeBridge(
+ self, caller_method, callee_frame, first_dest_reg, result);
}
}
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index a48d860..0e8a962 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -63,7 +63,7 @@
#include "runtime_intrinsics.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -158,17 +158,11 @@
{
ScopedObjectAccess soa(Thread::Current());
+ runtime_->GetClassLinker()->RunEarlyRootClinits(soa.Self());
+ InitializeIntrinsics();
runtime_->RunRootClinits(soa.Self());
}
- // We're back in native, take the opportunity to initialize well known classes and ensure
- // intrinsics are initialized.
- WellKnownClasses::Init(Thread::Current()->GetJniEnv());
- InitializeIntrinsics();
-
- // Create the heap thread pool so that the GC runs in parallel for tests. Normally, the thread
- // pool is created by the runtime.
- runtime_->GetHeap()->CreateThreadPool();
runtime_->GetHeap()->VerifyHeap(); // Check for heap corruption before the test
// Reduce timinig-dependent flakiness in OOME behavior (eg StubTest.AllocObject).
runtime_->GetHeap()->SetMinIntervalHomogeneousSpaceCompactionByOom(0U);
@@ -198,20 +192,17 @@
StackHandleScope<1> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader = hs.NewHandle(
soa.Decode<mirror::ClassLoader>(jclass_loader));
- return GetDexFiles(soa, class_loader);
+ return GetDexFiles(soa.Self(), class_loader);
}
std::vector<const DexFile*> CommonRuntimeTestImpl::GetDexFiles(
- ScopedObjectAccess& soa,
+ Thread* self,
Handle<mirror::ClassLoader> class_loader) {
- DCHECK(
- (class_loader->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader)) ||
- (class_loader->GetClass() ==
- soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DelegateLastClassLoader)));
+ DCHECK((class_loader->GetClass() == WellKnownClasses::dalvik_system_PathClassLoader) ||
+ (class_loader->GetClass() == WellKnownClasses::dalvik_system_DelegateLastClassLoader));
std::vector<const DexFile*> ret;
- VisitClassLoaderDexFiles(soa,
+ VisitClassLoaderDexFiles(self,
class_loader,
[&](const DexFile* cp_dex_file) {
if (cp_dex_file == nullptr) {
@@ -262,8 +253,9 @@
}
jobject
-CommonRuntimeTestImpl::LoadDexInWellKnownClassLoader(const std::vector<std::string>& dex_names,
- jclass loader_class,
+CommonRuntimeTestImpl::LoadDexInWellKnownClassLoader(ScopedObjectAccess& soa,
+ const std::vector<std::string>& dex_names,
+ ObjPtr<mirror::Class> loader_class,
jobject parent_loader,
jobject shared_libraries,
jobject shared_libraries_after) {
@@ -276,39 +268,44 @@
loaded_dex_files_.push_back(std::move(dex_file));
}
}
- Thread* self = Thread::Current();
- ScopedObjectAccess soa(self);
+ StackHandleScope<4> hs(soa.Self());
+ Handle<mirror::Class> h_loader_class = hs.NewHandle(loader_class);
+ Handle<mirror::ClassLoader> h_parent_loader =
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(parent_loader));
+ Handle<mirror::ObjectArray<mirror::ClassLoader>> h_shared_libraries =
+ hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::ClassLoader>>(shared_libraries));
+ Handle<mirror::ObjectArray<mirror::ClassLoader>> h_shared_libraries_after =
+ hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::ClassLoader>>(shared_libraries_after));
- jobject result = Runtime::Current()->GetClassLinker()->CreateWellKnownClassLoader(
- self,
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ObjPtr<mirror::ClassLoader> result = class_linker->CreateWellKnownClassLoader(
+ soa.Self(),
class_path,
- loader_class,
- parent_loader,
- shared_libraries,
- shared_libraries_after);
+ h_loader_class,
+ h_parent_loader,
+ h_shared_libraries,
+ h_shared_libraries_after);
{
// Verify we build the correct chain.
- ObjPtr<mirror::ClassLoader> actual_class_loader = soa.Decode<mirror::ClassLoader>(result);
// Verify that the result has the correct class.
- CHECK_EQ(soa.Decode<mirror::Class>(loader_class), actual_class_loader->GetClass());
+ CHECK_EQ(h_loader_class.Get(), result->GetClass());
// Verify that the parent is not null. The boot class loader will be set up as a
// proper object.
- ObjPtr<mirror::ClassLoader> actual_parent(actual_class_loader->GetParent());
+ ObjPtr<mirror::ClassLoader> actual_parent(result->GetParent());
CHECK(actual_parent != nullptr);
if (parent_loader != nullptr) {
// We were given a parent. Verify that it's what we expect.
- ObjPtr<mirror::ClassLoader> expected_parent = soa.Decode<mirror::ClassLoader>(parent_loader);
- CHECK_EQ(expected_parent, actual_parent);
+ CHECK_EQ(h_parent_loader.Get(), actual_parent);
} else {
// No parent given. The parent must be the BootClassLoader.
- CHECK(Runtime::Current()->GetClassLinker()->IsBootClassLoader(soa, actual_parent));
+ CHECK(class_linker->IsBootClassLoader(actual_parent));
}
}
- return result;
+ return soa.Env()->GetVm()->AddGlobalRef(soa.Self(), result);
}
jobject CommonRuntimeTestImpl::LoadDexInPathClassLoader(const std::string& dex_name,
@@ -325,8 +322,10 @@
jobject parent_loader,
jobject shared_libraries,
jobject shared_libraries_after) {
- return LoadDexInWellKnownClassLoader(names,
- WellKnownClasses::dalvik_system_PathClassLoader,
+ ScopedObjectAccess soa(Thread::Current());
+ return LoadDexInWellKnownClassLoader(soa,
+ names,
+ WellKnownClasses::dalvik_system_PathClassLoader.Get(),
parent_loader,
shared_libraries,
shared_libraries_after);
@@ -334,16 +333,22 @@
jobject CommonRuntimeTestImpl::LoadDexInDelegateLastClassLoader(const std::string& dex_name,
jobject parent_loader) {
- return LoadDexInWellKnownClassLoader({ dex_name },
- WellKnownClasses::dalvik_system_DelegateLastClassLoader,
- parent_loader);
+ ScopedObjectAccess soa(Thread::Current());
+ return LoadDexInWellKnownClassLoader(
+ soa,
+ { dex_name },
+ WellKnownClasses::dalvik_system_DelegateLastClassLoader.Get(),
+ parent_loader);
}
jobject CommonRuntimeTestImpl::LoadDexInInMemoryDexClassLoader(const std::string& dex_name,
jobject parent_loader) {
- return LoadDexInWellKnownClassLoader({ dex_name },
- WellKnownClasses::dalvik_system_InMemoryDexClassLoader,
- parent_loader);
+ ScopedObjectAccess soa(Thread::Current());
+ return LoadDexInWellKnownClassLoader(
+ soa,
+ { dex_name },
+ WellKnownClasses::dalvik_system_InMemoryDexClassLoader.Get(),
+ parent_loader);
}
void CommonRuntimeTestImpl::FillHeap(Thread* self,
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 9fa9c5d..7904aeb 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -152,6 +152,7 @@
jobject LoadMultiDex(const char* first_dex_name, const char* second_dex_name)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // The following helper functions return global JNI references to the class loader.
jobject LoadDexInPathClassLoader(const std::string& dex_name,
jobject parent_loader,
jobject shared_libraries = nullptr,
@@ -162,11 +163,13 @@
jobject shared_libraries_after = nullptr);
jobject LoadDexInDelegateLastClassLoader(const std::string& dex_name, jobject parent_loader);
jobject LoadDexInInMemoryDexClassLoader(const std::string& dex_name, jobject parent_loader);
- jobject LoadDexInWellKnownClassLoader(const std::vector<std::string>& dex_names,
- jclass loader_class,
+ jobject LoadDexInWellKnownClassLoader(ScopedObjectAccess& soa,
+ const std::vector<std::string>& dex_names,
+ ObjPtr<mirror::Class> loader_class,
jobject parent_loader,
jobject shared_libraries = nullptr,
- jobject shared_libraries_after = nullptr);
+ jobject shared_libraries_after = nullptr)
+ REQUIRES_SHARED(Locks::mutator_lock_);
void VisitDexes(ArrayRef<const std::string> dexes,
const std::function<void(MethodReference)>& method_visitor,
@@ -198,8 +201,7 @@
// Get the dex files from a PathClassLoader or DelegateLastClassLoader.
// This only looks into the current class loader and does not recurse into the parents.
std::vector<const DexFile*> GetDexFiles(jobject jclass_loader);
- std::vector<const DexFile*> GetDexFiles(ScopedObjectAccess& soa,
- Handle<mirror::ClassLoader> class_loader)
+ std::vector<const DexFile*> GetDexFiles(Thread* self, Handle<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_);
// Get the first dex file from a PathClassLoader. Will abort if it is null.
@@ -221,7 +223,7 @@
static std::string GetImageLocation();
static std::string GetSystemImageFile();
- static void EnterTransactionMode();
+ static void EnterTransactionMode() REQUIRES_SHARED(Locks::mutator_lock_);
static void ExitTransactionMode();
static void RollbackAndExitTransactionMode() REQUIRES_SHARED(Locks::mutator_lock_);
static bool IsTransactionAborted();
@@ -298,14 +300,8 @@
return; \
}
-#define TEST_DISABLED_FOR_STRING_COMPRESSION() \
- if (mirror::kUseStringCompression) { \
- printf("WARNING: TEST DISABLED FOR STRING COMPRESSION\n"); \
- return; \
- }
-
#define TEST_DISABLED_WITHOUT_BAKER_READ_BARRIERS() \
- if (!kEmitCompilerReadBarrier || !kUseBakerReadBarrier) { \
+ if (!gUseReadBarrier || !kUseBakerReadBarrier) { \
printf("WARNING: TEST DISABLED FOR GC WITHOUT BAKER READ BARRIER\n"); \
return; \
}
@@ -317,7 +313,7 @@
}
#define TEST_DISABLED_FOR_MEMORY_TOOL_WITH_HEAP_POISONING_WITHOUT_READ_BARRIERS() \
- if (kRunningOnMemoryTool && kPoisonHeapReferences && !kEmitCompilerReadBarrier) { \
+ if (kRunningOnMemoryTool && kPoisonHeapReferences && !gUseReadBarrier) { \
printf("WARNING: TEST DISABLED FOR MEMORY TOOL WITH HEAP POISONING WITHOUT READ BARRIERS\n"); \
return; \
}
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 17a0a8a..5182689 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -29,14 +29,14 @@
#include "dex/dex_file-inl.h"
#include "dex/dex_instruction-inl.h"
#include "dex/invoke_type.h"
-#include "mirror/class-inl.h"
+#include "mirror/class-alloc-inl.h"
#include "mirror/method_type.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "nativehelper/scoped_local_ref.h"
#include "obj_ptr-inl.h"
#include "thread.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -152,7 +152,6 @@
// ClassCastException
void ThrowClassCastException(ObjPtr<mirror::Class> dest_type, ObjPtr<mirror::Class> src_type) {
- DumpB77342775DebugData(dest_type, src_type);
ThrowException("Ljava/lang/ClassCastException;", nullptr,
StringPrintf("%s cannot be cast to %s",
mirror::Class::PrettyDescriptor(src_type).c_str(),
@@ -238,6 +237,19 @@
va_end(args);
}
+void ThrowIllegalAccessErrorForImplementingMethod(ObjPtr<mirror::Class> klass,
+ ArtMethod* implementation_method,
+ ArtMethod* interface_method)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(!implementation_method->IsAbstract());
+ DCHECK(!implementation_method->IsPublic());
+ ThrowIllegalAccessError(
+ klass,
+ "Method '%s' implementing interface method '%s' is not public",
+ implementation_method->PrettyMethod().c_str(),
+ interface_method->PrettyMethod().c_str());
+}
+
// IllegalAccessException
void ThrowIllegalAccessException(const char* msg) {
@@ -281,7 +293,6 @@
<< "' does not implement interface '"
<< mirror::Class::PrettyDescriptor(interface_method->GetDeclaringClass())
<< "' in call to '" << ArtMethod::PrettyMethod(interface_method) << "'";
- DumpB77342775DebugData(interface_method->GetDeclaringClass(), this_object->GetClass());
ThrowException("Ljava/lang/IncompatibleClassChangeError;",
referrer != nullptr ? referrer->GetDeclaringClass() : nullptr,
msg.str().c_str());
@@ -437,7 +448,7 @@
}
static bool IsValidReadBarrierImplicitCheck(uintptr_t addr) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
uint32_t monitor_offset = mirror::Object::MonitorOffset().Uint32Value();
if (kUseBakerReadBarrier &&
(kRuntimeISA == InstructionSet::kX86 || kRuntimeISA == InstructionSet::kX86_64)) {
@@ -472,7 +483,7 @@
}
case Instruction::IGET_OBJECT:
- if (kEmitCompilerReadBarrier && IsValidReadBarrierImplicitCheck(addr)) {
+ if (gUseReadBarrier && IsValidReadBarrierImplicitCheck(addr)) {
return true;
}
FALLTHROUGH_INTENDED;
@@ -496,7 +507,7 @@
}
case Instruction::AGET_OBJECT:
- if (kEmitCompilerReadBarrier && IsValidReadBarrierImplicitCheck(addr)) {
+ if (gUseReadBarrier && IsValidReadBarrierImplicitCheck(addr)) {
return true;
}
FALLTHROUGH_INTENDED;
@@ -690,20 +701,25 @@
}
self->SetStackEndForStackOverflow(); // Allow space on the stack for constructor to execute.
- JNIEnvExt* env = self->GetJniEnv();
- std::string msg("stack size ");
- msg += PrettySize(self->GetStackSize());
// Avoid running Java code for exception initialization.
// TODO: Checks to make this a bit less brittle.
//
- // Note: this lambda ensures that the destruction of the ScopedLocalRefs will run in the extended
- // stack, which is important for modes with larger stack sizes (e.g., ASAN). Using a lambda
- // instead of a block simplifies the control flow.
- auto create_and_throw = [&]() REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Note: This lambda is used to make sure the `StackOverflowError` intitialization code
+ // does not increase the frame size of `ThrowStackOverflowError()` itself. It runs
+ // with its own frame in the extended stack, which is especially important for modes
+ // with larger stack sizes (e.g., ASAN).
+ auto create_and_throw = [self]() REQUIRES_SHARED(Locks::mutator_lock_) NO_INLINE {
+ std::string msg("stack size ");
+ msg += PrettySize(self->GetStackSize());
+
+ ScopedObjectAccessUnchecked soa(self);
+ StackHandleScope<1u> hs(self);
+
// Allocate an uninitialized object.
- ScopedLocalRef<jobject> exc(env,
- env->AllocObject(WellKnownClasses::java_lang_StackOverflowError));
+ DCHECK(WellKnownClasses::java_lang_StackOverflowError->IsInitialized());
+ Handle<mirror::Object> exc = hs.NewHandle(
+ WellKnownClasses::java_lang_StackOverflowError->AllocObject(self));
if (exc == nullptr) {
LOG(WARNING) << "Could not allocate StackOverflowError object.";
return;
@@ -722,53 +738,54 @@
// fillInStackTrace();
// detailMessage.
- // TODO: Use String::FromModifiedUTF...?
- ScopedLocalRef<jstring> s(env, env->NewStringUTF(msg.c_str()));
- if (s == nullptr) {
- LOG(WARNING) << "Could not throw new StackOverflowError because JNI NewStringUTF failed.";
- return;
+ {
+ ObjPtr<mirror::String> s = mirror::String::AllocFromModifiedUtf8(self, msg.c_str());
+ if (s == nullptr) {
+ LOG(WARNING) << "Could not throw new StackOverflowError because message allocation failed.";
+ return;
+ }
+ WellKnownClasses::java_lang_Throwable_detailMessage
+ ->SetObject</*kTransactionActive=*/ false>(exc.Get(), s);
}
- env->SetObjectField(exc.get(), WellKnownClasses::java_lang_Throwable_detailMessage, s.get());
-
// cause.
- env->SetObjectField(exc.get(), WellKnownClasses::java_lang_Throwable_cause, exc.get());
+ WellKnownClasses::java_lang_Throwable_cause
+ ->SetObject</*kTransactionActive=*/ false>(exc.Get(), exc.Get());
// suppressedExceptions.
- ScopedLocalRef<jobject> emptylist(env, env->GetStaticObjectField(
- WellKnownClasses::java_util_Collections,
- WellKnownClasses::java_util_Collections_EMPTY_LIST));
- CHECK(emptylist != nullptr);
- env->SetObjectField(exc.get(),
- WellKnownClasses::java_lang_Throwable_suppressedExceptions,
- emptylist.get());
+ {
+ ObjPtr<mirror::Class> j_u_c = WellKnownClasses::java_util_Collections.Get();
+ DCHECK(j_u_c->IsInitialized());
+ ObjPtr<mirror::Object> empty_list =
+ WellKnownClasses::java_util_Collections_EMPTY_LIST->GetObject(j_u_c);
+ CHECK(empty_list != nullptr);
+ WellKnownClasses::java_lang_Throwable_suppressedExceptions
+ ->SetObject</*kTransactionActive=*/ false>(exc.Get(), empty_list);
+ }
// stackState is set as result of fillInStackTrace. fillInStackTrace calls
// nativeFillInStackTrace.
- ScopedLocalRef<jobject> stack_state_val(env, nullptr);
- {
- ScopedObjectAccessUnchecked soa(env); // TODO: Is this necessary?
- stack_state_val.reset(soa.Self()->CreateInternalStackTrace(soa));
- }
+ ObjPtr<mirror::Object> stack_state_val =
+ soa.Decode<mirror::Object>(self->CreateInternalStackTrace(soa));
if (stack_state_val != nullptr) {
- env->SetObjectField(exc.get(),
- WellKnownClasses::java_lang_Throwable_stackState,
- stack_state_val.get());
+ WellKnownClasses::java_lang_Throwable_stackState
+ ->SetObject</*kTransactionActive=*/ false>(exc.Get(), stack_state_val);
// stackTrace.
- ScopedLocalRef<jobject> stack_trace_elem(env, env->GetStaticObjectField(
- WellKnownClasses::libcore_util_EmptyArray,
- WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT));
- env->SetObjectField(exc.get(),
- WellKnownClasses::java_lang_Throwable_stackTrace,
- stack_trace_elem.get());
+ ObjPtr<mirror::Class> l_u_ea = WellKnownClasses::libcore_util_EmptyArray.Get();
+ DCHECK(l_u_ea->IsInitialized());
+ ObjPtr<mirror::Object> empty_ste =
+ WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT->GetObject(l_u_ea);
+ CHECK(empty_ste != nullptr);
+ WellKnownClasses::java_lang_Throwable_stackTrace
+ ->SetObject</*kTransactionActive=*/ false>(exc.Get(), empty_ste);
} else {
LOG(WARNING) << "Could not create stack trace.";
// Note: we'll create an exception without stack state, which is valid.
}
// Throw the exception.
- self->SetException(self->DecodeJObject(exc.get())->AsThrowable());
+ self->SetException(exc->AsThrowable());
};
create_and_throw();
CHECK(self->IsExceptionPending());
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index 843c455..d9620df 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -111,6 +111,11 @@
__attribute__((__format__(__printf__, 2, 3)))
REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+void ThrowIllegalAccessErrorForImplementingMethod(ObjPtr<mirror::Class> klass,
+ ArtMethod* implementation_method,
+ ArtMethod* interface_method)
+ REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
+
// IllegalAccessException
void ThrowIllegalAccessException(const char* msg)
diff --git a/runtime/compiler_callbacks.h b/runtime/compiler_callbacks.h
index c71d4ac..f76ee66 100644
--- a/runtime/compiler_callbacks.h
+++ b/runtime/compiler_callbacks.h
@@ -48,6 +48,7 @@
virtual ~CompilerCallbacks() { }
virtual void AddUncompilableMethod(MethodReference ref) = 0;
+ virtual void AddUncompilableClass(ClassReference ref) = 0;
virtual void ClassRejected(ClassReference ref) = 0;
virtual verifier::VerifierDeps* GetVerifierDeps() const = 0;
diff --git a/runtime/debug_print.cc b/runtime/debug_print.cc
index cde4d86..fd9e050 100644
--- a/runtime/debug_print.cc
+++ b/runtime/debug_print.cc
@@ -29,7 +29,7 @@
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -63,12 +63,10 @@
std::string DescribeLoaders(ObjPtr<mirror::ClassLoader> loader, const char* class_descriptor) {
std::ostringstream oss;
uint32_t hash = ComputeModifiedUtf8Hash(class_descriptor);
- ObjPtr<mirror::Class> path_class_loader =
- WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_PathClassLoader);
- ObjPtr<mirror::Class> dex_class_loader =
- WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DexClassLoader);
+ ObjPtr<mirror::Class> path_class_loader = WellKnownClasses::dalvik_system_PathClassLoader.Get();
+ ObjPtr<mirror::Class> dex_class_loader = WellKnownClasses::dalvik_system_DexClassLoader.Get();
ObjPtr<mirror::Class> delegate_last_class_loader =
- WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DelegateLastClassLoader);
+ WellKnownClasses::dalvik_system_DelegateLastClassLoader.Get();
// Print the class loader chain.
bool found_class = false;
@@ -97,13 +95,13 @@
loader->GetClass() == dex_class_loader ||
loader->GetClass() == delegate_last_class_loader) {
oss << "(";
- ScopedObjectAccessUnchecked soa(Thread::Current());
- StackHandleScope<1> hs(soa.Self());
+ Thread* self = Thread::Current();
+ StackHandleScope<1> hs(self);
Handle<mirror::ClassLoader> handle(hs.NewHandle(loader));
const char* path_separator = "";
const DexFile* base_dex_file = nullptr;
VisitClassLoaderDexFiles(
- soa,
+ self,
handle,
[&](const DexFile* dex_file) {
oss << path_separator;
@@ -129,60 +127,4 @@
return oss.str();
}
-void DumpB77342775DebugData(ObjPtr<mirror::Class> target_class, ObjPtr<mirror::Class> src_class) {
- std::string target_descriptor_storage;
- const char* target_descriptor = target_class->GetDescriptor(&target_descriptor_storage);
- const char kCheckedPrefix[] = "Lorg/apache/http/";
- // Avoid spam for other packages. (That spam would break some ART run-tests for example.)
- if (strncmp(target_descriptor, kCheckedPrefix, sizeof(kCheckedPrefix) - 1) != 0) {
- return;
- }
- auto matcher = [target_descriptor, target_class](ObjPtr<mirror::Class> klass)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (klass->DescriptorEquals(target_descriptor)) {
- LOG(ERROR) << " descriptor match in "
- << DescribeLoaders(klass->GetClassLoader(), target_descriptor)
- << " match? " << std::boolalpha << (klass == target_class);
- }
- };
-
- std::string source_descriptor_storage;
- const char* source_descriptor = src_class->GetDescriptor(&source_descriptor_storage);
-
- LOG(ERROR) << "Maybe bug 77342775, looking for " << target_descriptor
- << " " << target_class.Ptr() << "[" << DescribeSpace(target_class) << "]"
- << " defined in " << target_class->GetDexFile().GetLocation()
- << "/" << static_cast<const void*>(&target_class->GetDexFile())
- << "\n with loader: " << DescribeLoaders(target_class->GetClassLoader(), target_descriptor);
- if (target_class->IsInterface()) {
- ObjPtr<mirror::IfTable> iftable = src_class->GetIfTable();
- CHECK(iftable != nullptr);
- size_t ifcount = iftable->Count();
- LOG(ERROR) << " in interface table for " << source_descriptor
- << " " << src_class.Ptr() << "[" << DescribeSpace(src_class) << "]"
- << " defined in " << src_class->GetDexFile().GetLocation()
- << "/" << static_cast<const void*>(&src_class->GetDexFile())
- << " ifcount=" << ifcount
- << "\n with loader " << DescribeLoaders(src_class->GetClassLoader(), source_descriptor);
- for (size_t i = 0; i != ifcount; ++i) {
- ObjPtr<mirror::Class> iface = iftable->GetInterface(i);
- CHECK(iface != nullptr);
- LOG(ERROR) << " iface #" << i << ": " << iface->PrettyDescriptor();
- matcher(iface);
- }
- } else {
- LOG(ERROR) << " in superclass chain for " << source_descriptor
- << " " << src_class.Ptr() << "[" << DescribeSpace(src_class) << "]"
- << " defined in " << src_class->GetDexFile().GetLocation()
- << "/" << static_cast<const void*>(&src_class->GetDexFile())
- << "\n with loader " << DescribeLoaders(src_class->GetClassLoader(), source_descriptor);
- for (ObjPtr<mirror::Class> klass = src_class;
- klass != nullptr;
- klass = klass->GetSuperClass()) {
- LOG(ERROR) << " - " << klass->PrettyDescriptor();
- matcher(klass);
- }
- }
-}
-
} // namespace art
diff --git a/runtime/debug_print.h b/runtime/debug_print.h
index e2990d4..7c68402 100644
--- a/runtime/debug_print.h
+++ b/runtime/debug_print.h
@@ -29,9 +29,6 @@
std::string DescribeLoaders(ObjPtr<mirror::ClassLoader> loader, const char* class_descriptor)
REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
-void DumpB77342775DebugData(ObjPtr<mirror::Class> target_class, ObjPtr<mirror::Class> src_class)
- REQUIRES_SHARED(Locks::mutator_lock_) COLD_ATTR;
-
} // namespace art
#endif // ART_RUNTIME_DEBUG_PRINT_H_
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index af36531..a7b818e 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -187,33 +187,28 @@
const ArrayRef<const jbyte>& data,
/*out*/uint32_t* out_type,
/*out*/std::vector<uint8_t>* out_data) {
- ScopedLocalRef<jbyteArray> dataArray(env, env->NewByteArray(data.size()));
- if (dataArray.get() == nullptr) {
+ ScopedObjectAccess soa(env);
+ StackHandleScope<1u> hs(soa.Self());
+ Handle<mirror::ByteArray> data_array =
+ hs.NewHandle(mirror::ByteArray::Alloc(soa.Self(), data.size()));
+ if (data_array == nullptr) {
LOG(WARNING) << "byte[] allocation failed: " << data.size();
env->ExceptionClear();
return false;
}
- env->SetByteArrayRegion(dataArray.get(),
- 0,
- data.size(),
- reinterpret_cast<const jbyte*>(data.data()));
+ memcpy(data_array->GetData(), data.data(), data.size());
// Call "private static Chunk dispatch(int type, byte[] data, int offset, int length)".
- ScopedLocalRef<jobject> chunk(
- env,
- env->CallStaticObjectMethod(
- WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer,
- WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch,
- type, dataArray.get(), 0, data.size()));
- if (env->ExceptionCheck()) {
- Thread* self = Thread::Current();
- ScopedObjectAccess soa(self);
+ ArtMethod* dispatch = WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
+ ObjPtr<mirror::Object> chunk = dispatch->InvokeStatic<'L', 'I', 'L', 'I', 'I'>(
+ soa.Self(), type, data_array.Get(), 0, static_cast<jint>(data.size()));
+ if (soa.Self()->IsExceptionPending()) {
LOG(INFO) << StringPrintf("Exception thrown by dispatcher for 0x%08x", type) << std::endl
- << self->GetException()->Dump();
- self->ClearException();
+ << soa.Self()->GetException()->Dump();
+ soa.Self()->ClearException();
return false;
}
- if (chunk.get() == nullptr) {
+ if (chunk == nullptr) {
return false;
}
@@ -229,38 +224,33 @@
*
* So we're pretty much stuck with copying data around multiple times.
*/
- ScopedLocalRef<jbyteArray> replyData(
- env,
- reinterpret_cast<jbyteArray>(
- env->GetObjectField(
- chunk.get(), WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data)));
- jint offset = env->GetIntField(chunk.get(),
- WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_offset);
- jint length = env->GetIntField(chunk.get(),
- WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_length);
- *out_type = env->GetIntField(chunk.get(),
- WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_type);
+ ObjPtr<mirror::ByteArray> reply_data = ObjPtr<mirror::ByteArray>::DownCast(
+ WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data->GetObject(chunk));
+ jint offset = WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_offset->GetInt(chunk);
+ jint length = WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_length->GetInt(chunk);
+ *out_type = WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_type->GetInt(chunk);
VLOG(jdwp) << StringPrintf("DDM reply: type=0x%08x data=%p offset=%d length=%d",
type,
- replyData.get(),
+ reply_data.Ptr(),
offset,
length);
- out_data->resize(length);
- env->GetByteArrayRegion(replyData.get(),
- offset,
- length,
- reinterpret_cast<jbyte*>(out_data->data()));
- if (env->ExceptionCheck()) {
- Thread* self = Thread::Current();
- ScopedObjectAccess soa(self);
- LOG(INFO) << StringPrintf("Exception thrown when reading response data from dispatcher 0x%08x",
- type) << std::endl << self->GetException()->Dump();
- self->ClearException();
+ if (reply_data == nullptr) {
+ LOG(INFO) << "Null reply data";
return false;
}
+ jint reply_length = reply_data->GetLength();
+ if (offset < 0 || offset > reply_length || length < 0 || length > reply_length - offset) {
+ LOG(INFO) << "Invalid reply data range: offset=" << offset << ", length=" << length
+ << " reply_length=" << reply_length;
+ return false;
+ }
+
+ out_data->resize(length);
+ memcpy(out_data->data(), reply_data->GetData() + offset, length);
+
return true;
}
@@ -273,12 +263,13 @@
/* try anyway? */
}
+ // TODO: Can we really get here while not `Runnable`? If not, we do not need the `soa`.
+ ScopedObjectAccessUnchecked soa(self);
JNIEnv* env = self->GetJniEnv();
jint event = connect ? 1 /*DdmServer.CONNECTED*/ : 2 /*DdmServer.DISCONNECTED*/;
- env->CallStaticVoidMethod(WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer,
- WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast,
- event);
- if (env->ExceptionCheck()) {
+ ArtMethod* broadcast = WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
+ broadcast->InvokeStatic<'V', 'I'>(self, event);
+ if (self->IsExceptionPending()) {
LOG(ERROR) << "DdmServer.broadcast " << event << " failed";
env->ExceptionDescribe();
env->ExceptionClear();
diff --git a/runtime/deoptimization_kind.h b/runtime/deoptimization_kind.h
index c2e6a65..65a1cf1 100644
--- a/runtime/deoptimization_kind.h
+++ b/runtime/deoptimization_kind.h
@@ -56,9 +56,10 @@
// for functions that are already on stack. The value in the slot specifies the
// reason we need to deoptimize.
enum class DeoptimizeFlagValue: uint8_t {
- kCHA = 0b01,
- kDebug = 0b10,
- kAll = kCHA | kDebug
+ kCHA = 0b001,
+ kForceDeoptForRedefinition = 0b010,
+ kCheckCallerForDeopt = 0b100,
+ kAll = kCHA | kForceDeoptForRedefinition | kCheckCallerForDeopt
};
} // namespace art
diff --git a/runtime/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc
index 5a409f0..7c461fd 100644
--- a/runtime/dex/dex_file_annotations.cc
+++ b/runtime/dex/dex_file_annotations.cc
@@ -21,7 +21,7 @@
#include "android-base/stringprintf.h"
#include "art_field-inl.h"
-#include "art_method-inl.h"
+#include "art_method-alloc-inl.h"
#include "base/sdk_version.h"
#include "class_linker-inl.h"
#include "class_root-inl.h"
@@ -356,7 +356,6 @@
uint32_t size = DecodeUnsignedLeb128(annotation);
Thread* self = Thread::Current();
- ScopedObjectAccessUnchecked soa(self);
StackHandleScope<4> hs(self);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
Handle<mirror::Class> annotation_class(hs.NewHandle(
@@ -371,10 +370,8 @@
return nullptr;
}
- ObjPtr<mirror::Class> annotation_member_class =
- soa.Decode<mirror::Class>(WellKnownClasses::libcore_reflect_AnnotationMember);
ObjPtr<mirror::Class> annotation_member_array_class =
- class_linker->FindArrayClass(self, annotation_member_class);
+ WellKnownClasses::ToClass(WellKnownClasses::libcore_reflect_AnnotationMember__array);
if (annotation_member_array_class == nullptr) {
return nullptr;
}
@@ -397,18 +394,16 @@
h_element_array->SetWithoutChecks<false>(i, new_member);
}
- JValue result;
ArtMethod* create_annotation_method =
- jni::DecodeArtMethod(WellKnownClasses::libcore_reflect_AnnotationFactory_createAnnotation);
- uint32_t args[2] = { static_cast<uint32_t>(reinterpret_cast<uintptr_t>(annotation_class.Get())),
- static_cast<uint32_t>(reinterpret_cast<uintptr_t>(h_element_array.Get())) };
- create_annotation_method->Invoke(self, args, sizeof(args), &result, "LLL");
+ WellKnownClasses::libcore_reflect_AnnotationFactory_createAnnotation;
+ ObjPtr<mirror::Object> result = create_annotation_method->InvokeStatic<'L', 'L', 'L'>(
+ self, annotation_class.Get(), h_element_array.Get());
if (self->IsExceptionPending()) {
LOG(INFO) << "Exception in AnnotationFactory.createAnnotation";
return nullptr;
}
- return result.GetL();
+ return result;
}
template <bool kTransactionActive>
@@ -704,8 +699,6 @@
StackHandleScope<5> hs(self);
uint32_t element_name_index = DecodeUnsignedLeb128(annotation);
const char* name = dex_file.StringDataByIdx(dex::StringIndex(element_name_index));
- Handle<mirror::String> string_name(
- hs.NewHandle(mirror::String::AllocFromModifiedUtf8(self, name)));
PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
ArtMethod* annotation_method =
@@ -713,7 +706,19 @@
if (annotation_method == nullptr) {
return nullptr;
}
- Handle<mirror::Class> method_return(hs.NewHandle(annotation_method->ResolveReturnType()));
+
+ Handle<mirror::String> string_name =
+ hs.NewHandle(mirror::String::AllocFromModifiedUtf8(self, name));
+ if (UNLIKELY(string_name == nullptr)) {
+ LOG(ERROR) << "Failed to allocate name for annotation member";
+ return nullptr;
+ }
+
+ Handle<mirror::Class> method_return = hs.NewHandle(annotation_method->ResolveReturnType());
+ if (UNLIKELY(method_return == nullptr)) {
+ LOG(ERROR) << "Failed to resolve method return type for annotation member";
+ return nullptr;
+ }
DexFile::AnnotationValue annotation_value;
if (!ProcessAnnotationValue<false>(klass,
@@ -721,37 +726,26 @@
&annotation_value,
method_return,
DexFile::kAllObjects)) {
+ // TODO: Logging the error breaks run-test 005-annotations.
+ // LOG(ERROR) << "Failed to process annotation value for annotation member";
return nullptr;
}
- Handle<mirror::Object> value_object(hs.NewHandle(annotation_value.value_.GetL()));
+ Handle<mirror::Object> value_object = hs.NewHandle(annotation_value.value_.GetL());
- ObjPtr<mirror::Class> annotation_member_class =
- WellKnownClasses::ToClass(WellKnownClasses::libcore_reflect_AnnotationMember);
- Handle<mirror::Object> new_member(hs.NewHandle(annotation_member_class->AllocObject(self)));
- ObjPtr<mirror::Method> method_obj_ptr = (pointer_size == PointerSize::k64)
+ Handle<mirror::Method> method_object = hs.NewHandle((pointer_size == PointerSize::k64)
? mirror::Method::CreateFromArtMethod<PointerSize::k64>(self, annotation_method)
- : mirror::Method::CreateFromArtMethod<PointerSize::k32>(self, annotation_method);
- Handle<mirror::Method> method_object(hs.NewHandle(method_obj_ptr));
-
- if (new_member == nullptr || string_name == nullptr ||
- method_object == nullptr || method_return == nullptr) {
- LOG(ERROR) << StringPrintf("Failed creating annotation element (m=%p n=%p a=%p r=%p",
- new_member.Get(), string_name.Get(), method_object.Get(), method_return.Get());
+ : mirror::Method::CreateFromArtMethod<PointerSize::k32>(self, annotation_method));
+ if (UNLIKELY(method_object == nullptr)) {
+ LOG(ERROR) << "Failed to create method object for annotation member";
return nullptr;
}
- JValue result;
- ArtMethod* annotation_member_init =
- jni::DecodeArtMethod(WellKnownClasses::libcore_reflect_AnnotationMember_init);
- uint32_t args[5] = { static_cast<uint32_t>(reinterpret_cast<uintptr_t>(new_member.Get())),
- static_cast<uint32_t>(reinterpret_cast<uintptr_t>(string_name.Get())),
- static_cast<uint32_t>(reinterpret_cast<uintptr_t>(value_object.Get())),
- static_cast<uint32_t>(reinterpret_cast<uintptr_t>(method_return.Get())),
- static_cast<uint32_t>(reinterpret_cast<uintptr_t>(method_object.Get()))
- };
- annotation_member_init->Invoke(self, args, sizeof(args), &result, "VLLLL");
- if (self->IsExceptionPending()) {
- LOG(INFO) << "Exception in AnnotationMember.<init>";
+ Handle<mirror::Object> new_member =
+ WellKnownClasses::libcore_reflect_AnnotationMember_init->NewObject<'L', 'L', 'L', 'L'>(
+ hs, self, string_name, value_object, method_return, method_object);
+ if (new_member == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ LOG(ERROR) << "Failed to create annotation member";
return nullptr;
}
@@ -841,6 +835,38 @@
return annotation_value.value_.GetL();
}
+template<typename T>
+static inline ObjPtr<mirror::ObjectArray<T>> GetAnnotationArrayValue(
+ Handle<mirror::Class> klass,
+ const char* annotation_name,
+ const char* value_name)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ClassData data(klass);
+ const AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
+ if (annotation_set == nullptr) {
+ return nullptr;
+ }
+ const AnnotationItem* annotation_item =
+ SearchAnnotationSet(data.GetDexFile(), annotation_set, annotation_name,
+ DexFile::kDexVisibilitySystem);
+ if (annotation_item == nullptr) {
+ return nullptr;
+ }
+ StackHandleScope<1> hs(Thread::Current());
+ Handle<mirror::Class> class_array_class =
+ hs.NewHandle(GetClassRoot<mirror::ObjectArray<T>>());
+ DCHECK(class_array_class != nullptr);
+ ObjPtr<mirror::Object> obj = GetAnnotationValue(data,
+ annotation_item,
+ value_name,
+ class_array_class,
+ DexFile::kDexAnnotationArray);
+ if (obj == nullptr) {
+ return nullptr;
+ }
+ return obj->AsObjectArray<T>();
+}
+
static ObjPtr<mirror::ObjectArray<mirror::String>> GetSignatureValue(
const ClassData& klass,
const AnnotationSetItem* annotation_set)
@@ -895,10 +921,9 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
const DexFile& dex_file = klass.GetDexFile();
Thread* self = Thread::Current();
- ScopedObjectAccessUnchecked soa(self);
StackHandleScope<2> hs(self);
Handle<mirror::Class> annotation_array_class(hs.NewHandle(
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_annotation_Annotation__array)));
+ WellKnownClasses::ToClass(WellKnownClasses::java_lang_annotation_Annotation__array)));
if (annotation_set == nullptr) {
return mirror::ObjectArray<mirror::Object>::Alloc(self, annotation_array_class.Get(), 0);
}
@@ -953,10 +978,9 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
const DexFile& dex_file = klass.GetDexFile();
Thread* self = Thread::Current();
- ScopedObjectAccessUnchecked soa(self);
StackHandleScope<1> hs(self);
ObjPtr<mirror::Class> annotation_array_class =
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_annotation_Annotation__array);
+ WellKnownClasses::ToClass(WellKnownClasses::java_lang_annotation_Annotation__array);
ObjPtr<mirror::Class> annotation_array_array_class =
Runtime::Current()->GetClassLinker()->FindArrayClass(self, annotation_array_class);
if (annotation_array_array_class == nullptr) {
@@ -1310,6 +1334,20 @@
WellKnownClasses::dalvik_annotation_optimization_NeverCompile);
}
+bool MethodIsNeverInline(const DexFile& dex_file,
+ const dex::ClassDef& class_def,
+ uint32_t method_index) {
+ const dex::AnnotationSetItem* annotation_set =
+ FindAnnotationSetForMethod(dex_file, class_def, method_index);
+ if (annotation_set == nullptr) {
+ return false;
+ }
+ return IsMethodBuildAnnotationPresent(
+ dex_file,
+ *annotation_set,
+ "Ldalvik/annotation/optimization/NeverInline;",
+ WellKnownClasses::dalvik_annotation_optimization_NeverInline);
+}
bool FieldIsReachabilitySensitive(const DexFile& dex_file,
const dex::ClassDef& class_def,
@@ -1478,28 +1516,9 @@
}
ObjPtr<mirror::ObjectArray<mirror::Class>> GetDeclaredClasses(Handle<mirror::Class> klass) {
- ClassData data(klass);
- const AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
- if (annotation_set == nullptr) {
- return nullptr;
- }
- const AnnotationItem* annotation_item =
- SearchAnnotationSet(data.GetDexFile(), annotation_set, "Ldalvik/annotation/MemberClasses;",
- DexFile::kDexVisibilitySystem);
- if (annotation_item == nullptr) {
- return nullptr;
- }
- StackHandleScope<1> hs(Thread::Current());
- Handle<mirror::Class> class_array_class =
- hs.NewHandle(GetClassRoot<mirror::ObjectArray<mirror::Class>>());
- DCHECK(class_array_class != nullptr);
- ObjPtr<mirror::Object> obj =
- GetAnnotationValue(data, annotation_item, "value", class_array_class,
- DexFile::kDexAnnotationArray);
- if (obj == nullptr) {
- return nullptr;
- }
- return obj->AsObjectArray<mirror::Class>();
+ return GetAnnotationArrayValue<mirror::Class>(klass,
+ "Ldalvik/annotation/MemberClasses;",
+ "value");
}
ObjPtr<mirror::Class> GetDeclaringClass(Handle<mirror::Class> klass) {
@@ -1714,6 +1733,46 @@
return data.GetDexFile().StringDataByIdx(index);
}
+ObjPtr<mirror::Class> GetNestHost(Handle<mirror::Class> klass) {
+ ClassData data(klass);
+ const AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
+ if (annotation_set == nullptr) {
+ return nullptr;
+ }
+ const AnnotationItem* annotation_item =
+ SearchAnnotationSet(data.GetDexFile(), annotation_set, "Ldalvik/annotation/NestHost;",
+ DexFile::kDexVisibilitySystem);
+ if (annotation_item == nullptr) {
+ return nullptr;
+ }
+ ObjPtr<mirror::Object> obj = GetAnnotationValue(data,
+ annotation_item,
+ "host",
+ ScopedNullHandle<mirror::Class>(),
+ DexFile::kDexAnnotationType);
+ if (obj == nullptr) {
+ return nullptr;
+ }
+ if (!obj->IsClass()) {
+ // TypeNotPresentException, throw the NoClassDefFoundError.
+ Thread::Current()->SetException(obj->AsThrowable()->GetCause());
+ return nullptr;
+ }
+ return obj->AsClass();
+}
+
+ObjPtr<mirror::ObjectArray<mirror::Class>> GetNestMembers(Handle<mirror::Class> klass) {
+ return GetAnnotationArrayValue<mirror::Class>(klass,
+ "Ldalvik/annotation/NestMembers;",
+ "classes");
+}
+
+ObjPtr<mirror::ObjectArray<mirror::Class>> GetPermittedSubclasses(Handle<mirror::Class> klass) {
+ return GetAnnotationArrayValue<mirror::Class>(klass,
+ "Ldalvik/annotation/PermittedSubclasses;",
+ "value");
+}
+
bool IsClassAnnotationPresent(Handle<mirror::Class> klass, Handle<mirror::Class> annotation_class) {
ClassData data(klass);
const AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
diff --git a/runtime/dex/dex_file_annotations.h b/runtime/dex/dex_file_annotations.h
index 3ef67e5..ea11e10 100644
--- a/runtime/dex/dex_file_annotations.h
+++ b/runtime/dex/dex_file_annotations.h
@@ -91,6 +91,11 @@
bool MethodIsNeverCompile(const DexFile& dex_file,
const dex::ClassDef& class_def,
uint32_t method_index);
+// Is the method from the `dex_file` with the given `field_index`
+// annotated with @dalvik.annotation.optimization.NeverInline?
+bool MethodIsNeverInline(const DexFile& dex_file,
+ const dex::ClassDef& class_def,
+ uint32_t method_index);
// Is the field from the `dex_file` with the given `field_index`
// annotated with @dalvik.annotation.optimization.ReachabilitySensitive?
bool FieldIsReachabilitySensitive(const DexFile& dex_file,
@@ -136,6 +141,12 @@
Handle<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
const char* GetSourceDebugExtension(Handle<mirror::Class> klass)
REQUIRES_SHARED(Locks::mutator_lock_);
+ObjPtr<mirror::Class> GetNestHost(Handle<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ObjPtr<mirror::ObjectArray<mirror::Class>> GetNestMembers(Handle<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ObjPtr<mirror::ObjectArray<mirror::Class>> GetPermittedSubclasses(Handle<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_);
bool IsClassAnnotationPresent(Handle<mirror::Class> klass,
Handle<mirror::Class> annotation_class)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/dex2oat_environment_test.h b/runtime/dex2oat_environment_test.h
index e867c4f..bc25231 100644
--- a/runtime/dex2oat_environment_test.h
+++ b/runtime/dex2oat_environment_test.h
@@ -21,8 +21,6 @@
#include <string>
#include <vector>
-#include <gtest/gtest.h>
-
#include "base/file_utils.h"
#include "base/os.h"
#include "base/stl_util.h"
@@ -34,8 +32,10 @@
#include "exec_utils.h"
#include "gc/heap.h"
#include "gc/space/image_space.h"
+#include "gtest/gtest.h"
#include "oat_file_assistant.h"
#include "runtime.h"
+#include "ziparchive/zip_writer.h"
namespace art {
@@ -109,10 +109,9 @@
<< "Expected dex file to be at: " << GetDexSrc1();
ASSERT_TRUE(OS::FileExists(GetResourceOnlySrc1().c_str()))
<< "Expected stripped dex file to be at: " << GetResourceOnlySrc1();
- ASSERT_FALSE(
- dex_file_loader.GetMultiDexChecksums(
- GetResourceOnlySrc1().c_str(), &checksums, &dex_locations, &error_msg))
- << "Expected stripped dex file to be stripped: " << GetResourceOnlySrc1();
+ ASSERT_TRUE(dex_file_loader.GetMultiDexChecksums(
+ GetResourceOnlySrc1().c_str(), &checksums, &dex_locations, &error_msg))
+ << "Expected stripped dex file to be stripped: " << GetResourceOnlySrc1();
ASSERT_TRUE(OS::FileExists(GetDexSrc2().c_str()))
<< "Expected dex file to be at: " << GetDexSrc2();
@@ -243,6 +242,23 @@
return res.status_code;
}
+
+ void CreateDexMetadata(const std::string& vdex, const std::string& out_dm) {
+ // Read the vdex bytes.
+ std::unique_ptr<File> vdex_file(OS::OpenFileForReading(vdex.c_str()));
+ std::vector<uint8_t> data(vdex_file->GetLength());
+ ASSERT_TRUE(vdex_file->ReadFully(data.data(), data.size()));
+
+ // Zip the content.
+ FILE* file = fopen(out_dm.c_str(), "wb");
+ ZipWriter writer(file);
+ writer.StartEntry("primary.vdex", ZipWriter::kAlign32);
+ writer.WriteBytes(data.data(), data.size());
+ writer.FinishEntry();
+ writer.Finish();
+ fflush(file);
+ fclose(file);
+ }
};
} // namespace art
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc
index eb5b149..1f44a67 100644
--- a/runtime/dexopt_test.cc
+++ b/runtime/dexopt_test.cc
@@ -14,14 +14,17 @@
* limitations under the License.
*/
-#include <string>
-#include <vector>
+#include "dexopt_test.h"
#include <gtest/gtest.h>
#include <procinfo/process_map.h>
+#include <string>
+#include <vector>
+
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
+#include "arch/instruction_set.h"
#include "base/file_utils.h"
#include "base/mem_map.h"
#include "common_runtime_test.h"
@@ -29,10 +32,10 @@
#include "dex/art_dex_file_loader.h"
#include "dex/dex_file_loader.h"
#include "dex2oat_environment_test.h"
-#include "dexopt_test.h"
#include "gc/space/image_space.h"
#include "hidden_api.h"
#include "oat.h"
+#include "oat_file_assistant.h"
#include "profile/profile_compilation_info.h"
namespace art {
@@ -163,22 +166,13 @@
EXPECT_EQ(filter, odex_file->GetCompilerFilter());
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);
+ std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(/*spec=*/"");
+ OatFileAssistant oat_file_assistant(dex_location.c_str(),
+ kRuntimeISA,
+ context.get(),
+ /*load_executable=*/false);
- bool match = gc::space::ImageSpace::VerifyBootClassPathChecksums(
- checksums,
- oat_bcp,
- ArrayRef<const std::string>(&image_location, 1),
- ArrayRef<const std::string>(Runtime::Current()->GetBootClassPathLocations()),
- ArrayRef<const std::string>(Runtime::Current()->GetBootClassPath()),
- ArrayRef<const int>(Runtime::Current()->GetBootClassPathFds()),
- kRuntimeISA,
- &error_msg);
+ bool match = oat_file_assistant.ValidateBootClassPathChecksums(*odex_file);
ASSERT_EQ(!with_alternate_image, match) << error_msg;
}
}
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 4ee1013..91163f4 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -34,7 +34,6 @@
#include "imt_conflict_table.h"
#include "imtable-inl.h"
#include "indirect_reference_table.h"
-#include "jni/jni_internal.h"
#include "mirror/array-alloc-inl.h"
#include "mirror/class-alloc-inl.h"
#include "mirror/class-inl.h"
@@ -122,7 +121,7 @@
uint32_t method_index = code_info.GetMethodIndexOf(inline_info);
if (inline_info.GetDexPc() == static_cast<uint32_t>(-1)) {
// "charAt" special case. It is the only non-leaf method we inline across dex files.
- ArtMethod* inlined_method = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt);
+ ArtMethod* inlined_method = WellKnownClasses::java_lang_String_charAt;
DCHECK_EQ(inlined_method->GetDexMethodIndex(), method_index);
return inlined_method;
}
@@ -376,76 +375,77 @@
allocator_type);
}
-template<FindFieldType type, bool access_check>
+FLATTEN
+inline ArtField* ResolveFieldWithAccessChecks(Thread* self,
+ ClassLinker* class_linker,
+ uint16_t field_index,
+ ArtMethod* caller,
+ bool is_static,
+ bool is_put,
+ size_t resolve_field_type) // Resolve if not zero
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (caller->SkipAccessChecks()) {
+ return class_linker->ResolveField(field_index, caller, is_static);
+ }
+
+ caller = caller->GetInterfaceMethodIfProxy(class_linker->GetImagePointerSize());
+
+ StackHandleScope<2> hs(self);
+ Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(caller->GetDexCache()));
+ Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(caller->GetClassLoader()));
+
+ ArtField* resolved_field = class_linker->ResolveFieldJLS(field_index,
+ h_dex_cache,
+ h_class_loader);
+ if (resolved_field == nullptr) {
+ return nullptr;
+ }
+
+ ObjPtr<mirror::Class> fields_class = resolved_field->GetDeclaringClass();
+ if (UNLIKELY(resolved_field->IsStatic() != is_static)) {
+ ThrowIncompatibleClassChangeErrorField(resolved_field, is_static, caller);
+ return nullptr;
+ }
+ ObjPtr<mirror::Class> referring_class = caller->GetDeclaringClass();
+ if (UNLIKELY(!referring_class->CheckResolvedFieldAccess(fields_class,
+ resolved_field,
+ caller->GetDexCache(),
+ field_index))) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ if (UNLIKELY(is_put && !resolved_field->CanBeChangedBy(caller))) {
+ ThrowIllegalAccessErrorFinalField(caller, resolved_field);
+ return nullptr;
+ }
+
+ if (resolve_field_type != 0u) {
+ StackArtFieldHandleScope<1> rhs(self);
+ ReflectiveHandle<ArtField> field_handle(rhs.NewHandle(resolved_field));
+ if (resolved_field->ResolveType().IsNull()) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ resolved_field = field_handle.Get();
+ }
+ return resolved_field;
+}
+
+template<FindFieldType type>
inline ArtField* FindFieldFromCode(uint32_t field_idx,
ArtMethod* referrer,
Thread* self,
- size_t expected_size) {
- constexpr bool is_primitive = (type & FindFieldFlags::PrimitiveBit) != 0;
+ bool should_resolve_type = false) {
constexpr bool is_set = (type & FindFieldFlags::WriteBit) != 0;
constexpr bool is_static = (type & FindFieldFlags::StaticBit) != 0;
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-
- ArtField* resolved_field;
- if (access_check) {
- // Slow path: According to JLS 13.4.8, a linkage error may occur if a compile-time
- // qualifying type of a field and the resolved run-time qualifying type of a field differed
- // in their static-ness.
- //
- // In particular, don't assume the dex instruction already correctly knows if the
- // real field is static or not. The resolution must not be aware of this.
- ArtMethod* method = referrer->GetInterfaceMethodIfProxy(kRuntimePointerSize);
-
- StackHandleScope<2> hs(self);
- Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(method->GetDexCache()));
- Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(method->GetClassLoader()));
-
- resolved_field = class_linker->ResolveFieldJLS(field_idx,
- h_dex_cache,
- h_class_loader);
- } else {
- // Fast path: Verifier already would've called ResolveFieldJLS and we wouldn't
- // be executing here if there was a static/non-static mismatch.
- resolved_field = class_linker->ResolveField(field_idx, referrer, is_static);
- }
-
- if (UNLIKELY(resolved_field == nullptr)) {
- DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
- return nullptr; // Failure.
- }
- ObjPtr<mirror::Class> fields_class = resolved_field->GetDeclaringClass();
- if (access_check) {
- if (UNLIKELY(resolved_field->IsStatic() != is_static)) {
- ThrowIncompatibleClassChangeErrorField(resolved_field, is_static, referrer);
- return nullptr;
- }
- ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
- if (UNLIKELY(!referring_class->CheckResolvedFieldAccess(fields_class,
- resolved_field,
- referrer->GetDexCache(),
- field_idx))) {
- DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
- return nullptr; // Failure.
- }
- if (UNLIKELY(is_set && !resolved_field->CanBeChangedBy(referrer))) {
- ThrowIllegalAccessErrorFinalField(referrer, resolved_field);
- return nullptr; // Failure.
- } else {
- if (UNLIKELY(resolved_field->IsPrimitiveType() != is_primitive ||
- resolved_field->FieldSize() != expected_size)) {
- self->ThrowNewExceptionF("Ljava/lang/NoSuchFieldError;",
- "Attempted read of %zd-bit %s on field '%s'",
- expected_size * (32 / sizeof(int32_t)),
- is_primitive ? "primitive" : "non-primitive",
- resolved_field->PrettyField(true).c_str());
- return nullptr; // Failure.
- }
- }
- }
- if (!is_static) {
+ ArtField* resolved_field = ResolveFieldWithAccessChecks(
+ self, class_linker, field_idx, referrer, is_static, is_set, should_resolve_type ? 1u : 0u);
+ if (!is_static || resolved_field == nullptr) {
// instance fields must be being accessed on an initialized class
return resolved_field;
} else {
+ ObjPtr<mirror::Class> fields_class = resolved_field->GetDeclaringClass();
// If the class is initialized we're done.
if (LIKELY(fields_class->IsVisiblyInitialized())) {
return resolved_field;
@@ -463,28 +463,143 @@
}
}
+// NOLINTBEGIN(bugprone-macro-parentheses)
// Explicit template declarations of FindFieldFromCode for all field access types.
-#define EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \
+#define EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type) \
template REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE \
-ArtField* FindFieldFromCode<_type, _access_check>(uint32_t field_idx, \
- ArtMethod* referrer, \
- Thread* self, size_t expected_size) \
+ArtField* FindFieldFromCode<_type>(uint32_t field_idx, \
+ ArtMethod* referrer, \
+ Thread* self, \
+ bool should_resolve_type = false) \
-#define EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(_type) \
- EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, false); \
- EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(_type, true)
+EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(InstanceObjectRead);
+EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(InstanceObjectWrite);
+EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(InstancePrimitiveRead);
+EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(InstancePrimitiveWrite);
+EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(StaticObjectRead);
+EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(StaticObjectWrite);
+EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(StaticPrimitiveRead);
+EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL(StaticPrimitiveWrite);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstanceObjectRead);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstanceObjectWrite);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstancePrimitiveRead);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(InstancePrimitiveWrite);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticObjectRead);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticObjectWrite);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticPrimitiveRead);
-EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL(StaticPrimitiveWrite);
-
-#undef EXPLICIT_FIND_FIELD_FROM_CODE_TYPED_TEMPLATE_DECL
#undef EXPLICIT_FIND_FIELD_FROM_CODE_TEMPLATE_DECL
+// NOLINTEND(bugprone-macro-parentheses)
+
+static inline bool IsStringInit(const DexFile* dex_file, uint32_t method_idx)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ const dex::MethodId& method_id = dex_file->GetMethodId(method_idx);
+ const char* class_name = dex_file->StringByTypeIdx(method_id.class_idx_);
+ const char* method_name = dex_file->GetMethodName(method_id);
+ // Instead of calling ResolveMethod() which has suspend point and can trigger
+ // GC, look up the method symbolically.
+ // Compare method's class name and method name against string init.
+ // It's ok since it's not allowed to create your own java/lang/String.
+ // TODO: verify that assumption.
+ if ((strcmp(class_name, "Ljava/lang/String;") == 0) &&
+ (strcmp(method_name, "<init>") == 0)) {
+ return true;
+ }
+ return false;
+}
+
+static inline bool IsStringInit(const Instruction& instr, ArtMethod* caller)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (instr.Opcode() == Instruction::INVOKE_DIRECT ||
+ instr.Opcode() == Instruction::INVOKE_DIRECT_RANGE) {
+ uint16_t callee_method_idx = (instr.Opcode() == Instruction::INVOKE_DIRECT_RANGE) ?
+ instr.VRegB_3rc() : instr.VRegB_35c();
+ return IsStringInit(caller->GetDexFile(), callee_method_idx);
+ }
+ return false;
+}
+
+extern "C" size_t NterpGetMethod(Thread* self, ArtMethod* caller, const uint16_t* dex_pc_ptr);
+
+template <InvokeType type>
+ArtMethod* FindMethodToCall(Thread* self,
+ ArtMethod* caller,
+ ObjPtr<mirror::Object>* this_object,
+ const Instruction& inst,
+ /*out*/ bool* string_init)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+
+ // Try to find the method in thread-local cache.
+ size_t tls_value = 0u;
+ if (!self->GetInterpreterCache()->Get(self, &inst, &tls_value)) {
+ DCHECK(!self->IsExceptionPending());
+ // NterpGetMethod can suspend, so save this_object.
+ StackHandleScope<1> hs(self);
+ HandleWrapperObjPtr<mirror::Object> h_this(hs.NewHandleWrapper(this_object));
+ tls_value = NterpGetMethod(self, caller, reinterpret_cast<const uint16_t*>(&inst));
+ if (self->IsExceptionPending()) {
+ return nullptr;
+ }
+ }
+
+ if (type != kStatic && UNLIKELY((*this_object) == nullptr)) {
+ if (UNLIKELY(IsStringInit(inst, caller))) {
+ // Hack for String init:
+ //
+ // We assume that the input of String.<init> in verified code is always
+ // an uninitialized reference. If it is a null constant, it must have been
+ // optimized out by the compiler and we arrive here after deoptimization.
+ // Do not throw NullPointerException.
+ } else {
+ // Maintain interpreter-like semantics where NullPointerException is thrown
+ // after potential NoSuchMethodError from class linker.
+ const uint32_t method_idx = inst.VRegB();
+ ThrowNullPointerExceptionForMethodAccess(method_idx, type);
+ return nullptr;
+ }
+ }
+
+ static constexpr size_t kStringInitMethodFlag = 0b1;
+ static constexpr size_t kInvokeInterfaceOnObjectMethodFlag = 0b1;
+ static constexpr size_t kMethodMask = ~0b11;
+
+ ArtMethod* called_method = nullptr;
+ switch (type) {
+ case kDirect:
+ case kSuper:
+ case kStatic:
+ // Note: for the interpreter, the String.<init> special casing for invocation is handled
+ // in DoCallCommon.
+ *string_init = ((tls_value & kStringInitMethodFlag) != 0);
+ DCHECK_EQ(*string_init, IsStringInit(inst, caller));
+ called_method = reinterpret_cast<ArtMethod*>(tls_value & kMethodMask);
+ break;
+ case kInterface:
+ if ((tls_value & kInvokeInterfaceOnObjectMethodFlag) != 0) {
+ // invokeinterface on a j.l.Object method.
+ uint16_t method_index = tls_value >> 16;
+ called_method = (*this_object)->GetClass()->GetVTableEntry(method_index, pointer_size);
+ } else {
+ ArtMethod* interface_method = reinterpret_cast<ArtMethod*>(tls_value & kMethodMask);
+ called_method = (*this_object)->GetClass()->GetImt(pointer_size)->Get(
+ interface_method->GetImtIndex(), pointer_size);
+ if (called_method->IsRuntimeMethod()) {
+ called_method = (*this_object)->GetClass()->FindVirtualMethodForInterface(
+ interface_method, pointer_size);
+ if (UNLIKELY(called_method == nullptr)) {
+ ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(
+ interface_method, *this_object, caller);
+ return nullptr;
+ }
+ }
+ }
+ break;
+ case kVirtual:
+ called_method = (*this_object)->GetClass()->GetVTableEntry(tls_value, pointer_size);
+ break;
+ }
+
+ if (UNLIKELY(!called_method->IsInvokable())) {
+ called_method->ThrowInvocationTimeError((type == kStatic) ? nullptr : *this_object);
+ return nullptr;
+ }
+ DCHECK(!called_method->IsRuntimeMethod()) << called_method->PrettyMethod();
+ return called_method;
+}
template<bool access_check>
ALWAYS_INLINE ArtMethod* FindSuperMethodToCall(uint32_t method_idx,
@@ -546,130 +661,6 @@
return super_class->GetVTableEntry(vtable_index, linker->GetImagePointerSize());
}
-// Follow virtual/interface indirections if applicable.
-// Will throw null-pointer exception the if the object is null.
-template<InvokeType type, bool access_check>
-ALWAYS_INLINE ArtMethod* FindMethodToCall(uint32_t method_idx,
- ArtMethod* resolved_method,
- ObjPtr<mirror::Object>* this_object,
- ArtMethod* referrer,
- Thread* self)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
- // Null pointer check.
- if (UNLIKELY(*this_object == nullptr && type != kStatic)) {
- if (UNLIKELY(resolved_method->GetDeclaringClass()->IsStringClass() &&
- resolved_method->IsConstructor())) {
- // Hack for String init:
- //
- // We assume that the input of String.<init> in verified code is always
- // an unitialized reference. If it is a null constant, it must have been
- // optimized out by the compiler. Do not throw NullPointerException.
- } else {
- // Maintain interpreter-like semantics where NullPointerException is thrown
- // after potential NoSuchMethodError from class linker.
- ThrowNullPointerExceptionForMethodAccess(method_idx, type);
- return nullptr; // Failure.
- }
- }
- switch (type) {
- case kStatic:
- case kDirect:
- return resolved_method;
- case kVirtual: {
- ObjPtr<mirror::Class> klass = (*this_object)->GetClass();
- uint16_t vtable_index = resolved_method->GetMethodIndex();
- if (access_check &&
- (!klass->HasVTable() ||
- vtable_index >= static_cast<uint32_t>(klass->GetVTableLength()))) {
- // Behavior to agree with that of the verifier.
- ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(),
- resolved_method->GetName(), resolved_method->GetSignature());
- return nullptr; // Failure.
- }
- DCHECK(klass->HasVTable()) << klass->PrettyClass();
- return klass->GetVTableEntry(vtable_index, class_linker->GetImagePointerSize());
- }
- case kSuper: {
- return FindSuperMethodToCall<access_check>(method_idx, resolved_method, referrer, self);
- }
- case kInterface: {
- size_t imt_index = resolved_method->GetImtIndex();
- PointerSize pointer_size = class_linker->GetImagePointerSize();
- ObjPtr<mirror::Class> klass = (*this_object)->GetClass();
- ArtMethod* imt_method = klass->GetImt(pointer_size)->Get(imt_index, pointer_size);
- if (!imt_method->IsRuntimeMethod()) {
- if (kIsDebugBuild) {
- ArtMethod* method = klass->FindVirtualMethodForInterface(
- resolved_method, class_linker->GetImagePointerSize());
- CHECK_EQ(imt_method, method) << ArtMethod::PrettyMethod(resolved_method) << " / "
- << imt_method->PrettyMethod() << " / "
- << ArtMethod::PrettyMethod(method) << " / "
- << klass->PrettyClass();
- }
- return imt_method;
- } else {
- ArtMethod* interface_method = klass->FindVirtualMethodForInterface(
- resolved_method, class_linker->GetImagePointerSize());
- if (UNLIKELY(interface_method == nullptr)) {
- ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(resolved_method,
- *this_object, referrer);
- return nullptr; // Failure.
- }
- return interface_method;
- }
- }
- default:
- LOG(FATAL) << "Unknown invoke type " << type;
- return nullptr; // Failure.
- }
-}
-
-template<InvokeType type, bool access_check>
-inline ArtMethod* FindMethodFromCode(uint32_t method_idx,
- ObjPtr<mirror::Object>* this_object,
- ArtMethod* referrer,
- Thread* self) {
- ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
- constexpr ClassLinker::ResolveMode resolve_mode =
- access_check ? ClassLinker::ResolveMode::kCheckICCEAndIAE
- : ClassLinker::ResolveMode::kNoChecks;
- ArtMethod* resolved_method;
- if (type == kStatic) {
- resolved_method = class_linker->ResolveMethod<resolve_mode>(self, method_idx, referrer, type);
- } else {
- StackHandleScope<1> hs(self);
- HandleWrapperObjPtr<mirror::Object> h_this(hs.NewHandleWrapper(this_object));
- resolved_method = class_linker->ResolveMethod<resolve_mode>(self, method_idx, referrer, type);
- }
- if (UNLIKELY(resolved_method == nullptr)) {
- DCHECK(self->IsExceptionPending()); // Throw exception and unwind.
- return nullptr; // Failure.
- }
- return FindMethodToCall<type, access_check>(
- method_idx, resolved_method, this_object, referrer, self);
-}
-
-// Explicit template declarations of FindMethodFromCode for all invoke types.
-#define EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, _access_check) \
- template REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE \
- ArtMethod* FindMethodFromCode<_type, _access_check>(uint32_t method_idx, \
- ObjPtr<mirror::Object>* this_object, \
- ArtMethod* referrer, \
- Thread* self)
-#define EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(_type) \
- EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, false); \
- EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL(_type, true)
-
-EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kStatic);
-EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kDirect);
-EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kVirtual);
-EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kSuper);
-EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL(kInterface);
-
-#undef EXPLICIT_FIND_METHOD_FROM_CODE_TYPED_TEMPLATE_DECL
-#undef EXPLICIT_FIND_METHOD_FROM_CODE_TEMPLATE_DECL
-
inline ObjPtr<mirror::Class> ResolveVerifyAndClinit(dex::TypeIndex type_idx,
ArtMethod* referrer,
Thread* self,
@@ -724,13 +715,6 @@
}
}
-inline bool NeedsClinitCheckBeforeCall(ArtMethod* method) {
- // The class needs to be visibly initialized before we can use entrypoints to
- // compiled code for static methods. See b/18161648 . The class initializer is
- // special as it is invoked during initialization and does not need the check.
- return method->IsStatic() && !method->IsConstructor();
-}
-
inline ObjPtr<mirror::Object> GetGenericJniSynchronizationObject(Thread* self, ArtMethod* called)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(!called->IsCriticalNative());
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index 63d2aa4..ae6e98a 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -22,6 +22,7 @@
#include "base/mutex.h"
#include "base/sdk_version.h"
#include "class_linker-inl.h"
+#include "class_root-inl.h"
#include "dex/dex_file-inl.h"
#include "dex/method_reference.h"
#include "entrypoints/entrypoint_utils-inl.h"
@@ -33,14 +34,14 @@
#include "mirror/class-inl.h"
#include "mirror/method.h"
#include "mirror/object-inl.h"
-#include "mirror/object_array-inl.h"
+#include "mirror/object_array-alloc-inl.h"
#include "nth_caller_visitor.h"
#include "oat_file.h"
#include "oat_file-inl.h"
#include "oat_quick_method_header.h"
#include "reflection.h"
#include "scoped_thread_state_change-inl.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -65,60 +66,70 @@
jobject rcvr_jobj,
jobject interface_method_jobj,
std::vector<jvalue>& args) {
- DCHECK(soa.Env()->IsInstanceOf(rcvr_jobj, WellKnownClasses::java_lang_reflect_Proxy));
+ StackHandleScope<4u> hs(soa.Self());
+ DCHECK(rcvr_jobj != nullptr);
+ Handle<mirror::Object> h_receiver = hs.NewHandle(soa.Decode<mirror::Object>(rcvr_jobj));
+ DCHECK(h_receiver->InstanceOf(GetClassRoot(ClassRoot::kJavaLangReflectProxy)));
+ Handle<mirror::Method> h_interface_method =
+ hs.NewHandle(soa.Decode<mirror::Method>(interface_method_jobj));
// Build argument array possibly triggering GC.
soa.Self()->AssertThreadSuspensionIsAllowable();
- jobjectArray args_jobj = nullptr;
+ auto h_args = hs.NewHandle<mirror::ObjectArray<mirror::Object>>(nullptr);
const JValue zero;
- uint32_t target_sdk_version = Runtime::Current()->GetTargetSdkVersion();
+ Runtime* runtime = Runtime::Current();
+ uint32_t target_sdk_version = runtime->GetTargetSdkVersion();
// Do not create empty arrays unless needed to maintain Dalvik bug compatibility.
if (args.size() > 0 || IsSdkVersionSetAndAtMost(target_sdk_version, SdkVersion::kL)) {
- args_jobj = soa.Env()->NewObjectArray(args.size(), WellKnownClasses::java_lang_Object, nullptr);
- if (args_jobj == nullptr) {
+ h_args.Assign(mirror::ObjectArray<mirror::Object>::Alloc(
+ soa.Self(), GetClassRoot<mirror::ObjectArray<mirror::Object>>(), args.size()));
+ if (h_args == nullptr) {
CHECK(soa.Self()->IsExceptionPending());
return zero;
}
for (size_t i = 0; i < args.size(); ++i) {
+ ObjPtr<mirror::Object> value;
if (shorty[i + 1] == 'L') {
- jobject val = args[i].l;
- soa.Env()->SetObjectArrayElement(args_jobj, i, val);
+ value = soa.Decode<mirror::Object>(args[i].l);
} else {
JValue jv;
jv.SetJ(args[i].j);
- ObjPtr<mirror::Object> val = BoxPrimitive(Primitive::GetType(shorty[i + 1]), jv);
- if (val == nullptr) {
+ value = BoxPrimitive(Primitive::GetType(shorty[i + 1]), jv);
+ if (value == nullptr) {
CHECK(soa.Self()->IsExceptionPending());
return zero;
}
- soa.Decode<mirror::ObjectArray<mirror::Object>>(args_jobj)->Set<false>(i, val);
}
+ // We do not support `Proxy.invoke()` in a transaction.
+ h_args->SetWithoutChecks</*kActiveTransaction=*/ false>(i, value);
}
}
// Call Proxy.invoke(Proxy proxy, Method method, Object[] args).
- jvalue invocation_args[3];
- invocation_args[0].l = rcvr_jobj;
- invocation_args[1].l = interface_method_jobj;
- invocation_args[2].l = args_jobj;
- jobject result =
- soa.Env()->CallStaticObjectMethodA(WellKnownClasses::java_lang_reflect_Proxy,
- WellKnownClasses::java_lang_reflect_Proxy_invoke,
- invocation_args);
+ Handle<mirror::Object> h_result = hs.NewHandle(
+ WellKnownClasses::java_lang_reflect_Proxy_invoke->InvokeStatic<'L', 'L', 'L', 'L'>(
+ soa.Self(), h_receiver.Get(), h_interface_method.Get(), h_args.Get()));
// Unbox result and handle error conditions.
if (LIKELY(!soa.Self()->IsExceptionPending())) {
- if (shorty[0] == 'V' || (shorty[0] == 'L' && result == nullptr)) {
+ if (shorty[0] == 'V' || (shorty[0] == 'L' && h_result == nullptr)) {
// Do nothing.
return zero;
} else {
- ArtMethod* interface_method =
- soa.Decode<mirror::Method>(interface_method_jobj)->GetArtMethod();
- // This can cause thread suspension.
- ObjPtr<mirror::Class> result_type = interface_method->ResolveReturnType();
- ObjPtr<mirror::Object> result_ref = soa.Decode<mirror::Object>(result);
+ ObjPtr<mirror::Class> result_type;
+ if (shorty[0] == 'L') {
+ // This can cause thread suspension.
+ result_type = h_interface_method->GetArtMethod()->ResolveReturnType();
+ if (result_type == nullptr) {
+ DCHECK(soa.Self()->IsExceptionPending());
+ return zero;
+ }
+ } else {
+ result_type = runtime->GetClassLinker()->LookupPrimitiveClass(shorty[0]);
+ DCHECK(result_type != nullptr);
+ }
JValue result_unboxed;
- if (!UnboxPrimitiveForResult(result_ref, result_type, &result_unboxed)) {
+ if (!UnboxPrimitiveForResult(h_result.Get(), result_type, &result_unboxed)) {
DCHECK(soa.Self()->IsExceptionPending());
return zero;
}
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index 8b6fc69..777fd98 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -143,11 +143,12 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Roles::uninterruptible_);
-template<InvokeType type, bool access_check>
-inline ArtMethod* FindMethodFromCode(uint32_t method_idx,
- ObjPtr<mirror::Object>* this_object,
- ArtMethod* referrer,
- Thread* self)
+template<InvokeType type>
+inline ArtMethod* FindMethodToCall(Thread* self,
+ ArtMethod* referrer,
+ ObjPtr<mirror::Object>* this_object,
+ const Instruction& inst,
+ /*out*/ bool* string_init)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Roles::uninterruptible_);
@@ -203,10 +204,6 @@
ArtMethod* GetCalleeSaveOuterMethod(Thread* self, CalleeSaveType type)
REQUIRES_SHARED(Locks::mutator_lock_);
-// Returns whether we need to do class initialization check before invoking the method.
-// The caller is responsible for performing that check.
-bool NeedsClinitCheckBeforeCall(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
-
// Returns the synchronization object for a native method for a GenericJni frame
// we have just created or are about to exit. The synchronization object is
// the class object for static methods and the `this` object otherwise.
diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc
index c78b604..e606c21 100644
--- a/runtime/entrypoints/jni/jni_entrypoints.cc
+++ b/runtime/entrypoints/jni/jni_entrypoints.cc
@@ -75,7 +75,7 @@
// These calls do not have an explicit class initialization check, so do the check now.
// (When going through the stub or GenericJNI, the check was already done.)
- DCHECK(NeedsClinitCheckBeforeCall(target_method));
+ DCHECK(target_method->NeedsClinitCheckBeforeCall());
ObjPtr<mirror::Class> declaring_class = target_method->GetDeclaringClass();
if (UNLIKELY(!declaring_class->IsVisiblyInitialized())) {
StackHandleScope<1> hs(self);
@@ -87,11 +87,11 @@
}
// Replace the runtime method on the stack with the target method.
- DCHECK(!self->GetManagedStack()->GetTopQuickFrameTag());
+ DCHECK(!self->GetManagedStack()->GetTopQuickFrameGenericJniTag());
ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrameKnownNotTagged();
DCHECK(*sp == Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs));
*sp = target_method;
- self->SetTopOfStackTagged(sp); // Fake GenericJNI frame.
+ self->SetTopOfStackGenericJniTagged(sp); // Fake GenericJNI frame.
// Continue with the target method.
method = target_method;
diff --git a/runtime/entrypoints/math_entrypoints_test.cc b/runtime/entrypoints/math_entrypoints_test.cc
index f70a2da..fe61f5d 100644
--- a/runtime/entrypoints/math_entrypoints_test.cc
+++ b/runtime/entrypoints/math_entrypoints_test.cc
@@ -18,11 +18,11 @@
#include <limits>
-#include "common_runtime_test.h"
+#include "base/common_art_test.h"
namespace art {
-class MathEntrypointsTest : public CommonRuntimeTest {};
+class MathEntrypointsTest : public CommonArtTest {};
TEST_F(MathEntrypointsTest, DoubleToLong) {
EXPECT_EQ(std::numeric_limits<int64_t>::max(), art_d2l(1.85e19));
diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h
index f8856d8..cb3caac 100644
--- a/runtime/entrypoints/quick/quick_default_externs.h
+++ b/runtime/entrypoints/quick/quick_default_externs.h
@@ -122,6 +122,7 @@
extern "C" void art_jni_monitored_method_start();
extern "C" void art_jni_method_end();
extern "C" void art_jni_monitored_method_end();
+extern "C" void art_jni_method_entry_hook();
// JNI lock/unlock entrypoints. Note: Custom calling convention.
extern "C" void art_jni_lock_object(art::mirror::Object*);
diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
index 939feee..ea07788 100644
--- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
@@ -79,6 +79,7 @@
qpoints->SetQuickGenericJniTrampoline(art_quick_generic_jni_trampoline);
qpoints->SetJniDecodeReferenceResult(JniDecodeReferenceResult);
qpoints->SetJniReadBarrier(art_jni_read_barrier);
+ qpoints->SetJniMethodEntryHook(art_jni_method_entry_hook);
// Locks
if (UNLIKELY(VLOG_IS_ON(systrace_lock_logging))) {
diff --git a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
index d06dbcb..89d5c18 100644
--- a/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_deoptimization_entrypoints.cc
@@ -25,7 +25,10 @@
namespace art {
-NO_RETURN static void artDeoptimizeImpl(Thread* self, DeoptimizationKind kind, bool single_frame)
+NO_RETURN static void artDeoptimizeImpl(Thread* self,
+ DeoptimizationKind kind,
+ bool single_frame,
+ bool skip_method_exit_callbacks)
REQUIRES_SHARED(Locks::mutator_lock_) {
Runtime::Current()->IncrementDeoptimizationCount(kind);
if (VLOG_IS_ON(deopt)) {
@@ -43,7 +46,7 @@
if (single_frame) {
exception_handler.DeoptimizeSingleFrame(kind);
} else {
- exception_handler.DeoptimizeStack();
+ exception_handler.DeoptimizeStack(skip_method_exit_callbacks);
}
uintptr_t return_pc = exception_handler.UpdateInstrumentationStack();
if (exception_handler.IsFullFragmentDone()) {
@@ -57,9 +60,10 @@
}
}
-extern "C" NO_RETURN void artDeoptimize(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
+extern "C" NO_RETURN void artDeoptimize(Thread* self, bool skip_method_exit_callbacks)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
ScopedQuickEntrypointChecks sqec(self);
- artDeoptimizeImpl(self, DeoptimizationKind::kFullFrame, false);
+ artDeoptimizeImpl(self, DeoptimizationKind::kFullFrame, false, skip_method_exit_callbacks);
}
// This is called directly from compiled code by an HDeoptimize.
@@ -74,7 +78,9 @@
self->GetException(),
/* from_code= */ true,
DeoptimizationMethodType::kDefault);
- artDeoptimizeImpl(self, kind, true);
+ // Deopting from compiled code, so method exit haven't run yet. Don't skip method exit callbacks
+ // if required.
+ artDeoptimizeImpl(self, kind, true, /* skip_method_exit_callbacks= */ false);
}
} // namespace art
diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
index 60a5875..76bee21 100644
--- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
@@ -23,6 +23,7 @@
#include "dex/dex_file_types.h"
#include "entrypoints/entrypoint_utils-inl.h"
#include "gc/heap.h"
+#include "jvalue-inl.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "mirror/object-inl.h"
diff --git a/runtime/entrypoints/quick/quick_entrypoints.h b/runtime/entrypoints/quick/quick_entrypoints.h
index 7af1a0b..0e73c63 100644
--- a/runtime/entrypoints/quick/quick_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_entrypoints.h
@@ -67,6 +67,7 @@
// JNI entrypoints when monitoring entry/exit.
extern "C" void artJniMonitoredMethodStart(Thread* self) UNLOCK_FUNCTION(Locks::mutator_lock_);
extern "C" void artJniMonitoredMethodEnd(Thread* self) SHARED_LOCK_FUNCTION(Locks::mutator_lock_);
+extern "C" void artJniMethodEntryHook(Thread* self);
// StringAppend pattern entrypoint.
extern "C" mirror::String* artStringBuilderAppend(uint32_t format,
diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h
index dffaa4b..aa3360e 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_list.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_list.h
@@ -78,6 +78,7 @@
V(JniLockObject, void, mirror::Object*) \
V(JniUnlockObject, void, mirror::Object*) \
V(QuickGenericJniTrampoline, void, ArtMethod*) \
+ V(JniMethodEntryHook, void) \
\
V(LockObject, void, mirror::Object*) \
V(UnlockObject, void, mirror::Object*) \
@@ -150,6 +151,7 @@
\
V(NewEmptyString, void, void) \
V(NewStringFromBytes_B, void, void) \
+ V(NewStringFromBytes_BB, void, void) \
V(NewStringFromBytes_BI, void, void) \
V(NewStringFromBytes_BII, void, void) \
V(NewStringFromBytes_BIII, void, void) \
@@ -164,6 +166,7 @@
V(NewStringFromString, void, void) \
V(NewStringFromStringBuffer, void, void) \
V(NewStringFromStringBuilder, void, void) \
+ V(NewStringFromUtf16Bytes_BII, void, void) \
\
V(StringBuilderAppend, void*, uint32_t) \
\
diff --git a/runtime/entrypoints/quick/quick_field_entrypoints.cc b/runtime/entrypoints/quick/quick_field_entrypoints.cc
index d32aa39..e2fc232 100644
--- a/runtime/entrypoints/quick/quick_field_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_field_entrypoints.cc
@@ -32,7 +32,7 @@
inline ArtField* FindFieldFast(uint32_t field_idx,
ArtMethod* referrer,
FindFieldType type,
- size_t expected_size)
+ bool should_resolve_type = false)
REQUIRES(!Roles::uninterruptible_)
REQUIRES_SHARED(Locks::mutator_lock_) {
ScopedAssertNoThreadSuspension ants(__FUNCTION__);
@@ -41,7 +41,6 @@
return nullptr;
}
// Check for incompatible class change.
- const bool is_primitive = (type & FindFieldFlags::PrimitiveBit) != 0;
const bool is_set = (type & FindFieldFlags::WriteBit) != 0;
const bool is_static = (type & FindFieldFlags::StaticBit) != 0;
if (UNLIKELY(resolved_field->IsStatic() != is_static)) {
@@ -63,8 +62,7 @@
// Illegal access.
return nullptr;
}
- if (UNLIKELY(resolved_field->IsPrimitiveType() != is_primitive ||
- resolved_field->FieldSize() != expected_size)) {
+ if (should_resolve_type && resolved_field->LookupResolvedType() == nullptr) {
return nullptr;
}
return resolved_field;
@@ -73,17 +71,17 @@
// Helper function to do a null check after trying to resolve the field. Not for statics since obj
// does not exist there. There is a suspend check, object is a double pointer to update the value
// in the caller in case it moves.
-template<FindFieldType type, bool kAccessCheck>
+template<FindFieldType type>
ALWAYS_INLINE static inline ArtField* FindInstanceField(uint32_t field_idx,
ArtMethod* referrer,
Thread* self,
- size_t size,
- mirror::Object** obj)
+ mirror::Object** obj,
+ bool should_resolve_type = false)
REQUIRES(!Roles::uninterruptible_)
REQUIRES_SHARED(Locks::mutator_lock_) {
StackHandleScope<1> hs(self);
HandleWrapper<mirror::Object> h(hs.NewHandleWrapper(obj));
- ArtField* field = FindFieldFromCode<type, kAccessCheck>(field_idx, referrer, self, size);
+ ArtField* field = FindFieldFromCode<type>(field_idx, referrer, self, should_resolve_type);
if (LIKELY(field != nullptr) && UNLIKELY(h == nullptr)) {
ThrowNullPointerExceptionForFieldAccess(field, referrer, (type & FindFieldFlags::ReadBit) != 0);
return nullptr;
@@ -116,13 +114,12 @@
REQUIRES_SHARED(Locks::mutator_lock_) { \
ScopedQuickEntrypointChecks sqec(self); \
ArtField* field = FindFieldFast( \
- field_idx, referrer, Static ## PrimitiveOrObject ## Read, \
- sizeof(PrimitiveType)); \
+ field_idx, referrer, Static ## PrimitiveOrObject ## Read); \
if (LIKELY(field != nullptr)) { \
return field->Get ## Kind (field->GetDeclaringClass())Ptr; /* NOLINT */ \
} \
- field = FindFieldFromCode<Static ## PrimitiveOrObject ## Read, true>( \
- field_idx, referrer, self, sizeof(PrimitiveType)); \
+ field = FindFieldFromCode<Static ## PrimitiveOrObject ## Read>( \
+ field_idx, referrer, self); \
if (LIKELY(field != nullptr)) { \
return field->Get ## Kind (field->GetDeclaringClass())Ptr; /* NOLINT */ \
} \
@@ -137,13 +134,12 @@
REQUIRES_SHARED(Locks::mutator_lock_) { \
ScopedQuickEntrypointChecks sqec(self); \
ArtField* field = FindFieldFast( \
- field_idx, referrer, Instance ## PrimitiveOrObject ## Read, \
- sizeof(PrimitiveType)); \
+ field_idx, referrer, Instance ## PrimitiveOrObject ## Read); \
if (LIKELY(field != nullptr) && obj != nullptr) { \
return field->Get ## Kind (obj)Ptr; /* NOLINT */ \
} \
- field = FindInstanceField<Instance ## PrimitiveOrObject ## Read, true>( \
- field_idx, referrer, self, sizeof(PrimitiveType), &obj); \
+ field = FindInstanceField<Instance ## PrimitiveOrObject ## Read>( \
+ field_idx, referrer, self, &obj); \
if (LIKELY(field != nullptr)) { \
return field->Get ## Kind (obj)Ptr; /* NOLINT */ \
} \
@@ -157,33 +153,30 @@
Thread* self) \
REQUIRES_SHARED(Locks::mutator_lock_) { \
ScopedQuickEntrypointChecks sqec(self); \
+ bool should_resolve_type = (IsObject) && new_value != 0; \
ArtField* field = FindFieldFast( \
- field_idx, referrer, Static ## PrimitiveOrObject ## Write, \
- sizeof(PrimitiveType)); \
+ field_idx, \
+ referrer, \
+ Static ## PrimitiveOrObject ## Write, \
+ should_resolve_type); \
if (UNLIKELY(field == nullptr)) { \
if (IsObject) { \
StackHandleScope<1> hs(self); \
HandleWrapper<mirror::Object> h_obj(hs.NewHandleWrapper( \
reinterpret_cast<mirror::Object**>(&new_value))); \
- field = FindFieldFromCode<Static ## PrimitiveOrObject ## Write, true>( \
- field_idx, referrer, self, sizeof(PrimitiveType)); \
+ field = FindFieldFromCode<Static ## PrimitiveOrObject ## Write>( \
+ field_idx, \
+ referrer, \
+ self, \
+ should_resolve_type); \
} else { \
- field = FindFieldFromCode<Static ## PrimitiveOrObject ## Write, true>( \
- field_idx, referrer, self, sizeof(PrimitiveType)); \
+ field = FindFieldFromCode<Static ## PrimitiveOrObject ## Write>( \
+ field_idx, referrer, self); \
} \
if (UNLIKELY(field == nullptr)) { \
return -1; \
} \
} \
- if (!referrer->SkipAccessChecks() && IsObject && new_value != 0) { \
- StackArtFieldHandleScope<1> rhs(self); \
- ReflectiveHandle<ArtField> field_handle(rhs.NewHandle(field)); \
- if (field->ResolveType().IsNull()) { \
- self->AssertPendingException(); \
- return -1; \
- } \
- field = field_handle.Get(); \
- } \
field->Set ## Kind <false>(field->GetDeclaringClass(), new_value); \
return 0; \
} \
@@ -195,43 +188,31 @@
Thread* self) \
REQUIRES_SHARED(Locks::mutator_lock_) { \
ScopedQuickEntrypointChecks sqec(self); \
+ bool should_resolve_type = (IsObject) && new_value != 0; \
ArtField* field = FindFieldFast( \
- field_idx, referrer, Instance ## PrimitiveOrObject ## Write, \
- sizeof(PrimitiveType)); \
+ field_idx, \
+ referrer, \
+ Instance ## PrimitiveOrObject ## Write, \
+ should_resolve_type); \
if (UNLIKELY(field == nullptr || obj == nullptr)) { \
if (IsObject) { \
StackHandleScope<1> hs(self); \
HandleWrapper<mirror::Object> h_obj(hs.NewHandleWrapper( \
reinterpret_cast<mirror::Object**>(&new_value))); \
- field = \
- FindInstanceField<Instance ## PrimitiveOrObject ## Write, true>( \
- field_idx, \
- referrer, \
- self, \
- sizeof(PrimitiveType), \
- &obj); \
+ field = FindInstanceField<Instance ## PrimitiveOrObject ## Write>( \
+ field_idx, \
+ referrer, \
+ self, \
+ &obj, \
+ should_resolve_type); \
} else { \
- field = \
- FindInstanceField<Instance ## PrimitiveOrObject ## Write, true>( \
- field_idx, \
- referrer, \
- self, \
- sizeof(PrimitiveType), \
- &obj); \
+ field = FindInstanceField<Instance ## PrimitiveOrObject ## Write>( \
+ field_idx, referrer, self, &obj); \
} \
if (UNLIKELY(field == nullptr)) { \
return -1; \
} \
} \
- if (!referrer->SkipAccessChecks() && IsObject && new_value != 0) { \
- StackArtFieldHandleScope<1> rhs(self); \
- ReflectiveHandle<ArtField> field_handle(rhs.NewHandle(field)); \
- if (field->ResolveType().IsNull()) { \
- self->AssertPendingException(); \
- return -1; \
- } \
- field = field_handle.Get(); \
- } \
field->Set ## Kind<false>(obj, new_value); \
return 0; \
} \
@@ -435,7 +416,7 @@
}
extern "C" mirror::Object* artReadBarrierMark(mirror::Object* obj) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
return ReadBarrier::Mark(obj);
}
@@ -443,14 +424,12 @@
mirror::Object* obj,
uint32_t offset) {
// Used only in connection with non-volatile loads.
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
uint8_t* raw_addr = reinterpret_cast<uint8_t*>(obj) + offset;
mirror::HeapReference<mirror::Object>* ref_addr =
reinterpret_cast<mirror::HeapReference<mirror::Object>*>(raw_addr);
- constexpr ReadBarrierOption kReadBarrierOption =
- kUseReadBarrier ? kWithReadBarrier : kWithoutReadBarrier;
mirror::Object* result =
- ReadBarrier::Barrier<mirror::Object, /* kIsVolatile= */ false, kReadBarrierOption>(
+ ReadBarrier::Barrier<mirror::Object, /* kIsVolatile= */ false, kWithReadBarrier>(
obj,
MemberOffset(offset),
ref_addr);
@@ -458,7 +437,7 @@
}
extern "C" mirror::Object* artReadBarrierForRootSlow(GcRoot<mirror::Object>* root) {
- DCHECK(kEmitCompilerReadBarrier);
+ DCHECK(gUseReadBarrier);
return root->Read();
}
diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
index ab13bd9..6f69001 100644
--- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
@@ -38,11 +38,16 @@
namespace art {
-static_assert(sizeof(IRTSegmentState) == sizeof(uint32_t), "IRTSegmentState size unexpected");
-static_assert(std::is_trivial<IRTSegmentState>::value, "IRTSegmentState not trivial");
+extern "C" int artMethodExitHook(Thread* self,
+ ArtMethod* method,
+ uint64_t* gpr_result,
+ uint64_t* fpr_result);
+
+static_assert(sizeof(jni::LRTSegmentState) == sizeof(uint32_t), "LRTSegmentState size unexpected");
+static_assert(std::is_trivial<jni::LRTSegmentState>::value, "LRTSegmentState not trivial");
extern "C" void artJniReadBarrier(ArtMethod* method) {
- DCHECK(kUseReadBarrier);
+ DCHECK(gUseReadBarrier);
mirror::CompressedReference<mirror::Object>* declaring_class =
method->GetDeclaringClassAddressWithoutBarrier();
if (kUseBakerReadBarrier) {
@@ -77,7 +82,7 @@
env->CheckNoHeldMonitors();
}
env->SetLocalSegmentState(env->GetLocalRefCookie());
- env->SetLocalRefCookie(bit_cast<IRTSegmentState>(saved_local_ref_cookie));
+ env->SetLocalRefCookie(bit_cast<jni::LRTSegmentState>(saved_local_ref_cookie));
}
// TODO: annotalysis disabled as monitor semantics are maintained in Java code.
@@ -174,11 +179,11 @@
artJniUnlockObject(lock.Ptr(), self);
}
char return_shorty_char = called->GetShorty()[0];
+ uint64_t ret;
if (return_shorty_char == 'L') {
- uint64_t ret = reinterpret_cast<uint64_t>(
+ ret = reinterpret_cast<uint64_t>(
UNLIKELY(self->IsExceptionPending()) ? nullptr : JniDecodeReferenceResult(result.l, self));
PopLocalReferences(saved_local_ref_cookie, self);
- return ret;
} else {
if (LIKELY(!critical_native)) {
PopLocalReferences(saved_local_ref_cookie, self);
@@ -188,32 +193,43 @@
if (kRuntimeISA == InstructionSet::kX86) {
// Convert back the result to float.
double d = bit_cast<double, uint64_t>(result_f);
- return bit_cast<uint32_t, float>(static_cast<float>(d));
+ ret = bit_cast<uint32_t, float>(static_cast<float>(d));
} else {
- return result_f;
+ ret = result_f;
}
}
+ break;
case 'D':
- return result_f;
+ ret = result_f;
+ break;
case 'Z':
- return result.z;
+ ret = result.z;
+ break;
case 'B':
- return result.b;
+ ret = result.b;
+ break;
case 'C':
- return result.c;
+ ret = result.c;
+ break;
case 'S':
- return result.s;
+ ret = result.s;
+ break;
case 'I':
- return result.i;
+ ret = result.i;
+ break;
case 'J':
- return result.j;
+ ret = result.j;
+ break;
case 'V':
- return 0;
+ ret = 0;
+ break;
default:
LOG(FATAL) << "Unexpected return shorty character " << return_shorty_char;
UNREACHABLE();
}
}
+
+ return ret;
}
extern "C" void artJniMonitoredMethodStart(Thread* self) {
diff --git a/runtime/entrypoints/quick/quick_thread_entrypoints.cc b/runtime/entrypoints/quick/quick_thread_entrypoints.cc
index 93422cf..5dca58a 100644
--- a/runtime/entrypoints/quick/quick_thread_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_thread_entrypoints.cc
@@ -21,16 +21,46 @@
namespace art {
+extern "C" void artDeoptimizeIfNeeded(Thread* self, uintptr_t result, bool is_ref)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
+ DCHECK(!self->IsExceptionPending());
+
+ ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame();
+ DCHECK(sp != nullptr && (*sp)->IsRuntimeMethod());
+
+ DeoptimizationMethodType type = instr->GetDeoptimizationMethodType(*sp);
+ JValue jvalue;
+ jvalue.SetJ(result);
+ instr->DeoptimizeIfNeeded(self, sp, type, jvalue, is_ref);
+}
+
extern "C" void artTestSuspendFromCode(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
// Called when there is a pending checkpoint or suspend request.
ScopedQuickEntrypointChecks sqec(self);
self->CheckSuspend();
+
+ // We could have other dex instructions at the same dex pc as suspend and we need to execute
+ // those instructions. So we should start executing from the current dex pc.
+ ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame();
+ JValue result;
+ result.SetJ(0);
+ Runtime::Current()->GetInstrumentation()->DeoptimizeIfNeeded(
+ self, sp, DeoptimizationMethodType::kKeepDexPc, result, /* is_ref= */ false);
}
extern "C" void artImplicitSuspendFromCode(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
// Called when there is a pending checkpoint or suspend request.
ScopedQuickEntrypointChecks sqec(self);
self->CheckSuspend(/*implicit=*/ true);
+
+ // We could have other dex instructions at the same dex pc as suspend and we need to execute
+ // those instructions. So we should start executing from the current dex pc.
+ ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame();
+ JValue result;
+ result.SetJ(0);
+ Runtime::Current()->GetInstrumentation()->DeoptimizeIfNeeded(
+ self, sp, DeoptimizationMethodType::kKeepDexPc, result, /* is_ref= */ false);
}
extern "C" void artCompileOptimized(ArtMethod* method, Thread* self)
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index b6ece4a..4baf60c 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -61,7 +61,7 @@
namespace art {
extern "C" NO_RETURN void artDeoptimizeFromCompiledCode(DeoptimizationKind kind, Thread* self);
-extern "C" NO_RETURN void artDeoptimize(Thread* self);
+extern "C" NO_RETURN void artDeoptimize(Thread* self, bool skip_method_exit_callbacks);
// Visits the arguments as saved to the stack by a CalleeSaveType::kRefAndArgs callee save frame.
class QuickArgumentVisitor {
@@ -224,13 +224,8 @@
#endif
public:
- // Special handling for proxy methods. Proxy methods are instance methods so the
- // 'this' object is the 1st argument. They also have the same frame layout as the
- // kRefAndArgs runtime method. Since 'this' is a reference, it is located in the
- // 1st GPR.
- static StackReference<mirror::Object>* GetProxyThisObjectReference(ArtMethod** sp)
+ static StackReference<mirror::Object>* GetThisObjectReference(ArtMethod** sp)
REQUIRES_SHARED(Locks::mutator_lock_) {
- CHECK((*sp)->IsProxyMethod());
CHECK_GT(kNumQuickGprArgs, 0u);
constexpr uint32_t kThisGprIndex = 0u; // 'this' is in the 1st GPR.
size_t this_arg_offset = kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset +
@@ -529,7 +524,8 @@
// allows to use the QuickArgumentVisitor constants without moving all the code in its own module.
extern "C" mirror::Object* artQuickGetProxyThisObject(ArtMethod** sp)
REQUIRES_SHARED(Locks::mutator_lock_) {
- return QuickArgumentVisitor::GetProxyThisObjectReference(sp)->AsMirrorPtr();
+ DCHECK((*sp)->IsProxyMethod());
+ return QuickArgumentVisitor::GetThisObjectReference(sp)->AsMirrorPtr();
}
// Visits arguments on the stack placing them into the shadow frame.
@@ -647,6 +643,7 @@
method_type);
}
+NO_STACK_PROTECTOR
extern "C" uint64_t artQuickToInterpreterBridge(ArtMethod* method, Thread* self, ArtMethod** sp)
REQUIRES_SHARED(Locks::mutator_lock_) {
// Ensure we don't get thread suspension until the object arguments are safely in the shadow
@@ -654,100 +651,78 @@
ScopedQuickEntrypointChecks sqec(self);
if (UNLIKELY(!method->IsInvokable())) {
- method->ThrowInvocationTimeError();
+ method->ThrowInvocationTimeError(
+ method->IsStatic()
+ ? nullptr
+ : QuickArgumentVisitor::GetThisObjectReference(sp)->AsMirrorPtr());
return 0;
}
- JValue tmp_value;
- ShadowFrame* deopt_frame = self->PopStackedShadowFrame(
- StackedShadowFrameType::kDeoptimizationShadowFrame, false);
- ManagedStack fragment;
-
DCHECK(!method->IsNative()) << method->PrettyMethod();
- uint32_t shorty_len = 0;
- ArtMethod* non_proxy_method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize);
- DCHECK(non_proxy_method->GetCodeItem() != nullptr) << method->PrettyMethod();
- CodeItemDataAccessor accessor(non_proxy_method->DexInstructionData());
- const char* shorty = non_proxy_method->GetShorty(&shorty_len);
JValue result;
- bool force_frame_pop = false;
+ ArtMethod* non_proxy_method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize);
+ DCHECK(non_proxy_method->GetCodeItem() != nullptr) << method->PrettyMethod();
+ uint32_t shorty_len = 0;
+ const char* shorty = non_proxy_method->GetShorty(&shorty_len);
+
+ ManagedStack fragment;
+ ShadowFrame* deopt_frame = self->MaybePopDeoptimizedStackedShadowFrame();
if (UNLIKELY(deopt_frame != nullptr)) {
HandleDeoptimization(&result, method, deopt_frame, &fragment);
} else {
+ CodeItemDataAccessor accessor(non_proxy_method->DexInstructionData());
const char* old_cause = self->StartAssertNoThreadSuspension(
"Building interpreter shadow frame");
uint16_t num_regs = accessor.RegistersSize();
// No last shadow coming from quick.
ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
- CREATE_SHADOW_FRAME(num_regs, /* link= */ nullptr, method, /* dex_pc= */ 0);
+ CREATE_SHADOW_FRAME(num_regs, method, /* dex_pc= */ 0);
ShadowFrame* shadow_frame = shadow_frame_unique_ptr.get();
size_t first_arg_reg = accessor.RegistersSize() - accessor.InsSize();
BuildQuickShadowFrameVisitor shadow_frame_builder(sp, method->IsStatic(), shorty, shorty_len,
shadow_frame, first_arg_reg);
shadow_frame_builder.VisitArguments();
+ self->EndAssertNoThreadSuspension(old_cause);
+
+ // Potentially run <clinit> before pushing the shadow frame. We do not want
+ // to have the called method on the stack if there is an exception.
+ if (!EnsureInitialized(self, shadow_frame)) {
+ DCHECK(self->IsExceptionPending());
+ return 0;
+ }
+
// Push a transition back into managed code onto the linked list in thread.
self->PushManagedStackFragment(&fragment);
self->PushShadowFrame(shadow_frame);
- self->EndAssertNoThreadSuspension(old_cause);
-
- if (NeedsClinitCheckBeforeCall(method)) {
- ObjPtr<mirror::Class> declaring_class = method->GetDeclaringClass();
- if (UNLIKELY(!declaring_class->IsVisiblyInitialized())) {
- // Ensure static method's class is initialized.
- StackHandleScope<1> hs(self);
- Handle<mirror::Class> h_class(hs.NewHandle(declaring_class));
- if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
- DCHECK(Thread::Current()->IsExceptionPending()) << method->PrettyMethod();
- self->PopManagedStackFragment(fragment);
- return 0;
- }
- }
- }
-
result = interpreter::EnterInterpreterFromEntryPoint(self, accessor, shadow_frame);
- force_frame_pop = shadow_frame->GetForcePopFrame();
}
// Pop transition.
self->PopManagedStackFragment(fragment);
- // Request a stack deoptimization if needed
- ArtMethod* caller = QuickArgumentVisitor::GetCallingMethod(sp);
- uintptr_t caller_pc = QuickArgumentVisitor::GetCallingPc(sp);
+ // Check if caller needs to be deoptimized for instrumentation reasons.
+ instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
// If caller_pc is the instrumentation exit stub, the stub will check to see if deoptimization
// should be done and it knows the real return pc. NB If the upcall is null we don't need to do
// anything. This can happen during shutdown or early startup.
- if (UNLIKELY(
- caller != nullptr &&
- caller_pc != reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()) &&
- (self->IsForceInterpreter() || Dbg::IsForcedInterpreterNeededForUpcall(self, caller)))) {
- if (!Runtime::Current()->IsAsyncDeoptimizeable(caller_pc)) {
- LOG(WARNING) << "Got a deoptimization request on un-deoptimizable method "
- << caller->PrettyMethod();
- } else {
- VLOG(deopt) << "Forcing deoptimization on return from method " << method->PrettyMethod()
- << " to " << caller->PrettyMethod()
- << (force_frame_pop ? " for frame-pop" : "");
- DCHECK_IMPLIES(force_frame_pop, result.GetJ() == 0)
- << "Force frame pop should have no result.";
- if (force_frame_pop && self->GetException() != nullptr) {
- LOG(WARNING) << "Suppressing exception for instruction-retry: "
- << self->GetException()->Dump();
- }
- // Push the context of the deoptimization stack so we can restore the return value and the
- // exception before executing the deoptimized frames.
- self->PushDeoptimizationContext(
- result,
- shorty[0] == 'L' || shorty[0] == '[', /* class or array */
- force_frame_pop ? nullptr : self->GetException(),
- /* from_code= */ false,
- DeoptimizationMethodType::kDefault);
+ if (UNLIKELY(instr->ShouldDeoptimizeCaller(self, sp))) {
+ ArtMethod* caller = QuickArgumentVisitor::GetOuterMethod(sp);
+ uintptr_t caller_pc = QuickArgumentVisitor::GetCallingPc(sp);
+ DCHECK(Runtime::Current()->IsAsyncDeoptimizeable(caller, caller_pc));
+ DCHECK(caller != nullptr);
+ DCHECK(self->GetException() != Thread::GetDeoptimizationException());
+ // Push the context of the deoptimization stack so we can restore the return value and the
+ // exception before executing the deoptimized frames.
+ self->PushDeoptimizationContext(result,
+ shorty[0] == 'L' || shorty[0] == '[', /* class or array */
+ self->GetException(),
+ /* from_code= */ false,
+ DeoptimizationMethodType::kDefault);
- // Set special exception to cause deoptimization.
- self->SetException(Thread::GetDeoptimizationException());
- }
+ // Set special exception to cause deoptimization.
+ self->SetException(Thread::GetDeoptimizationException());
}
// No need to restore the args since the method has already been run by the interpreter.
@@ -862,7 +837,6 @@
instr->MethodEnterEvent(soa.Self(), proxy_method);
if (soa.Self()->IsExceptionPending()) {
instr->MethodUnwindEvent(self,
- soa.Decode<mirror::Object>(rcvr_jobj),
proxy_method,
0);
return 0;
@@ -872,7 +846,6 @@
if (soa.Self()->IsExceptionPending()) {
if (instr->HasMethodUnwindListeners()) {
instr->MethodUnwindEvent(self,
- soa.Decode<mirror::Object>(rcvr_jobj),
proxy_method,
0);
}
@@ -1037,7 +1010,7 @@
<< "Proxy method " << method->PrettyMethod()
<< " (declaring class: " << method->GetDeclaringClass()->PrettyClass() << ")"
<< " should not hit instrumentation entrypoint.";
- DCHECK(!instrumentation->IsDeoptimized(method));
+ DCHECK(!instrumentation->IsDeoptimized(method)) << method->PrettyMethod();
// This will get the entry point either from the oat file, the JIT or the appropriate bridge
// method if none of those can be found.
result = instrumentation->GetCodeForInvoke(method);
@@ -1054,10 +1027,10 @@
StackHandleScope<2> hs(self);
Handle<mirror::Object> h_object(hs.NewHandle(is_static ? nullptr : this_object));
- Handle<mirror::Class> h_class(hs.NewHandle(method->GetDeclaringClass()));
// Ensure that the called method's class is initialized.
- if (NeedsClinitCheckBeforeCall(method) && !h_class->IsVisiblyInitialized()) {
+ if (method->StillNeedsClinitCheck()) {
+ Handle<mirror::Class> h_class = hs.NewHandle(method->GetDeclaringClass());
if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
visitor.FixupReferences();
DCHECK(self->IsExceptionPending());
@@ -1065,6 +1038,7 @@
}
}
+ DCHECK(!method->IsRuntimeMethod());
instrumentation->PushInstrumentationStackFrame(self,
is_static ? nullptr : h_object.Get(),
method,
@@ -1115,7 +1089,7 @@
static std::string DumpInstruction(ArtMethod* method, uint32_t dex_pc)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (dex_pc == static_cast<uint32_t>(-1)) {
- CHECK(method == jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt));
+ CHECK(method == WellKnownClasses::java_lang_String_charAt);
return "<native>";
} else {
CodeItemInstructionAccessor accessor = method->DexInstructions();
@@ -1193,7 +1167,7 @@
if (dex_pc == static_cast<uint32_t>(-1)) {
tag = "special ";
CHECK(inline_info.Equals(inline_infos.back()));
- caller = jni::DecodeArtMethod(WellKnownClasses::java_lang_String_charAt);
+ caller = WellKnownClasses::java_lang_String_charAt;
CHECK_EQ(caller->GetDexMethodIndex(), method_index);
} else {
ObjPtr<mirror::DexCache> dex_cache = caller->GetDexCache();
@@ -1371,21 +1345,25 @@
// Static invokes need class initialization check but instance invokes can proceed even if
// the class is erroneous, i.e. in the edge case of escaping instances of erroneous classes.
bool success = true;
- ObjPtr<mirror::Class> called_class = called->GetDeclaringClass();
- if (NeedsClinitCheckBeforeCall(called) && !called_class->IsVisiblyInitialized()) {
+ if (called->StillNeedsClinitCheck()) {
// Ensure that the called method's class is initialized.
StackHandleScope<1> hs(soa.Self());
- HandleWrapperObjPtr<mirror::Class> h_called_class(hs.NewHandleWrapper(&called_class));
+ Handle<mirror::Class> h_called_class = hs.NewHandle(called->GetDeclaringClass());
success = linker->EnsureInitialized(soa.Self(), h_called_class, true, true);
}
if (success) {
+ // When the clinit check is at entry of the AOT/nterp code, we do the clinit check
+ // before doing the suspend check. To ensure the code sees the latest
+ // version of the class (the code doesn't do a read barrier to reduce
+ // size), do a suspend check now.
+ self->CheckSuspend();
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
// Check if we need instrumented code here. Since resolution stubs could suspend, it is
// possible that we instrumented the entry points after we started executing the resolution
// stub.
code = instrumentation->GetMaybeInstrumentedCodeForInvoke(called);
} else {
- DCHECK(called_class->IsErroneous());
+ DCHECK(called->GetDeclaringClass()->IsErroneous());
DCHECK(self->IsExceptionPending());
}
}
@@ -1795,7 +1773,7 @@
// Add space for cookie.
DCHECK_ALIGNED(managed_sp, sizeof(uintptr_t));
- static_assert(sizeof(uintptr_t) >= sizeof(IRTSegmentState));
+ static_assert(sizeof(uintptr_t) >= sizeof(jni::LRTSegmentState));
uint8_t* sp8 = reinterpret_cast<uint8_t*>(managed_sp) - sizeof(uintptr_t);
// Layout stack arguments.
@@ -1944,7 +1922,7 @@
// The declaring class must be marked.
auto* declaring_class = reinterpret_cast<mirror::CompressedReference<mirror::Class>*>(
method->GetDeclaringClassAddressWithoutBarrier());
- if (kUseReadBarrier) {
+ if (gUseReadBarrier) {
artJniReadBarrier(method);
}
sm_.AdvancePointer(declaring_class);
@@ -2091,7 +2069,7 @@
}
// Fix up managed-stack things in Thread. After this we can walk the stack.
- self->SetTopOfStackTagged(managed_sp);
+ self->SetTopOfStackGenericJniTagged(managed_sp);
self->VerifyStack();
@@ -2104,16 +2082,21 @@
// We can set the entrypoint of a native method to generic JNI even when the
// class hasn't been initialized, so we need to do the initialization check
// before invoking the native code.
- if (NeedsClinitCheckBeforeCall(called)) {
- ObjPtr<mirror::Class> declaring_class = called->GetDeclaringClass();
- if (UNLIKELY(!declaring_class->IsVisiblyInitialized())) {
- // Ensure static method's class is initialized.
- StackHandleScope<1> hs(self);
- Handle<mirror::Class> h_class(hs.NewHandle(declaring_class));
- if (!runtime->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
- DCHECK(Thread::Current()->IsExceptionPending()) << called->PrettyMethod();
- return nullptr; // Report error.
- }
+ if (called->StillNeedsClinitCheck()) {
+ // Ensure static method's class is initialized.
+ StackHandleScope<1> hs(self);
+ Handle<mirror::Class> h_class = hs.NewHandle(called->GetDeclaringClass());
+ if (!runtime->GetClassLinker()->EnsureInitialized(self, h_class, true, true)) {
+ DCHECK(Thread::Current()->IsExceptionPending()) << called->PrettyMethod();
+ return nullptr; // Report error.
+ }
+ }
+
+ instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instr->AreExitStubsInstalled() && Runtime::Current()->IsJavaDebuggable())) {
+ instr->MethodEnterEvent(self, called);
+ if (self->IsExceptionPending()) {
+ return nullptr;
}
}
@@ -2185,7 +2168,7 @@
// anything that requires a mutator lock before that would cause problems as GC may have the
// exclusive mutator lock and may be moving objects, etc.
ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame();
- DCHECK(self->GetManagedStack()->GetTopQuickFrameTag());
+ DCHECK(self->GetManagedStack()->GetTopQuickFrameGenericJniTag());
uint32_t* sp32 = reinterpret_cast<uint32_t*>(sp);
ArtMethod* called = *sp;
uint32_t cookie = *(sp32 - 1);
@@ -2274,12 +2257,18 @@
uint32_t shorty_len;
const char* shorty = dex_file->GetMethodShorty(dex_file->GetMethodId(method_idx), &shorty_len);
{
- // Remember the args in case a GC happens in FindMethodFromCode.
+ // Remember the args in case a GC happens in FindMethodToCall.
ScopedObjectAccessUnchecked soa(self->GetJniEnv());
RememberForGcArgumentVisitor visitor(sp, type == kStatic, shorty, shorty_len, &soa);
visitor.VisitArguments();
- method = FindMethodFromCode<type, /*access_check=*/true>(
- method_idx, &this_object, caller_method, self);
+
+ uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
+ CodeItemInstructionAccessor accessor(caller_method->DexInstructions());
+ CHECK_LT(dex_pc, accessor.InsnsSizeInCodeUnits());
+ const Instruction& instr = accessor.InstructionAt(dex_pc);
+ bool string_init = false;
+ method = FindMethodToCall<type>(self, caller_method, &this_object, instr, &string_init);
+
visitor.FixupReferences();
}
@@ -2527,10 +2516,9 @@
const size_t num_vregs = is_range ? inst.VRegA_4rcc() : inst.VRegA_45cc();
const size_t first_arg = 0;
ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
- CREATE_SHADOW_FRAME(num_vregs, /* link= */ nullptr, resolved_method, dex_pc);
+ CREATE_SHADOW_FRAME(num_vregs, resolved_method, dex_pc);
ShadowFrame* shadow_frame = shadow_frame_unique_ptr.get();
- ScopedStackedShadowFramePusher
- frame_pusher(self, shadow_frame, StackedShadowFrameType::kShadowFrameUnderConstruction);
+ ScopedStackedShadowFramePusher frame_pusher(self, shadow_frame);
BuildQuickShadowFrameVisitor shadow_frame_builder(sp,
kMethodIsStatic,
shorty,
@@ -2589,6 +2577,10 @@
// Pop transition record.
self->PopManagedStackFragment(fragment);
+ bool is_ref = (shorty[0] == 'L');
+ Runtime::Current()->GetInstrumentation()->PushDeoptContextIfNeeded(
+ self, DeoptimizationMethodType::kDefault, is_ref, result);
+
return result.GetJ();
}
@@ -2620,10 +2612,9 @@
const size_t first_arg = 0;
const size_t num_vregs = ArtMethod::NumArgRegisters(shorty);
ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
- CREATE_SHADOW_FRAME(num_vregs, /* link= */ nullptr, caller_method, dex_pc);
+ CREATE_SHADOW_FRAME(num_vregs, caller_method, dex_pc);
ShadowFrame* shadow_frame = shadow_frame_unique_ptr.get();
- ScopedStackedShadowFramePusher
- frame_pusher(self, shadow_frame, StackedShadowFrameType::kShadowFrameUnderConstruction);
+ ScopedStackedShadowFramePusher frame_pusher(self, shadow_frame);
BuildQuickShadowFrameVisitor shadow_frame_builder(sp,
kMethodIsStatic,
shorty,
@@ -2647,40 +2638,69 @@
// Pop transition record.
self->PopManagedStackFragment(fragment);
+ bool is_ref = (shorty[0] == 'L');
+ Runtime::Current()->GetInstrumentation()->PushDeoptContextIfNeeded(
+ self, DeoptimizationMethodType::kDefault, is_ref, result);
+
return result.GetJ();
}
-extern "C" void artMethodEntryHook(ArtMethod* method, Thread* self, ArtMethod** sp ATTRIBUTE_UNUSED)
+extern "C" void artJniMethodEntryHook(Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
+ ArtMethod* method = *self->GetManagedStack()->GetTopQuickFrame();
instr->MethodEnterEvent(self, method);
- if (instr->IsDeoptimized(method)) {
- // Instrumentation can request deoptimizing only a particular method (for
- // ex: when there are break points on the method). In such cases deoptimize
- // only this method. FullFrame deoptimizations are handled on method exits.
- artDeoptimizeFromCompiledCode(DeoptimizationKind::kDebugging, self);
+}
+
+extern "C" void artMethodEntryHook(ArtMethod* method, Thread* self, ArtMethod** sp)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
+ if (instr->HasMethodEntryListeners()) {
+ instr->MethodEnterEvent(self, method);
+ // MethodEnter callback could have requested a deopt for ex: by setting a breakpoint, so
+ // check if we need a deopt here.
+ if (instr->ShouldDeoptimizeCaller(self, sp) || instr->IsDeoptimized(method)) {
+ // Instrumentation can request deoptimizing only a particular method (for ex: when
+ // there are break points on the method). In such cases deoptimize only this method.
+ // FullFrame deoptimizations are handled on method exits.
+ artDeoptimizeFromCompiledCode(DeoptimizationKind::kDebugging, self);
+ }
+ } else {
+ DCHECK(!instr->IsDeoptimized(method));
}
}
-extern "C" int artMethodExitHook(Thread* self,
- ArtMethod* method,
- uint64_t* gpr_result,
- uint64_t* fpr_result)
- REQUIRES_SHARED(Locks::mutator_lock_) {
+extern "C" void artMethodExitHook(Thread* self,
+ ArtMethod** sp,
+ uint64_t* gpr_result,
+ uint64_t* fpr_result,
+ uint32_t frame_size)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // For GenericJniTrampolines we call artMethodExitHook even for non debuggable runtimes though we
+ // still install instrumentation stubs. So just return early here so we don't call method exit
+ // twice. In all other cases (JITed JNI stubs / JITed code) we only call this for debuggable
+ // runtimes.
+ if (!Runtime::Current()->IsJavaDebuggable()) {
+ return;
+ }
+
DCHECK_EQ(reinterpret_cast<uintptr_t>(self), reinterpret_cast<uintptr_t>(Thread::Current()));
- CHECK(gpr_result != nullptr);
- CHECK(fpr_result != nullptr);
// Instrumentation exit stub must not be entered with a pending exception.
CHECK(!self->IsExceptionPending())
<< "Enter instrumentation exit stub with pending exception " << self->GetException()->Dump();
instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
- DCHECK(instr->AreExitStubsInstalled());
- bool is_ref;
- JValue return_value = instr->GetReturnValue(self, method, &is_ref, gpr_result, fpr_result);
- bool deoptimize = false;
- {
+ JValue return_value;
+ bool is_ref = false;
+ ArtMethod* method = *sp;
+ if (instr->HasMethodExitListeners()) {
StackHandleScope<1> hs(self);
+
+ CHECK(gpr_result != nullptr);
+ CHECK(fpr_result != nullptr);
+ DCHECK(instr->AreExitStubsInstalled());
+
+ return_value = instr->GetReturnValue(method, &is_ref, gpr_result, fpr_result);
MutableHandle<mirror::Object> res(hs.NewHandle<mirror::Object>(nullptr));
if (is_ref) {
// Take a handle to the return value so we won't lose it if we suspend.
@@ -2688,20 +2708,10 @@
}
DCHECK(!method->IsRuntimeMethod());
- // Deoptimize if the caller needs to continue execution in the interpreter. Do nothing if we get
- // back to an upcall.
- NthCallerVisitor visitor(self, 1, /*include_runtime_and_upcalls=*/false);
- visitor.WalkStack(true);
- deoptimize = instr->ShouldDeoptimizeMethod(self, visitor);
-
// If we need a deoptimization MethodExitEvent will be called by the interpreter when it
- // re-executes the return instruction.
- if (!deoptimize) {
- instr->MethodExitEvent(self,
- method,
- /* frame= */ {},
- return_value);
- }
+ // re-executes the return instruction. For native methods we have to process method exit
+ // events here since deoptimization just removes the native frame.
+ instr->MethodExitEvent(self, method, /* frame= */ {}, return_value);
if (is_ref) {
// Restore the return value if it's a reference since it might have moved.
@@ -2711,17 +2721,23 @@
}
if (self->IsExceptionPending() || self->ObserveAsyncException()) {
- return 1;
- }
-
- if (deoptimize) {
- DeoptimizationMethodType deopt_method_type = instr->GetDeoptimizationMethodType(method);
- self->PushDeoptimizationContext(return_value, is_ref, nullptr, false, deopt_method_type);
- artDeoptimize(self);
+ // The exception was thrown from the method exit callback. We should not call method unwind
+ // callbacks for this case.
+ self->QuickDeliverException(/* is_method_exit_exception= */ true);
UNREACHABLE();
}
- return 0;
+ bool deoptimize = instr->ShouldDeoptimizeCaller(self, sp, frame_size);
+ if (deoptimize) {
+ JValue ret_val = instr->GetReturnValue(method, &is_ref, gpr_result, fpr_result);
+ DeoptimizationMethodType deopt_method_type = instr->GetDeoptimizationMethodType(method);
+ self->PushDeoptimizationContext(
+ ret_val, is_ref, self->GetException(), false, deopt_method_type);
+ // Method exit callback has already been run for this method. So tell the deoptimizer to skip
+ // callbacks for this frame.
+ artDeoptimize(self, /*skip_method_exit_callbacks = */ true);
+ UNREACHABLE();
+ }
}
} // namespace art
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index 240ecbd..50fa0ab 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -16,8 +16,8 @@
#include <memory>
+#include "base/common_art_test.h"
#include "base/macros.h"
-#include "common_runtime_test.h"
#include "thread.h"
// This test checks the offsets of values in the thread TLS and entrypoint structures. A failure
@@ -59,7 +59,7 @@
#define EXPECT_OFFSET_DIFF_GT3(type, first_field, second_field, diff, name) \
EXPECT_OFFSET_DIFF_GT(type, first_field, type, second_field, diff, name)
-class EntrypointsOrderTest : public CommonRuntimeTest {
+class EntrypointsOrderTest : public CommonArtTest {
protected:
void CheckThreadOffsets() {
CHECKED(OFFSETOF_MEMBER(Thread, tls32_.state_and_flags) == 0, thread_flags_at_zero);
@@ -135,10 +135,14 @@
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, thread_local_mark_stack, async_exception, sizeof(void*));
EXPECT_OFFSET_DIFFP(Thread, tlsPtr_, async_exception, top_reflective_handle_scope,
sizeof(void*));
+ EXPECT_OFFSET_DIFFP(
+ Thread, tlsPtr_, top_reflective_handle_scope, method_trace_buffer, sizeof(void*));
+ EXPECT_OFFSET_DIFFP(
+ Thread, tlsPtr_, method_trace_buffer, method_trace_buffer_index, sizeof(void*));
// The first field after tlsPtr_ is forced to a 16 byte alignment so it might have some space.
auto offset_tlsptr_end = OFFSETOF_MEMBER(Thread, tlsPtr_) +
sizeof(decltype(reinterpret_cast<Thread*>(16)->tlsPtr_));
- CHECKED(offset_tlsptr_end - OFFSETOF_MEMBER(Thread, tlsPtr_.top_reflective_handle_scope) ==
+ CHECKED(offset_tlsptr_end - OFFSETOF_MEMBER(Thread, tlsPtr_.method_trace_buffer_index) ==
sizeof(void*),
"async_exception last field");
}
@@ -225,7 +229,9 @@
pJniUnlockObject, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniUnlockObject,
pQuickGenericJniTrampoline, sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pQuickGenericJniTrampoline, pLockObject, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pQuickGenericJniTrampoline,
+ pJniMethodEntryHook, sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pJniMethodEntryHook, pLockObject, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pLockObject, pUnlockObject, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pUnlockObject, pCmpgDouble, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pCmpgDouble, pCmpgFloat, sizeof(void*));
@@ -299,7 +305,9 @@
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pA64Store, pNewEmptyString, sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewEmptyString, pNewStringFromBytes_B, sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromBytes_B, pNewStringFromBytes_BI,
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromBytes_B, pNewStringFromBytes_BB,
+ sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromBytes_BB, pNewStringFromBytes_BI,
sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromBytes_BI, pNewStringFromBytes_BII,
sizeof(void*));
@@ -327,7 +335,9 @@
sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromStringBuffer, pNewStringFromStringBuilder,
sizeof(void*));
- EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromStringBuilder, pStringBuilderAppend,
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromStringBuilder, pNewStringFromUtf16Bytes_BII,
+ sizeof(void*));
+ EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pNewStringFromUtf16Bytes_BII, pStringBuilderAppend,
sizeof(void*));
EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pStringBuilderAppend, pUpdateInlineCache,
sizeof(void*));
diff --git a/runtime/exec_utils.cc b/runtime/exec_utils.cc
index 463d458..dd389f8 100644
--- a/runtime/exec_utils.cc
+++ b/runtime/exec_utils.cc
@@ -16,22 +16,46 @@
#include "exec_utils.h"
+#include <poll.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <unistd.h>
+
+#include <ctime>
+#include <string_view>
+
+#ifdef __BIONIC__
+#include <sys/pidfd.h>
+#endif
+
+#include <chrono>
+#include <climits>
+#include <condition_variable>
+#include <cstdint>
+#include <mutex>
#include <string>
+#include <thread>
#include <vector>
+#include "android-base/file.h"
+#include "android-base/parseint.h"
+#include "android-base/scopeguard.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
-
+#include "android-base/unique_fd.h"
+#include "base/macros.h"
+#include "base/utils.h"
#include "runtime.h"
namespace art {
-using android::base::StringPrintf;
-
namespace {
+using ::android::base::ParseInt;
+using ::android::base::ReadFileToString;
+using ::android::base::StringPrintf;
+using ::android::base::unique_fd;
+
std::string ToCommandLine(const std::vector<std::string>& args) {
return android::base::Join(args, ' ');
}
@@ -40,7 +64,7 @@
// If there is a runtime (Runtime::Current != nullptr) then the subprocess is created with the
// same environment that existed when the runtime was started.
// Returns the process id of the child process on success, -1 otherwise.
-pid_t ExecWithoutWait(std::vector<std::string>& arg_vector) {
+pid_t ExecWithoutWait(const std::vector<std::string>& arg_vector, std::string* error_msg) {
// Convert the args to char pointers.
const char* program = arg_vector[0].c_str();
std::vector<char*> args;
@@ -65,110 +89,274 @@
} else {
execve(program, &args[0], envp);
}
- PLOG(ERROR) << "Failed to execve(" << ToCommandLine(arg_vector) << ")";
- // _exit to avoid atexit handlers in child.
- _exit(1);
+ // This should be regarded as a crash rather than a normal return.
+ PLOG(FATAL) << "Failed to execute (" << ToCommandLine(arg_vector) << ")";
+ UNREACHABLE();
+ } else if (pid == -1) {
+ *error_msg = StringPrintf("Failed to execute (%s) because fork failed: %s",
+ ToCommandLine(arg_vector).c_str(),
+ strerror(errno));
+ return -1;
} else {
return pid;
}
}
+ExecResult WaitChild(pid_t pid,
+ const std::vector<std::string>& arg_vector,
+ bool no_wait,
+ std::string* error_msg) {
+ siginfo_t info;
+ // WNOWAIT leaves the child in a waitable state. The call is still blocking.
+ int options = WEXITED | (no_wait ? WNOWAIT : 0);
+ if (TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, options)) != 0) {
+ *error_msg = StringPrintf("waitid failed for (%s) pid %d: %s",
+ ToCommandLine(arg_vector).c_str(),
+ pid,
+ strerror(errno));
+ return {.status = ExecResult::kUnknown};
+ }
+ if (info.si_pid != pid) {
+ *error_msg = StringPrintf("waitid failed for (%s): wanted pid %d, got %d: %s",
+ ToCommandLine(arg_vector).c_str(),
+ pid,
+ info.si_pid,
+ strerror(errno));
+ return {.status = ExecResult::kUnknown};
+ }
+ if (info.si_code != CLD_EXITED) {
+ *error_msg =
+ StringPrintf("Failed to execute (%s) because the child process is terminated by signal %d",
+ ToCommandLine(arg_vector).c_str(),
+ info.si_status);
+ return {.status = ExecResult::kSignaled, .signal = info.si_status};
+ }
+ return {.status = ExecResult::kExited, .exit_code = info.si_status};
+}
+
+// A fallback implementation of `WaitChildWithTimeout` that creates a thread to wait instead of
+// relying on `pidfd_open`.
+ExecResult WaitChildWithTimeoutFallback(pid_t pid,
+ const std::vector<std::string>& arg_vector,
+ int timeout_ms,
+ std::string* error_msg) {
+ bool child_exited = false;
+ bool timed_out = false;
+ std::condition_variable cv;
+ std::mutex m;
+
+ std::thread wait_thread([&]() {
+ std::unique_lock<std::mutex> lock(m);
+ if (!cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), [&] { return child_exited; })) {
+ timed_out = true;
+ kill(pid, SIGKILL);
+ }
+ });
+
+ ExecResult result = WaitChild(pid, arg_vector, /*no_wait=*/true, error_msg);
+
+ {
+ std::unique_lock<std::mutex> lock(m);
+ child_exited = true;
+ }
+ cv.notify_all();
+ wait_thread.join();
+
+ // The timeout error should have a higher priority than any other error.
+ if (timed_out) {
+ *error_msg =
+ StringPrintf("Failed to execute (%s) because the child process timed out after %dms",
+ ToCommandLine(arg_vector).c_str(),
+ timeout_ms);
+ return ExecResult{.status = ExecResult::kTimedOut};
+ }
+
+ return result;
+}
+
+// Waits for the child process to finish and leaves the child in a waitable state.
+ExecResult WaitChildWithTimeout(pid_t pid,
+ unique_fd pidfd,
+ const std::vector<std::string>& arg_vector,
+ int timeout_ms,
+ std::string* error_msg) {
+ auto cleanup = android::base::make_scope_guard([&]() {
+ kill(pid, SIGKILL);
+ std::string ignored_error_msg;
+ WaitChild(pid, arg_vector, /*no_wait=*/true, &ignored_error_msg);
+ });
+
+ struct pollfd pfd;
+ pfd.fd = pidfd.get();
+ pfd.events = POLLIN;
+ int poll_ret = TEMP_FAILURE_RETRY(poll(&pfd, /*nfds=*/1, timeout_ms));
+
+ pidfd.reset();
+
+ if (poll_ret < 0) {
+ *error_msg = StringPrintf("poll failed for pid %d: %s", pid, strerror(errno));
+ return {.status = ExecResult::kUnknown};
+ }
+ if (poll_ret == 0) {
+ *error_msg =
+ StringPrintf("Failed to execute (%s) because the child process timed out after %dms",
+ ToCommandLine(arg_vector).c_str(),
+ timeout_ms);
+ return {.status = ExecResult::kTimedOut};
+ }
+
+ cleanup.Disable();
+ return WaitChild(pid, arg_vector, /*no_wait=*/true, error_msg);
+}
+
+bool ParseProcStat(const std::string& stat_content,
+ int64_t uptime_ms,
+ int64_t ticks_per_sec,
+ /*out*/ ProcessStat* stat) {
+ size_t pos = stat_content.rfind(") ");
+ if (pos == std::string::npos) {
+ return false;
+ }
+ std::vector<std::string> stat_fields;
+ // Skip the first two fields. The second field is the parenthesized process filename, which can
+ // contain anything, including spaces.
+ Split(std::string_view(stat_content).substr(pos + 2), ' ', &stat_fields);
+ constexpr int kSkippedFields = 2;
+ int64_t utime, stime, cutime, cstime, starttime;
+ if (stat_fields.size() < 22 - kSkippedFields ||
+ !ParseInt(stat_fields[13 - kSkippedFields], &utime) ||
+ !ParseInt(stat_fields[14 - kSkippedFields], &stime) ||
+ !ParseInt(stat_fields[15 - kSkippedFields], &cutime) ||
+ !ParseInt(stat_fields[16 - kSkippedFields], &cstime) ||
+ !ParseInt(stat_fields[21 - kSkippedFields], &starttime)) {
+ return false;
+ }
+ stat->cpu_time_ms = (utime + stime + cutime + cstime) * 1000 / ticks_per_sec;
+ stat->wall_time_ms = uptime_ms - starttime * 1000 / ticks_per_sec;
+ return true;
+}
+
} // namespace
-int ExecAndReturnCode(std::vector<std::string>& arg_vector, std::string* error_msg) {
- pid_t pid = ExecWithoutWait(arg_vector);
- if (pid == -1) {
- *error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s",
- ToCommandLine(arg_vector).c_str(), strerror(errno));
- return -1;
- }
-
- // wait for subprocess to finish
- int status = -1;
- pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
- if (got_pid != pid) {
- *error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: "
- "wanted %d, got %d: %s",
- ToCommandLine(arg_vector).c_str(), pid, got_pid, strerror(errno));
- return -1;
- }
- if (WIFEXITED(status)) {
- return WEXITSTATUS(status);
- }
- return -1;
+int ExecUtils::ExecAndReturnCode(const std::vector<std::string>& arg_vector,
+ std::string* error_msg) const {
+ return ExecAndReturnResult(arg_vector, /*timeout_sec=*/-1, error_msg).exit_code;
}
-int ExecAndReturnCode(std::vector<std::string>& arg_vector,
- time_t timeout_secs,
- bool* timed_out,
- std::string* error_msg) {
- *timed_out = false;
+ExecResult ExecUtils::ExecAndReturnResult(const std::vector<std::string>& arg_vector,
+ int timeout_sec,
+ std::string* error_msg) const {
+ return ExecAndReturnResult(arg_vector, timeout_sec, ExecCallbacks(), /*stat=*/nullptr, error_msg);
+}
+
+ExecResult ExecUtils::ExecAndReturnResult(const std::vector<std::string>& arg_vector,
+ int timeout_sec,
+ const ExecCallbacks& callbacks,
+ /*out*/ ProcessStat* stat,
+ /*out*/ std::string* error_msg) const {
+ if (timeout_sec > INT_MAX / 1000) {
+ *error_msg = "Timeout too large";
+ return {.status = ExecResult::kStartFailed};
+ }
// Start subprocess.
- pid_t pid = ExecWithoutWait(arg_vector);
+ pid_t pid = ExecWithoutWait(arg_vector, error_msg);
if (pid == -1) {
- *error_msg = StringPrintf("Failed to execv(%s) because fork failed: %s",
- ToCommandLine(arg_vector).c_str(), strerror(errno));
- return -1;
+ return {.status = ExecResult::kStartFailed};
}
- // Add SIGCHLD to the signal set.
- sigset_t child_mask, original_mask;
- sigemptyset(&child_mask);
- sigaddset(&child_mask, SIGCHLD);
- if (sigprocmask(SIG_BLOCK, &child_mask, &original_mask) == -1) {
- *error_msg = StringPrintf("Failed to set sigprocmask(): %s", strerror(errno));
- return -1;
- }
-
- // Wait for a SIGCHLD notification.
- errno = 0;
- timespec ts = {timeout_secs, 0};
- int wait_result = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, nullptr, &ts));
- int wait_errno = errno;
-
- // Restore the original signal set.
- if (sigprocmask(SIG_SETMASK, &original_mask, nullptr) == -1) {
- *error_msg = StringPrintf("Fail to restore sigprocmask(): %s", strerror(errno));
- if (wait_result == 0) {
- return -1;
- }
- }
-
- // Having restored the signal set, see if we need to terminate the subprocess.
- if (wait_result == -1) {
- if (wait_errno == EAGAIN) {
- *error_msg = "Timed out.";
- *timed_out = true;
- } else {
- *error_msg = StringPrintf("Failed to sigtimedwait(): %s", strerror(errno));
- }
- if (kill(pid, SIGKILL) != 0) {
- PLOG(ERROR) << "Failed to kill() subprocess: ";
- }
- }
+ callbacks.on_start(pid);
// Wait for subprocess to finish.
- int status = -1;
- pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
- if (got_pid != pid) {
- *error_msg = StringPrintf("Failed after fork for execv(%s) because waitpid failed: "
- "wanted %d, got %d: %s",
- ToCommandLine(arg_vector).c_str(), pid, got_pid, strerror(errno));
- return -1;
+ ExecResult result;
+ if (timeout_sec >= 0) {
+ unique_fd pidfd = PidfdOpen(pid);
+ if (pidfd.get() >= 0) {
+ result =
+ WaitChildWithTimeout(pid, std::move(pidfd), arg_vector, timeout_sec * 1000, error_msg);
+ } else {
+ LOG(DEBUG) << StringPrintf(
+ "pidfd_open failed for pid %d: %s, falling back", pid, strerror(errno));
+ result = WaitChildWithTimeoutFallback(pid, arg_vector, timeout_sec * 1000, error_msg);
+ }
+ } else {
+ result = WaitChild(pid, arg_vector, /*no_wait=*/true, error_msg);
}
- if (WIFEXITED(status)) {
- return WEXITSTATUS(status);
+
+ if (stat != nullptr) {
+ std::string local_error_msg;
+ if (!GetStat(pid, stat, &local_error_msg)) {
+ LOG(ERROR) << "Failed to get process stat: " << local_error_msg;
+ }
}
- return -1;
+
+ callbacks.on_end(pid);
+
+ std::string local_error_msg;
+ // TODO(jiakaiz): Use better logic to detect waitid failure.
+ if (WaitChild(pid, arg_vector, /*no_wait=*/false, &local_error_msg).status ==
+ ExecResult::kUnknown) {
+ LOG(ERROR) << "Failed to clean up child process '" << arg_vector[0] << "': " << local_error_msg;
+ }
+
+ return result;
}
-
-bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg) {
+bool ExecUtils::Exec(const std::vector<std::string>& arg_vector, std::string* error_msg) const {
int status = ExecAndReturnCode(arg_vector, error_msg);
- if (status != 0) {
- *error_msg = StringPrintf("Failed execv(%s) because non-0 exit status",
- ToCommandLine(arg_vector).c_str());
+ if (status < 0) {
+ // Internal error. The error message is already set.
+ return false;
+ }
+ if (status > 0) {
+ *error_msg =
+ StringPrintf("Failed to execute (%s) because the child process returns non-zero exit code",
+ ToCommandLine(arg_vector).c_str());
+ return false;
+ }
+ return true;
+}
+
+unique_fd ExecUtils::PidfdOpen(pid_t pid) const {
+#ifdef __BIONIC__
+ return unique_fd(pidfd_open(pid, /*flags=*/0));
+#else
+ // There is no glibc wrapper for pidfd_open.
+#ifndef SYS_pidfd_open
+ constexpr int SYS_pidfd_open = 434;
+#endif
+ return unique_fd(syscall(SYS_pidfd_open, pid, /*flags=*/0));
+#endif
+}
+
+std::string ExecUtils::GetProcStat(pid_t pid) const {
+ std::string stat_content;
+ if (!ReadFileToString(StringPrintf("/proc/%d/stat", pid), &stat_content)) {
+ stat_content = "";
+ }
+ return stat_content;
+}
+
+int64_t ExecUtils::GetUptimeMs() const {
+ timespec t;
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ return t.tv_sec * 1000 + t.tv_nsec / 1000000;
+}
+
+int64_t ExecUtils::GetTicksPerSec() const { return sysconf(_SC_CLK_TCK); }
+
+bool ExecUtils::GetStat(pid_t pid,
+ /*out*/ ProcessStat* stat,
+ /*out*/ std::string* error_msg) const {
+ int64_t uptime_ms = GetUptimeMs();
+ std::string stat_content = GetProcStat(pid);
+ if (stat_content.empty()) {
+ *error_msg = StringPrintf("Failed to read /proc/%d/stat: %s", pid, strerror(errno));
+ return false;
+ }
+ int64_t ticks_per_sec = GetTicksPerSec();
+ if (!ParseProcStat(stat_content, uptime_ms, ticks_per_sec, stat)) {
+ *error_msg = StringPrintf("Failed to parse /proc/%d/stat '%s'", pid, stat_content.c_str());
return false;
}
return true;
diff --git a/runtime/exec_utils.h b/runtime/exec_utils.h
index 7ce0a9c..11ab579 100644
--- a/runtime/exec_utils.h
+++ b/runtime/exec_utils.h
@@ -19,48 +19,109 @@
#include <time.h>
+#include <functional>
#include <string>
#include <vector>
+#include "android-base/unique_fd.h"
+
namespace art {
+struct ProcessStat {
+ // The total wall time, in milliseconds, that the process spent, or 0 if failed to get the value.
+ int wall_time_ms = 0;
+ // The total CPU time, in milliseconds, that the process and any waited-for children spent, or 0
+ // if failed to get the value.
+ int cpu_time_ms = 0;
+};
+
+struct ExecCallbacks {
+ // Called in the parent process as soon as the child process is forked.
+ std::function<void(pid_t pid)> on_start = [](pid_t) {};
+ // Called in the parent process after the child process exits while still in a waitable state, no
+ // matter the child process succeeds or not.
+ std::function<void(pid_t pid)> on_end = [](pid_t) {};
+};
+
+struct ExecResult {
+ // This struct needs to be in sync with the ExecResultStatus enum contained within
+ // the OdrefreshReported atom in frameworks/proto_logging/atoms.proto.
+ enum Status {
+ // Unable to get the status.
+ kUnknown = 0,
+ // Process exited normally with an exit code.
+ kExited = 1,
+ // Process terminated by a signal.
+ kSignaled = 2,
+ // Process timed out and killed.
+ kTimedOut = 3,
+ // Failed to start the process.
+ kStartFailed = 4,
+ kLast = kStartFailed
+ };
+
+ Status status = kUnknown;
+
+ // The process exit code, if `status` is `kExited`, or -1.
+ int exit_code = -1;
+
+ // The signal that terminated the process, if `status` is `kSignaled`, or 0.
+ int signal = 0;
+};
+
// Wrapper on fork/execv to run a command in a subprocess.
// These spawn child processes using the environment as it was set when the single instance
// of the runtime (Runtime::Current()) was started. If no instance of the runtime was started, it
// will use the current environment settings.
-
-bool Exec(std::vector<std::string>& arg_vector, /*out*/ std::string* error_msg);
-int ExecAndReturnCode(std::vector<std::string>& arg_vector, /*out*/ std::string* error_msg);
-
-// Execute the command specified in `argv_vector` in a subprocess with a timeout.
-// Returns the process exit code on success, -1 otherwise.
-int ExecAndReturnCode(std::vector<std::string>& arg_vector,
- time_t timeout_secs,
- /*out*/ bool* timed_out,
- /*out*/ std::string* error_msg);
-
-// A wrapper class to make the functions above mockable.
class ExecUtils {
public:
virtual ~ExecUtils() = default;
- virtual bool Exec(std::vector<std::string>& arg_vector, /*out*/ std::string* error_msg) const {
- return art::Exec(arg_vector, error_msg);
- }
+ virtual bool Exec(const std::vector<std::string>& arg_vector,
+ /*out*/ std::string* error_msg) const;
- virtual int ExecAndReturnCode(std::vector<std::string>& arg_vector,
- /*out*/ std::string* error_msg) const {
- return art::ExecAndReturnCode(arg_vector, error_msg);
- }
+ virtual int ExecAndReturnCode(const std::vector<std::string>& arg_vector,
+ /*out*/ std::string* error_msg) const;
- virtual int ExecAndReturnCode(std::vector<std::string>& arg_vector,
- time_t timeout_secs,
- /*out*/ bool* timed_out,
- /*out*/ std::string* error_msg) const {
- return art::ExecAndReturnCode(arg_vector, timeout_secs, timed_out, error_msg);
- }
+ // Executes the command specified in `arg_vector` in a subprocess with a timeout.
+ // If `timeout_sec` is negative, blocks until the subprocess exits.
+ // Returns a structured result. If the status is not `kExited`, also returns a non-empty
+ // `error_msg`.
+ virtual ExecResult ExecAndReturnResult(const std::vector<std::string>& arg_vector,
+ int timeout_sec,
+ /*out*/ std::string* error_msg) const;
+
+ // Same as above, but also collects stat of the process and calls callbacks. The stat is collected
+ // no matter the child process succeeds or not.
+ virtual ExecResult ExecAndReturnResult(const std::vector<std::string>& arg_vector,
+ int timeout_sec,
+ const ExecCallbacks& callbacks,
+ /*out*/ ProcessStat* stat,
+ /*out*/ std::string* error_msg) const;
+
+ protected:
+ virtual android::base::unique_fd PidfdOpen(pid_t pid) const;
+
+ // Returns the content of `/proc/<pid>/stat`, or an empty string if failed.
+ virtual std::string GetProcStat(pid_t pid) const;
+
+ virtual int64_t GetUptimeMs() const;
+
+ virtual int64_t GetTicksPerSec() const;
+
+ private:
+ bool GetStat(pid_t pid, /*out*/ ProcessStat* stat, /*out*/ std::string* error_msg) const;
};
+inline bool Exec(const std::vector<std::string>& arg_vector, /*out*/ std::string* error_msg) {
+ return ExecUtils().Exec(arg_vector, error_msg);
+}
+
+inline int ExecAndReturnCode(const std::vector<std::string>& arg_vector,
+ /*out*/ std::string* error_msg) {
+ return ExecUtils().ExecAndReturnCode(arg_vector, error_msg);
+}
+
} // namespace art
#endif // ART_RUNTIME_EXEC_UTILS_H_
diff --git a/runtime/exec_utils_test.cc b/runtime/exec_utils_test.cc
index dc789aa..e89180b 100644
--- a/runtime/exec_utils_test.cc
+++ b/runtime/exec_utils_test.cc
@@ -16,68 +16,134 @@
#include "exec_utils.h"
+#include <sys/utsname.h>
+
+#include <csignal>
+#include <cstring>
+#include <filesystem>
+#include <memory>
+#include <tuple>
+
+#include "android-base/logging.h"
#include "android-base/stringprintf.h"
#include "base/file_utils.h"
#include "base/memory_tool.h"
#include "common_runtime_test.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
namespace art {
+using ::testing::_;
+using ::testing::AllOf;
+using ::testing::Gt;
+using ::testing::HasSubstr;
+using ::testing::InSequence;
+using ::testing::MockFunction;
+using ::testing::Ne;
+using ::testing::Return;
+
std::string PrettyArguments(const char* signature);
std::string PrettyReturnType(const char* signature);
-class ExecUtilsTest : public CommonRuntimeTest {};
-
-TEST_F(ExecUtilsTest, ExecSuccess) {
- std::vector<std::string> command;
+std::string GetBin(const std::string& name) {
if (kIsTargetBuild) {
std::string android_root(GetAndroidRoot());
- command.push_back(android_root + "/bin/id");
+ return android_root + "/bin/" + name;
+ } else if (std::filesystem::exists("/usr/bin/" + name)) {
+ return "/usr/bin/" + name;
} else {
- command.push_back("/usr/bin/id");
+ return "/bin/" + name;
}
+}
+
+std::tuple<int, int> GetKernelVersion() {
+ std::tuple<int, int> version;
+ utsname uts;
+ CHECK_EQ(uname(&uts), 0);
+ CHECK_EQ(sscanf(uts.release, "%d.%d", &std::get<0>(version), &std::get<1>(version)), 2);
+ return version;
+}
+
+class TestingExecUtils : public ExecUtils {
+ public:
+ MOCK_METHOD(std::string, GetProcStat, (pid_t pid), (const, override));
+ MOCK_METHOD(int64_t, GetUptimeMs, (), (const, override));
+ MOCK_METHOD(int64_t, GetTicksPerSec, (), (const, override));
+};
+
+class AlwaysFallbackExecUtils : public TestingExecUtils {
+ protected:
+ android::base::unique_fd PidfdOpen(pid_t) const override { return android::base::unique_fd(-1); }
+};
+
+class NeverFallbackExecUtils : public TestingExecUtils {
+ protected:
+ android::base::unique_fd PidfdOpen(pid_t pid) const override {
+ android::base::unique_fd pidfd = ExecUtils::PidfdOpen(pid);
+ CHECK_GE(pidfd.get(), 0) << strerror(errno);
+ return pidfd;
+ }
+};
+
+class ExecUtilsTest : public CommonRuntimeTest, public testing::WithParamInterface<bool> {
+ protected:
+ void SetUp() override {
+ CommonRuntimeTest::SetUp();
+ bool always_fallback = GetParam();
+ if (always_fallback) {
+ exec_utils_ = std::make_unique<AlwaysFallbackExecUtils>();
+ } else {
+ if (GetKernelVersion() >= std::make_tuple(5, 4)) {
+ exec_utils_ = std::make_unique<NeverFallbackExecUtils>();
+ } else {
+ GTEST_SKIP() << "Kernel version older than 5.4";
+ }
+ }
+ }
+
+ std::unique_ptr<TestingExecUtils> exec_utils_;
+};
+
+TEST_P(ExecUtilsTest, ExecSuccess) {
+ std::vector<std::string> command;
+ command.push_back(GetBin("id"));
std::string error_msg;
// Historical note: Running on Valgrind failed due to some memory
// that leaks in thread alternate signal stacks.
- EXPECT_TRUE(Exec(command, &error_msg));
+ EXPECT_TRUE(exec_utils_->Exec(command, &error_msg));
EXPECT_EQ(0U, error_msg.size()) << error_msg;
}
-TEST_F(ExecUtilsTest, ExecError) {
- // This will lead to error messages in the log.
- ScopedLogSeverity sls(LogSeverity::FATAL);
-
+TEST_P(ExecUtilsTest, ExecError) {
std::vector<std::string> command;
command.push_back("bogus");
std::string error_msg;
// Historical note: Running on Valgrind failed due to some memory
// that leaks in thread alternate signal stacks.
- EXPECT_FALSE(Exec(command, &error_msg));
+ ExecResult result = exec_utils_->ExecAndReturnResult(command, /*timeout_sec=*/-1, &error_msg);
+ EXPECT_EQ(result.status, ExecResult::kSignaled);
+ EXPECT_EQ(result.signal, SIGABRT);
EXPECT_FALSE(error_msg.empty());
}
-TEST_F(ExecUtilsTest, EnvSnapshotAdditionsAreNotVisible) {
+TEST_P(ExecUtilsTest, EnvSnapshotAdditionsAreNotVisible) {
static constexpr const char* kModifiedVariable = "EXEC_SHOULD_NOT_EXPORT_THIS";
static constexpr int kOverwrite = 1;
// Set an variable in the current environment.
EXPECT_EQ(setenv(kModifiedVariable, "NEVER", kOverwrite), 0);
// Test that it is not exported.
std::vector<std::string> command;
- if (kIsTargetBuild) {
- std::string android_root(GetAndroidRoot());
- command.push_back(android_root + "/bin/printenv");
- } else {
- command.push_back("/usr/bin/printenv");
- }
+ command.push_back(GetBin("printenv"));
command.push_back(kModifiedVariable);
std::string error_msg;
// Historical note: Running on Valgrind failed due to some memory
// that leaks in thread alternate signal stacks.
- EXPECT_FALSE(Exec(command, &error_msg));
+ EXPECT_FALSE(exec_utils_->Exec(command, &error_msg));
EXPECT_NE(0U, error_msg.size()) << error_msg;
}
-TEST_F(ExecUtilsTest, EnvSnapshotDeletionsAreNotVisible) {
+TEST_P(ExecUtilsTest, EnvSnapshotDeletionsAreNotVisible) {
static constexpr const char* kDeletedVariable = "PATH";
static constexpr int kOverwrite = 1;
// Save the variable's value.
@@ -87,17 +153,12 @@
EXPECT_EQ(unsetenv(kDeletedVariable), 0);
// Test that it is not exported.
std::vector<std::string> command;
- if (kIsTargetBuild) {
- std::string android_root(GetAndroidRoot());
- command.push_back(android_root + "/bin/printenv");
- } else {
- command.push_back("/usr/bin/printenv");
- }
+ command.push_back(GetBin("printenv"));
command.push_back(kDeletedVariable);
std::string error_msg;
// Historical note: Running on Valgrind failed due to some memory
// that leaks in thread alternate signal stacks.
- EXPECT_TRUE(Exec(command, &error_msg));
+ EXPECT_TRUE(exec_utils_->Exec(command, &error_msg));
EXPECT_EQ(0U, error_msg.size()) << error_msg;
// Restore the variable's value.
EXPECT_EQ(setenv(kDeletedVariable, save_value, kOverwrite), 0);
@@ -105,33 +166,106 @@
static std::vector<std::string> SleepCommand(int sleep_seconds) {
std::vector<std::string> command;
- if (kIsTargetBuild) {
- command.push_back(GetAndroidRoot() + "/bin/sleep");
- } else {
- command.push_back("/bin/sleep");
- }
+ command.push_back(GetBin("sleep"));
command.push_back(android::base::StringPrintf("%d", sleep_seconds));
return command;
}
-TEST_F(ExecUtilsTest, ExecTimeout) {
+TEST_P(ExecUtilsTest, ExecTimeout) {
static constexpr int kSleepSeconds = 5;
static constexpr int kWaitSeconds = 1;
std::vector<std::string> command = SleepCommand(kSleepSeconds);
std::string error_msg;
- bool timed_out;
- ASSERT_EQ(ExecAndReturnCode(command, kWaitSeconds, &timed_out, &error_msg), -1);
- EXPECT_TRUE(timed_out);
+ EXPECT_EQ(exec_utils_->ExecAndReturnResult(command, kWaitSeconds, &error_msg).status,
+ ExecResult::kTimedOut)
+ << error_msg;
+ EXPECT_THAT(error_msg, HasSubstr("timed out"));
}
-TEST_F(ExecUtilsTest, ExecNoTimeout) {
+TEST_P(ExecUtilsTest, ExecNoTimeout) {
static constexpr int kSleepSeconds = 1;
static constexpr int kWaitSeconds = 5;
std::vector<std::string> command = SleepCommand(kSleepSeconds);
std::string error_msg;
- bool timed_out;
- ASSERT_EQ(ExecAndReturnCode(command, kWaitSeconds, &timed_out, &error_msg), 0);
- EXPECT_FALSE(timed_out);
+ EXPECT_EQ(exec_utils_->ExecAndReturnResult(command, kWaitSeconds, &error_msg).status,
+ ExecResult::kExited)
+ << error_msg;
}
+TEST_P(ExecUtilsTest, ExecStat) {
+ std::vector<std::string> command;
+ command.push_back(GetBin("id"));
+
+ std::string error_msg;
+ ProcessStat stat;
+
+ // The process filename is "a) b".
+ EXPECT_CALL(*exec_utils_, GetProcStat(_))
+ .WillOnce(Return(
+ "14963 (a) b) Z 6067 14963 1 0 -1 4228108 105 0 0 0 94 5 0 0 39 19 1 0 162034388 0 0 "
+ "18446744073709551615 0 0 0 0 0 0 20999 0 0 1 0 0 17 71 0 0 0 0 0 0 0 0 0 0 0 0 9"));
+ EXPECT_CALL(*exec_utils_, GetUptimeMs()).WillOnce(Return(1620344887ll));
+ EXPECT_CALL(*exec_utils_, GetTicksPerSec()).WillOnce(Return(100));
+
+ ASSERT_EQ(
+ exec_utils_
+ ->ExecAndReturnResult(command, /*timeout_sec=*/-1, ExecCallbacks(), &stat, &error_msg)
+ .status,
+ ExecResult::kExited)
+ << error_msg;
+
+ EXPECT_EQ(stat.cpu_time_ms, 990);
+ EXPECT_EQ(stat.wall_time_ms, 1007);
+}
+
+TEST_P(ExecUtilsTest, ExecStatFailed) {
+ std::vector<std::string> command = SleepCommand(5);
+
+ std::string error_msg;
+ ProcessStat stat;
+
+ EXPECT_CALL(*exec_utils_, GetProcStat(_))
+ .WillOnce(Return(
+ "14963 (a) b) Z 6067 14963 1 0 -1 4228108 105 0 0 0 94 5 0 0 39 19 1 0 162034388 0 0 "
+ "18446744073709551615 0 0 0 0 0 0 20999 0 0 1 0 0 17 71 0 0 0 0 0 0 0 0 0 0 0 0 9"));
+ EXPECT_CALL(*exec_utils_, GetUptimeMs()).WillOnce(Return(1620344887ll));
+ EXPECT_CALL(*exec_utils_, GetTicksPerSec()).WillOnce(Return(100));
+
+ // This will always time out.
+ ASSERT_EQ(
+ exec_utils_
+ ->ExecAndReturnResult(command, /*timeout_sec=*/1, ExecCallbacks(), &stat, &error_msg)
+ .status,
+ ExecResult::kTimedOut);
+
+ EXPECT_EQ(stat.cpu_time_ms, 990);
+ EXPECT_EQ(stat.wall_time_ms, 1007);
+}
+
+TEST_P(ExecUtilsTest, ExecCallbacks) {
+ MockFunction<void(pid_t)> on_start;
+ MockFunction<void(pid_t)> on_end;
+
+ {
+ InSequence s;
+ EXPECT_CALL(on_start, Call(AllOf(Gt(0), Ne(getpid()))));
+ EXPECT_CALL(on_end, Call(AllOf(Gt(0), Ne(getpid()))));
+ }
+
+ std::vector<std::string> command;
+ command.push_back(GetBin("id"));
+
+ std::string error_msg;
+ exec_utils_->ExecAndReturnResult(command,
+ /*timeout_sec=*/-1,
+ ExecCallbacks{
+ .on_start = on_start.AsStdFunction(),
+ .on_end = on_end.AsStdFunction(),
+ },
+ /*stat=*/nullptr,
+ &error_msg);
+}
+
+INSTANTIATE_TEST_SUITE_P(AlwaysOrNeverFallback, ExecUtilsTest, testing::Values(true, false));
+
} // namespace art
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index f8bd213..dd28f36 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -16,12 +16,14 @@
#include "fault_handler.h"
+#include <atomic>
#include <string.h>
#include <sys/mman.h>
#include <sys/ucontext.h>
#include "art_method-inl.h"
#include "base/logging.h" // For VLOG
+#include "base/membarrier.h"
#include "base/safe_copy.h"
#include "base/stl_util.h"
#include "dex/dex_file_types.h"
@@ -51,82 +53,10 @@
return fault_manager.HandleFault(sig, info, context);
}
-#if defined(__linux__)
-
-// Change to verify the safe implementations against the original ones.
-constexpr bool kVerifySafeImpls = false;
-
-// Provide implementations of ArtMethod::GetDeclaringClass and VerifyClassClass that use SafeCopy
-// to safely dereference pointers which are potentially garbage.
-// Only available on Linux due to availability of SafeCopy.
-
-static mirror::Class* SafeGetDeclaringClass(ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- char* method_declaring_class =
- reinterpret_cast<char*>(method) + ArtMethod::DeclaringClassOffset().SizeValue();
-
- // ArtMethod::declaring_class_ is a GcRoot<mirror::Class>.
- // Read it out into as a CompressedReference directly for simplicity's sake.
- mirror::CompressedReference<mirror::Class> cls;
- ssize_t rc = SafeCopy(&cls, method_declaring_class, sizeof(cls));
- CHECK_NE(-1, rc);
-
- if (kVerifySafeImpls) {
- ObjPtr<mirror::Class> actual_class = method->GetDeclaringClassUnchecked<kWithoutReadBarrier>();
- CHECK_EQ(actual_class, cls.AsMirrorPtr());
- }
-
- if (rc != sizeof(cls)) {
- return nullptr;
- }
-
- return cls.AsMirrorPtr();
-}
-
-static mirror::Class* SafeGetClass(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
- char* obj_cls = reinterpret_cast<char*>(obj) + mirror::Object::ClassOffset().SizeValue();
-
- mirror::HeapReference<mirror::Class> cls;
- ssize_t rc = SafeCopy(&cls, obj_cls, sizeof(cls));
- CHECK_NE(-1, rc);
-
- if (kVerifySafeImpls) {
- mirror::Class* actual_class = obj->GetClass<kVerifyNone>();
- CHECK_EQ(actual_class, cls.AsMirrorPtr());
- }
-
- if (rc != sizeof(cls)) {
- return nullptr;
- }
-
- return cls.AsMirrorPtr();
-}
-
-static bool SafeVerifyClassClass(mirror::Class* cls) REQUIRES_SHARED(Locks::mutator_lock_) {
- mirror::Class* c_c = SafeGetClass(cls);
- bool result = c_c != nullptr && c_c == SafeGetClass(c_c);
-
- if (kVerifySafeImpls) {
- CHECK_EQ(VerifyClassClass(cls), result);
- }
-
- return result;
-}
-
-#else
-
-static mirror::Class* SafeGetDeclaringClass(ArtMethod* method_obj)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- return method_obj->GetDeclaringClassUnchecked<kWithoutReadBarrier>().Ptr();
-}
-
-static bool SafeVerifyClassClass(mirror::Class* cls) REQUIRES_SHARED(Locks::mutator_lock_) {
- return VerifyClassClass(cls);
-}
-#endif
-
-
-FaultManager::FaultManager() : initialized_(false) {
+FaultManager::FaultManager()
+ : generated_code_ranges_lock_("FaultHandler generated code ranges lock",
+ LockLevel::kGenericBottomLock),
+ initialized_(false) {
sigaction(SIGSEGV, nullptr, &oldaction_);
}
@@ -150,6 +80,27 @@
};
AddSpecialSignalHandlerFn(SIGSEGV, &sa);
+
+ // Notify the kernel that we intend to use a specific `membarrier()` command.
+ int result = art::membarrier(MembarrierCommand::kRegisterPrivateExpedited);
+ if (result != 0) {
+ LOG(WARNING) << "FaultHandler: MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED failed: "
+ << errno << " " << strerror(errno);
+ }
+
+ {
+ MutexLock lock(Thread::Current(), generated_code_ranges_lock_);
+ for (size_t i = 0; i != kNumLocalGeneratedCodeRanges; ++i) {
+ GeneratedCodeRange* next = (i + 1u != kNumLocalGeneratedCodeRanges)
+ ? &generated_code_ranges_storage_[i + 1u]
+ : nullptr;
+ generated_code_ranges_storage_[i].next.store(next, std::memory_order_relaxed);
+ generated_code_ranges_storage_[i].start = nullptr;
+ generated_code_ranges_storage_[i].size = 0u;
+ }
+ free_generated_code_ranges_ = generated_code_ranges_storage_;
+ }
+
initialized_ = true;
}
@@ -167,6 +118,24 @@
// Free all handlers.
STLDeleteElements(&generated_code_handlers_);
STLDeleteElements(&other_handlers_);
+
+ // Delete remaining code ranges if any (such as nterp code or oat code from
+ // oat files that have not been unloaded, including boot image oat files).
+ MutexLock lock(Thread::Current(), generated_code_ranges_lock_);
+ GeneratedCodeRange* range = generated_code_ranges_.load(std::memory_order_acquire);
+ generated_code_ranges_.store(nullptr, std::memory_order_release);
+ while (range != nullptr) {
+ GeneratedCodeRange* next_range = range->next.load(std::memory_order_relaxed);
+ std::less<GeneratedCodeRange*> less;
+ if (!less(range, generated_code_ranges_storage_) &&
+ less(range, generated_code_ranges_storage_ + kNumLocalGeneratedCodeRanges)) {
+ // Nothing to do - not adding `range` to the `free_generated_code_ranges_` anymore.
+ } else {
+ // Range is not in the `generated_code_ranges_storage_`.
+ delete range;
+ }
+ range = next_range;
+ }
}
}
@@ -221,7 +190,7 @@
raise(SIGSEGV);
#endif
- if (IsInGeneratedCode(info, context, true)) {
+ if (IsInGeneratedCode(info, context)) {
VLOG(signals) << "in generated code, looking for handler";
for (const auto& handler : generated_code_handlers_) {
VLOG(signals) << "invoking Action on handler " << handler;
@@ -268,37 +237,143 @@
LOG(FATAL) << "Attempted to remove non existent handler " << handler;
}
-static bool IsKnownPc(uintptr_t pc, ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
- // Check whether the pc is within nterp range.
- if (OatQuickMethodHeader::IsNterpPc(pc)) {
- return true;
+inline FaultManager::GeneratedCodeRange* FaultManager::CreateGeneratedCodeRange(
+ const void* start, size_t size) {
+ GeneratedCodeRange* range = free_generated_code_ranges_;
+ if (range != nullptr) {
+ std::less<GeneratedCodeRange*> less;
+ DCHECK(!less(range, generated_code_ranges_storage_));
+ DCHECK(less(range, generated_code_ranges_storage_ + kNumLocalGeneratedCodeRanges));
+ range->start = start;
+ range->size = size;
+ free_generated_code_ranges_ = range->next.load(std::memory_order_relaxed);
+ range->next.store(nullptr, std::memory_order_relaxed);
+ return range;
+ } else {
+ return new GeneratedCodeRange{nullptr, start, size};
}
-
- // Check whether the pc is in the JIT code cache.
- jit::Jit* jit = Runtime::Current()->GetJit();
- if (jit != nullptr && jit->GetCodeCache()->ContainsPc(reinterpret_cast<const void*>(pc))) {
- return true;
- }
-
- if (method->IsObsolete()) {
- // Obsolete methods never happen on AOT code.
- return false;
- }
-
- // Note: at this point, we trust it's truly an ArtMethod we found at the bottom of the stack,
- // and we can find its oat file through it.
- const OatDexFile* oat_dex_file = method->GetDeclaringClass()->GetDexFile().GetOatDexFile();
- if (oat_dex_file != nullptr &&
- oat_dex_file->GetOatFile()->Contains(reinterpret_cast<const void*>(pc))) {
- return true;
- }
-
- return false;
}
-// This function is called within the signal handler. It checks that
-// the mutator_lock is held (shared). No annotalysis is done.
-bool FaultManager::IsInGeneratedCode(siginfo_t* siginfo, void* context, bool check_dex_pc) {
+inline void FaultManager::FreeGeneratedCodeRange(GeneratedCodeRange* range) {
+ std::less<GeneratedCodeRange*> less;
+ if (!less(range, generated_code_ranges_storage_) &&
+ less(range, generated_code_ranges_storage_ + kNumLocalGeneratedCodeRanges)) {
+ MutexLock lock(Thread::Current(), generated_code_ranges_lock_);
+ range->start = nullptr;
+ range->size = 0u;
+ range->next.store(free_generated_code_ranges_, std::memory_order_relaxed);
+ free_generated_code_ranges_ = range;
+ } else {
+ // Range is not in the `generated_code_ranges_storage_`.
+ delete range;
+ }
+}
+
+void FaultManager::AddGeneratedCodeRange(const void* start, size_t size) {
+ GeneratedCodeRange* new_range = nullptr;
+ {
+ MutexLock lock(Thread::Current(), generated_code_ranges_lock_);
+ new_range = CreateGeneratedCodeRange(start, size);
+ GeneratedCodeRange* old_head = generated_code_ranges_.load(std::memory_order_relaxed);
+ new_range->next.store(old_head, std::memory_order_relaxed);
+ generated_code_ranges_.store(new_range, std::memory_order_release);
+ }
+
+ // The above release operation on `generated_code_ranges_` with an acquire operation
+ // on the same atomic object in `IsInGeneratedCode()` ensures the correct memory
+ // visibility for the contents of `*new_range` for any thread that loads the value
+ // written above (or a value written by a release sequence headed by that write).
+ //
+ // However, we also need to ensure that any thread that encounters a segmentation
+ // fault in the provided range shall actually see the written value. For JIT code
+ // cache and nterp, the registration happens while the process is single-threaded
+ // but the synchronization is more complicated for code in oat files.
+ //
+ // Threads that load classes register dex files under the `Locks::dex_lock_` and
+ // the first one to register a dex file with a given oat file shall add the oat
+ // code range; the memory visibility for these threads is guaranteed by the lock.
+ // However a thread that did not try to load a class with oat code can execute the
+ // code if a direct or indirect reference to such class escapes from one of the
+ // threads that loaded it. Use `membarrier()` for memory visibility in this case.
+ art::membarrier(MembarrierCommand::kPrivateExpedited);
+}
+
+void FaultManager::RemoveGeneratedCodeRange(const void* start, size_t size) {
+ Thread* self = Thread::Current();
+ GeneratedCodeRange* range = nullptr;
+ {
+ MutexLock lock(self, generated_code_ranges_lock_);
+ std::atomic<GeneratedCodeRange*>* before = &generated_code_ranges_;
+ range = before->load(std::memory_order_relaxed);
+ while (range != nullptr && range->start != start) {
+ before = &range->next;
+ range = before->load(std::memory_order_relaxed);
+ }
+ if (range != nullptr) {
+ GeneratedCodeRange* next = range->next.load(std::memory_order_relaxed);
+ if (before == &generated_code_ranges_) {
+ // Relaxed store directly to `generated_code_ranges_` would not satisfy
+ // conditions for a release sequence, so we need to use store-release.
+ before->store(next, std::memory_order_release);
+ } else {
+ // In the middle of the list, we can use a relaxed store as we're not
+ // publishing any newly written memory to potential reader threads.
+ // Whether they see the removed node or not is unimportant as we should
+ // not execute that code anymore. We're keeping the `next` link of the
+ // removed node, so that concurrent walk can use it to reach remaining
+ // retained nodes, if any.
+ before->store(next, std::memory_order_relaxed);
+ }
+ }
+ }
+ CHECK(range != nullptr);
+ DCHECK_EQ(range->start, start);
+ CHECK_EQ(range->size, size);
+
+ Runtime* runtime = Runtime::Current();
+ CHECK(runtime != nullptr);
+ if (runtime->IsStarted() && runtime->GetThreadList() != nullptr) {
+ // Run a checkpoint before deleting the range to ensure that no thread holds a
+ // pointer to the removed range while walking the list in `IsInGeneratedCode()`.
+ // That walk is guarded by checking that the thread is `Runnable`, so any walk
+ // started before the removal shall be done when running the checkpoint and the
+ // checkpoint also ensures the correct memory visibility of `next` links,
+ // so the thread shall not see the pointer during future walks.
+
+ // This function is currently called in different mutex and thread states.
+ // Semi-space GC performs the cleanup during its `MarkingPhase()` while holding
+ // the mutator exclusively, so we do not need a checkpoint. All other GCs perform
+ // the cleanup in their `ReclaimPhase()` while holding the mutator lock as shared
+ // and it's safe to release and re-acquire the mutator lock. Despite holding the
+ // mutator lock as shared, the thread is not always marked as `Runnable`.
+ // TODO: Clean up state transitions in different GC implementations. b/259440389
+ if (Locks::mutator_lock_->IsExclusiveHeld(self)) {
+ // We do not need a checkpoint because no other thread is Runnable.
+ } else {
+ DCHECK(Locks::mutator_lock_->IsSharedHeld(self));
+ // Use explicit state transitions or unlock/lock.
+ bool runnable = (self->GetState() == ThreadState::kRunnable);
+ if (runnable) {
+ self->TransitionFromRunnableToSuspended(ThreadState::kNative);
+ } else {
+ Locks::mutator_lock_->SharedUnlock(self);
+ }
+ DCHECK(!Locks::mutator_lock_->IsSharedHeld(self));
+ runtime->GetThreadList()->RunEmptyCheckpoint();
+ if (runnable) {
+ self->TransitionFromSuspendedToRunnable();
+ } else {
+ Locks::mutator_lock_->SharedLock(self);
+ }
+ }
+ }
+ FreeGeneratedCodeRange(range);
+}
+
+// This function is called within the signal handler. It checks that the thread
+// is `Runnable`, the `mutator_lock_` is held (shared) and the fault PC is in one
+// of the registered generated code ranges. No annotalysis is done.
+bool FaultManager::IsInGeneratedCode(siginfo_t* siginfo, void* context) {
// We can only be running Java code in the current thread if it
// is in Runnable state.
VLOG(signals) << "Checking for generated code";
@@ -321,76 +396,29 @@
return false;
}
- ArtMethod* method_obj = nullptr;
- uintptr_t return_pc = 0;
- uintptr_t sp = 0;
- bool is_stack_overflow = false;
-
- // Get the architecture specific method address and return address. These
- // are in architecture specific files in arch/<arch>/fault_handler_<arch>.
- GetMethodAndReturnPcAndSp(siginfo, context, &method_obj, &return_pc, &sp, &is_stack_overflow);
-
- // If we don't have a potential method, we're outta here.
- VLOG(signals) << "potential method: " << method_obj;
- // TODO: Check linear alloc and image.
- DCHECK_ALIGNED(ArtMethod::Size(kRuntimePointerSize), sizeof(void*))
- << "ArtMethod is not pointer aligned";
- if (method_obj == nullptr || !IsAligned<sizeof(void*)>(method_obj)) {
- VLOG(signals) << "no method";
+ uintptr_t fault_pc = GetFaultPc(siginfo, context);
+ if (fault_pc == 0u) {
+ VLOG(signals) << "no fault PC";
return false;
}
- // Verify that the potential method is indeed a method.
- // TODO: check the GC maps to make sure it's an object.
- // Check that the class pointer inside the object is not null and is aligned.
- // No read barrier because method_obj may not be a real object.
- mirror::Class* cls = SafeGetDeclaringClass(method_obj);
- if (cls == nullptr) {
- VLOG(signals) << "not a class";
- return false;
+ // Walk over the list of registered code ranges.
+ GeneratedCodeRange* range = generated_code_ranges_.load(std::memory_order_acquire);
+ while (range != nullptr) {
+ if (fault_pc - reinterpret_cast<uintptr_t>(range->start) < range->size) {
+ return true;
+ }
+ // We may or may not see ranges that were concurrently removed, depending
+ // on when the relaxed writes of the `next` links become visible. However,
+ // even if we're currently at a node that is being removed, we shall visit
+ // all remaining ranges that are not being removed as the removed nodes
+ // retain the `next` link at the time of removal (which may lead to other
+ // removed nodes before reaching remaining retained nodes, if any). Correct
+ // memory visibility of `start` and `size` fields of the visited ranges is
+ // ensured by the release and acquire operations on `generated_code_ranges_`.
+ range = range->next.load(std::memory_order_relaxed);
}
-
- if (!IsAligned<kObjectAlignment>(cls)) {
- VLOG(signals) << "not aligned";
- return false;
- }
-
- if (!SafeVerifyClassClass(cls)) {
- VLOG(signals) << "not a class class";
- return false;
- }
-
- if (!IsKnownPc(return_pc, method_obj)) {
- VLOG(signals) << "PC not in Java code";
- return false;
- }
-
- const OatQuickMethodHeader* method_header = method_obj->GetOatQuickMethodHeader(return_pc);
-
- if (method_header == nullptr) {
- VLOG(signals) << "no compiled code";
- return false;
- }
-
- // We can be certain that this is a method now. Check if we have a GC map
- // at the return PC address.
- if (true || kIsDebugBuild) {
- VLOG(signals) << "looking for dex pc for return pc " << std::hex << return_pc;
- uint32_t sought_offset = return_pc -
- reinterpret_cast<uintptr_t>(method_header->GetEntryPoint());
- VLOG(signals) << "pc offset: " << std::hex << sought_offset;
- }
- uint32_t dexpc = dex::kDexNoIndex;
- if (is_stack_overflow) {
- // If it's an implicit stack overflow check, the frame is not setup, so we
- // just infer the dex PC as zero.
- dexpc = 0;
- } else {
- CHECK_EQ(*reinterpret_cast<ArtMethod**>(sp), method_obj);
- dexpc = method_header->ToDexPc(reinterpret_cast<ArtMethod**>(sp), return_pc, false);
- }
- VLOG(signals) << "dexpc: " << dexpc;
- return !check_dex_pc || dexpc != dex::kDexNoIndex;
+ return false;
}
FaultHandler::FaultHandler(FaultManager* manager) : manager_(manager) {
@@ -403,6 +431,76 @@
manager_->AddHandler(this, true);
}
+bool NullPointerHandler::IsValidMethod(ArtMethod* method) {
+ // At this point we know that the thread is `Runnable` and the PC is in one of
+ // the registered code ranges. The `method` was read from the top of the stack
+ // and should really point to an actual `ArtMethod`, unless we're crashing during
+ // prologue or epilogue, or somehow managed to jump to the compiled code by some
+ // unexpected path, other than method invoke or exception delivery. We do a few
+ // quick checks without guarding from another fault.
+ VLOG(signals) << "potential method: " << method;
+
+ static_assert(IsAligned<sizeof(void*)>(ArtMethod::Size(kRuntimePointerSize)));
+ if (method == nullptr || !IsAligned<sizeof(void*)>(method)) {
+ VLOG(signals) << ((method == nullptr) ? "null method" : "unaligned method");
+ return false;
+ }
+
+ // Check that the presumed method actually points to a class. Read barriers
+ // are not needed (and would be undesirable in a signal handler) when reading
+ // a chain of constant references to get to a non-movable `Class.class` object.
+
+ // Note: Allowing nested faults. Checking that the method is in one of the
+ // `LinearAlloc` spaces, or that objects we look at are in the `Heap` would be
+ // slow and require locking a mutex, which is undesirable in a signal handler.
+ // (Though we could register valid ranges similarly to the generated code ranges.)
+
+ mirror::Object* klass =
+ method->GetDeclaringClassAddressWithoutBarrier()->AsMirrorPtr();
+ if (klass == nullptr || !IsAligned<kObjectAlignment>(klass)) {
+ VLOG(signals) << ((klass == nullptr) ? "null class" : "unaligned class");
+ return false;
+ }
+
+ mirror::Class* class_class = klass->GetClass<kVerifyNone, kWithoutReadBarrier>();
+ if (class_class == nullptr || !IsAligned<kObjectAlignment>(class_class)) {
+ VLOG(signals) << ((klass == nullptr) ? "null class_class" : "unaligned class_class");
+ return false;
+ }
+
+ if (class_class != class_class->GetClass<kVerifyNone, kWithoutReadBarrier>()) {
+ VLOG(signals) << "invalid class_class";
+ return false;
+ }
+
+ return true;
+}
+
+bool NullPointerHandler::IsValidReturnPc(ArtMethod** sp, uintptr_t return_pc) {
+ // Check if we can associate a dex PC with the return PC, whether from Nterp,
+ // or with an existing stack map entry for a compiled method.
+ // Note: Allowing nested faults if `IsValidMethod()` returned a false positive.
+ // Note: The `ArtMethod::GetOatQuickMethodHeader()` can acquire locks (at least
+ // `Locks::jit_lock_`) and if the thread already held such a lock, the signal
+ // handler would deadlock. However, if a thread is holding one of the locks
+ // below the mutator lock, the PC should be somewhere in ART code and should
+ // not match any registered generated code range, so such as a deadlock is
+ // unlikely. If it happens anyway, the worst case is that an internal ART crash
+ // would be reported as ANR.
+ ArtMethod* method = *sp;
+ const OatQuickMethodHeader* method_header = method->GetOatQuickMethodHeader(return_pc);
+ if (method_header == nullptr) {
+ VLOG(signals) << "No method header.";
+ return false;
+ }
+ VLOG(signals) << "looking for dex pc for return pc 0x" << std::hex << return_pc
+ << " pc offset: 0x" << std::hex
+ << (return_pc - reinterpret_cast<uintptr_t>(method_header->GetEntryPoint()));
+ uint32_t dexpc = method_header->ToDexPc(reinterpret_cast<ArtMethod**>(sp), return_pc, false);
+ VLOG(signals) << "dexpc: " << dexpc;
+ return dexpc != dex::kDexNoIndex;
+}
+
//
// Suspension fault handler
//
@@ -426,17 +524,13 @@
bool JavaStackTraceHandler::Action(int sig ATTRIBUTE_UNUSED, siginfo_t* siginfo, void* context) {
// Make sure that we are in the generated code, but we may not have a dex pc.
- bool in_generated_code = manager_->IsInGeneratedCode(siginfo, context, false);
+ bool in_generated_code = manager_->IsInGeneratedCode(siginfo, context);
if (in_generated_code) {
LOG(ERROR) << "Dumping java stack trace for crash in generated code";
- ArtMethod* method = nullptr;
- uintptr_t return_pc = 0;
- uintptr_t sp = 0;
- bool is_stack_overflow = false;
Thread* self = Thread::Current();
- manager_->GetMethodAndReturnPcAndSp(
- siginfo, context, &method, &return_pc, &sp, &is_stack_overflow);
+ uintptr_t sp = FaultManager::GetFaultSp(context);
+ CHECK_NE(sp, 0u); // Otherwise we should not have reached this handler.
// Inside of generated code, sp[0] is the method, so sp is the frame.
self->SetTopOfStack(reinterpret_cast<ArtMethod**>(sp));
self->DumpJavaStack(LOG_STREAM(ERROR));
diff --git a/runtime/fault_handler.h b/runtime/fault_handler.h
index 8b89c22..43f93e4 100644
--- a/runtime/fault_handler.h
+++ b/runtime/fault_handler.h
@@ -21,9 +21,11 @@
#include <signal.h>
#include <stdint.h>
+#include <atomic>
#include <vector>
#include "base/locks.h" // For annotalysis.
+#include "base/mutex.h"
#include "runtime_globals.h" // For CanDoImplicitNullCheckOn.
namespace art {
@@ -51,29 +53,57 @@
void AddHandler(FaultHandler* handler, bool generated_code);
void RemoveHandler(FaultHandler* handler);
- // Note that the following two functions are called in the context of a signal handler.
- // The IsInGeneratedCode() function checks that the mutator lock is held before it
- // calls GetMethodAndReturnPCAndSP().
- // TODO: think about adding lock assertions and fake lock and unlock functions.
- void GetMethodAndReturnPcAndSp(siginfo_t* siginfo,
- void* context,
- ArtMethod** out_method,
- uintptr_t* out_return_pc,
- uintptr_t* out_sp,
- bool* out_is_stack_overflow)
- NO_THREAD_SAFETY_ANALYSIS;
- bool IsInGeneratedCode(siginfo_t* siginfo, void *context, bool check_dex_pc)
- NO_THREAD_SAFETY_ANALYSIS;
+ void AddGeneratedCodeRange(const void* start, size_t size);
+ void RemoveGeneratedCodeRange(const void* start, size_t size)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Retrieves fault PC from architecture-dependent `context`, returns 0 on failure.
+ // Called in the context of a signal handler.
+ static uintptr_t GetFaultPc(siginfo_t* siginfo, void* context);
+
+ // Retrieves SP from architecture-dependent `context`.
+ // Called in the context of a signal handler.
+ static uintptr_t GetFaultSp(void* context);
+
+ // Checks if the fault happened while running generated code.
+ // Called in the context of a signal handler.
+ bool IsInGeneratedCode(siginfo_t* siginfo, void *context) NO_THREAD_SAFETY_ANALYSIS;
private:
+ struct GeneratedCodeRange {
+ std::atomic<GeneratedCodeRange*> next;
+ const void* start;
+ size_t size;
+ };
+
+ GeneratedCodeRange* CreateGeneratedCodeRange(const void* start, size_t size)
+ REQUIRES(generated_code_ranges_lock_);
+ void FreeGeneratedCodeRange(GeneratedCodeRange* range) REQUIRES(!generated_code_ranges_lock_);
+
// The HandleFaultByOtherHandlers function is only called by HandleFault function for generated code.
bool HandleFaultByOtherHandlers(int sig, siginfo_t* info, void* context)
NO_THREAD_SAFETY_ANALYSIS;
+ // Note: The lock guards modifications of the ranges but the function `IsInGeneratedCode()`
+ // walks the list in the context of a signal handler without holding the lock.
+ Mutex generated_code_ranges_lock_;
+ std::atomic<GeneratedCodeRange*> generated_code_ranges_ GUARDED_BY(generated_code_ranges_lock_);
+
std::vector<FaultHandler*> generated_code_handlers_;
std::vector<FaultHandler*> other_handlers_;
struct sigaction oldaction_;
bool initialized_;
+
+ // We keep a certain number of generated code ranges locally to avoid too many
+ // cache misses while traversing the singly-linked list `generated_code_ranges_`.
+ // 16 should be enough for the boot image (assuming `--multi-image`; there is
+ // only one entry for `--single-image`), nterp, JIT code cache and a few other
+ // entries for the app or system server.
+ static constexpr size_t kNumLocalGeneratedCodeRanges = 16;
+ GeneratedCodeRange generated_code_ranges_storage_[kNumLocalGeneratedCodeRanges];
+ GeneratedCodeRange* free_generated_code_ranges_
+ GUARDED_BY(generated_code_ranges_lock_);
+
DISALLOW_COPY_AND_ASSIGN(FaultManager);
};
@@ -98,17 +128,29 @@
public:
explicit NullPointerHandler(FaultManager* manager);
- bool Action(int sig, siginfo_t* siginfo, void* context) override;
-
- static bool IsValidImplicitCheck(siginfo_t* siginfo) {
- // Our implicit NPE checks always limit the range to a page.
- // Note that the runtime will do more exhaustive checks (that we cannot
- // reasonably do in signal processing code) based on the dex instruction
- // faulting.
- return CanDoImplicitNullCheckOn(reinterpret_cast<uintptr_t>(siginfo->si_addr));
- }
+ // NO_THREAD_SAFETY_ANALYSIS: Called after the fault manager determined that
+ // the thread is `Runnable` and holds the mutator lock (shared) but without
+ // telling annotalysis that we actually hold the lock.
+ bool Action(int sig, siginfo_t* siginfo, void* context) override
+ NO_THREAD_SAFETY_ANALYSIS;
private:
+ // Helper functions for checking whether the signal can be interpreted
+ // as implicit NPE check. Note that the runtime will do more exhaustive
+ // checks (that we cannot reasonably do in signal processing code) based
+ // on the dex instruction faulting.
+
+ static bool IsValidFaultAddress(uintptr_t fault_address) {
+ // Our implicit NPE checks always limit the range to a page.
+ return CanDoImplicitNullCheckOn(fault_address);
+ }
+
+ static bool IsValidMethod(ArtMethod* method)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ static bool IsValidReturnPc(ArtMethod** sp, uintptr_t return_pc)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
DISALLOW_COPY_AND_ASSIGN(NullPointerHandler);
};
diff --git a/runtime/gc/accounting/atomic_stack.h b/runtime/gc/accounting/atomic_stack.h
index 5e6bd88..a90a319 100644
--- a/runtime/gc/accounting/atomic_stack.h
+++ b/runtime/gc/accounting/atomic_stack.h
@@ -130,6 +130,35 @@
}
}
+ // Bump the back index by the given number of slots. Returns false if this
+ // operation will overflow the stack. New elements should be written
+ // to [*start_address, *end_address).
+ bool BumpBack(size_t num_slots,
+ StackReference<T>** start_address,
+ StackReference<T>** end_address)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (kIsDebugBuild) {
+ debug_is_sorted_ = false;
+ }
+ const int32_t index = back_index_.load(std::memory_order_relaxed);
+ const int32_t new_index = index + num_slots;
+ if (UNLIKELY(static_cast<size_t>(new_index) >= growth_limit_)) {
+ // Stack overflow.
+ return false;
+ }
+ back_index_.store(new_index, std::memory_order_relaxed);
+ *start_address = begin_ + index;
+ *end_address = begin_ + new_index;
+ if (kIsDebugBuild) {
+ // Check the memory is zero.
+ for (int32_t i = index; i < new_index; i++) {
+ DCHECK_EQ(begin_[i].AsMirrorPtr(), static_cast<T*>(nullptr))
+ << "i=" << i << " index=" << index << " new_index=" << new_index;
+ }
+ }
+ return true;
+ }
+
void PushBack(T* value) REQUIRES_SHARED(Locks::mutator_lock_) {
if (kIsDebugBuild) {
debug_is_sorted_ = false;
@@ -144,8 +173,16 @@
DCHECK_GT(back_index_.load(std::memory_order_relaxed),
front_index_.load(std::memory_order_relaxed));
// Decrement the back index non atomically.
- back_index_.store(back_index_.load(std::memory_order_relaxed) - 1, std::memory_order_relaxed);
- return begin_[back_index_.load(std::memory_order_relaxed)].AsMirrorPtr();
+ const int32_t index = back_index_.load(std::memory_order_relaxed) - 1;
+ back_index_.store(index, std::memory_order_relaxed);
+ T* ret = begin_[index].AsMirrorPtr();
+ // In debug builds we expect the stack elements to be null, which may not
+ // always be the case if the stack is being reused without resetting it
+ // in-between.
+ if (kIsDebugBuild) {
+ begin_[index].Clear();
+ }
+ return ret;
}
// Take an item from the front of the stack.
diff --git a/runtime/gc/accounting/bitmap.cc b/runtime/gc/accounting/bitmap.cc
index 37646b3..bd10958 100644
--- a/runtime/gc/accounting/bitmap.cc
+++ b/runtime/gc/accounting/bitmap.cc
@@ -21,6 +21,7 @@
#include "base/bit_utils.h"
#include "base/mem_map.h"
#include "card_table.h"
+#include "gc/collector/mark_compact.h"
#include "jit/jit_memory_region.h"
namespace art {
@@ -98,6 +99,7 @@
template class MemoryRangeBitmap<CardTable::kCardSize>;
template class MemoryRangeBitmap<jit::kJitCodeAccountingBytes>;
+template class MemoryRangeBitmap<collector::MarkCompact::kAlignment>;
} // namespace accounting
} // namespace gc
diff --git a/runtime/gc/accounting/bitmap.h b/runtime/gc/accounting/bitmap.h
index 68f2d04..06398d6 100644
--- a/runtime/gc/accounting/bitmap.h
+++ b/runtime/gc/accounting/bitmap.h
@@ -81,7 +81,7 @@
void CopyFrom(Bitmap* source_bitmap);
// Starting address of our internal storage.
- uintptr_t* Begin() {
+ uintptr_t* Begin() const {
return bitmap_begin_;
}
@@ -98,7 +98,7 @@
std::string Dump() const;
protected:
- static constexpr size_t kBitsPerBitmapWord = sizeof(uintptr_t) * kBitsPerByte;
+ static constexpr size_t kBitsPerBitmapWord = kBitsPerIntPtrT;
Bitmap(MemMap&& mem_map, size_t bitmap_size);
~Bitmap();
@@ -109,7 +109,9 @@
template<bool kSetBit>
ALWAYS_INLINE bool ModifyBit(uintptr_t bit_index);
- // Backing storage for bitmap.
+ // Backing storage for bitmap. This is interpreted as an array of
+ // kBitsPerBitmapWord-sized integers, with bits assigned in each word little
+ // endian first.
MemMap mem_map_;
// This bitmap itself, word sized for efficiency in scanning.
@@ -122,7 +124,7 @@
DISALLOW_IMPLICIT_CONSTRUCTORS(Bitmap);
};
-// One bit per kAlignment in range (start, end]
+// One bit per kAlignment in range [start, end)
template<size_t kAlignment>
class MemoryRangeBitmap : public Bitmap {
public:
@@ -138,7 +140,7 @@
// End of the memory range that the bitmap covers.
ALWAYS_INLINE uintptr_t CoverEnd() const {
- return cover_end_;
+ return cover_begin_ + kAlignment * BitmapSize();
}
// Return the address associated with a bit index.
@@ -150,39 +152,47 @@
// Return the bit index associated with an address .
ALWAYS_INLINE uintptr_t BitIndexFromAddr(uintptr_t addr) const {
- DCHECK(HasAddress(addr)) << CoverBegin() << " <= " << addr << " < " << CoverEnd();
- return (addr - CoverBegin()) / kAlignment;
+ uintptr_t result = (addr - CoverBegin()) / kAlignment;
+ DCHECK(result < BitmapSize()) << CoverBegin() << " <= " << addr << " < " << CoverEnd();
+ return result;
}
ALWAYS_INLINE bool HasAddress(const uintptr_t addr) const {
- return cover_begin_ <= addr && addr < cover_end_;
+ // Don't use BitIndexFromAddr() here as the addr passed to this function
+ // could be outside the range. If addr < cover_begin_, then the result
+ // underflows to some very large value past the end of the bitmap.
+ // Therefore, all operations are unsigned here.
+ bool ret = (addr - CoverBegin()) / kAlignment < BitmapSize();
+ if (ret) {
+ DCHECK(CoverBegin() <= addr && addr < CoverEnd())
+ << CoverBegin() << " <= " << addr << " < " << CoverEnd();
+ }
+ return ret;
}
ALWAYS_INLINE bool Set(uintptr_t addr) {
return SetBit(BitIndexFromAddr(addr));
}
- ALWAYS_INLINE bool Clear(size_t addr) {
+ ALWAYS_INLINE bool Clear(uintptr_t addr) {
return ClearBit(BitIndexFromAddr(addr));
}
- ALWAYS_INLINE bool Test(size_t addr) const {
+ ALWAYS_INLINE bool Test(uintptr_t addr) const {
return TestBit(BitIndexFromAddr(addr));
}
// Returns true if the object was previously set.
- ALWAYS_INLINE bool AtomicTestAndSet(size_t addr) {
+ ALWAYS_INLINE bool AtomicTestAndSet(uintptr_t addr) {
return AtomicTestAndSetBit(BitIndexFromAddr(addr));
}
private:
MemoryRangeBitmap(MemMap&& mem_map, uintptr_t begin, size_t num_bits)
: Bitmap(std::move(mem_map), num_bits),
- cover_begin_(begin),
- cover_end_(begin + kAlignment * num_bits) {}
+ cover_begin_(begin) {}
uintptr_t const cover_begin_;
- uintptr_t const cover_end_;
DISALLOW_IMPLICIT_CONSTRUCTORS(MemoryRangeBitmap);
};
diff --git a/runtime/gc/accounting/card_table.cc b/runtime/gc/accounting/card_table.cc
index fdf1615..b8b328c 100644
--- a/runtime/gc/accounting/card_table.cc
+++ b/runtime/gc/accounting/card_table.cc
@@ -31,11 +31,6 @@
namespace gc {
namespace accounting {
-constexpr size_t CardTable::kCardShift;
-constexpr size_t CardTable::kCardSize;
-constexpr uint8_t CardTable::kCardClean;
-constexpr uint8_t CardTable::kCardDirty;
-
/*
* Maintain a card table from the write barrier. All writes of
* non-null values to heap addresses should go through an entry in
diff --git a/runtime/gc/accounting/card_table_test.cc b/runtime/gc/accounting/card_table_test.cc
index 12baaa4..b34a883 100644
--- a/runtime/gc/accounting/card_table_test.cc
+++ b/runtime/gc/accounting/card_table_test.cc
@@ -19,8 +19,8 @@
#include <string>
#include "base/atomic.h"
+#include "base/common_art_test.h"
#include "base/utils.h"
-#include "common_runtime_test.h"
#include "handle_scope-inl.h"
#include "mirror/class-inl.h"
#include "mirror/string-inl.h" // Strings are easiest to allocate
@@ -36,7 +36,7 @@
namespace gc {
namespace accounting {
-class CardTableTest : public CommonRuntimeTest {
+class CardTableTest : public CommonArtTest {
public:
std::unique_ptr<CardTable> card_table_;
diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc
index b4026fc..4a84799 100644
--- a/runtime/gc/accounting/mod_union_table.cc
+++ b/runtime/gc/accounting/mod_union_table.cc
@@ -388,6 +388,11 @@
void ModUnionTableReferenceCache::VisitObjects(ObjectCallback callback, void* arg) {
CardTable* const card_table = heap_->GetCardTable();
ContinuousSpaceBitmap* live_bitmap = space_->GetLiveBitmap();
+ // Use an unordered_set for constant time search of card in the second loop.
+ // We don't want to change cleared_cards_ to unordered so that traversals are
+ // sequential in address order.
+ // TODO: Optimize this.
+ std::unordered_set<const uint8_t*> card_lookup_map;
for (uint8_t* card : cleared_cards_) {
uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card));
uintptr_t end = start + CardTable::kCardSize;
@@ -396,10 +401,13 @@
[callback, arg](mirror::Object* obj) {
callback(obj, arg);
});
+ card_lookup_map.insert(card);
}
- // This may visit the same card twice, TODO avoid this.
for (const auto& pair : references_) {
const uint8_t* card = pair.first;
+ if (card_lookup_map.find(card) != card_lookup_map.end()) {
+ continue;
+ }
uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card));
uintptr_t end = start + CardTable::kCardSize;
live_bitmap->VisitMarkedRange(start,
diff --git a/runtime/gc/accounting/mod_union_table_test.cc b/runtime/gc/accounting/mod_union_table_test.cc
index e42682a..3f38f50 100644
--- a/runtime/gc/accounting/mod_union_table_test.cc
+++ b/runtime/gc/accounting/mod_union_table_test.cc
@@ -46,6 +46,7 @@
class ModUnionTableTest : public CommonRuntimeTest {
public:
ModUnionTableTest() : java_lang_object_array_(nullptr) {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
}
mirror::ObjectArray<mirror::Object>* AllocObjectArray(
Thread* self, space::ContinuousMemMapAllocSpace* space, size_t component_count)
diff --git a/runtime/gc/accounting/space_bitmap-inl.h b/runtime/gc/accounting/space_bitmap-inl.h
index d460e00..e7825e6 100644
--- a/runtime/gc/accounting/space_bitmap-inl.h
+++ b/runtime/gc/accounting/space_bitmap-inl.h
@@ -64,7 +64,44 @@
}
template<size_t kAlignment>
-template<typename Visitor>
+inline mirror::Object* SpaceBitmap<kAlignment>::FindPrecedingObject(uintptr_t visit_begin,
+ uintptr_t visit_end) const {
+ // Covers [visit_end, visit_begin].
+ visit_end = std::max(heap_begin_, visit_end);
+ DCHECK_LE(visit_end, visit_begin);
+ DCHECK_LT(visit_begin, HeapLimit());
+
+ const uintptr_t offset_start = visit_begin - heap_begin_;
+ const uintptr_t offset_end = visit_end - heap_begin_;
+ uintptr_t index_start = OffsetToIndex(offset_start);
+ const uintptr_t index_end = OffsetToIndex(offset_end);
+
+ // Start with the right edge
+ uintptr_t word = bitmap_begin_[index_start].load(std::memory_order_relaxed);
+ // visit_begin could be the first word of the object we are looking for.
+ const uintptr_t right_edge_mask = OffsetToMask(offset_start);
+ word &= right_edge_mask | (right_edge_mask - 1);
+ while (index_start > index_end) {
+ if (word != 0) {
+ const uintptr_t ptr_base = IndexToOffset(index_start) + heap_begin_;
+ size_t pos_leading_set_bit = kBitsPerIntPtrT - CLZ(word) - 1;
+ return reinterpret_cast<mirror::Object*>(ptr_base + pos_leading_set_bit * kAlignment);
+ }
+ word = bitmap_begin_[--index_start].load(std::memory_order_relaxed);
+ }
+
+ word &= ~(OffsetToMask(offset_end) - 1);
+ if (word != 0) {
+ const uintptr_t ptr_base = IndexToOffset(index_end) + heap_begin_;
+ size_t pos_leading_set_bit = kBitsPerIntPtrT - CLZ(word) - 1;
+ return reinterpret_cast<mirror::Object*>(ptr_base + pos_leading_set_bit * kAlignment);
+ } else {
+ return nullptr;
+ }
+}
+
+template<size_t kAlignment>
+template<bool kVisitOnce, typename Visitor>
inline void SpaceBitmap<kAlignment>::VisitMarkedRange(uintptr_t visit_begin,
uintptr_t visit_end,
Visitor&& visitor) const {
@@ -114,6 +151,9 @@
const size_t shift = CTZ(left_edge);
mirror::Object* obj = reinterpret_cast<mirror::Object*>(ptr_base + shift * kAlignment);
visitor(obj);
+ if (kVisitOnce) {
+ return;
+ }
left_edge ^= (static_cast<uintptr_t>(1)) << shift;
} while (left_edge != 0);
}
@@ -128,6 +168,9 @@
const size_t shift = CTZ(w);
mirror::Object* obj = reinterpret_cast<mirror::Object*>(ptr_base + shift * kAlignment);
visitor(obj);
+ if (kVisitOnce) {
+ return;
+ }
w ^= (static_cast<uintptr_t>(1)) << shift;
} while (w != 0);
}
@@ -155,6 +198,9 @@
const size_t shift = CTZ(right_edge);
mirror::Object* obj = reinterpret_cast<mirror::Object*>(ptr_base + shift * kAlignment);
visitor(obj);
+ if (kVisitOnce) {
+ return;
+ }
right_edge ^= (static_cast<uintptr_t>(1)) << shift;
} while (right_edge != 0);
}
diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc
index 3c5688d..a0458d2 100644
--- a/runtime/gc/accounting/space_bitmap.cc
+++ b/runtime/gc/accounting/space_bitmap.cc
@@ -16,6 +16,9 @@
#include "space_bitmap-inl.h"
+#include <iomanip>
+#include <sstream>
+
#include "android-base/stringprintf.h"
#include "art_field-inl.h"
@@ -113,6 +116,37 @@
reinterpret_cast<void*>(HeapLimit()));
}
+template <size_t kAlignment>
+std::string SpaceBitmap<kAlignment>::DumpMemAround(mirror::Object* obj) const {
+ uintptr_t addr = reinterpret_cast<uintptr_t>(obj);
+ DCHECK_GE(addr, heap_begin_);
+ DCHECK(HasAddress(obj)) << obj;
+ const uintptr_t offset = addr - heap_begin_;
+ const size_t index = OffsetToIndex(offset);
+ const uintptr_t mask = OffsetToMask(offset);
+ size_t num_entries = bitmap_size_ / sizeof(uintptr_t);
+ DCHECK_LT(index, num_entries) << " bitmap_size_ = " << bitmap_size_;
+ Atomic<uintptr_t>* atomic_entry = &bitmap_begin_[index];
+ uintptr_t prev = 0;
+ uintptr_t next = 0;
+ if (index > 0) {
+ prev = (atomic_entry - 1)->load(std::memory_order_relaxed);
+ }
+ uintptr_t curr = atomic_entry->load(std::memory_order_relaxed);
+ if (index < num_entries - 1) {
+ next = (atomic_entry + 1)->load(std::memory_order_relaxed);
+ }
+ std::ostringstream oss;
+ oss << " offset: " << offset
+ << " index: " << index
+ << " mask: " << std::hex << std::setfill('0') << std::setw(16) << mask
+ << " words {" << std::hex << std::setfill('0') << std::setw(16) << prev
+ << ", " << std::hex << std::setfill('0') << std::setw(16) << curr
+ << ", " << std::hex <<std::setfill('0') << std::setw(16) << next
+ << "}";
+ return oss.str();
+}
+
template<size_t kAlignment>
void SpaceBitmap<kAlignment>::Clear() {
if (bitmap_begin_ != nullptr) {
diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h
index 0d8ffa0..e318933 100644
--- a/runtime/gc/accounting/space_bitmap.h
+++ b/runtime/gc/accounting/space_bitmap.h
@@ -40,8 +40,8 @@
template<size_t kAlignment>
class SpaceBitmap {
public:
- typedef void ScanCallback(mirror::Object* obj, void* finger, void* arg);
- typedef void SweepCallback(size_t ptr_count, mirror::Object** ptrs, void* arg);
+ using ScanCallback = void(mirror::Object* obj, void* finger, void* arg);
+ using SweepCallback = void(size_t ptr_count, mirror::Object** ptrs, void* arg);
// Initialize a space bitmap so that it points to a bitmap large enough to cover a heap at
// heap_begin of heap_capacity bytes, where objects are guaranteed to be kAlignment-aligned.
@@ -131,10 +131,15 @@
}
}
- // Visit the live objects in the range [visit_begin, visit_end).
+ // Find first object while scanning bitmap backwards from visit_begin -> visit_end.
+ // Covers [visit_end, visit_begin] range.
+ mirror::Object* FindPrecedingObject(uintptr_t visit_begin, uintptr_t visit_end = 0) const;
+
+ // Visit the live objects in the range [visit_begin, visit_end). If kVisitOnce
+ // is true, then only the first live object will be visited.
// TODO: Use lock annotations when clang is fixed.
// REQUIRES(Locks::heap_bitmap_lock_) REQUIRES_SHARED(Locks::mutator_lock_);
- template <typename Visitor>
+ template <bool kVisitOnce = false, typename Visitor>
void VisitMarkedRange(uintptr_t visit_begin, uintptr_t visit_end, Visitor&& visitor) const
NO_THREAD_SAFETY_ANALYSIS;
@@ -159,7 +164,7 @@
void CopyFrom(SpaceBitmap* source_bitmap);
// Starting address of our internal storage.
- Atomic<uintptr_t>* Begin() {
+ Atomic<uintptr_t>* Begin() const {
return bitmap_begin_;
}
@@ -202,6 +207,9 @@
std::string Dump() const;
+ // Dump three bitmap words around obj.
+ std::string DumpMemAround(mirror::Object* obj) const;
+
// Helper function for computing bitmap size based on a 64 bit capacity.
static size_t ComputeBitmapSize(uint64_t capacity);
static size_t ComputeHeapSize(uint64_t bitmap_bytes);
diff --git a/runtime/gc/accounting/space_bitmap_test.cc b/runtime/gc/accounting/space_bitmap_test.cc
index 3a69865..8fcf102 100644
--- a/runtime/gc/accounting/space_bitmap_test.cc
+++ b/runtime/gc/accounting/space_bitmap_test.cc
@@ -19,8 +19,8 @@
#include <stdint.h>
#include <memory>
+#include "base/common_art_test.h"
#include "base/mutex.h"
-#include "common_runtime_test.h"
#include "runtime_globals.h"
#include "space_bitmap-inl.h"
@@ -28,7 +28,7 @@
namespace gc {
namespace accounting {
-class SpaceBitmapTest : public CommonRuntimeTest {};
+class SpaceBitmapTest : public CommonArtTest {};
TEST_F(SpaceBitmapTest, Init) {
uint8_t* heap_begin = reinterpret_cast<uint8_t*>(0x10000000);
diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc
index 7bcf375..9586e9d 100644
--- a/runtime/gc/allocation_record.cc
+++ b/runtime/gc/allocation_record.cc
@@ -59,6 +59,13 @@
}
void AllocRecordObjectMap::VisitRoots(RootVisitor* visitor) {
+ gc::Heap* const heap = Runtime::Current()->GetHeap();
+ // When we are compacting in userfaultfd GC, the class GC-roots are already
+ // updated in SweepAllocationRecords()->SweepClassObject().
+ if (heap->CurrentCollectorType() == gc::CollectorType::kCollectorTypeCMC
+ && heap->MarkCompactCollector()->IsCompacting(Thread::Current())) {
+ return;
+ }
CHECK_LE(recent_record_max_, alloc_record_max_);
BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(visitor, RootInfo(kRootDebugger));
size_t count = recent_record_max_;
@@ -92,7 +99,10 @@
mirror::Object* new_object = visitor->IsMarked(old_object);
DCHECK(new_object != nullptr);
if (UNLIKELY(old_object != new_object)) {
- klass = GcRoot<mirror::Class>(new_object->AsClass());
+ // We can't use AsClass() as it uses IsClass in a DCHECK, which expects
+ // the class' contents to be there. This is not the case in userfaultfd
+ // GC.
+ klass = GcRoot<mirror::Class>(ObjPtr<mirror::Class>::DownCast(new_object));
}
}
}
@@ -131,13 +141,13 @@
}
void AllocRecordObjectMap::AllowNewAllocationRecords() {
- CHECK(!kUseReadBarrier);
+ CHECK(!gUseReadBarrier);
allow_new_record_ = true;
new_record_condition_.Broadcast(Thread::Current());
}
void AllocRecordObjectMap::DisallowNewAllocationRecords() {
- CHECK(!kUseReadBarrier);
+ CHECK(!gUseReadBarrier);
allow_new_record_ = false;
}
@@ -230,8 +240,8 @@
// Since nobody seemed to really notice or care it might not be worth the trouble.
// Wait for GC's sweeping to complete and allow new records.
- while (UNLIKELY((!kUseReadBarrier && !allow_new_record_) ||
- (kUseReadBarrier && !self->GetWeakRefAccessEnabled()))) {
+ while (UNLIKELY((!gUseReadBarrier && !allow_new_record_) ||
+ (gUseReadBarrier && !self->GetWeakRefAccessEnabled()))) {
// Check and run the empty checkpoint before blocking so the empty checkpoint will work in the
// presence of threads blocking for weak ref access.
self->CheckEmptyCheckpointFromWeakRefAccess(Locks::alloc_tracker_lock_);
diff --git a/runtime/gc/allocator/art-dlmalloc.cc b/runtime/gc/allocator/art-dlmalloc.cc
new file mode 100644
index 0000000..de0c85a
--- /dev/null
+++ b/runtime/gc/allocator/art-dlmalloc.cc
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2012 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 "art-dlmalloc.h"
+
+#include <android-base/logging.h>
+
+#include "base/bit_utils.h"
+
+// ART specific morecore implementation defined in space.cc.
+static void* art_heap_morecore(void* m, intptr_t increment);
+#define MORECORE(x) art_heap_morecore(m, x)
+
+// Custom heap error handling.
+#define PROCEED_ON_ERROR 0
+static void art_heap_corruption(const char* function);
+static void art_heap_usage_error(const char* function, void* p);
+#define CORRUPTION_ERROR_ACTION(m) art_heap_corruption(__FUNCTION__)
+#define USAGE_ERROR_ACTION(m, p) art_heap_usage_error(__FUNCTION__, p)
+
+// Ugly inclusion of C file so that ART specific #defines configure dlmalloc for our use for
+// mspaces (regular dlmalloc is still declared in bionic).
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#pragma GCC diagnostic ignored "-Wempty-body"
+#pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#pragma GCC diagnostic ignored "-Wnull-pointer-arithmetic"
+#pragma GCC diagnostic ignored "-Wexpansion-to-defined"
+#include "dlmalloc.c" // NOLINT
+// Note: dlmalloc.c uses a DEBUG define to drive debug code. This interferes with the DEBUG severity
+// of libbase, so undefine it now.
+#undef DEBUG
+#pragma GCC diagnostic pop
+
+static void* art_heap_morecore(void* m, intptr_t increment) {
+ return ::art::gc::allocator::ArtDlMallocMoreCore(m, increment);
+}
+
+static void art_heap_corruption(const char* function) {
+ LOG(FATAL) << "Corrupt heap detected in: " << function;
+}
+
+static void art_heap_usage_error(const char* function, void* p) {
+ LOG(FATAL) << "Incorrect use of function '" << function << "' argument " << p
+ << " not expected";
+}
+
+#include <sys/mman.h>
+
+#include "base/utils.h"
+#include "runtime_globals.h"
+
+extern "C" void DlmallocMadviseCallback(void* start, void* end, size_t used_bytes, void* arg) {
+ // Is this chunk in use?
+ if (used_bytes != 0) {
+ return;
+ }
+ // Do we have any whole pages to give back?
+ start = reinterpret_cast<void*>(art::RoundUp(reinterpret_cast<uintptr_t>(start), art::kPageSize));
+ end = reinterpret_cast<void*>(art::RoundDown(reinterpret_cast<uintptr_t>(end), art::kPageSize));
+ if (end > start) {
+ size_t length = reinterpret_cast<uint8_t*>(end) - reinterpret_cast<uint8_t*>(start);
+ int rc = madvise(start, length, MADV_DONTNEED);
+ if (UNLIKELY(rc != 0)) {
+ errno = rc;
+ PLOG(FATAL) << "madvise failed during heap trimming";
+ }
+ size_t* reclaimed = reinterpret_cast<size_t*>(arg);
+ *reclaimed += length;
+ }
+}
+
+extern "C" void DlmallocBytesAllocatedCallback(void* start ATTRIBUTE_UNUSED,
+ void* end ATTRIBUTE_UNUSED,
+ size_t used_bytes,
+ void* arg) {
+ if (used_bytes == 0) {
+ return;
+ }
+ size_t* bytes_allocated = reinterpret_cast<size_t*>(arg);
+ *bytes_allocated += used_bytes + sizeof(size_t);
+}
+
+extern "C" void DlmallocObjectsAllocatedCallback(void* start ATTRIBUTE_UNUSED,
+ void* end ATTRIBUTE_UNUSED,
+ size_t used_bytes,
+ void* arg) {
+ if (used_bytes == 0) {
+ return;
+ }
+ size_t* objects_allocated = reinterpret_cast<size_t*>(arg);
+ ++(*objects_allocated);
+}
diff --git a/runtime/gc/allocator/art-dlmalloc.h b/runtime/gc/allocator/art-dlmalloc.h
new file mode 100644
index 0000000..296de72
--- /dev/null
+++ b/runtime/gc/allocator/art-dlmalloc.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_GC_ALLOCATOR_ART_DLMALLOC_H_
+#define ART_RUNTIME_GC_ALLOCATOR_ART_DLMALLOC_H_
+
+#include <cstdint>
+
+// Configure dlmalloc for mspaces.
+// Avoid a collision with one used in llvm.
+#undef HAVE_MMAP
+#define HAVE_MMAP 0
+#define HAVE_MREMAP 0
+#define HAVE_MORECORE 1
+#define MSPACES 1
+#define NO_MALLINFO 1
+#define ONLY_MSPACES 1
+#define MALLOC_INSPECT_ALL 1
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#pragma GCC diagnostic ignored "-Wnull-pointer-arithmetic"
+#include "dlmalloc.h"
+#pragma GCC diagnostic pop
+
+// Callback for dlmalloc_inspect_all or mspace_inspect_all that will madvise(2) unused
+// pages back to the kernel.
+extern "C" void DlmallocMadviseCallback(void* start, void* end, size_t used_bytes, void* /*arg*/);
+
+// Callbacks for dlmalloc_inspect_all or mspace_inspect_all that will
+// count the number of bytes allocated and objects allocated,
+// respectively.
+extern "C" void DlmallocBytesAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg);
+extern "C" void DlmallocObjectsAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg);
+
+namespace art {
+namespace gc {
+namespace allocator {
+
+// Callback from dlmalloc when it needs to increase the footprint. Must be implemented somewhere
+// else (currently dlmalloc_space.cc).
+void* ArtDlMallocMoreCore(void* mspace, intptr_t increment);
+
+} // namespace allocator
+} // namespace gc
+} // namespace art
+
+#endif // ART_RUNTIME_GC_ALLOCATOR_ART_DLMALLOC_H_
diff --git a/runtime/gc/allocator/dlmalloc.cc b/runtime/gc/allocator/dlmalloc.cc
deleted file mode 100644
index 79d4fbf..0000000
--- a/runtime/gc/allocator/dlmalloc.cc
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2012 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 "dlmalloc.h"
-
-#include <android-base/logging.h>
-
-#include "base/bit_utils.h"
-
-// ART specific morecore implementation defined in space.cc.
-static void* art_heap_morecore(void* m, intptr_t increment);
-#define MORECORE(x) art_heap_morecore(m, x)
-
-// Custom heap error handling.
-#define PROCEED_ON_ERROR 0
-static void art_heap_corruption(const char* function);
-static void art_heap_usage_error(const char* function, void* p);
-#define CORRUPTION_ERROR_ACTION(m) art_heap_corruption(__FUNCTION__)
-#define USAGE_ERROR_ACTION(m, p) art_heap_usage_error(__FUNCTION__, p)
-
-// Ugly inclusion of C file so that ART specific #defines configure dlmalloc for our use for
-// mspaces (regular dlmalloc is still declared in bionic).
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wredundant-decls"
-#pragma GCC diagnostic ignored "-Wempty-body"
-#pragma GCC diagnostic ignored "-Wstrict-aliasing"
-#pragma GCC diagnostic ignored "-Wnull-pointer-arithmetic"
-#pragma GCC diagnostic ignored "-Wexpansion-to-defined"
-#include "../../../external/dlmalloc/malloc.c"
-// Note: malloc.c uses a DEBUG define to drive debug code. This interferes with the DEBUG severity
-// of libbase, so undefine it now.
-#undef DEBUG
-#pragma GCC diagnostic pop
-
-static void* art_heap_morecore(void* m, intptr_t increment) {
- return ::art::gc::allocator::ArtDlMallocMoreCore(m, increment);
-}
-
-static void art_heap_corruption(const char* function) {
- LOG(FATAL) << "Corrupt heap detected in: " << function;
-}
-
-static void art_heap_usage_error(const char* function, void* p) {
- LOG(FATAL) << "Incorrect use of function '" << function << "' argument " << p
- << " not expected";
-}
-
-#include <sys/mman.h>
-
-#include "base/utils.h"
-#include "runtime_globals.h"
-
-extern "C" void DlmallocMadviseCallback(void* start, void* end, size_t used_bytes, void* arg) {
- // Is this chunk in use?
- if (used_bytes != 0) {
- return;
- }
- // Do we have any whole pages to give back?
- start = reinterpret_cast<void*>(art::RoundUp(reinterpret_cast<uintptr_t>(start), art::kPageSize));
- end = reinterpret_cast<void*>(art::RoundDown(reinterpret_cast<uintptr_t>(end), art::kPageSize));
- if (end > start) {
- size_t length = reinterpret_cast<uint8_t*>(end) - reinterpret_cast<uint8_t*>(start);
- int rc = madvise(start, length, MADV_DONTNEED);
- if (UNLIKELY(rc != 0)) {
- errno = rc;
- PLOG(FATAL) << "madvise failed during heap trimming";
- }
- size_t* reclaimed = reinterpret_cast<size_t*>(arg);
- *reclaimed += length;
- }
-}
-
-extern "C" void DlmallocBytesAllocatedCallback(void* start ATTRIBUTE_UNUSED,
- void* end ATTRIBUTE_UNUSED,
- size_t used_bytes,
- void* arg) {
- if (used_bytes == 0) {
- return;
- }
- size_t* bytes_allocated = reinterpret_cast<size_t*>(arg);
- *bytes_allocated += used_bytes + sizeof(size_t);
-}
-
-extern "C" void DlmallocObjectsAllocatedCallback(void* start ATTRIBUTE_UNUSED,
- void* end ATTRIBUTE_UNUSED,
- size_t used_bytes,
- void* arg) {
- if (used_bytes == 0) {
- return;
- }
- size_t* objects_allocated = reinterpret_cast<size_t*>(arg);
- ++(*objects_allocated);
-}
diff --git a/runtime/gc/allocator/dlmalloc.h b/runtime/gc/allocator/dlmalloc.h
deleted file mode 100644
index b12691a..0000000
--- a/runtime/gc/allocator/dlmalloc.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_RUNTIME_GC_ALLOCATOR_DLMALLOC_H_
-#define ART_RUNTIME_GC_ALLOCATOR_DLMALLOC_H_
-
-#include <cstdint>
-
-// Configure dlmalloc for mspaces.
-// Avoid a collision with one used in llvm.
-#undef HAVE_MMAP
-#define HAVE_MMAP 0
-#define HAVE_MREMAP 0
-#define HAVE_MORECORE 1
-#define MSPACES 1
-#define NO_MALLINFO 1
-#define ONLY_MSPACES 1
-#define MALLOC_INSPECT_ALL 1
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wredundant-decls"
-#pragma GCC diagnostic ignored "-Wnull-pointer-arithmetic"
-#include "../../external/dlmalloc/malloc.h"
-#pragma GCC diagnostic pop
-
-// Callback for dlmalloc_inspect_all or mspace_inspect_all that will madvise(2) unused
-// pages back to the kernel.
-extern "C" void DlmallocMadviseCallback(void* start, void* end, size_t used_bytes, void* /*arg*/);
-
-// Callbacks for dlmalloc_inspect_all or mspace_inspect_all that will
-// count the number of bytes allocated and objects allocated,
-// respectively.
-extern "C" void DlmallocBytesAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg);
-extern "C" void DlmallocObjectsAllocatedCallback(void* start, void* end, size_t used_bytes, void* arg);
-
-namespace art {
-namespace gc {
-namespace allocator {
-
-// Callback from dlmalloc when it needs to increase the footprint. Must be implemented somewhere
-// else (currently dlmalloc_space.cc).
-void* ArtDlMallocMoreCore(void* mspace, intptr_t increment);
-
-} // namespace allocator
-} // namespace gc
-} // namespace art
-
-#endif // ART_RUNTIME_GC_ALLOCATOR_DLMALLOC_H_
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 0de62fe..5118523 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -160,17 +160,31 @@
if (young_gen_) {
gc_time_histogram_ = metrics->YoungGcCollectionTime();
metrics_gc_count_ = metrics->YoungGcCount();
+ metrics_gc_count_delta_ = metrics->YoungGcCountDelta();
gc_throughput_histogram_ = metrics->YoungGcThroughput();
gc_tracing_throughput_hist_ = metrics->YoungGcTracingThroughput();
gc_throughput_avg_ = metrics->YoungGcThroughputAvg();
gc_tracing_throughput_avg_ = metrics->YoungGcTracingThroughputAvg();
+ gc_scanned_bytes_ = metrics->YoungGcScannedBytes();
+ gc_scanned_bytes_delta_ = metrics->YoungGcScannedBytesDelta();
+ gc_freed_bytes_ = metrics->YoungGcFreedBytes();
+ gc_freed_bytes_delta_ = metrics->YoungGcFreedBytesDelta();
+ gc_duration_ = metrics->YoungGcDuration();
+ gc_duration_delta_ = metrics->YoungGcDurationDelta();
} else {
gc_time_histogram_ = metrics->FullGcCollectionTime();
metrics_gc_count_ = metrics->FullGcCount();
+ metrics_gc_count_delta_ = metrics->FullGcCountDelta();
gc_throughput_histogram_ = metrics->FullGcThroughput();
gc_tracing_throughput_hist_ = metrics->FullGcTracingThroughput();
gc_throughput_avg_ = metrics->FullGcThroughputAvg();
gc_tracing_throughput_avg_ = metrics->FullGcTracingThroughputAvg();
+ gc_scanned_bytes_ = metrics->FullGcScannedBytes();
+ gc_scanned_bytes_delta_ = metrics->FullGcScannedBytesDelta();
+ gc_freed_bytes_ = metrics->FullGcFreedBytes();
+ gc_freed_bytes_delta_ = metrics->FullGcFreedBytesDelta();
+ gc_duration_ = metrics->FullGcDuration();
+ gc_duration_delta_ = metrics->FullGcDurationDelta();
}
}
@@ -575,10 +589,11 @@
if (kIsDebugBuild && !cc->use_generational_cc_) {
cc->region_space_->AssertAllRegionLiveBytesZeroOrCleared();
}
- if (UNLIKELY(Runtime::Current()->IsActiveTransaction())) {
- CHECK(Runtime::Current()->IsAotCompiler());
+ Runtime* runtime = Runtime::Current();
+ if (UNLIKELY(runtime->IsActiveTransaction())) {
+ CHECK(runtime->IsAotCompiler());
TimingLogger::ScopedTiming split3("(Paused)VisitTransactionRoots", cc->GetTimings());
- Runtime::Current()->VisitTransactionRoots(cc);
+ runtime->VisitTransactionRoots(cc);
}
if (kUseBakerReadBarrier && kGrayDirtyImmuneObjects) {
cc->GrayAllNewlyDirtyImmuneObjects();
@@ -587,15 +602,10 @@
cc->VerifyGrayImmuneObjects();
}
}
- // May be null during runtime creation, in this case leave java_lang_Object null.
- // This is safe since single threaded behavior should mean FillWithFakeObject does not
- // happen when java_lang_Object_ is null.
- if (WellKnownClasses::java_lang_Object != nullptr) {
- cc->java_lang_Object_ = down_cast<mirror::Class*>(cc->Mark(thread,
- WellKnownClasses::ToClass(WellKnownClasses::java_lang_Object).Ptr()));
- } else {
- cc->java_lang_Object_ = nullptr;
- }
+ ObjPtr<mirror::Class> java_lang_Object =
+ GetClassRoot<mirror::Object, kWithoutReadBarrier>(runtime->GetClassLinker());
+ DCHECK(java_lang_Object != nullptr);
+ cc->java_lang_Object_ = down_cast<mirror::Class*>(cc->Mark(thread, java_lang_Object.Ptr()));
}
private:
@@ -1692,8 +1702,6 @@
if (kVerboseMode) {
LOG(INFO) << "SweepSystemWeaks done";
}
- // Free data for class loaders that we unloaded.
- Runtime::Current()->GetClassLinker()->CleanupClassLoaders();
// Marking is done. Disable marking.
DisableMarking();
CheckEmptyMarkStack();
@@ -1739,6 +1747,10 @@
thread->IsSuspended() ||
thread->GetState() == ThreadState::kWaitingPerformingGc)
<< thread->GetState() << " thread " << thread << " self " << self;
+ // We sweep interpreter caches here so that it can be done after all
+ // reachable objects are marked and the mutators can sweep their caches
+ // without synchronization.
+ thread->SweepInterpreterCache(concurrent_copying_);
// Disable the thread-local is_gc_marking flag.
// Note a thread that has just started right before this checkpoint may have already this flag
// set to false, which is ok.
@@ -2716,6 +2728,11 @@
}
Thread* self = Thread::Current();
+ // Free data for class loaders that we unloaded. This includes removing
+ // dead methods from JIT's internal maps. This must be done before
+ // reclaiming the memory of the dead methods' declaring classes.
+ Runtime::Current()->GetClassLinker()->CleanupClassLoaders();
+
{
// Double-check that the mark stack is empty.
// Note: need to set this after VerifyNoFromSpaceRef().
diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc
index 80b3982..03a432d 100644
--- a/runtime/gc/collector/garbage_collector.cc
+++ b/runtime/gc/collector/garbage_collector.cc
@@ -72,10 +72,17 @@
freed_bytes_histogram_((name_ + " freed-bytes").c_str(), kMemBucketSize, kMemBucketCount),
gc_time_histogram_(nullptr),
metrics_gc_count_(nullptr),
+ metrics_gc_count_delta_(nullptr),
gc_throughput_histogram_(nullptr),
gc_tracing_throughput_hist_(nullptr),
gc_throughput_avg_(nullptr),
gc_tracing_throughput_avg_(nullptr),
+ gc_scanned_bytes_(nullptr),
+ gc_scanned_bytes_delta_(nullptr),
+ gc_freed_bytes_(nullptr),
+ gc_freed_bytes_delta_(nullptr),
+ gc_duration_(nullptr),
+ gc_duration_delta_(nullptr),
cumulative_timings_(name),
pause_histogram_lock_("pause histogram lock", kDefaultMutexLevel, true),
is_transaction_active_(false),
@@ -189,19 +196,26 @@
RegisterPause(duration_ns);
}
total_time_ns_ += duration_ns;
- uint64_t total_pause_time = 0;
+ uint64_t total_pause_time_ns = 0;
for (uint64_t pause_time : current_iteration->GetPauseTimes()) {
MutexLock mu(self, pause_histogram_lock_);
pause_histogram_.AdjustAndAddValue(pause_time);
- total_pause_time += pause_time;
+ total_pause_time_ns += pause_time;
}
metrics::ArtMetrics* metrics = runtime->GetMetrics();
// Report STW pause time in microseconds.
- metrics->WorldStopTimeDuringGCAvg()->Add(total_pause_time / 1'000);
+ const uint64_t total_pause_time_us = total_pause_time_ns / 1'000;
+ metrics->WorldStopTimeDuringGCAvg()->Add(total_pause_time_us);
+ metrics->GcWorldStopTime()->Add(total_pause_time_us);
+ metrics->GcWorldStopTimeDelta()->Add(total_pause_time_us);
+ metrics->GcWorldStopCount()->AddOne();
+ metrics->GcWorldStopCountDelta()->AddOne();
// Report total collection time of all GCs put together.
metrics->TotalGcCollectionTime()->Add(NsToMs(duration_ns));
+ metrics->TotalGcCollectionTimeDelta()->Add(NsToMs(duration_ns));
if (are_metrics_initialized_) {
metrics_gc_count_->Add(1);
+ metrics_gc_count_delta_->Add(1);
// Report GC time in milliseconds.
gc_time_histogram_->Add(NsToMs(duration_ns));
// Throughput in bytes/s. Add 1us to prevent possible division by 0.
@@ -216,6 +230,13 @@
throughput = current_iteration->GetEstimatedThroughput() / MB;
gc_throughput_histogram_->Add(throughput);
gc_throughput_avg_->Add(throughput);
+
+ gc_scanned_bytes_->Add(current_iteration->GetScannedBytes());
+ gc_scanned_bytes_delta_->Add(current_iteration->GetScannedBytes());
+ gc_freed_bytes_->Add(current_iteration->GetFreedBytes());
+ gc_freed_bytes_delta_->Add(current_iteration->GetFreedBytes());
+ gc_duration_->Add(NsToMs(current_iteration->GetDurationNs()));
+ gc_duration_delta_->Add(NsToMs(current_iteration->GetDurationNs()));
}
is_transaction_active_ = false;
}
diff --git a/runtime/gc/collector/garbage_collector.h b/runtime/gc/collector/garbage_collector.h
index d439914..948a868 100644
--- a/runtime/gc/collector/garbage_collector.h
+++ b/runtime/gc/collector/garbage_collector.h
@@ -162,10 +162,17 @@
Histogram<size_t> freed_bytes_histogram_;
metrics::MetricsBase<int64_t>* gc_time_histogram_;
metrics::MetricsBase<uint64_t>* metrics_gc_count_;
+ metrics::MetricsBase<uint64_t>* metrics_gc_count_delta_;
metrics::MetricsBase<int64_t>* gc_throughput_histogram_;
metrics::MetricsBase<int64_t>* gc_tracing_throughput_hist_;
metrics::MetricsBase<uint64_t>* gc_throughput_avg_;
metrics::MetricsBase<uint64_t>* gc_tracing_throughput_avg_;
+ metrics::MetricsBase<uint64_t>* gc_scanned_bytes_;
+ metrics::MetricsBase<uint64_t>* gc_scanned_bytes_delta_;
+ metrics::MetricsBase<uint64_t>* gc_freed_bytes_;
+ metrics::MetricsBase<uint64_t>* gc_freed_bytes_delta_;
+ metrics::MetricsBase<uint64_t>* gc_duration_;
+ metrics::MetricsBase<uint64_t>* gc_duration_delta_;
uint64_t total_thread_cpu_time_ns_;
uint64_t total_time_ns_;
uint64_t total_freed_objects_;
diff --git a/runtime/gc/collector/immune_spaces_test.cc b/runtime/gc/collector/immune_spaces_test.cc
index a0ea60d..caa8106 100644
--- a/runtime/gc/collector/immune_spaces_test.cc
+++ b/runtime/gc/collector/immune_spaces_test.cc
@@ -16,7 +16,8 @@
#include <sys/mman.h>
-#include "common_runtime_test.h"
+#include "base/common_art_test.h"
+#include "base/utils.h"
#include "gc/collector/immune_spaces.h"
#include "gc/space/image_space.h"
#include "gc/space/space-inl.h"
@@ -46,7 +47,7 @@
MemMap&& oat_map)
: ImageSpace("FakeImageSpace",
/*image_location=*/"",
- /*profile_file=*/{},
+ /*profile_files=*/{},
std::move(map),
std::move(live_bitmap),
map.End()),
@@ -59,7 +60,7 @@
MemMap oat_map_;
};
-class ImmuneSpacesTest : public CommonRuntimeTest {
+class ImmuneSpacesTest : public CommonArtTest {
static constexpr size_t kMaxBitmaps = 10;
public:
diff --git a/runtime/gc/collector/mark_compact-inl.h b/runtime/gc/collector/mark_compact-inl.h
new file mode 100644
index 0000000..d6919aa
--- /dev/null
+++ b/runtime/gc/collector/mark_compact-inl.h
@@ -0,0 +1,393 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_GC_COLLECTOR_MARK_COMPACT_INL_H_
+#define ART_RUNTIME_GC_COLLECTOR_MARK_COMPACT_INL_H_
+
+#include "gc/space/bump_pointer_space.h"
+#include "mark_compact.h"
+#include "mirror/object-inl.h"
+
+namespace art {
+namespace gc {
+namespace collector {
+
+inline void MarkCompact::UpdateClassAfterObjectMap(mirror::Object* obj) {
+ mirror::Class* klass = obj->GetClass<kVerifyNone, kWithoutReadBarrier>();
+ // Track a class if it needs walking super-classes for visiting references or
+ // if it's higher in address order than its objects and is in moving space.
+ if (UNLIKELY(
+ (std::less<mirror::Object*>{}(obj, klass) && bump_pointer_space_->HasAddress(klass)) ||
+ (klass->GetReferenceInstanceOffsets<kVerifyNone>() == mirror::Class::kClassWalkSuper &&
+ walk_super_class_cache_ != klass))) {
+ // Since this function gets invoked in the compaction pause as well, it is
+ // preferable to store such super class separately rather than updating key
+ // as the latter would require traversing the hierarchy for every object of 'klass'.
+ auto ret1 = class_after_obj_hash_map_.try_emplace(ObjReference::FromMirrorPtr(klass),
+ ObjReference::FromMirrorPtr(obj));
+ if (ret1.second) {
+ if (klass->GetReferenceInstanceOffsets<kVerifyNone>() == mirror::Class::kClassWalkSuper) {
+ // In this case we require traversing through the super class hierarchy
+ // and find the super class at the highest address order.
+ mirror::Class* highest_klass = bump_pointer_space_->HasAddress(klass) ? klass : nullptr;
+ for (ObjPtr<mirror::Class> k = klass->GetSuperClass<kVerifyNone, kWithoutReadBarrier>();
+ k != nullptr;
+ k = k->GetSuperClass<kVerifyNone, kWithoutReadBarrier>()) {
+ // TODO: Can we break once we encounter a super class outside the moving space?
+ if (bump_pointer_space_->HasAddress(k.Ptr())) {
+ highest_klass = std::max(highest_klass, k.Ptr(), std::less<mirror::Class*>());
+ }
+ }
+ if (highest_klass != nullptr && highest_klass != klass) {
+ auto ret2 = super_class_after_class_hash_map_.try_emplace(
+ ObjReference::FromMirrorPtr(klass), ObjReference::FromMirrorPtr(highest_klass));
+ DCHECK(ret2.second);
+ } else {
+ walk_super_class_cache_ = klass;
+ }
+ }
+ } else if (std::less<mirror::Object*>{}(obj, ret1.first->second.AsMirrorPtr())) {
+ ret1.first->second = ObjReference::FromMirrorPtr(obj);
+ }
+ }
+}
+
+template <size_t kAlignment>
+inline uintptr_t MarkCompact::LiveWordsBitmap<kAlignment>::SetLiveWords(uintptr_t begin,
+ size_t size) {
+ const uintptr_t begin_bit_idx = MemRangeBitmap::BitIndexFromAddr(begin);
+ DCHECK(!Bitmap::TestBit(begin_bit_idx));
+ // Range to set bit: [begin, end]
+ uintptr_t end = begin + size - kAlignment;
+ const uintptr_t end_bit_idx = MemRangeBitmap::BitIndexFromAddr(end);
+ uintptr_t* begin_bm_address = Bitmap::Begin() + Bitmap::BitIndexToWordIndex(begin_bit_idx);
+ uintptr_t* end_bm_address = Bitmap::Begin() + Bitmap::BitIndexToWordIndex(end_bit_idx);
+ ptrdiff_t diff = end_bm_address - begin_bm_address;
+ uintptr_t mask = Bitmap::BitIndexToMask(begin_bit_idx);
+ // Bits that needs to be set in the first word, if it's not also the last word
+ mask = ~(mask - 1);
+ if (diff > 0) {
+ *begin_bm_address |= mask;
+ mask = ~0;
+ // Even though memset can handle the (diff == 1) case but we should avoid the
+ // overhead of a function call for this, highly likely (as most of the objects
+ // are small), case.
+ if (diff > 1) {
+ // Set all intermediate bits to 1.
+ std::memset(static_cast<void*>(begin_bm_address + 1), 0xff, (diff - 1) * sizeof(uintptr_t));
+ }
+ }
+ uintptr_t end_mask = Bitmap::BitIndexToMask(end_bit_idx);
+ *end_bm_address |= mask & (end_mask | (end_mask - 1));
+ return begin_bit_idx;
+}
+
+template <size_t kAlignment> template <typename Visitor>
+inline void MarkCompact::LiveWordsBitmap<kAlignment>::VisitLiveStrides(uintptr_t begin_bit_idx,
+ uint8_t* end,
+ const size_t bytes,
+ Visitor&& visitor) const {
+ // Range to visit [begin_bit_idx, end_bit_idx]
+ DCHECK(IsAligned<kAlignment>(end));
+ end -= kAlignment;
+ const uintptr_t end_bit_idx = MemRangeBitmap::BitIndexFromAddr(reinterpret_cast<uintptr_t>(end));
+ DCHECK_LE(begin_bit_idx, end_bit_idx);
+ uintptr_t begin_word_idx = Bitmap::BitIndexToWordIndex(begin_bit_idx);
+ const uintptr_t end_word_idx = Bitmap::BitIndexToWordIndex(end_bit_idx);
+ DCHECK(Bitmap::TestBit(begin_bit_idx));
+ size_t stride_size = 0;
+ size_t idx_in_word = 0;
+ size_t num_heap_words = bytes / kAlignment;
+ uintptr_t live_stride_start_idx;
+ uintptr_t word = Bitmap::Begin()[begin_word_idx];
+
+ // Setup the first word.
+ word &= ~(Bitmap::BitIndexToMask(begin_bit_idx) - 1);
+ begin_bit_idx = RoundDown(begin_bit_idx, Bitmap::kBitsPerBitmapWord);
+
+ do {
+ if (UNLIKELY(begin_word_idx == end_word_idx)) {
+ uintptr_t mask = Bitmap::BitIndexToMask(end_bit_idx);
+ word &= mask | (mask - 1);
+ }
+ if (~word == 0) {
+ // All bits in the word are marked.
+ if (stride_size == 0) {
+ live_stride_start_idx = begin_bit_idx;
+ }
+ stride_size += Bitmap::kBitsPerBitmapWord;
+ if (num_heap_words <= stride_size) {
+ break;
+ }
+ } else {
+ while (word != 0) {
+ // discard 0s
+ size_t shift = CTZ(word);
+ idx_in_word += shift;
+ word >>= shift;
+ if (stride_size > 0) {
+ if (shift > 0) {
+ if (num_heap_words <= stride_size) {
+ break;
+ }
+ visitor(live_stride_start_idx, stride_size, /*is_last*/ false);
+ num_heap_words -= stride_size;
+ live_stride_start_idx = begin_bit_idx + idx_in_word;
+ stride_size = 0;
+ }
+ } else {
+ live_stride_start_idx = begin_bit_idx + idx_in_word;
+ }
+ // consume 1s
+ shift = CTZ(~word);
+ DCHECK_NE(shift, 0u);
+ word >>= shift;
+ idx_in_word += shift;
+ stride_size += shift;
+ }
+ // If the whole word == 0 or the higher bits are 0s, then we exit out of
+ // the above loop without completely consuming the word, so call visitor,
+ // if needed.
+ if (idx_in_word < Bitmap::kBitsPerBitmapWord && stride_size > 0) {
+ if (num_heap_words <= stride_size) {
+ break;
+ }
+ visitor(live_stride_start_idx, stride_size, /*is_last*/ false);
+ num_heap_words -= stride_size;
+ stride_size = 0;
+ }
+ idx_in_word = 0;
+ }
+ begin_bit_idx += Bitmap::kBitsPerBitmapWord;
+ begin_word_idx++;
+ if (UNLIKELY(begin_word_idx > end_word_idx)) {
+ num_heap_words = std::min(stride_size, num_heap_words);
+ break;
+ }
+ word = Bitmap::Begin()[begin_word_idx];
+ } while (true);
+
+ if (stride_size > 0) {
+ visitor(live_stride_start_idx, num_heap_words, /*is_last*/ true);
+ }
+}
+
+template <size_t kAlignment>
+inline
+uint32_t MarkCompact::LiveWordsBitmap<kAlignment>::FindNthLiveWordOffset(size_t chunk_idx,
+ uint32_t n) const {
+ DCHECK_LT(n, kBitsPerVectorWord);
+ const size_t index = chunk_idx * kBitmapWordsPerVectorWord;
+ for (uint32_t i = 0; i < kBitmapWordsPerVectorWord; i++) {
+ uintptr_t word = Bitmap::Begin()[index + i];
+ if (~word == 0) {
+ if (n < Bitmap::kBitsPerBitmapWord) {
+ return i * Bitmap::kBitsPerBitmapWord + n;
+ }
+ n -= Bitmap::kBitsPerBitmapWord;
+ } else {
+ uint32_t j = 0;
+ while (word != 0) {
+ // count contiguous 0s
+ uint32_t shift = CTZ(word);
+ word >>= shift;
+ j += shift;
+ // count contiguous 1s
+ shift = CTZ(~word);
+ DCHECK_NE(shift, 0u);
+ if (shift > n) {
+ return i * Bitmap::kBitsPerBitmapWord + j + n;
+ }
+ n -= shift;
+ word >>= shift;
+ j += shift;
+ }
+ }
+ }
+ UNREACHABLE();
+}
+
+inline void MarkCompact::UpdateRef(mirror::Object* obj, MemberOffset offset) {
+ mirror::Object* old_ref = obj->GetFieldObject<
+ mirror::Object, kVerifyNone, kWithoutReadBarrier, /*kIsVolatile*/false>(offset);
+ if (kIsDebugBuild) {
+ if (live_words_bitmap_->HasAddress(old_ref)
+ && reinterpret_cast<uint8_t*>(old_ref) < black_allocations_begin_
+ && !moving_space_bitmap_->Test(old_ref)) {
+ mirror::Object* from_ref = GetFromSpaceAddr(old_ref);
+ std::ostringstream oss;
+ heap_->DumpSpaces(oss);
+ MemMap::DumpMaps(oss, /* terse= */ true);
+ LOG(FATAL) << "Not marked in the bitmap ref=" << old_ref
+ << " from_ref=" << from_ref
+ << " offset=" << offset
+ << " obj=" << obj
+ << " obj-validity=" << IsValidObject(obj)
+ << " from-space=" << static_cast<void*>(from_space_begin_)
+ << " bitmap= " << moving_space_bitmap_->DumpMemAround(old_ref)
+ << " from_ref "
+ << heap_->GetVerification()->DumpRAMAroundAddress(
+ reinterpret_cast<uintptr_t>(from_ref), 128)
+ << " obj "
+ << heap_->GetVerification()->DumpRAMAroundAddress(
+ reinterpret_cast<uintptr_t>(obj), 128)
+ << " old_ref " << heap_->GetVerification()->DumpRAMAroundAddress(
+ reinterpret_cast<uintptr_t>(old_ref), 128)
+ << " maps\n" << oss.str();
+ }
+ }
+ mirror::Object* new_ref = PostCompactAddress(old_ref);
+ if (new_ref != old_ref) {
+ obj->SetFieldObjectWithoutWriteBarrier<
+ /*kTransactionActive*/false, /*kCheckTransaction*/false, kVerifyNone, /*kIsVolatile*/false>(
+ offset,
+ new_ref);
+ }
+}
+
+inline bool MarkCompact::VerifyRootSingleUpdate(void* root,
+ mirror::Object* old_ref,
+ const RootInfo& info) {
+ // ASAN promotes stack-frames to heap in order to detect
+ // stack-use-after-return issues. So skip using this double-root update
+ // detection on ASAN as well.
+ if (kIsDebugBuild && !kMemoryToolIsAvailable) {
+ void* stack_low_addr = stack_low_addr_;
+ void* stack_high_addr = stack_high_addr_;
+ if (!live_words_bitmap_->HasAddress(old_ref)) {
+ return false;
+ }
+ if (UNLIKELY(stack_low_addr == nullptr)) {
+ Thread* self = Thread::Current();
+ stack_low_addr = self->GetStackEnd();
+ stack_high_addr = reinterpret_cast<char*>(stack_low_addr) + self->GetStackSize();
+ }
+ if (root < stack_low_addr || root > stack_high_addr) {
+ auto ret = updated_roots_.insert(root);
+ DCHECK(ret.second) << "root=" << root << " old_ref=" << old_ref
+ << " stack_low_addr=" << stack_low_addr
+ << " stack_high_addr=" << stack_high_addr;
+ }
+ DCHECK(reinterpret_cast<uint8_t*>(old_ref) >= black_allocations_begin_ ||
+ live_words_bitmap_->Test(old_ref))
+ << "ref=" << old_ref << " <" << mirror::Object::PrettyTypeOf(old_ref) << "> RootInfo ["
+ << info << "]";
+ }
+ return true;
+}
+
+inline void MarkCompact::UpdateRoot(mirror::CompressedReference<mirror::Object>* root,
+ const RootInfo& info) {
+ DCHECK(!root->IsNull());
+ mirror::Object* old_ref = root->AsMirrorPtr();
+ if (VerifyRootSingleUpdate(root, old_ref, info)) {
+ mirror::Object* new_ref = PostCompactAddress(old_ref);
+ if (old_ref != new_ref) {
+ root->Assign(new_ref);
+ }
+ }
+}
+
+inline void MarkCompact::UpdateRoot(mirror::Object** root, const RootInfo& info) {
+ mirror::Object* old_ref = *root;
+ if (VerifyRootSingleUpdate(root, old_ref, info)) {
+ mirror::Object* new_ref = PostCompactAddress(old_ref);
+ if (old_ref != new_ref) {
+ *root = new_ref;
+ }
+ }
+}
+
+template <size_t kAlignment>
+inline size_t MarkCompact::LiveWordsBitmap<kAlignment>::CountLiveWordsUpto(size_t bit_idx) const {
+ const size_t word_offset = Bitmap::BitIndexToWordIndex(bit_idx);
+ uintptr_t word;
+ size_t ret = 0;
+ // This is needed only if we decide to make chunks 128-bit but still
+ // choose to use 64-bit word for bitmap. Ideally we should use 128-bit
+ // SIMD instructions to compute popcount.
+ if (kBitmapWordsPerVectorWord > 1) {
+ for (size_t i = RoundDown(word_offset, kBitmapWordsPerVectorWord); i < word_offset; i++) {
+ word = Bitmap::Begin()[i];
+ ret += POPCOUNT(word);
+ }
+ }
+ word = Bitmap::Begin()[word_offset];
+ const uintptr_t mask = Bitmap::BitIndexToMask(bit_idx);
+ DCHECK_NE(word & mask, 0u)
+ << " word_offset:" << word_offset
+ << " bit_idx:" << bit_idx
+ << " bit_idx_in_word:" << (bit_idx % Bitmap::kBitsPerBitmapWord)
+ << std::hex << " word: 0x" << word
+ << " mask: 0x" << mask << std::dec;
+ ret += POPCOUNT(word & (mask - 1));
+ return ret;
+}
+
+inline mirror::Object* MarkCompact::PostCompactBlackObjAddr(mirror::Object* old_ref) const {
+ return reinterpret_cast<mirror::Object*>(reinterpret_cast<uint8_t*>(old_ref)
+ - black_objs_slide_diff_);
+}
+
+inline mirror::Object* MarkCompact::PostCompactOldObjAddr(mirror::Object* old_ref) const {
+ const uintptr_t begin = live_words_bitmap_->Begin();
+ const uintptr_t addr_offset = reinterpret_cast<uintptr_t>(old_ref) - begin;
+ const size_t vec_idx = addr_offset / kOffsetChunkSize;
+ const size_t live_bytes_in_bitmap_word =
+ live_words_bitmap_->CountLiveWordsUpto(addr_offset / kAlignment) * kAlignment;
+ return reinterpret_cast<mirror::Object*>(begin
+ + chunk_info_vec_[vec_idx]
+ + live_bytes_in_bitmap_word);
+}
+
+inline mirror::Object* MarkCompact::PostCompactAddressUnchecked(mirror::Object* old_ref) const {
+ if (reinterpret_cast<uint8_t*>(old_ref) >= black_allocations_begin_) {
+ return PostCompactBlackObjAddr(old_ref);
+ }
+ if (kIsDebugBuild) {
+ mirror::Object* from_ref = GetFromSpaceAddr(old_ref);
+ DCHECK(live_words_bitmap_->Test(old_ref))
+ << "ref=" << old_ref;
+ if (!moving_space_bitmap_->Test(old_ref)) {
+ std::ostringstream oss;
+ Runtime::Current()->GetHeap()->DumpSpaces(oss);
+ MemMap::DumpMaps(oss, /* terse= */ true);
+ LOG(FATAL) << "ref=" << old_ref
+ << " from_ref=" << from_ref
+ << " from-space=" << static_cast<void*>(from_space_begin_)
+ << " bitmap= " << moving_space_bitmap_->DumpMemAround(old_ref)
+ << heap_->GetVerification()->DumpRAMAroundAddress(
+ reinterpret_cast<uintptr_t>(from_ref), 128)
+ << " maps\n" << oss.str();
+ }
+ }
+ return PostCompactOldObjAddr(old_ref);
+}
+
+inline mirror::Object* MarkCompact::PostCompactAddress(mirror::Object* old_ref) const {
+ // TODO: To further speedup the check, maybe we should consider caching heap
+ // start/end in this object.
+ if (LIKELY(live_words_bitmap_->HasAddress(old_ref))) {
+ return PostCompactAddressUnchecked(old_ref);
+ }
+ return old_ref;
+}
+
+} // namespace collector
+} // namespace gc
+} // namespace art
+
+#endif // ART_RUNTIME_GC_COLLECTOR_MARK_COMPACT_INL_H_
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
new file mode 100644
index 0000000..5251a6d
--- /dev/null
+++ b/runtime/gc/collector/mark_compact.cc
@@ -0,0 +1,3787 @@
+/*
+ * Copyright 2021 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 <fcntl.h>
+// Glibc v2.19 doesn't include these in fcntl.h so host builds will fail without.
+#if !defined(FALLOC_FL_PUNCH_HOLE) || !defined(FALLOC_FL_KEEP_SIZE)
+#include <linux/falloc.h>
+#endif
+#include <linux/userfaultfd.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <numeric>
+
+#include "android-base/file.h"
+#include "android-base/properties.h"
+#include "base/memfd.h"
+#include "base/quasi_atomic.h"
+#include "base/systrace.h"
+#include "base/utils.h"
+#include "gc/accounting/mod_union_table-inl.h"
+#include "gc/collector_type.h"
+#include "gc/reference_processor.h"
+#include "gc/space/bump_pointer_space.h"
+#include "gc/task_processor.h"
+#include "gc/verification-inl.h"
+#include "jit/jit_code_cache.h"
+#include "mark_compact-inl.h"
+#include "mirror/object-refvisitor-inl.h"
+#include "read_barrier_config.h"
+#include "scoped_thread_state_change-inl.h"
+#include "sigchain.h"
+#include "thread_list.h"
+
+#ifndef __BIONIC__
+#ifndef MREMAP_DONTUNMAP
+#define MREMAP_DONTUNMAP 4
+#endif
+#ifndef MAP_FIXED_NOREPLACE
+#define MAP_FIXED_NOREPLACE 0x100000
+#endif
+#ifndef __NR_userfaultfd
+#if defined(__x86_64__)
+#define __NR_userfaultfd 323
+#elif defined(__i386__)
+#define __NR_userfaultfd 374
+#elif defined(__aarch64__)
+#define __NR_userfaultfd 282
+#elif defined(__arm__)
+#define __NR_userfaultfd 388
+#else
+#error "__NR_userfaultfd undefined"
+#endif
+#endif // __NR_userfaultfd
+#endif // __BIONIC__
+
+namespace {
+
+using ::android::base::GetBoolProperty;
+
+}
+
+namespace art {
+
+static bool HaveMremapDontunmap() {
+ void* old = mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+ CHECK_NE(old, MAP_FAILED);
+ void* addr = mremap(old, kPageSize, kPageSize, MREMAP_MAYMOVE | MREMAP_DONTUNMAP, nullptr);
+ CHECK_EQ(munmap(old, kPageSize), 0);
+ if (addr != MAP_FAILED) {
+ CHECK_EQ(munmap(addr, kPageSize), 0);
+ return true;
+ } else {
+ return false;
+ }
+}
+// We require MREMAP_DONTUNMAP functionality of the mremap syscall, which was
+// introduced in 5.13 kernel version. But it was backported to GKI kernels.
+static bool gHaveMremapDontunmap = IsKernelVersionAtLeast(5, 13) || HaveMremapDontunmap();
+// Bitmap of features supported by userfaultfd. This is obtained via uffd API ioctl.
+static uint64_t gUffdFeatures = 0;
+// Both, missing and minor faults on shmem are needed only for minor-fault mode.
+static constexpr uint64_t kUffdFeaturesForMinorFault =
+ UFFD_FEATURE_MISSING_SHMEM | UFFD_FEATURE_MINOR_SHMEM;
+
+static bool KernelSupportsUffd() {
+#ifdef __linux__
+ if (gHaveMremapDontunmap) {
+ int fd = syscall(__NR_userfaultfd, O_CLOEXEC | UFFD_USER_MODE_ONLY);
+ // On non-android devices we may not have the kernel patches that restrict
+ // userfaultfd to user mode. But that is not a security concern as we are
+ // on host. Therefore, attempt one more time without UFFD_USER_MODE_ONLY.
+ if (!kIsTargetAndroid && fd == -1 && errno == EINVAL) {
+ fd = syscall(__NR_userfaultfd, O_CLOEXEC);
+ }
+ if (fd >= 0) {
+ // We are only fetching the available features, which is returned by the
+ // ioctl.
+ struct uffdio_api api = {.api = UFFD_API, .features = 0, .ioctls = 0};
+ CHECK_EQ(ioctl(fd, UFFDIO_API, &api), 0) << "ioctl_userfaultfd : API:" << strerror(errno);
+ gUffdFeatures = api.features;
+ close(fd);
+ // Allow this GC to be used only if minor-fault feature is available.
+ return (api.features & kUffdFeaturesForMinorFault) == kUffdFeaturesForMinorFault;
+ }
+ }
+#endif
+ return false;
+}
+
+// The other cases are defined as constexpr in runtime/read_barrier_config.h
+#if !defined(ART_FORCE_USE_READ_BARRIER) && defined(ART_USE_READ_BARRIER)
+// Returns collector type asked to be used on the cmdline.
+static gc::CollectorType FetchCmdlineGcType() {
+ std::string argv;
+ gc::CollectorType gc_type = gc::CollectorType::kCollectorTypeNone;
+ if (android::base::ReadFileToString("/proc/self/cmdline", &argv)) {
+ if (argv.find("-Xgc:CMC") != std::string::npos) {
+ gc_type = gc::CollectorType::kCollectorTypeCMC;
+ } else if (argv.find("-Xgc:CC") != std::string::npos) {
+ gc_type = gc::CollectorType::kCollectorTypeCC;
+ }
+ }
+ return gc_type;
+}
+
+static bool SysPropSaysUffdGc() {
+ return GetBoolProperty("persist.device_config.runtime_native_boot.enable_uffd_gc",
+ GetBoolProperty("ro.dalvik.vm.enable_uffd_gc", false));
+}
+
+static bool ShouldUseUserfaultfd() {
+ static_assert(kUseBakerReadBarrier || kUseTableLookupReadBarrier);
+#ifdef __linux__
+ // Use CMC/CC if that is being explicitly asked for on cmdline. Otherwise,
+ // always use CC on host. On target, use CMC only if system properties says so
+ // and the kernel supports it.
+ gc::CollectorType gc_type = FetchCmdlineGcType();
+ return gc_type == gc::CollectorType::kCollectorTypeCMC ||
+ (gc_type == gc::CollectorType::kCollectorTypeNone &&
+ kIsTargetAndroid &&
+ SysPropSaysUffdGc() &&
+ KernelSupportsUffd());
+#else
+ return false;
+#endif
+}
+
+const bool gUseUserfaultfd = ShouldUseUserfaultfd();
+const bool gUseReadBarrier = !gUseUserfaultfd;
+#endif
+
+namespace gc {
+namespace collector {
+
+// Turn off kCheckLocks when profiling the GC as it slows down the GC
+// significantly.
+static constexpr bool kCheckLocks = kDebugLocking;
+static constexpr bool kVerifyRootsMarked = kIsDebugBuild;
+// Two threads should suffice on devices.
+static constexpr size_t kMaxNumUffdWorkers = 2;
+// Minimum from-space chunk to be madvised (during concurrent compaction) in one go.
+static constexpr ssize_t kMinFromSpaceMadviseSize = 1 * MB;
+// Concurrent compaction termination logic is different (and slightly more efficient) if the
+// kernel has the fault-retry feature (allowing repeated faults on the same page), which was
+// introduced in 5.7 (https://android-review.git.corp.google.com/c/kernel/common/+/1540088).
+// This allows a single page fault to be handled, in turn, by each worker thread, only waking
+// up the GC thread at the end.
+static const bool gKernelHasFaultRetry = IsKernelVersionAtLeast(5, 7);
+
+std::pair<bool, bool> MarkCompact::GetUffdAndMinorFault() {
+ bool uffd_available;
+ // In most cases the gUffdFeatures will already be initialized at boot time
+ // when libart is loaded. On very old kernels we may get '0' from the kernel,
+ // in which case we would be doing the syscalls each time this function is
+ // called. But that's very unlikely case. There are no correctness issues as
+ // the response from kernel never changes after boot.
+ if (UNLIKELY(gUffdFeatures == 0)) {
+ uffd_available = KernelSupportsUffd();
+ } else {
+ // We can have any uffd features only if uffd exists.
+ uffd_available = true;
+ }
+ bool minor_fault_available =
+ (gUffdFeatures & kUffdFeaturesForMinorFault) == kUffdFeaturesForMinorFault;
+ return std::pair<bool, bool>(uffd_available, minor_fault_available);
+}
+
+bool MarkCompact::CreateUserfaultfd(bool post_fork) {
+ if (post_fork || uffd_ == kFdUnused) {
+ // Don't use O_NONBLOCK as we rely on read waiting on uffd_ if there isn't
+ // any read event available. We don't use poll.
+ uffd_ = syscall(__NR_userfaultfd, O_CLOEXEC | UFFD_USER_MODE_ONLY);
+ // On non-android devices we may not have the kernel patches that restrict
+ // userfaultfd to user mode. But that is not a security concern as we are
+ // on host. Therefore, attempt one more time without UFFD_USER_MODE_ONLY.
+ if (!kIsTargetAndroid && UNLIKELY(uffd_ == -1 && errno == EINVAL)) {
+ uffd_ = syscall(__NR_userfaultfd, O_CLOEXEC);
+ }
+ if (UNLIKELY(uffd_ == -1)) {
+ uffd_ = kFallbackMode;
+ LOG(WARNING) << "Userfaultfd isn't supported (reason: " << strerror(errno)
+ << ") and therefore falling back to stop-the-world compaction.";
+ } else {
+ DCHECK(IsValidFd(uffd_));
+ // Initialize uffd with the features which are required and available.
+ struct uffdio_api api = {
+ .api = UFFD_API, .features = gUffdFeatures & kUffdFeaturesForMinorFault, .ioctls = 0};
+ CHECK_EQ(ioctl(uffd_, UFFDIO_API, &api), 0) << "ioctl_userfaultfd: API: " << strerror(errno);
+ }
+ }
+ uffd_initialized_ = !post_fork || uffd_ == kFallbackMode;
+ return IsValidFd(uffd_);
+}
+
+template <size_t kAlignment>
+MarkCompact::LiveWordsBitmap<kAlignment>* MarkCompact::LiveWordsBitmap<kAlignment>::Create(
+ uintptr_t begin, uintptr_t end) {
+ return static_cast<LiveWordsBitmap<kAlignment>*>(
+ MemRangeBitmap::Create("Concurrent Mark Compact live words bitmap", begin, end));
+}
+
+MarkCompact::MarkCompact(Heap* heap)
+ : GarbageCollector(heap, "concurrent mark compact"),
+ gc_barrier_(0),
+ mark_stack_lock_("mark compact mark stack lock", kMarkSweepMarkStackLock),
+ bump_pointer_space_(heap->GetBumpPointerSpace()),
+ moving_space_bitmap_(bump_pointer_space_->GetMarkBitmap()),
+ moving_to_space_fd_(kFdUnused),
+ moving_from_space_fd_(kFdUnused),
+ uffd_(kFdUnused),
+ thread_pool_counter_(0),
+ compaction_in_progress_count_(0),
+ compacting_(false),
+ uffd_initialized_(false),
+ uffd_minor_fault_supported_(GetUffdAndMinorFault().second),
+ minor_fault_initialized_(false),
+ map_linear_alloc_shared_(false) {
+ // TODO: Depending on how the bump-pointer space move is implemented. If we
+ // switch between two virtual memories each time, then we will have to
+ // initialize live_words_bitmap_ accordingly.
+ live_words_bitmap_.reset(LiveWordsBitmap<kAlignment>::Create(
+ reinterpret_cast<uintptr_t>(bump_pointer_space_->Begin()),
+ reinterpret_cast<uintptr_t>(bump_pointer_space_->Limit())));
+
+ // Create one MemMap for all the data structures
+ size_t moving_space_size = bump_pointer_space_->Capacity();
+ size_t chunk_info_vec_size = moving_space_size / kOffsetChunkSize;
+ size_t nr_moving_pages = moving_space_size / kPageSize;
+ size_t nr_non_moving_pages = heap->GetNonMovingSpace()->Capacity() / kPageSize;
+
+ std::string err_msg;
+ info_map_ = MemMap::MapAnonymous("Concurrent mark-compact chunk-info vector",
+ chunk_info_vec_size * sizeof(uint32_t)
+ + nr_non_moving_pages * sizeof(ObjReference)
+ + nr_moving_pages * sizeof(ObjReference)
+ + nr_moving_pages * sizeof(uint32_t),
+ PROT_READ | PROT_WRITE,
+ /*low_4gb=*/ false,
+ &err_msg);
+ if (UNLIKELY(!info_map_.IsValid())) {
+ LOG(FATAL) << "Failed to allocate concurrent mark-compact chunk-info vector: " << err_msg;
+ } else {
+ uint8_t* p = info_map_.Begin();
+ chunk_info_vec_ = reinterpret_cast<uint32_t*>(p);
+ vector_length_ = chunk_info_vec_size;
+
+ p += chunk_info_vec_size * sizeof(uint32_t);
+ first_objs_non_moving_space_ = reinterpret_cast<ObjReference*>(p);
+
+ p += nr_non_moving_pages * sizeof(ObjReference);
+ first_objs_moving_space_ = reinterpret_cast<ObjReference*>(p);
+
+ p += nr_moving_pages * sizeof(ObjReference);
+ pre_compact_offset_moving_space_ = reinterpret_cast<uint32_t*>(p);
+ }
+
+ size_t moving_space_alignment = BestPageTableAlignment(moving_space_size);
+ // The moving space is created at a fixed address, which is expected to be
+ // PMD-size aligned.
+ if (!IsAlignedParam(bump_pointer_space_->Begin(), moving_space_alignment)) {
+ LOG(WARNING) << "Bump pointer space is not aligned to " << PrettySize(moving_space_alignment)
+ << ". This can lead to longer stop-the-world pauses for compaction";
+ }
+ // NOTE: PROT_NONE is used here as these mappings are for address space reservation
+ // only and will be used only after appropriately remapping them.
+ from_space_map_ = MemMap::MapAnonymousAligned("Concurrent mark-compact from-space",
+ moving_space_size,
+ PROT_NONE,
+ /*low_4gb=*/kObjPtrPoisoning,
+ moving_space_alignment,
+ &err_msg);
+ if (UNLIKELY(!from_space_map_.IsValid())) {
+ LOG(FATAL) << "Failed to allocate concurrent mark-compact from-space" << err_msg;
+ } else {
+ from_space_begin_ = from_space_map_.Begin();
+ }
+
+ // In some cases (32-bit or kObjPtrPoisoning) it's too much to ask for 3
+ // heap-sized mappings in low-4GB. So tolerate failure here by attempting to
+ // mmap again right before the compaction pause. And if even that fails, then
+ // running the GC cycle in copy-mode rather than minor-fault.
+ //
+ // This map doesn't have to be aligned to 2MB as we don't mremap on it.
+ if (!kObjPtrPoisoning && uffd_minor_fault_supported_) {
+ // We need this map only if minor-fault feature is supported. But in that case
+ // don't create the mapping if obj-ptr poisoning is enabled as then the mapping
+ // has to be created in low_4gb. Doing this here rather than later causes the
+ // Dex2oatImageTest.TestExtension gtest to fail in 64-bit platforms.
+ shadow_to_space_map_ = MemMap::MapAnonymous("Concurrent mark-compact moving-space shadow",
+ moving_space_size,
+ PROT_NONE,
+ /*low_4gb=*/false,
+ &err_msg);
+ if (!shadow_to_space_map_.IsValid()) {
+ LOG(WARNING) << "Failed to allocate concurrent mark-compact moving-space shadow: " << err_msg;
+ }
+ }
+ const size_t num_pages = 1 + std::min(heap_->GetParallelGCThreadCount(), kMaxNumUffdWorkers);
+ compaction_buffers_map_ = MemMap::MapAnonymous("Concurrent mark-compact compaction buffers",
+ kPageSize * num_pages,
+ PROT_READ | PROT_WRITE,
+ /*low_4gb=*/kObjPtrPoisoning,
+ &err_msg);
+ if (UNLIKELY(!compaction_buffers_map_.IsValid())) {
+ LOG(FATAL) << "Failed to allocate concurrent mark-compact compaction buffers" << err_msg;
+ }
+ // We also use the first page-sized buffer for the purpose of terminating concurrent compaction.
+ conc_compaction_termination_page_ = compaction_buffers_map_.Begin();
+ // Touch the page deliberately to avoid userfaults on it. We madvise it in
+ // CompactionPhase() before using it to terminate concurrent compaction.
+ CHECK_EQ(*conc_compaction_termination_page_, 0);
+ // In most of the cases, we don't expect more than one LinearAlloc space.
+ linear_alloc_spaces_data_.reserve(1);
+}
+
+void MarkCompact::AddLinearAllocSpaceData(uint8_t* begin, size_t len) {
+ DCHECK_ALIGNED(begin, kPageSize);
+ DCHECK_ALIGNED(len, kPageSize);
+ DCHECK_GE(len, kPMDSize);
+ size_t alignment = BestPageTableAlignment(len);
+ bool is_shared = false;
+ // We use MAP_SHARED on non-zygote processes for leveraging userfaultfd's minor-fault feature.
+ if (map_linear_alloc_shared_) {
+ void* ret = mmap(begin,
+ len,
+ PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_SHARED | MAP_FIXED,
+ /*fd=*/-1,
+ /*offset=*/0);
+ CHECK_EQ(ret, begin) << "mmap failed: " << strerror(errno);
+ is_shared = true;
+ }
+ std::string err_msg;
+ MemMap shadow(MemMap::MapAnonymousAligned("linear-alloc shadow map",
+ len,
+ PROT_NONE,
+ /*low_4gb=*/false,
+ alignment,
+ &err_msg));
+ if (!shadow.IsValid()) {
+ LOG(FATAL) << "Failed to allocate linear-alloc shadow map: " << err_msg;
+ UNREACHABLE();
+ }
+
+ MemMap page_status_map(MemMap::MapAnonymous("linear-alloc page-status map",
+ len / kPageSize,
+ PROT_READ | PROT_WRITE,
+ /*low_4gb=*/false,
+ &err_msg));
+ if (!page_status_map.IsValid()) {
+ LOG(FATAL) << "Failed to allocate linear-alloc page-status shadow map: " << err_msg;
+ UNREACHABLE();
+ }
+ linear_alloc_spaces_data_.emplace_back(std::forward<MemMap>(shadow),
+ std::forward<MemMap>(page_status_map),
+ begin,
+ begin + len,
+ is_shared);
+}
+
+void MarkCompact::BindAndResetBitmaps() {
+ // TODO: We need to hold heap_bitmap_lock_ only for populating immune_spaces.
+ // The card-table and mod-union-table processing can be done without it. So
+ // change the logic below. Note that the bitmap clearing would require the
+ // lock.
+ TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
+ accounting::CardTable* const card_table = heap_->GetCardTable();
+ // Mark all of the spaces we never collect as immune.
+ for (const auto& space : GetHeap()->GetContinuousSpaces()) {
+ if (space->GetGcRetentionPolicy() == space::kGcRetentionPolicyNeverCollect ||
+ space->GetGcRetentionPolicy() == space::kGcRetentionPolicyFullCollect) {
+ CHECK(space->IsZygoteSpace() || space->IsImageSpace());
+ immune_spaces_.AddSpace(space);
+ accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space);
+ if (table != nullptr) {
+ table->ProcessCards();
+ } else {
+ // Keep cards aged if we don't have a mod-union table since we may need
+ // to scan them in future GCs. This case is for app images.
+ // TODO: We could probably scan the objects right here to avoid doing
+ // another scan through the card-table.
+ card_table->ModifyCardsAtomic(
+ space->Begin(),
+ space->End(),
+ [](uint8_t card) {
+ return (card == gc::accounting::CardTable::kCardClean)
+ ? card
+ : gc::accounting::CardTable::kCardAged;
+ },
+ /* card modified visitor */ VoidFunctor());
+ }
+ } else {
+ CHECK(!space->IsZygoteSpace());
+ CHECK(!space->IsImageSpace());
+ // The card-table corresponding to bump-pointer and non-moving space can
+ // be cleared, because we are going to traverse all the reachable objects
+ // in these spaces. This card-table will eventually be used to track
+ // mutations while concurrent marking is going on.
+ card_table->ClearCardRange(space->Begin(), space->Limit());
+ if (space != bump_pointer_space_) {
+ CHECK_EQ(space, heap_->GetNonMovingSpace());
+ non_moving_space_ = space;
+ non_moving_space_bitmap_ = space->GetMarkBitmap();
+ }
+ }
+ }
+}
+
+void MarkCompact::InitializePhase() {
+ TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
+ mark_stack_ = heap_->GetMarkStack();
+ CHECK(mark_stack_->IsEmpty());
+ immune_spaces_.Reset();
+ moving_first_objs_count_ = 0;
+ non_moving_first_objs_count_ = 0;
+ black_page_count_ = 0;
+ freed_objects_ = 0;
+ from_space_slide_diff_ = from_space_begin_ - bump_pointer_space_->Begin();
+ black_allocations_begin_ = bump_pointer_space_->Limit();
+ walk_super_class_cache_ = nullptr;
+ compacting_ = false;
+ // TODO: Would it suffice to read it once in the constructor, which is called
+ // in zygote process?
+ pointer_size_ = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+}
+
+void MarkCompact::RunPhases() {
+ Thread* self = Thread::Current();
+ thread_running_gc_ = self;
+ InitializePhase();
+ GetHeap()->PreGcVerification(this);
+ {
+ ReaderMutexLock mu(self, *Locks::mutator_lock_);
+ MarkingPhase();
+ }
+ {
+ ScopedPause pause(this);
+ MarkingPause();
+ if (kIsDebugBuild) {
+ bump_pointer_space_->AssertAllThreadLocalBuffersAreRevoked();
+ }
+ }
+ // To increase likelihood of black allocations. For testing purposes only.
+ if (kIsDebugBuild && heap_->GetTaskProcessor()->GetRunningThread() == thread_running_gc_) {
+ usleep(500'000);
+ }
+ {
+ ReaderMutexLock mu(self, *Locks::mutator_lock_);
+ ReclaimPhase();
+ PrepareForCompaction();
+ }
+ if (uffd_ != kFallbackMode) {
+ heap_->GetThreadPool()->WaitForWorkersToBeCreated();
+ }
+ {
+ heap_->ThreadFlipBegin(self);
+ {
+ ScopedPause pause(this);
+ PreCompactionPhase();
+ }
+ heap_->ThreadFlipEnd(self);
+ }
+
+ if (IsValidFd(uffd_)) {
+ ReaderMutexLock mu(self, *Locks::mutator_lock_);
+ CompactionPhase();
+ }
+
+ FinishPhase();
+ thread_running_gc_ = nullptr;
+ GetHeap()->PostGcVerification(this);
+}
+
+void MarkCompact::InitMovingSpaceFirstObjects(const size_t vec_len) {
+ // Find the first live word first.
+ size_t to_space_page_idx = 0;
+ uint32_t offset_in_chunk_word;
+ uint32_t offset;
+ mirror::Object* obj;
+ const uintptr_t heap_begin = moving_space_bitmap_->HeapBegin();
+
+ size_t chunk_idx;
+ // Find the first live word in the space
+ for (chunk_idx = 0; chunk_info_vec_[chunk_idx] == 0; chunk_idx++) {
+ if (chunk_idx > vec_len) {
+ // We don't have any live data on the moving-space.
+ return;
+ }
+ }
+ // Use live-words bitmap to find the first word
+ offset_in_chunk_word = live_words_bitmap_->FindNthLiveWordOffset(chunk_idx, /*n*/ 0);
+ offset = chunk_idx * kBitsPerVectorWord + offset_in_chunk_word;
+ DCHECK(live_words_bitmap_->Test(offset)) << "offset=" << offset
+ << " chunk_idx=" << chunk_idx
+ << " N=0"
+ << " offset_in_word=" << offset_in_chunk_word
+ << " word=" << std::hex
+ << live_words_bitmap_->GetWord(chunk_idx);
+ // The first object doesn't require using FindPrecedingObject().
+ obj = reinterpret_cast<mirror::Object*>(heap_begin + offset * kAlignment);
+ // TODO: add a check to validate the object.
+
+ pre_compact_offset_moving_space_[to_space_page_idx] = offset;
+ first_objs_moving_space_[to_space_page_idx].Assign(obj);
+ to_space_page_idx++;
+
+ uint32_t page_live_bytes = 0;
+ while (true) {
+ for (; page_live_bytes <= kPageSize; chunk_idx++) {
+ if (chunk_idx > vec_len) {
+ moving_first_objs_count_ = to_space_page_idx;
+ return;
+ }
+ page_live_bytes += chunk_info_vec_[chunk_idx];
+ }
+ chunk_idx--;
+ page_live_bytes -= kPageSize;
+ DCHECK_LE(page_live_bytes, kOffsetChunkSize);
+ DCHECK_LE(page_live_bytes, chunk_info_vec_[chunk_idx])
+ << " chunk_idx=" << chunk_idx
+ << " to_space_page_idx=" << to_space_page_idx
+ << " vec_len=" << vec_len;
+ DCHECK(IsAligned<kAlignment>(chunk_info_vec_[chunk_idx] - page_live_bytes));
+ offset_in_chunk_word =
+ live_words_bitmap_->FindNthLiveWordOffset(
+ chunk_idx, (chunk_info_vec_[chunk_idx] - page_live_bytes) / kAlignment);
+ offset = chunk_idx * kBitsPerVectorWord + offset_in_chunk_word;
+ DCHECK(live_words_bitmap_->Test(offset))
+ << "offset=" << offset
+ << " chunk_idx=" << chunk_idx
+ << " N=" << ((chunk_info_vec_[chunk_idx] - page_live_bytes) / kAlignment)
+ << " offset_in_word=" << offset_in_chunk_word
+ << " word=" << std::hex << live_words_bitmap_->GetWord(chunk_idx);
+ // TODO: Can we optimize this for large objects? If we are continuing a
+ // large object that spans multiple pages, then we may be able to do without
+ // calling FindPrecedingObject().
+ //
+ // Find the object which encapsulates offset in it, which could be
+ // starting at offset itself.
+ obj = moving_space_bitmap_->FindPrecedingObject(heap_begin + offset * kAlignment);
+ // TODO: add a check to validate the object.
+ pre_compact_offset_moving_space_[to_space_page_idx] = offset;
+ first_objs_moving_space_[to_space_page_idx].Assign(obj);
+ to_space_page_idx++;
+ chunk_idx++;
+ }
+}
+
+void MarkCompact::InitNonMovingSpaceFirstObjects() {
+ accounting::ContinuousSpaceBitmap* bitmap = non_moving_space_->GetLiveBitmap();
+ uintptr_t begin = reinterpret_cast<uintptr_t>(non_moving_space_->Begin());
+ const uintptr_t end = reinterpret_cast<uintptr_t>(non_moving_space_->End());
+ mirror::Object* prev_obj;
+ size_t page_idx;
+ {
+ // Find first live object
+ mirror::Object* obj = nullptr;
+ bitmap->VisitMarkedRange</*kVisitOnce*/ true>(begin,
+ end,
+ [&obj] (mirror::Object* o) {
+ obj = o;
+ });
+ if (obj == nullptr) {
+ // There are no live objects in the non-moving space
+ return;
+ }
+ page_idx = (reinterpret_cast<uintptr_t>(obj) - begin) / kPageSize;
+ first_objs_non_moving_space_[page_idx++].Assign(obj);
+ prev_obj = obj;
+ }
+ // TODO: check obj is valid
+ uintptr_t prev_obj_end = reinterpret_cast<uintptr_t>(prev_obj)
+ + RoundUp(prev_obj->SizeOf<kDefaultVerifyFlags>(), kAlignment);
+ // For every page find the object starting from which we need to call
+ // VisitReferences. It could either be an object that started on some
+ // preceding page, or some object starting within this page.
+ begin = RoundDown(reinterpret_cast<uintptr_t>(prev_obj) + kPageSize, kPageSize);
+ while (begin < end) {
+ // Utilize, if any, large object that started in some preceding page, but
+ // overlaps with this page as well.
+ if (prev_obj != nullptr && prev_obj_end > begin) {
+ DCHECK_LT(prev_obj, reinterpret_cast<mirror::Object*>(begin));
+ first_objs_non_moving_space_[page_idx].Assign(prev_obj);
+ mirror::Class* klass = prev_obj->GetClass<kVerifyNone, kWithoutReadBarrier>();
+ if (bump_pointer_space_->HasAddress(klass)) {
+ LOG(WARNING) << "found inter-page object " << prev_obj
+ << " in non-moving space with klass " << klass
+ << " in moving space";
+ }
+ } else {
+ prev_obj_end = 0;
+ // It's sufficient to only search for previous object in the preceding page.
+ // If no live object started in that page and some object had started in
+ // the page preceding to that page, which was big enough to overlap with
+ // the current page, then we wouldn't be in the else part.
+ prev_obj = bitmap->FindPrecedingObject(begin, begin - kPageSize);
+ if (prev_obj != nullptr) {
+ prev_obj_end = reinterpret_cast<uintptr_t>(prev_obj)
+ + RoundUp(prev_obj->SizeOf<kDefaultVerifyFlags>(), kAlignment);
+ }
+ if (prev_obj_end > begin) {
+ mirror::Class* klass = prev_obj->GetClass<kVerifyNone, kWithoutReadBarrier>();
+ if (bump_pointer_space_->HasAddress(klass)) {
+ LOG(WARNING) << "found inter-page object " << prev_obj
+ << " in non-moving space with klass " << klass
+ << " in moving space";
+ }
+ first_objs_non_moving_space_[page_idx].Assign(prev_obj);
+ } else {
+ // Find the first live object in this page
+ bitmap->VisitMarkedRange</*kVisitOnce*/ true>(
+ begin,
+ begin + kPageSize,
+ [this, page_idx] (mirror::Object* obj) {
+ first_objs_non_moving_space_[page_idx].Assign(obj);
+ });
+ }
+ // An empty entry indicates that the page has no live objects and hence
+ // can be skipped.
+ }
+ begin += kPageSize;
+ page_idx++;
+ }
+ non_moving_first_objs_count_ = page_idx;
+}
+
+bool MarkCompact::CanCompactMovingSpaceWithMinorFault() {
+ size_t min_size = (moving_first_objs_count_ + black_page_count_) * kPageSize;
+ return minor_fault_initialized_ && shadow_to_space_map_.IsValid() &&
+ shadow_to_space_map_.Size() >= min_size;
+}
+
+class MarkCompact::ConcurrentCompactionGcTask : public SelfDeletingTask {
+ public:
+ explicit ConcurrentCompactionGcTask(MarkCompact* collector, size_t idx)
+ : collector_(collector), index_(idx) {}
+
+ void Run(Thread* self ATTRIBUTE_UNUSED) override REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (collector_->CanCompactMovingSpaceWithMinorFault()) {
+ collector_->ConcurrentCompaction<MarkCompact::kMinorFaultMode>(/*buf=*/nullptr);
+ } else {
+ // The passed page/buf to ConcurrentCompaction is used by the thread as a
+ // kPageSize buffer for compacting and updating objects into and then
+ // passing the buf to uffd ioctls.
+ uint8_t* buf = collector_->compaction_buffers_map_.Begin() + index_ * kPageSize;
+ collector_->ConcurrentCompaction<MarkCompact::kCopyMode>(buf);
+ }
+ }
+
+ private:
+ MarkCompact* const collector_;
+ size_t index_;
+};
+
+void MarkCompact::PrepareForCompaction() {
+ uint8_t* space_begin = bump_pointer_space_->Begin();
+ size_t vector_len = (black_allocations_begin_ - space_begin) / kOffsetChunkSize;
+ DCHECK_LE(vector_len, vector_length_);
+ for (size_t i = 0; i < vector_len; i++) {
+ DCHECK_LE(chunk_info_vec_[i], kOffsetChunkSize);
+ DCHECK_EQ(chunk_info_vec_[i], live_words_bitmap_->LiveBytesInBitmapWord(i));
+ }
+ InitMovingSpaceFirstObjects(vector_len);
+ InitNonMovingSpaceFirstObjects();
+
+ // TODO: We can do a lot of neat tricks with this offset vector to tune the
+ // compaction as we wish. Originally, the compaction algorithm slides all
+ // live objects towards the beginning of the heap. This is nice because it
+ // keeps the spatial locality of objects intact.
+ // However, sometimes it's desired to compact objects in certain portions
+ // of the heap. For instance, it is expected that, over time,
+ // objects towards the beginning of the heap are long lived and are always
+ // densely packed. In this case, it makes sense to only update references in
+ // there and not try to compact it.
+ // Furthermore, we might have some large objects and may not want to move such
+ // objects.
+ // We can adjust, without too much effort, the values in the chunk_info_vec_ such
+ // that the objects in the dense beginning area aren't moved. OTOH, large
+ // objects, which could be anywhere in the heap, could also be kept from
+ // moving by using a similar trick. The only issue is that by doing this we will
+ // leave an unused hole in the middle of the heap which can't be used for
+ // allocations until we do a *full* compaction.
+ //
+ // At this point every element in the chunk_info_vec_ contains the live-bytes
+ // of the corresponding chunk. For old-to-new address computation we need
+ // every element to reflect total live-bytes till the corresponding chunk.
+
+ // Live-bytes count is required to compute post_compact_end_ below.
+ uint32_t total;
+ // Update the vector one past the heap usage as it is required for black
+ // allocated objects' post-compact address computation.
+ if (vector_len < vector_length_) {
+ vector_len++;
+ total = 0;
+ } else {
+ // Fetch the value stored in the last element before it gets overwritten by
+ // std::exclusive_scan().
+ total = chunk_info_vec_[vector_len - 1];
+ }
+ std::exclusive_scan(chunk_info_vec_, chunk_info_vec_ + vector_len, chunk_info_vec_, 0);
+ total += chunk_info_vec_[vector_len - 1];
+
+ for (size_t i = vector_len; i < vector_length_; i++) {
+ DCHECK_EQ(chunk_info_vec_[i], 0u);
+ }
+ post_compact_end_ = AlignUp(space_begin + total, kPageSize);
+ CHECK_EQ(post_compact_end_, space_begin + moving_first_objs_count_ * kPageSize);
+ black_objs_slide_diff_ = black_allocations_begin_ - post_compact_end_;
+ // How do we handle compaction of heap portion used for allocations after the
+ // marking-pause?
+ // All allocations after the marking-pause are considered black (reachable)
+ // for this GC cycle. However, they need not be allocated contiguously as
+ // different mutators use TLABs. So we will compact the heap till the point
+ // where allocations took place before the marking-pause. And everything after
+ // that will be slid with TLAB holes, and then TLAB info in TLS will be
+ // appropriately updated in the pre-compaction pause.
+ // The chunk-info vector entries for the post marking-pause allocations will be
+ // also updated in the pre-compaction pause.
+
+ bool is_zygote = Runtime::Current()->IsZygote();
+ if (!uffd_initialized_ && CreateUserfaultfd(/*post_fork*/false)) {
+ // Register the buffer that we use for terminating concurrent compaction
+ struct uffdio_register uffd_register;
+ uffd_register.range.start = reinterpret_cast<uintptr_t>(conc_compaction_termination_page_);
+ uffd_register.range.len = kPageSize;
+ uffd_register.mode = UFFDIO_REGISTER_MODE_MISSING;
+ CHECK_EQ(ioctl(uffd_, UFFDIO_REGISTER, &uffd_register), 0)
+ << "ioctl_userfaultfd: register compaction termination page: " << strerror(errno);
+
+ if (!uffd_minor_fault_supported_ && shadow_to_space_map_.IsValid()) {
+ // A valid shadow-map for moving space is only possible if we
+ // were able to map it in the constructor. That also means that its size
+ // matches the moving-space.
+ CHECK_EQ(shadow_to_space_map_.Size(), bump_pointer_space_->Capacity());
+ // Release the shadow map for moving-space if we don't support minor-fault
+ // as it's not required.
+ shadow_to_space_map_.Reset();
+ }
+ }
+ // For zygote we create the thread pool each time before starting compaction,
+ // and get rid of it when finished. This is expected to happen rarely as
+ // zygote spends most of the time in native fork loop.
+ if (uffd_ != kFallbackMode) {
+ ThreadPool* pool = heap_->GetThreadPool();
+ if (UNLIKELY(pool == nullptr)) {
+ // On devices with 2 cores, GetParallelGCThreadCount() will return 1,
+ // which is desired number of workers on such devices.
+ heap_->CreateThreadPool(std::min(heap_->GetParallelGCThreadCount(), kMaxNumUffdWorkers));
+ pool = heap_->GetThreadPool();
+ }
+ size_t num_threads = pool->GetThreadCount();
+ thread_pool_counter_ = num_threads;
+ for (size_t i = 0; i < num_threads; i++) {
+ pool->AddTask(thread_running_gc_, new ConcurrentCompactionGcTask(this, i + 1));
+ }
+ CHECK_EQ(pool->GetTaskCount(thread_running_gc_), num_threads);
+
+ /*
+ * Possible scenarios for mappings:
+ * A) All zygote GCs (or if minor-fault feature isn't available): uses
+ * uffd's copy mode
+ * 1) For moving-space ('to' space is same as the moving-space):
+ * a) Private-anonymous mappings for 'to' and 'from' space are created in
+ * the constructor.
+ * b) In the compaction pause, we mremap(dontunmap) from 'to' space to
+ * 'from' space. This results in moving all pages to 'from' space and
+ * emptying the 'to' space, thereby preparing it for userfaultfd
+ * registration.
+ *
+ * 2) For linear-alloc space:
+ * a) Private-anonymous mappings for the linear-alloc and its 'shadow'
+ * are created by the arena-pool.
+ * b) In the compaction pause, we mremap(dontumap) with similar effect as
+ * (A.1.b) above.
+ *
+ * B) First GC after zygote: uses uffd's copy-mode
+ * 1) For moving-space:
+ * a) If the mmap for shadow-map has been successful in the constructor,
+ * then we remap it (mmap with MAP_FIXED) to get a shared-anonymous
+ * mapping.
+ * b) Else, we create two memfd and ftruncate them to the moving-space
+ * size.
+ * c) Same as (A.1.b)
+ * d) If (B.1.a), then mremap(dontunmap) from shadow-map to
+ * 'to' space. This will make both of them map to the same pages
+ * e) If (B.1.b), then mmap with the first memfd in shared mode on the
+ * 'to' space.
+ * f) At the end of compaction, we will have moved the moving-space
+ * objects to a MAP_SHARED mapping, readying it for minor-fault from next
+ * GC cycle.
+ *
+ * 2) For linear-alloc space:
+ * a) Same as (A.2.b)
+ * b) mmap a shared-anonymous mapping onto the linear-alloc space.
+ * c) Same as (B.1.f)
+ *
+ * C) All subsequent GCs: preferable minor-fault mode. But may also require
+ * using copy-mode.
+ * 1) For moving-space:
+ * a) If the shadow-map is created and no memfd was used, then that means
+ * we are using shared-anonymous. Therefore, mmap a shared-anonymous on
+ * the shadow-space.
+ * b) If the shadow-map is not mapped yet, then mmap one with a size
+ * big enough to hold the compacted moving space. This may fail, in which
+ * case we will use uffd's copy-mode.
+ * c) If (b) is successful, then mmap the free memfd onto shadow-map.
+ * d) Same as (A.1.b)
+ * e) In compaction pause, if the shadow-map was not created, then use
+ * copy-mode.
+ * f) Else, if the created map is smaller than the required-size, then
+ * use mremap (without dontunmap) to expand the size. If failed, then use
+ * copy-mode.
+ * g) Otherwise, same as (B.1.d) and use minor-fault mode.
+ *
+ * 2) For linear-alloc space:
+ * a) Same as (A.2.b)
+ * b) Use minor-fault mode
+ */
+ auto mmap_shadow_map = [this](int flags, int fd) {
+ void* ret = mmap(shadow_to_space_map_.Begin(),
+ shadow_to_space_map_.Size(),
+ PROT_READ | PROT_WRITE,
+ flags,
+ fd,
+ /*offset=*/0);
+ DCHECK_NE(ret, MAP_FAILED) << "mmap for moving-space shadow failed:" << strerror(errno);
+ };
+ // Setup all the virtual memory ranges required for concurrent compaction.
+ if (minor_fault_initialized_) {
+ DCHECK(!is_zygote);
+ if (UNLIKELY(!shadow_to_space_map_.IsValid())) {
+ // This case happens only once on the first GC in minor-fault mode, if
+ // we were unable to reserve shadow-map for moving-space in the
+ // beginning.
+ DCHECK_GE(moving_to_space_fd_, 0);
+ // Take extra 4MB to reduce the likelihood of requiring resizing this
+ // map in the pause due to black allocations.
+ size_t reqd_size = std::min(moving_first_objs_count_ * kPageSize + 4 * MB,
+ bump_pointer_space_->Capacity());
+ // We cannot support memory-tool with shadow-map (as it requires
+ // appending a redzone) in this case because the mapping may have to be expanded
+ // using mremap (in KernelPreparation()), which would ignore the redzone.
+ // MemMap::MapFile() appends a redzone, but MemMap::MapAnonymous() doesn't.
+ std::string err_msg;
+ shadow_to_space_map_ = MemMap::MapAnonymous("moving-space-shadow",
+ reqd_size,
+ PROT_NONE,
+ /*low_4gb=*/kObjPtrPoisoning,
+ &err_msg);
+
+ if (shadow_to_space_map_.IsValid()) {
+ CHECK(!kMemoryToolAddsRedzones || shadow_to_space_map_.GetRedzoneSize() == 0u);
+ // We want to use MemMap to get low-4GB mapping, if required, but then also
+ // want to have its ownership as we may grow it (in
+ // KernelPreparation()). If the ownership is not taken and we try to
+ // resize MemMap, then it unmaps the virtual range.
+ MemMap temp = shadow_to_space_map_.TakeReservedMemory(shadow_to_space_map_.Size(),
+ /*reuse*/ true);
+ std::swap(temp, shadow_to_space_map_);
+ DCHECK(!temp.IsValid());
+ } else {
+ LOG(WARNING) << "Failed to create moving space's shadow map of " << PrettySize(reqd_size)
+ << " size. " << err_msg;
+ }
+ }
+
+ if (LIKELY(shadow_to_space_map_.IsValid())) {
+ int fd = moving_to_space_fd_;
+ int mmap_flags = MAP_SHARED | MAP_FIXED;
+ if (fd == kFdUnused) {
+ // Unused moving-to-space fd means we are using anonymous shared
+ // mapping.
+ DCHECK_EQ(shadow_to_space_map_.Size(), bump_pointer_space_->Capacity());
+ mmap_flags |= MAP_ANONYMOUS;
+ fd = -1;
+ }
+ // If the map is smaller than required, then we'll do mremap in the
+ // compaction pause to increase the size.
+ mmap_shadow_map(mmap_flags, fd);
+ }
+
+ for (auto& data : linear_alloc_spaces_data_) {
+ DCHECK_EQ(mprotect(data.shadow_.Begin(), data.shadow_.Size(), PROT_READ | PROT_WRITE), 0)
+ << "mprotect failed: " << strerror(errno);
+ }
+ } else if (!is_zygote && uffd_minor_fault_supported_) {
+ // First GC after zygote-fork. We will still use uffd's copy mode but will
+ // use it to move objects to MAP_SHARED (to prepare for subsequent GCs, which
+ // will use uffd's minor-fault feature).
+ if (shadow_to_space_map_.IsValid() &&
+ shadow_to_space_map_.Size() == bump_pointer_space_->Capacity()) {
+ mmap_shadow_map(MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, /*fd=*/-1);
+ } else {
+ size_t size = bump_pointer_space_->Capacity();
+ DCHECK_EQ(moving_to_space_fd_, kFdUnused);
+ DCHECK_EQ(moving_from_space_fd_, kFdUnused);
+ const char* name = bump_pointer_space_->GetName();
+ moving_to_space_fd_ = memfd_create(name, MFD_CLOEXEC);
+ CHECK_NE(moving_to_space_fd_, -1)
+ << "memfd_create: failed for " << name << ": " << strerror(errno);
+ moving_from_space_fd_ = memfd_create(name, MFD_CLOEXEC);
+ CHECK_NE(moving_from_space_fd_, -1)
+ << "memfd_create: failed for " << name << ": " << strerror(errno);
+
+ // memfds are considered as files from resource limits point of view.
+ // And the moving space could be several hundred MBs. So increase the
+ // limit, if it's lower than moving-space size.
+ bool rlimit_changed = false;
+ rlimit rlim_read;
+ CHECK_EQ(getrlimit(RLIMIT_FSIZE, &rlim_read), 0) << "getrlimit failed: " << strerror(errno);
+ if (rlim_read.rlim_cur < size) {
+ rlimit_changed = true;
+ rlimit rlim = rlim_read;
+ rlim.rlim_cur = size;
+ CHECK_EQ(setrlimit(RLIMIT_FSIZE, &rlim), 0) << "setrlimit failed: " << strerror(errno);
+ }
+
+ // moving-space will map this fd so that we compact objects into it.
+ int ret = ftruncate(moving_to_space_fd_, size);
+ CHECK_EQ(ret, 0) << "ftruncate failed for moving-space:" << strerror(errno);
+ ret = ftruncate(moving_from_space_fd_, size);
+ CHECK_EQ(ret, 0) << "ftruncate failed for moving-space:" << strerror(errno);
+
+ if (rlimit_changed) {
+ // reset the rlimit to the original limits.
+ CHECK_EQ(setrlimit(RLIMIT_FSIZE, &rlim_read), 0)
+ << "setrlimit failed: " << strerror(errno);
+ }
+ }
+ }
+ }
+}
+
+class MarkCompact::VerifyRootMarkedVisitor : public SingleRootVisitor {
+ public:
+ explicit VerifyRootMarkedVisitor(MarkCompact* collector) : collector_(collector) { }
+
+ void VisitRoot(mirror::Object* root, const RootInfo& info) override
+ REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
+ CHECK(collector_->IsMarked(root) != nullptr) << info.ToString();
+ }
+
+ private:
+ MarkCompact* const collector_;
+};
+
+void MarkCompact::ReMarkRoots(Runtime* runtime) {
+ TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
+ DCHECK_EQ(thread_running_gc_, Thread::Current());
+ Locks::mutator_lock_->AssertExclusiveHeld(thread_running_gc_);
+ MarkNonThreadRoots(runtime);
+ MarkConcurrentRoots(static_cast<VisitRootFlags>(kVisitRootFlagNewRoots
+ | kVisitRootFlagStopLoggingNewRoots
+ | kVisitRootFlagClearRootLog),
+ runtime);
+
+ if (kVerifyRootsMarked) {
+ TimingLogger::ScopedTiming t2("(Paused)VerifyRoots", GetTimings());
+ VerifyRootMarkedVisitor visitor(this);
+ runtime->VisitRoots(&visitor);
+ }
+}
+
+void MarkCompact::MarkingPause() {
+ TimingLogger::ScopedTiming t("(Paused)MarkingPause", GetTimings());
+ Runtime* runtime = Runtime::Current();
+ Locks::mutator_lock_->AssertExclusiveHeld(thread_running_gc_);
+ {
+ // Handle the dirty objects as we are a concurrent GC
+ WriterMutexLock mu(thread_running_gc_, *Locks::heap_bitmap_lock_);
+ {
+ MutexLock mu2(thread_running_gc_, *Locks::runtime_shutdown_lock_);
+ MutexLock mu3(thread_running_gc_, *Locks::thread_list_lock_);
+ std::list<Thread*> thread_list = runtime->GetThreadList()->GetList();
+ for (Thread* thread : thread_list) {
+ thread->VisitRoots(this, static_cast<VisitRootFlags>(0));
+ // Need to revoke all the thread-local allocation stacks since we will
+ // swap the allocation stacks (below) and don't want anybody to allocate
+ // into the live stack.
+ thread->RevokeThreadLocalAllocationStack();
+ bump_pointer_space_->RevokeThreadLocalBuffers(thread);
+ }
+ }
+ // Re-mark root set. Doesn't include thread-roots as they are already marked
+ // above.
+ ReMarkRoots(runtime);
+ // Scan dirty objects.
+ RecursiveMarkDirtyObjects(/*paused*/ true, accounting::CardTable::kCardDirty);
+ {
+ TimingLogger::ScopedTiming t2("SwapStacks", GetTimings());
+ heap_->SwapStacks();
+ live_stack_freeze_size_ = heap_->GetLiveStack()->Size();
+ }
+ }
+ // Fetch only the accumulated objects-allocated count as it is guaranteed to
+ // be up-to-date after the TLAB revocation above.
+ freed_objects_ += bump_pointer_space_->GetAccumulatedObjectsAllocated();
+ // TODO: For PreSweepingGcVerification(), find correct strategy to visit/walk
+ // objects in bump-pointer space when we have a mark-bitmap to indicate live
+ // objects. At the same time we also need to be able to visit black allocations,
+ // even though they are not marked in the bitmap. Without both of these we fail
+ // pre-sweeping verification. As well as we leave windows open wherein a
+ // VisitObjects/Walk on the space would either miss some objects or visit
+ // unreachable ones. These windows are when we are switching from shared
+ // mutator-lock to exclusive and vice-versa starting from here till compaction pause.
+ // heap_->PreSweepingGcVerification(this);
+
+ // Disallow new system weaks to prevent a race which occurs when someone adds
+ // a new system weak before we sweep them. Since this new system weak may not
+ // be marked, the GC may incorrectly sweep it. This also fixes a race where
+ // interning may attempt to return a strong reference to a string that is
+ // about to be swept.
+ runtime->DisallowNewSystemWeaks();
+ // Enable the reference processing slow path, needs to be done with mutators
+ // paused since there is no lock in the GetReferent fast path.
+ heap_->GetReferenceProcessor()->EnableSlowPath();
+
+ // Capture 'end' of moving-space at this point. Every allocation beyond this
+ // point will be considered as black.
+ // Align-up to page boundary so that black allocations happen from next page
+ // onwards.
+ black_allocations_begin_ = bump_pointer_space_->AlignEnd(thread_running_gc_, kPageSize);
+ DCHECK(IsAligned<kAlignment>(black_allocations_begin_));
+ black_allocations_begin_ = AlignUp(black_allocations_begin_, kPageSize);
+}
+
+void MarkCompact::SweepSystemWeaks(Thread* self, Runtime* runtime, const bool paused) {
+ TimingLogger::ScopedTiming t(paused ? "(Paused)SweepSystemWeaks" : "SweepSystemWeaks",
+ GetTimings());
+ ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ runtime->SweepSystemWeaks(this);
+}
+
+void MarkCompact::ProcessReferences(Thread* self) {
+ WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
+ GetHeap()->GetReferenceProcessor()->ProcessReferences(self, GetTimings());
+}
+
+void MarkCompact::Sweep(bool swap_bitmaps) {
+ TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
+ // Ensure that nobody inserted objects in the live stack after we swapped the
+ // stacks.
+ CHECK_GE(live_stack_freeze_size_, GetHeap()->GetLiveStack()->Size());
+ {
+ TimingLogger::ScopedTiming t2("MarkAllocStackAsLive", GetTimings());
+ // Mark everything allocated since the last GC as live so that we can sweep
+ // concurrently, knowing that new allocations won't be marked as live.
+ accounting::ObjectStack* live_stack = heap_->GetLiveStack();
+ heap_->MarkAllocStackAsLive(live_stack);
+ live_stack->Reset();
+ DCHECK(mark_stack_->IsEmpty());
+ }
+ for (const auto& space : GetHeap()->GetContinuousSpaces()) {
+ if (space->IsContinuousMemMapAllocSpace() && space != bump_pointer_space_) {
+ space::ContinuousMemMapAllocSpace* alloc_space = space->AsContinuousMemMapAllocSpace();
+ TimingLogger::ScopedTiming split(
+ alloc_space->IsZygoteSpace() ? "SweepZygoteSpace" : "SweepMallocSpace",
+ GetTimings());
+ RecordFree(alloc_space->Sweep(swap_bitmaps));
+ }
+ }
+ SweepLargeObjects(swap_bitmaps);
+}
+
+void MarkCompact::SweepLargeObjects(bool swap_bitmaps) {
+ space::LargeObjectSpace* los = heap_->GetLargeObjectsSpace();
+ if (los != nullptr) {
+ TimingLogger::ScopedTiming split(__FUNCTION__, GetTimings());
+ RecordFreeLOS(los->Sweep(swap_bitmaps));
+ }
+}
+
+void MarkCompact::ReclaimPhase() {
+ TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
+ DCHECK(thread_running_gc_ == Thread::Current());
+ Runtime* const runtime = Runtime::Current();
+ // Process the references concurrently.
+ ProcessReferences(thread_running_gc_);
+ // TODO: Try to merge this system-weak sweeping with the one while updating
+ // references during the compaction pause.
+ SweepSystemWeaks(thread_running_gc_, runtime, /*paused*/ false);
+ runtime->AllowNewSystemWeaks();
+ // Clean up class loaders after system weaks are swept since that is how we know if class
+ // unloading occurred.
+ runtime->GetClassLinker()->CleanupClassLoaders();
+ {
+ WriterMutexLock mu(thread_running_gc_, *Locks::heap_bitmap_lock_);
+ // Reclaim unmarked objects.
+ Sweep(false);
+ // Swap the live and mark bitmaps for each space which we modified space. This is an
+ // optimization that enables us to not clear live bits inside of the sweep. Only swaps unbound
+ // bitmaps.
+ SwapBitmaps();
+ // Unbind the live and mark bitmaps.
+ GetHeap()->UnBindBitmaps();
+ }
+}
+
+// We want to avoid checking for every reference if it's within the page or
+// not. This can be done if we know where in the page the holder object lies.
+// If it doesn't overlap either boundaries then we can skip the checks.
+template <bool kCheckBegin, bool kCheckEnd>
+class MarkCompact::RefsUpdateVisitor {
+ public:
+ explicit RefsUpdateVisitor(MarkCompact* collector,
+ mirror::Object* obj,
+ uint8_t* begin,
+ uint8_t* end)
+ : collector_(collector), obj_(obj), begin_(begin), end_(end) {
+ DCHECK(!kCheckBegin || begin != nullptr);
+ DCHECK(!kCheckEnd || end != nullptr);
+ }
+
+ void operator()(mirror::Object* old ATTRIBUTE_UNUSED, MemberOffset offset, bool /* is_static */)
+ const ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES_SHARED(Locks::heap_bitmap_lock_) {
+ bool update = true;
+ if (kCheckBegin || kCheckEnd) {
+ uint8_t* ref = reinterpret_cast<uint8_t*>(obj_) + offset.Int32Value();
+ update = (!kCheckBegin || ref >= begin_) && (!kCheckEnd || ref < end_);
+ }
+ if (update) {
+ collector_->UpdateRef(obj_, offset);
+ }
+ }
+
+ // For object arrays we don't need to check boundaries here as it's done in
+ // VisitReferenes().
+ // TODO: Optimize reference updating using SIMD instructions. Object arrays
+ // are perfect as all references are tightly packed.
+ void operator()(mirror::Object* old ATTRIBUTE_UNUSED,
+ MemberOffset offset,
+ bool /*is_static*/,
+ bool /*is_obj_array*/)
+ const ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES_SHARED(Locks::heap_bitmap_lock_) {
+ collector_->UpdateRef(obj_, offset);
+ }
+
+ void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+ ALWAYS_INLINE
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!root->IsNull()) {
+ VisitRoot(root);
+ }
+ }
+
+ void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+ ALWAYS_INLINE
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ collector_->UpdateRoot(root);
+ }
+
+ private:
+ MarkCompact* const collector_;
+ mirror::Object* const obj_;
+ uint8_t* const begin_;
+ uint8_t* const end_;
+};
+
+bool MarkCompact::IsValidObject(mirror::Object* obj) const {
+ mirror::Class* klass = obj->GetClass<kVerifyNone, kWithoutReadBarrier>();
+ if (!heap_->GetVerification()->IsValidHeapObjectAddress(klass)) {
+ return false;
+ }
+ return heap_->GetVerification()->IsValidClassUnchecked<kWithFromSpaceBarrier>(
+ obj->GetClass<kVerifyNone, kWithFromSpaceBarrier>());
+}
+
+template <typename Callback>
+void MarkCompact::VerifyObject(mirror::Object* ref, Callback& callback) const {
+ if (kIsDebugBuild) {
+ mirror::Class* klass = ref->GetClass<kVerifyNone, kWithFromSpaceBarrier>();
+ mirror::Class* pre_compact_klass = ref->GetClass<kVerifyNone, kWithoutReadBarrier>();
+ mirror::Class* klass_klass = klass->GetClass<kVerifyNone, kWithFromSpaceBarrier>();
+ mirror::Class* klass_klass_klass = klass_klass->GetClass<kVerifyNone, kWithFromSpaceBarrier>();
+ if (bump_pointer_space_->HasAddress(pre_compact_klass) &&
+ reinterpret_cast<uint8_t*>(pre_compact_klass) < black_allocations_begin_) {
+ CHECK(moving_space_bitmap_->Test(pre_compact_klass))
+ << "ref=" << ref
+ << " post_compact_end=" << static_cast<void*>(post_compact_end_)
+ << " pre_compact_klass=" << pre_compact_klass
+ << " black_allocations_begin=" << static_cast<void*>(black_allocations_begin_);
+ CHECK(live_words_bitmap_->Test(pre_compact_klass));
+ }
+ if (!IsValidObject(ref)) {
+ std::ostringstream oss;
+ oss << "Invalid object: "
+ << "ref=" << ref
+ << " klass=" << klass
+ << " klass_klass=" << klass_klass
+ << " klass_klass_klass=" << klass_klass_klass
+ << " pre_compact_klass=" << pre_compact_klass
+ << " from_space_begin=" << static_cast<void*>(from_space_begin_)
+ << " pre_compact_begin=" << static_cast<void*>(bump_pointer_space_->Begin())
+ << " post_compact_end=" << static_cast<void*>(post_compact_end_)
+ << " black_allocations_begin=" << static_cast<void*>(black_allocations_begin_);
+
+ // Call callback before dumping larger data like RAM and space dumps.
+ callback(oss);
+
+ oss << " \nobject="
+ << heap_->GetVerification()->DumpRAMAroundAddress(reinterpret_cast<uintptr_t>(ref), 128)
+ << " \nklass(from)="
+ << heap_->GetVerification()->DumpRAMAroundAddress(reinterpret_cast<uintptr_t>(klass), 128)
+ << "spaces:\n";
+ heap_->DumpSpaces(oss);
+ LOG(FATAL) << oss.str();
+ }
+ }
+}
+
+void MarkCompact::CompactPage(mirror::Object* obj,
+ uint32_t offset,
+ uint8_t* addr,
+ bool needs_memset_zero) {
+ DCHECK(moving_space_bitmap_->Test(obj)
+ && live_words_bitmap_->Test(obj));
+ DCHECK(live_words_bitmap_->Test(offset)) << "obj=" << obj
+ << " offset=" << offset
+ << " addr=" << static_cast<void*>(addr)
+ << " black_allocs_begin="
+ << static_cast<void*>(black_allocations_begin_)
+ << " post_compact_addr="
+ << static_cast<void*>(post_compact_end_);
+ uint8_t* const start_addr = addr;
+ // How many distinct live-strides do we have.
+ size_t stride_count = 0;
+ uint8_t* last_stride = addr;
+ uint32_t last_stride_begin = 0;
+ auto verify_obj_callback = [&] (std::ostream& os) {
+ os << " stride_count=" << stride_count
+ << " last_stride=" << static_cast<void*>(last_stride)
+ << " offset=" << offset
+ << " start_addr=" << static_cast<void*>(start_addr);
+ };
+ obj = GetFromSpaceAddr(obj);
+ live_words_bitmap_->VisitLiveStrides(offset,
+ black_allocations_begin_,
+ kPageSize,
+ [&addr,
+ &last_stride,
+ &stride_count,
+ &last_stride_begin,
+ verify_obj_callback,
+ this] (uint32_t stride_begin,
+ size_t stride_size,
+ bool /*is_last*/)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ const size_t stride_in_bytes = stride_size * kAlignment;
+ DCHECK_LE(stride_in_bytes, kPageSize);
+ last_stride_begin = stride_begin;
+ DCHECK(IsAligned<kAlignment>(addr));
+ memcpy(addr,
+ from_space_begin_ + stride_begin * kAlignment,
+ stride_in_bytes);
+ if (kIsDebugBuild) {
+ uint8_t* space_begin = bump_pointer_space_->Begin();
+ // We can interpret the first word of the stride as an
+ // obj only from second stride onwards, as the first
+ // stride's first-object may have started on previous
+ // page. The only exception is the first page of the
+ // moving space.
+ if (stride_count > 0
+ || stride_begin * kAlignment < kPageSize) {
+ mirror::Object* o =
+ reinterpret_cast<mirror::Object*>(space_begin
+ + stride_begin
+ * kAlignment);
+ CHECK(live_words_bitmap_->Test(o)) << "ref=" << o;
+ CHECK(moving_space_bitmap_->Test(o))
+ << "ref=" << o
+ << " bitmap: "
+ << moving_space_bitmap_->DumpMemAround(o);
+ VerifyObject(reinterpret_cast<mirror::Object*>(addr),
+ verify_obj_callback);
+ }
+ }
+ last_stride = addr;
+ addr += stride_in_bytes;
+ stride_count++;
+ });
+ DCHECK_LT(last_stride, start_addr + kPageSize);
+ DCHECK_GT(stride_count, 0u);
+ size_t obj_size = 0;
+ uint32_t offset_within_obj = offset * kAlignment
+ - (reinterpret_cast<uint8_t*>(obj) - from_space_begin_);
+ // First object
+ if (offset_within_obj > 0) {
+ mirror::Object* to_ref = reinterpret_cast<mirror::Object*>(start_addr - offset_within_obj);
+ if (stride_count > 1) {
+ RefsUpdateVisitor</*kCheckBegin*/true, /*kCheckEnd*/false> visitor(this,
+ to_ref,
+ start_addr,
+ nullptr);
+ obj_size = obj->VisitRefsForCompaction</*kFetchObjSize*/true, /*kVisitNativeRoots*/false>(
+ visitor, MemberOffset(offset_within_obj), MemberOffset(-1));
+ } else {
+ RefsUpdateVisitor</*kCheckBegin*/true, /*kCheckEnd*/true> visitor(this,
+ to_ref,
+ start_addr,
+ start_addr + kPageSize);
+ obj_size = obj->VisitRefsForCompaction</*kFetchObjSize*/true, /*kVisitNativeRoots*/false>(
+ visitor, MemberOffset(offset_within_obj), MemberOffset(offset_within_obj
+ + kPageSize));
+ }
+ obj_size = RoundUp(obj_size, kAlignment);
+ DCHECK_GT(obj_size, offset_within_obj);
+ obj_size -= offset_within_obj;
+ // If there is only one stride, then adjust last_stride_begin to the
+ // end of the first object.
+ if (stride_count == 1) {
+ last_stride_begin += obj_size / kAlignment;
+ }
+ }
+
+ // Except for the last page being compacted, the pages will have addr ==
+ // start_addr + kPageSize.
+ uint8_t* const end_addr = addr;
+ addr = start_addr;
+ size_t bytes_done = obj_size;
+ // All strides except the last one can be updated without any boundary
+ // checks.
+ DCHECK_LE(addr, last_stride);
+ size_t bytes_to_visit = last_stride - addr;
+ DCHECK_LE(bytes_to_visit, kPageSize);
+ while (bytes_to_visit > bytes_done) {
+ mirror::Object* ref = reinterpret_cast<mirror::Object*>(addr + bytes_done);
+ VerifyObject(ref, verify_obj_callback);
+ RefsUpdateVisitor</*kCheckBegin*/false, /*kCheckEnd*/false>
+ visitor(this, ref, nullptr, nullptr);
+ obj_size = ref->VisitRefsForCompaction(visitor, MemberOffset(0), MemberOffset(-1));
+ obj_size = RoundUp(obj_size, kAlignment);
+ bytes_done += obj_size;
+ }
+ // Last stride may have multiple objects in it and we don't know where the
+ // last object which crosses the page boundary starts, therefore check
+ // page-end in all of these objects. Also, we need to call
+ // VisitRefsForCompaction() with from-space object as we fetch object size,
+ // which in case of klass requires 'class_size_'.
+ uint8_t* from_addr = from_space_begin_ + last_stride_begin * kAlignment;
+ bytes_to_visit = end_addr - addr;
+ DCHECK_LE(bytes_to_visit, kPageSize);
+ while (bytes_to_visit > bytes_done) {
+ mirror::Object* ref = reinterpret_cast<mirror::Object*>(addr + bytes_done);
+ obj = reinterpret_cast<mirror::Object*>(from_addr);
+ VerifyObject(ref, verify_obj_callback);
+ RefsUpdateVisitor</*kCheckBegin*/false, /*kCheckEnd*/true>
+ visitor(this, ref, nullptr, start_addr + kPageSize);
+ obj_size = obj->VisitRefsForCompaction(visitor,
+ MemberOffset(0),
+ MemberOffset(end_addr - (addr + bytes_done)));
+ obj_size = RoundUp(obj_size, kAlignment);
+ from_addr += obj_size;
+ bytes_done += obj_size;
+ }
+ // The last page that we compact may have some bytes left untouched in the
+ // end, we should zero them as the kernel copies at page granularity.
+ if (needs_memset_zero && UNLIKELY(bytes_done < kPageSize)) {
+ std::memset(addr + bytes_done, 0x0, kPageSize - bytes_done);
+ }
+}
+
+// We store the starting point (pre_compact_page - first_obj) and first-chunk's
+// size. If more TLAB(s) started in this page, then those chunks are identified
+// using mark bitmap. All this info is prepared in UpdateMovingSpaceBlackAllocations().
+// If we find a set bit in the bitmap, then we copy the remaining page and then
+// use the bitmap to visit each object for updating references.
+void MarkCompact::SlideBlackPage(mirror::Object* first_obj,
+ const size_t page_idx,
+ uint8_t* const pre_compact_page,
+ uint8_t* dest,
+ bool needs_memset_zero) {
+ DCHECK(IsAligned<kPageSize>(pre_compact_page));
+ size_t bytes_copied;
+ const uint32_t first_chunk_size = black_alloc_pages_first_chunk_size_[page_idx];
+ mirror::Object* next_page_first_obj = first_objs_moving_space_[page_idx + 1].AsMirrorPtr();
+ uint8_t* src_addr = reinterpret_cast<uint8_t*>(GetFromSpaceAddr(first_obj));
+ uint8_t* pre_compact_addr = reinterpret_cast<uint8_t*>(first_obj);
+ uint8_t* const pre_compact_page_end = pre_compact_page + kPageSize;
+ uint8_t* const dest_page_end = dest + kPageSize;
+
+ auto verify_obj_callback = [&] (std::ostream& os) {
+ os << " first_obj=" << first_obj
+ << " next_page_first_obj=" << next_page_first_obj
+ << " first_chunk_sie=" << first_chunk_size
+ << " dest=" << static_cast<void*>(dest)
+ << " pre_compact_page="
+ << static_cast<void* const>(pre_compact_page);
+ };
+ // We have empty portion at the beginning of the page. Zero it.
+ if (pre_compact_addr > pre_compact_page) {
+ bytes_copied = pre_compact_addr - pre_compact_page;
+ DCHECK_LT(bytes_copied, kPageSize);
+ if (needs_memset_zero) {
+ std::memset(dest, 0x0, bytes_copied);
+ }
+ dest += bytes_copied;
+ } else {
+ bytes_copied = 0;
+ size_t offset = pre_compact_page - pre_compact_addr;
+ pre_compact_addr = pre_compact_page;
+ src_addr += offset;
+ DCHECK(IsAligned<kPageSize>(src_addr));
+ }
+ // Copy the first chunk of live words
+ std::memcpy(dest, src_addr, first_chunk_size);
+ // Update references in the first chunk. Use object size to find next object.
+ {
+ size_t bytes_to_visit = first_chunk_size;
+ size_t obj_size;
+ // The first object started in some previous page. So we need to check the
+ // beginning.
+ DCHECK_LE(reinterpret_cast<uint8_t*>(first_obj), pre_compact_addr);
+ size_t offset = pre_compact_addr - reinterpret_cast<uint8_t*>(first_obj);
+ if (bytes_copied == 0 && offset > 0) {
+ mirror::Object* to_obj = reinterpret_cast<mirror::Object*>(dest - offset);
+ mirror::Object* from_obj = reinterpret_cast<mirror::Object*>(src_addr - offset);
+ // If the next page's first-obj is in this page or nullptr, then we don't
+ // need to check end boundary
+ if (next_page_first_obj == nullptr
+ || (first_obj != next_page_first_obj
+ && reinterpret_cast<uint8_t*>(next_page_first_obj) <= pre_compact_page_end)) {
+ RefsUpdateVisitor</*kCheckBegin*/true, /*kCheckEnd*/false> visitor(this,
+ to_obj,
+ dest,
+ nullptr);
+ obj_size = from_obj->VisitRefsForCompaction<
+ /*kFetchObjSize*/true, /*kVisitNativeRoots*/false>(visitor,
+ MemberOffset(offset),
+ MemberOffset(-1));
+ } else {
+ RefsUpdateVisitor</*kCheckBegin*/true, /*kCheckEnd*/true> visitor(this,
+ to_obj,
+ dest,
+ dest_page_end);
+ from_obj->VisitRefsForCompaction<
+ /*kFetchObjSize*/false, /*kVisitNativeRoots*/false>(visitor,
+ MemberOffset(offset),
+ MemberOffset(offset
+ + kPageSize));
+ return;
+ }
+ obj_size = RoundUp(obj_size, kAlignment);
+ obj_size -= offset;
+ dest += obj_size;
+ bytes_to_visit -= obj_size;
+ }
+ bytes_copied += first_chunk_size;
+ // If the last object in this page is next_page_first_obj, then we need to check end boundary
+ bool check_last_obj = false;
+ if (next_page_first_obj != nullptr
+ && reinterpret_cast<uint8_t*>(next_page_first_obj) < pre_compact_page_end
+ && bytes_copied == kPageSize) {
+ size_t diff = pre_compact_page_end - reinterpret_cast<uint8_t*>(next_page_first_obj);
+ DCHECK_LE(diff, kPageSize);
+ DCHECK_LE(diff, bytes_to_visit);
+ bytes_to_visit -= diff;
+ check_last_obj = true;
+ }
+ while (bytes_to_visit > 0) {
+ mirror::Object* dest_obj = reinterpret_cast<mirror::Object*>(dest);
+ VerifyObject(dest_obj, verify_obj_callback);
+ RefsUpdateVisitor</*kCheckBegin*/false, /*kCheckEnd*/false> visitor(this,
+ dest_obj,
+ nullptr,
+ nullptr);
+ obj_size = dest_obj->VisitRefsForCompaction(visitor, MemberOffset(0), MemberOffset(-1));
+ obj_size = RoundUp(obj_size, kAlignment);
+ bytes_to_visit -= obj_size;
+ dest += obj_size;
+ }
+ DCHECK_EQ(bytes_to_visit, 0u);
+ if (check_last_obj) {
+ mirror::Object* dest_obj = reinterpret_cast<mirror::Object*>(dest);
+ VerifyObject(dest_obj, verify_obj_callback);
+ RefsUpdateVisitor</*kCheckBegin*/false, /*kCheckEnd*/true> visitor(this,
+ dest_obj,
+ nullptr,
+ dest_page_end);
+ mirror::Object* obj = GetFromSpaceAddr(next_page_first_obj);
+ obj->VisitRefsForCompaction</*kFetchObjSize*/false>(visitor,
+ MemberOffset(0),
+ MemberOffset(dest_page_end - dest));
+ return;
+ }
+ }
+
+ // Probably a TLAB finished on this page and/or a new TLAB started as well.
+ if (bytes_copied < kPageSize) {
+ src_addr += first_chunk_size;
+ pre_compact_addr += first_chunk_size;
+ // Use mark-bitmap to identify where objects are. First call
+ // VisitMarkedRange for only the first marked bit. If found, zero all bytes
+ // until that object and then call memcpy on the rest of the page.
+ // Then call VisitMarkedRange for all marked bits *after* the one found in
+ // this invocation. This time to visit references.
+ uintptr_t start_visit = reinterpret_cast<uintptr_t>(pre_compact_addr);
+ uintptr_t page_end = reinterpret_cast<uintptr_t>(pre_compact_page_end);
+ mirror::Object* found_obj = nullptr;
+ moving_space_bitmap_->VisitMarkedRange</*kVisitOnce*/true>(start_visit,
+ page_end,
+ [&found_obj](mirror::Object* obj) {
+ found_obj = obj;
+ });
+ size_t remaining_bytes = kPageSize - bytes_copied;
+ if (found_obj == nullptr) {
+ if (needs_memset_zero) {
+ // No more black objects in this page. Zero the remaining bytes and return.
+ std::memset(dest, 0x0, remaining_bytes);
+ }
+ return;
+ }
+ // Copy everything in this page, which includes any zeroed regions
+ // in-between.
+ std::memcpy(dest, src_addr, remaining_bytes);
+ DCHECK_LT(reinterpret_cast<uintptr_t>(found_obj), page_end);
+ moving_space_bitmap_->VisitMarkedRange(
+ reinterpret_cast<uintptr_t>(found_obj) + mirror::kObjectHeaderSize,
+ page_end,
+ [&found_obj, pre_compact_addr, dest, this, verify_obj_callback] (mirror::Object* obj)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ptrdiff_t diff = reinterpret_cast<uint8_t*>(found_obj) - pre_compact_addr;
+ mirror::Object* ref = reinterpret_cast<mirror::Object*>(dest + diff);
+ VerifyObject(ref, verify_obj_callback);
+ RefsUpdateVisitor</*kCheckBegin*/false, /*kCheckEnd*/false>
+ visitor(this, ref, nullptr, nullptr);
+ ref->VisitRefsForCompaction</*kFetchObjSize*/false>(visitor,
+ MemberOffset(0),
+ MemberOffset(-1));
+ // Remember for next round.
+ found_obj = obj;
+ });
+ // found_obj may have been updated in VisitMarkedRange. Visit the last found
+ // object.
+ DCHECK_GT(reinterpret_cast<uint8_t*>(found_obj), pre_compact_addr);
+ DCHECK_LT(reinterpret_cast<uintptr_t>(found_obj), page_end);
+ ptrdiff_t diff = reinterpret_cast<uint8_t*>(found_obj) - pre_compact_addr;
+ mirror::Object* ref = reinterpret_cast<mirror::Object*>(dest + diff);
+ VerifyObject(ref, verify_obj_callback);
+ RefsUpdateVisitor</*kCheckBegin*/false, /*kCheckEnd*/true> visitor(this,
+ ref,
+ nullptr,
+ dest_page_end);
+ ref->VisitRefsForCompaction</*kFetchObjSize*/false>(
+ visitor, MemberOffset(0), MemberOffset(page_end -
+ reinterpret_cast<uintptr_t>(found_obj)));
+ }
+}
+
+template <bool kFirstPageMapping>
+void MarkCompact::MapProcessedPages(uint8_t* to_space_start,
+ Atomic<PageState>* state_arr,
+ size_t arr_idx,
+ size_t arr_len) {
+ DCHECK(minor_fault_initialized_);
+ DCHECK_LT(arr_idx, arr_len);
+ DCHECK_ALIGNED(to_space_start, kPageSize);
+ // Claim all the contiguous pages, which are ready to be mapped, and then do
+ // so in a single ioctl. This helps avoid the overhead of invoking syscall
+ // several times and also maps the already-processed pages, avoiding
+ // unnecessary faults on them.
+ size_t length = kFirstPageMapping ? kPageSize : 0;
+ if (kFirstPageMapping) {
+ arr_idx++;
+ }
+ // We need to guarantee that we don't end up sucsessfully marking a later
+ // page 'mapping' and then fail to mark an earlier page. To guarantee that
+ // we use acq_rel order.
+ for (; arr_idx < arr_len; arr_idx++, length += kPageSize) {
+ PageState expected_state = PageState::kProcessed;
+ if (!state_arr[arr_idx].compare_exchange_strong(
+ expected_state, PageState::kProcessedAndMapping, std::memory_order_acq_rel)) {
+ break;
+ }
+ }
+ if (length > 0) {
+ // Note: We need the first page to be attempted (to be mapped) by the ioctl
+ // as this function is called due to some mutator thread waiting on the
+ // 'to_space_start' page. Therefore, the ioctl must always be called
+ // with 'to_space_start' as the 'start' address because it can bail out in
+ // the middle (not attempting to map the subsequent pages) if it finds any
+ // page either already mapped in between, or missing on the shadow-map.
+ struct uffdio_continue uffd_continue;
+ uffd_continue.range.start = reinterpret_cast<uintptr_t>(to_space_start);
+ uffd_continue.range.len = length;
+ uffd_continue.mode = 0;
+ int ret = ioctl(uffd_, UFFDIO_CONTINUE, &uffd_continue);
+ if (UNLIKELY(ret == -1 && errno == EAGAIN)) {
+ // This can happen only in linear-alloc.
+ DCHECK(linear_alloc_spaces_data_.end() !=
+ std::find_if(linear_alloc_spaces_data_.begin(),
+ linear_alloc_spaces_data_.end(),
+ [to_space_start](const LinearAllocSpaceData& data) {
+ return data.begin_ <= to_space_start && to_space_start < data.end_;
+ }));
+
+ // This could happen if userfaultfd couldn't find any pages mapped in the
+ // shadow map. For instance, if there are certain (contiguous) pages on
+ // linear-alloc which are allocated and have first-object set-up but have
+ // not been accessed yet.
+ // Bail out by setting the remaining pages' state back to kProcessed and
+ // then waking up any waiting threads.
+ DCHECK_GE(uffd_continue.mapped, 0);
+ DCHECK_ALIGNED(uffd_continue.mapped, kPageSize);
+ DCHECK_LT(uffd_continue.mapped, static_cast<ssize_t>(length));
+ if (kFirstPageMapping) {
+ // In this case the first page must be mapped.
+ DCHECK_GE(uffd_continue.mapped, static_cast<ssize_t>(kPageSize));
+ }
+ // Nobody would modify these pages' state simultaneously so only atomic
+ // store is sufficient. Use 'release' order to ensure that all states are
+ // modified sequentially.
+ for (size_t remaining_len = length - uffd_continue.mapped; remaining_len > 0;
+ remaining_len -= kPageSize) {
+ arr_idx--;
+ DCHECK_EQ(state_arr[arr_idx].load(std::memory_order_relaxed),
+ PageState::kProcessedAndMapping);
+ state_arr[arr_idx].store(PageState::kProcessed, std::memory_order_release);
+ }
+ uffd_continue.range.start =
+ reinterpret_cast<uintptr_t>(to_space_start) + uffd_continue.mapped;
+ uffd_continue.range.len = length - uffd_continue.mapped;
+ ret = ioctl(uffd_, UFFDIO_WAKE, &uffd_continue.range);
+ CHECK_EQ(ret, 0) << "ioctl_userfaultfd: wake failed: " << strerror(errno);
+ } else {
+ // We may receive ENOENT if gc-thread unregisters the
+ // range behind our back, which is fine because that
+ // happens only when it knows compaction is done.
+ CHECK(ret == 0 || !kFirstPageMapping || errno == ENOENT)
+ << "ioctl_userfaultfd: continue failed: " << strerror(errno);
+ if (ret == 0) {
+ DCHECK_EQ(uffd_continue.mapped, static_cast<ssize_t>(length));
+ }
+ }
+ }
+}
+
+template <int kMode, typename CompactionFn>
+void MarkCompact::DoPageCompactionWithStateChange(size_t page_idx,
+ size_t status_arr_len,
+ uint8_t* to_space_page,
+ uint8_t* page,
+ CompactionFn func) {
+ auto copy_ioctl = [this] (void* dst, void* buffer) {
+ struct uffdio_copy uffd_copy;
+ uffd_copy.src = reinterpret_cast<uintptr_t>(buffer);
+ uffd_copy.dst = reinterpret_cast<uintptr_t>(dst);
+ uffd_copy.len = kPageSize;
+ uffd_copy.mode = 0;
+ CHECK_EQ(ioctl(uffd_, UFFDIO_COPY, &uffd_copy), 0)
+ << "ioctl_userfaultfd: copy failed: " << strerror(errno)
+ << ". src:" << buffer << " dst:" << dst;
+ DCHECK_EQ(uffd_copy.copy, static_cast<ssize_t>(kPageSize));
+ };
+ PageState expected_state = PageState::kUnprocessed;
+ PageState desired_state =
+ kMode == kCopyMode ? PageState::kProcessingAndMapping : PageState::kProcessing;
+ // In the concurrent case (kMode != kFallbackMode) we need to ensure that the update
+ // to moving_spaces_status_[page_idx] is released before the contents of the page are
+ // made accessible to other threads.
+ //
+ // In minor-fault case, we need acquire ordering here to ensure that when the
+ // CAS fails, another thread has completed processing the page, which is guaranteed
+ // by the release below.
+ // Relaxed memory-order is used in copy mode as the subsequent ioctl syscall acts as a fence.
+ std::memory_order order =
+ kMode == kCopyMode ? std::memory_order_relaxed : std::memory_order_acquire;
+ if (kMode == kFallbackMode || moving_pages_status_[page_idx].compare_exchange_strong(
+ expected_state, desired_state, order)) {
+ func();
+ if (kMode == kCopyMode) {
+ copy_ioctl(to_space_page, page);
+ } else if (kMode == kMinorFaultMode) {
+ expected_state = PageState::kProcessing;
+ desired_state = PageState::kProcessed;
+ // the CAS needs to be with release order to ensure that stores to the
+ // page makes it to memory *before* other threads observe that it's
+ // ready to be mapped.
+ if (!moving_pages_status_[page_idx].compare_exchange_strong(
+ expected_state, desired_state, std::memory_order_release)) {
+ // Some mutator has requested to map the page after processing it.
+ DCHECK_EQ(expected_state, PageState::kProcessingAndMapping);
+ MapProcessedPages</*kFirstPageMapping=*/true>(
+ to_space_page, moving_pages_status_, page_idx, status_arr_len);
+ }
+ }
+ } else {
+ DCHECK_GT(expected_state, PageState::kProcessed);
+ }
+}
+
+void MarkCompact::FreeFromSpacePages(size_t cur_page_idx) {
+ // Thanks to sliding compaction, bump-pointer allocations, and reverse
+ // compaction (see CompactMovingSpace) the logic here is pretty simple: find
+ // the to-space page up to which compaction has finished, all the from-space
+ // pages corresponding to this onwards can be freed. There are some corner
+ // cases to be taken care of, which are described below.
+ size_t idx = last_checked_reclaim_page_idx_;
+ // Find the to-space page up to which the corresponding from-space pages can be
+ // freed.
+ for (; idx > cur_page_idx; idx--) {
+ PageState state = moving_pages_status_[idx - 1].load(std::memory_order_acquire);
+ if (state == PageState::kMutatorProcessing) {
+ // Some mutator is working on the page.
+ break;
+ }
+ DCHECK(state >= PageState::kProcessed ||
+ (state == PageState::kUnprocessed && idx > moving_first_objs_count_));
+ }
+
+ uint8_t* reclaim_begin;
+ uint8_t* idx_addr;
+ // Calculate the first from-space page to be freed using 'idx'. If the
+ // first-object of the idx'th to-space page started before the corresponding
+ // from-space page, which is almost always the case in the compaction portion
+ // of the moving-space, then it indicates that the subsequent pages that are
+ // yet to be compacted will need the from-space pages. Therefore, find the page
+ // (from the already compacted pages) whose first-object is different from
+ // ours. All the from-space pages starting from that one are safe to be
+ // removed. Please note that this iteration is not expected to be long in
+ // normal cases as objects are smaller than page size.
+ if (idx >= moving_first_objs_count_) {
+ // black-allocated portion of the moving-space
+ idx_addr = black_allocations_begin_ + (idx - moving_first_objs_count_) * kPageSize;
+ reclaim_begin = idx_addr;
+ mirror::Object* first_obj = first_objs_moving_space_[idx].AsMirrorPtr();
+ if (first_obj != nullptr && reinterpret_cast<uint8_t*>(first_obj) < reclaim_begin) {
+ size_t idx_len = moving_first_objs_count_ + black_page_count_;
+ for (size_t i = idx + 1; i < idx_len; i++) {
+ mirror::Object* obj = first_objs_moving_space_[i].AsMirrorPtr();
+ // A null first-object indicates that the corresponding to-space page is
+ // not used yet. So we can compute its from-space page and use that.
+ if (obj != first_obj) {
+ reclaim_begin = obj != nullptr
+ ? AlignUp(reinterpret_cast<uint8_t*>(obj), kPageSize)
+ : (black_allocations_begin_ + (i - moving_first_objs_count_) * kPageSize);
+ break;
+ }
+ }
+ }
+ } else {
+ DCHECK_GE(pre_compact_offset_moving_space_[idx], 0u);
+ idx_addr = bump_pointer_space_->Begin() + pre_compact_offset_moving_space_[idx] * kAlignment;
+ reclaim_begin = idx_addr;
+ DCHECK_LE(reclaim_begin, black_allocations_begin_);
+ mirror::Object* first_obj = first_objs_moving_space_[idx].AsMirrorPtr();
+ if (reinterpret_cast<uint8_t*>(first_obj) < reclaim_begin) {
+ DCHECK_LT(idx, moving_first_objs_count_);
+ mirror::Object* obj = first_obj;
+ for (size_t i = idx + 1; i < moving_first_objs_count_; i++) {
+ obj = first_objs_moving_space_[i].AsMirrorPtr();
+ if (first_obj != obj) {
+ DCHECK_LT(first_obj, obj);
+ DCHECK_LT(reclaim_begin, reinterpret_cast<uint8_t*>(obj));
+ reclaim_begin = reinterpret_cast<uint8_t*>(obj);
+ break;
+ }
+ }
+ if (obj == first_obj) {
+ reclaim_begin = black_allocations_begin_;
+ }
+ }
+ reclaim_begin = AlignUp(reclaim_begin, kPageSize);
+ }
+
+ DCHECK_NE(reclaim_begin, nullptr);
+ DCHECK_ALIGNED(reclaim_begin, kPageSize);
+ DCHECK_ALIGNED(last_reclaimed_page_, kPageSize);
+ // Check if the 'class_after_obj_map_' map allows pages to be freed.
+ for (; class_after_obj_iter_ != class_after_obj_ordered_map_.rend(); class_after_obj_iter_++) {
+ mirror::Object* klass = class_after_obj_iter_->first.AsMirrorPtr();
+ mirror::Class* from_klass = static_cast<mirror::Class*>(GetFromSpaceAddr(klass));
+ // Check with class' end to ensure that, if required, the entire class survives.
+ uint8_t* klass_end = reinterpret_cast<uint8_t*>(klass) + from_klass->SizeOf<kVerifyNone>();
+ DCHECK_LE(klass_end, last_reclaimed_page_);
+ if (reinterpret_cast<uint8_t*>(klass_end) >= reclaim_begin) {
+ // Found a class which is in the reclaim range.
+ uint8_t* obj_addr = reinterpret_cast<uint8_t*>(class_after_obj_iter_->second.AsMirrorPtr());
+ // NOTE: Don't assert that obj is of 'klass' type as klass could instead
+ // be its super-class.
+ if (obj_addr < idx_addr) {
+ // Its lowest-address object is not compacted yet. Reclaim starting from
+ // the end of this class.
+ reclaim_begin = AlignUp(klass_end, kPageSize);
+ } else {
+ // Continue consuming pairs wherein the lowest address object has already
+ // been compacted.
+ continue;
+ }
+ }
+ // All the remaining class (and thereby corresponding object) addresses are
+ // lower than the reclaim range.
+ break;
+ }
+
+ ssize_t size = last_reclaimed_page_ - reclaim_begin;
+ if (size >= kMinFromSpaceMadviseSize) {
+ int behavior = minor_fault_initialized_ ? MADV_REMOVE : MADV_DONTNEED;
+ CHECK_EQ(madvise(reclaim_begin + from_space_slide_diff_, size, behavior), 0)
+ << "madvise of from-space failed: " << strerror(errno);
+ last_reclaimed_page_ = reclaim_begin;
+ }
+ last_checked_reclaim_page_idx_ = idx;
+}
+
+void MarkCompact::UpdateClassAfterObjMap() {
+ CHECK(class_after_obj_ordered_map_.empty());
+ for (const auto& pair : class_after_obj_hash_map_) {
+ auto super_class_iter = super_class_after_class_hash_map_.find(pair.first);
+ ObjReference key = super_class_iter != super_class_after_class_hash_map_.end()
+ ? super_class_iter->second
+ : pair.first;
+ if (std::less<mirror::Object*>{}(pair.second.AsMirrorPtr(), key.AsMirrorPtr()) &&
+ bump_pointer_space_->HasAddress(key.AsMirrorPtr())) {
+ auto [ret_iter, success] = class_after_obj_ordered_map_.try_emplace(key, pair.second);
+ // It could fail only if the class 'key' has objects of its own, which are lower in
+ // address order, as well of some of its derived class. In this case
+ // choose the lowest address object.
+ if (!success &&
+ std::less<mirror::Object*>{}(pair.second.AsMirrorPtr(), ret_iter->second.AsMirrorPtr())) {
+ ret_iter->second = pair.second;
+ }
+ }
+ }
+ class_after_obj_hash_map_.clear();
+ super_class_after_class_hash_map_.clear();
+}
+
+template <int kMode>
+void MarkCompact::CompactMovingSpace(uint8_t* page) {
+ // For every page we have a starting object, which may have started in some
+ // preceding page, and an offset within that object from where we must start
+ // copying.
+ // Consult the live-words bitmap to copy all contiguously live words at a
+ // time. These words may constitute multiple objects. To avoid the need for
+ // consulting mark-bitmap to find where does the next live object start, we
+ // use the object-size returned by VisitRefsForCompaction.
+ //
+ // We do the compaction in reverse direction so that the pages containing
+ // TLAB and latest allocations are processed first.
+ TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
+ size_t page_status_arr_len = moving_first_objs_count_ + black_page_count_;
+ size_t idx = page_status_arr_len;
+ uint8_t* to_space_end = bump_pointer_space_->Begin() + page_status_arr_len * kPageSize;
+ uint8_t* shadow_space_end = nullptr;
+ if (kMode == kMinorFaultMode) {
+ shadow_space_end = shadow_to_space_map_.Begin() + page_status_arr_len * kPageSize;
+ }
+ uint8_t* pre_compact_page = black_allocations_begin_ + (black_page_count_ * kPageSize);
+
+ DCHECK(IsAligned<kPageSize>(pre_compact_page));
+
+ UpdateClassAfterObjMap();
+ // These variables are maintained by FreeFromSpacePages().
+ last_reclaimed_page_ = pre_compact_page;
+ last_checked_reclaim_page_idx_ = idx;
+ class_after_obj_iter_ = class_after_obj_ordered_map_.rbegin();
+ // Allocated-black pages
+ while (idx > moving_first_objs_count_) {
+ idx--;
+ pre_compact_page -= kPageSize;
+ to_space_end -= kPageSize;
+ if (kMode == kMinorFaultMode) {
+ shadow_space_end -= kPageSize;
+ page = shadow_space_end;
+ } else if (kMode == kFallbackMode) {
+ page = to_space_end;
+ }
+ mirror::Object* first_obj = first_objs_moving_space_[idx].AsMirrorPtr();
+ if (first_obj != nullptr) {
+ DoPageCompactionWithStateChange<kMode>(
+ idx,
+ page_status_arr_len,
+ to_space_end,
+ page,
+ [&]() REQUIRES_SHARED(Locks::mutator_lock_) {
+ SlideBlackPage(first_obj, idx, pre_compact_page, page, kMode == kCopyMode);
+ });
+ // We are sliding here, so no point attempting to madvise for every
+ // page. Wait for enough pages to be done.
+ if (idx % (kMinFromSpaceMadviseSize / kPageSize) == 0) {
+ FreeFromSpacePages(idx);
+ }
+ }
+ }
+ DCHECK_EQ(pre_compact_page, black_allocations_begin_);
+
+ while (idx > 0) {
+ idx--;
+ to_space_end -= kPageSize;
+ if (kMode == kMinorFaultMode) {
+ shadow_space_end -= kPageSize;
+ page = shadow_space_end;
+ } else if (kMode == kFallbackMode) {
+ page = to_space_end;
+ }
+ mirror::Object* first_obj = first_objs_moving_space_[idx].AsMirrorPtr();
+ DoPageCompactionWithStateChange<kMode>(
+ idx, page_status_arr_len, to_space_end, page, [&]() REQUIRES_SHARED(Locks::mutator_lock_) {
+ CompactPage(first_obj, pre_compact_offset_moving_space_[idx], page, kMode == kCopyMode);
+ });
+ FreeFromSpacePages(idx);
+ }
+ DCHECK_EQ(to_space_end, bump_pointer_space_->Begin());
+}
+
+void MarkCompact::UpdateNonMovingPage(mirror::Object* first, uint8_t* page) {
+ DCHECK_LT(reinterpret_cast<uint8_t*>(first), page + kPageSize);
+ // For every object found in the page, visit the previous object. This ensures
+ // that we can visit without checking page-end boundary.
+ // Call VisitRefsForCompaction with from-space read-barrier as the klass object and
+ // super-class loads require it.
+ // TODO: Set kVisitNativeRoots to false once we implement concurrent
+ // compaction
+ mirror::Object* curr_obj = first;
+ non_moving_space_bitmap_->VisitMarkedRange(
+ reinterpret_cast<uintptr_t>(first) + mirror::kObjectHeaderSize,
+ reinterpret_cast<uintptr_t>(page + kPageSize),
+ [&](mirror::Object* next_obj) {
+ // TODO: Once non-moving space update becomes concurrent, we'll
+ // require fetching the from-space address of 'curr_obj' and then call
+ // visitor on that.
+ if (reinterpret_cast<uint8_t*>(curr_obj) < page) {
+ RefsUpdateVisitor</*kCheckBegin*/true, /*kCheckEnd*/false>
+ visitor(this, curr_obj, page, page + kPageSize);
+ MemberOffset begin_offset(page - reinterpret_cast<uint8_t*>(curr_obj));
+ // Native roots shouldn't be visited as they are done when this
+ // object's beginning was visited in the preceding page.
+ curr_obj->VisitRefsForCompaction</*kFetchObjSize*/false, /*kVisitNativeRoots*/false>(
+ visitor, begin_offset, MemberOffset(-1));
+ } else {
+ RefsUpdateVisitor</*kCheckBegin*/false, /*kCheckEnd*/false>
+ visitor(this, curr_obj, page, page + kPageSize);
+ curr_obj->VisitRefsForCompaction</*kFetchObjSize*/false>(visitor,
+ MemberOffset(0),
+ MemberOffset(-1));
+ }
+ curr_obj = next_obj;
+ });
+
+ MemberOffset end_offset(page + kPageSize - reinterpret_cast<uint8_t*>(curr_obj));
+ if (reinterpret_cast<uint8_t*>(curr_obj) < page) {
+ RefsUpdateVisitor</*kCheckBegin*/true, /*kCheckEnd*/true>
+ visitor(this, curr_obj, page, page + kPageSize);
+ curr_obj->VisitRefsForCompaction</*kFetchObjSize*/false, /*kVisitNativeRoots*/false>(
+ visitor, MemberOffset(page - reinterpret_cast<uint8_t*>(curr_obj)), end_offset);
+ } else {
+ RefsUpdateVisitor</*kCheckBegin*/false, /*kCheckEnd*/true>
+ visitor(this, curr_obj, page, page + kPageSize);
+ curr_obj->VisitRefsForCompaction</*kFetchObjSize*/false>(visitor, MemberOffset(0), end_offset);
+ }
+}
+
+void MarkCompact::UpdateNonMovingSpace() {
+ TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
+ // Iterating in reverse ensures that the class pointer in objects which span
+ // across more than one page gets updated in the end. This is necessary for
+ // VisitRefsForCompaction() to work correctly.
+ // TODO: If and when we make non-moving space update concurrent, implement a
+ // mechanism to remember class pointers for such objects off-heap and pass it
+ // to VisitRefsForCompaction().
+ uint8_t* page = non_moving_space_->Begin() + non_moving_first_objs_count_ * kPageSize;
+ for (ssize_t i = non_moving_first_objs_count_ - 1; i >= 0; i--) {
+ mirror::Object* obj = first_objs_non_moving_space_[i].AsMirrorPtr();
+ page -= kPageSize;
+ // null means there are no objects on the page to update references.
+ if (obj != nullptr) {
+ UpdateNonMovingPage(obj, page);
+ }
+ }
+}
+
+void MarkCompact::UpdateMovingSpaceBlackAllocations() {
+ // For sliding black pages, we need the first-object, which overlaps with the
+ // first byte of the page. Additionally, we compute the size of first chunk of
+ // black objects. This will suffice for most black pages. Unlike, compaction
+ // pages, here we don't need to pre-compute the offset within first-obj from
+ // where sliding has to start. That can be calculated using the pre-compact
+ // address of the page. Therefore, to save space, we store the first chunk's
+ // size in black_alloc_pages_first_chunk_size_ array.
+ // For the pages which may have holes after the first chunk, which could happen
+ // if a new TLAB starts in the middle of the page, we mark the objects in
+ // the mark-bitmap. So, if the first-chunk size is smaller than kPageSize,
+ // then we use the mark-bitmap for the remainder of the page.
+ uint8_t* const begin = bump_pointer_space_->Begin();
+ uint8_t* black_allocs = black_allocations_begin_;
+ DCHECK_LE(begin, black_allocs);
+ size_t consumed_blocks_count = 0;
+ size_t first_block_size;
+ // Get the list of all blocks allocated in the bump-pointer space.
+ std::vector<size_t>* block_sizes = bump_pointer_space_->GetBlockSizes(thread_running_gc_,
+ &first_block_size);
+ DCHECK_LE(first_block_size, (size_t)(black_allocs - begin));
+ if (block_sizes != nullptr) {
+ size_t black_page_idx = moving_first_objs_count_;
+ uint8_t* block_end = begin + first_block_size;
+ uint32_t remaining_chunk_size = 0;
+ uint32_t first_chunk_size = 0;
+ mirror::Object* first_obj = nullptr;
+ for (size_t block_size : *block_sizes) {
+ block_end += block_size;
+ // Skip the blocks that are prior to the black allocations. These will be
+ // merged with the main-block later.
+ if (black_allocs >= block_end) {
+ consumed_blocks_count++;
+ continue;
+ }
+ mirror::Object* obj = reinterpret_cast<mirror::Object*>(black_allocs);
+ bool set_mark_bit = remaining_chunk_size > 0;
+ // We don't know how many objects are allocated in the current block. When we hit
+ // a null assume it's the end. This works as every block is expected to
+ // have objects allocated linearly using bump-pointer.
+ // BumpPointerSpace::Walk() also works similarly.
+ while (black_allocs < block_end
+ && obj->GetClass<kDefaultVerifyFlags, kWithoutReadBarrier>() != nullptr) {
+ if (first_obj == nullptr) {
+ first_obj = obj;
+ }
+ // We only need the mark-bitmap in the pages wherein a new TLAB starts in
+ // the middle of the page.
+ if (set_mark_bit) {
+ moving_space_bitmap_->Set(obj);
+ }
+ UpdateClassAfterObjectMap(obj);
+ size_t obj_size = RoundUp(obj->SizeOf(), kAlignment);
+ // Handle objects which cross page boundary, including objects larger
+ // than page size.
+ if (remaining_chunk_size + obj_size >= kPageSize) {
+ set_mark_bit = false;
+ first_chunk_size += kPageSize - remaining_chunk_size;
+ remaining_chunk_size += obj_size;
+ // We should not store first-object and remaining_chunk_size if there were
+ // unused bytes before this TLAB, in which case we must have already
+ // stored the values (below).
+ if (black_alloc_pages_first_chunk_size_[black_page_idx] == 0) {
+ black_alloc_pages_first_chunk_size_[black_page_idx] = first_chunk_size;
+ first_objs_moving_space_[black_page_idx].Assign(first_obj);
+ }
+ black_page_idx++;
+ remaining_chunk_size -= kPageSize;
+ // Consume an object larger than page size.
+ while (remaining_chunk_size >= kPageSize) {
+ black_alloc_pages_first_chunk_size_[black_page_idx] = kPageSize;
+ first_objs_moving_space_[black_page_idx].Assign(obj);
+ black_page_idx++;
+ remaining_chunk_size -= kPageSize;
+ }
+ first_obj = remaining_chunk_size > 0 ? obj : nullptr;
+ first_chunk_size = remaining_chunk_size;
+ } else {
+ DCHECK_LE(first_chunk_size, remaining_chunk_size);
+ first_chunk_size += obj_size;
+ remaining_chunk_size += obj_size;
+ }
+ black_allocs += obj_size;
+ obj = reinterpret_cast<mirror::Object*>(black_allocs);
+ }
+ DCHECK_LE(black_allocs, block_end);
+ DCHECK_LT(remaining_chunk_size, kPageSize);
+ // consume the unallocated portion of the block
+ if (black_allocs < block_end) {
+ // first-chunk of the current page ends here. Store it.
+ if (first_chunk_size > 0) {
+ black_alloc_pages_first_chunk_size_[black_page_idx] = first_chunk_size;
+ first_objs_moving_space_[black_page_idx].Assign(first_obj);
+ first_chunk_size = 0;
+ }
+ first_obj = nullptr;
+ size_t page_remaining = kPageSize - remaining_chunk_size;
+ size_t block_remaining = block_end - black_allocs;
+ if (page_remaining <= block_remaining) {
+ block_remaining -= page_remaining;
+ // current page and the subsequent empty pages in the block
+ black_page_idx += 1 + block_remaining / kPageSize;
+ remaining_chunk_size = block_remaining % kPageSize;
+ } else {
+ remaining_chunk_size += block_remaining;
+ }
+ black_allocs = block_end;
+ }
+ }
+ black_page_count_ = black_page_idx - moving_first_objs_count_;
+ delete block_sizes;
+ }
+ // Update bump-pointer space by consuming all the pre-black blocks into the
+ // main one.
+ bump_pointer_space_->SetBlockSizes(thread_running_gc_,
+ post_compact_end_ - begin,
+ consumed_blocks_count);
+}
+
+void MarkCompact::UpdateNonMovingSpaceBlackAllocations() {
+ accounting::ObjectStack* stack = heap_->GetAllocationStack();
+ const StackReference<mirror::Object>* limit = stack->End();
+ uint8_t* const space_begin = non_moving_space_->Begin();
+ for (StackReference<mirror::Object>* it = stack->Begin(); it != limit; ++it) {
+ mirror::Object* obj = it->AsMirrorPtr();
+ if (obj != nullptr && non_moving_space_bitmap_->HasAddress(obj)) {
+ non_moving_space_bitmap_->Set(obj);
+ // Clear so that we don't try to set the bit again in the next GC-cycle.
+ it->Clear();
+ size_t idx = (reinterpret_cast<uint8_t*>(obj) - space_begin) / kPageSize;
+ uint8_t* page_begin = AlignDown(reinterpret_cast<uint8_t*>(obj), kPageSize);
+ mirror::Object* first_obj = first_objs_non_moving_space_[idx].AsMirrorPtr();
+ if (first_obj == nullptr
+ || (obj < first_obj && reinterpret_cast<uint8_t*>(first_obj) > page_begin)) {
+ first_objs_non_moving_space_[idx].Assign(obj);
+ }
+ mirror::Object* next_page_first_obj = first_objs_non_moving_space_[++idx].AsMirrorPtr();
+ uint8_t* next_page_begin = page_begin + kPageSize;
+ if (next_page_first_obj == nullptr
+ || reinterpret_cast<uint8_t*>(next_page_first_obj) > next_page_begin) {
+ size_t obj_size = RoundUp(obj->SizeOf<kDefaultVerifyFlags>(), kAlignment);
+ uint8_t* obj_end = reinterpret_cast<uint8_t*>(obj) + obj_size;
+ while (next_page_begin < obj_end) {
+ first_objs_non_moving_space_[idx++].Assign(obj);
+ next_page_begin += kPageSize;
+ }
+ }
+ // update first_objs count in case we went past non_moving_first_objs_count_
+ non_moving_first_objs_count_ = std::max(non_moving_first_objs_count_, idx);
+ }
+ }
+}
+
+class MarkCompact::ImmuneSpaceUpdateObjVisitor {
+ public:
+ ImmuneSpaceUpdateObjVisitor(MarkCompact* collector, bool visit_native_roots)
+ : collector_(collector), visit_native_roots_(visit_native_roots) {}
+
+ ALWAYS_INLINE void operator()(mirror::Object* obj) const REQUIRES(Locks::mutator_lock_) {
+ RefsUpdateVisitor</*kCheckBegin*/false, /*kCheckEnd*/false> visitor(collector_,
+ obj,
+ /*begin_*/nullptr,
+ /*end_*/nullptr);
+ if (visit_native_roots_) {
+ obj->VisitRefsForCompaction</*kFetchObjSize*/ false, /*kVisitNativeRoots*/ true>(
+ visitor, MemberOffset(0), MemberOffset(-1));
+ } else {
+ obj->VisitRefsForCompaction</*kFetchObjSize*/ false>(
+ visitor, MemberOffset(0), MemberOffset(-1));
+ }
+ }
+
+ static void Callback(mirror::Object* obj, void* arg) REQUIRES(Locks::mutator_lock_) {
+ reinterpret_cast<ImmuneSpaceUpdateObjVisitor*>(arg)->operator()(obj);
+ }
+
+ private:
+ MarkCompact* const collector_;
+ const bool visit_native_roots_;
+};
+
+class MarkCompact::ClassLoaderRootsUpdater : public ClassLoaderVisitor {
+ public:
+ explicit ClassLoaderRootsUpdater(MarkCompact* collector) : collector_(collector) {}
+
+ void Visit(ObjPtr<mirror::ClassLoader> class_loader) override
+ REQUIRES_SHARED(Locks::classlinker_classes_lock_, Locks::mutator_lock_) {
+ ClassTable* const class_table = class_loader->GetClassTable();
+ if (class_table != nullptr) {
+ class_table->VisitRoots(*this);
+ }
+ }
+
+ void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+ REQUIRES(Locks::heap_bitmap_lock_) REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!root->IsNull()) {
+ VisitRoot(root);
+ }
+ }
+
+ void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+ REQUIRES(Locks::heap_bitmap_lock_) REQUIRES_SHARED(Locks::mutator_lock_) {
+ collector_->VisitRoots(&root, 1, RootInfo(RootType::kRootVMInternal));
+ }
+
+ private:
+ MarkCompact* collector_;
+};
+
+class MarkCompact::LinearAllocPageUpdater {
+ public:
+ explicit LinearAllocPageUpdater(MarkCompact* collector) : collector_(collector) {}
+
+ void operator()(uint8_t* page_begin, uint8_t* first_obj) const ALWAYS_INLINE
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK_ALIGNED(page_begin, kPageSize);
+ uint8_t* page_end = page_begin + kPageSize;
+ uint32_t obj_size;
+ for (uint8_t* byte = first_obj; byte < page_end;) {
+ TrackingHeader* header = reinterpret_cast<TrackingHeader*>(byte);
+ obj_size = header->GetSize();
+ if (UNLIKELY(obj_size == 0)) {
+ // No more objects in this page to visit.
+ break;
+ }
+ uint8_t* obj = byte + sizeof(TrackingHeader);
+ uint8_t* obj_end = byte + obj_size;
+ if (header->Is16Aligned()) {
+ obj = AlignUp(obj, 16);
+ }
+ uint8_t* begin_boundary = std::max(obj, page_begin);
+ uint8_t* end_boundary = std::min(obj_end, page_end);
+ if (begin_boundary < end_boundary) {
+ VisitObject(header->GetKind(), obj, begin_boundary, end_boundary);
+ }
+ if (ArenaAllocator::IsRunningOnMemoryTool()) {
+ obj_size += ArenaAllocator::kMemoryToolRedZoneBytes;
+ }
+ byte += RoundUp(obj_size, LinearAlloc::kAlignment);
+ }
+ }
+
+ void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+ ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!root->IsNull()) {
+ VisitRoot(root);
+ }
+ }
+
+ void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+ ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
+ mirror::Object* old_ref = root->AsMirrorPtr();
+ DCHECK_NE(old_ref, nullptr);
+ if (collector_->live_words_bitmap_->HasAddress(old_ref)) {
+ mirror::Object* new_ref = old_ref;
+ if (reinterpret_cast<uint8_t*>(old_ref) >= collector_->black_allocations_begin_) {
+ new_ref = collector_->PostCompactBlackObjAddr(old_ref);
+ } else if (collector_->live_words_bitmap_->Test(old_ref)) {
+ DCHECK(collector_->moving_space_bitmap_->Test(old_ref)) << old_ref;
+ new_ref = collector_->PostCompactOldObjAddr(old_ref);
+ }
+ if (old_ref != new_ref) {
+ root->Assign(new_ref);
+ }
+ }
+ }
+
+ private:
+ void VisitObject(LinearAllocKind kind,
+ void* obj,
+ uint8_t* start_boundary,
+ uint8_t* end_boundary) const REQUIRES_SHARED(Locks::mutator_lock_) {
+ switch (kind) {
+ case LinearAllocKind::kNoGCRoots:
+ break;
+ case LinearAllocKind::kGCRootArray:
+ {
+ GcRoot<mirror::Object>* root = reinterpret_cast<GcRoot<mirror::Object>*>(start_boundary);
+ GcRoot<mirror::Object>* last = reinterpret_cast<GcRoot<mirror::Object>*>(end_boundary);
+ for (; root < last; root++) {
+ VisitRootIfNonNull(root->AddressWithoutBarrier());
+ }
+ }
+ break;
+ case LinearAllocKind::kArtMethodArray:
+ {
+ LengthPrefixedArray<ArtMethod>* array = static_cast<LengthPrefixedArray<ArtMethod>*>(obj);
+ // Old methods are clobbered in debug builds. Check size to confirm if the array
+ // has any GC roots to visit. See ClassLinker::LinkMethodsHelper::ClobberOldMethods()
+ if (array->size() > 0) {
+ if (collector_->pointer_size_ == PointerSize::k64) {
+ ArtMethod::VisitArrayRoots<PointerSize::k64>(
+ *this, start_boundary, end_boundary, array);
+ } else {
+ DCHECK_EQ(collector_->pointer_size_, PointerSize::k32);
+ ArtMethod::VisitArrayRoots<PointerSize::k32>(
+ *this, start_boundary, end_boundary, array);
+ }
+ }
+ }
+ break;
+ case LinearAllocKind::kArtMethod:
+ ArtMethod::VisitRoots(*this, start_boundary, end_boundary, static_cast<ArtMethod*>(obj));
+ break;
+ case LinearAllocKind::kArtFieldArray:
+ ArtField::VisitArrayRoots(*this,
+ start_boundary,
+ end_boundary,
+ static_cast<LengthPrefixedArray<ArtField>*>(obj));
+ break;
+ case LinearAllocKind::kDexCacheArray:
+ {
+ mirror::DexCachePair<mirror::Object>* first =
+ reinterpret_cast<mirror::DexCachePair<mirror::Object>*>(start_boundary);
+ mirror::DexCachePair<mirror::Object>* last =
+ reinterpret_cast<mirror::DexCachePair<mirror::Object>*>(end_boundary);
+ mirror::DexCache::VisitDexCachePairRoots(*this, first, last);
+ }
+ }
+ }
+
+ MarkCompact* const collector_;
+};
+
+void MarkCompact::PreCompactionPhase() {
+ TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
+ Runtime* runtime = Runtime::Current();
+ non_moving_space_bitmap_ = non_moving_space_->GetLiveBitmap();
+ if (kIsDebugBuild) {
+ DCHECK_EQ(thread_running_gc_, Thread::Current());
+ stack_low_addr_ = thread_running_gc_->GetStackEnd();
+ stack_high_addr_ =
+ reinterpret_cast<char*>(stack_low_addr_) + thread_running_gc_->GetStackSize();
+ }
+
+ compacting_ = true;
+
+ {
+ TimingLogger::ScopedTiming t2("(Paused)UpdateCompactionDataStructures", GetTimings());
+ ReaderMutexLock rmu(thread_running_gc_, *Locks::heap_bitmap_lock_);
+ // Refresh data-structures to catch-up on allocations that may have
+ // happened since marking-phase pause.
+ // There could be several TLABs that got allocated since marking pause. We
+ // don't want to compact them and instead update the TLAB info in TLS and
+ // let mutators continue to use the TLABs.
+ // We need to set all the bits in live-words bitmap corresponding to allocated
+ // objects. Also, we need to find the objects that are overlapping with
+ // page-begin boundaries. Unlike objects allocated before
+ // black_allocations_begin_, which can be identified via mark-bitmap, we can get
+ // this info only via walking the space past black_allocations_begin_, which
+ // involves fetching object size.
+ // TODO: We can reduce the time spent on this in a pause by performing one
+ // round of this concurrently prior to the pause.
+ UpdateMovingSpaceBlackAllocations();
+ // TODO: If we want to avoid this allocation in a pause then we will have to
+ // allocate an array for the entire moving-space size, which can be made
+ // part of info_map_.
+ moving_pages_status_ = new Atomic<PageState>[moving_first_objs_count_ + black_page_count_];
+ if (kIsDebugBuild) {
+ size_t len = moving_first_objs_count_ + black_page_count_;
+ for (size_t i = 0; i < len; i++) {
+ CHECK_EQ(moving_pages_status_[i].load(std::memory_order_relaxed),
+ PageState::kUnprocessed);
+ }
+ }
+ // Iterate over the allocation_stack_, for every object in the non-moving
+ // space:
+ // 1. Mark the object in live bitmap
+ // 2. Erase the object from allocation stack
+ // 3. In the corresponding page, if the first-object vector needs updating
+ // then do so.
+ UpdateNonMovingSpaceBlackAllocations();
+
+ heap_->GetReferenceProcessor()->UpdateRoots(this);
+ }
+
+ {
+ // Thread roots must be updated first (before space mremap and native root
+ // updation) to ensure that pre-update content is accessible.
+ TimingLogger::ScopedTiming t2("(Paused)UpdateThreadRoots", GetTimings());
+ MutexLock mu1(thread_running_gc_, *Locks::runtime_shutdown_lock_);
+ MutexLock mu2(thread_running_gc_, *Locks::thread_list_lock_);
+ std::list<Thread*> thread_list = runtime->GetThreadList()->GetList();
+ for (Thread* thread : thread_list) {
+ thread->VisitRoots(this, kVisitRootFlagAllRoots);
+ // Interpreter cache is thread-local so it needs to be swept either in a
+ // checkpoint, or a stop-the-world pause.
+ thread->SweepInterpreterCache(this);
+ thread->AdjustTlab(black_objs_slide_diff_);
+ }
+ }
+ {
+ TimingLogger::ScopedTiming t2("(Paused)UpdateClassLoaderRoots", GetTimings());
+ ReaderMutexLock rmu(thread_running_gc_, *Locks::classlinker_classes_lock_);
+ {
+ ClassLoaderRootsUpdater updater(this);
+ runtime->GetClassLinker()->VisitClassLoaders(&updater);
+ }
+ }
+
+ bool has_zygote_space = heap_->HasZygoteSpace();
+ // TODO: Find out why it's not sufficient to visit native roots of immune
+ // spaces, and why all the pre-zygote fork arenas have to be linearly updated.
+ // Is it possible that some native root starts getting pointed to by some object
+ // in moving space after fork? Or are we missing a write-barrier somewhere
+ // when a native root is updated?
+ GcVisitedArenaPool* arena_pool =
+ static_cast<GcVisitedArenaPool*>(runtime->GetLinearAllocArenaPool());
+ if (uffd_ == kFallbackMode || (!has_zygote_space && runtime->IsZygote())) {
+ // Besides fallback-mode, visit linear-alloc space in the pause for zygote
+ // processes prior to first fork (that's when zygote space gets created).
+ if (kIsDebugBuild && IsValidFd(uffd_)) {
+ // All arenas allocated so far are expected to be pre-zygote fork.
+ arena_pool->ForEachAllocatedArena(
+ [](const TrackedArena& arena)
+ REQUIRES_SHARED(Locks::mutator_lock_) { CHECK(arena.IsPreZygoteForkArena()); });
+ }
+ LinearAllocPageUpdater updater(this);
+ arena_pool->VisitRoots(updater);
+ } else {
+ arena_pool->ForEachAllocatedArena(
+ [this](const TrackedArena& arena) REQUIRES_SHARED(Locks::mutator_lock_) {
+ // The pre-zygote fork arenas are not visited concurrently in the
+ // zygote children processes. The native roots of the dirty objects
+ // are visited during immune space visit below.
+ if (!arena.IsPreZygoteForkArena()) {
+ uint8_t* last_byte = arena.GetLastUsedByte();
+ CHECK(linear_alloc_arenas_.insert({&arena, last_byte}).second);
+ } else {
+ LinearAllocPageUpdater updater(this);
+ arena.VisitRoots(updater);
+ }
+ });
+ }
+
+ SweepSystemWeaks(thread_running_gc_, runtime, /*paused*/ true);
+
+ {
+ TimingLogger::ScopedTiming t2("(Paused)UpdateConcurrentRoots", GetTimings());
+ runtime->VisitConcurrentRoots(this, kVisitRootFlagAllRoots);
+ }
+ {
+ // TODO: don't visit the transaction roots if it's not active.
+ TimingLogger::ScopedTiming t2("(Paused)UpdateNonThreadRoots", GetTimings());
+ runtime->VisitNonThreadRoots(this);
+ }
+
+ {
+ // TODO: Immune space updation has to happen either before or after
+ // remapping pre-compact pages to from-space. And depending on when it's
+ // done, we have to invoke VisitRefsForCompaction() with or without
+ // read-barrier.
+ TimingLogger::ScopedTiming t2("(Paused)UpdateImmuneSpaces", GetTimings());
+ accounting::CardTable* const card_table = heap_->GetCardTable();
+ for (auto& space : immune_spaces_.GetSpaces()) {
+ DCHECK(space->IsImageSpace() || space->IsZygoteSpace());
+ accounting::ContinuousSpaceBitmap* live_bitmap = space->GetLiveBitmap();
+ accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space);
+ // Having zygote-space indicates that the first zygote fork has taken
+ // place and that the classes/dex-caches in immune-spaces may have allocations
+ // (ArtMethod/ArtField arrays, dex-cache array, etc.) in the
+ // non-userfaultfd visited private-anonymous mappings. Visit them here.
+ ImmuneSpaceUpdateObjVisitor visitor(this, /*visit_native_roots=*/false);
+ if (table != nullptr) {
+ table->ProcessCards();
+ table->VisitObjects(ImmuneSpaceUpdateObjVisitor::Callback, &visitor);
+ } else {
+ WriterMutexLock wmu(thread_running_gc_, *Locks::heap_bitmap_lock_);
+ card_table->Scan<false>(
+ live_bitmap,
+ space->Begin(),
+ space->Limit(),
+ visitor,
+ accounting::CardTable::kCardDirty - 1);
+ }
+ }
+ }
+
+ KernelPreparation();
+ UpdateNonMovingSpace();
+ // fallback mode
+ if (uffd_ == kFallbackMode) {
+ CompactMovingSpace<kFallbackMode>(nullptr);
+
+ int32_t freed_bytes = black_objs_slide_diff_;
+ bump_pointer_space_->RecordFree(freed_objects_, freed_bytes);
+ RecordFree(ObjectBytePair(freed_objects_, freed_bytes));
+ } else {
+ DCHECK_EQ(compaction_in_progress_count_.load(std::memory_order_relaxed), 0u);
+ // We must start worker threads before resuming mutators to avoid deadlocks.
+ heap_->GetThreadPool()->StartWorkers(thread_running_gc_);
+ }
+ stack_low_addr_ = nullptr;
+}
+
+void MarkCompact::KernelPrepareRange(uint8_t* to_addr,
+ uint8_t* from_addr,
+ size_t map_size,
+ size_t uffd_size,
+ int fd,
+ int uffd_mode,
+ uint8_t* shadow_addr) {
+ int mremap_flags = MREMAP_MAYMOVE | MREMAP_FIXED;
+ if (gHaveMremapDontunmap) {
+ mremap_flags |= MREMAP_DONTUNMAP;
+ }
+
+ void* ret = mremap(to_addr, map_size, map_size, mremap_flags, from_addr);
+ CHECK_EQ(ret, static_cast<void*>(from_addr))
+ << "mremap to move pages failed: " << strerror(errno)
+ << ". space-addr=" << reinterpret_cast<void*>(to_addr) << " size=" << PrettySize(map_size);
+
+ if (shadow_addr != nullptr) {
+ DCHECK_EQ(fd, kFdUnused);
+ DCHECK(gHaveMremapDontunmap);
+ ret = mremap(shadow_addr, map_size, map_size, mremap_flags, to_addr);
+ CHECK_EQ(ret, static_cast<void*>(to_addr))
+ << "mremap from shadow to to-space map failed: " << strerror(errno);
+ } else if (!gHaveMremapDontunmap || fd > kFdUnused) {
+ // Without MREMAP_DONTUNMAP the source mapping is unmapped by mremap. So mmap
+ // the moving space again.
+ int mmap_flags = MAP_FIXED;
+ if (fd == kFdUnused) {
+ // Use MAP_FIXED_NOREPLACE so that if someone else reserves 'to_addr'
+ // mapping in meantime, which can happen when MREMAP_DONTUNMAP isn't
+ // available, to avoid unmapping someone else' mapping and then causing
+ // crashes elsewhere.
+ mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE;
+ // On some platforms MAP_ANONYMOUS expects fd to be -1.
+ fd = -1;
+ } else if (IsValidFd(fd)) {
+ mmap_flags |= MAP_SHARED;
+ } else {
+ DCHECK_EQ(fd, kFdSharedAnon);
+ mmap_flags |= MAP_SHARED | MAP_ANONYMOUS;
+ }
+ ret = mmap(to_addr, map_size, PROT_READ | PROT_WRITE, mmap_flags, fd, 0);
+ CHECK_EQ(ret, static_cast<void*>(to_addr))
+ << "mmap for moving space failed: " << strerror(errno);
+ }
+ if (IsValidFd(uffd_)) {
+ // Userfaultfd registration
+ struct uffdio_register uffd_register;
+ uffd_register.range.start = reinterpret_cast<uintptr_t>(to_addr);
+ uffd_register.range.len = uffd_size;
+ uffd_register.mode = UFFDIO_REGISTER_MODE_MISSING;
+ if (uffd_mode == kMinorFaultMode) {
+ uffd_register.mode |= UFFDIO_REGISTER_MODE_MINOR;
+ }
+ CHECK_EQ(ioctl(uffd_, UFFDIO_REGISTER, &uffd_register), 0)
+ << "ioctl_userfaultfd: register failed: " << strerror(errno)
+ << ". start:" << static_cast<void*>(to_addr) << " len:" << PrettySize(uffd_size);
+ }
+}
+
+void MarkCompact::KernelPreparation() {
+ TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
+ uint8_t* moving_space_begin = bump_pointer_space_->Begin();
+ size_t moving_space_size = bump_pointer_space_->Capacity();
+ int mode = kCopyMode;
+ size_t moving_space_register_sz;
+ if (minor_fault_initialized_) {
+ moving_space_register_sz = (moving_first_objs_count_ + black_page_count_) * kPageSize;
+ if (shadow_to_space_map_.IsValid()) {
+ size_t shadow_size = shadow_to_space_map_.Size();
+ void* addr = shadow_to_space_map_.Begin();
+ if (shadow_size < moving_space_register_sz) {
+ addr = mremap(addr,
+ shadow_size,
+ moving_space_register_sz,
+ // Don't allow moving with obj-ptr poisoning as the
+ // mapping needs to be in <4GB address space.
+ kObjPtrPoisoning ? 0 : MREMAP_MAYMOVE,
+ /*new_address=*/nullptr);
+ if (addr != MAP_FAILED) {
+ // Succeeded in expanding the mapping. Update the MemMap entry for shadow map.
+ MemMap temp = MemMap::MapPlaceholder(
+ "moving-space-shadow", static_cast<uint8_t*>(addr), moving_space_register_sz);
+ std::swap(shadow_to_space_map_, temp);
+ }
+ }
+ if (addr != MAP_FAILED) {
+ mode = kMinorFaultMode;
+ } else {
+ // We are not going to use shadow map. So protect it to catch any
+ // potential bugs.
+ DCHECK_EQ(mprotect(shadow_to_space_map_.Begin(), shadow_to_space_map_.Size(), PROT_NONE), 0)
+ << "mprotect failed: " << strerror(errno);
+ }
+ }
+ } else {
+ moving_space_register_sz = moving_space_size;
+ }
+
+ bool map_shared =
+ minor_fault_initialized_ || (!Runtime::Current()->IsZygote() && uffd_minor_fault_supported_);
+ uint8_t* shadow_addr = nullptr;
+ if (moving_to_space_fd_ == kFdUnused && map_shared) {
+ DCHECK(gHaveMremapDontunmap);
+ DCHECK(shadow_to_space_map_.IsValid());
+ DCHECK_EQ(shadow_to_space_map_.Size(), moving_space_size);
+ shadow_addr = shadow_to_space_map_.Begin();
+ }
+
+ KernelPrepareRange(moving_space_begin,
+ from_space_begin_,
+ moving_space_size,
+ moving_space_register_sz,
+ moving_to_space_fd_,
+ mode,
+ shadow_addr);
+
+ if (IsValidFd(uffd_)) {
+ for (auto& data : linear_alloc_spaces_data_) {
+ KernelPrepareRange(data.begin_,
+ data.shadow_.Begin(),
+ data.shadow_.Size(),
+ data.shadow_.Size(),
+ map_shared && !data.already_shared_ ? kFdSharedAnon : kFdUnused,
+ minor_fault_initialized_ ? kMinorFaultMode : kCopyMode);
+ if (map_shared) {
+ data.already_shared_ = true;
+ }
+ }
+ }
+ if (map_shared) {
+ // Start mapping linear-alloc MAP_SHARED only after the compaction pause of
+ // the first GC in non-zygote processes. This is the GC which sets up
+ // mappings for using minor-fault in future. Up to this point we run
+ // userfaultfd in copy-mode, which requires the mappings (of linear-alloc)
+ // to be MAP_PRIVATE.
+ map_linear_alloc_shared_ = true;
+ }
+}
+
+template <int kMode>
+void MarkCompact::ConcurrentCompaction(uint8_t* buf) {
+ DCHECK_NE(kMode, kFallbackMode);
+ DCHECK(kMode != kCopyMode || buf != nullptr);
+ auto zeropage_ioctl = [this](void* addr, bool tolerate_eexist, bool tolerate_enoent) {
+ struct uffdio_zeropage uffd_zeropage;
+ DCHECK(IsAligned<kPageSize>(addr));
+ uffd_zeropage.range.start = reinterpret_cast<uintptr_t>(addr);
+ uffd_zeropage.range.len = kPageSize;
+ uffd_zeropage.mode = 0;
+ int ret = ioctl(uffd_, UFFDIO_ZEROPAGE, &uffd_zeropage);
+ if (LIKELY(ret == 0)) {
+ DCHECK_EQ(uffd_zeropage.zeropage, static_cast<ssize_t>(kPageSize));
+ } else {
+ CHECK((tolerate_enoent && errno == ENOENT) || (tolerate_eexist && errno == EEXIST))
+ << "ioctl_userfaultfd: zeropage failed: " << strerror(errno) << ". addr:" << addr;
+ }
+ };
+
+ auto copy_ioctl = [this] (void* fault_page, void* src) {
+ struct uffdio_copy uffd_copy;
+ uffd_copy.src = reinterpret_cast<uintptr_t>(src);
+ uffd_copy.dst = reinterpret_cast<uintptr_t>(fault_page);
+ uffd_copy.len = kPageSize;
+ uffd_copy.mode = 0;
+ int ret = ioctl(uffd_, UFFDIO_COPY, &uffd_copy);
+ CHECK_EQ(ret, 0) << "ioctl_userfaultfd: copy failed: " << strerror(errno)
+ << ". src:" << src << " fault_page:" << fault_page;
+ DCHECK_EQ(uffd_copy.copy, static_cast<ssize_t>(kPageSize));
+ };
+ size_t nr_moving_space_used_pages = moving_first_objs_count_ + black_page_count_;
+ while (true) {
+ struct uffd_msg msg;
+ ssize_t nread = read(uffd_, &msg, sizeof(msg));
+ CHECK_GT(nread, 0);
+ CHECK_EQ(msg.event, UFFD_EVENT_PAGEFAULT);
+ DCHECK_EQ(nread, static_cast<ssize_t>(sizeof(msg)));
+ uint8_t* fault_addr = reinterpret_cast<uint8_t*>(msg.arg.pagefault.address);
+ if (fault_addr == conc_compaction_termination_page_) {
+ // The counter doesn't need to be updated atomically as only one thread
+ // would wake up against the gc-thread's load to this fault_addr. In fact,
+ // the other threads would wake up serially because every exiting thread
+ // will wake up gc-thread, which would retry load but again would find the
+ // page missing. Also, the value will be flushed to caches due to the ioctl
+ // syscall below.
+ uint8_t ret = thread_pool_counter_--;
+ // If 'gKernelHasFaultRetry == true' then only the last thread should map the
+ // zeropage so that the gc-thread can proceed. Otherwise, each thread does
+ // it and the gc-thread will repeat this fault until thread_pool_counter == 0.
+ if (!gKernelHasFaultRetry || ret == 1) {
+ zeropage_ioctl(fault_addr, /*tolerate_eexist=*/false, /*tolerate_enoent=*/false);
+ } else {
+ struct uffdio_range uffd_range;
+ uffd_range.start = msg.arg.pagefault.address;
+ uffd_range.len = kPageSize;
+ CHECK_EQ(ioctl(uffd_, UFFDIO_WAKE, &uffd_range), 0)
+ << "ioctl_userfaultfd: wake failed for concurrent-compaction termination page: "
+ << strerror(errno);
+ }
+ break;
+ }
+ uint8_t* fault_page = AlignDown(fault_addr, kPageSize);
+ if (bump_pointer_space_->HasAddress(reinterpret_cast<mirror::Object*>(fault_addr))) {
+ ConcurrentlyProcessMovingPage<kMode>(
+ zeropage_ioctl, copy_ioctl, fault_page, buf, nr_moving_space_used_pages);
+ } else if (minor_fault_initialized_) {
+ ConcurrentlyProcessLinearAllocPage<kMinorFaultMode>(
+ zeropage_ioctl,
+ copy_ioctl,
+ fault_page,
+ (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_MINOR) != 0);
+ } else {
+ ConcurrentlyProcessLinearAllocPage<kCopyMode>(
+ zeropage_ioctl,
+ copy_ioctl,
+ fault_page,
+ (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_MINOR) != 0);
+ }
+ }
+}
+
+template <int kMode, typename ZeropageType, typename CopyType>
+void MarkCompact::ConcurrentlyProcessMovingPage(ZeropageType& zeropage_ioctl,
+ CopyType& copy_ioctl,
+ uint8_t* fault_page,
+ uint8_t* buf,
+ size_t nr_moving_space_used_pages) {
+ class ScopedInProgressCount {
+ public:
+ explicit ScopedInProgressCount(MarkCompact* collector) : collector_(collector) {
+ collector_->compaction_in_progress_count_.fetch_add(1, std::memory_order_relaxed);
+ }
+
+ ~ScopedInProgressCount() {
+ collector_->compaction_in_progress_count_.fetch_add(-1, std::memory_order_relaxed);
+ }
+
+ private:
+ MarkCompact* collector_;
+ };
+
+ uint8_t* unused_space_begin =
+ bump_pointer_space_->Begin() + nr_moving_space_used_pages * kPageSize;
+ DCHECK(IsAligned<kPageSize>(unused_space_begin));
+ DCHECK(kMode == kCopyMode || fault_page < unused_space_begin);
+ if (kMode == kCopyMode && fault_page >= unused_space_begin) {
+ // There is a race which allows more than one thread to install a
+ // zero-page. But we can tolerate that. So absorb the EEXIST returned by
+ // the ioctl and move on.
+ zeropage_ioctl(fault_page, /*tolerate_eexist=*/true, /*tolerate_enoent=*/true);
+ return;
+ }
+ size_t page_idx = (fault_page - bump_pointer_space_->Begin()) / kPageSize;
+ mirror::Object* first_obj = first_objs_moving_space_[page_idx].AsMirrorPtr();
+ if (first_obj == nullptr) {
+ // We should never have a case where two workers are trying to install a
+ // zeropage in this range as we synchronize using moving_pages_status_[page_idx].
+ PageState expected_state = PageState::kUnprocessed;
+ if (moving_pages_status_[page_idx].compare_exchange_strong(
+ expected_state, PageState::kProcessedAndMapping, std::memory_order_relaxed)) {
+ // Note: ioctl acts as an acquire fence.
+ zeropage_ioctl(fault_page, /*tolerate_eexist=*/false, /*tolerate_enoent=*/true);
+ } else {
+ DCHECK_EQ(expected_state, PageState::kProcessedAndMapping);
+ }
+ return;
+ }
+
+ PageState state = moving_pages_status_[page_idx].load(std::memory_order_relaxed);
+ while (true) {
+ switch (state) {
+ case PageState::kUnprocessed: {
+ // The increment to the in-progress counter must be done before updating
+ // the page's state. Otherwise, we will end up leaving a window wherein
+ // the GC-thread could observe that no worker is working on compaction
+ // and could end up unregistering the moving space from userfaultfd.
+ ScopedInProgressCount in_progress(this);
+ // Acquire order to ensure we don't start writing to shadow map, which is
+ // shared, before the CAS is successful. Release order to ensure that the
+ // increment to moving_compactions_in_progress above is not re-ordered
+ // after the CAS.
+ if (moving_pages_status_[page_idx].compare_exchange_strong(
+ state, PageState::kMutatorProcessing, std::memory_order_acquire)) {
+ if (kMode == kMinorFaultMode) {
+ DCHECK_EQ(buf, nullptr);
+ buf = shadow_to_space_map_.Begin() + page_idx * kPageSize;
+ }
+
+ if (fault_page < post_compact_end_) {
+ // The page has to be compacted.
+ CompactPage(
+ first_obj, pre_compact_offset_moving_space_[page_idx], buf, kMode == kCopyMode);
+ } else {
+ DCHECK_NE(first_obj, nullptr);
+ DCHECK_GT(pre_compact_offset_moving_space_[page_idx], 0u);
+ uint8_t* pre_compact_page = black_allocations_begin_ + (fault_page - post_compact_end_);
+ DCHECK(IsAligned<kPageSize>(pre_compact_page));
+ SlideBlackPage(first_obj, page_idx, pre_compact_page, buf, kMode == kCopyMode);
+ }
+ // Nobody else would simultaneously modify this page's state so an
+ // atomic store is sufficient. Use 'release' order to guarantee that
+ // loads/stores to the page are finished before this store.
+ moving_pages_status_[page_idx].store(PageState::kProcessedAndMapping,
+ std::memory_order_release);
+ if (kMode == kCopyMode) {
+ copy_ioctl(fault_page, buf);
+ return;
+ } else {
+ break;
+ }
+ }
+ }
+ continue;
+ case PageState::kProcessing:
+ DCHECK_EQ(kMode, kMinorFaultMode);
+ if (moving_pages_status_[page_idx].compare_exchange_strong(
+ state, PageState::kProcessingAndMapping, std::memory_order_relaxed)) {
+ // Somebody else took or will take care of finishing the compaction and
+ // then mapping the page.
+ return;
+ }
+ continue;
+ case PageState::kProcessed:
+ // The page is processed but not mapped. We should map it.
+ break;
+ default:
+ // Somebody else took care of the page.
+ return;
+ }
+ break;
+ }
+
+ DCHECK_EQ(kMode, kMinorFaultMode);
+ if (state == PageState::kUnprocessed) {
+ MapProcessedPages</*kFirstPageMapping=*/true>(
+ fault_page, moving_pages_status_, page_idx, nr_moving_space_used_pages);
+ } else {
+ DCHECK_EQ(state, PageState::kProcessed);
+ MapProcessedPages</*kFirstPageMapping=*/false>(
+ fault_page, moving_pages_status_, page_idx, nr_moving_space_used_pages);
+ }
+}
+
+template <int kMode, typename ZeropageType, typename CopyType>
+void MarkCompact::ConcurrentlyProcessLinearAllocPage(ZeropageType& zeropage_ioctl,
+ CopyType& copy_ioctl,
+ uint8_t* fault_page,
+ bool is_minor_fault) {
+ DCHECK(!is_minor_fault || kMode == kMinorFaultMode);
+ auto arena_iter = linear_alloc_arenas_.end();
+ {
+ TrackedArena temp_arena(fault_page);
+ arena_iter = linear_alloc_arenas_.upper_bound(&temp_arena);
+ arena_iter = arena_iter != linear_alloc_arenas_.begin() ? std::prev(arena_iter)
+ : linear_alloc_arenas_.end();
+ }
+ if (arena_iter == linear_alloc_arenas_.end() || arena_iter->second <= fault_page) {
+ // Fault page isn't in any of the arenas that existed before we started
+ // compaction. So map zeropage and return.
+ zeropage_ioctl(fault_page, /*tolerate_eexist=*/true, /*tolerate_enoent=*/false);
+ } else {
+ // fault_page should always belong to some arena.
+ DCHECK(arena_iter != linear_alloc_arenas_.end())
+ << "fault_page:" << static_cast<void*>(fault_page) << "is_minor_fault:" << is_minor_fault;
+ // Find the linear-alloc space containing fault-page
+ LinearAllocSpaceData* space_data = nullptr;
+ for (auto& data : linear_alloc_spaces_data_) {
+ if (data.begin_ <= fault_page && fault_page < data.end_) {
+ space_data = &data;
+ break;
+ }
+ }
+ DCHECK_NE(space_data, nullptr);
+ ptrdiff_t diff = space_data->shadow_.Begin() - space_data->begin_;
+ size_t page_idx = (fault_page - space_data->begin_) / kPageSize;
+ Atomic<PageState>* state_arr =
+ reinterpret_cast<Atomic<PageState>*>(space_data->page_status_map_.Begin());
+ PageState state = state_arr[page_idx].load(std::memory_order_relaxed);
+ while (true) {
+ switch (state) {
+ case PageState::kUnprocessed:
+ if (state_arr[page_idx].compare_exchange_strong(
+ state, PageState::kProcessingAndMapping, std::memory_order_acquire)) {
+ if (kMode == kCopyMode || is_minor_fault) {
+ uint8_t* first_obj = arena_iter->first->GetFirstObject(fault_page);
+ DCHECK_NE(first_obj, nullptr);
+ LinearAllocPageUpdater updater(this);
+ updater(fault_page + diff, first_obj + diff);
+ if (kMode == kCopyMode) {
+ copy_ioctl(fault_page, fault_page + diff);
+ return;
+ }
+ } else {
+ // Don't touch the page in this case (there is no reason to do so
+ // anyways) as it would mean reading from first_obj, which could be on
+ // another missing page and hence may cause this thread to block, leading
+ // to deadlocks.
+ // Force read the page if it is missing so that a zeropage gets mapped on
+ // the shadow map and then CONTINUE ioctl will map it on linear-alloc.
+ ForceRead(fault_page + diff);
+ }
+ MapProcessedPages</*kFirstPageMapping=*/true>(
+ fault_page, state_arr, page_idx, space_data->page_status_map_.Size());
+ return;
+ }
+ continue;
+ case PageState::kProcessing:
+ DCHECK_EQ(kMode, kMinorFaultMode);
+ if (state_arr[page_idx].compare_exchange_strong(
+ state, PageState::kProcessingAndMapping, std::memory_order_relaxed)) {
+ // Somebody else took or will take care of finishing the updates and
+ // then mapping the page.
+ return;
+ }
+ continue;
+ case PageState::kProcessed:
+ // The page is processed but not mapped. We should map it.
+ break;
+ default:
+ // Somebody else took care of the page.
+ return;
+ }
+ break;
+ }
+
+ DCHECK_EQ(kMode, kMinorFaultMode);
+ DCHECK_EQ(state, PageState::kProcessed);
+ if (!is_minor_fault) {
+ // Force read the page if it is missing so that a zeropage gets mapped on
+ // the shadow map and then CONTINUE ioctl will map it on linear-alloc.
+ ForceRead(fault_page + diff);
+ }
+ MapProcessedPages</*kFirstPageMapping=*/false>(
+ fault_page, state_arr, page_idx, space_data->page_status_map_.Size());
+ }
+}
+
+void MarkCompact::ProcessLinearAlloc() {
+ for (auto& pair : linear_alloc_arenas_) {
+ const TrackedArena* arena = pair.first;
+ uint8_t* last_byte = pair.second;
+ DCHECK_ALIGNED(last_byte, kPageSize);
+ bool others_processing = false;
+ // Find the linear-alloc space containing the arena
+ LinearAllocSpaceData* space_data = nullptr;
+ for (auto& data : linear_alloc_spaces_data_) {
+ if (data.begin_ <= arena->Begin() && arena->Begin() < data.end_) {
+ space_data = &data;
+ break;
+ }
+ }
+ DCHECK_NE(space_data, nullptr);
+ ptrdiff_t diff = space_data->shadow_.Begin() - space_data->begin_;
+ auto visitor = [space_data, last_byte, diff, this, &others_processing](
+ uint8_t* page_begin,
+ uint8_t* first_obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+ // No need to process pages past last_byte as they already have updated
+ // gc-roots, if any.
+ if (page_begin >= last_byte) {
+ return;
+ }
+ LinearAllocPageUpdater updater(this);
+ size_t page_idx = (page_begin - space_data->begin_) / kPageSize;
+ DCHECK_LT(page_idx, space_data->page_status_map_.Size());
+ Atomic<PageState>* state_arr =
+ reinterpret_cast<Atomic<PageState>*>(space_data->page_status_map_.Begin());
+ PageState expected_state = PageState::kUnprocessed;
+ PageState desired_state =
+ minor_fault_initialized_ ? PageState::kProcessing : PageState::kProcessingAndMapping;
+ // Acquire order to ensure that we don't start accessing the shadow page,
+ // which is shared with other threads, prior to CAS. Also, for same
+ // reason, we used 'release' order for changing the state to 'processed'.
+ if (state_arr[page_idx].compare_exchange_strong(
+ expected_state, desired_state, std::memory_order_acquire)) {
+ updater(page_begin + diff, first_obj + diff);
+ expected_state = PageState::kProcessing;
+ if (!minor_fault_initialized_) {
+ struct uffdio_copy uffd_copy;
+ uffd_copy.src = reinterpret_cast<uintptr_t>(page_begin + diff);
+ uffd_copy.dst = reinterpret_cast<uintptr_t>(page_begin);
+ uffd_copy.len = kPageSize;
+ uffd_copy.mode = 0;
+ CHECK_EQ(ioctl(uffd_, UFFDIO_COPY, &uffd_copy), 0)
+ << "ioctl_userfaultfd: linear-alloc copy failed:" << strerror(errno)
+ << ". dst:" << static_cast<void*>(page_begin);
+ DCHECK_EQ(uffd_copy.copy, static_cast<ssize_t>(kPageSize));
+ } else if (!state_arr[page_idx].compare_exchange_strong(
+ expected_state, PageState::kProcessed, std::memory_order_release)) {
+ DCHECK_EQ(expected_state, PageState::kProcessingAndMapping);
+ // Force read in case the page was missing and updater didn't touch it
+ // as there was nothing to do. This will ensure that a zeropage is
+ // faulted on the shadow map.
+ ForceRead(page_begin + diff);
+ MapProcessedPages</*kFirstPageMapping=*/true>(
+ page_begin, state_arr, page_idx, space_data->page_status_map_.Size());
+ }
+ } else {
+ others_processing = true;
+ }
+ };
+
+ arena->VisitRoots(visitor);
+ // If we are not in minor-fault mode and if no other thread was found to be
+ // processing any pages in this arena, then we can madvise the shadow size.
+ // Otherwise, we will double the memory use for linear-alloc.
+ if (!minor_fault_initialized_ && !others_processing) {
+ ZeroAndReleasePages(arena->Begin() + diff, arena->Size());
+ }
+ }
+}
+
+void MarkCompact::UnregisterUffd(uint8_t* start, size_t len) {
+ struct uffdio_range range;
+ range.start = reinterpret_cast<uintptr_t>(start);
+ range.len = len;
+ CHECK_EQ(ioctl(uffd_, UFFDIO_UNREGISTER, &range), 0)
+ << "ioctl_userfaultfd: unregister failed: " << strerror(errno)
+ << ". addr:" << static_cast<void*>(start) << " len:" << PrettySize(len);
+ // Due to an oversight in the kernel implementation of 'unregister', the
+ // waiting threads are woken up only for copy uffds. Therefore, for now, we
+ // have to explicitly wake up the threads in minor-fault case.
+ // TODO: The fix in the kernel is being worked on. Once the kernel version
+ // containing the fix is known, make it conditional on that as well.
+ if (minor_fault_initialized_) {
+ CHECK_EQ(ioctl(uffd_, UFFDIO_WAKE, &range), 0)
+ << "ioctl_userfaultfd: wake failed: " << strerror(errno)
+ << ". addr:" << static_cast<void*>(start) << " len:" << PrettySize(len);
+ }
+}
+
+void MarkCompact::CompactionPhase() {
+ TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
+ {
+ int32_t freed_bytes = black_objs_slide_diff_;
+ bump_pointer_space_->RecordFree(freed_objects_, freed_bytes);
+ RecordFree(ObjectBytePair(freed_objects_, freed_bytes));
+ }
+
+ if (CanCompactMovingSpaceWithMinorFault()) {
+ CompactMovingSpace<kMinorFaultMode>(/*page=*/nullptr);
+ } else {
+ CompactMovingSpace<kCopyMode>(compaction_buffers_map_.Begin());
+ }
+
+ // TODO: add more sophisticated logic here wherein we sleep after attempting
+ // yield a couple of times.
+ while (compaction_in_progress_count_.load(std::memory_order_relaxed) > 0) {
+ sched_yield();
+ }
+
+ size_t moving_space_size = bump_pointer_space_->Capacity();
+ UnregisterUffd(bump_pointer_space_->Begin(),
+ minor_fault_initialized_ ?
+ (moving_first_objs_count_ + black_page_count_) * kPageSize :
+ moving_space_size);
+
+ // Release all of the memory taken by moving-space's from-map
+ if (minor_fault_initialized_) {
+ if (IsValidFd(moving_from_space_fd_)) {
+ // A strange behavior is observed wherein between GC cycles the from-space'
+ // first page is accessed. But the memfd that is mapped on from-space, is
+ // used on to-space in next GC cycle, causing issues with userfaultfd as the
+ // page isn't missing. A possible reason for this could be prefetches. The
+ // mprotect ensures that such accesses don't succeed.
+ int ret = mprotect(from_space_begin_, moving_space_size, PROT_NONE);
+ CHECK_EQ(ret, 0) << "mprotect(PROT_NONE) for from-space failed: " << strerror(errno);
+ // madvise(MADV_REMOVE) needs PROT_WRITE. Use fallocate() instead, which
+ // does the same thing.
+ ret = fallocate(moving_from_space_fd_,
+ FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
+ /*offset=*/0,
+ moving_space_size);
+ CHECK_EQ(ret, 0) << "fallocate for from-space failed: " << strerror(errno);
+ } else {
+ // We don't have a valid fd, so use madvise(MADV_REMOVE) instead. mprotect
+ // is not required in this case as we create fresh
+ // MAP_SHARED+MAP_ANONYMOUS mapping in each GC cycle.
+ int ret = madvise(from_space_begin_, moving_space_size, MADV_REMOVE);
+ CHECK_EQ(ret, 0) << "madvise(MADV_REMOVE) failed for from-space map:" << strerror(errno);
+ }
+ } else {
+ from_space_map_.MadviseDontNeedAndZero();
+ }
+ // mprotect(PROT_NONE) all maps except to-space in debug-mode to catch any unexpected accesses.
+ if (shadow_to_space_map_.IsValid()) {
+ DCHECK_EQ(mprotect(shadow_to_space_map_.Begin(), shadow_to_space_map_.Size(), PROT_NONE), 0)
+ << "mprotect(PROT_NONE) for shadow-map failed:" << strerror(errno);
+ }
+ if (!IsValidFd(moving_from_space_fd_)) {
+ // The other case is already mprotected above.
+ DCHECK_EQ(mprotect(from_space_begin_, moving_space_size, PROT_NONE), 0)
+ << "mprotect(PROT_NONE) for from-space failed: " << strerror(errno);
+ }
+
+ ProcessLinearAlloc();
+
+ DCHECK(IsAligned<kPageSize>(conc_compaction_termination_page_));
+ // We will only iterate once if gKernelHasFaultRetry is true.
+ do {
+ // madvise the page so that we can get userfaults on it.
+ ZeroAndReleasePages(conc_compaction_termination_page_, kPageSize);
+ // The following load triggers 'special' userfaults. When received by the
+ // thread-pool workers, they will exit out of the compaction task. This fault
+ // happens because we madvised the page.
+ ForceRead(conc_compaction_termination_page_);
+ } while (thread_pool_counter_ > 0);
+
+ // Unregister linear-alloc spaces
+ for (auto& data : linear_alloc_spaces_data_) {
+ DCHECK_EQ(data.end_ - data.begin_, static_cast<ssize_t>(data.shadow_.Size()));
+ UnregisterUffd(data.begin_, data.shadow_.Size());
+ // madvise linear-allocs's page-status array
+ data.page_status_map_.MadviseDontNeedAndZero();
+ // Madvise the entire linear-alloc space's shadow. In copy-mode it gets rid
+ // of the pages which are still mapped. In minor-fault mode this unmaps all
+ // pages, which is good in reducing the mremap (done in STW pause) time in
+ // next GC cycle.
+ data.shadow_.MadviseDontNeedAndZero();
+ if (minor_fault_initialized_) {
+ DCHECK_EQ(mprotect(data.shadow_.Begin(), data.shadow_.Size(), PROT_NONE), 0)
+ << "mprotect failed: " << strerror(errno);
+ }
+ }
+
+ heap_->GetThreadPool()->StopWorkers(thread_running_gc_);
+}
+
+template <size_t kBufferSize>
+class MarkCompact::ThreadRootsVisitor : public RootVisitor {
+ public:
+ explicit ThreadRootsVisitor(MarkCompact* mark_compact, Thread* const self)
+ : mark_compact_(mark_compact), self_(self) {}
+
+ ~ThreadRootsVisitor() {
+ Flush();
+ }
+
+ void VisitRoots(mirror::Object*** roots, size_t count, const RootInfo& info ATTRIBUTE_UNUSED)
+ override REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_) {
+ for (size_t i = 0; i < count; i++) {
+ mirror::Object* obj = *roots[i];
+ if (mark_compact_->MarkObjectNonNullNoPush</*kParallel*/true>(obj)) {
+ Push(obj);
+ }
+ }
+ }
+
+ void VisitRoots(mirror::CompressedReference<mirror::Object>** roots,
+ size_t count,
+ const RootInfo& info ATTRIBUTE_UNUSED)
+ override REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_) {
+ for (size_t i = 0; i < count; i++) {
+ mirror::Object* obj = roots[i]->AsMirrorPtr();
+ if (mark_compact_->MarkObjectNonNullNoPush</*kParallel*/true>(obj)) {
+ Push(obj);
+ }
+ }
+ }
+
+ private:
+ void Flush() REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_) {
+ StackReference<mirror::Object>* start;
+ StackReference<mirror::Object>* end;
+ {
+ MutexLock mu(self_, mark_compact_->mark_stack_lock_);
+ // Loop here because even after expanding once it may not be sufficient to
+ // accommodate all references. It's almost impossible, but there is no harm
+ // in implementing it this way.
+ while (!mark_compact_->mark_stack_->BumpBack(idx_, &start, &end)) {
+ mark_compact_->ExpandMarkStack();
+ }
+ }
+ while (idx_ > 0) {
+ *start++ = roots_[--idx_];
+ }
+ DCHECK_EQ(start, end);
+ }
+
+ void Push(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_) {
+ if (UNLIKELY(idx_ >= kBufferSize)) {
+ Flush();
+ }
+ roots_[idx_++].Assign(obj);
+ }
+
+ StackReference<mirror::Object> roots_[kBufferSize];
+ size_t idx_ = 0;
+ MarkCompact* const mark_compact_;
+ Thread* const self_;
+};
+
+class MarkCompact::CheckpointMarkThreadRoots : public Closure {
+ public:
+ explicit CheckpointMarkThreadRoots(MarkCompact* mark_compact) : mark_compact_(mark_compact) {}
+
+ void Run(Thread* thread) override NO_THREAD_SAFETY_ANALYSIS {
+ ScopedTrace trace("Marking thread roots");
+ // Note: self is not necessarily equal to thread since thread may be
+ // suspended.
+ Thread* const self = Thread::Current();
+ CHECK(thread == self
+ || thread->IsSuspended()
+ || thread->GetState() == ThreadState::kWaitingPerformingGc)
+ << thread->GetState() << " thread " << thread << " self " << self;
+ {
+ ThreadRootsVisitor</*kBufferSize*/ 20> visitor(mark_compact_, self);
+ thread->VisitRoots(&visitor, kVisitRootFlagAllRoots);
+ }
+
+ // If thread is a running mutator, then act on behalf of the garbage
+ // collector. See the code in ThreadList::RunCheckpoint.
+ mark_compact_->GetBarrier().Pass(self);
+ }
+
+ private:
+ MarkCompact* const mark_compact_;
+};
+
+void MarkCompact::MarkRootsCheckpoint(Thread* self, Runtime* runtime) {
+ // We revote TLABs later during paused round of marking.
+ TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
+ CheckpointMarkThreadRoots check_point(this);
+ ThreadList* thread_list = runtime->GetThreadList();
+ gc_barrier_.Init(self, 0);
+ // Request the check point is run on all threads returning a count of the threads that must
+ // run through the barrier including self.
+ size_t barrier_count = thread_list->RunCheckpoint(&check_point);
+ // Release locks then wait for all mutator threads to pass the barrier.
+ // If there are no threads to wait which implys that all the checkpoint functions are finished,
+ // then no need to release locks.
+ if (barrier_count == 0) {
+ return;
+ }
+ Locks::heap_bitmap_lock_->ExclusiveUnlock(self);
+ Locks::mutator_lock_->SharedUnlock(self);
+ {
+ ScopedThreadStateChange tsc(self, ThreadState::kWaitingForCheckPointsToRun);
+ gc_barrier_.Increment(self, barrier_count);
+ }
+ Locks::mutator_lock_->SharedLock(self);
+ Locks::heap_bitmap_lock_->ExclusiveLock(self);
+}
+
+void MarkCompact::MarkNonThreadRoots(Runtime* runtime) {
+ TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
+ runtime->VisitNonThreadRoots(this);
+}
+
+void MarkCompact::MarkConcurrentRoots(VisitRootFlags flags, Runtime* runtime) {
+ TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
+ runtime->VisitConcurrentRoots(this, flags);
+}
+
+void MarkCompact::RevokeAllThreadLocalBuffers() {
+ TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
+ bump_pointer_space_->RevokeAllThreadLocalBuffers();
+}
+
+class MarkCompact::ScanObjectVisitor {
+ public:
+ explicit ScanObjectVisitor(MarkCompact* const mark_compact) ALWAYS_INLINE
+ : mark_compact_(mark_compact) {}
+
+ void operator()(ObjPtr<mirror::Object> obj) const
+ ALWAYS_INLINE
+ REQUIRES(Locks::heap_bitmap_lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ mark_compact_->ScanObject</*kUpdateLiveWords*/ false>(obj.Ptr());
+ }
+
+ private:
+ MarkCompact* const mark_compact_;
+};
+
+void MarkCompact::UpdateAndMarkModUnion() {
+ accounting::CardTable* const card_table = heap_->GetCardTable();
+ for (const auto& space : immune_spaces_.GetSpaces()) {
+ const char* name = space->IsZygoteSpace()
+ ? "UpdateAndMarkZygoteModUnionTable"
+ : "UpdateAndMarkImageModUnionTable";
+ DCHECK(space->IsZygoteSpace() || space->IsImageSpace()) << *space;
+ TimingLogger::ScopedTiming t(name, GetTimings());
+ accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space);
+ if (table != nullptr) {
+ // UpdateAndMarkReferences() doesn't visit Reference-type objects. But
+ // that's fine because these objects are immutable enough (referent can
+ // only be cleared) and hence the only referents they can have are intra-space.
+ table->UpdateAndMarkReferences(this);
+ } else {
+ // No mod-union table, scan all dirty/aged cards in the corresponding
+ // card-table. This can only occur for app images.
+ card_table->Scan</*kClearCard*/ false>(space->GetMarkBitmap(),
+ space->Begin(),
+ space->End(),
+ ScanObjectVisitor(this),
+ gc::accounting::CardTable::kCardAged);
+ }
+ }
+}
+
+void MarkCompact::MarkReachableObjects() {
+ UpdateAndMarkModUnion();
+ // Recursively mark all the non-image bits set in the mark bitmap.
+ ProcessMarkStack();
+}
+
+class MarkCompact::CardModifiedVisitor {
+ public:
+ explicit CardModifiedVisitor(MarkCompact* const mark_compact,
+ accounting::ContinuousSpaceBitmap* const bitmap,
+ accounting::CardTable* const card_table)
+ : visitor_(mark_compact), bitmap_(bitmap), card_table_(card_table) {}
+
+ void operator()(uint8_t* card,
+ uint8_t expected_value,
+ uint8_t new_value ATTRIBUTE_UNUSED) const {
+ if (expected_value == accounting::CardTable::kCardDirty) {
+ uintptr_t start = reinterpret_cast<uintptr_t>(card_table_->AddrFromCard(card));
+ bitmap_->VisitMarkedRange(start, start + accounting::CardTable::kCardSize, visitor_);
+ }
+ }
+
+ private:
+ ScanObjectVisitor visitor_;
+ accounting::ContinuousSpaceBitmap* bitmap_;
+ accounting::CardTable* const card_table_;
+};
+
+void MarkCompact::ScanDirtyObjects(bool paused, uint8_t minimum_age) {
+ accounting::CardTable* card_table = heap_->GetCardTable();
+ for (const auto& space : heap_->GetContinuousSpaces()) {
+ const char* name = nullptr;
+ switch (space->GetGcRetentionPolicy()) {
+ case space::kGcRetentionPolicyNeverCollect:
+ name = paused ? "(Paused)ScanGrayImmuneSpaceObjects" : "ScanGrayImmuneSpaceObjects";
+ break;
+ case space::kGcRetentionPolicyFullCollect:
+ name = paused ? "(Paused)ScanGrayZygoteSpaceObjects" : "ScanGrayZygoteSpaceObjects";
+ break;
+ case space::kGcRetentionPolicyAlwaysCollect:
+ name = paused ? "(Paused)ScanGrayAllocSpaceObjects" : "ScanGrayAllocSpaceObjects";
+ break;
+ default:
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
+ }
+ TimingLogger::ScopedTiming t(name, GetTimings());
+ ScanObjectVisitor visitor(this);
+ const bool is_immune_space = space->IsZygoteSpace() || space->IsImageSpace();
+ if (paused) {
+ DCHECK_EQ(minimum_age, gc::accounting::CardTable::kCardDirty);
+ // We can clear the card-table for any non-immune space.
+ if (is_immune_space) {
+ card_table->Scan</*kClearCard*/false>(space->GetMarkBitmap(),
+ space->Begin(),
+ space->End(),
+ visitor,
+ minimum_age);
+ } else {
+ card_table->Scan</*kClearCard*/true>(space->GetMarkBitmap(),
+ space->Begin(),
+ space->End(),
+ visitor,
+ minimum_age);
+ }
+ } else {
+ DCHECK_EQ(minimum_age, gc::accounting::CardTable::kCardAged);
+ accounting::ModUnionTable* table = heap_->FindModUnionTableFromSpace(space);
+ if (table) {
+ table->ProcessCards();
+ card_table->Scan</*kClearCard*/false>(space->GetMarkBitmap(),
+ space->Begin(),
+ space->End(),
+ visitor,
+ minimum_age);
+ } else {
+ CardModifiedVisitor card_modified_visitor(this, space->GetMarkBitmap(), card_table);
+ // For the alloc spaces we should age the dirty cards and clear the rest.
+ // For image and zygote-space without mod-union-table, age the dirty
+ // cards but keep the already aged cards unchanged.
+ // In either case, visit the objects on the cards that were changed from
+ // dirty to aged.
+ if (is_immune_space) {
+ card_table->ModifyCardsAtomic(space->Begin(),
+ space->End(),
+ [](uint8_t card) {
+ return (card == gc::accounting::CardTable::kCardClean)
+ ? card
+ : gc::accounting::CardTable::kCardAged;
+ },
+ card_modified_visitor);
+ } else {
+ card_table->ModifyCardsAtomic(space->Begin(),
+ space->End(),
+ AgeCardVisitor(),
+ card_modified_visitor);
+ }
+ }
+ }
+ }
+}
+
+void MarkCompact::RecursiveMarkDirtyObjects(bool paused, uint8_t minimum_age) {
+ ScanDirtyObjects(paused, minimum_age);
+ ProcessMarkStack();
+}
+
+void MarkCompact::MarkRoots(VisitRootFlags flags) {
+ TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
+ Runtime* runtime = Runtime::Current();
+ // Make sure that the checkpoint which collects the stack roots is the first
+ // one capturning GC-roots. As this one is supposed to find the address
+ // everything allocated after that (during this marking phase) will be
+ // considered 'marked'.
+ MarkRootsCheckpoint(thread_running_gc_, runtime);
+ MarkNonThreadRoots(runtime);
+ MarkConcurrentRoots(flags, runtime);
+}
+
+void MarkCompact::PreCleanCards() {
+ TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
+ CHECK(!Locks::mutator_lock_->IsExclusiveHeld(thread_running_gc_));
+ MarkRoots(static_cast<VisitRootFlags>(kVisitRootFlagClearRootLog | kVisitRootFlagNewRoots));
+ RecursiveMarkDirtyObjects(/*paused*/ false, accounting::CardTable::kCardDirty - 1);
+}
+
+// In a concurrent marking algorithm, if we are not using a write/read barrier, as
+// in this case, then we need a stop-the-world (STW) round in the end to mark
+// objects which were written into concurrently while concurrent marking was
+// performed.
+// In order to minimize the pause time, we could take one of the two approaches:
+// 1. Keep repeating concurrent marking of dirty cards until the time spent goes
+// below a threshold.
+// 2. Do two rounds concurrently and then attempt a paused one. If we figure
+// that it's taking too long, then resume mutators and retry.
+//
+// Given the non-trivial fixed overhead of running a round (card table and root
+// scan), it might be better to go with approach 2.
+void MarkCompact::MarkingPhase() {
+ TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
+ DCHECK_EQ(thread_running_gc_, Thread::Current());
+ WriterMutexLock mu(thread_running_gc_, *Locks::heap_bitmap_lock_);
+ BindAndResetBitmaps();
+ MarkRoots(
+ static_cast<VisitRootFlags>(kVisitRootFlagAllRoots | kVisitRootFlagStartLoggingNewRoots));
+ MarkReachableObjects();
+ // Pre-clean dirtied cards to reduce pauses.
+ PreCleanCards();
+
+ // Setup reference processing and forward soft references once before enabling
+ // slow path (in MarkingPause)
+ ReferenceProcessor* rp = GetHeap()->GetReferenceProcessor();
+ bool clear_soft_references = GetCurrentIteration()->GetClearSoftReferences();
+ rp->Setup(thread_running_gc_, this, /*concurrent=*/ true, clear_soft_references);
+ if (!clear_soft_references) {
+ // Forward as many SoftReferences as possible before inhibiting reference access.
+ rp->ForwardSoftReferences(GetTimings());
+ }
+}
+
+class MarkCompact::RefFieldsVisitor {
+ public:
+ ALWAYS_INLINE explicit RefFieldsVisitor(MarkCompact* const mark_compact)
+ : mark_compact_(mark_compact) {}
+
+ ALWAYS_INLINE void operator()(mirror::Object* obj,
+ MemberOffset offset,
+ bool is_static ATTRIBUTE_UNUSED) const
+ REQUIRES(Locks::heap_bitmap_lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (kCheckLocks) {
+ Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
+ Locks::heap_bitmap_lock_->AssertExclusiveHeld(Thread::Current());
+ }
+ mark_compact_->MarkObject(obj->GetFieldObject<mirror::Object>(offset), obj, offset);
+ }
+
+ void operator()(ObjPtr<mirror::Class> klass, ObjPtr<mirror::Reference> ref) const
+ REQUIRES(Locks::heap_bitmap_lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ mark_compact_->DelayReferenceReferent(klass, ref);
+ }
+
+ void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root) const
+ REQUIRES(Locks::heap_bitmap_lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!root->IsNull()) {
+ VisitRoot(root);
+ }
+ }
+
+ void VisitRoot(mirror::CompressedReference<mirror::Object>* root) const
+ REQUIRES(Locks::heap_bitmap_lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (kCheckLocks) {
+ Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
+ Locks::heap_bitmap_lock_->AssertExclusiveHeld(Thread::Current());
+ }
+ mark_compact_->MarkObject(root->AsMirrorPtr());
+ }
+
+ private:
+ MarkCompact* const mark_compact_;
+};
+
+template <size_t kAlignment>
+size_t MarkCompact::LiveWordsBitmap<kAlignment>::LiveBytesInBitmapWord(size_t chunk_idx) const {
+ const size_t index = chunk_idx * kBitmapWordsPerVectorWord;
+ size_t words = 0;
+ for (uint32_t i = 0; i < kBitmapWordsPerVectorWord; i++) {
+ words += POPCOUNT(Bitmap::Begin()[index + i]);
+ }
+ return words * kAlignment;
+}
+
+void MarkCompact::UpdateLivenessInfo(mirror::Object* obj) {
+ DCHECK(obj != nullptr);
+ uintptr_t obj_begin = reinterpret_cast<uintptr_t>(obj);
+ UpdateClassAfterObjectMap(obj);
+ size_t size = RoundUp(obj->SizeOf<kDefaultVerifyFlags>(), kAlignment);
+ uintptr_t bit_index = live_words_bitmap_->SetLiveWords(obj_begin, size);
+ size_t chunk_idx = (obj_begin - live_words_bitmap_->Begin()) / kOffsetChunkSize;
+ // Compute the bit-index within the chunk-info vector word.
+ bit_index %= kBitsPerVectorWord;
+ size_t first_chunk_portion = std::min(size, (kBitsPerVectorWord - bit_index) * kAlignment);
+
+ chunk_info_vec_[chunk_idx++] += first_chunk_portion;
+ DCHECK_LE(first_chunk_portion, size);
+ for (size -= first_chunk_portion; size > kOffsetChunkSize; size -= kOffsetChunkSize) {
+ DCHECK_EQ(chunk_info_vec_[chunk_idx], 0u);
+ chunk_info_vec_[chunk_idx++] = kOffsetChunkSize;
+ }
+ chunk_info_vec_[chunk_idx] += size;
+ freed_objects_--;
+}
+
+template <bool kUpdateLiveWords>
+void MarkCompact::ScanObject(mirror::Object* obj) {
+ RefFieldsVisitor visitor(this);
+ DCHECK(IsMarked(obj)) << "Scanning marked object " << obj << "\n" << heap_->DumpSpaces();
+ if (kUpdateLiveWords && moving_space_bitmap_->HasAddress(obj)) {
+ UpdateLivenessInfo(obj);
+ }
+ obj->VisitReferences(visitor, visitor);
+}
+
+// Scan anything that's on the mark stack.
+void MarkCompact::ProcessMarkStack() {
+ TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
+ // TODO: try prefetch like in CMS
+ while (!mark_stack_->IsEmpty()) {
+ mirror::Object* obj = mark_stack_->PopBack();
+ DCHECK(obj != nullptr);
+ ScanObject</*kUpdateLiveWords*/ true>(obj);
+ }
+}
+
+void MarkCompact::ExpandMarkStack() {
+ const size_t new_size = mark_stack_->Capacity() * 2;
+ std::vector<StackReference<mirror::Object>> temp(mark_stack_->Begin(),
+ mark_stack_->End());
+ mark_stack_->Resize(new_size);
+ for (auto& ref : temp) {
+ mark_stack_->PushBack(ref.AsMirrorPtr());
+ }
+ DCHECK(!mark_stack_->IsFull());
+}
+
+inline void MarkCompact::PushOnMarkStack(mirror::Object* obj) {
+ if (UNLIKELY(mark_stack_->IsFull())) {
+ ExpandMarkStack();
+ }
+ mark_stack_->PushBack(obj);
+}
+
+inline void MarkCompact::MarkObjectNonNull(mirror::Object* obj,
+ mirror::Object* holder,
+ MemberOffset offset) {
+ DCHECK(obj != nullptr);
+ if (MarkObjectNonNullNoPush</*kParallel*/false>(obj, holder, offset)) {
+ PushOnMarkStack(obj);
+ }
+}
+
+template <bool kParallel>
+inline bool MarkCompact::MarkObjectNonNullNoPush(mirror::Object* obj,
+ mirror::Object* holder,
+ MemberOffset offset) {
+ // We expect most of the referenes to be in bump-pointer space, so try that
+ // first to keep the cost of this function minimal.
+ if (LIKELY(moving_space_bitmap_->HasAddress(obj))) {
+ return kParallel ? !moving_space_bitmap_->AtomicTestAndSet(obj)
+ : !moving_space_bitmap_->Set(obj);
+ } else if (non_moving_space_bitmap_->HasAddress(obj)) {
+ return kParallel ? !non_moving_space_bitmap_->AtomicTestAndSet(obj)
+ : !non_moving_space_bitmap_->Set(obj);
+ } else if (immune_spaces_.ContainsObject(obj)) {
+ DCHECK(IsMarked(obj) != nullptr);
+ return false;
+ } else {
+ // Must be a large-object space, otherwise it's a case of heap corruption.
+ if (!IsAligned<kPageSize>(obj)) {
+ // Objects in large-object space are page aligned. So if we have an object
+ // which doesn't belong to any space and is not page-aligned as well, then
+ // it's memory corruption.
+ // TODO: implement protect/unprotect in bump-pointer space.
+ heap_->GetVerification()->LogHeapCorruption(holder, offset, obj, /*fatal*/ true);
+ }
+ DCHECK_NE(heap_->GetLargeObjectsSpace(), nullptr)
+ << "ref=" << obj
+ << " doesn't belong to any of the spaces and large object space doesn't exist";
+ accounting::LargeObjectBitmap* los_bitmap = heap_->GetLargeObjectsSpace()->GetMarkBitmap();
+ DCHECK(los_bitmap->HasAddress(obj));
+ return kParallel ? !los_bitmap->AtomicTestAndSet(obj)
+ : !los_bitmap->Set(obj);
+ }
+}
+
+inline void MarkCompact::MarkObject(mirror::Object* obj,
+ mirror::Object* holder,
+ MemberOffset offset) {
+ if (obj != nullptr) {
+ MarkObjectNonNull(obj, holder, offset);
+ }
+}
+
+mirror::Object* MarkCompact::MarkObject(mirror::Object* obj) {
+ MarkObject(obj, nullptr, MemberOffset(0));
+ return obj;
+}
+
+void MarkCompact::MarkHeapReference(mirror::HeapReference<mirror::Object>* obj,
+ bool do_atomic_update ATTRIBUTE_UNUSED) {
+ MarkObject(obj->AsMirrorPtr(), nullptr, MemberOffset(0));
+}
+
+void MarkCompact::VisitRoots(mirror::Object*** roots,
+ size_t count,
+ const RootInfo& info) {
+ if (compacting_) {
+ for (size_t i = 0; i < count; ++i) {
+ UpdateRoot(roots[i], info);
+ }
+ } else {
+ for (size_t i = 0; i < count; ++i) {
+ MarkObjectNonNull(*roots[i]);
+ }
+ }
+}
+
+void MarkCompact::VisitRoots(mirror::CompressedReference<mirror::Object>** roots,
+ size_t count,
+ const RootInfo& info) {
+ // TODO: do we need to check if the root is null or not?
+ if (compacting_) {
+ for (size_t i = 0; i < count; ++i) {
+ UpdateRoot(roots[i], info);
+ }
+ } else {
+ for (size_t i = 0; i < count; ++i) {
+ MarkObjectNonNull(roots[i]->AsMirrorPtr());
+ }
+ }
+}
+
+mirror::Object* MarkCompact::IsMarked(mirror::Object* obj) {
+ if (moving_space_bitmap_->HasAddress(obj)) {
+ const bool is_black = reinterpret_cast<uint8_t*>(obj) >= black_allocations_begin_;
+ if (compacting_) {
+ if (is_black) {
+ return PostCompactBlackObjAddr(obj);
+ } else if (live_words_bitmap_->Test(obj)) {
+ return PostCompactOldObjAddr(obj);
+ } else {
+ return nullptr;
+ }
+ }
+ return (is_black || moving_space_bitmap_->Test(obj)) ? obj : nullptr;
+ } else if (non_moving_space_bitmap_->HasAddress(obj)) {
+ return non_moving_space_bitmap_->Test(obj) ? obj : nullptr;
+ } else if (immune_spaces_.ContainsObject(obj)) {
+ return obj;
+ } else {
+ DCHECK(heap_->GetLargeObjectsSpace())
+ << "ref=" << obj
+ << " doesn't belong to any of the spaces and large object space doesn't exist";
+ accounting::LargeObjectBitmap* los_bitmap = heap_->GetLargeObjectsSpace()->GetMarkBitmap();
+ if (los_bitmap->HasAddress(obj)) {
+ DCHECK(IsAligned<kPageSize>(obj));
+ return los_bitmap->Test(obj) ? obj : nullptr;
+ } else {
+ // The given obj is not in any of the known spaces, so return null. This could
+ // happen for instance in interpreter caches wherein a concurrent updation
+ // to the cache could result in obj being a non-reference. This is
+ // tolerable because SweepInterpreterCaches only updates if the given
+ // object has moved, which can't be the case for the non-reference.
+ return nullptr;
+ }
+ }
+}
+
+bool MarkCompact::IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* obj,
+ bool do_atomic_update ATTRIBUTE_UNUSED) {
+ mirror::Object* ref = obj->AsMirrorPtr();
+ if (ref == nullptr) {
+ return true;
+ }
+ return IsMarked(ref);
+}
+
+// Process the 'referent' field in a java.lang.ref.Reference. If the referent
+// has not yet been marked, put it on the appropriate list in the heap for later
+// processing.
+void MarkCompact::DelayReferenceReferent(ObjPtr<mirror::Class> klass,
+ ObjPtr<mirror::Reference> ref) {
+ heap_->GetReferenceProcessor()->DelayReferenceReferent(klass, ref, this);
+}
+
+void MarkCompact::FinishPhase() {
+ bool is_zygote = Runtime::Current()->IsZygote();
+ minor_fault_initialized_ = !is_zygote && uffd_minor_fault_supported_;
+ // When poisoning ObjPtr, we are forced to use buffers for page compaction in
+ // lower 4GB. Now that the usage is done, madvise them. But skip the first
+ // page, which is used by the gc-thread for the next iteration. Otherwise, we
+ // get into a deadlock due to userfault on it in the next iteration. This page
+ // is not consuming any physical memory because we already madvised it above
+ // and then we triggered a read userfault, which maps a special zero-page.
+ if (!minor_fault_initialized_ || !shadow_to_space_map_.IsValid() ||
+ shadow_to_space_map_.Size() < (moving_first_objs_count_ + black_page_count_) * kPageSize) {
+ ZeroAndReleasePages(compaction_buffers_map_.Begin() + kPageSize,
+ compaction_buffers_map_.Size() - kPageSize);
+ } else if (shadow_to_space_map_.Size() == bump_pointer_space_->Capacity()) {
+ // Now that we are going to use minor-faults from next GC cycle, we can
+ // unmap the buffers used by worker threads.
+ compaction_buffers_map_.SetSize(kPageSize);
+ }
+
+ info_map_.MadviseDontNeedAndZero();
+ live_words_bitmap_->ClearBitmap();
+ // TODO: We can clear this bitmap right before compaction pause. But in that
+ // case we need to ensure that we don't assert on this bitmap afterwards.
+ // Also, we would still need to clear it here again as we may have to use the
+ // bitmap for black-allocations (see UpdateMovingSpaceBlackAllocations()).
+ moving_space_bitmap_->Clear();
+
+ if (UNLIKELY(is_zygote && IsValidFd(uffd_))) {
+ heap_->DeleteThreadPool();
+ // This unregisters all ranges as a side-effect.
+ close(uffd_);
+ uffd_ = kFdUnused;
+ uffd_initialized_ = false;
+ }
+ CHECK(mark_stack_->IsEmpty()); // Ensure that the mark stack is empty.
+ mark_stack_->Reset();
+ updated_roots_.clear();
+ class_after_obj_ordered_map_.clear();
+ delete[] moving_pages_status_;
+ linear_alloc_arenas_.clear();
+ {
+ DCHECK_EQ(thread_running_gc_, Thread::Current());
+ ReaderMutexLock mu(thread_running_gc_, *Locks::mutator_lock_);
+ WriterMutexLock mu2(thread_running_gc_, *Locks::heap_bitmap_lock_);
+ heap_->ClearMarkedObjects();
+ }
+ std::swap(moving_to_space_fd_, moving_from_space_fd_);
+ if (IsValidFd(moving_to_space_fd_)) {
+ // Confirm that the memfd to be used on to-space in next GC cycle is empty.
+ struct stat buf;
+ DCHECK_EQ(fstat(moving_to_space_fd_, &buf), 0) << "fstat failed: " << strerror(errno);
+ DCHECK_EQ(buf.st_blocks, 0u);
+ }
+}
+
+} // namespace collector
+} // namespace gc
+} // namespace art
diff --git a/runtime/gc/collector/mark_compact.h b/runtime/gc/collector/mark_compact.h
new file mode 100644
index 0000000..f97c0d7
--- /dev/null
+++ b/runtime/gc/collector/mark_compact.h
@@ -0,0 +1,754 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_GC_COLLECTOR_MARK_COMPACT_H_
+#define ART_RUNTIME_GC_COLLECTOR_MARK_COMPACT_H_
+
+#include <map>
+#include <memory>
+#include <unordered_map>
+#include <unordered_set>
+
+#include "barrier.h"
+#include "base/atomic.h"
+#include "base/gc_visited_arena_pool.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "garbage_collector.h"
+#include "gc/accounting/atomic_stack.h"
+#include "gc/accounting/bitmap-inl.h"
+#include "gc/accounting/heap_bitmap.h"
+#include "gc_root.h"
+#include "immune_spaces.h"
+#include "offsets.h"
+
+namespace art {
+
+namespace mirror {
+class DexCache;
+} // namespace mirror
+
+namespace gc {
+
+class Heap;
+
+namespace space {
+class BumpPointerSpace;
+} // namespace space
+
+namespace collector {
+class MarkCompact final : public GarbageCollector {
+ public:
+ static constexpr size_t kAlignment = kObjectAlignment;
+ static constexpr int kCopyMode = -1;
+ static constexpr int kMinorFaultMode = -2;
+ // Fake file descriptor for fall back mode (when uffd isn't available)
+ static constexpr int kFallbackMode = -3;
+
+ static constexpr int kFdSharedAnon = -1;
+ static constexpr int kFdUnused = -2;
+
+ explicit MarkCompact(Heap* heap);
+
+ ~MarkCompact() {}
+
+ void RunPhases() override REQUIRES(!Locks::mutator_lock_);
+
+ // Updated before (or in) pre-compaction pause and is accessed only in the
+ // pause or during concurrent compaction. The flag is reset after compaction
+ // is completed and never accessed by mutators. Therefore, safe to update
+ // without any memory ordering.
+ bool IsCompacting(Thread* self) const {
+ return compacting_ && self == thread_running_gc_;
+ }
+
+ GcType GetGcType() const override {
+ return kGcTypeFull;
+ }
+
+ CollectorType GetCollectorType() const override {
+ return kCollectorTypeCMC;
+ }
+
+ Barrier& GetBarrier() {
+ return gc_barrier_;
+ }
+
+ mirror::Object* MarkObject(mirror::Object* obj) override
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_);
+
+ void MarkHeapReference(mirror::HeapReference<mirror::Object>* obj,
+ bool do_atomic_update) override
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_);
+
+ void VisitRoots(mirror::Object*** roots,
+ size_t count,
+ const RootInfo& info) override
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_);
+ void VisitRoots(mirror::CompressedReference<mirror::Object>** roots,
+ size_t count,
+ const RootInfo& info) override
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_);
+
+ bool IsNullOrMarkedHeapReference(mirror::HeapReference<mirror::Object>* obj,
+ bool do_atomic_update) override
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_);
+
+ void RevokeAllThreadLocalBuffers() override;
+
+ void DelayReferenceReferent(ObjPtr<mirror::Class> klass,
+ ObjPtr<mirror::Reference> reference) override
+ REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
+
+ mirror::Object* IsMarked(mirror::Object* obj) override
+ REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
+
+ // Perform GC-root updation and heap protection so that during the concurrent
+ // compaction phase we can receive faults and compact the corresponding pages
+ // on the fly. This is performed in a STW pause.
+ void CompactionPause() REQUIRES(Locks::mutator_lock_, !Locks::heap_bitmap_lock_);
+
+ mirror::Object* GetFromSpaceAddrFromBarrier(mirror::Object* old_ref) {
+ CHECK(compacting_);
+ if (live_words_bitmap_->HasAddress(old_ref)) {
+ return GetFromSpaceAddr(old_ref);
+ }
+ return old_ref;
+ }
+ // Called from Heap::PostForkChildAction() for non-zygote processes and from
+ // PrepareForCompaction() for zygote processes. Returns true if uffd was
+ // created or was already done.
+ bool CreateUserfaultfd(bool post_fork);
+
+ // Returns a pair indicating if userfaultfd itself is available (first) and if
+ // so then whether its minor-fault feature is available or not (second).
+ static std::pair<bool, bool> GetUffdAndMinorFault();
+
+ // Add linear-alloc space data when a new space is added to
+ // GcVisitedArenaPool, which mostly happens only once.
+ void AddLinearAllocSpaceData(uint8_t* begin, size_t len);
+
+ // In copy-mode of userfaultfd, we don't need to reach a 'processed' state as
+ // it's given that processing thread also copies the page, thereby mapping it.
+ // The order is important as we may treat them as integers.
+ enum class PageState : uint8_t {
+ kUnprocessed = 0, // Not processed yet
+ kProcessing = 1, // Being processed by GC thread and will not be mapped
+ kProcessed = 2, // Processed but not mapped
+ kProcessingAndMapping = 3, // Being processed by GC or mutator and will be mapped
+ kMutatorProcessing = 4, // Being processed by mutator thread
+ kProcessedAndMapping = 5 // Processed and will be mapped
+ };
+
+ private:
+ using ObjReference = mirror::ObjectReference</*kPoisonReferences*/ false, mirror::Object>;
+ // Number of bits (live-words) covered by a single chunk-info (below)
+ // entry/word.
+ // TODO: Since popcount is performed usomg SIMD instructions, we should
+ // consider using 128-bit in order to halve the chunk-info size.
+ static constexpr uint32_t kBitsPerVectorWord = kBitsPerIntPtrT;
+ static constexpr uint32_t kOffsetChunkSize = kBitsPerVectorWord * kAlignment;
+ static_assert(kOffsetChunkSize < kPageSize);
+ // Bitmap with bits corresponding to every live word set. For an object
+ // which is 4 words in size will have the corresponding 4 bits set. This is
+ // required for efficient computation of new-address (post-compaction) from
+ // the given old-address (pre-compaction).
+ template <size_t kAlignment>
+ class LiveWordsBitmap : private accounting::MemoryRangeBitmap<kAlignment> {
+ using Bitmap = accounting::Bitmap;
+ using MemRangeBitmap = accounting::MemoryRangeBitmap<kAlignment>;
+
+ public:
+ static_assert(IsPowerOfTwo(kBitsPerVectorWord));
+ static_assert(IsPowerOfTwo(Bitmap::kBitsPerBitmapWord));
+ static_assert(kBitsPerVectorWord >= Bitmap::kBitsPerBitmapWord);
+ static constexpr uint32_t kBitmapWordsPerVectorWord =
+ kBitsPerVectorWord / Bitmap::kBitsPerBitmapWord;
+ static_assert(IsPowerOfTwo(kBitmapWordsPerVectorWord));
+ static LiveWordsBitmap* Create(uintptr_t begin, uintptr_t end);
+
+ // Return offset (within the indexed chunk-info) of the nth live word.
+ uint32_t FindNthLiveWordOffset(size_t chunk_idx, uint32_t n) const;
+ // Sets all bits in the bitmap corresponding to the given range. Also
+ // returns the bit-index of the first word.
+ ALWAYS_INLINE uintptr_t SetLiveWords(uintptr_t begin, size_t size);
+ // Count number of live words upto the given bit-index. This is to be used
+ // to compute the post-compact address of an old reference.
+ ALWAYS_INLINE size_t CountLiveWordsUpto(size_t bit_idx) const;
+ // Call 'visitor' for every stride of contiguous marked bits in the live-words
+ // bitmap, starting from begin_bit_idx. Only visit 'bytes' live bytes or
+ // until 'end', whichever comes first.
+ // Visitor is called with index of the first marked bit in the stride,
+ // stride size and whether it's the last stride in the given range or not.
+ template <typename Visitor>
+ ALWAYS_INLINE void VisitLiveStrides(uintptr_t begin_bit_idx,
+ uint8_t* end,
+ const size_t bytes,
+ Visitor&& visitor) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // Count the number of live bytes in the given vector entry.
+ size_t LiveBytesInBitmapWord(size_t chunk_idx) const;
+ void ClearBitmap() { Bitmap::Clear(); }
+ ALWAYS_INLINE uintptr_t Begin() const { return MemRangeBitmap::CoverBegin(); }
+ ALWAYS_INLINE bool HasAddress(mirror::Object* obj) const {
+ return MemRangeBitmap::HasAddress(reinterpret_cast<uintptr_t>(obj));
+ }
+ ALWAYS_INLINE bool Test(uintptr_t bit_index) const {
+ return Bitmap::TestBit(bit_index);
+ }
+ ALWAYS_INLINE bool Test(mirror::Object* obj) const {
+ return MemRangeBitmap::Test(reinterpret_cast<uintptr_t>(obj));
+ }
+ ALWAYS_INLINE uintptr_t GetWord(size_t index) const {
+ static_assert(kBitmapWordsPerVectorWord == 1);
+ return Bitmap::Begin()[index * kBitmapWordsPerVectorWord];
+ }
+ };
+
+ // For a given object address in pre-compact space, return the corresponding
+ // address in the from-space, where heap pages are relocated in the compaction
+ // pause.
+ mirror::Object* GetFromSpaceAddr(mirror::Object* obj) const {
+ DCHECK(live_words_bitmap_->HasAddress(obj)) << " obj=" << obj;
+ return reinterpret_cast<mirror::Object*>(reinterpret_cast<uintptr_t>(obj)
+ + from_space_slide_diff_);
+ }
+
+ // Verifies that that given object reference refers to a valid object.
+ // Otherwise fataly dumps logs, including those from callback.
+ template <typename Callback>
+ void VerifyObject(mirror::Object* ref, Callback& callback) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // Check if the obj is within heap and has a klass which is likely to be valid
+ // mirror::Class.
+ bool IsValidObject(mirror::Object* obj) const REQUIRES_SHARED(Locks::mutator_lock_);
+ void InitializePhase();
+ void FinishPhase() REQUIRES(!Locks::mutator_lock_, !Locks::heap_bitmap_lock_);
+ void MarkingPhase() REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::heap_bitmap_lock_);
+ void CompactionPhase() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void SweepSystemWeaks(Thread* self, Runtime* runtime, const bool paused)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::heap_bitmap_lock_);
+ // Update the reference at given offset in the given object with post-compact
+ // address.
+ ALWAYS_INLINE void UpdateRef(mirror::Object* obj, MemberOffset offset)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Verify that the gc-root is updated only once. Returns false if the update
+ // shouldn't be done.
+ ALWAYS_INLINE bool VerifyRootSingleUpdate(void* root,
+ mirror::Object* old_ref,
+ const RootInfo& info)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // Update the given root with post-compact address.
+ ALWAYS_INLINE void UpdateRoot(mirror::CompressedReference<mirror::Object>* root,
+ const RootInfo& info = RootInfo(RootType::kRootUnknown))
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ ALWAYS_INLINE void UpdateRoot(mirror::Object** root,
+ const RootInfo& info = RootInfo(RootType::kRootUnknown))
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // Given the pre-compact address, the function returns the post-compact
+ // address of the given object.
+ ALWAYS_INLINE mirror::Object* PostCompactAddress(mirror::Object* old_ref) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // Compute post-compact address of an object in moving space. This function
+ // assumes that old_ref is in moving space.
+ ALWAYS_INLINE mirror::Object* PostCompactAddressUnchecked(mirror::Object* old_ref) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // Compute the new address for an object which was allocated prior to starting
+ // this GC cycle.
+ ALWAYS_INLINE mirror::Object* PostCompactOldObjAddr(mirror::Object* old_ref) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // Compute the new address for an object which was black allocated during this
+ // GC cycle.
+ ALWAYS_INLINE mirror::Object* PostCompactBlackObjAddr(mirror::Object* old_ref) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // Identify immune spaces and reset card-table, mod-union-table, and mark
+ // bitmaps.
+ void BindAndResetBitmaps() REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_);
+
+ // Perform one last round of marking, identifying roots from dirty cards
+ // during a stop-the-world (STW) pause.
+ void MarkingPause() REQUIRES(Locks::mutator_lock_, !Locks::heap_bitmap_lock_);
+ // Perform stop-the-world pause prior to concurrent compaction.
+ // Updates GC-roots and protects heap so that during the concurrent
+ // compaction phase we can receive faults and compact the corresponding pages
+ // on the fly.
+ void PreCompactionPhase() REQUIRES(Locks::mutator_lock_);
+ // Compute offsets (in chunk_info_vec_) and other data structures required
+ // during concurrent compaction.
+ void PrepareForCompaction() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Copy kPageSize live bytes starting from 'offset' (within the moving space),
+ // which must be within 'obj', into the kPageSize sized memory pointed by 'addr'.
+ // Then update the references within the copied objects. The boundary objects are
+ // partially updated such that only the references that lie in the page are updated.
+ // This is necessary to avoid cascading userfaults.
+ void CompactPage(mirror::Object* obj, uint32_t offset, uint8_t* addr, bool needs_memset_zero)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // Compact the bump-pointer space. Pass page that should be used as buffer for
+ // userfaultfd.
+ template <int kMode>
+ void CompactMovingSpace(uint8_t* page) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Compact the given page as per func and change its state. Also map/copy the
+ // page, if required.
+ template <int kMode, typename CompactionFn>
+ ALWAYS_INLINE void DoPageCompactionWithStateChange(size_t page_idx,
+ size_t status_arr_len,
+ uint8_t* to_space_page,
+ uint8_t* page,
+ CompactionFn func)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Update all the objects in the given non-moving space page. 'first' object
+ // could have started in some preceding page.
+ void UpdateNonMovingPage(mirror::Object* first, uint8_t* page)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // Update all the references in the non-moving space.
+ void UpdateNonMovingSpace() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // For all the pages in non-moving space, find the first object that overlaps
+ // with the pages' start address, and store in first_objs_non_moving_space_ array.
+ void InitNonMovingSpaceFirstObjects() REQUIRES_SHARED(Locks::mutator_lock_);
+ // In addition to the first-objects for every post-compact moving space page,
+ // also find offsets within those objects from where the contents should be
+ // copied to the page. The offsets are relative to the moving-space's
+ // beginning. Store the computed first-object and offset in first_objs_moving_space_
+ // and pre_compact_offset_moving_space_ respectively.
+ void InitMovingSpaceFirstObjects(const size_t vec_len) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Gather the info related to black allocations from bump-pointer space to
+ // enable concurrent sliding of these pages.
+ void UpdateMovingSpaceBlackAllocations() REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
+ // Update first-object info from allocation-stack for non-moving space black
+ // allocations.
+ void UpdateNonMovingSpaceBlackAllocations() REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
+
+ // Slides (retain the empty holes, which are usually part of some in-use TLAB)
+ // black page in the moving space. 'first_obj' is the object that overlaps with
+ // the first byte of the page being slid. pre_compact_page is the pre-compact
+ // address of the page being slid. 'page_idx' is used to fetch the first
+ // allocated chunk's size and next page's first_obj. 'dest' is the kPageSize
+ // sized memory where the contents would be copied.
+ void SlideBlackPage(mirror::Object* first_obj,
+ const size_t page_idx,
+ uint8_t* const pre_compact_page,
+ uint8_t* dest,
+ bool needs_memset_zero) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Perform reference-processing and the likes before sweeping the non-movable
+ // spaces.
+ void ReclaimPhase() REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::heap_bitmap_lock_);
+
+ // Mark GC-roots (except from immune spaces and thread-stacks) during a STW pause.
+ void ReMarkRoots(Runtime* runtime) REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
+ // Concurrently mark GC-roots, except from immune spaces.
+ void MarkRoots(VisitRootFlags flags) REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_);
+ // Collect thread stack roots via a checkpoint.
+ void MarkRootsCheckpoint(Thread* self, Runtime* runtime) REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_);
+ // Second round of concurrent marking. Mark all gray objects that got dirtied
+ // since the first round.
+ void PreCleanCards() REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);
+
+ void MarkNonThreadRoots(Runtime* runtime) REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_);
+ void MarkConcurrentRoots(VisitRootFlags flags, Runtime* runtime)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);
+
+ // Traverse through the reachable objects and mark them.
+ void MarkReachableObjects() REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_);
+ // Scan (only) immune spaces looking for references into the garbage collected
+ // spaces.
+ void UpdateAndMarkModUnion() REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_);
+ // Scan mod-union and card tables, covering all the spaces, to identify dirty objects.
+ // These are in 'minimum age' cards, which is 'kCardAged' in case of concurrent (second round)
+ // marking and kCardDirty during the STW pause.
+ void ScanDirtyObjects(bool paused, uint8_t minimum_age) REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_);
+ // Recursively mark dirty objects. Invoked both concurrently as well in a STW
+ // pause in PausePhase().
+ void RecursiveMarkDirtyObjects(bool paused, uint8_t minimum_age)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_);
+ // Go through all the objects in the mark-stack until it's empty.
+ void ProcessMarkStack() override REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_);
+ void ExpandMarkStack() REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_);
+
+ // Scan object for references. If kUpdateLivewords is true then set bits in
+ // the live-words bitmap and add size to chunk-info.
+ template <bool kUpdateLiveWords>
+ void ScanObject(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_);
+ // Push objects to the mark-stack right after successfully marking objects.
+ void PushOnMarkStack(mirror::Object* obj)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_);
+
+ // Update the live-words bitmap as well as add the object size to the
+ // chunk-info vector. Both are required for computation of post-compact addresses.
+ // Also updates freed_objects_ counter.
+ void UpdateLivenessInfo(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void ProcessReferences(Thread* self)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::heap_bitmap_lock_);
+
+ void MarkObjectNonNull(mirror::Object* obj,
+ mirror::Object* holder = nullptr,
+ MemberOffset offset = MemberOffset(0))
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_);
+
+ void MarkObject(mirror::Object* obj, mirror::Object* holder, MemberOffset offset)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_);
+
+ template <bool kParallel>
+ bool MarkObjectNonNullNoPush(mirror::Object* obj,
+ mirror::Object* holder = nullptr,
+ MemberOffset offset = MemberOffset(0))
+ REQUIRES(Locks::heap_bitmap_lock_)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void Sweep(bool swap_bitmaps) REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_);
+ void SweepLargeObjects(bool swap_bitmaps) REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::heap_bitmap_lock_);
+
+ // Perform all kernel operations required for concurrent compaction. Includes
+ // mremap to move pre-compact pages to from-space, followed by userfaultfd
+ // registration on the moving space and linear-alloc.
+ void KernelPreparation();
+ // Called by KernelPreparation() for every memory range being prepared.
+ void KernelPrepareRange(uint8_t* to_addr,
+ uint8_t* from_addr,
+ size_t map_size,
+ size_t uffd_size,
+ int fd,
+ int uffd_mode,
+ uint8_t* shadow_addr = nullptr);
+ // Unregister given range from userfaultfd.
+ void UnregisterUffd(uint8_t* start, size_t len);
+
+ // Called by thread-pool workers to read uffd_ and process fault events.
+ template <int kMode>
+ void ConcurrentCompaction(uint8_t* buf) REQUIRES_SHARED(Locks::mutator_lock_);
+ // Called by thread-pool workers to compact and copy/map the fault page in
+ // moving space.
+ template <int kMode, typename ZeropageType, typename CopyType>
+ void ConcurrentlyProcessMovingPage(ZeropageType& zeropage_ioctl,
+ CopyType& copy_ioctl,
+ uint8_t* fault_page,
+ uint8_t* buf,
+ size_t nr_moving_space_used_pages)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // Called by thread-pool workers to process and copy/map the fault page in
+ // linear-alloc.
+ template <int kMode, typename ZeropageType, typename CopyType>
+ void ConcurrentlyProcessLinearAllocPage(ZeropageType& zeropage_ioctl,
+ CopyType& copy_ioctl,
+ uint8_t* fault_page,
+ bool is_minor_fault)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Process concurrently all the pages in linear-alloc. Called by gc-thread.
+ void ProcessLinearAlloc() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Returns true if the moving space can be compacted using uffd's minor-fault
+ // feature.
+ bool CanCompactMovingSpaceWithMinorFault();
+
+ void FreeFromSpacePages(size_t cur_page_idx) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Maps processed pages (from moving space and linear-alloc) for uffd's
+ // minor-fault feature. We try to 'claim' all processed (and unmapped) pages
+ // contiguous to 'to_space_start'.
+ // kFirstPageMapping indicates if the first page is already claimed or not. It
+ // also indicates that the ioctl must succeed in mapping the first page.
+ template <bool kFirstPageMapping>
+ void MapProcessedPages(uint8_t* to_space_start,
+ Atomic<PageState>* state_arr,
+ size_t arr_idx,
+ size_t arr_len) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ bool IsValidFd(int fd) const { return fd >= 0; }
+ // Add/update <class, obj> pair if class > obj and obj is the lowest address
+ // object of class.
+ ALWAYS_INLINE void UpdateClassAfterObjectMap(mirror::Object* obj)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Updates 'class_after_obj_map_' map by updating the keys (class) with its
+ // highest-address super-class (obtained from 'super_class_after_class_map_'),
+ // if there is any. This is to ensure we don't free from-space pages before
+ // the lowest-address obj is compacted.
+ void UpdateClassAfterObjMap();
+
+ // Buffers, one per worker thread + gc-thread, to be used when
+ // kObjPtrPoisoning == true as in that case we can't have the buffer on the
+ // stack. The first page of the buffer is assigned to
+ // conc_compaction_termination_page_. A read access to this page signals
+ // termination of concurrent compaction by making worker threads terminate the
+ // userfaultfd read loop.
+ MemMap compaction_buffers_map_;
+ // For checkpoints
+ Barrier gc_barrier_;
+ // Every object inside the immune spaces is assumed to be marked.
+ ImmuneSpaces immune_spaces_;
+ // Required only when mark-stack is accessed in shared mode, which happens
+ // when collecting thread-stack roots using checkpoint.
+ Mutex mark_stack_lock_;
+ accounting::ObjectStack* mark_stack_;
+ // Special bitmap wherein all the bits corresponding to an object are set.
+ // TODO: make LiveWordsBitmap encapsulated in this class rather than a
+ // pointer. We tend to access its members in performance-sensitive
+ // code-path. Also, use a single MemMap for all the GC's data structures,
+ // which we will clear in the end. This would help in limiting the number of
+ // VMAs that get created in the kernel.
+ std::unique_ptr<LiveWordsBitmap<kAlignment>> live_words_bitmap_;
+ // Track GC-roots updated so far in a GC-cycle. This is to confirm that no
+ // GC-root is updated twice.
+ // TODO: Must be replaced with an efficient mechanism eventually. Or ensure
+ // that double updation doesn't happen in the first place.
+ std::unordered_set<void*> updated_roots_;
+ MemMap from_space_map_;
+ MemMap shadow_to_space_map_;
+ // Any array of live-bytes in logical chunks of kOffsetChunkSize size
+ // in the 'to-be-compacted' space.
+ MemMap info_map_;
+
+ class LessByArenaAddr {
+ public:
+ bool operator()(const TrackedArena* a, const TrackedArena* b) const {
+ return std::less<uint8_t*>{}(a->Begin(), b->Begin());
+ }
+ };
+
+ // Map of arenas allocated in LinearAlloc arena-pool and last non-zero page,
+ // captured during compaction pause for concurrent updates.
+ std::map<const TrackedArena*, uint8_t*, LessByArenaAddr> linear_alloc_arenas_;
+ // Set of PageStatus arrays, one per arena-pool space. It's extremely rare to
+ // have more than one, but this is to be ready for the worst case.
+ class LinearAllocSpaceData {
+ public:
+ LinearAllocSpaceData(MemMap&& shadow,
+ MemMap&& page_status_map,
+ uint8_t* begin,
+ uint8_t* end,
+ bool already_shared)
+ : shadow_(std::move(shadow)),
+ page_status_map_(std::move(page_status_map)),
+ begin_(begin),
+ end_(end),
+ already_shared_(already_shared) {}
+
+ MemMap shadow_;
+ MemMap page_status_map_;
+ uint8_t* begin_;
+ uint8_t* end_;
+ // Indicates if the linear-alloc is already MAP_SHARED.
+ bool already_shared_;
+ };
+
+ std::vector<LinearAllocSpaceData> linear_alloc_spaces_data_;
+
+ class ObjReferenceHash {
+ public:
+ uint32_t operator()(const ObjReference& ref) const {
+ return ref.AsVRegValue() >> kObjectAlignmentShift;
+ }
+ };
+
+ class ObjReferenceEqualFn {
+ public:
+ bool operator()(const ObjReference& a, const ObjReference& b) const {
+ return a.AsMirrorPtr() == b.AsMirrorPtr();
+ }
+ };
+
+ class LessByObjReference {
+ public:
+ bool operator()(const ObjReference& a, const ObjReference& b) const {
+ return std::less<mirror::Object*>{}(a.AsMirrorPtr(), b.AsMirrorPtr());
+ }
+ };
+
+ // Data structures used to track objects whose layout information is stored in later
+ // allocated classes (at higher addresses). We must be careful not to free the
+ // corresponding from-space pages prematurely.
+ using ObjObjOrderedMap = std::map<ObjReference, ObjReference, LessByObjReference>;
+ using ObjObjUnorderedMap =
+ std::unordered_map<ObjReference, ObjReference, ObjReferenceHash, ObjReferenceEqualFn>;
+ // Unordered map of <K, S> such that the class K (in moving space) has kClassWalkSuper
+ // in reference bitmap and S is its highest address super class.
+ ObjObjUnorderedMap super_class_after_class_hash_map_;
+ // Unordered map of <K, V> such that the class K (in moving space) is after its objects
+ // or would require iterating super-class hierarchy when visiting references. And V is
+ // its lowest address object (in moving space).
+ ObjObjUnorderedMap class_after_obj_hash_map_;
+ // Ordered map constructed before starting compaction using the above two maps. Key is a
+ // class (or super-class) which is higher in address order than some of its object(s) and
+ // value is the corresponding object with lowest address.
+ ObjObjOrderedMap class_after_obj_ordered_map_;
+ // Since the compaction is done in reverse, we use a reverse iterator. It is maintained
+ // either at the pair whose class is lower than the first page to be freed, or at the
+ // pair whose object is not yet compacted.
+ ObjObjOrderedMap::const_reverse_iterator class_after_obj_iter_;
+ // Cached reference to the last class which has kClassWalkSuper in reference
+ // bitmap but has all its super classes lower address order than itself.
+ mirror::Class* walk_super_class_cache_;
+ // Used by FreeFromSpacePages() for maintaining markers in the moving space for
+ // how far the pages have been reclaimed/checked.
+ size_t last_checked_reclaim_page_idx_;
+ uint8_t* last_reclaimed_page_;
+
+ space::ContinuousSpace* non_moving_space_;
+ space::BumpPointerSpace* const bump_pointer_space_;
+ // The main space bitmap
+ accounting::ContinuousSpaceBitmap* const moving_space_bitmap_;
+ accounting::ContinuousSpaceBitmap* non_moving_space_bitmap_;
+ Thread* thread_running_gc_;
+ // Array of pages' compaction status.
+ Atomic<PageState>* moving_pages_status_;
+ size_t vector_length_;
+ size_t live_stack_freeze_size_;
+
+ // For every page in the to-space (post-compact heap) we need to know the
+ // first object from which we must compact and/or update references. This is
+ // for both non-moving and moving space. Additionally, for the moving-space,
+ // we also need the offset within the object from where we need to start
+ // copying.
+ // chunk_info_vec_ holds live bytes for chunks during marking phase. After
+ // marking we perform an exclusive scan to compute offset for every chunk.
+ uint32_t* chunk_info_vec_;
+ // For pages before black allocations, pre_compact_offset_moving_space_[i]
+ // holds offset within the space from where the objects need to be copied in
+ // the ith post-compact page.
+ // Otherwise, black_alloc_pages_first_chunk_size_[i] holds the size of first
+ // non-empty chunk in the ith black-allocations page.
+ union {
+ uint32_t* pre_compact_offset_moving_space_;
+ uint32_t* black_alloc_pages_first_chunk_size_;
+ };
+ // first_objs_moving_space_[i] is the pre-compact address of the object which
+ // would overlap with the starting boundary of the ith post-compact page.
+ ObjReference* first_objs_moving_space_;
+ // First object for every page. It could be greater than the page's start
+ // address, or null if the page is empty.
+ ObjReference* first_objs_non_moving_space_;
+ size_t non_moving_first_objs_count_;
+ // Length of first_objs_moving_space_ and pre_compact_offset_moving_space_
+ // arrays. Also the number of pages which are to be compacted.
+ size_t moving_first_objs_count_;
+ // Number of pages containing black-allocated objects, indicating number of
+ // pages to be slid.
+ size_t black_page_count_;
+
+ uint8_t* from_space_begin_;
+ // moving-space's end pointer at the marking pause. All allocations beyond
+ // this will be considered black in the current GC cycle. Aligned up to page
+ // size.
+ uint8_t* black_allocations_begin_;
+ // End of compacted space. Use for computing post-compact addr of black
+ // allocated objects. Aligned up to page size.
+ uint8_t* post_compact_end_;
+ // Cache (black_allocations_begin_ - post_compact_end_) for post-compact
+ // address computations.
+ ptrdiff_t black_objs_slide_diff_;
+ // Cache (from_space_begin_ - bump_pointer_space_->Begin()) so that we can
+ // compute from-space address of a given pre-comapct addr efficiently.
+ ptrdiff_t from_space_slide_diff_;
+
+ // TODO: Remove once an efficient mechanism to deal with double root updation
+ // is incorporated.
+ void* stack_high_addr_;
+ void* stack_low_addr_;
+
+ uint8_t* conc_compaction_termination_page_;
+
+ PointerSize pointer_size_;
+ // Number of objects freed during this GC in moving space. It is decremented
+ // every time an object is discovered. And total-object count is added to it
+ // in MarkingPause(). It reaches the correct count only once the marking phase
+ // is completed.
+ int32_t freed_objects_;
+ // memfds for moving space for using userfaultfd's minor-fault feature.
+ // Initialized to kFdUnused to indicate that mmap should be MAP_PRIVATE in
+ // KernelPrepareRange().
+ int moving_to_space_fd_;
+ int moving_from_space_fd_;
+ // Userfault file descriptor, accessed only by the GC itself.
+ // kFallbackMode value indicates that we are in the fallback mode.
+ int uffd_;
+ // Used to exit from compaction loop at the end of concurrent compaction
+ uint8_t thread_pool_counter_;
+ std::atomic<uint8_t> compaction_in_progress_count_;
+ // True while compacting.
+ bool compacting_;
+ // Flag indicating whether one-time uffd initialization has been done. It will
+ // be false on the first GC for non-zygote processes, and always for zygote.
+ // Its purpose is to minimize the userfaultfd overhead to the minimal in
+ // Heap::PostForkChildAction() as it's invoked in app startup path. With
+ // this, we register the compaction-termination page on the first GC.
+ bool uffd_initialized_;
+ // Flag indicating if userfaultfd supports minor-faults. Set appropriately in
+ // CreateUserfaultfd(), where we get this information from the kernel.
+ const bool uffd_minor_fault_supported_;
+ // For non-zygote processes this flag indicates if the spaces are ready to
+ // start using userfaultfd's minor-fault feature. This initialization involves
+ // starting to use shmem (memfd_create) for the userfaultfd protected spaces.
+ bool minor_fault_initialized_;
+ // Set to true when linear-alloc can start mapping with MAP_SHARED. Set on
+ // non-zygote processes during first GC, which sets up everyting for using
+ // minor-fault from next GC.
+ bool map_linear_alloc_shared_;
+
+ class VerifyRootMarkedVisitor;
+ class ScanObjectVisitor;
+ class CheckpointMarkThreadRoots;
+ template<size_t kBufferSize> class ThreadRootsVisitor;
+ class CardModifiedVisitor;
+ class RefFieldsVisitor;
+ template <bool kCheckBegin, bool kCheckEnd> class RefsUpdateVisitor;
+ class ArenaPoolPageUpdater;
+ class ClassLoaderRootsUpdater;
+ class LinearAllocPageUpdater;
+ class ImmuneSpaceUpdateObjVisitor;
+ class ConcurrentCompactionGcTask;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(MarkCompact);
+};
+
+std::ostream& operator<<(std::ostream& os, MarkCompact::PageState value);
+
+} // namespace collector
+} // namespace gc
+} // namespace art
+
+#endif // ART_RUNTIME_GC_COLLECTOR_MARK_COMPACT_H_
diff --git a/runtime/gc/collector/mark_sweep.cc b/runtime/gc/collector/mark_sweep.cc
index bd5ce37..8e3899a 100644
--- a/runtime/gc/collector/mark_sweep.cc
+++ b/runtime/gc/collector/mark_sweep.cc
@@ -340,6 +340,8 @@
Thread* const self = Thread::Current();
// Process the references concurrently.
ProcessReferences(self);
+ // There is no need to sweep interpreter caches as this GC doesn't move
+ // objects and hence would be a nop.
SweepSystemWeaks(self);
Runtime* const runtime = Runtime::Current();
runtime->AllowNewSystemWeaks();
@@ -1127,7 +1129,9 @@
TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
// Verify system weaks, uses a special object visitor which returns the input object.
VerifySystemWeakVisitor visitor(this);
- Runtime::Current()->SweepSystemWeaks(&visitor);
+ Runtime* runtime = Runtime::Current();
+ runtime->SweepSystemWeaks(&visitor);
+ runtime->GetThreadList()->SweepInterpreterCaches(&visitor);
}
class MarkSweep::CheckpointMarkThreadRoots : public Closure, public RootVisitor {
diff --git a/runtime/gc/collector/mark_sweep.h b/runtime/gc/collector/mark_sweep.h
index 6af7c54..12fd7f9 100644
--- a/runtime/gc/collector/mark_sweep.h
+++ b/runtime/gc/collector/mark_sweep.h
@@ -181,7 +181,7 @@
REQUIRES_SHARED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
void VerifySystemWeaks()
- REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
+ REQUIRES(Locks::mutator_lock_) REQUIRES_SHARED(Locks::heap_bitmap_lock_);
// Verify that an object is live, either in a live bitmap or in the allocation stack.
void VerifyIsLive(const mirror::Object* obj)
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index 53b0604..acd4807 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -500,7 +500,9 @@
void SemiSpace::SweepSystemWeaks() {
TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
- Runtime::Current()->SweepSystemWeaks(this);
+ Runtime* runtime = Runtime::Current();
+ runtime->SweepSystemWeaks(this);
+ runtime->GetThreadList()->SweepInterpreterCaches(this);
}
bool SemiSpace::ShouldSweepSpace(space::ContinuousSpace* space) const {
diff --git a/runtime/gc/collector/semi_space.h b/runtime/gc/collector/semi_space.h
index 245ea10..6d3ac08 100644
--- a/runtime/gc/collector/semi_space.h
+++ b/runtime/gc/collector/semi_space.h
@@ -143,7 +143,7 @@
void SweepLargeObjects(bool swap_bitmaps) REQUIRES(Locks::heap_bitmap_lock_);
void SweepSystemWeaks()
- REQUIRES_SHARED(Locks::heap_bitmap_lock_, Locks::mutator_lock_);
+ REQUIRES_SHARED(Locks::heap_bitmap_lock_) REQUIRES(Locks::mutator_lock_);
void VisitRoots(mirror::Object*** roots, size_t count, const RootInfo& info) override
REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_);
diff --git a/runtime/gc/collector_type.h b/runtime/gc/collector_type.h
index 9c99964..c20e3a7 100644
--- a/runtime/gc/collector_type.h
+++ b/runtime/gc/collector_type.h
@@ -30,6 +30,8 @@
kCollectorTypeMS,
// Concurrent mark-sweep.
kCollectorTypeCMS,
+ // Concurrent mark-compact.
+ kCollectorTypeCMC,
// Semi-space / mark-sweep hybrid, enables compaction.
kCollectorTypeSS,
// Heap trimming collector, doesn't do any actual collecting.
@@ -63,12 +65,13 @@
std::ostream& operator<<(std::ostream& os, CollectorType collector_type);
static constexpr CollectorType kCollectorTypeDefault =
-#if ART_DEFAULT_GC_TYPE_IS_CMS
- kCollectorTypeCMS
+#if ART_DEFAULT_GC_TYPE_IS_CMC
+ kCollectorTypeCMC
#elif ART_DEFAULT_GC_TYPE_IS_SS
kCollectorTypeSS
-#else
+#elif ART_DEFAULT_GC_TYPE_IS_CMS
kCollectorTypeCMS
+#else
#error "ART default GC type must be set"
#endif
; // NOLINT [whitespace/semicolon] [5]
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index 9e1524e..922b588 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -209,13 +209,12 @@
}
// IsGcConcurrent() isn't known at compile time so we can optimize by not checking it for the
// BumpPointer or TLAB allocators. This is nice since it allows the entire if statement to be
- // optimized out. And for the other allocators, AllocatorMayHaveConcurrentGC is a constant
- // since the allocator_type should be constant propagated.
- if (AllocatorMayHaveConcurrentGC(allocator) && IsGcConcurrent()
- && UNLIKELY(ShouldConcurrentGCForJava(new_num_bytes_allocated))) {
+ // optimized out.
+ if (IsGcConcurrent() && UNLIKELY(ShouldConcurrentGCForJava(new_num_bytes_allocated))) {
need_gc = true;
}
GetMetrics()->TotalBytesAllocated()->Add(bytes_tl_bulk_allocated);
+ GetMetrics()->TotalBytesAllocatedDelta()->Add(bytes_tl_bulk_allocated);
}
}
if (kIsDebugBuild && Runtime::Current()->IsStarted()) {
@@ -442,7 +441,7 @@
return byte_count >= large_object_threshold_ && (c->IsPrimitiveArray() || c->IsStringClass());
}
-inline bool Heap::IsOutOfMemoryOnAllocation(AllocatorType allocator_type,
+inline bool Heap::IsOutOfMemoryOnAllocation(AllocatorType allocator_type ATTRIBUTE_UNUSED,
size_t alloc_size,
bool grow) {
size_t old_target = target_footprint_.load(std::memory_order_relaxed);
@@ -457,7 +456,7 @@
return true;
}
// We are between target_footprint_ and growth_limit_ .
- if (AllocatorMayHaveConcurrentGC(allocator_type) && IsGcConcurrent()) {
+ if (IsGcConcurrent()) {
return false;
} else {
if (grow) {
diff --git a/runtime/gc/heap-visit-objects-inl.h b/runtime/gc/heap-visit-objects-inl.h
index e20d981..a235c44 100644
--- a/runtime/gc/heap-visit-objects-inl.h
+++ b/runtime/gc/heap-visit-objects-inl.h
@@ -118,7 +118,7 @@
// For speed reasons, only perform it when Rosalloc could possibly be used.
// (Disabled for read barriers because it never uses Rosalloc).
// (See the DCHECK in RosAllocSpace constructor).
- if (!kUseReadBarrier) {
+ if (!gUseReadBarrier) {
// Rosalloc has a race in allocation. Objects can be written into the allocation
// stack before their header writes are visible to this thread.
// See b/28790624 for more details.
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 8407ba4..b433623 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -21,10 +21,6 @@
#if defined(__BIONIC__) || defined(__GLIBC__)
#include <malloc.h> // For mallinfo()
#endif
-#if defined(__BIONIC__) && defined(ART_TARGET)
-#include <linux/userfaultfd.h>
-#include <sys/ioctl.h>
-#endif
#include <memory>
#include <random>
#include <unistd.h>
@@ -61,6 +57,7 @@
#include "gc/accounting/remembered_set.h"
#include "gc/accounting/space_bitmap-inl.h"
#include "gc/collector/concurrent_copying.h"
+#include "gc/collector/mark_compact.h"
#include "gc/collector/mark_sweep.h"
#include "gc/collector/partial_mark_sweep.h"
#include "gc/collector/semi_space.h"
@@ -106,6 +103,7 @@
#include "runtime.h"
#include "javaheapprof/javaheapsampler.h"
#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
#include "thread_list.h"
#include "verify_object-inl.h"
#include "well_known_classes.h"
@@ -339,6 +337,7 @@
// this one.
process_state_update_lock_("process state update lock", kPostMonitorLock),
min_foreground_target_footprint_(0),
+ min_foreground_concurrent_start_bytes_(0),
concurrent_start_bytes_(std::numeric_limits<size_t>::max()),
total_bytes_freed_ever_(0),
total_objects_freed_ever_(0),
@@ -410,7 +409,6 @@
backtrace_lock_(nullptr),
seen_backtrace_count_(0u),
unique_backtrace_count_(0u),
- uffd_(-1),
gc_disabled_for_shutdown_(false),
dump_region_info_before_gc_(dump_region_info_before_gc),
dump_region_info_after_gc_(dump_region_info_after_gc),
@@ -421,7 +419,8 @@
if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
LOG(INFO) << "Heap() entering";
}
- if (kUseReadBarrier) {
+ LOG(INFO) << "Using " << foreground_collector_type_ << " GC.";
+ if (gUseReadBarrier) {
CHECK_EQ(foreground_collector_type_, kCollectorTypeCC);
CHECK_EQ(background_collector_type_, kCollectorTypeCCBackground);
} else if (background_collector_type_ != gc::kCollectorTypeHomogeneousSpaceCompact) {
@@ -448,7 +447,8 @@
mark_bitmap_.reset(new accounting::HeapBitmap(this));
// We don't have hspace compaction enabled with CC.
- if (foreground_collector_type_ == kCollectorTypeCC) {
+ if (foreground_collector_type_ == kCollectorTypeCC
+ || foreground_collector_type_ == kCollectorTypeCMC) {
use_homogeneous_space_compaction_for_oom_ = false;
}
bool support_homogeneous_space_compaction =
@@ -486,6 +486,7 @@
runtime->ShouldRelocate(),
/*executable=*/ !runtime->IsAotCompiler(),
heap_reservation_size,
+ runtime->AllowInMemoryCompilation(),
&boot_image_spaces,
&heap_reservation)) {
DCHECK_EQ(heap_reservation_size, heap_reservation.IsValid() ? heap_reservation.Size() : 0u);
@@ -629,10 +630,14 @@
std::move(main_mem_map_1));
CHECK(bump_pointer_space_ != nullptr) << "Failed to create bump pointer space";
AddSpace(bump_pointer_space_);
- temp_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space 2",
- std::move(main_mem_map_2));
- CHECK(temp_space_ != nullptr) << "Failed to create bump pointer space";
- AddSpace(temp_space_);
+ // For Concurrent Mark-compact GC we don't need the temp space to be in
+ // lower 4GB. So its temp space will be created by the GC itself.
+ if (foreground_collector_type_ != kCollectorTypeCMC) {
+ temp_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space 2",
+ std::move(main_mem_map_2));
+ CHECK(temp_space_ != nullptr) << "Failed to create bump pointer space";
+ AddSpace(temp_space_);
+ }
CHECK(separate_non_moving_space);
} else {
CreateMainMallocSpace(std::move(main_mem_map_1), initial_size, growth_limit_, capacity_);
@@ -758,6 +763,10 @@
semi_space_collector_ = new collector::SemiSpace(this);
garbage_collectors_.push_back(semi_space_collector_);
}
+ if (MayUseCollector(kCollectorTypeCMC)) {
+ mark_compact_ = new collector::MarkCompact(this);
+ garbage_collectors_.push_back(mark_compact_);
+ }
if (MayUseCollector(kCollectorTypeCC)) {
concurrent_copying_collector_ = new collector::ConcurrentCopying(this,
/*young_gen=*/false,
@@ -963,7 +972,6 @@
void Heap::IncrementDisableThreadFlip(Thread* self) {
// Supposed to be called by mutators. If thread_flip_running_ is true, block. Otherwise, go ahead.
- CHECK(kUseReadBarrier);
bool is_nested = self->GetDisableThreadFlipCount() > 0;
self->IncrementDisableThreadFlipCount();
if (is_nested) {
@@ -994,10 +1002,23 @@
}
}
+void Heap::EnsureObjectUserfaulted(ObjPtr<mirror::Object> obj) {
+ if (gUseUserfaultfd) {
+ // Use volatile to ensure that compiler loads from memory to trigger userfaults, if required.
+ const uint8_t* start = reinterpret_cast<uint8_t*>(obj.Ptr());
+ const uint8_t* end = AlignUp(start + obj->SizeOf(), kPageSize);
+ // The first page is already touched by SizeOf().
+ start += kPageSize;
+ while (start < end) {
+ ForceRead(start);
+ start += kPageSize;
+ }
+ }
+}
+
void Heap::DecrementDisableThreadFlip(Thread* self) {
// Supposed to be called by mutators. Decrement disable_thread_flip_count_ and potentially wake up
// the GC waiting before doing a thread flip.
- CHECK(kUseReadBarrier);
self->DecrementDisableThreadFlipCount();
bool is_outermost = self->GetDisableThreadFlipCount() == 0;
if (!is_outermost) {
@@ -1017,7 +1038,6 @@
void Heap::ThreadFlipBegin(Thread* self) {
// Supposed to be called by GC. Set thread_flip_running_ to be true. If disable_thread_flip_count_
// > 0, block. Otherwise, go ahead.
- CHECK(kUseReadBarrier);
ScopedThreadStateChange tsc(self, ThreadState::kWaitingForGcThreadFlip);
MutexLock mu(self, *thread_flip_lock_);
thread_flip_cond_->CheckSafeToWait(self);
@@ -1043,7 +1063,6 @@
void Heap::ThreadFlipEnd(Thread* self) {
// Supposed to be called by GC. Set thread_flip_running_ to false and potentially wake up mutators
// waiting before doing a JNI critical.
- CHECK(kUseReadBarrier);
MutexLock mu(self, *thread_flip_lock_);
CHECK(thread_flip_running_);
thread_flip_running_ = false;
@@ -1059,7 +1078,9 @@
min_foreground_target_footprint_,
std::memory_order_relaxed);
}
- min_foreground_target_footprint_ = 0;
+ if (IsGcConcurrent() && concurrent_start_bytes_ < min_foreground_concurrent_start_bytes_) {
+ concurrent_start_bytes_ = min_foreground_concurrent_start_bytes_;
+ }
}
void Heap::UpdateProcessState(ProcessState old_process_state, ProcessState new_process_state) {
@@ -1083,13 +1104,23 @@
}
}
-void Heap::CreateThreadPool() {
- const size_t num_threads = std::max(parallel_gc_threads_, conc_gc_threads_);
+void Heap::CreateThreadPool(size_t num_threads) {
+ if (num_threads == 0) {
+ num_threads = std::max(parallel_gc_threads_, conc_gc_threads_);
+ }
if (num_threads != 0) {
thread_pool_.reset(new ThreadPool("Heap thread pool", num_threads));
}
}
+void Heap::WaitForWorkersToBeCreated() {
+ DCHECK(!Runtime::Current()->IsShuttingDown(Thread::Current()))
+ << "Cannot create new threads during runtime shutdown";
+ if (thread_pool_ != nullptr) {
+ thread_pool_->WaitForWorkersToBeCreated();
+ }
+}
+
void Heap::MarkAllocStackAsLive(accounting::ObjectStack* stack) {
space::ContinuousSpace* space1 = main_space_ != nullptr ? main_space_ : non_moving_space_;
space::ContinuousSpace* space2 = non_moving_space_;
@@ -1451,6 +1482,8 @@
Runtime::Current()->GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow());
return;
}
+ // Allow plugins to intercept out of memory errors.
+ Runtime::Current()->OutOfMemoryErrorHook();
std::ostringstream oss;
size_t total_bytes_free = GetFreeMemory();
@@ -1497,6 +1530,20 @@
void Heap::DoPendingCollectorTransition() {
CollectorType desired_collector_type = desired_collector_type_;
+
+ if (collector_type_ == kCollectorTypeCC) {
+ // App's allocations (since last GC) more than the threshold then do TransitionGC
+ // when the app was in background. If not then don't do TransitionGC.
+ size_t num_bytes_allocated_since_gc = GetBytesAllocated() - num_bytes_alive_after_gc_;
+ if (num_bytes_allocated_since_gc <
+ (UnsignedDifference(target_footprint_.load(std::memory_order_relaxed),
+ num_bytes_alive_after_gc_)/4)
+ && !kStressCollectorTransition
+ && !IsLowMemoryMode()) {
+ return;
+ }
+ }
+
// Launch homogeneous space compaction if it is desired.
if (desired_collector_type == kCollectorTypeHomogeneousSpaceCompact) {
if (!CareAboutPauseTimes()) {
@@ -1505,12 +1552,12 @@
VLOG(gc) << "Homogeneous compaction ignored due to jank perceptible process state";
}
} else if (desired_collector_type == kCollectorTypeCCBackground) {
- DCHECK(kUseReadBarrier);
+ DCHECK(gUseReadBarrier);
if (!CareAboutPauseTimes()) {
// Invoke CC full compaction.
CollectGarbageInternal(collector::kGcTypeFull,
kGcCauseCollectorTransition,
- /*clear_soft_references=*/false, GC_NUM_ANY);
+ /*clear_soft_references=*/false, GetCurrentGcNum() + 1);
} else {
VLOG(gc) << "CC background compaction ignored due to jank perceptible process state";
}
@@ -2199,6 +2246,15 @@
}
break;
}
+ case kCollectorTypeCMC: {
+ gc_plan_.push_back(collector::kGcTypeFull);
+ if (use_tlab_) {
+ ChangeAllocator(kAllocatorTypeTLAB);
+ } else {
+ ChangeAllocator(kAllocatorTypeBumpPointer);
+ }
+ break;
+ }
case kCollectorTypeSS: {
gc_plan_.push_back(collector::kGcTypeFull);
if (use_tlab_) {
@@ -2368,18 +2424,16 @@
}
// We need to close userfaultfd fd for app/webview zygotes to avoid getattr
// (stat) on the fd during fork.
- if (uffd_ >= 0) {
- close(uffd_);
- uffd_ = -1;
- }
Thread* self = Thread::Current();
MutexLock mu(self, zygote_creation_lock_);
// Try to see if we have any Zygote spaces.
if (HasZygoteSpace()) {
return;
}
- Runtime::Current()->GetInternTable()->AddNewTable();
- Runtime::Current()->GetClassLinker()->MoveClassTableToPreZygote();
+ Runtime* runtime = Runtime::Current();
+ runtime->GetInternTable()->AddNewTable();
+ runtime->GetClassLinker()->MoveClassTableToPreZygote();
+ runtime->SetupLinearAllocForPostZygoteFork(self);
VLOG(heap) << "Starting PreZygoteFork";
// The end of the non-moving space may be protected, unprotect it so that we can copy the zygote
// there.
@@ -2710,6 +2764,9 @@
semi_space_collector_->SetSwapSemiSpaces(true);
collector = semi_space_collector_;
break;
+ case kCollectorTypeCMC:
+ collector = mark_compact_;
+ break;
case kCollectorTypeCC:
collector::ConcurrentCopying* active_cc_collector;
if (use_generational_cc_) {
@@ -2728,7 +2785,9 @@
default:
LOG(FATAL) << "Invalid collector type " << static_cast<size_t>(collector_type_);
}
- if (collector != active_concurrent_copying_collector_.load(std::memory_order_relaxed)) {
+ // temp_space_ will be null for kCollectorTypeCMC.
+ if (temp_space_ != nullptr
+ && collector != active_concurrent_copying_collector_.load(std::memory_order_relaxed)) {
temp_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE);
if (kIsDebugBuild) {
// Try to read each page of the memory map in case mprotect didn't work properly b/19894268.
@@ -3680,7 +3739,9 @@
// process-state switch.
min_foreground_target_footprint_ =
(multiplier <= 1.0 && grow_bytes > 0)
- ? bytes_allocated + static_cast<size_t>(grow_bytes * foreground_heap_growth_multiplier_)
+ ? std::min(
+ bytes_allocated + static_cast<size_t>(grow_bytes * foreground_heap_growth_multiplier_),
+ GetMaxMemory())
: 0;
if (IsGcConcurrent()) {
@@ -3712,6 +3773,12 @@
// allocation rate is very high, remaining_bytes could tell us that we should start a GC
// right away.
concurrent_start_bytes_ = std::max(target_footprint - remaining_bytes, bytes_allocated);
+ // Store concurrent_start_bytes_ (computed with foreground heap growth multiplier) for update
+ // itself when process state switches to foreground.
+ min_foreground_concurrent_start_bytes_ =
+ min_foreground_target_footprint_ != 0
+ ? std::max(min_foreground_target_footprint_ - remaining_bytes, bytes_allocated)
+ : 0;
}
}
}
@@ -3762,12 +3829,11 @@
void Heap::AddFinalizerReference(Thread* self, ObjPtr<mirror::Object>* object) {
ScopedObjectAccess soa(self);
- ScopedLocalRef<jobject> arg(self->GetJniEnv(), soa.AddLocalReference<jobject>(*object));
- jvalue args[1];
- args[0].l = arg.get();
- InvokeWithJValues(soa, nullptr, WellKnownClasses::java_lang_ref_FinalizerReference_add, args);
- // Restore object in case it gets moved.
- *object = soa.Decode<mirror::Object>(arg.get());
+ StackHandleScope<1u> hs(self);
+ // Use handle wrapper to update the `*object` if the object gets moved.
+ HandleWrapperObjPtr<mirror::Object> h_object = hs.NewHandleWrapper(object);
+ WellKnownClasses::java_lang_ref_FinalizerReference_add->InvokeStatic<'V', 'L'>(
+ self, h_object.Get());
}
void Heap::RequestConcurrentGCAndSaveObject(Thread* self,
@@ -3829,70 +3895,6 @@
return true; // Vacuously.
}
-#if defined(__BIONIC__) && defined(ART_TARGET)
-void Heap::MaybePerformUffdIoctls(GcCause cause, uint32_t requested_gc_num) const {
- if (uffd_ >= 0
- && cause == kGcCauseBackground
- && (requested_gc_num < 5 || requested_gc_num % 5 == 0)) {
- // Attempt to use all userfaultfd ioctls that we intend to use.
- // Register ioctl
- {
- struct uffdio_register uffd_register;
- uffd_register.range.start = 0;
- uffd_register.range.len = 0;
- uffd_register.mode = UFFDIO_REGISTER_MODE_MISSING;
- int ret = ioctl(uffd_, UFFDIO_REGISTER, &uffd_register);
- CHECK_EQ(ret, -1);
- CHECK_EQ(errno, EINVAL);
- }
- // Copy ioctl
- {
- struct uffdio_copy uffd_copy = {.src = 0, .dst = 0, .len = 0, .mode = 0};
- int ret = ioctl(uffd_, UFFDIO_COPY, &uffd_copy);
- CHECK_EQ(ret, -1);
- CHECK_EQ(errno, EINVAL);
- }
- // Zeropage ioctl
- {
- struct uffdio_zeropage uffd_zeropage;
- uffd_zeropage.range.start = 0;
- uffd_zeropage.range.len = 0;
- uffd_zeropage.mode = 0;
- int ret = ioctl(uffd_, UFFDIO_ZEROPAGE, &uffd_zeropage);
- CHECK_EQ(ret, -1);
- CHECK_EQ(errno, EINVAL);
- }
- // Continue ioctl
- {
- struct uffdio_continue uffd_continue;
- uffd_continue.range.start = 0;
- uffd_continue.range.len = 0;
- uffd_continue.mode = 0;
- int ret = ioctl(uffd_, UFFDIO_CONTINUE, &uffd_continue);
- CHECK_EQ(ret, -1);
- CHECK_EQ(errno, EINVAL);
- }
- // Wake ioctl
- {
- struct uffdio_range uffd_range = {.start = 0, .len = 0};
- int ret = ioctl(uffd_, UFFDIO_WAKE, &uffd_range);
- CHECK_EQ(ret, -1);
- CHECK_EQ(errno, EINVAL);
- }
- // Unregister ioctl
- {
- struct uffdio_range uffd_range = {.start = 0, .len = 0};
- int ret = ioctl(uffd_, UFFDIO_UNREGISTER, &uffd_range);
- CHECK_EQ(ret, -1);
- CHECK_EQ(errno, EINVAL);
- }
- }
-}
-#else
-void Heap::MaybePerformUffdIoctls(GcCause cause ATTRIBUTE_UNUSED,
- uint32_t requested_gc_num ATTRIBUTE_UNUSED) const {}
-#endif
-
void Heap::ConcurrentGC(Thread* self, GcCause cause, bool force_full, uint32_t requested_gc_num) {
if (!Runtime::Current()->IsShuttingDown(self)) {
// Wait for any GCs currently running to finish. If this incremented GC number, we're done.
@@ -3905,7 +3907,7 @@
}
// If we can't run the GC type we wanted to run, find the next appropriate one and try
// that instead. E.g. can't do partial, so do full instead.
- // We must ensure that we run something that ends up inrementing gcs_completed_.
+ // We must ensure that we run something that ends up incrementing gcs_completed_.
// In the kGcTypePartial case, the initial CollectGarbageInternal call may not have that
// effect, but the subsequent KGcTypeFull call will.
if (CollectGarbageInternal(next_gc_type, cause, false, requested_gc_num)
@@ -3919,12 +3921,9 @@
if (gc_type > next_gc_type &&
CollectGarbageInternal(gc_type, cause, false, requested_gc_num)
!= collector::kGcTypeNone) {
- MaybePerformUffdIoctls(cause, requested_gc_num);
break;
}
}
- } else {
- MaybePerformUffdIoctls(cause, requested_gc_num);
}
}
}
@@ -3956,16 +3955,6 @@
// For CC, we invoke a full compaction when going to the background, but the collector type
// doesn't change.
DCHECK_EQ(desired_collector_type_, kCollectorTypeCCBackground);
- // App's allocations (since last GC) more than the threshold then do TransitionGC
- // when the app was in background. If not then don't do TransitionGC.
- size_t num_bytes_allocated_since_gc = GetBytesAllocated() - num_bytes_alive_after_gc_;
- if (num_bytes_allocated_since_gc <
- (UnsignedDifference(target_footprint_.load(std::memory_order_relaxed),
- num_bytes_alive_after_gc_)/4)
- && !kStressCollectorTransition
- && !IsLowMemoryMode()) {
- return;
- }
}
DCHECK_NE(collector_type_, kCollectorTypeCCBackground);
CollectorTransitionTask* added_task = nullptr;
@@ -4076,12 +4065,6 @@
}
}
-void Heap::RunFinalization(JNIEnv* env, uint64_t timeout) {
- env->CallStaticVoidMethod(WellKnownClasses::dalvik_system_VMRuntime,
- WellKnownClasses::dalvik_system_VMRuntime_runFinalization,
- static_cast<jlong>(timeout));
-}
-
// For GC triggering purposes, we count old (pre-last-GC) and new native allocations as
// different fractions of Java allocations.
// For now, we essentially do not count old native allocations at all, so that we can preserve the
@@ -4167,7 +4150,7 @@
// About kNotifyNativeInterval allocations have occurred. Check whether we should garbage collect.
void Heap::NotifyNativeAllocations(JNIEnv* env) {
native_objects_notified_.fetch_add(kNotifyNativeInterval, std::memory_order_relaxed);
- CheckGCForNative(ThreadForEnv(env));
+ CheckGCForNative(Thread::ForEnv(env));
}
// Register a native allocation with an explicit size.
@@ -4181,7 +4164,7 @@
native_objects_notified_.fetch_add(1, std::memory_order_relaxed);
if (objects_notified % kNotifyNativeInterval == kNotifyNativeInterval - 1
|| bytes > kCheckImmediatelyThreshold) {
- CheckGCForNative(ThreadForEnv(env));
+ CheckGCForNative(Thread::ForEnv(env));
}
// Heap profiler treats this as a Java allocation with a null object.
JHPCheckNonTlabSampleAllocation(Thread::Current(), nullptr, bytes);
@@ -4280,7 +4263,7 @@
}
void Heap::AllowNewAllocationRecords() const {
- CHECK(!kUseReadBarrier);
+ CHECK(!gUseReadBarrier);
MutexLock mu(Thread::Current(), *Locks::alloc_tracker_lock_);
AllocRecordObjectMap* allocation_records = GetAllocationRecords();
if (allocation_records != nullptr) {
@@ -4289,7 +4272,7 @@
}
void Heap::DisallowNewAllocationRecords() const {
- CHECK(!kUseReadBarrier);
+ CHECK(!gUseReadBarrier);
MutexLock mu(Thread::Current(), *Locks::alloc_tracker_lock_);
AllocRecordObjectMap* allocation_records = GetAllocationRecords();
if (allocation_records != nullptr) {
@@ -4412,12 +4395,15 @@
}
void Heap::DisableGCForShutdown() {
- Thread* const self = Thread::Current();
- CHECK(Runtime::Current()->IsShuttingDown(self));
- MutexLock mu(self, *gc_complete_lock_);
+ MutexLock mu(Thread::Current(), *gc_complete_lock_);
gc_disabled_for_shutdown_ = true;
}
+bool Heap::IsGCDisabledForShutdown() const {
+ MutexLock mu(Thread::Current(), *gc_complete_lock_);
+ return gc_disabled_for_shutdown_;
+}
+
bool Heap::ObjectIsInBootImageSpace(ObjPtr<mirror::Object> obj) const {
DCHECK_EQ(IsBootImageAddress(obj.Ptr()),
any_of(boot_image_spaces_.begin(),
@@ -4494,8 +4480,13 @@
DCHECK_LE(alloc_size, self->TlabSize());
} else if (allocator_type == kAllocatorTypeTLAB) {
DCHECK(bump_pointer_space_ != nullptr);
+ // Try to allocate a page-aligned TLAB (not necessary though).
+ // TODO: for large allocations, which are rare, maybe we should allocate
+ // that object and return. There is no need to revoke the current TLAB,
+ // particularly if it's mostly unutilized.
+ size_t def_pr_tlab_size = RoundDown(alloc_size + kDefaultTLABSize, kPageSize) - alloc_size;
size_t next_tlab_size = JHPCalculateNextTlabSize(self,
- kDefaultTLABSize,
+ def_pr_tlab_size,
alloc_size,
&take_sample,
&bytes_until_sample);
@@ -4658,42 +4649,33 @@
uint64_t last_adj_time = NanoTime();
next_gc_type_ = NonStickyGcType(); // Always start with a full gc.
-#if defined(__BIONIC__) && defined(ART_TARGET)
- uffd_ = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY);
- if (uffd_ >= 0) {
- struct uffdio_api api = {.api = UFFD_API, .features = 0};
- int ret = ioctl(uffd_, UFFDIO_API, &api);
- CHECK_EQ(ret, 0) << "ioctl_userfaultfd: API: " << strerror(errno);
- } else {
- // The syscall should fail only if it doesn't exist in the kernel or if it's
- // denied by SELinux.
- CHECK(errno == ENOSYS || errno == EACCES) << "userfaultfd: " << strerror(errno);
+ LOG(INFO) << "Using " << foreground_collector_type_ << " GC.";
+ if (gUseUserfaultfd) {
+ DCHECK_NE(mark_compact_, nullptr);
+ mark_compact_->CreateUserfaultfd(/*post_fork*/true);
}
-#endif
// Temporarily increase target_footprint_ and concurrent_start_bytes_ to
// max values to avoid GC during app launch.
- if (!IsLowMemoryMode()) {
- // Set target_footprint_ to the largest allowed value.
- SetIdealFootprint(growth_limit_);
- SetDefaultConcurrentStartBytes();
+ // Set target_footprint_ to the largest allowed value.
+ SetIdealFootprint(growth_limit_);
+ SetDefaultConcurrentStartBytes();
- // Shrink heap after kPostForkMaxHeapDurationMS, to force a memory hog process to GC.
- // This remains high enough that many processes will continue without a GC.
- if (initial_heap_size_ < growth_limit_) {
- size_t first_shrink_size = std::max(growth_limit_ / 4, initial_heap_size_);
- last_adj_time += MsToNs(kPostForkMaxHeapDurationMS);
+ // Shrink heap after kPostForkMaxHeapDurationMS, to force a memory hog process to GC.
+ // This remains high enough that many processes will continue without a GC.
+ if (initial_heap_size_ < growth_limit_) {
+ size_t first_shrink_size = std::max(growth_limit_ / 4, initial_heap_size_);
+ last_adj_time += MsToNs(kPostForkMaxHeapDurationMS);
+ GetTaskProcessor()->AddTask(
+ self, new ReduceTargetFootprintTask(last_adj_time, first_shrink_size, starting_gc_num));
+ // Shrink to a small value after a substantial time period. This will typically force a
+ // GC if none has occurred yet. Has no effect if there was a GC before this anyway, which
+ // is commonly the case, e.g. because of a process transition.
+ if (initial_heap_size_ < first_shrink_size) {
+ last_adj_time += MsToNs(4 * kPostForkMaxHeapDurationMS);
GetTaskProcessor()->AddTask(
- self, new ReduceTargetFootprintTask(last_adj_time, first_shrink_size, starting_gc_num));
- // Shrink to a small value after a substantial time period. This will typically force a
- // GC if none has occurred yet. Has no effect if there was a GC before this anyway, which
- // is commonly the case, e.g. because of a process transition.
- if (initial_heap_size_ < first_shrink_size) {
- last_adj_time += MsToNs(4 * kPostForkMaxHeapDurationMS);
- GetTaskProcessor()->AddTask(
- self,
- new ReduceTargetFootprintTask(last_adj_time, initial_heap_size_, starting_gc_num));
- }
+ self,
+ new ReduceTargetFootprintTask(last_adj_time, initial_heap_size_, starting_gc_num));
}
}
// Schedule a GC after a substantial period of time. This will become a no-op if another GC is
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 232c96b..1148a76 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -87,6 +87,7 @@
namespace collector {
class ConcurrentCopying;
class GarbageCollector;
+class MarkCompact;
class MarkSweep;
class SemiSpace;
} // namespace collector
@@ -150,7 +151,7 @@
static constexpr size_t kMinLargeObjectThreshold = 3 * kPageSize;
static constexpr size_t kDefaultLargeObjectThreshold = kMinLargeObjectThreshold;
// Whether or not parallel GC is enabled. If not, then we never create the thread pool.
- static constexpr bool kDefaultEnableParallelGC = false;
+ static constexpr bool kDefaultEnableParallelGC = true;
static uint8_t* const kPreferredAllocSpaceBegin;
// Whether or not we use the free list large object space. Only use it if USE_ART_LOW_4G_ALLOCATOR
@@ -385,6 +386,9 @@
void ThreadFlipBegin(Thread* self) REQUIRES(!*thread_flip_lock_);
void ThreadFlipEnd(Thread* self) REQUIRES(!*thread_flip_lock_);
+ // Ensures that the obj doesn't cause userfaultfd in JNI critical calls.
+ void EnsureObjectUserfaulted(ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_);
+
// Clear all of the mark bits, doesn't clear bitmaps which have the same live bits as mark bits.
// Mutator lock is required for GetContinuousSpaces.
void ClearMarkedObjects()
@@ -578,6 +582,9 @@
return region_space_;
}
+ space::BumpPointerSpace* GetBumpPointerSpace() const {
+ return bump_pointer_space_;
+ }
// Implements java.lang.Runtime.maxMemory, returning the maximum amount of memory a program can
// consume. For a regular VM this would relate to the -Xmx option and would return -1 if no Xmx
// were specified. Android apps start with a growth limit (small heap size) which is
@@ -661,6 +668,10 @@
return live_stack_.get();
}
+ accounting::ObjectStack* GetAllocationStack() REQUIRES_SHARED(Locks::heap_bitmap_lock_) {
+ return allocation_stack_.get();
+ }
+
void PreZygoteFork() NO_THREAD_SAFETY_ANALYSIS;
// Mark and empty stack.
@@ -760,8 +771,10 @@
REQUIRES(!*gc_complete_lock_);
void ResetGcPerformanceInfo() REQUIRES(!*gc_complete_lock_);
- // Thread pool.
- void CreateThreadPool();
+ // Thread pool. Create either the given number of threads, or as per the
+ // values of conc_gc_threads_ and parallel_gc_threads_.
+ void CreateThreadPool(size_t num_threads = 0);
+ void WaitForWorkersToBeCreated();
void DeleteThreadPool();
ThreadPool* GetThreadPool() {
return thread_pool_.get();
@@ -812,10 +825,16 @@
return active_collector;
}
+ collector::MarkCompact* MarkCompactCollector() {
+ return mark_compact_;
+ }
+
CollectorType CurrentCollectorType() {
return collector_type_;
}
+ CollectorType GetForegroundCollectorType() const { return foreground_collector_type_; }
+
bool IsGcConcurrentAndMoving() const {
if (IsGcConcurrent() && IsMovingGc(collector_type_)) {
// Assume no transition when a concurrent moving collector is used.
@@ -939,6 +958,7 @@
REQUIRES(!Locks::alloc_tracker_lock_);
void DisableGCForShutdown() REQUIRES(!*gc_complete_lock_);
+ bool IsGCDisabledForShutdown() const REQUIRES(!*gc_complete_lock_);
// Create a new alloc space and compact default alloc space to it.
HomogeneousSpaceCompactResult PerformHomogeneousSpaceCompact()
@@ -1001,9 +1021,6 @@
return main_space_backup_ != nullptr;
}
- // Attempt to use all the userfaultfd related ioctls.
- void MaybePerformUffdIoctls(GcCause cause, uint32_t requested_gc_num) const;
-
// Size_t saturating arithmetic
static ALWAYS_INLINE size_t UnsignedDifference(size_t x, size_t y) {
return x > y ? x - y : 0;
@@ -1019,19 +1036,11 @@
allocator_type != kAllocatorTypeTLAB &&
allocator_type != kAllocatorTypeRegion;
}
- static ALWAYS_INLINE bool AllocatorMayHaveConcurrentGC(AllocatorType allocator_type) {
- if (kUseReadBarrier) {
- // Read barrier may have the TLAB allocator but is always concurrent. TODO: clean this up.
- return true;
- }
- return
- allocator_type != kAllocatorTypeTLAB &&
- allocator_type != kAllocatorTypeBumpPointer;
- }
static bool IsMovingGc(CollectorType collector_type) {
return
collector_type == kCollectorTypeCC ||
collector_type == kCollectorTypeSS ||
+ collector_type == kCollectorTypeCMC ||
collector_type == kCollectorTypeCCBackground ||
collector_type == kCollectorTypeHomogeneousSpaceCompact;
}
@@ -1117,9 +1126,6 @@
size_t alloc_size,
bool grow);
- // Run the finalizers. If timeout is non zero, then we use the VMRuntime version.
- void RunFinalization(JNIEnv* env, uint64_t timeout);
-
// Blocks the caller until the garbage collector becomes idle and returns the type of GC we
// waited for.
collector::GcType WaitForGcToCompleteLocked(GcCause cause, Thread* self)
@@ -1223,6 +1229,7 @@
// sweep GC, false for other GC types.
bool IsGcConcurrent() const ALWAYS_INLINE {
return collector_type_ == kCollectorTypeCC ||
+ collector_type_ == kCollectorTypeCMC ||
collector_type_ == kCollectorTypeCMS ||
collector_type_ == kCollectorTypeCCBackground;
}
@@ -1326,7 +1333,7 @@
// The current collector type.
CollectorType collector_type_;
// Which collector we use when the app is in the foreground.
- CollectorType foreground_collector_type_;
+ const CollectorType foreground_collector_type_;
// Which collector we will use when the app is notified of a transition to background.
CollectorType background_collector_type_;
// Desired collector type, heap trimming daemon transitions the heap if it is != collector_type_.
@@ -1437,8 +1444,9 @@
// Computed with foreground-multiplier in GrowForUtilization() when run in
// jank non-perceptible state. On update to process state from background to
- // foreground we set target_footprint_ to this value.
+ // foreground we set target_footprint_ and concurrent_start_bytes_ to the corresponding value.
size_t min_foreground_target_footprint_ GUARDED_BY(process_state_update_lock_);
+ size_t min_foreground_concurrent_start_bytes_ GUARDED_BY(process_state_update_lock_);
// When num_bytes_allocated_ exceeds this amount then a concurrent GC should be requested so that
// it completes ahead of an allocation failing.
@@ -1588,6 +1596,7 @@
std::vector<collector::GarbageCollector*> garbage_collectors_;
collector::SemiSpace* semi_space_collector_;
+ collector::MarkCompact* mark_compact_;
Atomic<collector::ConcurrentCopying*> active_concurrent_copying_collector_;
collector::ConcurrentCopying* young_concurrent_copying_collector_;
collector::ConcurrentCopying* concurrent_copying_collector_;
@@ -1680,9 +1689,6 @@
// Stack trace hashes that we already saw,
std::unordered_set<uint64_t> seen_backtraces_ GUARDED_BY(backtrace_lock_);
- // Userfaultfd file descriptor.
- // TODO (lokeshgidra): remove this when the userfaultfd-based GC is in use.
- int uffd_;
// We disable GC when we are shutting down the runtime in case there are daemon threads still
// allocating.
bool gc_disabled_for_shutdown_ GUARDED_BY(gc_complete_lock_);
@@ -1712,6 +1718,7 @@
friend class CollectorTransitionTask;
friend class collector::GarbageCollector;
friend class collector::ConcurrentCopying;
+ friend class collector::MarkCompact;
friend class collector::MarkSweep;
friend class collector::SemiSpace;
friend class GCCriticalSection;
diff --git a/runtime/gc/heap_test.cc b/runtime/gc/heap_test.cc
index 5e8c1e3..55ddd72 100644
--- a/runtime/gc/heap_test.cc
+++ b/runtime/gc/heap_test.cc
@@ -30,6 +30,10 @@
class HeapTest : public CommonRuntimeTest {
public:
+ HeapTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+
void SetUp() override {
MemMap::Init();
std::string error_msg;
@@ -100,6 +104,11 @@
}
class ZygoteHeapTest : public CommonRuntimeTest {
+ public:
+ ZygoteHeapTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+
void SetUpRuntimeOptions(RuntimeOptions* options) override {
CommonRuntimeTest::SetUpRuntimeOptions(options);
options->push_back(std::make_pair("-Xzygote", nullptr));
diff --git a/runtime/gc/heap_verification_test.cc b/runtime/gc/heap_verification_test.cc
index ca6a30b..a7583fe 100644
--- a/runtime/gc/heap_verification_test.cc
+++ b/runtime/gc/heap_verification_test.cc
@@ -26,14 +26,16 @@
#include "mirror/string.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
-#include "verification.h"
+#include "verification-inl.h"
namespace art {
namespace gc {
class VerificationTest : public CommonRuntimeTest {
protected:
- VerificationTest() {}
+ VerificationTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
template <class T>
ObjPtr<mirror::ObjectArray<T>> AllocObjectArray(Thread* self, size_t length)
@@ -76,11 +78,11 @@
Handle<mirror::String> string(
hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "test")));
const Verification* const v = Runtime::Current()->GetHeap()->GetVerification();
- EXPECT_FALSE(v->IsValidClass(reinterpret_cast<const void*>(1)));
- EXPECT_FALSE(v->IsValidClass(reinterpret_cast<const void*>(4)));
+ EXPECT_FALSE(v->IsValidClass(reinterpret_cast<mirror::Class*>(1)));
+ EXPECT_FALSE(v->IsValidClass(reinterpret_cast<mirror::Class*>(4)));
EXPECT_FALSE(v->IsValidClass(nullptr));
EXPECT_TRUE(v->IsValidClass(string->GetClass()));
- EXPECT_FALSE(v->IsValidClass(string.Get()));
+ EXPECT_FALSE(v->IsValidClass(reinterpret_cast<mirror::Class*>(string.Get())));
}
TEST_F(VerificationTest, IsValidClassInHeap) {
@@ -95,9 +97,9 @@
Handle<mirror::String> string(
hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), "test")));
const Verification* const v = Runtime::Current()->GetHeap()->GetVerification();
- const uintptr_t uint_klass = reinterpret_cast<uintptr_t>(string->GetClass());
- EXPECT_FALSE(v->IsValidClass(reinterpret_cast<const void*>(uint_klass - kObjectAlignment)));
- EXPECT_FALSE(v->IsValidClass(reinterpret_cast<const void*>(&uint_klass)));
+ uintptr_t uint_klass = reinterpret_cast<uintptr_t>(string->GetClass());
+ EXPECT_FALSE(v->IsValidClass(reinterpret_cast<mirror::Class*>(uint_klass - kObjectAlignment)));
+ EXPECT_FALSE(v->IsValidClass(reinterpret_cast<mirror::Class*>(&uint_klass)));
}
TEST_F(VerificationTest, DumpInvalidObjectInfo) {
diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc
index 5e41ee4..f24c942 100644
--- a/runtime/gc/reference_processor.cc
+++ b/runtime/gc/reference_processor.cc
@@ -90,7 +90,7 @@
ObjPtr<mirror::Object> ReferenceProcessor::GetReferent(Thread* self,
ObjPtr<mirror::Reference> reference) {
auto slow_path_required = [this, self]() REQUIRES_SHARED(Locks::mutator_lock_) {
- return kUseReadBarrier ? !self->GetWeakRefAccessEnabled() : SlowPathEnabled();
+ return gUseReadBarrier ? !self->GetWeakRefAccessEnabled() : SlowPathEnabled();
};
if (!slow_path_required()) {
return reference->GetReferent();
@@ -118,10 +118,10 @@
// Keeping reference_processor_lock_ blocks the broadcast when we try to reenable the fast path.
while (slow_path_required()) {
DCHECK(collector_ != nullptr);
- constexpr bool kOtherReadBarrier = kUseReadBarrier && !kUseBakerReadBarrier;
+ const bool other_read_barrier = !kUseBakerReadBarrier && gUseReadBarrier;
if (UNLIKELY(reference->IsFinalizerReferenceInstance()
|| rp_state_ == RpState::kStarting /* too early to determine mark state */
- || (kOtherReadBarrier && reference->IsPhantomReferenceInstance()))) {
+ || (other_read_barrier && reference->IsPhantomReferenceInstance()))) {
// Odd cases in which it doesn't hurt to just wait, or the wait is likely to be very brief.
// Check and run the empty checkpoint before blocking so the empty checkpoint will work in the
@@ -210,7 +210,7 @@
}
{
MutexLock mu(self, *Locks::reference_processor_lock_);
- if (!kUseReadBarrier) {
+ if (!gUseReadBarrier) {
CHECK_EQ(SlowPathEnabled(), concurrent_) << "Slow path must be enabled iff concurrent";
} else {
// Weak ref access is enabled at Zygote compaction by SemiSpace (concurrent_ == false).
@@ -305,7 +305,7 @@
// could result in a stale is_marked_callback_ being called before the reference processing
// starts since there is a small window of time where slow_path_enabled_ is enabled but the
// callback isn't yet set.
- if (!kUseReadBarrier && concurrent_) {
+ if (!gUseReadBarrier && concurrent_) {
// Done processing, disable the slow path and broadcast to the waiters.
DisableSlowPath(self);
}
@@ -363,9 +363,8 @@
}
void Run(Thread* thread) override {
ScopedObjectAccess soa(thread);
- jvalue args[1];
- args[0].l = cleared_references_;
- InvokeWithJValues(soa, nullptr, WellKnownClasses::java_lang_ref_ReferenceQueue_add, args);
+ WellKnownClasses::java_lang_ref_ReferenceQueue_add->InvokeStatic<'V', 'L'>(
+ thread, soa.Decode<mirror::Object>(cleared_references_));
soa.Env()->DeleteGlobalRef(cleared_references_);
}
@@ -418,8 +417,8 @@
void ReferenceProcessor::WaitUntilDoneProcessingReferences(Thread* self) {
// Wait until we are done processing reference.
- while ((!kUseReadBarrier && SlowPathEnabled()) ||
- (kUseReadBarrier && !self->GetWeakRefAccessEnabled())) {
+ while ((!gUseReadBarrier && SlowPathEnabled()) ||
+ (gUseReadBarrier && !self->GetWeakRefAccessEnabled())) {
// Check and run the empty checkpoint before blocking so the empty checkpoint will work in the
// presence of threads blocking for weak ref access.
self->CheckEmptyCheckpointFromWeakRefAccess(Locks::reference_processor_lock_);
diff --git a/runtime/gc/reference_queue_test.cc b/runtime/gc/reference_queue_test.cc
index c680fb5..c8e71b0 100644
--- a/runtime/gc/reference_queue_test.cc
+++ b/runtime/gc/reference_queue_test.cc
@@ -26,7 +26,12 @@
namespace art {
namespace gc {
-class ReferenceQueueTest : public CommonRuntimeTest {};
+class ReferenceQueueTest : public CommonRuntimeTest {
+ protected:
+ ReferenceQueueTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+};
TEST_F(ReferenceQueueTest, EnqueueDequeue) {
Thread* self = Thread::Current();
diff --git a/runtime/gc/space/bump_pointer_space-inl.h b/runtime/gc/space/bump_pointer_space-inl.h
index 20f7a93..2774b9e 100644
--- a/runtime/gc/space/bump_pointer_space-inl.h
+++ b/runtime/gc/space/bump_pointer_space-inl.h
@@ -20,6 +20,7 @@
#include "bump_pointer_space.h"
#include "base/bit_utils.h"
+#include "mirror/object-inl.h"
namespace art {
namespace gc {
@@ -89,6 +90,11 @@
return ret;
}
+inline mirror::Object* BumpPointerSpace::GetNextObject(mirror::Object* obj) {
+ const uintptr_t position = reinterpret_cast<uintptr_t>(obj) + obj->SizeOf();
+ return reinterpret_cast<mirror::Object*>(RoundUp(position, kAlignment));
+}
+
} // namespace space
} // namespace gc
} // namespace art
diff --git a/runtime/gc/space/bump_pointer_space-walk-inl.h b/runtime/gc/space/bump_pointer_space-walk-inl.h
index 5d05ea2..a978f62 100644
--- a/runtime/gc/space/bump_pointer_space-walk-inl.h
+++ b/runtime/gc/space/bump_pointer_space-walk-inl.h
@@ -17,12 +17,14 @@
#ifndef ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_WALK_INL_H_
#define ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_WALK_INL_H_
-#include "bump_pointer_space.h"
+#include "bump_pointer_space-inl.h"
#include "base/bit_utils.h"
#include "mirror/object-inl.h"
#include "thread-current-inl.h"
+#include <memory>
+
namespace art {
namespace gc {
namespace space {
@@ -32,6 +34,7 @@
uint8_t* pos = Begin();
uint8_t* end = End();
uint8_t* main_end = pos;
+ std::unique_ptr<std::vector<size_t>> block_sizes_copy;
// Internal indirection w/ NO_THREAD_SAFETY_ANALYSIS. Optimally, we'd like to have an annotation
// like
// REQUIRES_AS(visitor.operator(mirror::Object*))
@@ -49,15 +52,17 @@
MutexLock mu(Thread::Current(), block_lock_);
// If we have 0 blocks then we need to update the main header since we have bump pointer style
// allocation into an unbounded region (actually bounded by Capacity()).
- if (num_blocks_ == 0) {
+ if (block_sizes_.empty()) {
UpdateMainBlock();
}
main_end = Begin() + main_block_size_;
- if (num_blocks_ == 0) {
+ if (block_sizes_.empty()) {
// We don't have any other blocks, this means someone else may be allocating into the main
// block. In this case, we don't want to try and visit the other blocks after the main block
// since these could actually be part of the main block.
end = main_end;
+ } else {
+ block_sizes_copy.reset(new std::vector<size_t>(block_sizes_.begin(), block_sizes_.end()));
}
}
// Walk all of the objects in the main block first.
@@ -66,31 +71,33 @@
// No read barrier because obj may not be a valid object.
if (obj->GetClass<kDefaultVerifyFlags, kWithoutReadBarrier>() == nullptr) {
// There is a race condition where a thread has just allocated an object but not set the
- // class. We can't know the size of this object, so we don't visit it and exit the function
- // since there is guaranteed to be not other blocks.
- return;
+ // class. We can't know the size of this object, so we don't visit it and break the loop
+ pos = main_end;
+ break;
} else {
no_thread_safety_analysis_visit(obj);
pos = reinterpret_cast<uint8_t*>(GetNextObject(obj));
}
}
// Walk the other blocks (currently only TLABs).
- while (pos < end) {
- BlockHeader* header = reinterpret_cast<BlockHeader*>(pos);
- size_t block_size = header->size_;
- pos += sizeof(BlockHeader); // Skip the header so that we know where the objects
- mirror::Object* obj = reinterpret_cast<mirror::Object*>(pos);
- const mirror::Object* end_obj = reinterpret_cast<const mirror::Object*>(pos + block_size);
- CHECK_LE(reinterpret_cast<const uint8_t*>(end_obj), End());
- // We don't know how many objects are allocated in the current block. When we hit a null class
- // assume its the end. TODO: Have a thread update the header when it flushes the block?
- // No read barrier because obj may not be a valid object.
- while (obj < end_obj && obj->GetClass<kDefaultVerifyFlags, kWithoutReadBarrier>() != nullptr) {
- no_thread_safety_analysis_visit(obj);
- obj = GetNextObject(obj);
+ if (block_sizes_copy != nullptr) {
+ for (size_t block_size : *block_sizes_copy) {
+ mirror::Object* obj = reinterpret_cast<mirror::Object*>(pos);
+ const mirror::Object* end_obj = reinterpret_cast<const mirror::Object*>(pos + block_size);
+ CHECK_LE(reinterpret_cast<const uint8_t*>(end_obj), End());
+ // We don't know how many objects are allocated in the current block. When we hit a null class
+ // assume it's the end. TODO: Have a thread update the header when it flushes the block?
+ // No read barrier because obj may not be a valid object.
+ while (obj < end_obj && obj->GetClass<kDefaultVerifyFlags, kWithoutReadBarrier>() != nullptr) {
+ no_thread_safety_analysis_visit(obj);
+ obj = GetNextObject(obj);
+ }
+ pos += block_size;
}
- pos += block_size;
+ } else {
+ CHECK_EQ(end, main_end);
}
+ CHECK_EQ(pos, end);
}
} // namespace space
diff --git a/runtime/gc/space/bump_pointer_space.cc b/runtime/gc/space/bump_pointer_space.cc
index 3a0155a..7753f73 100644
--- a/runtime/gc/space/bump_pointer_space.cc
+++ b/runtime/gc/space/bump_pointer_space.cc
@@ -54,8 +54,9 @@
growth_end_(limit),
objects_allocated_(0), bytes_allocated_(0),
block_lock_("Block lock"),
- main_block_size_(0),
- num_blocks_(0) {
+ main_block_size_(0) {
+ // This constructor gets called only from Heap::PreZygoteFork(), which
+ // doesn't require a mark_bitmap.
}
BumpPointerSpace::BumpPointerSpace(const std::string& name, MemMap&& mem_map)
@@ -68,8 +69,11 @@
growth_end_(mem_map_.End()),
objects_allocated_(0), bytes_allocated_(0),
block_lock_("Block lock", kBumpPointerSpaceBlockLock),
- main_block_size_(0),
- num_blocks_(0) {
+ main_block_size_(0) {
+ mark_bitmap_ =
+ accounting::ContinuousSpaceBitmap::Create("bump-pointer space live bitmap",
+ Begin(),
+ Capacity());
}
void BumpPointerSpace::Clear() {
@@ -86,7 +90,7 @@
growth_end_ = Limit();
{
MutexLock mu(Thread::Current(), block_lock_);
- num_blocks_ = 0;
+ block_sizes_.clear();
main_block_size_ = 0;
}
}
@@ -97,11 +101,6 @@
<< reinterpret_cast<void*>(Limit());
}
-mirror::Object* BumpPointerSpace::GetNextObject(mirror::Object* obj) {
- const uintptr_t position = reinterpret_cast<uintptr_t>(obj) + obj->SizeOf();
- return reinterpret_cast<mirror::Object*>(RoundUp(position, kAlignment));
-}
-
size_t BumpPointerSpace::RevokeThreadLocalBuffers(Thread* thread) {
MutexLock mu(Thread::Current(), block_lock_);
RevokeThreadLocalBuffersLocked(thread);
@@ -141,23 +140,19 @@
}
void BumpPointerSpace::UpdateMainBlock() {
- DCHECK_EQ(num_blocks_, 0U);
+ DCHECK(block_sizes_.empty());
main_block_size_ = Size();
}
// Returns the start of the storage.
uint8_t* BumpPointerSpace::AllocBlock(size_t bytes) {
bytes = RoundUp(bytes, kAlignment);
- if (!num_blocks_) {
+ if (block_sizes_.empty()) {
UpdateMainBlock();
}
- uint8_t* storage = reinterpret_cast<uint8_t*>(
- AllocNonvirtualWithoutAccounting(bytes + sizeof(BlockHeader)));
+ uint8_t* storage = reinterpret_cast<uint8_t*>(AllocNonvirtualWithoutAccounting(bytes));
if (LIKELY(storage != nullptr)) {
- BlockHeader* header = reinterpret_cast<BlockHeader*>(storage);
- header->size_ = bytes; // Write out the block header.
- storage += sizeof(BlockHeader);
- ++num_blocks_;
+ block_sizes_.push_back(bytes);
}
return storage;
}
@@ -177,7 +172,7 @@
MutexLock mu3(Thread::Current(), block_lock_);
// If we don't have any blocks, we don't have any thread local buffers. This check is required
// since there can exist multiple bump pointer spaces which exist at the same time.
- if (num_blocks_ > 0) {
+ if (!block_sizes_.empty()) {
for (Thread* thread : thread_list) {
total += thread->GetThreadLocalBytesAllocated();
}
@@ -195,7 +190,7 @@
MutexLock mu3(Thread::Current(), block_lock_);
// If we don't have any blocks, we don't have any thread local buffers. This check is required
// since there can exist multiple bump pointer spaces which exist at the same time.
- if (num_blocks_ > 0) {
+ if (!block_sizes_.empty()) {
for (Thread* thread : thread_list) {
total += thread->GetThreadLocalObjectsAllocated();
}
@@ -240,6 +235,52 @@
return num_bytes;
}
+uint8_t* BumpPointerSpace::AlignEnd(Thread* self, size_t alignment) {
+ Locks::mutator_lock_->AssertExclusiveHeld(self);
+ DCHECK(IsAligned<kAlignment>(alignment));
+ uint8_t* end = end_.load(std::memory_order_relaxed);
+ uint8_t* aligned_end = AlignUp(end, alignment);
+ ptrdiff_t diff = aligned_end - end;
+ if (diff > 0) {
+ end_.store(aligned_end, std::memory_order_relaxed);
+ // If we have blocks after the main one. Then just add the diff to the last
+ // block.
+ MutexLock mu(self, block_lock_);
+ if (!block_sizes_.empty()) {
+ block_sizes_.back() += diff;
+ }
+ }
+ return end;
+}
+
+std::vector<size_t>* BumpPointerSpace::GetBlockSizes(Thread* self, size_t* main_block_size) {
+ std::vector<size_t>* block_sizes = nullptr;
+ MutexLock mu(self, block_lock_);
+ if (!block_sizes_.empty()) {
+ block_sizes = new std::vector<size_t>(block_sizes_.begin(), block_sizes_.end());
+ } else {
+ UpdateMainBlock();
+ }
+ *main_block_size = main_block_size_;
+ return block_sizes;
+}
+
+void BumpPointerSpace::SetBlockSizes(Thread* self,
+ const size_t main_block_size,
+ const size_t first_valid_idx) {
+ MutexLock mu(self, block_lock_);
+ main_block_size_ = main_block_size;
+ if (!block_sizes_.empty()) {
+ block_sizes_.erase(block_sizes_.begin(), block_sizes_.begin() + first_valid_idx);
+ }
+ size_t size = main_block_size;
+ for (size_t block_size : block_sizes_) {
+ size += block_size;
+ }
+ DCHECK(IsAligned<kAlignment>(size));
+ end_.store(Begin() + size, std::memory_order_relaxed);
+}
+
} // namespace space
} // namespace gc
} // namespace art
diff --git a/runtime/gc/space/bump_pointer_space.h b/runtime/gc/space/bump_pointer_space.h
index 08ed503..bba1711 100644
--- a/runtime/gc/space/bump_pointer_space.h
+++ b/runtime/gc/space/bump_pointer_space.h
@@ -17,9 +17,10 @@
#ifndef ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_H_
#define ART_RUNTIME_GC_SPACE_BUMP_POINTER_SPACE_H_
+#include "base/mutex.h"
#include "space.h"
-#include "base/mutex.h"
+#include <deque>
namespace art {
@@ -30,6 +31,7 @@
namespace gc {
namespace collector {
+class MarkCompact;
class MarkSweep;
} // namespace collector
@@ -39,7 +41,7 @@
// implementation as its intended to be evacuated.
class BumpPointerSpace final : public ContinuousMemMapAllocSpace {
public:
- typedef void(*WalkCallback)(void *start, void *end, size_t num_bytes, void* callback_arg);
+ using WalkCallback = void (*)(void *, void *, int, void *);
SpaceType GetType() const override {
return kSpaceTypeBumpPointerSpace;
@@ -100,10 +102,6 @@
return nullptr;
}
- accounting::ContinuousSpaceBitmap* GetMarkBitmap() override {
- return nullptr;
- }
-
// Reset the space to empty.
void Clear() override REQUIRES(!block_lock_);
@@ -120,6 +118,11 @@
REQUIRES(!*Locks::runtime_shutdown_lock_, !*Locks::thread_list_lock_, !block_lock_);
uint64_t GetObjectsAllocated() override REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!*Locks::runtime_shutdown_lock_, !*Locks::thread_list_lock_, !block_lock_);
+ // Return the pre-determined allocated object count. This could be beneficial
+ // when we know that all the TLABs are revoked.
+ int32_t GetAccumulatedObjectsAllocated() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return objects_allocated_.load(std::memory_order_relaxed);
+ }
bool IsEmpty() const {
return Begin() == End();
}
@@ -128,18 +131,9 @@
return true;
}
- bool Contains(const mirror::Object* obj) const override {
- const uint8_t* byte_obj = reinterpret_cast<const uint8_t*>(obj);
- return byte_obj >= Begin() && byte_obj < End();
- }
-
// TODO: Change this? Mainly used for compacting to a particular region of memory.
BumpPointerSpace(const std::string& name, uint8_t* begin, uint8_t* limit);
- // Return the object which comes after obj, while ensuring alignment.
- static mirror::Object* GetNextObject(mirror::Object* obj)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
// Allocate a new TLAB, returns false if the allocation failed.
bool AllocNewTlab(Thread* self, size_t bytes) REQUIRES(!block_lock_);
@@ -165,7 +159,7 @@
REQUIRES_SHARED(Locks::mutator_lock_);
// Object alignment within the space.
- static constexpr size_t kAlignment = 8;
+ static constexpr size_t kAlignment = kObjectAlignment;
protected:
BumpPointerSpace(const std::string& name, MemMap&& mem_map);
@@ -183,23 +177,40 @@
AtomicInteger objects_allocated_; // Accumulated from revoked thread local regions.
AtomicInteger bytes_allocated_; // Accumulated from revoked thread local regions.
Mutex block_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
- // The objects at the start of the space are stored in the main block. The main block doesn't
- // have a header, this lets us walk empty spaces which are mprotected.
+ // The objects at the start of the space are stored in the main block.
size_t main_block_size_ GUARDED_BY(block_lock_);
- // The number of blocks in the space, if it is 0 then the space has one long continuous block
- // which doesn't have an updated header.
- size_t num_blocks_ GUARDED_BY(block_lock_);
+ // List of block sizes (in bytes) after the main-block. Needed for Walk().
+ // If empty then the space has only one long continuous block. Each TLAB
+ // allocation has one entry in this deque.
+ // Keeping block-sizes off-heap simplifies sliding compaction algorithms.
+ // The compaction algorithm should ideally compact all objects into the main
+ // block, thereby enabling erasing corresponding entries from here.
+ std::deque<size_t> block_sizes_ GUARDED_BY(block_lock_);
private:
- struct BlockHeader {
- size_t size_; // Size of the block in bytes, does not include the header.
- size_t unused_; // Ensures alignment of kAlignment.
- };
+ // Return the object which comes after obj, while ensuring alignment.
+ static mirror::Object* GetNextObject(mirror::Object* obj)
+ REQUIRES_SHARED(Locks::mutator_lock_);
- static_assert(sizeof(BlockHeader) % kAlignment == 0,
- "continuous block must be kAlignment aligned");
+ // Return a vector of block sizes on the space. Required by MarkCompact GC for
+ // walking black objects allocated after marking phase.
+ std::vector<size_t>* GetBlockSizes(Thread* self, size_t* main_block_size) REQUIRES(!block_lock_);
+
+ // Once the MarkCompact decides the post-compact layout of the space in the
+ // pre-compaction pause, it calls this function to update the block sizes. It is
+ // done by passing the new main-block size, which consumes a bunch of blocks
+ // into itself, and the index of first unconsumed block. This works as all the
+ // block sizes are ordered. Also updates 'end_' to reflect the change.
+ void SetBlockSizes(Thread* self, const size_t main_block_size, const size_t first_valid_idx)
+ REQUIRES(!block_lock_, Locks::mutator_lock_);
+
+ // Align end to the given alignment. This is done in MarkCompact GC when
+ // mutators are suspended so that upcoming TLAB allocations start with a new
+ // page. Returns the pre-alignment end.
+ uint8_t* AlignEnd(Thread* self, size_t alignment) REQUIRES(Locks::mutator_lock_);
friend class collector::MarkSweep;
+ friend class collector::MarkCompact;
DISALLOW_COPY_AND_ASSIGN(BumpPointerSpace);
};
diff --git a/runtime/gc/space/dlmalloc_space-inl.h b/runtime/gc/space/dlmalloc_space-inl.h
index 4fc4ada..6041fd0 100644
--- a/runtime/gc/space/dlmalloc_space-inl.h
+++ b/runtime/gc/space/dlmalloc_space-inl.h
@@ -18,7 +18,7 @@
#define ART_RUNTIME_GC_SPACE_DLMALLOC_SPACE_INL_H_
#include "dlmalloc_space.h"
-#include "gc/allocator/dlmalloc.h"
+#include "gc/allocator/art-dlmalloc.h"
#include "thread.h"
namespace art {
diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc
index 25cac7e..1edcdbd 100644
--- a/runtime/gc/space/dlmalloc_space.cc
+++ b/runtime/gc/space/dlmalloc_space.cc
@@ -350,11 +350,18 @@
}
#endif
+struct MspaceCbArgs {
+ size_t max_contiguous;
+ size_t used;
+};
+
static void MSpaceChunkCallback(void* start, void* end, size_t used_bytes, void* arg) {
size_t chunk_size = reinterpret_cast<uint8_t*>(end) - reinterpret_cast<uint8_t*>(start);
+ MspaceCbArgs* mspace_cb_args = reinterpret_cast<MspaceCbArgs*>(arg);
+ mspace_cb_args->used += used_bytes;
if (used_bytes < chunk_size) {
size_t chunk_free_bytes = chunk_size - used_bytes;
- size_t& max_contiguous_allocation = *reinterpret_cast<size_t*>(arg);
+ size_t& max_contiguous_allocation = mspace_cb_args->max_contiguous;
max_contiguous_allocation = std::max(max_contiguous_allocation, chunk_free_bytes);
}
}
@@ -362,16 +369,17 @@
bool DlMallocSpace::LogFragmentationAllocFailure(std::ostream& os,
size_t failed_alloc_bytes) {
Thread* const self = Thread::Current();
- size_t max_contiguous_allocation = 0;
+ MspaceCbArgs mspace_cb_args = {0, 0};
// To allow the Walk/InspectAll() to exclusively-lock the mutator
// lock, temporarily release the shared access to the mutator
// lock here by transitioning to the suspended state.
Locks::mutator_lock_->AssertSharedHeld(self);
ScopedThreadSuspension sts(self, ThreadState::kSuspended);
- Walk(MSpaceChunkCallback, &max_contiguous_allocation);
- if (failed_alloc_bytes > max_contiguous_allocation) {
- os << "; failed due to fragmentation (largest possible contiguous allocation "
- << max_contiguous_allocation << " bytes)";
+ Walk(MSpaceChunkCallback, &mspace_cb_args);
+ if (failed_alloc_bytes > mspace_cb_args.max_contiguous) {
+ os << "; failed due to malloc_space fragmentation (largest possible contiguous allocation "
+ << mspace_cb_args.max_contiguous << " bytes, space in use " << mspace_cb_args.used
+ << " bytes, capacity = " << Capacity() << ")";
return true;
}
return false;
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 6afd63e..72fce00 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -23,7 +23,9 @@
#include <memory>
#include <random>
#include <string>
+#include <vector>
+#include "android-base/logging.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "android-base/unique_fd.h"
@@ -49,6 +51,7 @@
#include "dex/art_dex_file_loader.h"
#include "dex/dex_file_loader.h"
#include "exec_utils.h"
+#include "fmt/format.h"
#include "gc/accounting/space_bitmap-inl.h"
#include "gc/task_processor.h"
#include "image-inl.h"
@@ -69,14 +72,20 @@
namespace gc {
namespace space {
-using android::base::Join;
-using android::base::StringAppendF;
-using android::base::StringPrintf;
+namespace {
+
+using ::android::base::Join;
+using ::android::base::StringAppendF;
+using ::android::base::StringPrintf;
+
+using ::fmt::literals::operator""_format; // NOLINT
// 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;
+} // namespace
+
Atomic<uint32_t> ImageSpace::bitmap_index_(0);
ImageSpace::ImageSpace(const std::string& image_filename,
@@ -198,7 +207,6 @@
// Helper class for relocating from one range of memory to another.
class RelocationRange {
public:
- RelocationRange() = default;
RelocationRange(const RelocationRange&) = default;
RelocationRange(uintptr_t source, uintptr_t dest, uintptr_t length)
: source_(source),
@@ -513,7 +521,8 @@
// Check the oat file checksum.
const uint32_t oat_checksum = oat_file->GetOatHeader().GetChecksum();
const uint32_t image_oat_checksum = image_header.GetOatChecksum();
- if (oat_checksum != image_oat_checksum) {
+ // Note image_oat_checksum is 0 for images generated by the runtime.
+ if (image_oat_checksum != 0u && oat_checksum != image_oat_checksum) {
*error_msg = StringPrintf("Oat checksum 0x%x does not match the image one 0x%x in image %s",
oat_checksum,
image_oat_checksum,
@@ -1366,9 +1375,9 @@
}
};
-static void AppendImageChecksum(uint32_t component_count,
- uint32_t checksum,
- /*inout*/std::string* checksums) {
+void ImageSpace::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);
}
@@ -1378,7 +1387,7 @@
/*inout*/std::string_view* oat_checksums,
/*out*/std::string* error_msg) {
std::string image_checksum;
- AppendImageChecksum(component_count, checksum, &image_checksum);
+ ImageSpace::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(),
@@ -1389,182 +1398,6 @@
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;
- std::vector<std::string> profile_files;
- size_t start_index;
- uint32_t component_count;
- uint32_t image_space_count;
- uint32_t reservation_size;
- uint32_t checksum;
- uint32_t boot_image_component_count;
- uint32_t boot_image_checksum;
- uint32_t boot_image_size;
-
- // The following file descriptors hold the memfd files for extensions compiled
- // in memory and described by the above fields. We want to use them to mmap()
- // the contents and then close them while treating the ImageChunk description
- // as immutable (const), so make these fields explicitly mutable.
- mutable android::base::unique_fd art_fd;
- mutable android::base::unique_fd vdex_fd;
- mutable android::base::unique_fd oat_fd;
- };
-
- BootImageLayout(ArrayRef<const std::string> image_locations,
- ArrayRef<const std::string> boot_class_path,
- ArrayRef<const std::string> boot_class_path_locations,
- ArrayRef<const int> boot_class_path_fds,
- ArrayRef<const int> boot_class_path_image_fds,
- ArrayRef<const int> boot_class_path_vdex_fds,
- ArrayRef<const int> boot_class_path_oat_fds)
- : image_locations_(image_locations),
- boot_class_path_(boot_class_path),
- boot_class_path_locations_(boot_class_path_locations),
- boot_class_path_fds_(boot_class_path_fds),
- boot_class_path_image_fds_(boot_class_path_image_fds),
- boot_class_path_vdex_fds_(boot_class_path_vdex_fds),
- boot_class_path_oat_fds_(boot_class_path_oat_fds) {}
-
- 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);
- }
-
- 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:
- struct NamedComponentLocation {
- std::string base_location;
- size_t bcp_index;
- std::vector<std::string> profile_filenames;
- };
-
- 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);
- }
- }
-
- std::string GetBcpComponentPath(size_t bcp_index) {
- DCHECK_LE(bcp_index, boot_class_path_.size());
- size_t bcp_slash_pos = boot_class_path_[bcp_index].rfind('/');
- DCHECK_NE(bcp_slash_pos, std::string::npos);
- return boot_class_path_[bcp_index].substr(0u, bcp_slash_pos + 1u);
- }
-
- bool VerifyImageLocation(ArrayRef<const 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<NamedComponentLocation>* named_component_locations,
- /*out*/std::string* error_msg);
-
- bool ValidateBootImageChecksum(const char* file_description,
- const ImageHeader& header,
- /*out*/std::string* error_msg);
-
- bool ValidateHeader(const ImageHeader& header,
- size_t bcp_index,
- const char* file_description,
- /*out*/std::string* error_msg);
-
- bool ValidateOatFile(const std::string& base_location,
- const std::string& base_filename,
- size_t bcp_index,
- size_t component_count,
- /*out*/std::string* error_msg);
-
- bool ReadHeader(const std::string& base_location,
- const std::string& base_filename,
- size_t bcp_index,
- /*out*/std::string* error_msg);
-
- // Compiles a consecutive subsequence of bootclasspath dex files, whose contents are included in
- // the profiles specified by `profile_filenames`, starting from `bcp_index`.
- bool CompileBootclasspathElements(const std::string& base_location,
- const std::string& base_filename,
- size_t bcp_index,
- const std::vector<std::string>& profile_filenames,
- ArrayRef<const std::string> dependencies,
- /*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);
-
- ArrayRef<const std::string> image_locations_;
- ArrayRef<const std::string> boot_class_path_;
- ArrayRef<const std::string> boot_class_path_locations_;
- ArrayRef<const int> boot_class_path_fds_;
- ArrayRef<const int> boot_class_path_image_fds_;
- ArrayRef<const int> boot_class_path_vdex_fds_;
- ArrayRef<const int> boot_class_path_oat_fds_;
-
- 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() {
DCHECK(!image_locations_.empty());
std::string location = image_locations_[0];
@@ -1886,7 +1719,7 @@
error_msg->c_str());
return false;
}
- if (!ImageSpace::ValidateOatFile(*oat_file, error_msg, dex_filenames, dex_fds)) {
+ if (!ImageSpace::ValidateOatFile(*oat_file, error_msg, dex_filenames, dex_fds, apex_versions_)) {
return false;
}
return true;
@@ -2151,48 +1984,12 @@
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) {
+bool ImageSpace::BootImageLayout::Load(FilenameFn&& filename_fn,
+ bool allow_in_memory_compilation,
+ /*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_IMPLIES(validate, StartsWith(*oat_checksums, "i"));
ArrayRef<const std::string> components = image_locations_;
size_t named_components_count = 0u;
@@ -2223,24 +2020,21 @@
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 = 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, err_msg)) {
- if (validate) {
- return false;
- }
+ if (!filename_fn(base_location, &base_filename, &local_error_msg) ||
+ !ReadHeader(base_location, base_filename, bcp_index, &local_error_msg)) {
LOG(ERROR) << "Error reading named image component header for " << base_location
<< ", error: " << local_error_msg;
// If the primary boot image is invalid, we generate a single full image. This is faster than
// generating the primary boot image and the extension separately.
if (bcp_index == 0) {
+ if (!allow_in_memory_compilation) {
+ // The boot image is unusable and we can't continue by generating a boot image in memory.
+ // All we can do is to return.
+ *error_msg = std::move(local_error_msg);
+ return false;
+ }
// We must at least have profiles for the core libraries.
if (profile_filenames.empty()) {
*error_msg = "Full boot image cannot be compiled because no profile is provided.";
@@ -2264,14 +2058,15 @@
// No extensions are needed.
return true;
}
- if (profile_filenames.empty() ||
+ bool should_compile_extension = allow_in_memory_compilation && !profile_filenames.empty();
+ if (!should_compile_extension ||
!CompileBootclasspathElements(base_location,
base_filename,
bcp_index,
profile_filenames,
components.SubArray(/*pos=*/ 0, /*length=*/ 1),
&local_error_msg)) {
- if (!profile_filenames.empty()) {
+ if (should_compile_extension) {
LOG(ERROR) << "Error compiling boot image extension for " << boot_class_path_[bcp_index]
<< ", error: " << local_error_msg;
}
@@ -2280,14 +2075,6 @@
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();
}
@@ -2320,24 +2107,10 @@
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;
}
}
@@ -2346,16 +2119,16 @@
return true;
}
-bool ImageSpace::BootImageLayout::LoadOrValidateFromSystem(InstructionSet image_isa,
- /*inout*/std::string_view* oat_checksums,
- /*out*/std::string* error_msg) {
+bool ImageSpace::BootImageLayout::LoadFromSystem(InstructionSet image_isa,
+ bool allow_in_memory_compilation,
+ /*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);
+ return Load(filename_fn, allow_in_memory_compilation, error_msg);
}
class ImageSpace::BootImageLoader {
@@ -2403,6 +2176,7 @@
bool HasSystem() const { return has_system_; }
bool LoadFromSystem(size_t extra_reservation_size,
+ bool allow_in_memory_compilation,
/*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
/*out*/MemMap* extra_reservation,
/*out*/std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -2697,7 +2471,8 @@
int32_t class_roots_index = enum_cast<int32_t>(ImageHeader::kClassRoots);
DCHECK_LT(class_roots_index, image_roots->GetLength<kVerifyNone>());
class_roots = ObjPtr<mirror::ObjectArray<mirror::Class>>::DownCast(base_relocate_visitor(
- image_roots->GetWithoutChecks<kVerifyNone>(class_roots_index).Ptr()));
+ image_roots->GetWithoutChecks<kVerifyNone,
+ kWithoutReadBarrier>(class_roots_index).Ptr()));
if (kExtension) {
// Class roots must have been visited if we relocated the primary boot image.
DCHECK(base_diff == 0 || patched_objects->Test(class_roots.Ptr()));
@@ -2863,6 +2638,14 @@
DCHECK_EQ(base_diff64, 0);
}
+ // While `Thread::Current()` is null, the `ScopedDebugDisallowReadBarriers`
+ // cannot be used but the class `ReadBarrier` shall not allow read barriers anyway.
+ // For some gtests we actually have an initialized `Thread:Current()`.
+ std::optional<ScopedDebugDisallowReadBarriers> sddrb(std::nullopt);
+ if (kCheckDebugDisallowReadBarrierCount && Thread::Current() != nullptr) {
+ sddrb.emplace(Thread::Current());
+ }
+
ArrayRef<const std::unique_ptr<ImageSpace>> spaces_ref(spaces);
PointerSize pointer_size = first_space_header.GetPointerSize();
if (pointer_size == PointerSize::k64) {
@@ -3111,8 +2894,24 @@
return false;
}
}
+
+ // As an optimization, madvise the oat file into memory if it's being used
+ // for execution with an active runtime. This can significantly improve
+ // ZygoteInit class preload performance.
+ if (executable_) {
+ Runtime* runtime = Runtime::Current();
+ if (runtime != nullptr) {
+ Runtime::MadviseFileForRange(runtime->GetMadviseWillNeedSizeOdex(),
+ oat_file->Size(),
+ oat_file->Begin(),
+ oat_file->End(),
+ oat_file->GetLocation());
+ }
+ }
+
space->oat_file_ = std::move(oat_file);
space->oat_file_non_owned_ = space->oat_file_.get();
+
return true;
}
@@ -3345,6 +3144,7 @@
bool ImageSpace::BootImageLoader::LoadFromSystem(
size_t extra_reservation_size,
+ bool allow_in_memory_compilation,
/*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
/*out*/MemMap* extra_reservation,
/*out*/std::string* error_msg) {
@@ -3357,7 +3157,7 @@
boot_class_path_image_fds_,
boot_class_path_vdex_fds_,
boot_class_path_oat_fds_);
- if (!layout.LoadFromSystem(image_isa_, error_msg)) {
+ if (!layout.LoadFromSystem(image_isa_, allow_in_memory_compilation, error_msg)) {
return false;
}
@@ -3420,6 +3220,7 @@
bool relocate,
bool executable,
size_t extra_reservation_size,
+ bool allow_in_memory_compilation,
/*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
/*out*/MemMap* extra_reservation) {
ScopedTrace trace(__FUNCTION__);
@@ -3450,8 +3251,11 @@
std::vector<std::string> error_msgs;
std::string error_msg;
- if (loader.LoadFromSystem(
- extra_reservation_size, boot_image_spaces, extra_reservation, &error_msg)) {
+ if (loader.LoadFromSystem(extra_reservation_size,
+ allow_in_memory_compilation,
+ boot_image_spaces,
+ extra_reservation,
+ &error_msg)) {
return true;
}
error_msgs.push_back(error_msg);
@@ -3519,7 +3323,9 @@
<< ",name=\"" << GetName() << "\"]";
}
-bool ImageSpace::ValidateApexVersions(const OatFile& oat_file, std::string* error_msg) {
+bool ImageSpace::ValidateApexVersions(const OatFile& oat_file,
+ const std::string& apex_versions,
+ std::string* error_msg) {
// For a boot image, the key value store only exists in the first OAT file. Skip other OAT files.
if (oat_file.GetOatHeader().GetKeyValueStoreSize() == 0) {
return true;
@@ -3542,27 +3348,42 @@
// For a boot image, it can be generated from a subset of the bootclasspath.
// For an app image, some dex files get compiled with a subset of the bootclasspath.
// For such cases, the OAT APEX versions will be a prefix of the runtime APEX versions.
- if (!android::base::StartsWith(Runtime::Current()->GetApexVersions(), oat_apex_versions)) {
+ if (!android::base::StartsWith(apex_versions, oat_apex_versions)) {
*error_msg = StringPrintf(
"ValidateApexVersions found APEX versions mismatch between oat file '%s' and the runtime "
"(Oat file: '%s', Runtime: '%s')",
oat_file.GetLocation().c_str(),
oat_apex_versions,
- Runtime::Current()->GetApexVersions().c_str());
+ apex_versions.c_str());
return false;
}
return true;
}
bool ImageSpace::ValidateOatFile(const OatFile& oat_file, std::string* error_msg) {
- return ValidateOatFile(oat_file, error_msg, ArrayRef<const std::string>(), ArrayRef<const int>());
+ DCHECK(Runtime::Current() != nullptr);
+ return ValidateOatFile(oat_file,
+ error_msg,
+ ArrayRef<const std::string>(),
+ ArrayRef<const int>(),
+ Runtime::Current()->GetApexVersions());
}
bool ImageSpace::ValidateOatFile(const OatFile& oat_file,
std::string* error_msg,
ArrayRef<const std::string> dex_filenames,
- ArrayRef<const int> dex_fds) {
- if (!ValidateApexVersions(oat_file, error_msg)) {
+ ArrayRef<const int> dex_fds,
+ const std::string& apex_versions) {
+ if (!ValidateApexVersions(oat_file, apex_versions, error_msg)) {
+ return false;
+ }
+
+ // For a boot image, the key value store only exists in the first OAT file. Skip other OAT files.
+ if (oat_file.GetOatHeader().GetKeyValueStoreSize() != 0 &&
+ oat_file.GetOatHeader().IsConcurrentCopying() != gUseReadBarrier) {
+ *error_msg =
+ "ValidateOatFile found read barrier state mismatch (oat file: {}, runtime: {})"_format(
+ oat_file.GetOatHeader().IsConcurrentCopying(), gUseReadBarrier);
return false;
}
@@ -3695,7 +3516,7 @@
return n;
}
-static size_t CheckAndCountBCPComponents(std::string_view oat_boot_class_path,
+size_t ImageSpace::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.
@@ -3727,110 +3548,6 @@
return component_count;
}
-bool ImageSpace::VerifyBootClassPathChecksums(std::string_view oat_checksums,
- std::string_view oat_boot_class_path,
- ArrayRef<const std::string> image_locations,
- ArrayRef<const std::string> boot_class_path_locations,
- ArrayRef<const std::string> boot_class_path,
- ArrayRef<const int> boot_class_path_fds,
- InstructionSet image_isa,
- /*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. FDs are optional, so only pass the
- // sub-array if provided.
- ArrayRef<const int> bcp_fds = boot_class_path_fds.empty()
- ? ArrayRef<const int>()
- : boot_class_path_fds.SubArray(/*pos=*/ 0u, bcp_size);
- BootImageLayout layout(image_locations,
- boot_class_path.SubArray(/*pos=*/ 0u, bcp_size),
- boot_class_path_locations.SubArray(/*pos=*/ 0u, bcp_size),
- bcp_fds,
- /*boot_class_path_image_fds=*/ ArrayRef<const int>(),
- /*boot_class_path_vdex_fds=*/ ArrayRef<const int>(),
- /*boot_class_path_oat_fds=*/ ArrayRef<const int>());
- std::string primary_image_location = layout.GetPrimaryImageLocation();
- std::string system_filename;
- bool has_system = false;
- if (!FindImageFilename(primary_image_location.c_str(),
- image_isa,
- &system_filename,
- &has_system)) {
- *error_msg = StringPrintf("Unable to find image file for %s and %s",
- android::base::Join(image_locations, kComponentSeparator).c_str(),
- GetInstructionSetString(image_isa));
- return false;
- }
-
- DCHECK(has_system);
- if (!layout.ValidateFromSystem(image_isa, &oat_checksums, error_msg)) {
- return false;
- }
- bcp_pos = layout.GetNextBcpIndex();
- }
-
- 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<uint32_t> checksums;
- std::vector<std::string> dex_locations;
- const ArtDexFileLoader dex_file_loader;
- if (!dex_file_loader.GetMultiDexChecksums(bcp_filename.c_str(),
- &checksums,
- &dex_locations,
- error_msg)) {
- return false;
- }
- DCHECK(!checksums.empty());
- for (uint32_t checksum : checksums) {
- std::string dex_file_checksum = StringPrintf("/%08x", checksum);
- if (!StartsWith(oat_checksums, dex_file_checksum)) {
- *error_msg = StringPrintf(
- "Dex checksum mismatch for bootclasspath file %s, expected %s to start with %s",
- bcp_filename.c_str(),
- 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;
- }
- oat_checksums.remove_prefix(1u);
- }
- }
- if (!oat_checksums.empty()) {
- *error_msg = StringPrintf("Checksum too long, unexpected tail %s",
- std::string(oat_checksums).c_str());
- return false;
- }
- return true;
-}
-
bool ImageSpace::VerifyBootClassPathChecksums(
std::string_view oat_checksums,
std::string_view oat_boot_class_path,
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index 8a93f2b..58030de 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -17,13 +17,15 @@
#ifndef ART_RUNTIME_GC_SPACE_IMAGE_SPACE_H_
#define ART_RUNTIME_GC_SPACE_IMAGE_SPACE_H_
+#include "android-base/unique_fd.h"
+#include "base/array_ref.h"
#include "gc/accounting/space_bitmap.h"
#include "image.h"
+#include "runtime.h"
#include "space.h"
namespace art {
-template <typename T> class ArrayRef;
class DexFile;
enum class InstructionSet;
class OatFile;
@@ -142,6 +144,7 @@
bool relocate,
bool executable,
size_t extra_reservation_size,
+ bool allow_in_memory_compilation,
/*out*/std::vector<std::unique_ptr<ImageSpace>>* boot_image_spaces,
/*out*/MemMap* extra_reservation) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -239,18 +242,6 @@
// Returns the total number of components (jar files) associated with the image spaces.
static size_t GetNumberOfComponents(ArrayRef<gc::space::ImageSpace* const> image_spaces);
- // 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,
- ArrayRef<const std::string> image_locations,
- ArrayRef<const std::string> boot_class_path_locations,
- ArrayRef<const std::string> boot_class_path,
- ArrayRef<const int> boot_class_path_fds,
- InstructionSet image_isa,
- /*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(
@@ -267,8 +258,10 @@
const std::string& image_location,
bool boot_image_extension = false);
- // Returns true if the APEX versions in the OAT file match the current APEX versions.
- static bool ValidateApexVersions(const OatFile& oat_file, std::string* error_msg);
+ // Returns true if the APEX versions in the OAT file match the given APEX versions.
+ static bool ValidateApexVersions(const OatFile& oat_file,
+ const std::string& apex_versions,
+ std::string* error_msg);
// 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
@@ -279,17 +272,23 @@
// oat and odex file.
//
// This function is exposed for testing purposes.
+ //
+ // Calling this function requires an active runtime.
static bool ValidateOatFile(const OatFile& oat_file, std::string* error_msg);
// Same as above, but allows to use `dex_filenames` and `dex_fds` to find the dex files instead of
- // using the dex filenames in the header of the oat file. This overload is useful when the actual
- // dex filenames are different from what's in the header (e.g., when we run dex2oat on host), or
- // when the runtime can only access files through FDs (e.g., when we run dex2oat on target in a
- // restricted SELinux domain).
+ // using the dex filenames in the header of the oat file, and also takes `apex_versions` from the
+ // input. This overload is useful when the actual dex filenames are different from what's in the
+ // header (e.g., when we run dex2oat on host), when the runtime can only access files through FDs
+ // (e.g., when we run dex2oat on target in a restricted SELinux domain), or when there is no
+ // active runtime.
+ //
+ // Calling this function does not require an active runtime.
static bool ValidateOatFile(const OatFile& oat_file,
std::string* error_msg,
ArrayRef<const std::string> dex_filenames,
- ArrayRef<const int> dex_fds);
+ ArrayRef<const int> dex_fds,
+ const std::string& apex_versions);
// Return the end of the image which includes non-heap objects such as ArtMethods and ArtFields.
uint8_t* GetImageEnd() const {
@@ -303,6 +302,182 @@
void ReleaseMetadata() REQUIRES_SHARED(Locks::mutator_lock_);
+ static void AppendImageChecksum(uint32_t component_count,
+ uint32_t checksum,
+ /*inout*/ std::string* checksums);
+
+ static size_t CheckAndCountBCPComponents(std::string_view oat_boot_class_path,
+ ArrayRef<const std::string> boot_class_path,
+ /*out*/ std::string* error_msg);
+
+ // Helper class to find the primary boot image and boot image extensions
+ // and determine the boot image layout.
+ class 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;
+ std::vector<std::string> profile_files;
+ size_t start_index;
+ uint32_t component_count;
+ uint32_t image_space_count;
+ uint32_t reservation_size;
+ uint32_t checksum;
+ uint32_t boot_image_component_count;
+ uint32_t boot_image_checksum;
+ uint32_t boot_image_size;
+
+ // The following file descriptors hold the memfd files for extensions compiled
+ // in memory and described by the above fields. We want to use them to mmap()
+ // the contents and then close them while treating the ImageChunk description
+ // as immutable (const), so make these fields explicitly mutable.
+ mutable android::base::unique_fd art_fd;
+ mutable android::base::unique_fd vdex_fd;
+ mutable android::base::unique_fd oat_fd;
+ };
+
+ BootImageLayout(ArrayRef<const std::string> image_locations,
+ ArrayRef<const std::string> boot_class_path,
+ ArrayRef<const std::string> boot_class_path_locations,
+ ArrayRef<const int> boot_class_path_fds,
+ ArrayRef<const int> boot_class_path_image_fds,
+ ArrayRef<const int> boot_class_path_vdex_fds,
+ ArrayRef<const int> boot_class_path_oat_fds,
+ const std::string* apex_versions = nullptr)
+ : image_locations_(image_locations),
+ boot_class_path_(boot_class_path),
+ boot_class_path_locations_(boot_class_path_locations),
+ boot_class_path_fds_(boot_class_path_fds),
+ boot_class_path_image_fds_(boot_class_path_image_fds),
+ boot_class_path_vdex_fds_(boot_class_path_vdex_fds),
+ boot_class_path_oat_fds_(boot_class_path_oat_fds),
+ apex_versions_(GetApexVersions(apex_versions)) {}
+
+ std::string GetPrimaryImageLocation();
+
+ bool LoadFromSystem(InstructionSet image_isa,
+ bool allow_in_memory_compilation,
+ /*out*/ std::string* 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:
+ struct NamedComponentLocation {
+ std::string base_location;
+ size_t bcp_index;
+ std::vector<std::string> profile_filenames;
+ };
+
+ 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);
+ }
+ }
+
+ std::string GetBcpComponentPath(size_t bcp_index) {
+ DCHECK_LE(bcp_index, boot_class_path_.size());
+ size_t bcp_slash_pos = boot_class_path_[bcp_index].rfind('/');
+ DCHECK_NE(bcp_slash_pos, std::string::npos);
+ return boot_class_path_[bcp_index].substr(0u, bcp_slash_pos + 1u);
+ }
+
+ bool VerifyImageLocation(ArrayRef<const 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<NamedComponentLocation>* named_component_locations,
+ /*out*/ std::string* error_msg);
+
+ bool ValidateBootImageChecksum(const char* file_description,
+ const ImageHeader& header,
+ /*out*/ std::string* error_msg);
+
+ bool ValidateHeader(const ImageHeader& header,
+ size_t bcp_index,
+ const char* file_description,
+ /*out*/ std::string* error_msg);
+
+ bool ValidateOatFile(const std::string& base_location,
+ const std::string& base_filename,
+ size_t bcp_index,
+ size_t component_count,
+ /*out*/ std::string* error_msg);
+
+ bool ReadHeader(const std::string& base_location,
+ const std::string& base_filename,
+ size_t bcp_index,
+ /*out*/ std::string* error_msg);
+
+ // Compiles a consecutive subsequence of bootclasspath dex files, whose contents are included in
+ // the profiles specified by `profile_filenames`, starting from `bcp_index`.
+ bool CompileBootclasspathElements(const std::string& base_location,
+ const std::string& base_filename,
+ size_t bcp_index,
+ const std::vector<std::string>& profile_filenames,
+ ArrayRef<const std::string> dependencies,
+ /*out*/ std::string* error_msg);
+
+ // Returns true if a least one chuck has been loaded.
+ template <typename FilenameFn>
+ bool Load(FilenameFn&& filename_fn,
+ bool allow_in_memory_compilation,
+ /*out*/ std::string* error_msg);
+
+ // This function prefers taking APEX versions from the input instead of from the runtime if
+ // possible. If the input is present, `ValidateFromSystem` can work without an active runtime.
+ static const std::string& GetApexVersions(const std::string* apex_versions) {
+ if (apex_versions == nullptr) {
+ DCHECK(Runtime::Current() != nullptr);
+ return Runtime::Current()->GetApexVersions();
+ } else {
+ return *apex_versions;
+ }
+ }
+
+ ArrayRef<const std::string> image_locations_;
+ ArrayRef<const std::string> boot_class_path_;
+ ArrayRef<const std::string> boot_class_path_locations_;
+ ArrayRef<const int> boot_class_path_fds_;
+ ArrayRef<const int> boot_class_path_image_fds_;
+ ArrayRef<const int> boot_class_path_vdex_fds_;
+ ArrayRef<const int> boot_class_path_oat_fds_;
+
+ 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;
+ const std::string& apex_versions_;
+ };
+
protected:
// Tries to initialize an ImageSpace from the given image path, returning null on error.
//
@@ -342,7 +517,6 @@
friend class Space;
private:
- class BootImageLayout;
class BootImageLoader;
template <typename ReferenceVisitor>
class ClassTableVisitor;
diff --git a/runtime/gc/space/image_space_test.cc b/runtime/gc/space/image_space_test.cc
index 3a6d0e1..b85560c 100644
--- a/runtime/gc/space/image_space_test.cc
+++ b/runtime/gc/space/image_space_test.cc
@@ -145,6 +145,7 @@
/*relocate=*/ false,
/*executable=*/ true,
/*extra_reservation_size=*/ 0u,
+ /*allow_in_memory_compilation=*/ false,
&boot_image_spaces,
&extra_reservation);
};
@@ -321,56 +322,6 @@
EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg));
}
-TEST_F(DexoptTest, Checksums) {
- Runtime* runtime = Runtime::Current();
- ASSERT_TRUE(runtime != nullptr);
- ASSERT_FALSE(runtime->GetHeap()->GetBootImageSpaces().empty());
-
- std::vector<std::string> bcp = runtime->GetBootClassPath();
- std::vector<std::string> bcp_locations = runtime->GetBootClassPathLocations();
- std::vector<const DexFile*> dex_files = runtime->GetClassLinker()->GetBootClassPath();
-
- std::string error_msg;
- auto create_and_verify = [&]() {
- std::string checksums = gc::space::ImageSpace::GetBootClassPathChecksums(
- ArrayRef<gc::space::ImageSpace* const>(runtime->GetHeap()->GetBootImageSpaces()),
- ArrayRef<const DexFile* const>(dex_files));
- return gc::space::ImageSpace::VerifyBootClassPathChecksums(
- checksums,
- android::base::Join(bcp_locations, ':'),
- ArrayRef<const std::string>(runtime->GetImageLocations()),
- ArrayRef<const std::string>(bcp_locations),
- ArrayRef<const std::string>(bcp),
- /*boot_class_path_fds=*/ ArrayRef<const int>(),
- kRuntimeISA,
- &error_msg);
- };
-
- ASSERT_TRUE(create_and_verify()) << error_msg;
-
- std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
- for (const std::string& src : { GetDexSrc1(), GetDexSrc2() }) {
- std::vector<std::unique_ptr<const DexFile>> new_dex_files;
- const ArtDexFileLoader dex_file_loader;
- ASSERT_TRUE(dex_file_loader.Open(src.c_str(),
- src,
- /*verify=*/ true,
- /*verify_checksum=*/ false,
- &error_msg,
- &new_dex_files))
- << error_msg;
-
- bcp.push_back(src);
- bcp_locations.push_back(src);
- for (std::unique_ptr<const DexFile>& df : new_dex_files) {
- dex_files.push_back(df.get());
- opened_dex_files.push_back(std::move(df));
- }
-
- ASSERT_TRUE(create_and_verify()) << error_msg;
- }
-}
-
template <bool kImage, bool kRelocate>
class ImageSpaceLoadingTest : public CommonRuntimeTest {
protected:
@@ -380,6 +331,7 @@
options->emplace_back(android::base::StringPrintf("-Ximage:%s", image_location.c_str()),
nullptr);
options->emplace_back(kRelocate ? "-Xrelocate" : "-Xnorelocate", nullptr);
+ options->emplace_back("-Xallowinmemorycompilation", nullptr);
// We want to test the relocation behavior of ImageSpace. As such, don't pretend we're a
// compiler.
diff --git a/runtime/gc/space/large_object_space.cc b/runtime/gc/space/large_object_space.cc
index 2d17a18..f1df45f 100644
--- a/runtime/gc/space/large_object_space.cc
+++ b/runtime/gc/space/large_object_space.cc
@@ -336,7 +336,7 @@
size_t FreeListSpace::GetSlotIndexForAllocationInfo(const AllocationInfo* info) const {
DCHECK_GE(info, allocation_info_);
- DCHECK_LT(info, reinterpret_cast<AllocationInfo*>(allocation_info_map_.End()));
+ DCHECK_LE(info, reinterpret_cast<AllocationInfo*>(allocation_info_map_.End()));
return info - allocation_info_;
}
@@ -457,6 +457,10 @@
// The previous allocation info must not be free since we are supposed to always coalesce.
DCHECK_EQ(info->GetPrevFreeBytes(), 0U) << "Previous allocation was free";
}
+ // NOTE: next_info could be pointing right after the allocation_info_map_
+ // when freeing object in the very end of the space. But that's safe
+ // as we don't dereference it in that case. We only use it to calculate
+ // next_addr using offset within the map.
uintptr_t next_addr = GetAddressForAllocationInfo(next_info);
if (next_addr >= free_end_start) {
// Easy case, the next chunk is the end free region.
diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h
index 5000656..59ab3f3 100644
--- a/runtime/gc/space/malloc_space.h
+++ b/runtime/gc/space/malloc_space.h
@@ -38,7 +38,7 @@
// A common parent of DlMallocSpace and RosAllocSpace.
class MallocSpace : public ContinuousMemMapAllocSpace {
public:
- typedef void(*WalkCallback)(void *start, void *end, size_t num_bytes, void* callback_arg);
+ using WalkCallback = void (*)(void *start, void *end, size_t num_bytes, void* callback_arg);
SpaceType GetType() const override {
return kSpaceTypeMallocSpace;
diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc
index 171c5cd..60141d6 100644
--- a/runtime/gc/space/region_space.cc
+++ b/runtime/gc/space/region_space.cc
@@ -36,7 +36,7 @@
static constexpr bool kProtectClearedRegions = kIsDebugBuild;
// Wether we poison memory areas occupied by dead objects in unevacuated regions.
-static constexpr bool kPoisonDeadObjectsInUnevacuatedRegions = true;
+static constexpr bool kPoisonDeadObjectsInUnevacuatedRegions = kIsDebugBuild;
// Special 32-bit value used to poison memory areas occupied by dead
// objects in unevacuated regions. Dereferencing this value is expected
@@ -741,10 +741,19 @@
max_contiguous_allocation = std::min(max_contiguous_allocation,
regions_free_for_alloc * kRegionSize);
if (failed_alloc_bytes > max_contiguous_allocation) {
+ // Region space does not normally fragment in the conventional sense. However we can run out
+ // of region space prematurely if we have many threads, each with a partially committed TLAB.
+ // The whole TLAB uses up region address space, but we only count the section that was
+ // actually given to the thread so far as allocated. For unlikely allocation request sequences
+ // involving largish objects that don't qualify for large objects space, we may also be unable
+ // to fully utilize entire TLABs, and thus generate enough actual fragmentation to get
+ // here. This appears less likely, since we usually reuse sufficiently large TLAB "tails"
+ // that are no longer needed.
os << "; failed due to fragmentation (largest possible contiguous allocation "
- << max_contiguous_allocation << " bytes). Number of "
- << PrettySize(kRegionSize)
- << " sized free regions are: " << regions_free_for_alloc;
+ << max_contiguous_allocation << " bytes). Number of " << PrettySize(kRegionSize)
+ << " sized free regions are: " << regions_free_for_alloc
+ << ". Likely cause: (1) Too much memory in use, and "
+ << "(2) many threads or many larger objects of the wrong kind";
return true;
}
// Caller's job to print failed_alloc_bytes.
diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h
index 1463eb7..27b9e9c 100644
--- a/runtime/gc/space/region_space.h
+++ b/runtime/gc/space/region_space.h
@@ -46,7 +46,7 @@
// A space that consists of equal-sized regions.
class RegionSpace final : public ContinuousMemMapAllocSpace {
public:
- typedef void(*WalkCallback)(void *start, void *end, size_t num_bytes, void* callback_arg);
+ using WalkCallback = void (*)(void *start, void *end, size_t num_bytes, void* callback_arg);
enum EvacMode {
kEvacModeNewlyAllocated,
diff --git a/runtime/gc/system_weak.h b/runtime/gc/system_weak.h
index ef85b39..77b9548 100644
--- a/runtime/gc/system_weak.h
+++ b/runtime/gc/system_weak.h
@@ -48,7 +48,7 @@
void Allow() override
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!allow_disallow_lock_) {
- CHECK(!kUseReadBarrier);
+ CHECK(!gUseReadBarrier);
MutexLock mu(Thread::Current(), allow_disallow_lock_);
allow_new_system_weak_ = true;
new_weak_condition_.Broadcast(Thread::Current());
@@ -57,7 +57,7 @@
void Disallow() override
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!allow_disallow_lock_) {
- CHECK(!kUseReadBarrier);
+ CHECK(!gUseReadBarrier);
MutexLock mu(Thread::Current(), allow_disallow_lock_);
allow_new_system_weak_ = false;
}
@@ -78,8 +78,8 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(allow_disallow_lock_) {
// Wait for GC's sweeping to complete and allow new records
- while (UNLIKELY((!kUseReadBarrier && !allow_new_system_weak_) ||
- (kUseReadBarrier && !self->GetWeakRefAccessEnabled()))) {
+ while (UNLIKELY((!gUseReadBarrier && !allow_new_system_weak_) ||
+ (gUseReadBarrier && !self->GetWeakRefAccessEnabled()))) {
// Check and run the empty checkpoint before blocking so the empty checkpoint will work in the
// presence of threads blocking for weak ref access.
self->CheckEmptyCheckpointFromWeakRefAccess(&allow_disallow_lock_);
diff --git a/runtime/gc/system_weak_test.cc b/runtime/gc/system_weak_test.cc
index ca11297..dd93653 100644
--- a/runtime/gc/system_weak_test.cc
+++ b/runtime/gc/system_weak_test.cc
@@ -35,6 +35,10 @@
namespace gc {
class SystemWeakTest : public CommonRuntimeTest {
+ protected:
+ SystemWeakTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
};
struct CountingSystemWeakHolder : public SystemWeakHolder {
@@ -111,6 +115,7 @@
CollectorType type = Runtime::Current()->GetHeap()->CurrentCollectorType();
switch (type) {
case CollectorType::kCollectorTypeCMS:
+ case CollectorType::kCollectorTypeCMC:
case CollectorType::kCollectorTypeCC:
case CollectorType::kCollectorTypeSS:
return true;
@@ -124,6 +129,7 @@
CollectorType type = Runtime::Current()->GetHeap()->CurrentCollectorType();
switch (type) {
case CollectorType::kCollectorTypeCMS:
+ case CollectorType::kCollectorTypeCMC:
return true;
default:
@@ -149,7 +155,12 @@
// Expect the holder to have been called.
EXPECT_EQ(CollectorDoesAllowOrBroadcast() ? 1U : 0U, cswh.allow_count_);
EXPECT_EQ(CollectorDoesDisallow() ? 1U : 0U, cswh.disallow_count_);
- EXPECT_EQ(1U, cswh.sweep_count_);
+ // Userfaultfd GC uses SweepSystemWeaks also for concurrent updation.
+ // TODO: Explore this can be reverted back to unconditionally compare with 1
+ // once concurrent updation of native roots is full implemented in userfaultfd
+ // GC.
+ size_t expected_sweep_count = gUseUserfaultfd ? 2U : 1U;
+ EXPECT_EQ(expected_sweep_count, cswh.sweep_count_);
// Expect the weak to not be cleared.
EXPECT_FALSE(cswh.Get().IsNull());
@@ -170,7 +181,12 @@
// Expect the holder to have been called.
EXPECT_EQ(CollectorDoesAllowOrBroadcast() ? 1U : 0U, cswh.allow_count_);
EXPECT_EQ(CollectorDoesDisallow() ? 1U : 0U, cswh.disallow_count_);
- EXPECT_EQ(1U, cswh.sweep_count_);
+ // Userfaultfd GC uses SweepSystemWeaks also for concurrent updation.
+ // TODO: Explore this can be reverted back to unconditionally compare with 1
+ // once concurrent updation of native roots is full implemented in userfaultfd
+ // GC.
+ size_t expected_sweep_count = gUseUserfaultfd ? 2U : 1U;
+ EXPECT_EQ(expected_sweep_count, cswh.sweep_count_);
// Expect the weak to be cleared.
EXPECT_TRUE(cswh.Get().IsNull());
@@ -194,7 +210,12 @@
// Expect the holder to have been called.
ASSERT_EQ(CollectorDoesAllowOrBroadcast() ? 1U : 0U, cswh.allow_count_);
ASSERT_EQ(CollectorDoesDisallow() ? 1U : 0U, cswh.disallow_count_);
- ASSERT_EQ(1U, cswh.sweep_count_);
+ // Userfaultfd GC uses SweepSystemWeaks also for concurrent updation.
+ // TODO: Explore this can be reverted back to unconditionally compare with 1
+ // once concurrent updation of native roots is full implemented in userfaultfd
+ // GC.
+ size_t expected_sweep_count = gUseUserfaultfd ? 2U : 1U;
+ EXPECT_EQ(expected_sweep_count, cswh.sweep_count_);
// Expect the weak to not be cleared.
ASSERT_FALSE(cswh.Get().IsNull());
@@ -209,7 +230,7 @@
// Expectation: no change in the numbers.
EXPECT_EQ(CollectorDoesAllowOrBroadcast() ? 1U : 0U, cswh.allow_count_);
EXPECT_EQ(CollectorDoesDisallow() ? 1U : 0U, cswh.disallow_count_);
- EXPECT_EQ(1U, cswh.sweep_count_);
+ EXPECT_EQ(expected_sweep_count, cswh.sweep_count_);
}
} // namespace gc
diff --git a/runtime/gc/verification-inl.h b/runtime/gc/verification-inl.h
new file mode 100644
index 0000000..1ef96e2
--- /dev/null
+++ b/runtime/gc/verification-inl.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_GC_VERIFICATION_INL_H_
+#define ART_RUNTIME_GC_VERIFICATION_INL_H_
+
+#include "verification.h"
+
+#include "mirror/class-inl.h"
+
+namespace art {
+namespace gc {
+
+template <ReadBarrierOption kReadBarrierOption>
+bool Verification::IsValidClassUnchecked(mirror::Class* klass) const {
+ mirror::Class* k1 = klass->GetClass<kVerifyNone, kReadBarrierOption>();
+ if (!IsValidHeapObjectAddress(k1)) {
+ return false;
+ }
+ // `k1` should be class class, take the class again to verify.
+ // Note that this check may not be valid for the no image space
+ // since the class class might move around from moving GC.
+ mirror::Class* k2 = k1->GetClass<kVerifyNone, kReadBarrierOption>();
+ if (!IsValidHeapObjectAddress(k2)) {
+ return false;
+ }
+ return k1 == k2;
+}
+
+template <ReadBarrierOption kReadBarrierOption>
+bool Verification::IsValidClass(mirror::Class* klass) const {
+ if (!IsValidHeapObjectAddress(klass)) {
+ return false;
+ }
+ return IsValidClassUnchecked<kReadBarrierOption>(klass);
+}
+
+template <ReadBarrierOption kReadBarrierOption>
+bool Verification::IsValidObject(mirror::Object* obj) const {
+ if (!IsValidHeapObjectAddress(obj)) {
+ return false;
+ }
+ mirror::Class* klass = obj->GetClass<kVerifyNone, kReadBarrierOption>();
+ return IsValidClass(klass);
+}
+
+} // namespace gc
+} // namespace art
+
+#endif // ART_RUNTIME_GC_VERIFICATION_INL_H_
diff --git a/runtime/gc/verification.cc b/runtime/gc/verification.cc
index 9e0b8a2..195986f 100644
--- a/runtime/gc/verification.cc
+++ b/runtime/gc/verification.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "verification.h"
+#include "verification-inl.h"
#include <iomanip>
#include <sstream>
@@ -29,23 +29,16 @@
namespace gc {
std::string Verification::DumpRAMAroundAddress(uintptr_t addr, uintptr_t bytes) const {
- const uintptr_t dump_start = addr - bytes;
- const uintptr_t dump_end = addr + bytes;
+ uintptr_t* dump_start = reinterpret_cast<uintptr_t*>(addr - bytes);
+ uintptr_t* dump_end = reinterpret_cast<uintptr_t*>(addr + bytes);
std::ostringstream oss;
- if (dump_start < dump_end &&
- IsAddressInHeapSpace(reinterpret_cast<const void*>(dump_start)) &&
- IsAddressInHeapSpace(reinterpret_cast<const void*>(dump_end - 1))) {
- oss << " adjacent_ram=";
- for (uintptr_t p = dump_start; p < dump_end; ++p) {
- if (p == addr) {
- // Marker of where the address is.
- oss << "|";
- }
- uint8_t* ptr = reinterpret_cast<uint8_t*>(p);
- oss << std::hex << std::setfill('0') << std::setw(2) << static_cast<uintptr_t>(*ptr);
+ oss << " adjacent_ram=";
+ for (const uintptr_t* p = dump_start; p < dump_end; ++p) {
+ if (p == reinterpret_cast<uintptr_t*>(addr)) {
+ // Marker of where the address is.
+ oss << "|";
}
- } else {
- oss << " <invalid address>";
+ oss << std::hex << std::setfill('0') << std::setw(sizeof(uintptr_t) * 2) << *p << " ";
}
return oss.str();
}
@@ -93,7 +86,7 @@
std::ostringstream oss;
oss << "GC tried to mark invalid reference " << ref << std::endl;
oss << DumpObjectInfo(ref, "ref") << "\n";
- oss << DumpObjectInfo(holder.Ptr(), "holder");
+ oss << DumpObjectInfo(holder.Ptr(), "holder") << "\n";
if (holder != nullptr) {
mirror::Class* holder_klass = holder->GetClass<kVerifyNone, kWithoutReadBarrier>();
if (IsValidClass(holder_klass)) {
@@ -132,25 +125,6 @@
return IsAligned<kObjectAlignment>(addr) && IsAddressInHeapSpace(addr, out_space);
}
-bool Verification::IsValidClass(const void* addr) const {
- if (!IsValidHeapObjectAddress(addr)) {
- return false;
- }
- mirror::Class* klass = reinterpret_cast<mirror::Class*>(const_cast<void*>(addr));
- mirror::Class* k1 = klass->GetClass<kVerifyNone, kWithoutReadBarrier>();
- if (!IsValidHeapObjectAddress(k1)) {
- return false;
- }
- // `k1` should be class class, take the class again to verify.
- // Note that this check may not be valid for the no image space since the class class might move
- // around from moving GC.
- mirror::Class* k2 = k1->GetClass<kVerifyNone, kWithoutReadBarrier>();
- if (!IsValidHeapObjectAddress(k2)) {
- return false;
- }
- return k1 == k2;
-}
-
using ObjectSet = std::set<mirror::Object*>;
using WorkQueue = std::deque<std::pair<mirror::Object*, std::string>>;
diff --git a/runtime/gc/verification.h b/runtime/gc/verification.h
index 6b456fd..7a5d01a 100644
--- a/runtime/gc/verification.h
+++ b/runtime/gc/verification.h
@@ -19,6 +19,7 @@
#include "obj_ptr.h"
#include "offsets.h"
+#include "read_barrier_option.h"
namespace art {
@@ -50,7 +51,16 @@
bool fatal) const REQUIRES_SHARED(Locks::mutator_lock_);
// Return true if the klass is likely to be a valid mirror::Class.
- bool IsValidClass(const void* klass) const REQUIRES_SHARED(Locks::mutator_lock_);
+ // Returns true if the class is a valid mirror::Class or possibly spuriously.
+ template <ReadBarrierOption kReadBarrierOption = kWithoutReadBarrier>
+ bool IsValidClassUnchecked(mirror::Class* klass) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // Return true if the klass is likely to be a valid mirror::Class.
+ template <ReadBarrierOption kReadBarrierOption = kWithoutReadBarrier>
+ bool IsValidClass(mirror::Class* klass) const REQUIRES_SHARED(Locks::mutator_lock_);
+ // Return true if the obj is likely to be a valid obj with valid mirror::Class.
+ template <ReadBarrierOption kReadBarrierOption = kWithoutReadBarrier>
+ bool IsValidObject(mirror::Object* obj) const REQUIRES_SHARED(Locks::mutator_lock_);
// Does not allow null, checks alignment.
bool IsValidHeapObjectAddress(const void* addr, space::Space** out_space = nullptr) const
diff --git a/runtime/handle.cc b/runtime/handle.cc
index af77e23..e9c9113 100644
--- a/runtime/handle.cc
+++ b/runtime/handle.cc
@@ -42,6 +42,7 @@
namespace art {
+// NOLINTBEGIN(bugprone-macro-parentheses)
#define MAKE_OBJECT_FOR_GDB(ROOT, NAME, MIRROR) \
template <> MIRROR* Handle<MIRROR>::GetFromGdb() { \
return Get(); \
@@ -53,5 +54,6 @@
CLASS_MIRROR_ROOT_LIST(MAKE_OBJECT_FOR_GDB)
#undef MAKE_OBJECT_FOR_GDB
+// NOLINTEND(bugprone-macro-parentheses)
} // namespace art
diff --git a/runtime/handle_scope_test.cc b/runtime/handle_scope_test.cc
index d72dbe6..9207303 100644
--- a/runtime/handle_scope_test.cc
+++ b/runtime/handle_scope_test.cc
@@ -38,7 +38,12 @@
static_assert(std::is_trivially_copyable<ScopedNullHandle<mirror::Object>>::value,
"ScopedNullHandle should be trivially copyable");
-class HandleScopeTest : public CommonRuntimeTest {};
+class HandleScopeTest : public CommonRuntimeTest {
+ protected:
+ HandleScopeTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+};
// Test the offsets computed for members of HandleScope. Because of cross-compiling
// it is impossible the use OFFSETOF_MEMBER, so we do some reasonable computations ourselves. This
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
index 54acbb4..6acbe51 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -149,7 +149,7 @@
void InitializeCorePlatformApiPrivateFields() {
// The following fields in WellKnownClasses correspond to private fields in the Core Platform
// API that cannot be otherwise expressed and propagated through tooling (b/144502743).
- jfieldID private_core_platform_api_fields[] = {
+ ArtField* private_core_platform_api_fields[] = {
WellKnownClasses::java_io_FileDescriptor_descriptor,
WellKnownClasses::java_nio_Buffer_address,
WellKnownClasses::java_nio_Buffer_elementSizeShift,
@@ -158,8 +158,7 @@
};
ScopedObjectAccess soa(Thread::Current());
- for (const auto private_core_platform_api_field : private_core_platform_api_fields) {
- ArtField* field = jni::DecodeArtField(private_core_platform_api_field);
+ for (ArtField* field : private_core_platform_api_fields) {
const uint32_t access_flags = field->GetAccessFlags();
uint32_t new_access_flags = access_flags | kAccCorePlatformApi;
DCHECK(new_access_flags != access_flags);
@@ -367,30 +366,35 @@
if (runtime->IsAotCompiler()) {
return;
}
- JNIEnvExt* env = Thread::Current()->GetJniEnv();
- const std::string& package_name = Runtime::Current()->GetProcessPackageName();
- ScopedLocalRef<jstring> package_str(env, env->NewStringUTF(package_name.c_str()));
- if (env->ExceptionCheck()) {
- env->ExceptionClear();
- LOG(ERROR) << "Unable to allocate string for package name which called hidden api";
- }
+
+ const std::string& package_name = runtime->GetProcessPackageName();
std::ostringstream signature_str;
Dump(signature_str);
- ScopedLocalRef<jstring> signature_jstr(env,
- env->NewStringUTF(signature_str.str().c_str()));
- if (env->ExceptionCheck()) {
- env->ExceptionClear();
+
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<2u> hs(soa.Self());
+ Handle<mirror::String> package_str = hs.NewHandle(
+ mirror::String::AllocFromModifiedUtf8(soa.Self(), package_name.c_str()));
+ if (soa.Self()->IsExceptionPending()) {
+ soa.Self()->ClearException();
+ LOG(ERROR) << "Unable to allocate string for package name which called hidden api";
+ }
+ Handle<mirror::String> signature_jstr = hs.NewHandle(
+ mirror::String::AllocFromModifiedUtf8(soa.Self(), signature_str.str().c_str()));
+ if (soa.Self()->IsExceptionPending()) {
+ soa.Self()->ClearException();
LOG(ERROR) << "Unable to allocate string for hidden api method signature";
}
- env->CallStaticVoidMethod(WellKnownClasses::dalvik_system_VMRuntime,
- WellKnownClasses::dalvik_system_VMRuntime_hiddenApiUsed,
- sampled_value,
- package_str.get(),
- signature_jstr.get(),
- static_cast<jint>(access_method),
- access_denied);
- if (env->ExceptionCheck()) {
- env->ExceptionClear();
+ WellKnownClasses::dalvik_system_VMRuntime_hiddenApiUsed
+ ->InvokeStatic<'V', 'I', 'L', 'L', 'I', 'Z'>(
+ soa.Self(),
+ static_cast<jint>(sampled_value),
+ package_str.Get(),
+ signature_jstr.Get(),
+ static_cast<jint>(access_method),
+ access_denied);
+ if (soa.Self()->IsExceptionPending()) {
+ soa.Self()->ClearException();
LOG(ERROR) << "Unable to report hidden api usage";
}
#else
@@ -408,26 +412,28 @@
Runtime* runtime = Runtime::Current();
if (!runtime->IsAotCompiler()) {
- ScopedObjectAccessUnchecked soa(Thread::Current());
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<2u> hs(soa.Self());
- ScopedLocalRef<jobject> consumer_object(soa.Env(),
- soa.Env()->GetStaticObjectField(
- WellKnownClasses::dalvik_system_VMRuntime,
- WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer));
+ ArtField* consumer_field = WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
+ DCHECK(consumer_field->GetDeclaringClass()->IsInitialized());
+ Handle<mirror::Object> consumer_object =
+ hs.NewHandle(consumer_field->GetObject(consumer_field->GetDeclaringClass()));
+
// If the consumer is non-null, we call back to it to let it know that we
// have encountered an API that's in one of our lists.
if (consumer_object != nullptr) {
std::ostringstream member_signature_str;
Dump(member_signature_str);
- ScopedLocalRef<jobject> signature_str(
- soa.Env(),
- soa.Env()->NewStringUTF(member_signature_str.str().c_str()));
+ Handle<mirror::String> signature_str = hs.NewHandle(
+ mirror::String::AllocFromModifiedUtf8(soa.Self(), member_signature_str.str().c_str()));
+ // FIXME: Handle OOME. For now, crash immediatelly (do not continue with a pending exception).
+ CHECK(signature_str != nullptr);
// Call through to Consumer.accept(String memberSignature);
- soa.Env()->CallVoidMethod(consumer_object.get(),
- WellKnownClasses::java_util_function_Consumer_accept,
- signature_str.get());
+ WellKnownClasses::java_util_function_Consumer_accept->InvokeInterface<'V', 'L'>(
+ soa.Self(), consumer_object.Get(), signature_str.Get());
}
}
}
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index 1eb5e17..281c1f3 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -29,7 +29,6 @@
#include "mirror/class_loader.h"
#include "reflection.h"
#include "runtime.h"
-#include "well_known_classes.h"
namespace art {
namespace hiddenapi {
@@ -123,7 +122,7 @@
if (domain == Domain::kApplication &&
klass->ShouldSkipHiddenApiChecks() &&
- Runtime::Current()->IsJavaDebuggable()) {
+ Runtime::Current()->IsJavaDebuggableAtInit()) {
// Class is known, it is marked trusted and we are in debuggable mode.
domain = ComputeDomain(/* is_trusted= */ true);
}
diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc
index e204c57..a3d5db1 100644
--- a/runtime/hidden_api_test.cc
+++ b/runtime/hidden_api_test.cc
@@ -26,7 +26,7 @@
#include "common_runtime_test.h"
#include "jni/jni_internal.h"
#include "proxy_test.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -59,7 +59,7 @@
}
static bool LoadDexFiles(const std::string& path,
- ScopedObjectAccess& soa,
+ Thread* self,
/* out */ std::vector<std::unique_ptr<const DexFile>>* dex_files,
/* out */ ObjPtr<mirror::ClassLoader>* class_loader,
/* out */ std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -74,11 +74,11 @@
ClassLinker* const linker = Runtime::Current()->GetClassLinker();
- StackHandleScope<2> hs(soa.Self());
- Handle<mirror::Class> h_class = hs.NewHandle(soa.Decode<mirror::Class>(
- WellKnownClasses::dalvik_system_PathClassLoader));
+ StackHandleScope<2> hs(self);
+ Handle<mirror::Class> h_class =
+ hs.NewHandle(WellKnownClasses::dalvik_system_PathClassLoader.Get());
Handle<mirror::ClassLoader> h_loader = hs.NewHandle(linker->CreateWellKnownClassLoader(
- soa.Self(),
+ self,
MakeNonOwningPointerVector(*dex_files),
h_class,
/* parent_loader= */ ScopedNullHandle<mirror::ClassLoader>(),
@@ -198,7 +198,7 @@
ObjPtr<mirror::ClassLoader> class_loader;
ASSERT_TRUE(Copy(GetTestDexFileName("Main"), location, &error_msg)) << error_msg;
- ASSERT_TRUE(LoadDexFiles(location, soa, &dex_files, &class_loader, &error_msg))
+ ASSERT_TRUE(LoadDexFiles(location, soa.Self(), &dex_files, &class_loader, &error_msg))
<< error_msg;
ASSERT_GE(dex_files.size(), 1u);
ASSERT_TRUE(CheckAllDexFilesInDomain(class_loader,
diff --git a/runtime/image.cc b/runtime/image.cc
index b39ffe5..133782d 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -19,6 +19,8 @@
#include <lz4.h>
#include <sstream>
+#include "android-base/stringprintf.h"
+
#include "base/bit_utils.h"
#include "base/length_prefixed_array.h"
#include "base/utils.h"
@@ -29,8 +31,8 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-// Last change: Math.fma(double, double, double) intrinsic.
-const uint8_t ImageHeader::kImageVersion[] = { '1', '0', '6', '\0' };
+// Last change: StringBuilderAppend for float/double.
+const uint8_t ImageHeader::kImageVersion[] = { '1', '0', '7', '\0' };
ImageHeader::ImageHeader(uint32_t image_reservation_size,
uint32_t component_count,
@@ -65,12 +67,14 @@
image_roots_(image_roots),
pointer_size_(pointer_size) {
CHECK_EQ(image_begin, RoundUp(image_begin, kPageSize));
- CHECK_EQ(oat_file_begin, RoundUp(oat_file_begin, kPageSize));
- CHECK_EQ(oat_data_begin, RoundUp(oat_data_begin, kPageSize));
- CHECK_LT(image_roots, oat_file_begin);
- CHECK_LE(oat_file_begin, oat_data_begin);
- CHECK_LT(oat_data_begin, oat_data_end);
- CHECK_LE(oat_data_end, oat_file_end);
+ if (oat_checksum != 0u) {
+ CHECK_EQ(oat_file_begin, RoundUp(oat_file_begin, kPageSize));
+ CHECK_EQ(oat_data_begin, RoundUp(oat_data_begin, kPageSize));
+ CHECK_LT(image_roots, oat_file_begin);
+ CHECK_LE(oat_file_begin, oat_data_begin);
+ CHECK_LT(oat_data_begin, oat_data_end);
+ CHECK_LE(oat_data_end, oat_file_end);
+ }
CHECK(ValidPointerSize(pointer_size_)) << pointer_size_;
memcpy(magic_, kImageMagic, sizeof(kImageMagic));
memcpy(version_, kImageVersion, sizeof(kImageVersion));
@@ -127,14 +131,16 @@
if (image_begin_ >= image_begin_ + image_size_) {
return false;
}
- if (oat_file_begin_ > oat_file_end_) {
- return false;
- }
- if (oat_data_begin_ > oat_data_end_) {
- return false;
- }
- if (oat_file_begin_ >= oat_data_begin_) {
- return false;
+ if (oat_checksum_ != 0u) {
+ if (oat_file_begin_ > oat_file_end_) {
+ return false;
+ }
+ if (oat_data_begin_ > oat_data_end_) {
+ return false;
+ }
+ if (oat_file_begin_ >= oat_data_begin_) {
+ return false;
+ }
}
return true;
}
@@ -170,6 +176,23 @@
return ConvertToPointerSize(pointer_size_);
}
+bool LZ4_decompress_safe_checked(const char* source,
+ char* dest,
+ int compressed_size,
+ int max_decompressed_size,
+ /*out*/ size_t* decompressed_size_checked,
+ /*out*/ std::string* error_msg) {
+ int decompressed_size = LZ4_decompress_safe(source, dest, compressed_size, max_decompressed_size);
+ if (UNLIKELY(decompressed_size < 0)) {
+ *error_msg = android::base::StringPrintf("LZ4_decompress_safe() returned negative size: %d",
+ decompressed_size);
+ return false;
+ } else {
+ *decompressed_size_checked = static_cast<size_t>(decompressed_size);
+ return true;
+ }
+}
+
bool ImageHeader::Block::Decompress(uint8_t* out_ptr,
const uint8_t* in_ptr,
std::string* error_msg) const {
@@ -182,11 +205,17 @@
case kStorageModeLZ4:
case kStorageModeLZ4HC: {
// LZ4HC and LZ4 have same internal format, both use LZ4_decompress.
- const size_t decompressed_size = LZ4_decompress_safe(
+ size_t decompressed_size;
+ bool ok = LZ4_decompress_safe_checked(
reinterpret_cast<const char*>(in_ptr) + data_offset_,
reinterpret_cast<char*>(out_ptr) + image_offset_,
data_size_,
- image_size_);
+ image_size_,
+ &decompressed_size,
+ error_msg);
+ if (!ok) {
+ return false;
+ }
CHECK_EQ(decompressed_size, image_size_);
break;
}
diff --git a/runtime/image.h b/runtime/image.h
index 8f045e9..caf1aa5 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -230,6 +230,8 @@
// Aliases.
kAppImageClassLoader = kSpecialRoots, // The class loader used to build the app image.
kBootImageLiveObjects = kSpecialRoots, // Array of boot image objects that must be kept live.
+ kAppImageDexChecksums = kSpecialRoots, // Array of dex checksums for app images generated by
+ // the runtime.
};
enum BootImageLiveObjects {
@@ -502,6 +504,7 @@
uint32_t blocks_count_ = 0u;
friend class linker::ImageWriter;
+ friend class RuntimeImageHelper;
};
/*
@@ -520,6 +523,14 @@
std::ostream& operator<<(std::ostream& os, const ImageSection& section);
+// Wrapper over LZ4_decompress_safe() that checks if return value is negative. See b/242914915.
+bool LZ4_decompress_safe_checked(const char* source,
+ char* dest,
+ int compressed_size,
+ int max_decompressed_size,
+ /*out*/ size_t* decompressed_size_checked,
+ /*out*/ std::string* error_msg);
+
} // namespace art
#endif // ART_RUNTIME_IMAGE_H_
diff --git a/runtime/index_bss_mapping.cc b/runtime/index_bss_mapping.cc
index 8d9d8cf..f6e083d 100644
--- a/runtime/index_bss_mapping.cc
+++ b/runtime/index_bss_mapping.cc
@@ -44,8 +44,6 @@
}
}
-constexpr size_t IndexBssMappingLookup::npos;
-
size_t IndexBssMappingLookup::GetBssOffset(const IndexBssMapping* mapping,
uint32_t index,
uint32_t number_of_indexes,
diff --git a/runtime/indirect_reference_table-inl.h b/runtime/indirect_reference_table-inl.h
index 6ea035b..23df2c8 100644
--- a/runtime/indirect_reference_table-inl.h
+++ b/runtime/indirect_reference_table-inl.h
@@ -37,7 +37,7 @@
/*out*/std::string* error_msg) const {
DCHECK(iref != nullptr);
DCHECK_EQ(GetIndirectRefKind(iref), kind_);
- const uint32_t top_index = segment_state_.top_index;
+ const uint32_t top_index = top_index_;
uint32_t idx = ExtractIndex(iref);
if (UNLIKELY(idx >= top_index)) {
*error_msg = android::base::StringPrintf("deleted reference at index %u in a table of size %u",
@@ -82,7 +82,7 @@
inline ObjPtr<mirror::Object> IndirectReferenceTable::Get(IndirectRef iref) const {
DCHECK_EQ(GetIndirectRefKind(iref), kind_);
uint32_t idx = ExtractIndex(iref);
- DCHECK_LT(idx, segment_state_.top_index);
+ DCHECK_LT(idx, top_index_);
DCHECK_EQ(DecodeSerial(reinterpret_cast<uintptr_t>(iref)), table_[idx].GetSerial());
DCHECK(!table_[idx].GetReference()->IsNull());
ObjPtr<mirror::Object> obj = table_[idx].GetReference()->Read<kReadBarrierOption>();
@@ -93,7 +93,7 @@
inline void IndirectReferenceTable::Update(IndirectRef iref, ObjPtr<mirror::Object> obj) {
DCHECK_EQ(GetIndirectRefKind(iref), kind_);
uint32_t idx = ExtractIndex(iref);
- DCHECK_LT(idx, segment_state_.top_index);
+ DCHECK_LT(idx, top_index_);
DCHECK_EQ(DecodeSerial(reinterpret_cast<uintptr_t>(iref)), table_[idx].GetSerial());
DCHECK(!table_[idx].GetReference()->IsNull());
table_[idx].SetReference(obj);
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index ebf382f..479eda5 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-#include "base/bit_utils.h"
-#include "base/globals.h"
#include "indirect_reference_table-inl.h"
+#include "base/bit_utils.h"
+#include "base/globals.h"
#include "base/mutator_locked_dumpable.h"
#include "base/systrace.h"
#include "base/utils.h"
@@ -26,8 +26,9 @@
#include "jni/jni_internal.h"
#include "mirror/object-inl.h"
#include "nth_caller_visitor.h"
+#include "object_callbacks.h"
#include "reference_table.h"
-#include "runtime.h"
+#include "runtime-inl.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
@@ -35,16 +36,15 @@
namespace art {
-static constexpr bool kDumpStackOnNonLocalReference = false;
static constexpr bool kDebugIRT = false;
// Maximum table size we allow.
static constexpr size_t kMaxTableSizeInBytes = 128 * MB;
-const char* GetIndirectRefKindString(const IndirectRefKind& kind) {
+const char* GetIndirectRefKindString(IndirectRefKind kind) {
switch (kind) {
- case kJniTransitionOrInvalid:
- return "JniTransitionOrInvalid";
+ case kJniTransition:
+ return "JniTransition";
case kLocal:
return "Local";
case kGlobal:
@@ -79,86 +79,37 @@
return result;
}
-SmallIrtAllocator::SmallIrtAllocator()
- : small_irt_freelist_(nullptr), lock_("Small IRT table lock", LockLevel::kGenericBottomLock) {
-}
-
-// Allocate an IRT table for kSmallIrtEntries.
-IrtEntry* SmallIrtAllocator::Allocate(std::string* error_msg) {
- MutexLock lock(Thread::Current(), lock_);
- if (small_irt_freelist_ == nullptr) {
- // Refill.
- MemMap map = NewIRTMap(kPageSize, error_msg);
- if (map.IsValid()) {
- small_irt_freelist_ = reinterpret_cast<IrtEntry*>(map.Begin());
- for (uint8_t* p = map.Begin(); p + kInitialIrtBytes < map.End(); p += kInitialIrtBytes) {
- *reinterpret_cast<IrtEntry**>(p) = reinterpret_cast<IrtEntry*>(p + kInitialIrtBytes);
- }
- shared_irt_maps_.emplace_back(std::move(map));
- }
- }
- if (small_irt_freelist_ == nullptr) {
- return nullptr;
- }
- IrtEntry* result = small_irt_freelist_;
- small_irt_freelist_ = *reinterpret_cast<IrtEntry**>(small_irt_freelist_);
- // Clear pointer in first entry.
- new(result) IrtEntry();
- return result;
-}
-
-void SmallIrtAllocator::Deallocate(IrtEntry* unneeded) {
- MutexLock lock(Thread::Current(), lock_);
- *reinterpret_cast<IrtEntry**>(unneeded) = small_irt_freelist_;
- small_irt_freelist_ = unneeded;
-}
-
-IndirectReferenceTable::IndirectReferenceTable(size_t max_count,
- IndirectRefKind desired_kind,
- ResizableCapacity resizable,
- std::string* error_msg)
- : segment_state_(kIRTFirstSegment),
+IndirectReferenceTable::IndirectReferenceTable(IndirectRefKind kind)
+ : table_mem_map_(),
table_(nullptr),
- kind_(desired_kind),
- max_entries_(max_count),
- current_num_holes_(0),
- resizable_(resizable) {
+ kind_(kind),
+ top_index_(0u),
+ max_entries_(0u),
+ current_num_holes_(0) {
+ CHECK_NE(kind, kJniTransition);
+ CHECK_NE(kind, kLocal);
+}
+
+bool IndirectReferenceTable::Initialize(size_t max_count, std::string* error_msg) {
CHECK(error_msg != nullptr);
- CHECK_NE(desired_kind, kJniTransitionOrInvalid);
// Overflow and maximum check.
CHECK_LE(max_count, kMaxTableSizeInBytes / sizeof(IrtEntry));
- if (max_entries_ <= kSmallIrtEntries) {
- table_ = Runtime::Current()->GetSmallIrtAllocator()->Allocate(error_msg);
- if (table_ != nullptr) {
- max_entries_ = kSmallIrtEntries;
- // table_mem_map_ remains invalid.
- }
+ const size_t table_bytes = RoundUp(max_count * sizeof(IrtEntry), kPageSize);
+ table_mem_map_ = NewIRTMap(table_bytes, error_msg);
+ if (!table_mem_map_.IsValid()) {
+ DCHECK(!error_msg->empty());
+ return false;
}
- if (table_ == nullptr) {
- const size_t table_bytes = RoundUp(max_count * sizeof(IrtEntry), kPageSize);
- table_mem_map_ = NewIRTMap(table_bytes, error_msg);
- if (!table_mem_map_.IsValid() && error_msg->empty()) {
- *error_msg = "Unable to map memory for indirect ref table";
- }
- if (table_mem_map_.IsValid()) {
- table_ = reinterpret_cast<IrtEntry*>(table_mem_map_.Begin());
- } else {
- table_ = nullptr;
- }
- // Take into account the actual length.
- max_entries_ = table_bytes / sizeof(IrtEntry);
- }
- segment_state_ = kIRTFirstSegment;
- last_known_previous_state_ = kIRTFirstSegment;
+ table_ = reinterpret_cast<IrtEntry*>(table_mem_map_.Begin());
+ // Take into account the actual length.
+ max_entries_ = table_bytes / sizeof(IrtEntry);
+ return true;
}
IndirectReferenceTable::~IndirectReferenceTable() {
- if (table_ != nullptr && !table_mem_map_.IsValid()) {
- Runtime::Current()->GetSmallIrtAllocator()->Deallocate(table_);
- }
}
void IndirectReferenceTable::ConstexprChecks() {
@@ -189,10 +140,6 @@
static_assert(DecodeIndex(EncodeIndex(3u)) == 3u, "Index encoding error");
}
-bool IndirectReferenceTable::IsValid() const {
- return table_ != nullptr;
-}
-
// Holes:
//
// To keep the IRT compact, we want to fill "holes" created by non-stack-discipline Add & Remove
@@ -200,37 +147,10 @@
// similar. Instead, we scan for holes, with the expectation that we will find holes fast as they
// are usually near the end of the table (see the header, TODO: verify this assumption). To avoid
// scans when there are no holes, the number of known holes should be tracked.
-//
-// A previous implementation stored the top index and the number of holes as the segment state.
-// This constraints the maximum number of references to 16-bit. We want to relax this, as it
-// is easy to require more references (e.g., to list all classes in large applications). Thus,
-// the implicitly stack-stored state, the IRTSegmentState, is only the top index.
-//
-// Thus, hole count is a local property of the current segment, and needs to be recovered when
-// (or after) a frame is pushed or popped. To keep JNI transitions simple (and inlineable), we
-// cannot do work when the segment changes. Thus, Add and Remove need to ensure the current
-// hole count is correct.
-//
-// To be able to detect segment changes, we require an additional local field that can describe
-// the known segment. This is last_known_previous_state_. The requirement will become clear with
-// the following (some non-trivial) cases that have to be supported:
-//
-// 1) Segment with holes (current_num_holes_ > 0), push new segment, add/remove reference
-// 2) Segment with holes (current_num_holes_ > 0), pop segment, add/remove reference
-// 3) Segment with holes (current_num_holes_ > 0), push new segment, pop segment, add/remove
-// reference
-// 4) Empty segment, push new segment, create a hole, pop a segment, add/remove a reference
-// 5) Base segment, push new segment, create a hole, pop a segment, push new segment, add/remove
-// reference
-//
-// Storing the last known *previous* state (bottom index) allows conservatively detecting all the
-// segment changes above. The condition is simply that the last known state is greater than or
-// equal to the current previous state, and smaller than the current state (top index). The
-// condition is conservative as it adds O(1) overhead to operations on an empty segment.
-static size_t CountNullEntries(const IrtEntry* table, size_t from, size_t to) {
+static size_t CountNullEntries(const IrtEntry* table, size_t to) {
size_t count = 0;
- for (size_t index = from; index != to; ++index) {
+ for (size_t index = 0u; index != to; ++index) {
if (table[index].GetReference()->IsNull()) {
count++;
}
@@ -238,121 +158,37 @@
return count;
}
-void IndirectReferenceTable::RecoverHoles(IRTSegmentState prev_state) {
- if (last_known_previous_state_.top_index >= segment_state_.top_index ||
- last_known_previous_state_.top_index < prev_state.top_index) {
- const size_t top_index = segment_state_.top_index;
- size_t count = CountNullEntries(table_, prev_state.top_index, top_index);
-
- if (kDebugIRT) {
- LOG(INFO) << "+++ Recovered holes: "
- << " Current prev=" << prev_state.top_index
- << " Current top_index=" << top_index
- << " Old num_holes=" << current_num_holes_
- << " New num_holes=" << count;
- }
-
- current_num_holes_ = count;
- last_known_previous_state_ = prev_state;
- } else if (kDebugIRT) {
- LOG(INFO) << "No need to recover holes";
- }
-}
-
ALWAYS_INLINE
static inline void CheckHoleCount(IrtEntry* table,
size_t exp_num_holes,
- IRTSegmentState prev_state,
- IRTSegmentState cur_state) {
+ size_t top_index) {
if (kIsDebugBuild) {
- size_t count = CountNullEntries(table, prev_state.top_index, cur_state.top_index);
- CHECK_EQ(exp_num_holes, count) << "prevState=" << prev_state.top_index
- << " topIndex=" << cur_state.top_index;
+ size_t count = CountNullEntries(table, top_index);
+ CHECK_EQ(exp_num_holes, count) << " topIndex=" << top_index;
}
}
-bool IndirectReferenceTable::Resize(size_t new_size, std::string* error_msg) {
- CHECK_GT(new_size, max_entries_);
-
- constexpr size_t kMaxEntries = kMaxTableSizeInBytes / sizeof(IrtEntry);
- if (new_size > kMaxEntries) {
- *error_msg = android::base::StringPrintf("Requested size exceeds maximum: %zu", new_size);
- return false;
- }
- // Note: the above check also ensures that there is no overflow below.
-
- const size_t table_bytes = RoundUp(new_size * sizeof(IrtEntry), kPageSize);
-
- MemMap new_map = NewIRTMap(table_bytes, error_msg);
- if (!new_map.IsValid()) {
- return false;
- }
-
- memcpy(new_map.Begin(), table_, max_entries_ * sizeof(IrtEntry));
- if (!table_mem_map_.IsValid()) {
- // Didn't have its own map; deallocate old table.
- Runtime::Current()->GetSmallIrtAllocator()->Deallocate(table_);
- }
- table_mem_map_ = std::move(new_map);
- table_ = reinterpret_cast<IrtEntry*>(table_mem_map_.Begin());
- const size_t real_new_size = table_bytes / sizeof(IrtEntry);
- DCHECK_GE(real_new_size, new_size);
- max_entries_ = real_new_size;
-
- return true;
-}
-
-IndirectRef IndirectReferenceTable::Add(IRTSegmentState previous_state,
- ObjPtr<mirror::Object> obj,
- std::string* error_msg) {
+IndirectRef IndirectReferenceTable::Add(ObjPtr<mirror::Object> obj, std::string* error_msg) {
if (kDebugIRT) {
- LOG(INFO) << "+++ Add: previous_state=" << previous_state.top_index
- << " top_index=" << segment_state_.top_index
- << " last_known_prev_top_index=" << last_known_previous_state_.top_index
+ LOG(INFO) << "+++ Add: top_index=" << top_index_
<< " holes=" << current_num_holes_;
}
- size_t top_index = segment_state_.top_index;
-
CHECK(obj != nullptr);
VerifyObject(obj);
DCHECK(table_ != nullptr);
- if (top_index == max_entries_) {
- if (resizable_ == ResizableCapacity::kNo) {
- std::ostringstream oss;
- oss << "JNI ERROR (app bug): " << kind_ << " table overflow "
- << "(max=" << max_entries_ << ")"
- << MutatorLockedDumpable<IndirectReferenceTable>(*this);
- *error_msg = oss.str();
- return nullptr;
- }
-
- // Try to double space.
- if (std::numeric_limits<size_t>::max() / 2 < max_entries_) {
- std::ostringstream oss;
- oss << "JNI ERROR (app bug): " << kind_ << " table overflow "
- << "(max=" << max_entries_ << ")" << std::endl
- << MutatorLockedDumpable<IndirectReferenceTable>(*this)
- << " Resizing failed: exceeds size_t";
- *error_msg = oss.str();
- return nullptr;
- }
-
- std::string inner_error_msg;
- if (!Resize(max_entries_ * 2, &inner_error_msg)) {
- std::ostringstream oss;
- oss << "JNI ERROR (app bug): " << kind_ << " table overflow "
- << "(max=" << max_entries_ << ")" << std::endl
- << MutatorLockedDumpable<IndirectReferenceTable>(*this)
- << " Resizing failed: " << inner_error_msg;
- *error_msg = oss.str();
- return nullptr;
- }
+ if (top_index_ == max_entries_) {
+ // TODO: Fill holes before reporting error.
+ std::ostringstream oss;
+ oss << "JNI ERROR (app bug): " << kind_ << " table overflow "
+ << "(max=" << max_entries_ << ")"
+ << MutatorLockedDumpable<IndirectReferenceTable>(*this);
+ *error_msg = oss.str();
+ return nullptr;
}
- RecoverHoles(previous_state);
- CheckHoleCount(table_, current_num_holes_, previous_state, segment_state_);
+ CheckHoleCount(table_, current_num_holes_, top_index_);
// We know there's enough room in the table. Now we just need to find
// the right spot. If there's a hole, find it and fill it; otherwise,
@@ -360,26 +196,26 @@
IndirectRef result;
size_t index;
if (current_num_holes_ > 0) {
- DCHECK_GT(top_index, 1U);
+ DCHECK_GT(top_index_, 1U);
// Find the first hole; likely to be near the end of the list.
- IrtEntry* p_scan = &table_[top_index - 1];
+ IrtEntry* p_scan = &table_[top_index_ - 1];
DCHECK(!p_scan->GetReference()->IsNull());
--p_scan;
while (!p_scan->GetReference()->IsNull()) {
- DCHECK_GE(p_scan, table_ + previous_state.top_index);
+ DCHECK_GT(p_scan, table_);
--p_scan;
}
index = p_scan - table_;
current_num_holes_--;
} else {
// Add to the end.
- index = top_index++;
- segment_state_.top_index = top_index;
+ index = top_index_;
+ ++top_index_;
}
table_[index].Add(obj);
result = ToIndirectRef(index);
if (kDebugIRT) {
- LOG(INFO) << "+++ added at " << ExtractIndex(result) << " top=" << segment_state_.top_index
+ LOG(INFO) << "+++ added at " << ExtractIndex(result) << " top=" << top_index_
<< " holes=" << current_num_holes_;
}
@@ -387,72 +223,31 @@
return result;
}
-void IndirectReferenceTable::AssertEmpty() {
- for (size_t i = 0; i < Capacity(); ++i) {
- if (!table_[i].GetReference()->IsNull()) {
- LOG(FATAL) << "Internal Error: non-empty local reference table\n"
- << MutatorLockedDumpable<IndirectReferenceTable>(*this);
- UNREACHABLE();
- }
- }
-}
-
// Removes an object. We extract the table offset bits from "iref"
// and zap the corresponding entry, leaving a hole if it's not at the top.
-// If the entry is not between the current top index and the bottom index
-// specified by the cookie, we don't remove anything. This is the behavior
-// required by JNI's DeleteLocalRef function.
-// This method is not called when a local frame is popped; this is only used
-// for explicit single removals.
// Returns "false" if nothing was removed.
-bool IndirectReferenceTable::Remove(IRTSegmentState previous_state, IndirectRef iref) {
+bool IndirectReferenceTable::Remove(IndirectRef iref) {
if (kDebugIRT) {
- LOG(INFO) << "+++ Remove: previous_state=" << previous_state.top_index
- << " top_index=" << segment_state_.top_index
- << " last_known_prev_top_index=" << last_known_previous_state_.top_index
+ LOG(INFO) << "+++ Remove: top_index=" << top_index_
<< " holes=" << current_num_holes_;
}
- const uint32_t top_index = segment_state_.top_index;
- const uint32_t bottom_index = previous_state.top_index;
+ // TODO: We should eagerly check the ref kind against the `kind_` instead of postponing until
+ // `CheckEntry()` below. Passing the wrong kind shall currently result in misleading warnings.
+
+ const uint32_t top_index = top_index_;
DCHECK(table_ != nullptr);
- // TODO: We should eagerly check the ref kind against the `kind_` instead of
- // relying on this weak check and postponing the rest until `CheckEntry()` below.
- // Passing the wrong kind shall currently result in misleading warnings.
- if (GetIndirectRefKind(iref) == kJniTransitionOrInvalid) {
- auto* self = Thread::Current();
- ScopedObjectAccess soa(self);
- if (self->IsJniTransitionReference(reinterpret_cast<jobject>(iref))) {
- auto* env = self->GetJniEnv();
- DCHECK(env != nullptr);
- if (env->IsCheckJniEnabled()) {
- LOG(WARNING) << "Attempt to remove non-JNI local reference, dumping thread";
- if (kDumpStackOnNonLocalReference) {
- self->Dump(LOG_STREAM(WARNING));
- }
- }
- return true;
- }
- }
-
const uint32_t idx = ExtractIndex(iref);
- if (idx < bottom_index) {
- // Wrong segment.
- LOG(WARNING) << "Attempt to remove index outside index area (" << idx
- << " vs " << bottom_index << "-" << top_index << ")";
- return false;
- }
if (idx >= top_index) {
// Bad --- stale reference?
LOG(WARNING) << "Attempt to remove invalid index " << idx
- << " (bottom=" << bottom_index << " top=" << top_index << ")";
+ << " (top=" << top_index << ")";
return false;
}
- RecoverHoles(previous_state);
- CheckHoleCount(table_, current_num_holes_, previous_state, segment_state_);
+ CheckHoleCount(table_, current_num_holes_, top_index_);
if (idx == top_index - 1) {
// Top-most entry. Scan up and consume holes.
@@ -464,11 +259,10 @@
*table_[idx].GetReference() = GcRoot<mirror::Object>(nullptr);
if (current_num_holes_ != 0) {
uint32_t collapse_top_index = top_index;
- while (--collapse_top_index > bottom_index && current_num_holes_ != 0) {
+ while (--collapse_top_index > 0u && current_num_holes_ != 0) {
if (kDebugIRT) {
ScopedObjectAccess soa(Thread::Current());
- LOG(INFO) << "+++ checking for hole at " << collapse_top_index - 1
- << " (previous_state=" << bottom_index << ") val="
+ LOG(INFO) << "+++ checking for hole at " << collapse_top_index - 1 << " val="
<< table_[collapse_top_index - 1].GetReference()->Read<kWithoutReadBarrier>();
}
if (!table_[collapse_top_index - 1].GetReference()->IsNull()) {
@@ -479,11 +273,11 @@
}
current_num_holes_--;
}
- segment_state_.top_index = collapse_top_index;
+ top_index_ = collapse_top_index;
- CheckHoleCount(table_, current_num_holes_, previous_state, segment_state_);
+ CheckHoleCount(table_, current_num_holes_, top_index_);
} else {
- segment_state_.top_index = top_index - 1;
+ top_index_ = top_index - 1;
if (kDebugIRT) {
LOG(INFO) << "+++ ate last entry " << top_index - 1;
}
@@ -501,7 +295,7 @@
*table_[idx].GetReference() = GcRoot<mirror::Object>(nullptr);
current_num_holes_++;
- CheckHoleCount(table_, current_num_holes_, previous_state, segment_state_);
+ CheckHoleCount(table_, current_num_holes_, top_index_);
if (kDebugIRT) {
LOG(INFO) << "+++ left hole at " << idx << ", holes=" << current_num_holes_;
}
@@ -512,10 +306,7 @@
void IndirectReferenceTable::Trim() {
ScopedTrace trace(__PRETTY_FUNCTION__);
- if (!table_mem_map_.IsValid()) {
- // Small table; nothing to do here.
- return;
- }
+ DCHECK(table_mem_map_.IsValid());
const size_t top_index = Capacity();
uint8_t* release_start = AlignUp(reinterpret_cast<uint8_t*>(&table_[top_index]), kPageSize);
uint8_t* release_end = static_cast<uint8_t*>(table_mem_map_.BaseEnd());
@@ -529,7 +320,8 @@
void IndirectReferenceTable::VisitRoots(RootVisitor* visitor, const RootInfo& root_info) {
BufferedRootVisitor<kDefaultBufferedRootCount> root_visitor(visitor, root_info);
- for (auto ref : *this) {
+ for (size_t i = 0, capacity = Capacity(); i != capacity; ++i) {
+ GcRoot<mirror::Object>* ref = table_[i].GetReference();
if (!ref->IsNull()) {
root_visitor.VisitRoot(*ref);
DCHECK(!ref->IsNull());
@@ -537,6 +329,24 @@
}
}
+void IndirectReferenceTable::SweepJniWeakGlobals(IsMarkedVisitor* visitor) {
+ CHECK_EQ(kind_, kWeakGlobal);
+ MutexLock mu(Thread::Current(), *Locks::jni_weak_globals_lock_);
+ Runtime* const runtime = Runtime::Current();
+ for (size_t i = 0, capacity = Capacity(); i != capacity; ++i) {
+ GcRoot<mirror::Object>* entry = table_[i].GetReference();
+ // Need to skip null here to distinguish between null entries and cleared weak ref entries.
+ if (!entry->IsNull()) {
+ mirror::Object* obj = entry->Read<kWithoutReadBarrier>();
+ mirror::Object* new_obj = visitor->IsMarked(obj);
+ if (new_obj == nullptr) {
+ new_obj = runtime->GetClearedJniWeakGlobal();
+ }
+ *entry = GcRoot<mirror::Object>(new_obj);
+ }
+ }
+}
+
void IndirectReferenceTable::Dump(std::ostream& os) const {
os << kind_ << " table dump:\n";
ReferenceTable::Table entries;
@@ -550,47 +360,8 @@
ReferenceTable::Dump(os, entries);
}
-void IndirectReferenceTable::SetSegmentState(IRTSegmentState new_state) {
- if (kDebugIRT) {
- LOG(INFO) << "Setting segment state: "
- << segment_state_.top_index
- << " -> "
- << new_state.top_index;
- }
- segment_state_ = new_state;
-}
-
-bool IndirectReferenceTable::EnsureFreeCapacity(size_t free_capacity, std::string* error_msg) {
- DCHECK_GE(free_capacity, static_cast<size_t>(1));
- if (free_capacity > kMaxTableSizeInBytes) {
- // Arithmetic might even overflow.
- *error_msg = "Requested table size implausibly large";
- return false;
- }
- size_t top_index = segment_state_.top_index;
- if (top_index + free_capacity <= max_entries_) {
- return true;
- }
-
- // We're only gonna do a simple best-effort here, ensuring the asked-for capacity at the end.
- if (resizable_ == ResizableCapacity::kNo) {
- *error_msg = "Table is not resizable";
- return false;
- }
-
- // Try to increase the table size.
- if (!Resize(top_index + free_capacity, error_msg)) {
- LOG(WARNING) << "JNI ERROR: Unable to reserve space in EnsureFreeCapacity (" << free_capacity
- << "): " << std::endl
- << MutatorLockedDumpable<IndirectReferenceTable>(*this)
- << " Resizing failed: " << *error_msg;
- return false;
- }
- return true;
-}
-
size_t IndirectReferenceTable::FreeCapacity() const {
- return max_entries_ - segment_state_.top_index;
+ return max_entries_ - top_index_;
}
} // namespace art
diff --git a/runtime/indirect_reference_table.h b/runtime/indirect_reference_table.h
index e279422..59729ac 100644
--- a/runtime/indirect_reference_table.h
+++ b/runtime/indirect_reference_table.h
@@ -37,75 +37,65 @@
namespace art {
+class IsMarkedVisitor;
class RootInfo;
namespace mirror {
class Object;
} // namespace mirror
-// Maintain a table of indirect references. Used for local/global JNI references.
-//
-// The table contains object references, where the strong (local/global) references are part of the
-// GC root set (but not the weak global references). When an object is added we return an
-// IndirectRef that is not a valid pointer but can be used to find the original value in O(1) time.
-// Conversions to and from indirect references are performed on upcalls and downcalls, so they need
-// to be very fast.
-//
-// To be efficient for JNI local variable storage, we need to provide operations that allow us to
-// operate on segments of the table, where segments are pushed and popped as if on a stack. For
-// example, deletion of an entry should only succeed if it appears in the current segment, and we
-// want to be able to strip off the current segment quickly when a method returns. Additions to the
-// table must be made in the current segment even if space is available in an earlier area.
-//
-// A new segment is created when we call into native code from interpreted code, or when we handle
-// the JNI PushLocalFrame function.
-//
-// The GC must be able to scan the entire table quickly.
-//
-// In summary, these must be very fast:
-// - adding or removing a segment
-// - adding references to a new segment
-// - converting an indirect reference back to an Object
-// These can be a little slower, but must still be pretty quick:
-// - adding references to a "mature" segment
-// - removing individual references
-// - scanning the entire table straight through
-//
-// If there's more than one segment, we don't guarantee that the table will fill completely before
-// we fail due to lack of space. We do ensure that the current segment will pack tightly, which
-// should satisfy JNI requirements (e.g. EnsureLocalCapacity).
-//
-// Only SynchronizedGet is synchronized.
-
// Indirect reference definition. This must be interchangeable with JNI's jobject, and it's
// convenient to let null be null, so we use void*.
//
-// We need a (potentially) large table index and a 2-bit reference type (global, local, weak
-// global). We also reserve some bits to be used to detect stale indirect references: we put a
-// serial number in the extra bits, and keep a copy of the serial number in the table. This requires
-// more memory and additional memory accesses on add/get, but is moving-GC safe. It will catch
-// additional problems, e.g.: create iref1 for obj, delete iref1, create iref2 for same obj,
-// lookup iref1. A pattern based on object bits will miss this.
+// We need a 2-bit reference kind (global, local, weak global) and the rest of the `IndirectRef`
+// is used to locate the actual reference storage.
+//
+// For global and weak global references, we need a (potentially) large table index and we also
+// reserve some bits to be used to detect stale indirect references: we put a serial number in
+// the extra bits, and keep a copy of the serial number in the table. This requires more memory
+// and additional memory accesses on add/get, but is moving-GC safe. It will catch additional
+// problems, e.g.: create iref1 for obj, delete iref1, create iref2 for same obj, lookup iref1.
+// A pattern based on object bits will miss this.
+//
+// Local references use the same bits for the reference kind but the rest of their `IndirectRef`
+// encoding is different, see `LocalReferenceTable` for details.
using IndirectRef = void*;
// Indirect reference kind, used as the two low bits of IndirectRef.
//
-// For convenience these match up with enum jobjectRefType from jni.h.
+// For convenience these match up with enum jobjectRefType from jni.h, except that
+// we use value 0 for JNI transitions instead of marking invalid reference type.
enum IndirectRefKind {
- kJniTransitionOrInvalid = 0, // <<JNI transition frame reference or invalid reference>>
- kLocal = 1, // <<local reference>>
- kGlobal = 2, // <<global reference>>
- kWeakGlobal = 3, // <<weak global reference>>
- kLastKind = kWeakGlobal
+ kJniTransition = 0, // <<JNI transition frame reference>>
+ kLocal = 1, // <<local reference>>
+ kGlobal = 2, // <<global reference>>
+ kWeakGlobal = 3, // <<weak global reference>>
+ kLastKind = kWeakGlobal
};
std::ostream& operator<<(std::ostream& os, IndirectRefKind rhs);
-const char* GetIndirectRefKindString(const IndirectRefKind& kind);
+const char* GetIndirectRefKindString(IndirectRefKind kind);
+
+// Maintain a table of indirect references. Used for global and weak global JNI references.
+//
+// The table contains object references, where the strong global references are part of the
+// GC root set (but not the weak global references). When an object is added we return an
+// `IndirectRef` that is not a valid pointer but can be used to find the original value in O(1)
+// time. Conversions to and from indirect references are performed in JNI functions and when
+// returning from native methods to managed code, so they need to be very fast.
+//
+// The GC must be able to scan the entire table quickly.
+//
+// In summary, these must be very fast:
+// - adding references
+// - converting an indirect reference back to an Object
+// These can be a little slower, but must still be pretty quick:
+// - removing individual references
+// - scanning the entire table straight through
// Table definition.
//
-// For the global reference table, the expected common operations are adding a new entry and
-// removing a recently-added entry (usually the most-recently-added entry). For JNI local
-// references, the common operations are adding a new entry and removing an entire table segment.
+// For the global reference tables, the expected common operations are adding a new entry and
+// removing a recently-added entry (usually the most-recently-added entry).
//
// If we delete entries from the middle of the list, we will be left with "holes". We track the
// number of holes so that, when adding new elements, we can quickly decide to do a trivial append
@@ -114,18 +104,6 @@
// When the top-most entry is removed, any holes immediately below it are also removed. Thus,
// deletion of an entry may reduce "top_index" by more than one.
//
-// To get the desired behavior for JNI locals, we need to know the bottom and top of the current
-// "segment". The top is managed internally, and the bottom is passed in as a function argument.
-// When we call a native method or push a local frame, the current top index gets pushed on, and
-// serves as the new bottom. When we pop a frame off, the value from the stack becomes the new top
-// index, and the value stored in the previous frame becomes the new bottom.
-//
-// Holes are being locally cached for the segment. Otherwise we'd have to pass bottom index and
-// number of holes, which restricts us to 16 bits for the top index. The value is cached within the
-// table. To avoid code in generated JNI transitions, which implicitly form segments, the code for
-// adding and removing references needs to detect the change of a segment. Helper fields are used
-// for this detection.
-//
// Common alternative implementation: make IndirectRef a pointer to the actual reference slot.
// Instead of getting a table and doing a lookup, the lookup can be done instantly. Operations like
// determining the type and deleting the reference are more expensive because the table must be
@@ -135,20 +113,7 @@
// approaches).
//
// TODO: consider a "lastDeleteIndex" for quick hole-filling when an add immediately follows a
-// delete; must invalidate after segment pop might be worth only using it for JNI globals.
-//
-// TODO: may want completely different add/remove algorithms for global and local refs to improve
-// performance. A large circular buffer might reduce the amortized cost of adding global
-// references.
-
-// The state of the current segment. We only store the index. Splitting it for index and hole
-// count restricts the range too much.
-struct IRTSegmentState {
- uint32_t top_index;
-};
-
-// Use as initial value for "cookie", and when table has only one segment.
-static constexpr IRTSegmentState kIRTFirstSegment = { 0 };
+// delete.
// We associate a few bits of serial number with each reference, for error checking.
static constexpr unsigned int kIRTSerialBits = 3;
@@ -181,110 +146,22 @@
static_assert(sizeof(IrtEntry) == 2 * sizeof(uint32_t), "Unexpected sizeof(IrtEntry)");
static_assert(IsPowerOfTwo(sizeof(IrtEntry)), "Unexpected sizeof(IrtEntry)");
-class IrtIterator {
- public:
- IrtIterator(IrtEntry* table, size_t i, size_t capacity) REQUIRES_SHARED(Locks::mutator_lock_)
- : table_(table), i_(i), capacity_(capacity) {
- // capacity_ is used in some target; has warning with unused attribute.
- UNUSED(capacity_);
- }
-
- IrtIterator& operator++() REQUIRES_SHARED(Locks::mutator_lock_) {
- ++i_;
- return *this;
- }
-
- GcRoot<mirror::Object>* operator*() REQUIRES_SHARED(Locks::mutator_lock_) {
- // This does not have a read barrier as this is used to visit roots.
- return table_[i_].GetReference();
- }
-
- bool equals(const IrtIterator& rhs) const {
- return (i_ == rhs.i_ && table_ == rhs.table_);
- }
-
- private:
- IrtEntry* const table_;
- size_t i_;
- const size_t capacity_;
-};
-
-bool inline operator==(const IrtIterator& lhs, const IrtIterator& rhs) {
- return lhs.equals(rhs);
-}
-
-bool inline operator!=(const IrtIterator& lhs, const IrtIterator& rhs) {
- return !lhs.equals(rhs);
-}
-
-// We initially allocate local reference tables with a very small number of entries, packing
-// multiple tables into a single page. If we need to expand one, we allocate them in units of
-// pages.
-// TODO: We should allocate all IRT tables as nonmovable Java objects, That in turn works better
-// if we break up each table into 2 parallel arrays, one for the Java reference, and one for the
-// serial number. The current scheme page-aligns regions containing IRT tables, and so allows them
-// to be identified and page-protected in the future.
-constexpr size_t kInitialIrtBytes = 512; // Number of bytes in an initial local table.
-constexpr size_t kSmallIrtEntries = kInitialIrtBytes / sizeof(IrtEntry);
-static_assert(kPageSize % kInitialIrtBytes == 0);
-static_assert(kInitialIrtBytes % sizeof(IrtEntry) == 0);
-static_assert(kInitialIrtBytes % sizeof(void *) == 0);
-
-// A minimal stopgap allocator for initial small local IRT tables.
-class SmallIrtAllocator {
- public:
- SmallIrtAllocator();
-
- // Allocate an IRT table for kSmallIrtEntries.
- IrtEntry* Allocate(std::string* error_msg) REQUIRES(!lock_);
-
- void Deallocate(IrtEntry* unneeded) REQUIRES(!lock_);
-
- private:
- // A free list of kInitialIrtBytes chunks linked through the first word.
- IrtEntry* small_irt_freelist_;
-
- // Repository of MemMaps used for small IRT tables.
- std::vector<MemMap> shared_irt_maps_;
-
- Mutex lock_; // Level kGenericBottomLock; acquired before mem_map_lock_, which is a C++ mutex.
-};
-
class IndirectReferenceTable {
public:
- enum class ResizableCapacity {
- kNo,
- kYes
- };
+ // Constructs an uninitialized indirect reference table. Use `Initialize()` to initialize it.
+ explicit IndirectReferenceTable(IndirectRefKind kind);
- // WARNING: Construction of the IndirectReferenceTable may fail.
- // error_msg must not be null. If error_msg is set by the constructor, then
- // construction has failed and the IndirectReferenceTable will be in an
- // invalid state. Use IsValid to check whether the object is in an invalid
- // state.
- // Max_count is the minimum initial capacity (resizable), or minimum total capacity
- // (not resizable). A value of 1 indicates an implementation-convenient small size.
- IndirectReferenceTable(size_t max_count,
- IndirectRefKind kind,
- ResizableCapacity resizable,
- std::string* error_msg);
+ // Initialize the indirect reference table.
+ //
+ // Max_count is the requested total capacity (not resizable). The actual total capacity
+ // can be higher to utilize all allocated memory (rounding up to whole pages).
+ bool Initialize(size_t max_count, std::string* error_msg);
~IndirectReferenceTable();
- /*
- * Checks whether construction of the IndirectReferenceTable succeeded.
- *
- * This object must only be used if IsValid() returns true. It is safe to
- * call IsValid from multiple threads without locking or other explicit
- * synchronization.
- */
- bool IsValid() const;
-
// Add a new entry. "obj" must be a valid non-null object reference. This function will
// return null if an error happened (with an appropriate error message set).
- IndirectRef Add(IRTSegmentState previous_state,
- ObjPtr<mirror::Object> obj,
- std::string* error_msg)
+ IndirectRef Add(ObjPtr<mirror::Object> obj, std::string* error_msg)
REQUIRES_SHARED(Locks::mutator_lock_);
// Given an IndirectRef in the table, return the Object it refers to.
@@ -294,13 +171,6 @@
ObjPtr<mirror::Object> Get(IndirectRef iref) const REQUIRES_SHARED(Locks::mutator_lock_)
ALWAYS_INLINE;
- // Synchronized get which reads a reference, acquiring a lock if necessary.
- template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
- ObjPtr<mirror::Object> SynchronizedGet(IndirectRef iref) const
- REQUIRES_SHARED(Locks::mutator_lock_) {
- return Get<kReadBarrierOption>(iref);
- }
-
// Updates an existing indirect reference to point to a new object.
void Update(IndirectRef iref, ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -311,9 +181,7 @@
// required by JNI's DeleteLocalRef function.
//
// Returns "false" if nothing was removed.
- bool Remove(IRTSegmentState previous_state, IndirectRef iref);
-
- void AssertEmpty() REQUIRES_SHARED(Locks::mutator_lock_);
+ bool Remove(IndirectRef iref);
void Dump(std::ostream& os) const
REQUIRES_SHARED(Locks::mutator_lock_)
@@ -326,48 +194,22 @@
// Return the #of entries in the entire table. This includes holes, and
// so may be larger than the actual number of "live" entries.
size_t Capacity() const {
- return segment_state_.top_index;
+ return top_index_;
}
// Return the number of non-null entries in the table. Only reliable for a
// single segment table.
int32_t NEntriesForGlobal() {
- return segment_state_.top_index - current_num_holes_;
+ return top_index_ - current_num_holes_;
}
- // Ensure that at least free_capacity elements are available, or return false.
- // Caller ensures free_capacity > 0.
- bool EnsureFreeCapacity(size_t free_capacity, std::string* error_msg)
- REQUIRES_SHARED(Locks::mutator_lock_);
- // See implementation of EnsureFreeCapacity. We'll only state here how much is trivially free,
- // without recovering holes. Thus this is a conservative estimate.
+ // We'll only state here how much is trivially free, without recovering holes.
+ // Thus this is a conservative estimate.
size_t FreeCapacity() const;
- // Note IrtIterator does not have a read barrier as it's used to visit roots.
- IrtIterator begin() {
- return IrtIterator(table_, 0, Capacity());
- }
-
- IrtIterator end() {
- return IrtIterator(table_, Capacity(), Capacity());
- }
-
void VisitRoots(RootVisitor* visitor, const RootInfo& root_info)
REQUIRES_SHARED(Locks::mutator_lock_);
- IRTSegmentState GetSegmentState() const {
- return segment_state_;
- }
-
- void SetSegmentState(IRTSegmentState new_state);
-
- static Offset SegmentStateOffset(size_t pointer_size ATTRIBUTE_UNUSED) {
- // Note: Currently segment_state_ is at offset 0. We're testing the expected value in
- // jni_internal_test to make sure it stays correct. It is not OFFSETOF_MEMBER, as that
- // is not pointer-size-safe.
- return Offset(0);
- }
-
// Release pages past the end of the table that may have previously held references.
void Trim() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -380,6 +222,10 @@
bool IsValidReference(IndirectRef, /*out*/std::string* error_msg) const
REQUIRES_SHARED(Locks::mutator_lock_);
+ void SweepJniWeakGlobals(IsMarkedVisitor* visitor)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::jni_weak_globals_lock_);
+
private:
static constexpr uint32_t kShiftedSerialMask = (1u << kIRTSerialBits) - 1;
@@ -429,42 +275,30 @@
return reinterpret_cast<IndirectRef>(EncodeIndirectRef(table_index, serial));
}
- // Resize the backing table to be at least new_size elements long. Currently
- // must be larger than the current size. After return max_entries_ >= new_size.
- bool Resize(size_t new_size, std::string* error_msg);
-
- void RecoverHoles(IRTSegmentState from);
-
// Abort if check_jni is not enabled. Otherwise, just log as an error.
static void AbortIfNoCheckJNI(const std::string& msg);
/* extra debugging checks */
bool CheckEntry(const char*, IndirectRef, uint32_t) const;
- /// semi-public - read/write by jni down calls.
- IRTSegmentState segment_state_;
-
- // Mem map where we store the indirect refs. If it's invalid, and table_ is non-null, then
- // table_ is valid, but was allocated via allocSmallIRT();
+ // Mem map where we store the indirect refs.
MemMap table_mem_map_;
- // bottom of the stack. Do not directly access the object references
+ // Bottom of the stack. Do not directly access the object references
// in this as they are roots. Use Get() that has a read barrier.
IrtEntry* table_;
- // bit mask, ORed into all irefs.
+ // Bit mask, ORed into all irefs.
const IndirectRefKind kind_;
- // max #of entries allowed (modulo resizing).
+ // The "top of stack" index where new references are added.
+ size_t top_index_;
+
+ // Maximum number of entries allowed.
size_t max_entries_;
- // Some values to retain old behavior with holes. Description of the algorithm is in the .cc
- // file.
+ // Some values to retain old behavior with holes.
+ // Description of the algorithm is in the .cc file.
// TODO: Consider other data structures for compact tables, e.g., free lists.
size_t current_num_holes_; // Number of holes in the current / top segment.
- IRTSegmentState last_known_previous_state_;
-
- // Whether the table's capacity may be resized. As there are no locks used, it is the caller's
- // responsibility to ensure thread-safety.
- ResizableCapacity resizable_;
};
} // namespace art
diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc
index 5da7a30..ac22f3f 100644
--- a/runtime/indirect_reference_table_test.cc
+++ b/runtime/indirect_reference_table_test.cc
@@ -28,7 +28,12 @@
using android::base::StringPrintf;
-class IndirectReferenceTableTest : public CommonRuntimeTest {};
+class IndirectReferenceTableTest : public CommonRuntimeTest {
+ protected:
+ IndirectReferenceTableTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+};
static void CheckDump(IndirectReferenceTable* irt, size_t num_objects, size_t num_unique)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -54,12 +59,10 @@
ScopedObjectAccess soa(Thread::Current());
static const size_t kTableMax = 20;
+ IndirectReferenceTable irt(kGlobal);
std::string error_msg;
- IndirectReferenceTable irt(kTableMax,
- kGlobal,
- IndirectReferenceTable::ResizableCapacity::kNo,
- &error_msg);
- ASSERT_TRUE(irt.IsValid()) << error_msg;
+ bool success = irt.Initialize(kTableMax, &error_msg);
+ ASSERT_TRUE(success) << error_msg;
StackHandleScope<5> hs(soa.Self());
Handle<mirror::Class> c =
@@ -74,21 +77,19 @@
Handle<mirror::Object> obj3 = hs.NewHandle(c->AllocObject(soa.Self()));
ASSERT_TRUE(obj3 != nullptr);
- const IRTSegmentState cookie = kIRTFirstSegment;
-
CheckDump(&irt, 0, 0);
IndirectRef iref0 = (IndirectRef) 0x11110;
- EXPECT_FALSE(irt.Remove(cookie, iref0)) << "unexpectedly successful removal";
+ EXPECT_FALSE(irt.Remove(iref0)) << "unexpectedly successful removal";
// Add three, check, remove in the order in which they were added.
- iref0 = irt.Add(cookie, obj0.Get(), &error_msg);
+ iref0 = irt.Add(obj0.Get(), &error_msg);
EXPECT_TRUE(iref0 != nullptr);
CheckDump(&irt, 1, 1);
- IndirectRef iref1 = irt.Add(cookie, obj1.Get(), &error_msg);
+ IndirectRef iref1 = irt.Add(obj1.Get(), &error_msg);
EXPECT_TRUE(iref1 != nullptr);
CheckDump(&irt, 2, 2);
- IndirectRef iref2 = irt.Add(cookie, obj2.Get(), &error_msg);
+ IndirectRef iref2 = irt.Add(obj2.Get(), &error_msg);
EXPECT_TRUE(iref2 != nullptr);
CheckDump(&irt, 3, 3);
@@ -96,11 +97,11 @@
EXPECT_OBJ_PTR_EQ(obj1.Get(), irt.Get(iref1));
EXPECT_OBJ_PTR_EQ(obj2.Get(), irt.Get(iref2));
- EXPECT_TRUE(irt.Remove(cookie, iref0));
+ EXPECT_TRUE(irt.Remove(iref0));
CheckDump(&irt, 2, 2);
- EXPECT_TRUE(irt.Remove(cookie, iref1));
+ EXPECT_TRUE(irt.Remove(iref1));
CheckDump(&irt, 1, 1);
- EXPECT_TRUE(irt.Remove(cookie, iref2));
+ EXPECT_TRUE(irt.Remove(iref2));
CheckDump(&irt, 0, 0);
// Table should be empty now.
@@ -111,19 +112,19 @@
EXPECT_FALSE(irt.IsValidReference(iref0, &error_msg));
// Add three, remove in the opposite order.
- iref0 = irt.Add(cookie, obj0.Get(), &error_msg);
+ iref0 = irt.Add(obj0.Get(), &error_msg);
EXPECT_TRUE(iref0 != nullptr);
- iref1 = irt.Add(cookie, obj1.Get(), &error_msg);
+ iref1 = irt.Add(obj1.Get(), &error_msg);
EXPECT_TRUE(iref1 != nullptr);
- iref2 = irt.Add(cookie, obj2.Get(), &error_msg);
+ iref2 = irt.Add(obj2.Get(), &error_msg);
EXPECT_TRUE(iref2 != nullptr);
CheckDump(&irt, 3, 3);
- ASSERT_TRUE(irt.Remove(cookie, iref2));
+ ASSERT_TRUE(irt.Remove(iref2));
CheckDump(&irt, 2, 2);
- ASSERT_TRUE(irt.Remove(cookie, iref1));
+ ASSERT_TRUE(irt.Remove(iref1));
CheckDump(&irt, 1, 1);
- ASSERT_TRUE(irt.Remove(cookie, iref0));
+ ASSERT_TRUE(irt.Remove(iref0));
CheckDump(&irt, 0, 0);
// Table should be empty now.
@@ -131,27 +132,27 @@
// Add three, remove middle / middle / bottom / top. (Second attempt
// to remove middle should fail.)
- iref0 = irt.Add(cookie, obj0.Get(), &error_msg);
+ iref0 = irt.Add(obj0.Get(), &error_msg);
EXPECT_TRUE(iref0 != nullptr);
- iref1 = irt.Add(cookie, obj1.Get(), &error_msg);
+ iref1 = irt.Add(obj1.Get(), &error_msg);
EXPECT_TRUE(iref1 != nullptr);
- iref2 = irt.Add(cookie, obj2.Get(), &error_msg);
+ iref2 = irt.Add(obj2.Get(), &error_msg);
EXPECT_TRUE(iref2 != nullptr);
CheckDump(&irt, 3, 3);
ASSERT_EQ(3U, irt.Capacity());
- ASSERT_TRUE(irt.Remove(cookie, iref1));
+ ASSERT_TRUE(irt.Remove(iref1));
CheckDump(&irt, 2, 2);
- ASSERT_FALSE(irt.Remove(cookie, iref1));
+ ASSERT_FALSE(irt.Remove(iref1));
CheckDump(&irt, 2, 2);
// Check that the reference to the hole is not valid.
EXPECT_FALSE(irt.IsValidReference(iref1, &error_msg));
- ASSERT_TRUE(irt.Remove(cookie, iref2));
+ ASSERT_TRUE(irt.Remove(iref2));
CheckDump(&irt, 1, 1);
- ASSERT_TRUE(irt.Remove(cookie, iref0));
+ ASSERT_TRUE(irt.Remove(iref0));
CheckDump(&irt, 0, 0);
// Table should be empty now.
@@ -160,35 +161,35 @@
// Add four entries. Remove #1, add new entry, verify that table size
// is still 4 (i.e. holes are getting filled). Remove #1 and #3, verify
// that we delete one and don't hole-compact the other.
- iref0 = irt.Add(cookie, obj0.Get(), &error_msg);
+ iref0 = irt.Add(obj0.Get(), &error_msg);
EXPECT_TRUE(iref0 != nullptr);
- iref1 = irt.Add(cookie, obj1.Get(), &error_msg);
+ iref1 = irt.Add(obj1.Get(), &error_msg);
EXPECT_TRUE(iref1 != nullptr);
- iref2 = irt.Add(cookie, obj2.Get(), &error_msg);
+ iref2 = irt.Add(obj2.Get(), &error_msg);
EXPECT_TRUE(iref2 != nullptr);
- IndirectRef iref3 = irt.Add(cookie, obj3.Get(), &error_msg);
+ IndirectRef iref3 = irt.Add(obj3.Get(), &error_msg);
EXPECT_TRUE(iref3 != nullptr);
CheckDump(&irt, 4, 4);
- ASSERT_TRUE(irt.Remove(cookie, iref1));
+ ASSERT_TRUE(irt.Remove(iref1));
CheckDump(&irt, 3, 3);
- iref1 = irt.Add(cookie, obj1.Get(), &error_msg);
+ iref1 = irt.Add(obj1.Get(), &error_msg);
EXPECT_TRUE(iref1 != nullptr);
ASSERT_EQ(4U, irt.Capacity()) << "hole not filled";
CheckDump(&irt, 4, 4);
- ASSERT_TRUE(irt.Remove(cookie, iref1));
+ ASSERT_TRUE(irt.Remove(iref1));
CheckDump(&irt, 3, 3);
- ASSERT_TRUE(irt.Remove(cookie, iref3));
+ ASSERT_TRUE(irt.Remove(iref3));
CheckDump(&irt, 2, 2);
ASSERT_EQ(3U, irt.Capacity()) << "should be 3 after two deletions";
- ASSERT_TRUE(irt.Remove(cookie, iref2));
+ ASSERT_TRUE(irt.Remove(iref2));
CheckDump(&irt, 1, 1);
- ASSERT_TRUE(irt.Remove(cookie, iref0));
+ ASSERT_TRUE(irt.Remove(iref0));
CheckDump(&irt, 0, 0);
ASSERT_EQ(0U, irt.Capacity()) << "not empty after split remove";
@@ -196,320 +197,72 @@
// Add an entry, remove it, add a new entry, and try to use the original
// iref. They have the same slot number but are for different objects.
// With the extended checks in place, this should fail.
- iref0 = irt.Add(cookie, obj0.Get(), &error_msg);
+ iref0 = irt.Add(obj0.Get(), &error_msg);
EXPECT_TRUE(iref0 != nullptr);
CheckDump(&irt, 1, 1);
- ASSERT_TRUE(irt.Remove(cookie, iref0));
+ ASSERT_TRUE(irt.Remove(iref0));
CheckDump(&irt, 0, 0);
- iref1 = irt.Add(cookie, obj1.Get(), &error_msg);
+ iref1 = irt.Add(obj1.Get(), &error_msg);
EXPECT_TRUE(iref1 != nullptr);
CheckDump(&irt, 1, 1);
- ASSERT_FALSE(irt.Remove(cookie, iref0)) << "mismatched del succeeded";
+ ASSERT_FALSE(irt.Remove(iref0)) << "mismatched del succeeded";
CheckDump(&irt, 1, 1);
- ASSERT_TRUE(irt.Remove(cookie, iref1)) << "switched del failed";
+ ASSERT_TRUE(irt.Remove(iref1)) << "switched del failed";
ASSERT_EQ(0U, irt.Capacity()) << "switching del not empty";
CheckDump(&irt, 0, 0);
// Same as above, but with the same object. A more rigorous checker
// (e.g. with slot serialization) will catch this.
- iref0 = irt.Add(cookie, obj0.Get(), &error_msg);
+ iref0 = irt.Add(obj0.Get(), &error_msg);
EXPECT_TRUE(iref0 != nullptr);
CheckDump(&irt, 1, 1);
- ASSERT_TRUE(irt.Remove(cookie, iref0));
+ ASSERT_TRUE(irt.Remove(iref0));
CheckDump(&irt, 0, 0);
- iref1 = irt.Add(cookie, obj0.Get(), &error_msg);
+ iref1 = irt.Add(obj0.Get(), &error_msg);
EXPECT_TRUE(iref1 != nullptr);
CheckDump(&irt, 1, 1);
if (iref0 != iref1) {
// Try 0, should not work.
- ASSERT_FALSE(irt.Remove(cookie, iref0)) << "temporal del succeeded";
+ ASSERT_FALSE(irt.Remove(iref0)) << "temporal del succeeded";
}
- ASSERT_TRUE(irt.Remove(cookie, iref1)) << "temporal cleanup failed";
+ ASSERT_TRUE(irt.Remove(iref1)) << "temporal cleanup failed";
ASSERT_EQ(0U, irt.Capacity()) << "temporal del not empty";
CheckDump(&irt, 0, 0);
// Stale reference is not valid.
- iref0 = irt.Add(cookie, obj0.Get(), &error_msg);
+ iref0 = irt.Add(obj0.Get(), &error_msg);
EXPECT_TRUE(iref0 != nullptr);
CheckDump(&irt, 1, 1);
- ASSERT_TRUE(irt.Remove(cookie, iref0));
+ ASSERT_TRUE(irt.Remove(iref0));
EXPECT_FALSE(irt.IsValidReference(iref0, &error_msg)) << "stale lookup succeeded";
CheckDump(&irt, 0, 0);
- // Test table resizing.
- // These ones fit...
+ // Test deleting all but the last entry.
+ // We shall delete these.
static const size_t kTableInitial = kTableMax / 2;
IndirectRef manyRefs[kTableInitial];
for (size_t i = 0; i < kTableInitial; i++) {
- manyRefs[i] = irt.Add(cookie, obj0.Get(), &error_msg);
+ manyRefs[i] = irt.Add(obj0.Get(), &error_msg);
ASSERT_TRUE(manyRefs[i] != nullptr) << "Failed adding " << i;
CheckDump(&irt, i + 1, 1);
}
- // ...this one causes overflow.
- iref0 = irt.Add(cookie, obj0.Get(), &error_msg);
+ // We shall keep this one.
+ iref0 = irt.Add(obj0.Get(), &error_msg);
ASSERT_TRUE(iref0 != nullptr);
ASSERT_EQ(kTableInitial + 1, irt.Capacity());
CheckDump(&irt, kTableInitial + 1, 1);
-
+ // Delete all but the last entry.
for (size_t i = 0; i < kTableInitial; i++) {
- ASSERT_TRUE(irt.Remove(cookie, manyRefs[i])) << "failed removing " << i;
+ ASSERT_TRUE(irt.Remove(manyRefs[i])) << "failed removing " << i;
CheckDump(&irt, kTableInitial - i, 1);
}
// Because of removal order, should have 11 entries, 10 of them holes.
ASSERT_EQ(kTableInitial + 1, irt.Capacity());
- ASSERT_TRUE(irt.Remove(cookie, iref0)) << "multi-remove final failed";
+ ASSERT_TRUE(irt.Remove(iref0)) << "multi-remove final failed";
ASSERT_EQ(0U, irt.Capacity()) << "multi-del not empty";
CheckDump(&irt, 0, 0);
}
-TEST_F(IndirectReferenceTableTest, Holes) {
- // Test the explicitly named cases from the IRT implementation:
- //
- // 1) Segment with holes (current_num_holes_ > 0), push new segment, add/remove reference
- // 2) Segment with holes (current_num_holes_ > 0), pop segment, add/remove reference
- // 3) Segment with holes (current_num_holes_ > 0), push new segment, pop segment, add/remove
- // reference
- // 4) Empty segment, push new segment, create a hole, pop a segment, add/remove a reference
- // 5) Base segment, push new segment, create a hole, pop a segment, push new segment, add/remove
- // reference
-
- ScopedObjectAccess soa(Thread::Current());
- static const size_t kTableMax = 10;
-
- StackHandleScope<6> hs(soa.Self());
- Handle<mirror::Class> c = hs.NewHandle(
- class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"));
- ASSERT_TRUE(c != nullptr);
- Handle<mirror::Object> obj0 = hs.NewHandle(c->AllocObject(soa.Self()));
- ASSERT_TRUE(obj0 != nullptr);
- Handle<mirror::Object> obj1 = hs.NewHandle(c->AllocObject(soa.Self()));
- ASSERT_TRUE(obj1 != nullptr);
- Handle<mirror::Object> obj2 = hs.NewHandle(c->AllocObject(soa.Self()));
- ASSERT_TRUE(obj2 != nullptr);
- Handle<mirror::Object> obj3 = hs.NewHandle(c->AllocObject(soa.Self()));
- ASSERT_TRUE(obj3 != nullptr);
- Handle<mirror::Object> obj4 = hs.NewHandle(c->AllocObject(soa.Self()));
- ASSERT_TRUE(obj4 != nullptr);
-
- std::string error_msg;
-
- // 1) Segment with holes (current_num_holes_ > 0), push new segment, add/remove reference.
- {
- IndirectReferenceTable irt(kTableMax,
- kGlobal,
- IndirectReferenceTable::ResizableCapacity::kNo,
- &error_msg);
- ASSERT_TRUE(irt.IsValid()) << error_msg;
-
- const IRTSegmentState cookie0 = kIRTFirstSegment;
-
- CheckDump(&irt, 0, 0);
-
- IndirectRef iref0 = irt.Add(cookie0, obj0.Get(), &error_msg);
- IndirectRef iref1 = irt.Add(cookie0, obj1.Get(), &error_msg);
- IndirectRef iref2 = irt.Add(cookie0, obj2.Get(), &error_msg);
-
- EXPECT_TRUE(irt.Remove(cookie0, iref1));
-
- // New segment.
- const IRTSegmentState cookie1 = irt.GetSegmentState();
-
- IndirectRef iref3 = irt.Add(cookie1, obj3.Get(), &error_msg);
-
- // Must not have filled the previous hole.
- EXPECT_EQ(irt.Capacity(), 4u);
- EXPECT_FALSE(irt.IsValidReference(iref1, &error_msg));
- CheckDump(&irt, 3, 3);
-
- UNUSED(iref0, iref1, iref2, iref3);
- }
-
- // 2) Segment with holes (current_num_holes_ > 0), pop segment, add/remove reference
- {
- IndirectReferenceTable irt(kTableMax,
- kGlobal,
- IndirectReferenceTable::ResizableCapacity::kNo,
- &error_msg);
- ASSERT_TRUE(irt.IsValid()) << error_msg;
-
- const IRTSegmentState cookie0 = kIRTFirstSegment;
-
- CheckDump(&irt, 0, 0);
-
- IndirectRef iref0 = irt.Add(cookie0, obj0.Get(), &error_msg);
-
- // New segment.
- const IRTSegmentState cookie1 = irt.GetSegmentState();
-
- IndirectRef iref1 = irt.Add(cookie1, obj1.Get(), &error_msg);
- IndirectRef iref2 = irt.Add(cookie1, obj2.Get(), &error_msg);
- IndirectRef iref3 = irt.Add(cookie1, obj3.Get(), &error_msg);
-
- EXPECT_TRUE(irt.Remove(cookie1, iref2));
-
- // Pop segment.
- irt.SetSegmentState(cookie1);
-
- IndirectRef iref4 = irt.Add(cookie1, obj4.Get(), &error_msg);
-
- EXPECT_EQ(irt.Capacity(), 2u);
- EXPECT_FALSE(irt.IsValidReference(iref2, &error_msg));
- CheckDump(&irt, 2, 2);
-
- UNUSED(iref0, iref1, iref2, iref3, iref4);
- }
-
- // 3) Segment with holes (current_num_holes_ > 0), push new segment, pop segment, add/remove
- // reference.
- {
- IndirectReferenceTable irt(kTableMax,
- kGlobal,
- IndirectReferenceTable::ResizableCapacity::kNo,
- &error_msg);
- ASSERT_TRUE(irt.IsValid()) << error_msg;
-
- const IRTSegmentState cookie0 = kIRTFirstSegment;
-
- CheckDump(&irt, 0, 0);
-
- IndirectRef iref0 = irt.Add(cookie0, obj0.Get(), &error_msg);
-
- // New segment.
- const IRTSegmentState cookie1 = irt.GetSegmentState();
-
- IndirectRef iref1 = irt.Add(cookie1, obj1.Get(), &error_msg);
- IndirectRef iref2 = irt.Add(cookie1, obj2.Get(), &error_msg);
-
- EXPECT_TRUE(irt.Remove(cookie1, iref1));
-
- // New segment.
- const IRTSegmentState cookie2 = irt.GetSegmentState();
-
- IndirectRef iref3 = irt.Add(cookie2, obj3.Get(), &error_msg);
-
- // Pop segment.
- irt.SetSegmentState(cookie2);
-
- IndirectRef iref4 = irt.Add(cookie1, obj4.Get(), &error_msg);
-
- EXPECT_EQ(irt.Capacity(), 3u);
- EXPECT_FALSE(irt.IsValidReference(iref1, &error_msg));
- CheckDump(&irt, 3, 3);
-
- UNUSED(iref0, iref1, iref2, iref3, iref4);
- }
-
- // 4) Empty segment, push new segment, create a hole, pop a segment, add/remove a reference.
- {
- IndirectReferenceTable irt(kTableMax,
- kGlobal,
- IndirectReferenceTable::ResizableCapacity::kNo,
- &error_msg);
- ASSERT_TRUE(irt.IsValid()) << error_msg;
-
- const IRTSegmentState cookie0 = kIRTFirstSegment;
-
- CheckDump(&irt, 0, 0);
-
- IndirectRef iref0 = irt.Add(cookie0, obj0.Get(), &error_msg);
-
- // New segment.
- const IRTSegmentState cookie1 = irt.GetSegmentState();
-
- IndirectRef iref1 = irt.Add(cookie1, obj1.Get(), &error_msg);
- EXPECT_TRUE(irt.Remove(cookie1, iref1));
-
- // Emptied segment, push new one.
- const IRTSegmentState cookie2 = irt.GetSegmentState();
-
- IndirectRef iref2 = irt.Add(cookie1, obj1.Get(), &error_msg);
- IndirectRef iref3 = irt.Add(cookie1, obj2.Get(), &error_msg);
- IndirectRef iref4 = irt.Add(cookie1, obj3.Get(), &error_msg);
-
- EXPECT_TRUE(irt.Remove(cookie1, iref3));
-
- // Pop segment.
- UNUSED(cookie2);
- irt.SetSegmentState(cookie1);
-
- IndirectRef iref5 = irt.Add(cookie1, obj4.Get(), &error_msg);
-
- EXPECT_EQ(irt.Capacity(), 2u);
- EXPECT_FALSE(irt.IsValidReference(iref3, &error_msg));
- CheckDump(&irt, 2, 2);
-
- UNUSED(iref0, iref1, iref2, iref3, iref4, iref5);
- }
-
- // 5) Base segment, push new segment, create a hole, pop a segment, push new segment, add/remove
- // reference
- {
- IndirectReferenceTable irt(kTableMax,
- kGlobal,
- IndirectReferenceTable::ResizableCapacity::kNo,
- &error_msg);
- ASSERT_TRUE(irt.IsValid()) << error_msg;
-
- const IRTSegmentState cookie0 = kIRTFirstSegment;
-
- CheckDump(&irt, 0, 0);
-
- IndirectRef iref0 = irt.Add(cookie0, obj0.Get(), &error_msg);
-
- // New segment.
- const IRTSegmentState cookie1 = irt.GetSegmentState();
-
- IndirectRef iref1 = irt.Add(cookie1, obj1.Get(), &error_msg);
- IndirectRef iref2 = irt.Add(cookie1, obj1.Get(), &error_msg);
- IndirectRef iref3 = irt.Add(cookie1, obj2.Get(), &error_msg);
-
- EXPECT_TRUE(irt.Remove(cookie1, iref2));
-
- // Pop segment.
- irt.SetSegmentState(cookie1);
-
- // Push segment.
- const IRTSegmentState cookie1_second = irt.GetSegmentState();
- UNUSED(cookie1_second);
-
- IndirectRef iref4 = irt.Add(cookie1, obj3.Get(), &error_msg);
-
- EXPECT_EQ(irt.Capacity(), 2u);
- EXPECT_FALSE(irt.IsValidReference(iref3, &error_msg));
- CheckDump(&irt, 2, 2);
-
- UNUSED(iref0, iref1, iref2, iref3, iref4);
- }
-}
-
-TEST_F(IndirectReferenceTableTest, Resize) {
- ScopedObjectAccess soa(Thread::Current());
- static const size_t kTableMax = 512;
-
- StackHandleScope<2> hs(soa.Self());
- Handle<mirror::Class> c = hs.NewHandle(
- class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"));
- ASSERT_TRUE(c != nullptr);
- Handle<mirror::Object> obj0 = hs.NewHandle(c->AllocObject(soa.Self()));
- ASSERT_TRUE(obj0 != nullptr);
-
- std::string error_msg;
- IndirectReferenceTable irt(kTableMax,
- kLocal,
- IndirectReferenceTable::ResizableCapacity::kYes,
- &error_msg);
- ASSERT_TRUE(irt.IsValid()) << error_msg;
-
- CheckDump(&irt, 0, 0);
- const IRTSegmentState cookie = kIRTFirstSegment;
-
- for (size_t i = 0; i != kTableMax + 1; ++i) {
- irt.Add(cookie, obj0.Get(), &error_msg);
- }
-
- EXPECT_EQ(irt.Capacity(), kTableMax + 1);
-}
-
} // namespace art
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 6ec98ff..0863ddf 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -55,6 +55,9 @@
#include "thread_list.h"
namespace art {
+extern "C" NO_RETURN void artDeoptimize(Thread* self, bool skip_method_exit_callbacks);
+extern "C" NO_RETURN void artDeliverPendingExceptionFromCode(Thread* self);
+
namespace instrumentation {
constexpr bool kVerboseInstrumentation = false;
@@ -104,69 +107,6 @@
Instrumentation* const instrumentation_;
};
-InstrumentationStackPopper::InstrumentationStackPopper(Thread* self)
- : self_(self),
- instrumentation_(Runtime::Current()->GetInstrumentation()),
- pop_until_(0u) {}
-
-InstrumentationStackPopper::~InstrumentationStackPopper() {
- std::map<uintptr_t, instrumentation::InstrumentationStackFrame>* stack =
- self_->GetInstrumentationStack();
- for (auto i = stack->begin(); i != stack->end() && i->first <= pop_until_;) {
- i = stack->erase(i);
- }
-}
-
-bool InstrumentationStackPopper::PopFramesTo(uintptr_t stack_pointer,
- MutableHandle<mirror::Throwable>& exception) {
- std::map<uintptr_t, instrumentation::InstrumentationStackFrame>* stack =
- self_->GetInstrumentationStack();
- DCHECK(!self_->IsExceptionPending());
- if (!instrumentation_->HasMethodUnwindListeners()) {
- pop_until_ = stack_pointer;
- return true;
- }
- if (kVerboseInstrumentation) {
- LOG(INFO) << "Popping frames for exception " << exception->Dump();
- }
- // The instrumentation events expect the exception to be set.
- self_->SetException(exception.Get());
- bool new_exception_thrown = false;
- auto i = stack->upper_bound(pop_until_);
-
- // Now pop all frames until reaching stack_pointer, or a new exception is
- // thrown. Note that `stack_pointer` doesn't need to be a return PC address
- // (in fact the exception handling code passes the start of the frame where
- // the catch handler is).
- for (; i != stack->end() && i->first <= stack_pointer; i++) {
- const InstrumentationStackFrame& frame = i->second;
- ArtMethod* method = frame.method_;
- // Notify listeners of method unwind.
- // TODO: improve the dex_pc information here.
- uint32_t dex_pc = dex::kDexNoIndex;
- if (kVerboseInstrumentation) {
- LOG(INFO) << "Popping for unwind " << method->PrettyMethod();
- }
- if (!method->IsRuntimeMethod() && !frame.interpreter_entry_) {
- instrumentation_->MethodUnwindEvent(self_, frame.this_object_, method, dex_pc);
- new_exception_thrown = self_->GetException() != exception.Get();
- if (new_exception_thrown) {
- pop_until_ = i->first;
- break;
- }
- }
- }
- if (!new_exception_thrown) {
- pop_until_ = stack_pointer;
- }
- exception.Assign(self_->GetException());
- self_->ClearException();
- if (kVerboseInstrumentation && new_exception_thrown) {
- LOG(INFO) << "Did partial pop of frames due to new exception";
- }
- return !new_exception_thrown;
-}
-
Instrumentation::Instrumentation()
: current_force_deopt_id_(0),
instrumentation_stubs_installed_(false),
@@ -182,12 +122,54 @@
have_watched_frame_pop_listeners_(false),
have_branch_listeners_(false),
have_exception_handled_listeners_(false),
- deoptimized_methods_lock_(new ReaderWriterMutex("deoptimized methods lock",
- kGenericBottomLock)),
quick_alloc_entry_points_instrumentation_counter_(0),
alloc_entrypoints_instrumented_(false) {
}
+bool Instrumentation::ProcessMethodUnwindCallbacks(Thread* self,
+ std::queue<ArtMethod*>& methods,
+ MutableHandle<mirror::Throwable>& exception) {
+ DCHECK(!self->IsExceptionPending());
+ if (!HasMethodUnwindListeners()) {
+ return true;
+ }
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << "Popping frames for exception " << exception->Dump();
+ }
+ // The instrumentation events expect the exception to be set.
+ self->SetException(exception.Get());
+ bool new_exception_thrown = false;
+
+ // Process callbacks for all methods that would be unwound until a new exception is thrown.
+ while (!methods.empty()) {
+ ArtMethod* method = methods.front();
+ methods.pop();
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << "Popping for unwind " << method->PrettyMethod();
+ }
+
+ if (method->IsRuntimeMethod()) {
+ continue;
+ }
+
+ // Notify listeners of method unwind.
+ // TODO: improve the dex_pc information here.
+ uint32_t dex_pc = dex::kDexNoIndex;
+ MethodUnwindEvent(self, method, dex_pc);
+ new_exception_thrown = self->GetException() != exception.Get();
+ if (new_exception_thrown) {
+ break;
+ }
+ }
+
+ exception.Assign(self->GetException());
+ self->ClearException();
+ if (kVerboseInstrumentation && new_exception_thrown) {
+ LOG(INFO) << "Did partial pop of frames due to new exception";
+ }
+ return !new_exception_thrown;
+}
+
void Instrumentation::InstallStubsForClass(ObjPtr<mirror::Class> klass) {
if (!klass->IsResolved()) {
// We need the class to be resolved to install/uninstall stubs. Otherwise its methods
@@ -206,6 +188,7 @@
return class_linker->IsQuickResolutionStub(code) ||
class_linker->IsQuickToInterpreterBridge(code) ||
class_linker->IsQuickGenericJniStub(code) ||
+ (code == interpreter::GetNterpWithClinitEntryPoint()) ||
(code == GetQuickInstrumentationEntryPoint());
}
@@ -213,8 +196,7 @@
// Annoyingly this can be called before we have actually initialized WellKnownClasses so therefore
// we also need to check this based on the declaring-class descriptor. The check is valid because
// Proxy only has a single constructor.
- ArtMethod* well_known_proxy_init = jni::DecodeArtMethod(
- WellKnownClasses::java_lang_reflect_Proxy_init);
+ ArtMethod* well_known_proxy_init = WellKnownClasses::java_lang_reflect_Proxy_init;
if (well_known_proxy_init == method) {
return true;
}
@@ -227,11 +209,50 @@
method->GetDeclaringClass()->DescriptorEquals("Ljava/lang/reflect/Proxy;");
}
+// Returns true if we need entry exit stub to call entry hooks. JITed code
+// directly call entry / exit hooks and don't need the stub.
+static bool CodeSupportsEntryExitHooks(const void* entry_point, ArtMethod* method)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Proxy.init should always run with the switch interpreter where entry / exit hooks are
+ // supported.
+ if (IsProxyInit(method)) {
+ return true;
+ }
+
+ // In some tests runtime isn't setup fully and hence the entry points could be nullptr.
+ // just be conservative and return false here.
+ if (entry_point == nullptr) {
+ return false;
+ }
+
+ // Code running in the interpreter doesn't need entry/exit stubs.
+ if (Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(entry_point)) {
+ return true;
+ }
+
+ // When jiting code for debuggable runtimes / instrumentation is active we generate the code to
+ // call method entry / exit hooks when required.
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit != nullptr && jit->GetCodeCache()->ContainsPc(entry_point)) {
+ // If JITed code was compiled with instrumentation support we support entry / exit hooks.
+ OatQuickMethodHeader* header = OatQuickMethodHeader::FromEntryPoint(entry_point);
+ return CodeInfo::IsDebuggable(header->GetOptimizedCodeInfoPtr());
+ }
+
+ // GenericJni trampoline can handle entry / exit hooks.
+ if (Runtime::Current()->GetClassLinker()->IsQuickGenericJniStub(entry_point)) {
+ return true;
+ }
+
+ // The remaining cases are nterp / oat code / JIT code that isn't compiled with instrumentation
+ // support.
+ return false;
+}
+
static void UpdateEntryPoints(ArtMethod* method, const void* quick_code)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (kIsDebugBuild) {
- if (NeedsClinitCheckBeforeCall(method) &&
- !method->GetDeclaringClass()->IsVisiblyInitialized()) {
+ if (method->StillNeedsClinitCheckMayBeDead()) {
CHECK(CanHandleInitializationCheck(quick_code));
}
jit::Jit* jit = Runtime::Current()->GetJit();
@@ -244,6 +265,10 @@
if (IsProxyInit(method)) {
CHECK_NE(quick_code, GetQuickInstrumentationEntryPoint());
}
+ const Instrumentation* instr = Runtime::Current()->GetInstrumentation();
+ if (instr->EntryExitStubsInstalled()) {
+ DCHECK(CodeSupportsEntryExitHooks(quick_code, method));
+ }
}
// If the method is from a boot image, don't dirty it if the entrypoint
// doesn't change.
@@ -252,64 +277,22 @@
}
}
-bool Instrumentation::CodeNeedsEntryExitStub(const void* code, ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- // Proxy.init should never have entry/exit stubs.
- if (IsProxyInit(method)) {
- return false;
- }
-
- // In some tests runtime isn't setup fully and hence the entry points could
- // be nullptr.
- if (code == nullptr) {
- return true;
- }
-
- // Code running in the interpreter doesn't need entry/exit stubs.
- if (Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(code)) {
- return false;
- }
-
- // When jiting code for debuggable apps we generate the code to call method
- // entry / exit hooks when required. Hence it is not required to update
- // to instrumentation entry point for JITed code in debuggable mode.
- if (!Runtime::Current()->IsJavaDebuggable()) {
- return true;
- }
-
- // Native functions can have JITed entry points but we don't include support
- // for calling entry / exit hooks directly from the JITed code for native
- // functions. So we still have to install entry exit stubs for such cases.
- if (method->IsNative()) {
- return true;
- }
-
- jit::Jit* jit = Runtime::Current()->GetJit();
- if (jit != nullptr && jit->GetCodeCache()->ContainsPc(code)) {
- return false;
- }
- return true;
+bool Instrumentation::NeedsDexPcEvents(ArtMethod* method, Thread* thread) {
+ return (InterpretOnly(method) || thread->IsForceInterpreter()) && HasDexPcListeners();
}
bool Instrumentation::InterpretOnly(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
if (method->IsNative()) {
return false;
}
- return InterpretOnly() ||
- IsDeoptimized(method) ||
- Runtime::Current()->GetRuntimeCallbacks()->IsMethodBeingInspected(method);
+ return InterpretOnly() || IsDeoptimized(method);
}
-static bool CanUseAotCode(ArtMethod* method, const void* quick_code)
+static bool CanUseAotCode(const void* quick_code)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (quick_code == nullptr) {
return false;
}
- if (method->IsNative()) {
- // AOT code for native methods can always be used.
- return true;
- }
-
Runtime* runtime = Runtime::Current();
// For simplicity, we never use AOT code for debuggable.
if (runtime->IsJavaDebuggable()) {
@@ -332,7 +315,7 @@
static bool CanUseNterp(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
return interpreter::CanRuntimeUseNterp() &&
CanMethodUseNterp(method) &&
- method->GetDeclaringClass()->IsVerified();
+ method->IsDeclaringClassVerifiedMayBeDead();
}
static const void* GetOptimizedCodeFor(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -345,7 +328,7 @@
// In debuggable mode, we can only use AOT code for native methods.
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
const void* aot_code = method->GetOatMethodQuickCode(class_linker->GetImagePointerSize());
- if (CanUseAotCode(method, aot_code)) {
+ if (CanUseAotCode(aot_code)) {
return aot_code;
}
@@ -370,24 +353,24 @@
void Instrumentation::InitializeMethodsCode(ArtMethod* method, const void* aot_code)
REQUIRES_SHARED(Locks::mutator_lock_) {
- // Use instrumentation entrypoints if instrumentation is installed.
- if (UNLIKELY(EntryExitStubsInstalled()) && !IsProxyInit(method)) {
- if (!method->IsNative() && InterpretOnly(method)) {
- UpdateEntryPoints(method, GetQuickToInterpreterBridge());
- } else {
- UpdateEntryPoints(method, GetQuickInstrumentationEntryPoint());
- }
+ if (!method->IsInvokable()) {
+ DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr ||
+ Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(
+ method->GetEntryPointFromQuickCompiledCode()));
+ UpdateEntryPoints(method, GetQuickToInterpreterBridge());
return;
}
- if (UNLIKELY(IsForcedInterpretOnly() || IsDeoptimized(method))) {
+ // Use instrumentation entrypoints if instrumentation is installed.
+ if (UNLIKELY(EntryExitStubsInstalled() || IsForcedInterpretOnly() || IsDeoptimized(method))) {
UpdateEntryPoints(
method, method->IsNative() ? GetQuickGenericJniStub() : GetQuickToInterpreterBridge());
return;
}
// Special case if we need an initialization check.
- if (NeedsClinitCheckBeforeCall(method) && !method->GetDeclaringClass()->IsVisiblyInitialized()) {
+ // The method and its declaring class may be dead when starting JIT GC during managed heap GC.
+ if (method->StillNeedsClinitCheckMayBeDead()) {
// If we have code but the method needs a class initialization check before calling
// that code, install the resolution stub that will perform the check.
// It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines
@@ -396,7 +379,12 @@
// stub only if we have compiled code or we can execute nterp, and the method needs a class
// initialization check.
if (aot_code != nullptr || method->IsNative() || CanUseNterp(method)) {
- UpdateEntryPoints(method, GetQuickResolutionStub());
+ if (kIsDebugBuild && CanUseNterp(method)) {
+ // Adds some test coverage for the nterp clinit entrypoint.
+ UpdateEntryPoints(method, interpreter::GetNterpWithClinitEntryPoint());
+ } else {
+ UpdateEntryPoints(method, GetQuickResolutionStub());
+ }
} else {
UpdateEntryPoints(method, GetQuickToInterpreterBridge());
}
@@ -404,7 +392,7 @@
}
// Use the provided AOT code if possible.
- if (CanUseAotCode(method, aot_code)) {
+ if (CanUseAotCode(aot_code)) {
UpdateEntryPoints(method, aot_code);
return;
}
@@ -442,9 +430,11 @@
}
if (EntryExitStubsInstalled()) {
- // Install the instrumentation entry point if needed.
- if (CodeNeedsEntryExitStub(method->GetEntryPointFromQuickCompiledCode(), method)) {
- UpdateEntryPoints(method, GetQuickInstrumentationEntryPoint());
+ // Install interpreter bridge / GenericJni stub if the existing code doesn't support
+ // entry / exit hooks.
+ if (!CodeSupportsEntryExitHooks(method->GetEntryPointFromQuickCompiledCode(), method)) {
+ UpdateEntryPoints(
+ method, method->IsNative() ? GetQuickGenericJniStub() : GetQuickToInterpreterBridge());
}
return;
}
@@ -452,13 +442,45 @@
// We're being asked to restore the entrypoints after instrumentation.
CHECK_EQ(instrumentation_level_, InstrumentationLevel::kInstrumentNothing);
// We need to have the resolution stub still if the class is not initialized.
- if (NeedsClinitCheckBeforeCall(method) && !method->GetDeclaringClass()->IsVisiblyInitialized()) {
+ if (method->StillNeedsClinitCheck()) {
UpdateEntryPoints(method, GetQuickResolutionStub());
return;
}
UpdateEntryPoints(method, GetOptimizedCodeFor(method));
}
+void Instrumentation::UpdateEntrypointsForDebuggable() {
+ Runtime* runtime = Runtime::Current();
+ // If we are transitioning from non-debuggable to debuggable, we patch
+ // entry points of methods to remove any aot / JITed entry points.
+ InstallStubsClassVisitor visitor(this);
+ runtime->GetClassLinker()->VisitClasses(&visitor);
+}
+
+bool Instrumentation::MethodSupportsExitEvents(ArtMethod* method,
+ const OatQuickMethodHeader* header) {
+ if (header == nullptr) {
+ // Header can be a nullptr for runtime / proxy methods that doesn't support method exit hooks
+ // or for native methods that use generic jni stubs. Generic jni stubs support method exit
+ // hooks.
+ return method->IsNative();
+ }
+
+ if (header->IsNterpMethodHeader()) {
+ // Nterp doesn't support method exit events
+ return false;
+ }
+
+ DCHECK(header->IsOptimized());
+ if (CodeInfo::IsDebuggable(header->GetOptimizedCodeInfoPtr())) {
+ // For optimized code, we only support method entry / exit hooks if they are compiled as
+ // debuggable.
+ return true;
+ }
+
+ return false;
+}
+
// Places the instrumentation exit pc as the return PC for every quick frame. This also allows
// deoptimization of quick frames to interpreter frames. When force_deopt is
// true the frames have to be deoptimized. If the frame has a deoptimization
@@ -474,116 +496,72 @@
struct InstallStackVisitor final : public StackVisitor {
InstallStackVisitor(Thread* thread_in,
Context* context,
- uintptr_t instrumentation_exit_pc,
- uint64_t force_deopt_id,
bool deopt_all_frames)
: StackVisitor(thread_in, context, kInstrumentationStackWalk),
- instrumentation_stack_(thread_in->GetInstrumentationStack()),
- instrumentation_exit_pc_(instrumentation_exit_pc),
- reached_existing_instrumentation_frames_(false),
- force_deopt_id_(force_deopt_id),
- deopt_all_frames_(deopt_all_frames) {}
+ deopt_all_frames_(deopt_all_frames),
+ runtime_methods_need_deopt_check_(false) {}
bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* m = GetMethod();
- if (m == nullptr) {
+ if (m == nullptr || m->IsRuntimeMethod()) {
if (kVerboseInstrumentation) {
- LOG(INFO) << " Skipping upcall. Frame " << GetFrameId();
+ LOG(INFO) << " Skipping upcall / runtime method. Frame " << GetFrameId();
}
- return true; // Ignore upcalls.
+ return true; // Ignore upcalls and runtime methods.
}
+
+ // Handle interpreter frame.
if (GetCurrentQuickFrame() == nullptr) {
+ // Since we are updating the instrumentation related information we have to recalculate
+ // NeedsDexPcEvents. For example, when a new method or thread is deoptimized / interpreter
+ // stubs are installed the NeedsDexPcEvents could change for the shadow frames on the stack.
+ // If we don't update it here we would miss reporting dex pc events which is incorrect.
+ ShadowFrame* shadow_frame = GetCurrentShadowFrame();
+ DCHECK(shadow_frame != nullptr);
+ shadow_frame->SetNotifyDexPcMoveEvents(
+ Runtime::Current()->GetInstrumentation()->NeedsDexPcEvents(GetMethod(), GetThread()));
if (kVerboseInstrumentation) {
LOG(INFO) << "Pushing shadow frame method " << m->PrettyMethod();
}
stack_methods_.push_back(m);
return true; // Continue.
}
- uintptr_t return_pc = GetReturnPc();
+
+ DCHECK(!m->IsRuntimeMethod());
if (kVerboseInstrumentation) {
- LOG(INFO) << " Installing exit stub in " << DescribeLocation();
+ LOG(INFO) << " Processing quick frame for updating exit hooks " << DescribeLocation();
}
- if (return_pc == instrumentation_exit_pc_) {
- auto it = instrumentation_stack_->find(GetReturnPcAddr());
- CHECK(it != instrumentation_stack_->end());
- const InstrumentationStackFrame& frame = it->second;
- if (m->IsRuntimeMethod()) {
- if (frame.interpreter_entry_) {
- return true;
- }
- }
- // We've reached a frame which has already been installed with instrumentation exit stub.
- // We should have already installed instrumentation or be interpreter on previous frames.
- reached_existing_instrumentation_frames_ = true;
-
- // Trampolines get replaced with their actual method in the stack,
- // so don't do the check below for runtime methods.
- if (!frame.method_->IsRuntimeMethod()) {
- CHECK_EQ(m->GetNonObsoleteMethod(), frame.method_->GetNonObsoleteMethod())
- << "Expected " << ArtMethod::PrettyMethod(m)
- << ", Found " << ArtMethod::PrettyMethod(frame.method_);
- }
- return_pc = frame.return_pc_;
- if (kVerboseInstrumentation) {
- LOG(INFO) << "Ignoring already instrumented " << frame.Dump();
- }
- } else {
- // If it is a JITed frame then just set the deopt bit if required
- // otherwise continue
- const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader();
- if (method_header != nullptr && method_header->HasShouldDeoptimizeFlag()) {
- if (deopt_all_frames_) {
- SetShouldDeoptimizeFlag(DeoptimizeFlagValue::kDebug);
- }
- return true;
- }
- CHECK_NE(return_pc, 0U);
- if (UNLIKELY(reached_existing_instrumentation_frames_ && !m->IsRuntimeMethod())) {
- // We already saw an existing instrumentation frame so this should be a runtime-method
- // inserted by the interpreter or runtime.
- std::string thread_name;
- GetThread()->GetThreadName(thread_name);
- LOG(FATAL) << "While walking " << thread_name << " found unexpected non-runtime method"
- << " without instrumentation exit return or interpreter frame."
- << " method is " << GetMethod()->PrettyMethod()
- << " return_pc is " << std::hex << return_pc;
- UNREACHABLE();
- }
- if (m->IsRuntimeMethod()) {
- size_t frame_size = GetCurrentQuickFrameInfo().FrameSizeInBytes();
- ArtMethod** caller_frame = reinterpret_cast<ArtMethod**>(
- reinterpret_cast<uint8_t*>(GetCurrentQuickFrame()) + frame_size);
- if (*caller_frame != nullptr && (*caller_frame)->IsNative()) {
- // Do not install instrumentation exit on return to JNI stubs.
- return true;
- }
- }
- InstrumentationStackFrame instrumentation_frame(
- m->IsRuntimeMethod() ? nullptr : GetThisObject().Ptr(),
- m,
- return_pc,
- false,
- force_deopt_id_);
- if (kVerboseInstrumentation) {
- LOG(INFO) << "Pushing frame " << instrumentation_frame.Dump();
- }
-
- if (!m->IsRuntimeMethod()) {
- // Runtime methods don't need to run method entry callbacks.
- stack_methods_.push_back(m);
- }
- instrumentation_stack_->insert({GetReturnPcAddr(), instrumentation_frame});
- SetReturnPc(instrumentation_exit_pc_);
+ const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader();
+ if (Runtime::Current()->GetInstrumentation()->MethodSupportsExitEvents(m, method_header)) {
+ // It is unexpected to see a method enter event but not a method exit event so record stack
+ // methods only for frames that support method exit events. Even if we deoptimize we make
+ // sure that we only call method exit event if the frame supported it in the first place.
+ // For ex: deoptimizing from JITed code with debug support calls a method exit hook but
+ // deoptimizing from nterp doesn't.
+ stack_methods_.push_back(m);
}
+
+ // If it is a JITed frame then just set the deopt bit if required otherwise continue.
+ // We need kForceDeoptForRedefinition to ensure we don't use any JITed code after a
+ // redefinition. We support redefinition only if the runtime has started off as a
+ // debuggable runtime which makes sure we don't use any AOT or Nterp code.
+ // The CheckCallerForDeopt is an optimization which we only do for non-native JITed code for
+ // now. We can extend it to native methods but that needs reserving an additional stack slot.
+ // We don't do it currently since that wasn't important for debugger performance.
+ if (method_header != nullptr && method_header->HasShouldDeoptimizeFlag()) {
+ if (deopt_all_frames_) {
+ runtime_methods_need_deopt_check_ = true;
+ SetShouldDeoptimizeFlag(DeoptimizeFlagValue::kForceDeoptForRedefinition);
+ }
+ SetShouldDeoptimizeFlag(DeoptimizeFlagValue::kCheckCallerForDeopt);
+ }
+
return true; // Continue.
}
- std::map<uintptr_t, InstrumentationStackFrame>* const instrumentation_stack_;
std::vector<ArtMethod*> stack_methods_;
- const uintptr_t instrumentation_exit_pc_;
- bool reached_existing_instrumentation_frames_;
- uint64_t force_deopt_id_;
bool deopt_all_frames_;
+ bool runtime_methods_need_deopt_check_;
};
if (kVerboseInstrumentation) {
std::string thread_name;
@@ -593,14 +571,15 @@
Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg);
std::unique_ptr<Context> context(Context::Create());
- uintptr_t instrumentation_exit_pc = reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc());
InstallStackVisitor visitor(thread,
context.get(),
- instrumentation_exit_pc,
- instrumentation->current_force_deopt_id_,
deopt_all_frames);
visitor.WalkStack(true);
+ if (visitor.runtime_methods_need_deopt_check_) {
+ thread->SetDeoptCheckRequired(true);
+ }
+
if (instrumentation->ShouldNotifyMethodEnterExitEvents()) {
// Create method enter events for all methods currently on the thread's stack. We only do this
// if we haven't already processed the method enter events.
@@ -611,6 +590,34 @@
thread->VerifyStack();
}
+void UpdateNeedsDexPcEventsOnStack(Thread* thread) REQUIRES(Locks::mutator_lock_) {
+ Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
+
+ struct InstallStackVisitor final : public StackVisitor {
+ InstallStackVisitor(Thread* thread_in, Context* context)
+ : StackVisitor(thread_in, context, kInstrumentationStackWalk) {}
+
+ bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) {
+ ShadowFrame* shadow_frame = GetCurrentShadowFrame();
+ if (shadow_frame != nullptr) {
+ shadow_frame->SetNotifyDexPcMoveEvents(
+ Runtime::Current()->GetInstrumentation()->NeedsDexPcEvents(GetMethod(), GetThread()));
+ }
+ return true;
+ }
+ };
+
+ if (kVerboseInstrumentation) {
+ std::string thread_name;
+ thread->GetThreadName(thread_name);
+ LOG(INFO) << "Updating DexPcMoveEvents on shadow frames on stack " << thread_name;
+ }
+
+ std::unique_ptr<Context> context(Context::Create());
+ InstallStackVisitor visitor(thread, context.get());
+ visitor.WalkStack(true);
+}
+
void Instrumentation::InstrumentThreadStack(Thread* thread, bool force_deopt) {
instrumentation_stubs_installed_ = true;
InstrumentationInstallStack(thread, this, force_deopt);
@@ -622,19 +629,14 @@
Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
struct RestoreStackVisitor final : public StackVisitor {
- RestoreStackVisitor(Thread* thread_in, uintptr_t instrumentation_exit_pc,
+ RestoreStackVisitor(Thread* thread_in,
Instrumentation* instrumentation)
: StackVisitor(thread_in, nullptr, kInstrumentationStackWalk),
thread_(thread_in),
- instrumentation_exit_pc_(instrumentation_exit_pc),
instrumentation_(instrumentation),
- instrumentation_stack_(thread_in->GetInstrumentationStack()),
- frames_removed_(0) {}
+ runtime_methods_need_deopt_check_(false) {}
bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) {
- if (instrumentation_stack_->size() == 0) {
- return false; // Stop.
- }
ArtMethod* m = GetMethod();
if (GetCurrentQuickFrame() == nullptr) {
if (kVerboseInstrumentation) {
@@ -647,59 +649,32 @@
if (kVerboseInstrumentation) {
LOG(INFO) << " Skipping upcall. Frame " << GetFrameId();
}
- return true; // Ignore upcalls.
+ return true; // Ignore upcalls and runtime methods.
}
- auto it = instrumentation_stack_->find(GetReturnPcAddr());
- if (it != instrumentation_stack_->end()) {
- const InstrumentationStackFrame& instrumentation_frame = it->second;
- if (kVerboseInstrumentation) {
- LOG(INFO) << " Removing exit stub in " << DescribeLocation();
+ const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader();
+ if (method_header != nullptr && method_header->HasShouldDeoptimizeFlag()) {
+ if (ShouldForceDeoptForRedefinition()) {
+ runtime_methods_need_deopt_check_ = true;
}
- if (instrumentation_frame.interpreter_entry_) {
- CHECK(m == Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs));
- } else {
- CHECK_EQ(m->GetNonObsoleteMethod(),
- instrumentation_frame.method_->GetNonObsoleteMethod())
- << ArtMethod::PrettyMethod(m)
- << " and " << instrumentation_frame.method_->GetNonObsoleteMethod()->PrettyMethod();
- }
- SetReturnPc(instrumentation_frame.return_pc_);
- if (instrumentation_->ShouldNotifyMethodEnterExitEvents() &&
- !m->IsRuntimeMethod()) {
- // Create the method exit events. As the methods didn't really exit the result is 0.
- // We only do this if no debugger is attached to prevent from posting events twice.
- JValue val;
- instrumentation_->MethodExitEvent(thread_, m, OptionalFrame{}, val);
- }
- frames_removed_++;
- } else {
- if (kVerboseInstrumentation) {
- LOG(INFO) << " No exit stub in " << DescribeLocation();
- }
+ UnsetShouldDeoptimizeFlag(DeoptimizeFlagValue::kCheckCallerForDeopt);
}
return true; // Continue.
}
Thread* const thread_;
- const uintptr_t instrumentation_exit_pc_;
Instrumentation* const instrumentation_;
- std::map<uintptr_t, instrumentation::InstrumentationStackFrame>* const instrumentation_stack_;
- size_t frames_removed_;
+ bool runtime_methods_need_deopt_check_;
};
if (kVerboseInstrumentation) {
std::string thread_name;
thread->GetThreadName(thread_name);
LOG(INFO) << "Removing exit stubs in " << thread_name;
}
- std::map<uintptr_t, instrumentation::InstrumentationStackFrame>* stack =
- thread->GetInstrumentationStack();
- if (stack->size() > 0) {
- Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg);
- uintptr_t instrumentation_exit_pc =
- reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc());
- RestoreStackVisitor visitor(thread, instrumentation_exit_pc, instrumentation);
- visitor.WalkStack(true);
- CHECK_EQ(visitor.frames_removed_, stack->size());
- stack->clear();
+ Instrumentation* instrumentation = reinterpret_cast<Instrumentation*>(arg);
+ RestoreStackVisitor visitor(thread, instrumentation);
+ visitor.WalkStack(true);
+ DCHECK_IMPLIES(visitor.runtime_methods_need_deopt_check_, thread->IsDeoptCheckRequired());
+ if (!visitor.runtime_methods_need_deopt_check_) {
+ thread->SetDeoptCheckRequired(false);
}
}
@@ -791,6 +766,12 @@
exception_handled_listeners_,
listener,
&have_exception_handled_listeners_);
+ if (HasEvent(kDexPcMoved, events)) {
+ MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
+ for (Thread* thread : Runtime::Current()->GetThreadList()->GetList()) {
+ UpdateNeedsDexPcEventsOnStack(thread);
+ }
+ }
}
static void PotentiallyRemoveListenerFrom(Instrumentation::InstrumentationEvent event,
@@ -872,6 +853,12 @@
exception_handled_listeners_,
listener,
&have_exception_handled_listeners_);
+ if (HasEvent(kDexPcMoved, events)) {
+ MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
+ for (Thread* thread : Runtime::Current()->GetThreadList()->GetList()) {
+ UpdateNeedsDexPcEventsOnStack(thread);
+ }
+ }
}
Instrumentation::InstrumentationLevel Instrumentation::GetCurrentInstrumentationLevel() const {
@@ -950,17 +937,15 @@
Locks::mutator_lock_->AssertExclusiveHeld(self);
Locks::thread_list_lock_->AssertNotHeld(self);
UpdateInstrumentationLevel(requested_level);
+ InstallStubsClassVisitor visitor(this);
+ runtime->GetClassLinker()->VisitClasses(&visitor);
if (requested_level > InstrumentationLevel::kInstrumentNothing) {
- InstallStubsClassVisitor visitor(this);
- runtime->GetClassLinker()->VisitClasses(&visitor);
instrumentation_stubs_installed_ = true;
MutexLock mu(self, *Locks::thread_list_lock_);
for (Thread* thread : Runtime::Current()->GetThreadList()->GetList()) {
InstrumentThreadStack(thread, /* deopt_all_frames= */ false);
}
} else {
- InstallStubsClassVisitor visitor(this);
- runtime->GetClassLinker()->VisitClasses(&visitor);
MaybeRestoreInstrumentationStack();
}
}
@@ -1046,6 +1031,8 @@
return "obsolete";
} else if (code == interpreter::GetNterpEntryPoint()) {
return "nterp";
+ } else if (code == interpreter::GetNterpWithClinitEntryPoint()) {
+ return "nterp with clinit";
} else if (class_linker->IsQuickGenericJniStub(code)) {
return "generic jni";
} else if (Runtime::Current()->GetOatFileManager().ContainsPc(code)) {
@@ -1076,12 +1063,11 @@
return;
}
- if (EntryExitStubsInstalled() && CodeNeedsEntryExitStub(new_code, method)) {
- DCHECK(method->GetEntryPointFromQuickCompiledCode() == GetQuickInstrumentationEntryPoint() ||
- class_linker->IsQuickToInterpreterBridge(method->GetEntryPointFromQuickCompiledCode()))
- << EntryPointString(method->GetEntryPointFromQuickCompiledCode())
- << " " << method->PrettyMethod();
- // If the code we want to update the method with still needs entry/exit stub, just skip.
+ if (EntryExitStubsInstalled() && !CodeSupportsEntryExitHooks(new_code, method)) {
+ DCHECK(CodeSupportsEntryExitHooks(method->GetEntryPointFromQuickCompiledCode(), method))
+ << EntryPointString(method->GetEntryPointFromQuickCompiledCode()) << " "
+ << method->PrettyMethod();
+ // If we need entry / exit stubs but the new_code doesn't support entry / exit hooks just skip.
return;
}
@@ -1093,8 +1079,9 @@
// We don't do any read barrier on `method`'s declaring class in this code, as the JIT might
// enter here on a soon-to-be deleted ArtMethod. Updating the entrypoint is OK though, as
// the ArtMethod is still in memory.
- if (EntryExitStubsInstalled()) {
- // If stubs are installed don't update.
+ if (EntryExitStubsInstalled() && !CodeSupportsEntryExitHooks(new_code, method)) {
+ // If the new code doesn't support entry exit hooks but we need them don't update with the new
+ // code.
return;
}
UpdateEntryPoints(method, new_code);
@@ -1119,14 +1106,6 @@
return deoptimized_methods_.find(method) != deoptimized_methods_.end();
}
-ArtMethod* Instrumentation::BeginDeoptimizedMethod() {
- if (deoptimized_methods_.empty()) {
- // Empty.
- return nullptr;
- }
- return *deoptimized_methods_.begin();
-}
-
bool Instrumentation::RemoveDeoptimizedMethod(ArtMethod* method) {
auto it = deoptimized_methods_.find(method);
if (it == deoptimized_methods_.end()) {
@@ -1136,10 +1115,6 @@
return true;
}
-bool Instrumentation::IsDeoptimizedMethodsEmptyLocked() const {
- return deoptimized_methods_.empty();
-}
-
void Instrumentation::Deoptimize(ArtMethod* method) {
CHECK(!method->IsNative());
CHECK(!method->IsProxyMethod());
@@ -1147,7 +1122,7 @@
Thread* self = Thread::Current();
{
- WriterMutexLock mu(self, *GetDeoptimizedMethodsLock());
+ Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
bool has_not_been_deoptimized = AddDeoptimizedMethod(method);
CHECK(has_not_been_deoptimized) << "Method " << ArtMethod::PrettyMethod(method)
<< " is already deoptimized";
@@ -1173,9 +1148,8 @@
CHECK(!method->IsProxyMethod());
CHECK(method->IsInvokable());
- Thread* self = Thread::Current();
{
- WriterMutexLock mu(self, *GetDeoptimizedMethodsLock());
+ Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
bool found_and_erased = RemoveDeoptimizedMethod(method);
CHECK(found_and_erased) << "Method " << ArtMethod::PrettyMethod(method)
<< " is not deoptimized";
@@ -1186,12 +1160,19 @@
return;
}
+ if (method->IsObsolete()) {
+ // Don't update entry points for obsolete methods. The entrypoint should
+ // have been set to InvokeObsoleteMethoStub.
+ DCHECK_EQ(method->GetEntryPointFromQuickCompiledCodePtrSize(kRuntimePointerSize),
+ GetInvokeObsoleteMethodStub());
+ return;
+ }
+
// We are not using interpreter stubs for deoptimization. Restore the code of the method.
// We still retain interpreter bridge if we need it for other reasons.
if (InterpretOnly(method)) {
UpdateEntryPoints(method, GetQuickToInterpreterBridge());
- } else if (NeedsClinitCheckBeforeCall(method) &&
- !method->GetDeclaringClass()->IsVisiblyInitialized()) {
+ } else if (method->StillNeedsClinitCheck()) {
UpdateEntryPoints(method, GetQuickResolutionStub());
} else {
UpdateEntryPoints(method, GetMaybeInstrumentedCodeForInvoke(method));
@@ -1204,29 +1185,26 @@
}
bool Instrumentation::IsDeoptimizedMethodsEmpty() const {
- ReaderMutexLock mu(Thread::Current(), *GetDeoptimizedMethodsLock());
return deoptimized_methods_.empty();
}
bool Instrumentation::IsDeoptimized(ArtMethod* method) {
DCHECK(method != nullptr);
- ReaderMutexLock mu(Thread::Current(), *GetDeoptimizedMethodsLock());
return IsDeoptimizedMethod(method);
}
-
void Instrumentation::DisableDeoptimization(const char* key) {
// Remove any instrumentation support added for deoptimization.
ConfigureStubs(key, InstrumentationLevel::kInstrumentNothing);
+ Locks::mutator_lock_->AssertExclusiveHeld(Thread::Current());
// Undeoptimized selected methods.
while (true) {
ArtMethod* method;
{
- ReaderMutexLock mu(Thread::Current(), *GetDeoptimizedMethodsLock());
- if (IsDeoptimizedMethodsEmptyLocked()) {
+ if (deoptimized_methods_.empty()) {
break;
}
- method = BeginDeoptimizedMethod();
+ method = *deoptimized_methods_.begin();
CHECK(method != nullptr);
}
Undeoptimize(method);
@@ -1271,8 +1249,8 @@
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
const void* code = method->GetEntryPointFromQuickCompiledCodePtrSize(kRuntimePointerSize);
// If we don't have the instrumentation, the resolution stub, or the
- // interpreter as entrypoint, just return the current entrypoint, assuming
- // it's the most optimized.
+ // interpreter, just return the current entrypoint,
+ // assuming it's the most optimized.
if (code != GetQuickInstrumentationEntryPoint() &&
!class_linker->IsQuickResolutionStub(code) &&
!class_linker->IsQuickToInterpreterBridge(code)) {
@@ -1291,8 +1269,8 @@
// This is called by resolution trampolines and that should never be getting proxy methods.
DCHECK(!method->IsProxyMethod()) << method->PrettyMethod();
const void* code = GetCodeForInvoke(method);
- if (EntryExitStubsInstalled() && CodeNeedsEntryExitStub(code, method)) {
- return GetQuickInstrumentationEntryPoint();
+ if (EntryExitStubsInstalled() && !CodeSupportsEntryExitHooks(code, method)) {
+ return method->IsNative() ? GetQuickGenericJniStub() : GetQuickToInterpreterBridge();
}
return code;
}
@@ -1345,16 +1323,12 @@
}
void Instrumentation::MethodUnwindEvent(Thread* thread,
- ObjPtr<mirror::Object> this_object,
ArtMethod* method,
uint32_t dex_pc) const {
if (HasMethodUnwindListeners()) {
- Thread* self = Thread::Current();
- StackHandleScope<1> hs(self);
- Handle<mirror::Object> thiz(hs.NewHandle(this_object));
for (InstrumentationListener* listener : method_unwind_listeners_) {
if (listener != nullptr) {
- listener->MethodUnwind(thread, thiz, method, dex_pc);
+ listener->MethodUnwind(thread, method, dex_pc);
}
}
}
@@ -1489,7 +1463,7 @@
if (!interpreter_entry) {
MethodEnterEvent(self, method);
if (self->IsExceptionPending()) {
- MethodUnwindEvent(self, h_this.Get(), method, 0);
+ MethodUnwindEvent(self, method, 0);
return;
}
}
@@ -1518,83 +1492,18 @@
return DeoptimizationMethodType::kDefault;
}
-// Try to get the shorty of a runtime method if it's an invocation stub.
-static char GetRuntimeMethodShorty(Thread* thread) REQUIRES_SHARED(Locks::mutator_lock_) {
- char shorty = 'V';
- StackVisitor::WalkStack(
- [&shorty](const art::StackVisitor* stack_visitor) REQUIRES_SHARED(Locks::mutator_lock_) {
- ArtMethod* m = stack_visitor->GetMethod();
- if (m == nullptr || m->IsRuntimeMethod()) {
- return true;
- }
- // The first Java method.
- if (m->IsNative()) {
- // Use JNI method's shorty for the jni stub.
- shorty = m->GetShorty()[0];
- } else if (m->IsProxyMethod()) {
- // Proxy method just invokes its proxied method via
- // art_quick_proxy_invoke_handler.
- shorty = m->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty()[0];
- } else {
- const Instruction& instr = m->DexInstructions().InstructionAt(stack_visitor->GetDexPc());
- if (instr.IsInvoke()) {
- uint16_t method_index = static_cast<uint16_t>(instr.VRegB());
- const DexFile* dex_file = m->GetDexFile();
- if (interpreter::IsStringInit(dex_file, method_index)) {
- // Invoking string init constructor is turned into invoking
- // StringFactory.newStringFromChars() which returns a string.
- shorty = 'L';
- } else {
- shorty = dex_file->GetMethodShorty(method_index)[0];
- }
-
- } else {
- // It could be that a non-invoke opcode invokes a stub, which in turn
- // invokes Java code. In such cases, we should never expect a return
- // value from the stub.
- }
- }
- // Stop stack walking since we've seen a Java frame.
- return false;
- },
- thread,
- /* context= */ nullptr,
- art::StackVisitor::StackWalkKind::kIncludeInlinedFrames);
- return shorty;
-}
-
-JValue Instrumentation::GetReturnValue(
- Thread* self, ArtMethod* method, bool* is_ref, uint64_t* gpr_result, uint64_t* fpr_result) {
+JValue Instrumentation::GetReturnValue(ArtMethod* method,
+ bool* is_ref,
+ uint64_t* gpr_result,
+ uint64_t* fpr_result) {
uint32_t length;
const PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- char return_shorty;
// Runtime method does not call into MethodExitEvent() so there should not be
// suspension point below.
ScopedAssertNoThreadSuspension ants(__FUNCTION__, method->IsRuntimeMethod());
- if (method->IsRuntimeMethod()) {
- Runtime* runtime = Runtime::Current();
- if (method != runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForClinit) &&
- method != runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForSuspendCheck)) {
- // If the caller is at an invocation point and the runtime method is not
- // for clinit, we need to pass return results to the caller.
- // We need the correct shorty to decide whether we need to pass the return
- // result for deoptimization below.
- return_shorty = GetRuntimeMethodShorty(self);
- } else {
- // Some runtime methods such as allocations, unresolved field getters, etc.
- // have return value. We don't need to set return_value since MethodExitEvent()
- // below isn't called for runtime methods. Deoptimization doesn't need the
- // value either since the dex instruction will be re-executed by the
- // interpreter, except these two cases:
- // (1) For an invoke, which is handled above to get the correct shorty.
- // (2) For MONITOR_ENTER/EXIT, which cannot be re-executed since it's not
- // idempotent. However there is no return value for it anyway.
- return_shorty = 'V';
- }
- } else {
- return_shorty = method->GetInterfaceMethodIfProxy(pointer_size)->GetShorty(&length)[0];
- }
+ DCHECK(!method->IsRuntimeMethod());
+ char return_shorty = method->GetInterfaceMethodIfProxy(pointer_size)->GetShorty(&length)[0];
*is_ref = return_shorty == '[' || return_shorty == 'L';
JValue return_value;
@@ -1608,27 +1517,143 @@
return return_value;
}
-bool Instrumentation::ShouldDeoptimizeMethod(Thread* self, const NthCallerVisitor& visitor) {
- bool should_deoptimize_frame = false;
- const OatQuickMethodHeader* header = visitor.GetCurrentOatQuickMethodHeader();
- if (header != nullptr && header->HasShouldDeoptimizeFlag()) {
- uint8_t should_deopt_flag = visitor.GetShouldDeoptimizeFlag();
- // DeoptimizeFlag could be set for debugging or for CHA invalidations.
- // Deoptimize here only if it was requested for debugging. CHA
- // invalidations are handled in the JITed code.
- if ((should_deopt_flag & static_cast<uint8_t>(DeoptimizeFlagValue::kDebug)) != 0) {
- should_deoptimize_frame = true;
- }
+bool Instrumentation::PushDeoptContextIfNeeded(Thread* self,
+ DeoptimizationMethodType deopt_type,
+ bool is_ref,
+ const JValue& return_value)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (self->IsExceptionPending()) {
+ return false;
}
- return (visitor.caller != nullptr) &&
- (InterpreterStubsInstalled() || IsDeoptimized(visitor.caller) ||
+
+ ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame();
+ DCHECK(sp != nullptr && (*sp)->IsRuntimeMethod());
+ if (!ShouldDeoptimizeCaller(self, sp)) {
+ return false;
+ }
+
+ // TODO(mythria): The current deopt behaviour is we just re-execute the
+ // alloc instruction so we don't need the return value. For instrumentation
+ // related deopts, we actually don't need to and can use the result we got
+ // here. Since this is a debug only feature it is not very important but
+ // consider reusing the result in future.
+ self->PushDeoptimizationContext(
+ return_value, is_ref, nullptr, /* from_code= */ false, deopt_type);
+ self->SetException(Thread::GetDeoptimizationException());
+ return true;
+}
+
+void Instrumentation::DeoptimizeIfNeeded(Thread* self,
+ ArtMethod** sp,
+ DeoptimizationMethodType type,
+ JValue return_value,
+ bool is_reference) {
+ if (self->IsAsyncExceptionPending() || ShouldDeoptimizeCaller(self, sp)) {
+ self->PushDeoptimizationContext(return_value,
+ is_reference,
+ nullptr,
+ /* from_code= */ false,
+ type);
+ // This is requested from suspend points or when returning from runtime methods so exit
+ // callbacks wouldn't be run yet. So don't skip method callbacks.
+ artDeoptimize(self, /* skip_method_exit_callbacks= */ false);
+ }
+}
+
+bool Instrumentation::NeedsSlowInterpreterForMethod(Thread* self, ArtMethod* method) {
+ return (method != nullptr) &&
+ (InterpreterStubsInstalled() ||
+ IsDeoptimized(method) ||
self->IsForceInterpreter() ||
// NB Since structurally obsolete compiled methods might have the offsets of
// methods/fields compiled in we need to go back to interpreter whenever we hit
// them.
- visitor.caller->GetDeclaringClass()->IsObsoleteObject() ||
- Dbg::IsForcedInterpreterNeededForUpcall(self, visitor.caller) ||
- should_deoptimize_frame);
+ method->GetDeclaringClass()->IsObsoleteObject() ||
+ Dbg::IsForcedInterpreterNeededForUpcall(self, method));
+}
+
+bool Instrumentation::ShouldDeoptimizeCaller(Thread* self, ArtMethod** sp) {
+ // When exit stubs aren't installed we don't need to check for any instrumentation related
+ // deoptimizations.
+ // TODO(mythria): Once we remove instrumentation stubs rename AreExitStubsInstalled. This is
+ // used to check if any instrumentation related work needs to be done. For ex: calling method
+ // entry / exit hooks, checking for instrumentation related deopts in suspend points
+ if (!AreExitStubsInstalled()) {
+ return false;
+ }
+
+ ArtMethod* runtime_method = *sp;
+ DCHECK(runtime_method->IsRuntimeMethod());
+ QuickMethodFrameInfo frame_info = Runtime::Current()->GetRuntimeMethodFrameInfo(runtime_method);
+ return ShouldDeoptimizeCaller(self, sp, frame_info.FrameSizeInBytes());
+}
+
+bool Instrumentation::ShouldDeoptimizeCaller(Thread* self, ArtMethod** sp, size_t frame_size) {
+ uintptr_t caller_sp = reinterpret_cast<uintptr_t>(sp) + frame_size;
+ ArtMethod* caller = *(reinterpret_cast<ArtMethod**>(caller_sp));
+ uintptr_t caller_pc_addr = reinterpret_cast<uintptr_t>(sp) + (frame_size - sizeof(void*));
+ uintptr_t caller_pc = *reinterpret_cast<uintptr_t*>(caller_pc_addr);
+ return ShouldDeoptimizeCaller(self, caller, caller_pc, caller_sp);
+}
+
+
+bool Instrumentation::ShouldDeoptimizeCaller(Thread* self, const NthCallerVisitor& visitor) {
+ uintptr_t caller_sp = reinterpret_cast<uintptr_t>(visitor.GetCurrentQuickFrame());
+ // When the caller isn't executing quick code there is no need to deoptimize.
+ if (visitor.GetCurrentOatQuickMethodHeader() == nullptr) {
+ return false;
+ }
+ return ShouldDeoptimizeCaller(self, visitor.GetOuterMethod(), visitor.caller_pc, caller_sp);
+}
+
+bool Instrumentation::ShouldDeoptimizeCaller(Thread* self,
+ ArtMethod* caller,
+ uintptr_t caller_pc,
+ uintptr_t caller_sp) {
+ if (caller == nullptr ||
+ caller->IsNative() ||
+ caller->IsRuntimeMethod() ||
+ caller_pc == reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc())) {
+ // If caller_pc is QuickInstrumentationExit then deoptimization will be handled by the
+ // instrumentation exit trampoline so we don't need to handle deoptimizations here.
+ // We need to check for a deoptimization here because when a redefinition happens it is
+ // not safe to use any compiled code because the field offsets might change. For native
+ // methods, we don't embed any field offsets so no need to check for a deoptimization.
+ // If the caller is null we don't need to do anything. This can happen when the caller
+ // is being interpreted by the switch interpreter (when called from
+ // artQuickToInterpreterBridge) / during shutdown / early startup.
+ return false;
+ }
+
+ bool needs_deopt = NeedsSlowInterpreterForMethod(self, caller);
+
+ // Non java debuggable apps don't support redefinition and hence it isn't required to check if
+ // frame needs to be deoptimized. Even in debuggable apps, we only need this check when a
+ // redefinition has actually happened. This is indicated by IsDeoptCheckRequired flag. We also
+ // want to avoid getting method header when we need a deopt anyway.
+ if (Runtime::Current()->IsJavaDebuggable() && !needs_deopt && self->IsDeoptCheckRequired()) {
+ const OatQuickMethodHeader* header = caller->GetOatQuickMethodHeader(caller_pc);
+ if (header != nullptr && header->HasShouldDeoptimizeFlag()) {
+ DCHECK(header->IsOptimized());
+ uint8_t* should_deopt_flag_addr =
+ reinterpret_cast<uint8_t*>(caller_sp) + header->GetShouldDeoptimizeFlagOffset();
+ if ((*should_deopt_flag_addr &
+ static_cast<uint8_t>(DeoptimizeFlagValue::kForceDeoptForRedefinition)) != 0) {
+ needs_deopt = true;
+ }
+ }
+ }
+
+ if (needs_deopt) {
+ if (!Runtime::Current()->IsAsyncDeoptimizeable(caller, caller_pc)) {
+ LOG(WARNING) << "Got a deoptimization request on un-deoptimizable method "
+ << caller->PrettyMethod();
+ return false;
+ }
+ return true;
+ }
+
+ return false;
}
TwoWordReturn Instrumentation::PopInstrumentationStackFrame(Thread* self,
@@ -1653,19 +1678,19 @@
self->VerifyStack();
ArtMethod* method = instrumentation_frame.method_;
+ DCHECK(!method->IsRuntimeMethod());
bool is_ref;
- JValue return_value = GetReturnValue(self, method, &is_ref, gpr_result, fpr_result);
+ JValue return_value = GetReturnValue(method, &is_ref, gpr_result, fpr_result);
StackHandleScope<1> hs(self);
MutableHandle<mirror::Object> res(hs.NewHandle<mirror::Object>(nullptr));
if (is_ref) {
// Take a handle to the return value so we won't lose it if we suspend.
- // FIXME: The `is_ref` is often guessed wrong, so even object aligment
- // assertion would fail for some tests. See b/204766614 .
- // DCHECK_ALIGNED(return_value.GetL(), kObjectAlignment);
+ DCHECK_ALIGNED(return_value.GetL(), kObjectAlignment);
res.Assign(return_value.GetL());
}
- if (!method->IsRuntimeMethod() && !instrumentation_frame.interpreter_entry_) {
+ if (!instrumentation_frame.interpreter_entry_) {
+ DCHECK(!method->IsRuntimeMethod());
// Note that sending the event may change the contents of *return_pc_addr.
MethodExitEvent(self, instrumentation_frame.method_, OptionalFrame{}, return_value);
}
@@ -1677,57 +1702,61 @@
// Check if we forced all threads to deoptimize in the time between this frame being created and
// now.
bool should_deoptimize_frame = instrumentation_frame.force_deopt_id_ != current_force_deopt_id_;
- bool deoptimize = ShouldDeoptimizeMethod(self, visitor) || should_deoptimize_frame;
+ bool deoptimize = ShouldDeoptimizeCaller(self, visitor) || should_deoptimize_frame;
if (is_ref) {
// Restore the return value if it's a reference since it might have moved.
*reinterpret_cast<mirror::Object**>(gpr_result) = res.Get();
}
- if (deoptimize && Runtime::Current()->IsAsyncDeoptimizeable(*return_pc_addr)) {
- if (kVerboseInstrumentation) {
- LOG(INFO) << "Deoptimizing "
- << visitor.caller->PrettyMethod()
- << " by returning from "
- << method->PrettyMethod()
- << " with result "
- << std::hex << return_value.GetJ() << std::dec
- << " in "
- << *self;
+
+ if (deoptimize) {
+ // NthCallerVisitor also takes inlined frames into consideration, so visitor.caller points to
+ // the inlined function. We need the actual method corresponding to the return_pc_addr to check
+ // if the method is deoptimizeable. So fetch the outer method.
+ if (Runtime::Current()->IsAsyncDeoptimizeable(visitor.GetOuterMethod(), *return_pc_addr)) {
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << "Deoptimizing "
+ << visitor.caller->PrettyMethod()
+ << " by returning from "
+ << method->PrettyMethod()
+ << " with result "
+ << std::hex << return_value.GetJ() << std::dec
+ << " in "
+ << *self;
+ }
+ DeoptimizationMethodType deopt_method_type = GetDeoptimizationMethodType(method);
+ self->PushDeoptimizationContext(return_value,
+ is_ref,
+ /* exception= */ nullptr,
+ /* from_code= */ false,
+ deopt_method_type);
+ return GetTwoWordSuccessValue(
+ *return_pc_addr, reinterpret_cast<uintptr_t>(GetQuickDeoptimizationEntryPoint()));
+ } else {
+ VLOG(deopt) << "Got a deoptimization request on un-deoptimizable "
+ << visitor.caller->PrettyMethod() << " at PC "
+ << reinterpret_cast<void*>(*return_pc_addr);
}
- DeoptimizationMethodType deopt_method_type = GetDeoptimizationMethodType(method);
- self->PushDeoptimizationContext(return_value,
- is_ref,
- /* exception= */ nullptr,
- /* from_code= */ false,
- deopt_method_type);
- return GetTwoWordSuccessValue(*return_pc_addr,
- reinterpret_cast<uintptr_t>(GetQuickDeoptimizationEntryPoint()));
- } else {
- if (deoptimize && !Runtime::Current()->IsAsyncDeoptimizeable(*return_pc_addr)) {
- VLOG(deopt) << "Got a deoptimization request on un-deoptimizable " << method->PrettyMethod()
- << " at PC " << reinterpret_cast<void*>(*return_pc_addr);
- }
- if (kVerboseInstrumentation) {
- LOG(INFO) << "Returning from " << method->PrettyMethod()
- << " to PC " << reinterpret_cast<void*>(*return_pc_addr);
- }
- return GetTwoWordSuccessValue(0, *return_pc_addr);
}
+
+ if (kVerboseInstrumentation) {
+ LOG(INFO) << "Returning from " << method->PrettyMethod() << " to PC "
+ << reinterpret_cast<void*>(*return_pc_addr);
+ }
+ return GetTwoWordSuccessValue(0, *return_pc_addr);
}
-uintptr_t Instrumentation::PopFramesForDeoptimization(Thread* self, uintptr_t pop_until) const {
+uintptr_t Instrumentation::PopInstrumentationStackUntil(Thread* self, uintptr_t pop_until) const {
std::map<uintptr_t, instrumentation::InstrumentationStackFrame>* stack =
self->GetInstrumentationStack();
// Pop all instrumentation frames below `pop_until`.
uintptr_t return_pc = 0u;
for (auto i = stack->begin(); i != stack->end() && i->first <= pop_until;) {
- auto e = i;
- ++i;
if (kVerboseInstrumentation) {
- LOG(INFO) << "Popping for deoptimization " << e->second.method_->PrettyMethod();
+ LOG(INFO) << "Popping for deoptimization " << i->second.method_->PrettyMethod();
}
- return_pc = e->second.return_pc_;
- stack->erase(e);
+ return_pc = i->second.return_pc_;
+ i = stack->erase(i);
}
return return_pc;
}
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index c811935..aecad1f 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -23,6 +23,7 @@
#include <list>
#include <memory>
#include <optional>
+#include <queue>
#include <unordered_set>
#include "arch/instruction_set.h"
@@ -31,6 +32,7 @@
#include "base/macros.h"
#include "base/safe_map.h"
#include "gc_root.h"
+#include "jvalue.h"
#include "offsets.h"
namespace art {
@@ -45,6 +47,7 @@
template <typename T> class MutableHandle;
struct NthCallerVisitor;
union JValue;
+class OatQuickMethodHeader;
class SHARED_LOCKABLE ReaderWriterMutex;
class ShadowFrame;
class Thread;
@@ -92,7 +95,6 @@
// Call-back for when a method is popped due to an exception throw. A method will either cause a
// MethodExited call-back or a MethodUnwind call-back when its activation is removed.
virtual void MethodUnwind(Thread* thread,
- Handle<mirror::Object> this_object,
ArtMethod* method,
uint32_t dex_pc)
REQUIRES_SHARED(Locks::mutator_lock_) = 0;
@@ -199,7 +201,7 @@
Instrumentation();
- static constexpr MemberOffset NeedsEntryExitHooksOffset() {
+ static constexpr MemberOffset NeedsExitHooksOffset() {
// Assert that instrumentation_stubs_installed_ is 8bits wide. If the size changes
// update the compare instructions in the code generator when generating checks for
// MethodEntryExitHooks.
@@ -208,6 +210,24 @@
return MemberOffset(OFFSETOF_MEMBER(Instrumentation, instrumentation_stubs_installed_));
}
+ static constexpr MemberOffset HaveMethodEntryListenersOffset() {
+ // Assert that have_method_entry_listeners_ is 8bits wide. If the size changes
+ // update the compare instructions in the code generator when generating checks for
+ // MethodEntryExitHooks.
+ static_assert(sizeof(have_method_entry_listeners_) == 1,
+ "have_method_entry_listeners_ isn't expected size");
+ return MemberOffset(OFFSETOF_MEMBER(Instrumentation, have_method_entry_listeners_));
+ }
+
+ static constexpr MemberOffset HaveMethodExitListenersOffset() {
+ // Assert that have_method_exit_listeners_ is 8bits wide. If the size changes
+ // update the compare instructions in the code generator when generating checks for
+ // MethodEntryExitHooks.
+ static_assert(sizeof(have_method_exit_listeners_) == 1,
+ "have_method_exit_listeners_ isn't expected size");
+ return MemberOffset(OFFSETOF_MEMBER(Instrumentation, have_method_exit_listeners_));
+ }
+
// Add a listener to be notified of the masked together sent of instrumentation events. This
// suspend the runtime to install stubs. You are expected to hold the mutator lock as a proxy
// for saying you should have suspended all threads (installing stubs while threads are running
@@ -221,8 +241,7 @@
// Calls UndeoptimizeEverything which may visit class linker classes through ConfigureStubs.
void DisableDeoptimization(const char* key)
- REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
- REQUIRES(!GetDeoptimizedMethodsLock());
+ REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_);
bool AreAllMethodsDeoptimized() const {
return InterpreterStubsInstalled();
@@ -233,52 +252,44 @@
void DeoptimizeEverything(const char* key)
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
REQUIRES(!Locks::thread_list_lock_,
- !Locks::classlinker_classes_lock_,
- !GetDeoptimizedMethodsLock());
+ !Locks::classlinker_classes_lock_);
// Executes everything with compiled code (or interpreter if there is no code). May visit class
// linker classes through ConfigureStubs.
void UndeoptimizeEverything(const char* key)
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
REQUIRES(!Locks::thread_list_lock_,
- !Locks::classlinker_classes_lock_,
- !GetDeoptimizedMethodsLock());
+ !Locks::classlinker_classes_lock_);
// Deoptimize a method by forcing its execution with the interpreter. Nevertheless, a static
// method (except a class initializer) set to the resolution trampoline will be deoptimized only
// once its declaring class is initialized.
- void Deoptimize(ArtMethod* method)
- REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !GetDeoptimizedMethodsLock());
+ void Deoptimize(ArtMethod* method) REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_);
// Undeoptimze the method by restoring its entrypoints. Nevertheless, a static method
// (except a class initializer) set to the resolution trampoline will be updated only once its
// declaring class is initialized.
- void Undeoptimize(ArtMethod* method)
- REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !GetDeoptimizedMethodsLock());
+ void Undeoptimize(ArtMethod* method) REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_);
// Indicates whether the method has been deoptimized so it is executed with the interpreter.
- bool IsDeoptimized(ArtMethod* method)
- REQUIRES(!GetDeoptimizedMethodsLock()) REQUIRES_SHARED(Locks::mutator_lock_);
+ bool IsDeoptimized(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
// Indicates if any method needs to be deoptimized. This is used to avoid walking the stack to
// determine if a deoptimization is required.
- bool IsDeoptimizedMethodsEmpty() const
- REQUIRES(!GetDeoptimizedMethodsLock()) REQUIRES_SHARED(Locks::mutator_lock_);
+ bool IsDeoptimizedMethodsEmpty() const REQUIRES_SHARED(Locks::mutator_lock_);
// Enable method tracing by installing instrumentation entry/exit stubs or interpreter.
void EnableMethodTracing(const char* key,
bool needs_interpreter = kDeoptimizeForAccurateMethodEntryExitListeners)
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
REQUIRES(!Locks::thread_list_lock_,
- !Locks::classlinker_classes_lock_,
- !GetDeoptimizedMethodsLock());
+ !Locks::classlinker_classes_lock_);
// Disable method tracing by uninstalling instrumentation entry/exit stubs or interpreter.
void DisableMethodTracing(const char* key)
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
REQUIRES(!Locks::thread_list_lock_,
- !Locks::classlinker_classes_lock_,
- !GetDeoptimizedMethodsLock());
+ !Locks::classlinker_classes_lock_);
void InstrumentQuickAllocEntryPoints() REQUIRES(!Locks::instrument_entrypoints_lock_);
@@ -300,11 +311,11 @@
// Update the code of a method respecting any installed stubs.
void UpdateMethodsCode(ArtMethod* method, const void* new_code)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
+ REQUIRES_SHARED(Locks::mutator_lock_);
// Update the code of a native method to a JITed stub.
void UpdateNativeMethodsCodeToJitCode(ArtMethod* method, const void* new_code)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
+ REQUIRES_SHARED(Locks::mutator_lock_);
// Return the code that we can execute for an invoke including from the JIT.
const void* GetCodeForInvoke(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -383,6 +394,20 @@
return have_exception_handled_listeners_;
}
+ // Returns if dex pc events need to be reported for the specified method.
+ // These events are reported when DexPCListeners are installed and at least one of the
+ // following conditions hold:
+ // 1. The method is deoptimized. This is done when there is a breakpoint on method.
+ // 2. When the thread is deoptimized. This is used when single stepping a single thread.
+ // 3. When interpreter stubs are installed. In this case no additional information is maintained
+ // about which methods need dex pc move events. This is usually used for features which need
+ // them for several methods across threads or need expensive processing. So it is OK to not
+ // further optimize this case.
+ // DexPCListeners are installed when there is a breakpoint on any method / single stepping
+ // on any of thread. These are removed when the last breakpoint was removed. See AddListener and
+ // RemoveListener for more details.
+ bool NeedsDexPcEvents(ArtMethod* method, Thread* thread) REQUIRES_SHARED(Locks::mutator_lock_);
+
bool NeedsSlowInterpreterForListeners() const REQUIRES_SHARED(Locks::mutator_lock_) {
return have_field_read_listeners_ ||
have_field_write_listeners_ ||
@@ -413,7 +438,6 @@
// Inform listeners that a method has been exited due to an exception.
void MethodUnwindEvent(Thread* thread,
- ObjPtr<mirror::Object> this_object,
ArtMethod* method,
uint32_t dex_pc) const
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -479,12 +503,37 @@
void ExceptionHandledEvent(Thread* thread, ObjPtr<mirror::Throwable> exception_object) const
REQUIRES_SHARED(Locks::mutator_lock_);
- JValue GetReturnValue(Thread* self,
- ArtMethod* method,
- bool* is_ref,
- uint64_t* gpr_result,
- uint64_t* fpr_result) REQUIRES_SHARED(Locks::mutator_lock_);
- bool ShouldDeoptimizeMethod(Thread* self, const NthCallerVisitor& visitor)
+ JValue GetReturnValue(ArtMethod* method, bool* is_ref, uint64_t* gpr_result, uint64_t* fpr_result)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ bool PushDeoptContextIfNeeded(Thread* self,
+ DeoptimizationMethodType deopt_type,
+ bool is_ref,
+ const JValue& result) REQUIRES_SHARED(Locks::mutator_lock_);
+ void DeoptimizeIfNeeded(Thread* self,
+ ArtMethod** sp,
+ DeoptimizationMethodType type,
+ JValue result,
+ bool is_ref) REQUIRES_SHARED(Locks::mutator_lock_);
+ // TODO(mythria): Update uses of ShouldDeoptimizeCaller that takes a visitor by a method that
+ // doesn't need to walk the stack. This is used on method exits to check if the caller needs a
+ // deoptimization.
+ bool ShouldDeoptimizeCaller(Thread* self, const NthCallerVisitor& visitor)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // This returns if the caller of runtime method requires a deoptimization. This checks both if the
+ // method requires a deopt or if this particular frame needs a deopt because of a class
+ // redefinition.
+ bool ShouldDeoptimizeCaller(Thread* self, ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_);
+ bool ShouldDeoptimizeCaller(Thread* self, ArtMethod** sp, size_t frame_size)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // This is a helper function used by the two variants of ShouldDeoptimizeCaller.
+ // Remove this once ShouldDeoptimizeCaller is updated not to use NthCallerVisitor.
+ bool ShouldDeoptimizeCaller(Thread* self,
+ ArtMethod* caller,
+ uintptr_t caller_pc,
+ uintptr_t caller_sp) REQUIRES_SHARED(Locks::mutator_lock_);
+ // This returns if the specified method requires a deoptimization. This doesn't account if a stack
+ // frame involving this method requires a deoptimization.
+ bool NeedsSlowInterpreterForMethod(Thread* self, ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_);
// Called when an instrumented method is entered. The intended link register (lr) is saved so
@@ -510,19 +559,19 @@
uintptr_t* return_pc_addr,
uint64_t* gpr_result,
uint64_t* fpr_result)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
+ REQUIRES_SHARED(Locks::mutator_lock_);
- // Pops nframes instrumentation frames from the current thread. Returns the return pc for the last
- // instrumentation frame that's popped.
- uintptr_t PopFramesForDeoptimization(Thread* self, uintptr_t stack_pointer) const
+ // Pops instrumentation frames until the specified stack_pointer from the current thread. Returns
+ // the return pc for the last instrumentation frame that's popped.
+ uintptr_t PopInstrumentationStackUntil(Thread* self, uintptr_t stack_pointer) const
REQUIRES_SHARED(Locks::mutator_lock_);
// Call back for configure stubs.
- void InstallStubsForClass(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!GetDeoptimizedMethodsLock());
+ void InstallStubsForClass(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);
- void InstallStubsForMethod(ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
+ void InstallStubsForMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void UpdateEntrypointsForDebuggable() REQUIRES(art::Locks::mutator_lock_);
// Install instrumentation exit stub on every method of the stack of the given thread.
// This is used by:
@@ -548,18 +597,21 @@
return alloc_entrypoints_instrumented_;
}
+ bool ProcessMethodUnwindCallbacks(Thread* self,
+ std::queue<ArtMethod*>& methods,
+ MutableHandle<mirror::Throwable>& exception)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
InstrumentationLevel GetCurrentInstrumentationLevel() const;
+ bool MethodSupportsExitEvents(ArtMethod* method, const OatQuickMethodHeader* header)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
// Returns true if moving to the given instrumentation level requires the installation of stubs.
// False otherwise.
bool RequiresInstrumentationInstallation(InstrumentationLevel new_level) const;
- // Returns true if we need entry exit stub to call entry hooks. JITed code
- // directly call entry / exit hooks and don't need the stub.
- static bool CodeNeedsEntryExitStub(const void* code, ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
// Update the current instrumentation_level_.
void UpdateInstrumentationLevel(InstrumentationLevel level);
@@ -570,12 +622,10 @@
// becomes the highest instrumentation level required by a client.
void ConfigureStubs(const char* key, InstrumentationLevel desired_instrumentation_level)
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
- REQUIRES(!GetDeoptimizedMethodsLock(),
- !Locks::thread_list_lock_,
+ REQUIRES(!Locks::thread_list_lock_,
!Locks::classlinker_classes_lock_);
void UpdateStubs() REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
- REQUIRES(!GetDeoptimizedMethodsLock(),
- !Locks::thread_list_lock_,
+ REQUIRES(!Locks::thread_list_lock_,
!Locks::classlinker_classes_lock_);
// If there are no pending deoptimizations restores the stack to the normal state by updating the
@@ -619,22 +669,11 @@
REQUIRES_SHARED(Locks::mutator_lock_);
// Read barrier-aware utility functions for accessing deoptimized_methods_
- bool AddDeoptimizedMethod(ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(GetDeoptimizedMethodsLock());
- bool IsDeoptimizedMethod(ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_, GetDeoptimizedMethodsLock());
- bool RemoveDeoptimizedMethod(ArtMethod* method)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(GetDeoptimizedMethodsLock());
- ArtMethod* BeginDeoptimizedMethod()
- REQUIRES_SHARED(Locks::mutator_lock_, GetDeoptimizedMethodsLock());
- bool IsDeoptimizedMethodsEmptyLocked() const
- REQUIRES_SHARED(Locks::mutator_lock_, GetDeoptimizedMethodsLock());
+ bool AddDeoptimizedMethod(ArtMethod* method) REQUIRES(Locks::mutator_lock_);
+ bool IsDeoptimizedMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
+ bool RemoveDeoptimizedMethod(ArtMethod* method) REQUIRES(Locks::mutator_lock_);
void UpdateMethodsCodeImpl(ArtMethod* method, const void* new_code)
- REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
-
- ReaderWriterMutex* GetDeoptimizedMethodsLock() const {
- return deoptimized_methods_lock_.get();
- }
+ REQUIRES_SHARED(Locks::mutator_lock_);
// A counter that's incremented every time a DeoptimizeAllFrames. We check each
// InstrumentationStackFrames creation id against this number and if they differ we deopt even if
@@ -718,8 +757,7 @@
// The set of methods being deoptimized (by the debugger) which must be executed with interpreter
// only.
- mutable std::unique_ptr<ReaderWriterMutex> deoptimized_methods_lock_ BOTTOM_MUTEX_ACQUIRED_AFTER;
- std::unordered_set<ArtMethod*> deoptimized_methods_ GUARDED_BY(GetDeoptimizedMethodsLock());
+ std::unordered_set<ArtMethod*> deoptimized_methods_ GUARDED_BY(Locks::mutator_lock_);
// Current interpreter handler table. This is updated each time the thread state flags are
// modified.
diff --git a/runtime/instrumentation_test.cc b/runtime/instrumentation_test.cc
index b0a81b6..260ccb6 100644
--- a/runtime/instrumentation_test.cc
+++ b/runtime/instrumentation_test.cc
@@ -77,7 +77,6 @@
}
void MethodUnwind(Thread* thread ATTRIBUTE_UNUSED,
- Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
ArtMethod* method ATTRIBUTE_UNUSED,
uint32_t dex_pc ATTRIBUTE_UNUSED)
override REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -184,6 +183,10 @@
static constexpr const char* kClientOneKey = "TestClient1";
static constexpr const char* kClientTwoKey = "TestClient2";
+ InstrumentationTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+
void CheckConfigureStubs(const char* key, Instrumentation::InstrumentationLevel level) {
ScopedObjectAccess soa(Thread::Current());
instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
@@ -223,7 +226,7 @@
mirror::Object* const event_obj = nullptr;
const uint32_t event_dex_pc = 0;
- ShadowFrameAllocaUniquePtr test_frame = CREATE_SHADOW_FRAME(0, nullptr, event_method, 0);
+ ShadowFrameAllocaUniquePtr test_frame = CREATE_SHADOW_FRAME(0, event_method, 0);
// Check the listener is registered and is notified of the event.
EXPECT_TRUE(HasEventListener(instr, instrumentation_event));
@@ -387,7 +390,7 @@
break;
}
case instrumentation::Instrumentation::kMethodUnwind:
- instr->MethodUnwindEvent(self, obj, method, dex_pc);
+ instr->MethodUnwindEvent(self, method, dex_pc);
break;
case instrumentation::Instrumentation::kDexPcMoved:
instr->DexPcMovedEvent(self, obj, method, dex_pc);
diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc
index f587d01..10b2d65 100644
--- a/runtime/intern_table.cc
+++ b/runtime/intern_table.cc
@@ -190,8 +190,8 @@
{
ScopedThreadSuspension sts(self, ThreadState::kWaitingWeakGcRootRead);
MutexLock mu(self, *Locks::intern_table_lock_);
- while ((!kUseReadBarrier && weak_root_state_ == gc::kWeakRootStateNoReadsOrWrites) ||
- (kUseReadBarrier && !self->GetWeakRefAccessEnabled())) {
+ while ((!gUseReadBarrier && weak_root_state_ == gc::kWeakRootStateNoReadsOrWrites) ||
+ (gUseReadBarrier && !self->GetWeakRefAccessEnabled())) {
weak_intern_condition_.Wait(self);
}
}
@@ -218,7 +218,7 @@
if (strong != nullptr) {
return strong;
}
- if (kUseReadBarrier ? self->GetWeakRefAccessEnabled()
+ if (gUseReadBarrier ? self->GetWeakRefAccessEnabled()
: weak_root_state_ != gc::kWeakRootStateNoReadsOrWrites) {
break;
}
@@ -230,7 +230,7 @@
auto h = hs.NewHandleWrapper(&s);
WaitUntilAccessible(self);
}
- if (!kUseReadBarrier) {
+ if (!gUseReadBarrier) {
CHECK_EQ(weak_root_state_, gc::kWeakRootStateNormal);
} else {
CHECK(self->GetWeakRefAccessEnabled());
@@ -405,7 +405,10 @@
if (new_object == nullptr) {
it = set->erase(it);
} else {
- *it = GcRoot<mirror::String>(new_object->AsString());
+ // Don't use AsString as it does IsString check in debug builds which, in
+ // case of userfaultfd GC, is called when the object's content isn't
+ // thereyet.
+ *it = GcRoot<mirror::String>(ObjPtr<mirror::String>::DownCast(new_object));
++it;
}
}
@@ -426,7 +429,7 @@
}
void InternTable::ChangeWeakRootStateLocked(gc::WeakRootState new_state) {
- CHECK(!kUseReadBarrier);
+ CHECK(!gUseReadBarrier);
weak_root_state_ = new_state;
if (new_state != gc::kWeakRootStateNoReadsOrWrites) {
weak_intern_condition_.Broadcast(Thread::Current());
diff --git a/runtime/intern_table_test.cc b/runtime/intern_table_test.cc
index 9900c2a..d77ffcb 100644
--- a/runtime/intern_table_test.cc
+++ b/runtime/intern_table_test.cc
@@ -27,7 +27,12 @@
namespace art {
-class InternTableTest : public CommonRuntimeTest {};
+class InternTableTest : public CommonRuntimeTest {
+ protected:
+ InternTableTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+};
TEST_F(InternTableTest, Intern) {
ScopedObjectAccess soa(Thread::Current());
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 38c94ab..42596d8 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -255,6 +255,7 @@
}
}
+NO_STACK_PROTECTOR
static inline JValue Execute(
Thread* self,
const CodeItemDataAccessor& accessor,
@@ -265,41 +266,22 @@
DCHECK(!shadow_frame.GetMethod()->IsAbstract());
DCHECK(!shadow_frame.GetMethod()->IsNative());
+ // We cache the result of NeedsDexPcEvents in the shadow frame so we don't need to call
+ // NeedsDexPcEvents on every instruction for better performance. NeedsDexPcEvents only gets
+ // updated asynchronoulsy in a SuspendAll scope and any existing shadow frames are updated with
+ // new value. So it is safe to cache it here.
+ shadow_frame.SetNotifyDexPcMoveEvents(
+ Runtime::Current()->GetInstrumentation()->NeedsDexPcEvents(shadow_frame.GetMethod(), self));
+
if (LIKELY(!from_deoptimize)) { // Entering the method, but not via deoptimization.
if (kIsDebugBuild) {
CHECK_EQ(shadow_frame.GetDexPC(), 0u);
self->AssertNoPendingException();
}
- instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
ArtMethod *method = shadow_frame.GetMethod();
- if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
- instrumentation->MethodEnterEvent(self, method);
- if (UNLIKELY(shadow_frame.GetForcePopFrame())) {
- // The caller will retry this invoke or ignore the result. Just return immediately without
- // any value.
- DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
- JValue ret = JValue();
- PerformNonStandardReturn<MonitorState::kNoMonitorsLocked>(
- self, shadow_frame, ret, instrumentation, accessor.InsSize());
- return ret;
- }
- if (UNLIKELY(self->IsExceptionPending())) {
- instrumentation->MethodUnwindEvent(self,
- shadow_frame.GetThisObject(accessor.InsSize()),
- method,
- 0);
- JValue ret = JValue();
- if (UNLIKELY(shadow_frame.GetForcePopFrame())) {
- DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
- PerformNonStandardReturn<MonitorState::kNoMonitorsLocked>(
- self, shadow_frame, ret, instrumentation, accessor.InsSize());
- }
- return ret;
- }
- }
-
- if (!stay_in_interpreter && !self->IsForceInterpreter()) {
+ // If we can continue in JIT and have JITed code available execute JITed code.
+ if (!stay_in_interpreter && !self->IsForceInterpreter() && !shadow_frame.GetForcePopFrame()) {
jit::Jit* jit = Runtime::Current()->GetJit();
if (jit != nullptr) {
jit->MethodEntered(self, shadow_frame.GetMethod());
@@ -320,6 +302,32 @@
}
}
}
+
+ instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+ if (UNLIKELY(instrumentation->HasMethodEntryListeners() || shadow_frame.GetForcePopFrame())) {
+ instrumentation->MethodEnterEvent(self, method);
+ if (UNLIKELY(shadow_frame.GetForcePopFrame())) {
+ // The caller will retry this invoke or ignore the result. Just return immediately without
+ // any value.
+ DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
+ JValue ret = JValue();
+ PerformNonStandardReturn<MonitorState::kNoMonitorsLocked>(
+ self, shadow_frame, ret, instrumentation, accessor.InsSize());
+ return ret;
+ }
+ if (UNLIKELY(self->IsExceptionPending())) {
+ instrumentation->MethodUnwindEvent(self,
+ method,
+ 0);
+ JValue ret = JValue();
+ if (UNLIKELY(shadow_frame.GetForcePopFrame())) {
+ DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
+ PerformNonStandardReturn<MonitorState::kNoMonitorsLocked>(
+ self, shadow_frame, ret, instrumentation, accessor.InsSize());
+ }
+ return ret;
+ }
+ }
}
ArtMethod* method = shadow_frame.GetMethod();
@@ -366,7 +374,7 @@
num_ins = accessor.InsSize();
} else if (!method->IsInvokable()) {
self->EndAssertNoThreadSuspension(old_cause);
- method->ThrowInvocationTimeError();
+ method->ThrowInvocationTimeError(receiver);
return;
} else {
DCHECK(method->IsNative()) << method->PrettyMethod();
@@ -377,11 +385,9 @@
}
}
// Set up shadow frame with matching number of reference slots to vregs.
- ShadowFrame* last_shadow_frame = self->GetManagedStack()->GetTopShadowFrame();
ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
- CREATE_SHADOW_FRAME(num_regs, last_shadow_frame, method, /* dex pc */ 0);
+ CREATE_SHADOW_FRAME(num_regs, method, /* dex pc */ 0);
ShadowFrame* shadow_frame = shadow_frame_unique_ptr.get();
- self->PushShadowFrame(shadow_frame);
size_t cur_reg = num_regs - num_ins;
if (!method->IsStatic()) {
@@ -413,21 +419,10 @@
}
}
self->EndAssertNoThreadSuspension(old_cause);
- // Do this after populating the shadow frame in case EnsureInitialized causes a GC.
- if (method->IsStatic()) {
- ObjPtr<mirror::Class> declaring_class = method->GetDeclaringClass();
- if (UNLIKELY(!declaring_class->IsVisiblyInitialized())) {
- StackHandleScope<1> hs(self);
- Handle<mirror::Class> h_class(hs.NewHandle(declaring_class));
- if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(
- self, h_class, /*can_init_fields=*/ true, /*can_init_parents=*/ true))) {
- CHECK(self->IsExceptionPending());
- self->PopShadowFrame();
- return;
- }
- DCHECK(h_class->IsInitializing());
- }
+ if (!EnsureInitialized(self, shadow_frame)) {
+ return;
}
+ self->PushShadowFrame(shadow_frame);
if (LIKELY(!method->IsNative())) {
JValue r = Execute(self, accessor, *shadow_frame, JValue(), stay_in_interpreter);
if (result != nullptr) {
@@ -476,6 +471,7 @@
const uint32_t dex_pc = shadow_frame->GetDexPC();
uint32_t new_dex_pc = dex_pc;
if (UNLIKELY(self->IsExceptionPending())) {
+ DCHECK(self->GetException() != Thread::GetDeoptimizationException());
// If we deoptimize from the QuickExceptionHandler, we already reported the exception throw
// event to the instrumentation. Skip throw listeners for the first frame. The deopt check
// should happen after the throw listener is called as throw listener can trigger a
@@ -514,7 +510,7 @@
new_dex_pc = dex_pc + instr->SizeInCodeUnits();
} else if (instr->IsInvoke()) {
DCHECK(deopt_method_type == DeoptimizationMethodType::kDefault);
- if (IsStringInit(instr, shadow_frame->GetMethod())) {
+ if (IsStringInit(*instr, shadow_frame->GetMethod())) {
uint16_t this_obj_vreg = GetReceiverRegisterForStringInit(instr);
// Move the StringFactory.newStringFromChars() result into the register representing
// "this object" when invoking the string constructor in the original dex instruction.
@@ -569,6 +565,7 @@
ret_val->SetJ(value.GetJ());
}
+NO_STACK_PROTECTOR
JValue EnterInterpreterFromEntryPoint(Thread* self, const CodeItemDataAccessor& accessor,
ShadowFrame* shadow_frame) {
DCHECK_EQ(self, Thread::Current());
@@ -585,6 +582,7 @@
return Execute(self, accessor, *shadow_frame, JValue());
}
+NO_STACK_PROTECTOR
void ArtInterpreterToInterpreterBridge(Thread* self,
const CodeItemDataAccessor& accessor,
ShadowFrame* shadow_frame,
@@ -596,23 +594,6 @@
}
self->PushShadowFrame(shadow_frame);
- ArtMethod* method = shadow_frame->GetMethod();
- // Ensure static methods are initialized.
- const bool is_static = method->IsStatic();
- if (is_static) {
- ObjPtr<mirror::Class> declaring_class = method->GetDeclaringClass();
- if (UNLIKELY(!declaring_class->IsVisiblyInitialized())) {
- StackHandleScope<1> hs(self);
- Handle<mirror::Class> h_class(hs.NewHandle(declaring_class));
- if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(
- self, h_class, /*can_init_fields=*/ true, /*can_init_parents=*/ true))) {
- DCHECK(self->IsExceptionPending());
- self->PopShadowFrame();
- return;
- }
- DCHECK(h_class->IsInitializing());
- }
- }
if (LIKELY(!shadow_frame->GetMethod()->IsNative())) {
result->SetJ(Execute(self, accessor, *shadow_frame, JValue()).GetJ());
@@ -620,6 +601,7 @@
// We don't expect to be asked to interpret native code (which is entered via a JNI compiler
// generated stub) except during testing and image writing.
CHECK(!Runtime::Current()->IsStarted());
+ bool is_static = shadow_frame->GetMethod()->IsStatic();
ObjPtr<mirror::Object> receiver = is_static ? nullptr : shadow_frame->GetVRegReference(0);
uint32_t* args = shadow_frame->GetVRegArgs(is_static ? 0 : 1);
UnstartedRuntime::Jni(self, shadow_frame->GetMethod(), receiver.Ptr(), args, result);
diff --git a/runtime/interpreter/interpreter_cache-inl.h b/runtime/interpreter/interpreter_cache-inl.h
index cea8157..804d382 100644
--- a/runtime/interpreter/interpreter_cache-inl.h
+++ b/runtime/interpreter/interpreter_cache-inl.h
@@ -35,13 +35,9 @@
inline void InterpreterCache::Set(Thread* self, const void* key, size_t value) {
DCHECK(self->GetInterpreterCache() == this) << "Must be called from owning thread";
-
- // For simplicity, only update the cache if weak ref accesses are enabled. If
- // they are disabled, this means the GC is processing the cache, and is
- // reading it concurrently.
- if (kUseReadBarrier && self->GetWeakRefAccessEnabled()) {
- data_[IndexOf(key)] = Entry{key, value};
- }
+ // Simple store works here as the cache is always read/written by the owning
+ // thread only (or in a stop-the-world pause).
+ data_[IndexOf(key)] = Entry{key, value};
}
} // namespace art
diff --git a/runtime/interpreter/interpreter_cache.h b/runtime/interpreter/interpreter_cache.h
index c57d023..8714bc6 100644
--- a/runtime/interpreter/interpreter_cache.h
+++ b/runtime/interpreter/interpreter_cache.h
@@ -47,7 +47,7 @@
class ALIGNED(16) InterpreterCache {
public:
// Aligned since we load the whole entry in single assembly instruction.
- typedef std::pair<const void*, size_t> Entry ALIGNED(2 * sizeof(size_t));
+ using Entry ALIGNED(2 * sizeof(size_t)) = std::pair<const void*, size_t>;
// 2x size increase/decrease corresponds to ~0.5% interpreter performance change.
// Value of 256 has around 75% cache hit rate.
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index c8a87c1..404eb69 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -185,7 +185,6 @@
// Exception is not caught by the current method. We will unwind to the
// caller. Notify any instrumentation listener.
instrumentation->MethodUnwindEvent(self,
- shadow_frame.GetThisObject(),
shadow_frame.GetMethod(),
shadow_frame.GetDexPC());
}
@@ -243,7 +242,8 @@
JValue* result,
uint16_t number_of_inputs,
uint32_t (&arg)[Instruction::kMaxVarArgRegs],
- uint32_t vregC) REQUIRES_SHARED(Locks::mutator_lock_);
+ uint32_t vregC,
+ bool string_init) REQUIRES_SHARED(Locks::mutator_lock_);
template <bool is_range>
ALWAYS_INLINE void CopyRegisters(ShadowFrame& caller_frame,
@@ -255,6 +255,7 @@
// END DECLARATIONS.
+NO_STACK_PROTECTOR
void ArtInterpreterToCompiledCodeBridge(Thread* self,
ArtMethod* caller,
ShadowFrame* shadow_frame,
@@ -262,25 +263,6 @@
JValue* result)
REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* method = shadow_frame->GetMethod();
- // Ensure static methods are initialized.
- if (method->IsStatic()) {
- ObjPtr<mirror::Class> declaringClass = method->GetDeclaringClass();
- if (UNLIKELY(!declaringClass->IsVisiblyInitialized())) {
- self->PushShadowFrame(shadow_frame);
- StackHandleScope<1> hs(self);
- Handle<mirror::Class> h_class(hs.NewHandle(declaringClass));
- if (UNLIKELY(!Runtime::Current()->GetClassLinker()->EnsureInitialized(
- self, h_class, /*can_init_fields=*/ true, /*can_init_parents=*/ true))) {
- self->PopShadowFrame();
- DCHECK(self->IsExceptionPending());
- return;
- }
- self->PopShadowFrame();
- DCHECK(h_class->IsInitializing());
- // Reload from shadow frame in case the method moved, this is faster than adding a handle.
- method = shadow_frame->GetMethod();
- }
- }
// Basic checks for the arg_offset. If there's no code item, the arg_offset must be 0. Otherwise,
// check that the arg_offset isn't greater than the number of registers. A stronger check is
// difficult since the frame may contain space for all the registers in the method, or only enough
@@ -1018,11 +1000,9 @@
// Set-up a shadow frame for invoking the bootstrap method handle.
ShadowFrameAllocaUniquePtr bootstrap_frame =
CREATE_SHADOW_FRAME(call_site_type->NumberOfVRegs(),
- nullptr,
referrer,
shadow_frame.GetDexPC());
- ScopedStackedShadowFramePusher pusher(
- self, bootstrap_frame.get(), StackedShadowFrameType::kShadowFrameUnderConstruction);
+ ScopedStackedShadowFramePusher pusher(self, bootstrap_frame.get());
ShadowFrameSetter setter(bootstrap_frame.get(), 0u);
// The first parameter is a MethodHandles lookup instance.
@@ -1213,15 +1193,8 @@
JValue* result,
uint16_t number_of_inputs,
uint32_t (&arg)[Instruction::kMaxVarArgRegs],
- uint32_t vregC) {
- bool string_init = false;
- // Replace calls to String.<init> with equivalent StringFactory call.
- if (UNLIKELY(called_method->GetDeclaringClass()->IsStringClass()
- && called_method->IsConstructor())) {
- called_method = WellKnownClasses::StringInitToStringFactory(called_method);
- string_init = true;
- }
-
+ uint32_t vregC,
+ bool string_init) {
// Compute method information.
CodeItemDataAccessor accessor(called_method->DexInstructionData());
// Number of registers for the callee's call frame.
@@ -1288,7 +1261,7 @@
// Allocate shadow frame on the stack.
const char* old_cause = self->StartAssertNoThreadSuspension("DoCallCommon");
ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
- CREATE_SHADOW_FRAME(num_regs, &shadow_frame, called_method, /* dex pc */ 0);
+ CREATE_SHADOW_FRAME(num_regs, called_method, /* dex pc */ 0);
ShadowFrame* new_shadow_frame = shadow_frame_unique_ptr.get();
// Initialize new shadow frame by copying the registers from the callee shadow frame.
@@ -1296,8 +1269,7 @@
// Slow path.
// We might need to do class loading, which incurs a thread state change to kNative. So
// register the shadow frame as under construction and allow suspension again.
- ScopedStackedShadowFramePusher pusher(
- self, new_shadow_frame, StackedShadowFrameType::kShadowFrameUnderConstruction);
+ ScopedStackedShadowFramePusher pusher(self, new_shadow_frame);
self->EndAssertNoThreadSuspension(old_cause);
// ArtMethod here is needed to check type information of the call site against the callee.
@@ -1411,8 +1383,14 @@
}
template<bool is_range, bool do_assignability_check>
-bool DoCall(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst, uint16_t inst_data, JValue* result) {
+NO_STACK_PROTECTOR
+bool DoCall(ArtMethod* called_method,
+ Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ bool is_string_init,
+ JValue* result) {
// Argument word count.
const uint16_t number_of_inputs =
(is_range) ? inst->VRegA_3rc(inst_data) : inst->VRegA_35c(inst_data);
@@ -1429,8 +1407,14 @@
}
return DoCallCommon<is_range, do_assignability_check>(
- called_method, self, shadow_frame,
- result, number_of_inputs, arg, vregC);
+ called_method,
+ self,
+ shadow_frame,
+ result,
+ number_of_inputs,
+ arg,
+ vregC,
+ is_string_init);
}
template <bool is_range, bool do_access_check, bool transaction_active>
@@ -1557,9 +1541,12 @@
// Explicit DoCall template function declarations.
#define EXPLICIT_DO_CALL_TEMPLATE_DECL(_is_range, _do_assignability_check) \
template REQUIRES_SHARED(Locks::mutator_lock_) \
- bool DoCall<_is_range, _do_assignability_check>(ArtMethod* method, Thread* self, \
+ bool DoCall<_is_range, _do_assignability_check>(ArtMethod* method, \
+ Thread* self, \
ShadowFrame& shadow_frame, \
- const Instruction* inst, uint16_t inst_data, \
+ const Instruction* inst, \
+ uint16_t inst_data, \
+ bool string_init, \
JValue* result)
EXPLICIT_DO_CALL_TEMPLATE_DECL(false, false);
EXPLICIT_DO_CALL_TEMPLATE_DECL(false, true);
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 0b91120..64d4d71 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -20,7 +20,6 @@
#include "android-base/macros.h"
#include "instrumentation.h"
#include "interpreter.h"
-#include "interpreter_intrinsics.h"
#include "transaction.h"
#include <math.h>
@@ -126,8 +125,13 @@
// DoFastInvoke and DoInvokeVirtualQuick functions.
// Returns true on success, otherwise throws an exception and returns false.
template<bool is_range, bool do_assignability_check>
-bool DoCall(ArtMethod* called_method, Thread* self, ShadowFrame& shadow_frame,
- const Instruction* inst, uint16_t inst_data, JValue* result);
+bool DoCall(ArtMethod* called_method,
+ Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data,
+ bool string_init,
+ JValue* result);
// Called by the switch interpreter to know if we can stay in it.
bool ShouldStayInSwitchInterpreter(ArtMethod* method)
@@ -220,7 +224,7 @@
// Handles all invoke-XXX/range instructions except for invoke-polymorphic[/range].
// Returns true on success, otherwise throws an exception and returns false.
-template<InvokeType type, bool is_range, bool do_access_check, bool is_mterp>
+template<InvokeType type, bool is_range, bool do_access_check>
static ALWAYS_INLINE bool DoInvoke(Thread* self,
ShadowFrame& shadow_frame,
const Instruction* inst,
@@ -231,63 +235,19 @@
if (UNLIKELY(self->ObserveAsyncException())) {
return false;
}
- const uint32_t method_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c();
- const uint32_t vregC = (is_range) ? inst->VRegC_3rc() : inst->VRegC_35c();
+ const uint32_t vregC = is_range ? inst->VRegC_3rc() : inst->VRegC_35c();
+ ObjPtr<mirror::Object> obj = type == kStatic ? nullptr : shadow_frame.GetVRegReference(vregC);
ArtMethod* sf_method = shadow_frame.GetMethod();
-
- // Try to find the method in small thread-local cache first (only used when
- // nterp is not used as mterp and nterp use the cache in an incompatible way).
- InterpreterCache* tls_cache = self->GetInterpreterCache();
- size_t tls_value;
- ArtMethod* resolved_method;
- if (!IsNterpSupported() && LIKELY(tls_cache->Get(self, inst, &tls_value))) {
- resolved_method = reinterpret_cast<ArtMethod*>(tls_value);
- } else {
- ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
- constexpr ClassLinker::ResolveMode resolve_mode =
- do_access_check ? ClassLinker::ResolveMode::kCheckICCEAndIAE
- : ClassLinker::ResolveMode::kNoChecks;
- resolved_method = class_linker->ResolveMethod<resolve_mode>(self, method_idx, sf_method, type);
- if (UNLIKELY(resolved_method == nullptr)) {
- CHECK(self->IsExceptionPending());
- result->SetJ(0);
- return false;
- }
- if (!IsNterpSupported()) {
- tls_cache->Set(self, inst, reinterpret_cast<size_t>(resolved_method));
- }
- }
-
- // Null pointer check and virtual method resolution.
- ObjPtr<mirror::Object> receiver =
- (type == kStatic) ? nullptr : shadow_frame.GetVRegReference(vregC);
- ArtMethod* called_method;
- called_method = FindMethodToCall<type, do_access_check>(
- method_idx, resolved_method, &receiver, sf_method, self);
- if (UNLIKELY(called_method == nullptr)) {
- CHECK(self->IsExceptionPending());
- result->SetJ(0);
- return false;
- }
- if (UNLIKELY(!called_method->IsInvokable())) {
- called_method->ThrowInvocationTimeError();
+ bool string_init = false;
+ ArtMethod* called_method = FindMethodToCall<type>(self, sf_method, &obj, *inst, &string_init);
+ if (called_method == nullptr) {
+ DCHECK(self->IsExceptionPending());
result->SetJ(0);
return false;
}
- jit::Jit* jit = Runtime::Current()->GetJit();
- if (is_mterp && !is_range && called_method->IsIntrinsic()) {
- if (MterpHandleIntrinsic(&shadow_frame, called_method, inst, inst_data,
- shadow_frame.GetResultRegister())) {
- if (jit != nullptr && sf_method != nullptr) {
- jit->NotifyInterpreterToCompiledCodeTransition(self, sf_method);
- }
- return !self->IsExceptionPending();
- }
- }
-
- return DoCall<is_range, do_access_check>(called_method, self, shadow_frame, inst, inst_data,
- result);
+ return DoCall<is_range, do_access_check>(
+ called_method, self, shadow_frame, inst, inst_data, string_init, result);
}
static inline ObjPtr<mirror::MethodHandle> ResolveMethodHandle(Thread* self,
@@ -386,24 +346,78 @@
return field_value;
}
+extern "C" size_t NterpGetStaticField(Thread* self,
+ ArtMethod* caller,
+ const uint16_t* dex_pc_ptr,
+ size_t resolve_field_type);
+
+extern "C" uint32_t NterpGetInstanceFieldOffset(Thread* self,
+ ArtMethod* caller,
+ const uint16_t* dex_pc_ptr,
+ size_t resolve_field_type);
+
+static inline void GetFieldInfo(Thread* self,
+ ArtMethod* caller,
+ const uint16_t* dex_pc_ptr,
+ bool is_static,
+ bool resolve_field_type,
+ ArtField** field,
+ bool* is_volatile,
+ MemberOffset* offset) {
+ size_t tls_value = 0u;
+ if (!self->GetInterpreterCache()->Get(self, dex_pc_ptr, &tls_value)) {
+ if (is_static) {
+ tls_value = NterpGetStaticField(self, caller, dex_pc_ptr, resolve_field_type);
+ } else {
+ tls_value = NterpGetInstanceFieldOffset(self, caller, dex_pc_ptr, resolve_field_type);
+ }
+
+ if (self->IsExceptionPending()) {
+ return;
+ }
+ }
+
+ if (is_static) {
+ DCHECK_NE(tls_value, 0u);
+ *is_volatile = ((tls_value & 1) != 0);
+ *field = reinterpret_cast<ArtField*>(tls_value & ~static_cast<size_t>(1u));
+ *offset = (*field)->GetOffset();
+ } else {
+ *is_volatile = (static_cast<int32_t>(tls_value) < 0);
+ *offset = MemberOffset(std::abs(static_cast<int32_t>(tls_value)));
+ }
+}
+
// Handles iget-XXX and sget-XXX instructions.
// Returns true on success, otherwise throws an exception and returns false.
-template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check,
+template<FindFieldType find_type,
+ Primitive::Type field_type,
+ bool do_access_check,
bool transaction_active = false>
-ALWAYS_INLINE bool DoFieldGet(Thread* self, ShadowFrame& shadow_frame, const Instruction* inst,
+ALWAYS_INLINE bool DoFieldGet(Thread* self,
+ ShadowFrame& shadow_frame,
+ const Instruction* inst,
uint16_t inst_data) REQUIRES_SHARED(Locks::mutator_lock_) {
const bool is_static = (find_type == StaticObjectRead) || (find_type == StaticPrimitiveRead);
- const uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
- ArtMethod* method = shadow_frame.GetMethod();
- ArtField* f = FindFieldFromCode<find_type, do_access_check>(
- field_idx, method, self, Primitive::ComponentSize(field_type));
- if (UNLIKELY(f == nullptr)) {
- CHECK(self->IsExceptionPending());
+ bool should_report = Runtime::Current()->GetInstrumentation()->HasFieldReadListeners();
+ ArtField* field = nullptr;
+ MemberOffset offset(0u);
+ bool is_volatile;
+ GetFieldInfo(self,
+ shadow_frame.GetMethod(),
+ reinterpret_cast<const uint16_t*>(inst),
+ is_static,
+ /*resolve_field_type=*/ false,
+ &field,
+ &is_volatile,
+ &offset);
+ if (self->IsExceptionPending()) {
return false;
}
+
ObjPtr<mirror::Object> obj;
if (is_static) {
- obj = f->GetDeclaringClass();
+ obj = field->GetDeclaringClass();
if (transaction_active) {
if (Runtime::Current()->GetTransaction()->ReadConstraint(obj)) {
Runtime::Current()->AbortTransactionAndThrowAbortError(self, "Can't read static fields of "
@@ -413,40 +427,57 @@
}
} else {
obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
- if (UNLIKELY(obj == nullptr)) {
- ThrowNullPointerExceptionForFieldAccess(f, method, true);
+ if (should_report || obj == nullptr) {
+ field = ResolveFieldWithAccessChecks(self,
+ Runtime::Current()->GetClassLinker(),
+ inst->VRegC_22c(),
+ shadow_frame.GetMethod(),
+ /* is_static= */ false,
+ /* is_put= */ false,
+ /* resolve_field_type= */ false);
+ if (obj == nullptr) {
+ ThrowNullPointerExceptionForFieldAccess(
+ field, shadow_frame.GetMethod(), /* is_read= */ true);
+ return false;
+ }
+ // Reload in case suspension happened during field resolution.
+ obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
+ }
+ }
+
+ uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
+ JValue result;
+ if (should_report) {
+ DCHECK(field != nullptr);
+ if (UNLIKELY(!DoFieldGetCommon<field_type>(self, shadow_frame, obj, field, &result))) {
+ // Instrumentation threw an error!
+ CHECK(self->IsExceptionPending());
return false;
}
}
- JValue result;
- if (UNLIKELY(!DoFieldGetCommon<field_type>(self, shadow_frame, obj, f, &result))) {
- // Instrumentation threw an error!
- CHECK(self->IsExceptionPending());
- return false;
- }
- uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
+#define FIELD_GET(prim, type, jtype, vreg) \
+ case Primitive::kPrim ##prim: \
+ shadow_frame.SetVReg ##vreg(vregA, \
+ should_report ? result.Get ##jtype() \
+ : is_volatile ? obj->GetField ## type ## Volatile(offset) \
+ : obj->GetField ##type(offset)); \
+ break;
+
switch (field_type) {
- case Primitive::kPrimBoolean:
- shadow_frame.SetVReg(vregA, result.GetZ());
- break;
- case Primitive::kPrimByte:
- shadow_frame.SetVReg(vregA, result.GetB());
- break;
- case Primitive::kPrimChar:
- shadow_frame.SetVReg(vregA, result.GetC());
- break;
- case Primitive::kPrimShort:
- shadow_frame.SetVReg(vregA, result.GetS());
- break;
- case Primitive::kPrimInt:
- shadow_frame.SetVReg(vregA, result.GetI());
- break;
- case Primitive::kPrimLong:
- shadow_frame.SetVRegLong(vregA, result.GetJ());
- break;
+ FIELD_GET(Boolean, Boolean, Z, )
+ FIELD_GET(Byte, Byte, B, )
+ FIELD_GET(Char, Char, C, )
+ FIELD_GET(Short, Short, S, )
+ FIELD_GET(Int, 32, I, )
+ FIELD_GET(Long, 64, J, Long)
+#undef FIELD_GET
case Primitive::kPrimNot:
- shadow_frame.SetVRegReference(vregA, result.GetL());
+ shadow_frame.SetVRegReference(
+ vregA,
+ should_report ? result.GetL()
+ : is_volatile ? obj->GetFieldObjectVolatile<mirror::Object>(offset)
+ : obj->GetFieldObject<mirror::Object>(offset));
break;
default:
LOG(FATAL) << "Unreachable: " << field_type;
@@ -487,34 +518,57 @@
// Returns true on success, otherwise throws an exception and returns false.
template<FindFieldType find_type, Primitive::Type field_type, bool do_access_check,
bool transaction_active>
-ALWAYS_INLINE bool DoFieldPut(Thread* self, const ShadowFrame& shadow_frame,
- const Instruction* inst, uint16_t inst_data)
+ALWAYS_INLINE bool DoFieldPut(Thread* self,
+ const ShadowFrame& shadow_frame,
+ const Instruction* inst,
+ uint16_t inst_data)
REQUIRES_SHARED(Locks::mutator_lock_) {
+ bool should_report = Runtime::Current()->GetInstrumentation()->HasFieldWriteListeners();
const bool do_assignability_check = do_access_check;
bool is_static = (find_type == StaticObjectWrite) || (find_type == StaticPrimitiveWrite);
- uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c();
- ArtMethod* method = shadow_frame.GetMethod();
- ArtField* f = FindFieldFromCode<find_type, do_access_check>(
- field_idx, method, self, Primitive::ComponentSize(field_type));
- if (UNLIKELY(f == nullptr)) {
- CHECK(self->IsExceptionPending());
+ uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
+ bool resolve_field_type = (shadow_frame.GetVRegReference(vregA) != nullptr);
+ ArtField* field = nullptr;
+ MemberOffset offset(0u);
+ bool is_volatile;
+ GetFieldInfo(self,
+ shadow_frame.GetMethod(),
+ reinterpret_cast<const uint16_t*>(inst),
+ is_static,
+ resolve_field_type,
+ &field,
+ &is_volatile,
+ &offset);
+ if (self->IsExceptionPending()) {
return false;
}
+
ObjPtr<mirror::Object> obj;
if (is_static) {
- obj = f->GetDeclaringClass();
+ obj = field->GetDeclaringClass();
} else {
obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
- if (UNLIKELY(obj == nullptr)) {
- ThrowNullPointerExceptionForFieldAccess(f, method, false);
- return false;
+ if (should_report || obj == nullptr) {
+ field = ResolveFieldWithAccessChecks(self,
+ Runtime::Current()->GetClassLinker(),
+ inst->VRegC_22c(),
+ shadow_frame.GetMethod(),
+ /* is_static= */ false,
+ /* is_put= */ true,
+ resolve_field_type);
+ if (UNLIKELY(obj == nullptr)) {
+ ThrowNullPointerExceptionForFieldAccess(
+ field, shadow_frame.GetMethod(), /* is_read= */ false);
+ return false;
+ }
+ // Reload in case suspension happened during field resolution.
+ obj = shadow_frame.GetVRegReference(inst->VRegB_22c(inst_data));
}
}
if (transaction_active && !CheckWriteConstraint(self, obj)) {
return false;
}
- uint32_t vregA = is_static ? inst->VRegA_21c(inst_data) : inst->VRegA_22c(inst_data);
JValue value = GetFieldValue<field_type>(shadow_frame, vregA);
if (transaction_active &&
@@ -522,12 +576,43 @@
!CheckWriteValueConstraint(self, value.GetL())) {
return false;
}
+ if (should_report) {
+ return DoFieldPutCommon<field_type, do_assignability_check, transaction_active>(self,
+ shadow_frame,
+ obj,
+ field,
+ value);
+ }
+#define FIELD_SET(prim, type, jtype) \
+ case Primitive::kPrim ## prim: \
+ if (is_volatile) { \
+ obj->SetField ## type ## Volatile<transaction_active>(offset, value.Get ## jtype()); \
+ } else { \
+ obj->SetField ## type<transaction_active>(offset, value.Get ## jtype()); \
+ } \
+ break;
- return DoFieldPutCommon<field_type, do_assignability_check, transaction_active>(self,
- shadow_frame,
- obj,
- f,
- value);
+ switch (field_type) {
+ FIELD_SET(Boolean, Boolean, Z)
+ FIELD_SET(Byte, Byte, B)
+ FIELD_SET(Char, Char, C)
+ FIELD_SET(Short, Short, S)
+ FIELD_SET(Int, 32, I)
+ FIELD_SET(Long, 64, J)
+ FIELD_SET(Not, Object, L)
+ case Primitive::kPrimVoid: {
+ LOG(FATAL) << "Unreachable " << field_type;
+ break;
+ }
+ }
+#undef FIELD_SET
+
+ if (transaction_active) {
+ if (UNLIKELY(self->IsExceptionPending())) {
+ return false;
+ }
+ }
+ return true;
}
// Handles string resolution for const-string and const-string-jumbo instructions. Also ensures the
@@ -755,34 +840,6 @@
uint16_t arg_offset,
JValue* result);
-static inline bool IsStringInit(const DexFile* dex_file, uint32_t method_idx)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- const dex::MethodId& method_id = dex_file->GetMethodId(method_idx);
- const char* class_name = dex_file->StringByTypeIdx(method_id.class_idx_);
- const char* method_name = dex_file->GetMethodName(method_id);
- // Instead of calling ResolveMethod() which has suspend point and can trigger
- // GC, look up the method symbolically.
- // Compare method's class name and method name against string init.
- // It's ok since it's not allowed to create your own java/lang/String.
- // TODO: verify that assumption.
- if ((strcmp(class_name, "Ljava/lang/String;") == 0) &&
- (strcmp(method_name, "<init>") == 0)) {
- return true;
- }
- return false;
-}
-
-static inline bool IsStringInit(const Instruction* instr, ArtMethod* caller)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (instr->Opcode() == Instruction::INVOKE_DIRECT ||
- instr->Opcode() == Instruction::INVOKE_DIRECT_RANGE) {
- uint16_t callee_method_idx = (instr->Opcode() == Instruction::INVOKE_DIRECT_RANGE) ?
- instr->VRegB_3rc() : instr->VRegB_35c();
- return IsStringInit(caller->GetDexFile(), callee_method_idx);
- }
- return false;
-}
-
// Set string value created from StringFactory.newStringFromXXX() into all aliases of
// StringFactory.newEmptyString().
void SetStringInitValueToAllAliases(ShadowFrame* shadow_frame,
diff --git a/runtime/interpreter/interpreter_intrinsics.cc b/runtime/interpreter/interpreter_intrinsics.cc
deleted file mode 100644
index c8344bc..0000000
--- a/runtime/interpreter/interpreter_intrinsics.cc
+++ /dev/null
@@ -1,678 +0,0 @@
-/*
- * 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.
- */
-
-#include "interpreter/interpreter_intrinsics.h"
-
-#include "dex/dex_instruction.h"
-#include "intrinsics_enum.h"
-#include "interpreter/interpreter_common.h"
-
-namespace art {
-namespace interpreter {
-
-
-#define BINARY_INTRINSIC(name, op, get1, get2, set) \
-static ALWAYS_INLINE bool name(ShadowFrame* shadow_frame, \
- const Instruction* inst, \
- uint16_t inst_data, \
- JValue* result_register) \
- REQUIRES_SHARED(Locks::mutator_lock_) { \
- uint32_t arg[Instruction::kMaxVarArgRegs] = {}; \
- inst->GetVarArgs(arg, inst_data); \
- result_register->set(op(shadow_frame->get1, shadow_frame->get2)); \
- return true; \
-}
-
-#define BINARY_II_INTRINSIC(name, op, set) \
- BINARY_INTRINSIC(name, op, GetVReg(arg[0]), GetVReg(arg[1]), set)
-
-#define BINARY_JJ_INTRINSIC(name, op, set) \
- BINARY_INTRINSIC(name, op, GetVRegLong(arg[0]), GetVRegLong(arg[2]), set)
-
-#define BINARY_JI_INTRINSIC(name, op, set) \
- BINARY_INTRINSIC(name, op, GetVRegLong(arg[0]), GetVReg(arg[2]), set)
-
-#define UNARY_INTRINSIC(name, op, get, set) \
-static ALWAYS_INLINE bool name(ShadowFrame* shadow_frame, \
- const Instruction* inst, \
- uint16_t inst_data, \
- JValue* result_register) \
- REQUIRES_SHARED(Locks::mutator_lock_) { \
- uint32_t arg[Instruction::kMaxVarArgRegs] = {}; \
- inst->GetVarArgs(arg, inst_data); \
- result_register->set(op(shadow_frame->get(arg[0]))); \
- return true; \
-}
-
-
-// java.lang.Integer.reverse(I)I
-UNARY_INTRINSIC(MterpIntegerReverse, ReverseBits32, GetVReg, SetI);
-
-// java.lang.Integer.reverseBytes(I)I
-UNARY_INTRINSIC(MterpIntegerReverseBytes, BSWAP, GetVReg, SetI);
-
-// java.lang.Integer.bitCount(I)I
-UNARY_INTRINSIC(MterpIntegerBitCount, POPCOUNT, GetVReg, SetI);
-
-// java.lang.Integer.compare(II)I
-BINARY_II_INTRINSIC(MterpIntegerCompare, Compare, SetI);
-
-// java.lang.Integer.highestOneBit(I)I
-UNARY_INTRINSIC(MterpIntegerHighestOneBit, HighestOneBitValue, GetVReg, SetI);
-
-// java.lang.Integer.LowestOneBit(I)I
-UNARY_INTRINSIC(MterpIntegerLowestOneBit, LowestOneBitValue, GetVReg, SetI);
-
-// java.lang.Integer.numberOfLeadingZeros(I)I
-UNARY_INTRINSIC(MterpIntegerNumberOfLeadingZeros, JAVASTYLE_CLZ, GetVReg, SetI);
-
-// java.lang.Integer.numberOfTrailingZeros(I)I
-UNARY_INTRINSIC(MterpIntegerNumberOfTrailingZeros, JAVASTYLE_CTZ, GetVReg, SetI);
-
-// java.lang.Integer.rotateRight(II)I
-BINARY_II_INTRINSIC(MterpIntegerRotateRight, (Rot<int32_t, false>), SetI);
-
-// java.lang.Integer.rotateLeft(II)I
-BINARY_II_INTRINSIC(MterpIntegerRotateLeft, (Rot<int32_t, true>), SetI);
-
-// java.lang.Integer.signum(I)I
-UNARY_INTRINSIC(MterpIntegerSignum, Signum, GetVReg, SetI);
-
-// java.lang.Long.reverse(J)J
-UNARY_INTRINSIC(MterpLongReverse, ReverseBits64, GetVRegLong, SetJ);
-
-// java.lang.Long.reverseBytes(J)J
-UNARY_INTRINSIC(MterpLongReverseBytes, BSWAP, GetVRegLong, SetJ);
-
-// java.lang.Long.bitCount(J)I
-UNARY_INTRINSIC(MterpLongBitCount, POPCOUNT, GetVRegLong, SetI);
-
-// java.lang.Long.compare(JJ)I
-BINARY_JJ_INTRINSIC(MterpLongCompare, Compare, SetI);
-
-// java.lang.Long.highestOneBit(J)J
-UNARY_INTRINSIC(MterpLongHighestOneBit, HighestOneBitValue, GetVRegLong, SetJ);
-
-// java.lang.Long.lowestOneBit(J)J
-UNARY_INTRINSIC(MterpLongLowestOneBit, LowestOneBitValue, GetVRegLong, SetJ);
-
-// java.lang.Long.numberOfLeadingZeros(J)I
-UNARY_INTRINSIC(MterpLongNumberOfLeadingZeros, JAVASTYLE_CLZ, GetVRegLong, SetJ);
-
-// java.lang.Long.numberOfTrailingZeros(J)I
-UNARY_INTRINSIC(MterpLongNumberOfTrailingZeros, JAVASTYLE_CTZ, GetVRegLong, SetJ);
-
-// java.lang.Long.rotateRight(JI)J
-BINARY_JI_INTRINSIC(MterpLongRotateRight, (Rot<int64_t, false>), SetJ);
-
-// java.lang.Long.rotateLeft(JI)J
-BINARY_JI_INTRINSIC(MterpLongRotateLeft, (Rot<int64_t, true>), SetJ);
-
-// java.lang.Long.signum(J)I
-UNARY_INTRINSIC(MterpLongSignum, Signum, GetVRegLong, SetI);
-
-// java.lang.Short.reverseBytes(S)S
-UNARY_INTRINSIC(MterpShortReverseBytes, BSWAP, GetVRegShort, SetS);
-
-// java.lang.Math.min(II)I
-BINARY_II_INTRINSIC(MterpMathMinIntInt, std::min, SetI);
-
-// java.lang.Math.min(JJ)J
-BINARY_JJ_INTRINSIC(MterpMathMinLongLong, std::min, SetJ);
-
-// java.lang.Math.max(II)I
-BINARY_II_INTRINSIC(MterpMathMaxIntInt, std::max, SetI);
-
-// java.lang.Math.max(JJ)J
-BINARY_JJ_INTRINSIC(MterpMathMaxLongLong, std::max, SetJ);
-
-// java.lang.Math.abs(I)I
-UNARY_INTRINSIC(MterpMathAbsInt, std::abs, GetVReg, SetI);
-
-// java.lang.Math.abs(J)J
-UNARY_INTRINSIC(MterpMathAbsLong, std::abs, GetVRegLong, SetJ);
-
-// java.lang.Math.abs(F)F
-UNARY_INTRINSIC(MterpMathAbsFloat, 0x7fffffff&, GetVReg, SetI);
-
-// java.lang.Math.abs(D)D
-UNARY_INTRINSIC(MterpMathAbsDouble, INT64_C(0x7fffffffffffffff)&, GetVRegLong, SetJ);
-
-// java.lang.Math.sqrt(D)D
-UNARY_INTRINSIC(MterpMathSqrt, std::sqrt, GetVRegDouble, SetD);
-
-// java.lang.Math.ceil(D)D
-UNARY_INTRINSIC(MterpMathCeil, std::ceil, GetVRegDouble, SetD);
-
-// java.lang.Math.floor(D)D
-UNARY_INTRINSIC(MterpMathFloor, std::floor, GetVRegDouble, SetD);
-
-// java.lang.Math.sin(D)D
-UNARY_INTRINSIC(MterpMathSin, std::sin, GetVRegDouble, SetD);
-
-// java.lang.Math.cos(D)D
-UNARY_INTRINSIC(MterpMathCos, std::cos, GetVRegDouble, SetD);
-
-// java.lang.Math.tan(D)D
-UNARY_INTRINSIC(MterpMathTan, std::tan, GetVRegDouble, SetD);
-
-// java.lang.Math.asin(D)D
-UNARY_INTRINSIC(MterpMathAsin, std::asin, GetVRegDouble, SetD);
-
-// java.lang.Math.acos(D)D
-UNARY_INTRINSIC(MterpMathAcos, std::acos, GetVRegDouble, SetD);
-
-// java.lang.Math.atan(D)D
-UNARY_INTRINSIC(MterpMathAtan, std::atan, GetVRegDouble, SetD);
-
-// java.lang.String.charAt(I)C
-static ALWAYS_INLINE bool MterpStringCharAt(ShadowFrame* shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result_register)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- uint32_t arg[Instruction::kMaxVarArgRegs] = {};
- inst->GetVarArgs(arg, inst_data);
- ObjPtr<mirror::String> str = shadow_frame->GetVRegReference(arg[0])->AsString();
- int length = str->GetLength();
- int index = shadow_frame->GetVReg(arg[1]);
- uint16_t res;
- if (UNLIKELY(index < 0) || (index >= length)) {
- return false; // Punt and let non-intrinsic version deal with the throw.
- }
- if (str->IsCompressed()) {
- res = str->GetValueCompressed()[index];
- } else {
- res = str->GetValue()[index];
- }
- result_register->SetC(res);
- return true;
-}
-
-// java.lang.String.compareTo(Ljava/lang/string)I
-static ALWAYS_INLINE bool MterpStringCompareTo(ShadowFrame* shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result_register)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- uint32_t arg[Instruction::kMaxVarArgRegs] = {};
- inst->GetVarArgs(arg, inst_data);
- ObjPtr<mirror::String> str = shadow_frame->GetVRegReference(arg[0])->AsString();
- ObjPtr<mirror::Object> arg1 = shadow_frame->GetVRegReference(arg[1]);
- if (arg1 == nullptr) {
- return false;
- }
- result_register->SetI(str->CompareTo(arg1->AsString()));
- return true;
-}
-
-#define STRING_INDEXOF_INTRINSIC(name, starting_pos) \
-static ALWAYS_INLINE bool Mterp##name(ShadowFrame* shadow_frame, \
- const Instruction* inst, \
- uint16_t inst_data, \
- JValue* result_register) \
- REQUIRES_SHARED(Locks::mutator_lock_) { \
- uint32_t arg[Instruction::kMaxVarArgRegs] = {}; \
- inst->GetVarArgs(arg, inst_data); \
- ObjPtr<mirror::String> str = shadow_frame->GetVRegReference(arg[0])->AsString(); \
- int ch = shadow_frame->GetVReg(arg[1]); \
- if (ch >= 0x10000) { \
- /* Punt if supplementary char. */ \
- return false; \
- } \
- result_register->SetI(str->FastIndexOf(ch, starting_pos)); \
- return true; \
-}
-
-// java.lang.String.indexOf(I)I
-STRING_INDEXOF_INTRINSIC(StringIndexOf, 0);
-
-// java.lang.String.indexOf(II)I
-STRING_INDEXOF_INTRINSIC(StringIndexOfAfter, shadow_frame->GetVReg(arg[2]));
-
-#define SIMPLE_STRING_INTRINSIC(name, operation) \
-static ALWAYS_INLINE bool Mterp##name(ShadowFrame* shadow_frame, \
- const Instruction* inst, \
- uint16_t inst_data, \
- JValue* result_register) \
- REQUIRES_SHARED(Locks::mutator_lock_) { \
- uint32_t arg[Instruction::kMaxVarArgRegs] = {}; \
- inst->GetVarArgs(arg, inst_data); \
- ObjPtr<mirror::String> str = shadow_frame->GetVRegReference(arg[0])->AsString(); \
- result_register->operation; \
- return true; \
-}
-
-// java.lang.String.isEmpty()Z
-SIMPLE_STRING_INTRINSIC(StringIsEmpty, SetZ(str->GetLength() == 0))
-
-// java.lang.String.length()I
-SIMPLE_STRING_INTRINSIC(StringLength, SetI(str->GetLength()))
-
-// java.lang.String.getCharsNoCheck(II[CI)V
-static ALWAYS_INLINE bool MterpStringGetCharsNoCheck(ShadowFrame* shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result_register ATTRIBUTE_UNUSED)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- // Start, end & index already checked by caller - won't throw. Destination is uncompressed.
- uint32_t arg[Instruction::kMaxVarArgRegs] = {};
- inst->GetVarArgs(arg, inst_data);
- ObjPtr<mirror::String> str = shadow_frame->GetVRegReference(arg[0])->AsString();
- int32_t start = shadow_frame->GetVReg(arg[1]);
- int32_t end = shadow_frame->GetVReg(arg[2]);
- int32_t index = shadow_frame->GetVReg(arg[4]);
- ObjPtr<mirror::CharArray> array = shadow_frame->GetVRegReference(arg[3])->AsCharArray();
- uint16_t* dst = array->GetData() + index;
- int32_t len = (end - start);
- if (str->IsCompressed()) {
- const uint8_t* src_8 = str->GetValueCompressed() + start;
- for (int i = 0; i < len; i++) {
- dst[i] = src_8[i];
- }
- } else {
- uint16_t* src_16 = str->GetValue() + start;
- memcpy(dst, src_16, len * sizeof(uint16_t));
- }
- return true;
-}
-
-// java.lang.String.equalsLjava/lang/Object;)Z
-static ALWAYS_INLINE bool MterpStringEquals(ShadowFrame* shadow_frame,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result_register)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- uint32_t arg[Instruction::kMaxVarArgRegs] = {};
- inst->GetVarArgs(arg, inst_data);
- ObjPtr<mirror::String> str = shadow_frame->GetVRegReference(arg[0])->AsString();
- ObjPtr<mirror::Object> obj = shadow_frame->GetVRegReference(arg[1]);
- bool res = false; // Assume not equal.
- if ((obj != nullptr) && obj->IsString()) {
- ObjPtr<mirror::String> str2 = obj->AsString();
- if (str->GetCount() == str2->GetCount()) {
- // Length & compression status are same. Can use block compare.
- void* bytes1;
- void* bytes2;
- int len = str->GetLength();
- if (str->IsCompressed()) {
- bytes1 = str->GetValueCompressed();
- bytes2 = str2->GetValueCompressed();
- } else {
- len *= sizeof(uint16_t);
- bytes1 = str->GetValue();
- bytes2 = str2->GetValue();
- }
- res = (memcmp(bytes1, bytes2, len) == 0);
- }
- }
- result_register->SetZ(res);
- return true;
-}
-
-#define VARHANDLE_FENCE_INTRINSIC(name, std_memory_operation) \
-static ALWAYS_INLINE bool name(ShadowFrame* shadow_frame ATTRIBUTE_UNUSED, \
- const Instruction* inst ATTRIBUTE_UNUSED, \
- uint16_t inst_data ATTRIBUTE_UNUSED, \
- JValue* result_register ATTRIBUTE_UNUSED) \
- REQUIRES_SHARED(Locks::mutator_lock_) { \
- std::atomic_thread_fence(std_memory_operation); \
- return true; \
-}
-
-// The VarHandle fence methods are static (unlike jdk.internal.misc.Unsafe versions).
-// The fences for the LoadLoadFence and StoreStoreFence are stronger
-// than strictly required, but the impact should be marginal.
-VARHANDLE_FENCE_INTRINSIC(MterpVarHandleFullFence, std::memory_order_seq_cst)
-VARHANDLE_FENCE_INTRINSIC(MterpVarHandleAcquireFence, std::memory_order_acquire)
-VARHANDLE_FENCE_INTRINSIC(MterpVarHandleReleaseFence, std::memory_order_release)
-VARHANDLE_FENCE_INTRINSIC(MterpVarHandleLoadLoadFence, std::memory_order_acquire)
-VARHANDLE_FENCE_INTRINSIC(MterpVarHandleStoreStoreFence, std::memory_order_release)
-
-#define METHOD_HANDLE_INVOKE_INTRINSIC(name) \
-static ALWAYS_INLINE bool Mterp##name(ShadowFrame* shadow_frame, \
- const Instruction* inst, \
- uint16_t inst_data, \
- JValue* result) \
- REQUIRES_SHARED(Locks::mutator_lock_) { \
- if (inst->Opcode() == Instruction::INVOKE_POLYMORPHIC) { \
- return DoInvokePolymorphic<false>(Thread::Current(), *shadow_frame, inst, inst_data, result); \
- } else { \
- return DoInvokePolymorphic<true>(Thread::Current(), *shadow_frame, inst, inst_data, result); \
- } \
-}
-
-METHOD_HANDLE_INVOKE_INTRINSIC(MethodHandleInvokeExact)
-METHOD_HANDLE_INVOKE_INTRINSIC(MethodHandleInvoke)
-
-#define VAR_HANDLE_ACCESSOR_INTRINSIC(name) \
-static ALWAYS_INLINE bool Mterp##name(ShadowFrame* shadow_frame, \
- const Instruction* inst, \
- uint16_t inst_data, \
- JValue* result) \
- REQUIRES_SHARED(Locks::mutator_lock_) { \
- return Do##name(Thread::Current(), *shadow_frame, inst, inst_data, result); \
-}
-
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleCompareAndExchange)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleCompareAndExchangeAcquire)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleCompareAndExchangeRelease)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleCompareAndSet)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGet);
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAcquire)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndAdd)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndAddAcquire)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndAddRelease)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseAnd)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseAndAcquire)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseAndRelease)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseOr)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseOrAcquire)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseOrRelease)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseXor)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseXorAcquire)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndBitwiseXorRelease)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndSet)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndSetAcquire)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetAndSetRelease)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetOpaque)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleGetVolatile)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleSet)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleSetOpaque)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleSetRelease)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleSetVolatile)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSet)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSetAcquire)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSetPlain)
-VAR_HANDLE_ACCESSOR_INTRINSIC(VarHandleWeakCompareAndSetRelease)
-
-static ALWAYS_INLINE bool MterpReachabilityFence(ShadowFrame* shadow_frame ATTRIBUTE_UNUSED,
- const Instruction* inst ATTRIBUTE_UNUSED,
- uint16_t inst_data ATTRIBUTE_UNUSED,
- JValue* result_register ATTRIBUTE_UNUSED)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- // Do nothing; Its only purpose is to keep the argument reference live
- // at preceding suspend points. That's automatic in the interpreter.
- return true;
-}
-
-// Macro to help keep track of what's left to implement.
-#define UNIMPLEMENTED_CASE(name) \
- case Intrinsics::k##name: \
- res = false; \
- break;
-
-#define INTRINSIC_CASE(name) \
- case Intrinsics::k##name: \
- res = Mterp##name(shadow_frame, inst, inst_data, result_register); \
- break;
-
-bool MterpHandleIntrinsic(ShadowFrame* shadow_frame,
- ArtMethod* const called_method,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result_register)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- Intrinsics intrinsic = static_cast<Intrinsics>(called_method->GetIntrinsic());
- bool res = false; // Assume failure
- switch (intrinsic) {
- UNIMPLEMENTED_CASE(DoubleDoubleToRawLongBits /* (D)J */)
- UNIMPLEMENTED_CASE(DoubleDoubleToLongBits /* (D)J */)
- UNIMPLEMENTED_CASE(DoubleIsInfinite /* (D)Z */)
- UNIMPLEMENTED_CASE(DoubleIsNaN /* (D)Z */)
- UNIMPLEMENTED_CASE(DoubleLongBitsToDouble /* (J)D */)
- UNIMPLEMENTED_CASE(FloatFloatToRawIntBits /* (F)I */)
- UNIMPLEMENTED_CASE(FloatFloatToIntBits /* (F)I */)
- UNIMPLEMENTED_CASE(FloatIsInfinite /* (F)Z */)
- UNIMPLEMENTED_CASE(FloatIsNaN /* (F)Z */)
- UNIMPLEMENTED_CASE(FloatIntBitsToFloat /* (I)F */)
- UNIMPLEMENTED_CASE(IntegerDivideUnsigned /* (II)I */)
- UNIMPLEMENTED_CASE(LongDivideUnsigned /* (JJ)J */)
- INTRINSIC_CASE(IntegerReverse)
- INTRINSIC_CASE(IntegerReverseBytes)
- INTRINSIC_CASE(IntegerBitCount)
- INTRINSIC_CASE(IntegerCompare)
- INTRINSIC_CASE(IntegerHighestOneBit)
- INTRINSIC_CASE(IntegerLowestOneBit)
- INTRINSIC_CASE(IntegerNumberOfLeadingZeros)
- INTRINSIC_CASE(IntegerNumberOfTrailingZeros)
- INTRINSIC_CASE(IntegerRotateRight)
- INTRINSIC_CASE(IntegerRotateLeft)
- INTRINSIC_CASE(IntegerSignum)
- INTRINSIC_CASE(LongReverse)
- INTRINSIC_CASE(LongReverseBytes)
- INTRINSIC_CASE(LongBitCount)
- INTRINSIC_CASE(LongCompare)
- INTRINSIC_CASE(LongHighestOneBit)
- INTRINSIC_CASE(LongLowestOneBit)
- INTRINSIC_CASE(LongNumberOfLeadingZeros)
- INTRINSIC_CASE(LongNumberOfTrailingZeros)
- INTRINSIC_CASE(LongRotateRight)
- INTRINSIC_CASE(LongRotateLeft)
- INTRINSIC_CASE(LongSignum)
- INTRINSIC_CASE(ShortReverseBytes)
- INTRINSIC_CASE(MathAbsDouble)
- INTRINSIC_CASE(MathAbsFloat)
- INTRINSIC_CASE(MathAbsLong)
- INTRINSIC_CASE(MathAbsInt)
- UNIMPLEMENTED_CASE(MathFmaDouble /* (DDD)D */)
- UNIMPLEMENTED_CASE(MathFmaFloat /* (FFF)F */)
- UNIMPLEMENTED_CASE(MathMinDoubleDouble /* (DD)D */)
- UNIMPLEMENTED_CASE(MathMinFloatFloat /* (FF)F */)
- INTRINSIC_CASE(MathMinLongLong)
- INTRINSIC_CASE(MathMinIntInt)
- UNIMPLEMENTED_CASE(MathMaxDoubleDouble /* (DD)D */)
- UNIMPLEMENTED_CASE(MathMaxFloatFloat /* (FF)F */)
- INTRINSIC_CASE(MathMaxLongLong)
- INTRINSIC_CASE(MathMaxIntInt)
- INTRINSIC_CASE(MathCos)
- INTRINSIC_CASE(MathSin)
- INTRINSIC_CASE(MathAcos)
- INTRINSIC_CASE(MathAsin)
- INTRINSIC_CASE(MathAtan)
- UNIMPLEMENTED_CASE(MathAtan2 /* (DD)D */)
- UNIMPLEMENTED_CASE(MathCbrt /* (D)D */)
- UNIMPLEMENTED_CASE(MathCosh /* (D)D */)
- UNIMPLEMENTED_CASE(MathExp /* (D)D */)
- UNIMPLEMENTED_CASE(MathExpm1 /* (D)D */)
- UNIMPLEMENTED_CASE(MathHypot /* (DD)D */)
- UNIMPLEMENTED_CASE(MathLog /* (D)D */)
- UNIMPLEMENTED_CASE(MathLog10 /* (D)D */)
- UNIMPLEMENTED_CASE(MathNextAfter /* (DD)D */)
- UNIMPLEMENTED_CASE(MathPow /* (DD)D */)
- UNIMPLEMENTED_CASE(MathSinh /* (D)D */)
- INTRINSIC_CASE(MathTan)
- UNIMPLEMENTED_CASE(MathTanh /* (D)D */)
- INTRINSIC_CASE(MathSqrt)
- INTRINSIC_CASE(MathCeil)
- INTRINSIC_CASE(MathFloor)
- UNIMPLEMENTED_CASE(MathRint /* (D)D */)
- UNIMPLEMENTED_CASE(MathRoundDouble /* (D)J */)
- UNIMPLEMENTED_CASE(MathRoundFloat /* (F)I */)
- UNIMPLEMENTED_CASE(MathMultiplyHigh /* (JJ)J */)
- UNIMPLEMENTED_CASE(SystemArrayCopyByte /* ([BI[BII)V */)
- UNIMPLEMENTED_CASE(SystemArrayCopyChar /* ([CI[CII)V */)
- UNIMPLEMENTED_CASE(SystemArrayCopyInt /* ([II[III)V */)
- UNIMPLEMENTED_CASE(SystemArrayCopy /* (Ljava/lang/Object;ILjava/lang/Object;II)V */)
- UNIMPLEMENTED_CASE(ThreadCurrentThread /* ()Ljava/lang/Thread; */)
- UNIMPLEMENTED_CASE(MemoryPeekByte /* (J)B */)
- UNIMPLEMENTED_CASE(MemoryPeekIntNative /* (J)I */)
- UNIMPLEMENTED_CASE(MemoryPeekLongNative /* (J)J */)
- UNIMPLEMENTED_CASE(MemoryPeekShortNative /* (J)S */)
- UNIMPLEMENTED_CASE(MemoryPokeByte /* (JB)V */)
- UNIMPLEMENTED_CASE(MemoryPokeIntNative /* (JI)V */)
- UNIMPLEMENTED_CASE(MemoryPokeLongNative /* (JJ)V */)
- UNIMPLEMENTED_CASE(MemoryPokeShortNative /* (JS)V */)
- INTRINSIC_CASE(ReachabilityFence /* (Ljava/lang/Object;)V */)
- INTRINSIC_CASE(StringCharAt)
- INTRINSIC_CASE(StringCompareTo)
- INTRINSIC_CASE(StringEquals)
- INTRINSIC_CASE(StringGetCharsNoCheck)
- INTRINSIC_CASE(StringIndexOf)
- INTRINSIC_CASE(StringIndexOfAfter)
- UNIMPLEMENTED_CASE(StringStringIndexOf /* (Ljava/lang/String;)I */)
- UNIMPLEMENTED_CASE(StringStringIndexOfAfter /* (Ljava/lang/String;I)I */)
- INTRINSIC_CASE(StringIsEmpty)
- INTRINSIC_CASE(StringLength)
- UNIMPLEMENTED_CASE(StringNewStringFromBytes /* ([BIII)Ljava/lang/String; */)
- UNIMPLEMENTED_CASE(StringNewStringFromChars /* (II[C)Ljava/lang/String; */)
- UNIMPLEMENTED_CASE(StringNewStringFromString /* (Ljava/lang/String;)Ljava/lang/String; */)
- UNIMPLEMENTED_CASE(StringBufferAppend /* (Ljava/lang/String;)Ljava/lang/StringBuffer; */)
- UNIMPLEMENTED_CASE(StringBufferLength /* ()I */)
- UNIMPLEMENTED_CASE(StringBufferToString /* ()Ljava/lang/String; */)
- UNIMPLEMENTED_CASE(
- StringBuilderAppendObject /* (Ljava/lang/Object;)Ljava/lang/StringBuilder; */)
- UNIMPLEMENTED_CASE(
- StringBuilderAppendString /* (Ljava/lang/String;)Ljava/lang/StringBuilder; */)
- UNIMPLEMENTED_CASE(
- StringBuilderAppendCharSequence /* (Ljava/lang/CharSequence;)Ljava/lang/StringBuilder; */)
- UNIMPLEMENTED_CASE(StringBuilderAppendCharArray /* ([C)Ljava/lang/StringBuilder; */)
- UNIMPLEMENTED_CASE(StringBuilderAppendBoolean /* (Z)Ljava/lang/StringBuilder; */)
- UNIMPLEMENTED_CASE(StringBuilderAppendChar /* (C)Ljava/lang/StringBuilder; */)
- UNIMPLEMENTED_CASE(StringBuilderAppendInt /* (I)Ljava/lang/StringBuilder; */)
- UNIMPLEMENTED_CASE(StringBuilderAppendLong /* (J)Ljava/lang/StringBuilder; */)
- UNIMPLEMENTED_CASE(StringBuilderAppendFloat /* (F)Ljava/lang/StringBuilder; */)
- UNIMPLEMENTED_CASE(StringBuilderAppendDouble /* (D)Ljava/lang/StringBuilder; */)
- UNIMPLEMENTED_CASE(StringBuilderLength /* ()I */)
- UNIMPLEMENTED_CASE(StringBuilderToString /* ()Ljava/lang/String; */)
- UNIMPLEMENTED_CASE(UnsafeCASInt /* (Ljava/lang/Object;JII)Z */)
- UNIMPLEMENTED_CASE(UnsafeCASLong /* (Ljava/lang/Object;JJJ)Z */)
- UNIMPLEMENTED_CASE(UnsafeCASObject /* (Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z */)
- UNIMPLEMENTED_CASE(UnsafeGet /* (Ljava/lang/Object;J)I */)
- UNIMPLEMENTED_CASE(UnsafeGetVolatile /* (Ljava/lang/Object;J)I */)
- UNIMPLEMENTED_CASE(UnsafeGetObject /* (Ljava/lang/Object;J)Ljava/lang/Object; */)
- UNIMPLEMENTED_CASE(UnsafeGetObjectVolatile /* (Ljava/lang/Object;J)Ljava/lang/Object; */)
- UNIMPLEMENTED_CASE(UnsafeGetLong /* (Ljava/lang/Object;J)J */)
- UNIMPLEMENTED_CASE(UnsafeGetLongVolatile /* (Ljava/lang/Object;J)J */)
- UNIMPLEMENTED_CASE(UnsafePut /* (Ljava/lang/Object;JI)V */)
- UNIMPLEMENTED_CASE(UnsafePutOrdered /* (Ljava/lang/Object;JI)V */)
- UNIMPLEMENTED_CASE(UnsafePutVolatile /* (Ljava/lang/Object;JI)V */)
- UNIMPLEMENTED_CASE(UnsafePutObject /* (Ljava/lang/Object;JLjava/lang/Object;)V */)
- UNIMPLEMENTED_CASE(UnsafePutObjectOrdered /* (Ljava/lang/Object;JLjava/lang/Object;)V */)
- UNIMPLEMENTED_CASE(UnsafePutObjectVolatile /* (Ljava/lang/Object;JLjava/lang/Object;)V */)
- UNIMPLEMENTED_CASE(UnsafePutLong /* (Ljava/lang/Object;JJ)V */)
- UNIMPLEMENTED_CASE(UnsafePutLongOrdered /* (Ljava/lang/Object;JJ)V */)
- UNIMPLEMENTED_CASE(UnsafePutLongVolatile /* (Ljava/lang/Object;JJ)V */)
- UNIMPLEMENTED_CASE(UnsafeGetAndAddInt /* (Ljava/lang/Object;JI)I */)
- UNIMPLEMENTED_CASE(UnsafeGetAndAddLong /* (Ljava/lang/Object;JJ)J */)
- UNIMPLEMENTED_CASE(UnsafeGetAndSetInt /* (Ljava/lang/Object;JI)I */)
- UNIMPLEMENTED_CASE(UnsafeGetAndSetLong /* (Ljava/lang/Object;JJ)J */)
- UNIMPLEMENTED_CASE(UnsafeGetAndSetObject /* (Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object; */)
- UNIMPLEMENTED_CASE(UnsafeLoadFence /* ()V */)
- UNIMPLEMENTED_CASE(UnsafeStoreFence /* ()V */)
- UNIMPLEMENTED_CASE(UnsafeFullFence /* ()V */)
- UNIMPLEMENTED_CASE(JdkUnsafeCASInt /* (Ljava/lang/Object;JII)Z */)
- UNIMPLEMENTED_CASE(JdkUnsafeCASLong /* (Ljava/lang/Object;JJJ)Z */)
- UNIMPLEMENTED_CASE(JdkUnsafeCASObject /* (Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z */)
- UNIMPLEMENTED_CASE(JdkUnsafeCompareAndSetInt /* (Ljava/lang/Object;JII)Z */)
- UNIMPLEMENTED_CASE(JdkUnsafeCompareAndSetLong /* (Ljava/lang/Object;JJJ)Z */)
- UNIMPLEMENTED_CASE(JdkUnsafeCompareAndSetObject /* (Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z */)
- UNIMPLEMENTED_CASE(JdkUnsafeGet /* (Ljava/lang/Object;J)I */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetVolatile /* (Ljava/lang/Object;J)I */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetAcquire /* (Ljava/lang/Object;J)I */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetObject /* (Ljava/lang/Object;J)Ljava/lang/Object; */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetObjectVolatile /* (Ljava/lang/Object;J)Ljava/lang/Object; */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetObjectAcquire /* (Ljava/lang/Object;J)Ljava/lang/Object; */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetLong /* (Ljava/lang/Object;J)J */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetLongVolatile /* (Ljava/lang/Object;J)J */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetLongAcquire /* (Ljava/lang/Object;J)J */)
- UNIMPLEMENTED_CASE(JdkUnsafePut /* (Ljava/lang/Object;JI)V */)
- UNIMPLEMENTED_CASE(JdkUnsafePutOrdered /* (Ljava/lang/Object;JI)V */)
- UNIMPLEMENTED_CASE(JdkUnsafePutVolatile /* (Ljava/lang/Object;JI)V */)
- UNIMPLEMENTED_CASE(JdkUnsafePutRelease /* (Ljava/lang/Object;JI)V */)
- UNIMPLEMENTED_CASE(JdkUnsafePutObject /* (Ljava/lang/Object;JLjava/lang/Object;)V */)
- UNIMPLEMENTED_CASE(JdkUnsafePutObjectOrdered /* (Ljava/lang/Object;JLjava/lang/Object;)V */)
- UNIMPLEMENTED_CASE(JdkUnsafePutObjectVolatile /* (Ljava/lang/Object;JLjava/lang/Object;)V */)
- UNIMPLEMENTED_CASE(JdkUnsafePutObjectRelease /* (Ljava/lang/Object;JLjava/lang/Object;)V */)
- UNIMPLEMENTED_CASE(JdkUnsafePutLong /* (Ljava/lang/Object;JJ)V */)
- UNIMPLEMENTED_CASE(JdkUnsafePutLongOrdered /* (Ljava/lang/Object;JJ)V */)
- UNIMPLEMENTED_CASE(JdkUnsafePutLongVolatile /* (Ljava/lang/Object;JJ)V */)
- UNIMPLEMENTED_CASE(JdkUnsafePutLongRelease /* (Ljava/lang/Object;JJ)V */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetAndAddInt /* (Ljava/lang/Object;JI)I */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetAndAddLong /* (Ljava/lang/Object;JJ)J */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetAndSetInt /* (Ljava/lang/Object;JI)I */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetAndSetLong /* (Ljava/lang/Object;JJ)J */)
- UNIMPLEMENTED_CASE(JdkUnsafeGetAndSetObject /* (Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object; */)
- UNIMPLEMENTED_CASE(JdkUnsafeLoadFence /* ()V */)
- UNIMPLEMENTED_CASE(JdkUnsafeStoreFence /* ()V */)
- UNIMPLEMENTED_CASE(JdkUnsafeFullFence /* ()V */)
- UNIMPLEMENTED_CASE(ReferenceGetReferent /* ()Ljava/lang/Object; */)
- UNIMPLEMENTED_CASE(ReferenceRefersTo /* (Ljava/lang/Object;)Z */)
- UNIMPLEMENTED_CASE(IntegerValueOf /* (I)Ljava/lang/Integer; */)
- UNIMPLEMENTED_CASE(ThreadInterrupted /* ()Z */)
- UNIMPLEMENTED_CASE(CRC32Update /* (II)I */)
- UNIMPLEMENTED_CASE(CRC32UpdateBytes /* (I[BII)I */)
- UNIMPLEMENTED_CASE(CRC32UpdateByteBuffer /* (IJII)I */)
- UNIMPLEMENTED_CASE(FP16Compare /* (SS)I */)
- UNIMPLEMENTED_CASE(FP16ToFloat /* (S)F */)
- UNIMPLEMENTED_CASE(FP16ToHalf /* (F)S */)
- UNIMPLEMENTED_CASE(FP16Floor /* (S)S */)
- UNIMPLEMENTED_CASE(FP16Ceil /* (S)S */)
- UNIMPLEMENTED_CASE(FP16Rint /* (S)S */)
- UNIMPLEMENTED_CASE(FP16Greater /* (SS)Z */)
- UNIMPLEMENTED_CASE(FP16GreaterEquals /* (SS)Z */)
- UNIMPLEMENTED_CASE(FP16Less /* (SS)Z */)
- UNIMPLEMENTED_CASE(FP16LessEquals /* (SS)Z */)
- UNIMPLEMENTED_CASE(FP16Min /* (SS)S */)
- UNIMPLEMENTED_CASE(FP16Max /* (SS)S */)
- INTRINSIC_CASE(VarHandleFullFence)
- INTRINSIC_CASE(VarHandleAcquireFence)
- INTRINSIC_CASE(VarHandleReleaseFence)
- INTRINSIC_CASE(VarHandleLoadLoadFence)
- INTRINSIC_CASE(VarHandleStoreStoreFence)
- INTRINSIC_CASE(MethodHandleInvokeExact)
- INTRINSIC_CASE(MethodHandleInvoke)
- INTRINSIC_CASE(VarHandleCompareAndExchange)
- INTRINSIC_CASE(VarHandleCompareAndExchangeAcquire)
- INTRINSIC_CASE(VarHandleCompareAndExchangeRelease)
- INTRINSIC_CASE(VarHandleCompareAndSet)
- INTRINSIC_CASE(VarHandleGet)
- INTRINSIC_CASE(VarHandleGetAcquire)
- INTRINSIC_CASE(VarHandleGetAndAdd)
- INTRINSIC_CASE(VarHandleGetAndAddAcquire)
- INTRINSIC_CASE(VarHandleGetAndAddRelease)
- INTRINSIC_CASE(VarHandleGetAndBitwiseAnd)
- INTRINSIC_CASE(VarHandleGetAndBitwiseAndAcquire)
- INTRINSIC_CASE(VarHandleGetAndBitwiseAndRelease)
- INTRINSIC_CASE(VarHandleGetAndBitwiseOr)
- INTRINSIC_CASE(VarHandleGetAndBitwiseOrAcquire)
- INTRINSIC_CASE(VarHandleGetAndBitwiseOrRelease)
- INTRINSIC_CASE(VarHandleGetAndBitwiseXor)
- INTRINSIC_CASE(VarHandleGetAndBitwiseXorAcquire)
- INTRINSIC_CASE(VarHandleGetAndBitwiseXorRelease)
- INTRINSIC_CASE(VarHandleGetAndSet)
- INTRINSIC_CASE(VarHandleGetAndSetAcquire)
- INTRINSIC_CASE(VarHandleGetAndSetRelease)
- INTRINSIC_CASE(VarHandleGetOpaque)
- INTRINSIC_CASE(VarHandleGetVolatile)
- INTRINSIC_CASE(VarHandleSet)
- INTRINSIC_CASE(VarHandleSetOpaque)
- INTRINSIC_CASE(VarHandleSetRelease)
- INTRINSIC_CASE(VarHandleSetVolatile)
- INTRINSIC_CASE(VarHandleWeakCompareAndSet)
- INTRINSIC_CASE(VarHandleWeakCompareAndSetAcquire)
- INTRINSIC_CASE(VarHandleWeakCompareAndSetPlain)
- INTRINSIC_CASE(VarHandleWeakCompareAndSetRelease)
- case Intrinsics::kNone:
- res = false;
- break;
- // Note: no default case to ensure we catch any newly added intrinsics.
- }
- return res;
-}
-
-} // namespace interpreter
-} // namespace art
diff --git a/runtime/interpreter/interpreter_intrinsics.h b/runtime/interpreter/interpreter_intrinsics.h
deleted file mode 100644
index 2a23002..0000000
--- a/runtime/interpreter/interpreter_intrinsics.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ART_RUNTIME_INTERPRETER_INTERPRETER_INTRINSICS_H_
-#define ART_RUNTIME_INTERPRETER_INTERPRETER_INTRINSICS_H_
-
-#include "jvalue.h"
-
-namespace art {
-
-class ArtMethod;
-class Instruction;
-class ShadowFrame;
-
-namespace interpreter {
-
-// Invokes to methods identified as intrinics are routed here. If there is
-// no interpreter implementation, return false and a normal invoke will proceed.
-bool MterpHandleIntrinsic(ShadowFrame* shadow_frame,
- ArtMethod* const called_method,
- const Instruction* inst,
- uint16_t inst_data,
- JValue* result_register);
-
-} // namespace interpreter
-} // namespace art
-
-#endif // ART_RUNTIME_INTERPRETER_INTERPRETER_INTRINSICS_H_
diff --git a/runtime/interpreter/interpreter_switch_impl-inl.h b/runtime/interpreter/interpreter_switch_impl-inl.h
index d95c507..215194e 100644
--- a/runtime/interpreter/interpreter_switch_impl-inl.h
+++ b/runtime/interpreter/interpreter_switch_impl-inl.h
@@ -144,7 +144,7 @@
if (!CheckForceReturn()) {
return false;
}
- if (UNLIKELY(Instrumentation()->HasDexPcListeners())) {
+ if (UNLIKELY(shadow_frame_.GetNotifyDexPcMoveEvents())) {
uint8_t opcode = inst_->Opcode(inst_data_);
bool is_move_result_object = (opcode == Instruction::MOVE_RESULT_OBJECT);
JValue* save_ref = is_move_result_object ? &ctx_->result_register : nullptr;
@@ -353,7 +353,7 @@
template<InvokeType type, bool is_range>
HANDLER_ATTRIBUTES bool HandleInvoke() {
- bool success = DoInvoke<type, is_range, do_access_check, /*is_mterp=*/ false>(
+ bool success = DoInvoke<type, is_range, do_access_check>(
Self(), shadow_frame_, inst_, inst_data_, ResultRegister());
return PossiblyHandlePendingExceptionOnInvoke(!success);
}
@@ -1816,7 +1816,7 @@
#define OPCODE_CASE(OPCODE, OPCODE_NAME, NAME, FORMAT, i, a, e, v) \
template<bool do_access_check, bool transaction_active> \
-ASAN_NO_INLINE static bool OP_##OPCODE_NAME( \
+ASAN_NO_INLINE NO_STACK_PROTECTOR static bool OP_##OPCODE_NAME( \
SwitchImplContext* ctx, \
const instrumentation::Instrumentation* instrumentation, \
Thread* self, \
@@ -1834,6 +1834,7 @@
#undef OPCODE_CASE
template<bool do_access_check, bool transaction_active>
+NO_STACK_PROTECTOR
void ExecuteSwitchImplCpp(SwitchImplContext* ctx) {
Thread* self = ctx->self;
const CodeItemDataAccessor& accessor = ctx->accessor;
diff --git a/runtime/interpreter/mterp/arm64ng/array.S b/runtime/interpreter/mterp/arm64ng/array.S
index 6863662..9edbb22 100644
--- a/runtime/interpreter/mterp/arm64ng/array.S
+++ b/runtime/interpreter/mterp/arm64ng/array.S
@@ -179,7 +179,7 @@
mov x0, xSELF
ldr x1, [sp, 0]
mov x2, xPC
- bl nterp_get_class_or_allocate_object
+ bl nterp_get_class
b 1b
3:
bl art_quick_read_barrier_mark_reg00
diff --git a/runtime/interpreter/mterp/arm64ng/main.S b/runtime/interpreter/mterp/arm64ng/main.S
index 89de81f..99816c6 100644
--- a/runtime/interpreter/mterp/arm64ng/main.S
+++ b/runtime/interpreter/mterp/arm64ng/main.S
@@ -273,7 +273,9 @@
bl \helper
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- RETURN_OR_DELIVER_PENDING_EXCEPTION
+ ldr xIP0, [xSELF, # THREAD_EXCEPTION_OFFSET] // Get exception field.
+ cbnz xIP0, nterp_deliver_pending_exception
+ ret
END \name
.endm
@@ -1590,6 +1592,29 @@
* rest method parameters
*/
+OAT_ENTRY ExecuteNterpWithClinitImpl, EndExecuteNterpWithClinitImpl
+ // For simplicity, we don't do a read barrier here, but instead rely
+ // on art_quick_resolution_trampoline to always have a suspend point before
+ // calling back here.
+ ldr wip, [x0, #ART_METHOD_DECLARING_CLASS_OFFSET]
+ ldrb wip2, [ip, #MIRROR_CLASS_IS_VISIBLY_INITIALIZED_OFFSET]
+ cmp ip2, #MIRROR_CLASS_IS_VISIBLY_INITIALIZED_VALUE
+ b.hs ExecuteNterpImpl
+ cmp ip2, #MIRROR_CLASS_IS_INITIALIZED_VALUE
+ b.lo .Linitializing_check
+ dmb ish
+ b ExecuteNterpImpl
+.Linitializing_check:
+ cmp ip2, #MIRROR_CLASS_IS_INITIALIZING_VALUE
+ b.lo .Lresolution_trampoline
+ ldr wip2, [ip, #MIRROR_CLASS_CLINIT_THREAD_ID_OFFSET]
+ ldr wip, [xSELF, #THREAD_TID_OFFSET]
+ cmp wip, wip2
+ b.eq ExecuteNterpImpl
+.Lresolution_trampoline:
+ b art_quick_resolution_trampoline
+EndExecuteNterpWithClinitImpl:
+
OAT_ENTRY ExecuteNterpImpl, EndExecuteNterpImpl
.cfi_startproc
sub x16, sp, #STACK_OVERFLOW_RESERVED_BYTES
@@ -1887,11 +1912,16 @@
NTERP_TRAMPOLINE nterp_get_instance_field_offset, NterpGetInstanceFieldOffset
NTERP_TRAMPOLINE nterp_filled_new_array, NterpFilledNewArray
NTERP_TRAMPOLINE nterp_filled_new_array_range, NterpFilledNewArrayRange
-NTERP_TRAMPOLINE nterp_get_class_or_allocate_object, NterpGetClassOrAllocateObject
+NTERP_TRAMPOLINE nterp_get_class, NterpGetClass
+NTERP_TRAMPOLINE nterp_allocate_object, NterpAllocateObject
NTERP_TRAMPOLINE nterp_get_method, NterpGetMethod
NTERP_TRAMPOLINE nterp_hot_method, NterpHotMethod
NTERP_TRAMPOLINE nterp_load_object, NterpLoadObject
+ENTRY nterp_deliver_pending_exception
+ DELIVER_PENDING_EXCEPTION
+END nterp_deliver_pending_exception
+
// gen_mterp.py will inline the following definitions
// within [ExecuteNterpImpl, EndExecuteNterpImpl).
%def instruction_end():
diff --git a/runtime/interpreter/mterp/arm64ng/object.S b/runtime/interpreter/mterp/arm64ng/object.S
index df044d9..b843fcd 100644
--- a/runtime/interpreter/mterp/arm64ng/object.S
+++ b/runtime/interpreter/mterp/arm64ng/object.S
@@ -19,7 +19,7 @@
mov x0, xSELF
ldr x1, [sp]
mov x2, xPC
- bl nterp_get_class_or_allocate_object
+ bl nterp_get_class
mov x1, x0
b 1b
@@ -90,7 +90,7 @@
mov x0, xSELF
ldr x1, [sp]
mov x2, xPC
- bl nterp_get_class_or_allocate_object
+ bl nterp_get_class
mov x1, x0
b 1b
@@ -476,7 +476,7 @@
mov x0, xSELF
ldr x1, [sp]
mov x2, xPC
- bl nterp_get_class_or_allocate_object
+ bl nterp_allocate_object
b 1b
3:
bl art_quick_read_barrier_mark_reg00
diff --git a/runtime/interpreter/mterp/arm64ng/other.S b/runtime/interpreter/mterp/arm64ng/other.S
index 1feafd5..3470ee8 100644
--- a/runtime/interpreter/mterp/arm64ng/other.S
+++ b/runtime/interpreter/mterp/arm64ng/other.S
@@ -66,7 +66,7 @@
b 1b
%def op_const_class():
-% op_const_object(jumbo="0", helper="nterp_get_class_or_allocate_object")
+% op_const_object(jumbo="0", helper="nterp_get_class")
%def op_const_method_handle():
% op_const_object(jumbo="0")
diff --git a/runtime/interpreter/mterp/armng/array.S b/runtime/interpreter/mterp/armng/array.S
index 4ab418c..49c4e38 100644
--- a/runtime/interpreter/mterp/armng/array.S
+++ b/runtime/interpreter/mterp/armng/array.S
@@ -186,7 +186,7 @@
mov r0, rSELF
ldr r1, [sp]
mov r2, rPC
- bl nterp_get_class_or_allocate_object
+ bl nterp_get_class
b 1b
3:
bl art_quick_read_barrier_mark_reg00
diff --git a/runtime/interpreter/mterp/armng/main.S b/runtime/interpreter/mterp/armng/main.S
index 310a3fd..9b739b0 100644
--- a/runtime/interpreter/mterp/armng/main.S
+++ b/runtime/interpreter/mterp/armng/main.S
@@ -284,7 +284,10 @@
bl \helper
RESTORE_SAVE_REFS_ONLY_FRAME
REFRESH_MARKING_REGISTER
- RETURN_OR_DELIVER_PENDING_EXCEPTION
+ ldr ip, [rSELF, #THREAD_EXCEPTION_OFFSET] @ Get exception field.
+ cmp ip, #0
+ bne nterp_deliver_pending_exception
+ bx lr
END \name
.endm
@@ -1608,6 +1611,28 @@
* rest method parameters
*/
+OAT_ENTRY ExecuteNterpWithClinitImpl, EndExecuteNterpWithClinitImpl
+ // For simplicity, we don't do a read barrier here, but instead rely
+ // on art_quick_resolution_trampoline to always have a suspend point before
+ // calling back here.
+ ldr r4, [r0, ART_METHOD_DECLARING_CLASS_OFFSET]
+ ldrb ip, [r4, MIRROR_CLASS_IS_VISIBLY_INITIALIZED_OFFSET]
+ cmp ip, #MIRROR_CLASS_IS_VISIBLY_INITIALIZED_VALUE
+ bcs ExecuteNterpImpl
+ cmp ip, #MIRROR_CLASS_IS_INITIALIZED_VALUE
+ blo .Linitializing_check
+ dmb ish
+ b ExecuteNterpImpl
+.Linitializing_check:
+ cmp ip, #MIRROR_CLASS_IS_INITIALIZING_VALUE
+ blo art_quick_resolution_trampoline
+ ldr r4, [r4, #MIRROR_CLASS_CLINIT_THREAD_ID_OFFSET]
+ ldr ip, [rSELF, #THREAD_TID_OFFSET]
+ cmp r4, ip
+ beq ExecuteNterpImpl
+ b art_quick_resolution_trampoline
+EndExecuteNterpWithClinitImpl:
+
OAT_ENTRY ExecuteNterpImpl, EndExecuteNterpImpl
.cfi_startproc
sub ip, sp, #STACK_OVERFLOW_RESERVED_BYTES
@@ -1969,11 +1994,16 @@
NTERP_TRAMPOLINE nterp_get_instance_field_offset, NterpGetInstanceFieldOffset
NTERP_TRAMPOLINE nterp_filled_new_array, NterpFilledNewArray
NTERP_TRAMPOLINE nterp_filled_new_array_range, NterpFilledNewArrayRange
-NTERP_TRAMPOLINE nterp_get_class_or_allocate_object, NterpGetClassOrAllocateObject
+NTERP_TRAMPOLINE nterp_get_class, NterpGetClass
+NTERP_TRAMPOLINE nterp_allocate_object, NterpAllocateObject
NTERP_TRAMPOLINE nterp_get_method, NterpGetMethod
NTERP_TRAMPOLINE nterp_hot_method, NterpHotMethod
NTERP_TRAMPOLINE nterp_load_object, NterpLoadObject
+ENTRY nterp_deliver_pending_exception
+ DELIVER_PENDING_EXCEPTION
+END nterp_deliver_pending_exception
+
// gen_mterp.py will inline the following definitions
// within [ExecuteNterpImpl, EndExecuteNterpImpl).
%def instruction_end():
diff --git a/runtime/interpreter/mterp/armng/object.S b/runtime/interpreter/mterp/armng/object.S
index 7deffaf..984565c 100644
--- a/runtime/interpreter/mterp/armng/object.S
+++ b/runtime/interpreter/mterp/armng/object.S
@@ -20,7 +20,7 @@
mov r0, rSELF
ldr r1, [sp]
mov r2, rPC
- bl nterp_get_class_or_allocate_object
+ bl nterp_get_class
mov r1, r0
b 1b
@@ -91,7 +91,7 @@
mov r0, rSELF
ldr r1, [sp]
mov r2, rPC
- bl nterp_get_class_or_allocate_object
+ bl nterp_get_class
mov r1, r0
b 1b
@@ -523,7 +523,7 @@
mov r0, rSELF
ldr r1, [sp]
mov r2, rPC
- bl nterp_get_class_or_allocate_object
+ bl nterp_allocate_object
b 1b
3:
bl art_quick_read_barrier_mark_reg00
diff --git a/runtime/interpreter/mterp/armng/other.S b/runtime/interpreter/mterp/armng/other.S
index 3376808..7dfed62 100644
--- a/runtime/interpreter/mterp/armng/other.S
+++ b/runtime/interpreter/mterp/armng/other.S
@@ -67,7 +67,7 @@
b 1b
%def op_const_class():
-% op_const_object(jumbo="0", helper="nterp_get_class_or_allocate_object")
+% op_const_object(jumbo="0", helper="nterp_get_class")
%def op_const_method_handle():
% op_const_object(jumbo="0")
diff --git a/runtime/interpreter/mterp/nterp.cc b/runtime/interpreter/mterp/nterp.cc
index d70a846..6d14f6d 100644
--- a/runtime/interpreter/mterp/nterp.cc
+++ b/runtime/interpreter/mterp/nterp.cc
@@ -26,7 +26,6 @@
#include "entrypoints/entrypoint_utils-inl.h"
#include "interpreter/interpreter_cache-inl.h"
#include "interpreter/interpreter_common.h"
-#include "interpreter/interpreter_intrinsics.h"
#include "interpreter/shadow_frame-inl.h"
#include "mirror/string-alloc-inl.h"
#include "nterp_helpers.h"
@@ -35,7 +34,7 @@
namespace interpreter {
bool IsNterpSupported() {
- return !kPoisonHeapReferences && kUseReadBarrier;
+ return !kPoisonHeapReferences && kReserveMarkingRegister;
}
bool CanRuntimeUseNterp() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -45,9 +44,11 @@
// parts of the runtime (like instrumentation) make assumption on an
// interpreter-only runtime to always be in a switch-like interpreter.
return IsNterpSupported() &&
+ !runtime->IsJavaDebuggable() &&
+ !instr->AreExitStubsInstalled() &&
!instr->InterpretOnly() &&
!runtime->IsAotCompiler() &&
- !runtime->GetInstrumentation()->NeedsSlowInterpreterForListeners() &&
+ !instr->NeedsSlowInterpreterForListeners() &&
// An async exception has been thrown. We need to go to the switch interpreter. nterp doesn't
// know how to deal with these so we could end up never dealing with it if we are in an
// infinite loop.
@@ -58,10 +59,17 @@
// The entrypoint for nterp, which ArtMethods can directly point to.
extern "C" void ExecuteNterpImpl() REQUIRES_SHARED(Locks::mutator_lock_);
+// Another entrypoint, which does a clinit check at entry.
+extern "C" void ExecuteNterpWithClinitImpl() REQUIRES_SHARED(Locks::mutator_lock_);
+
const void* GetNterpEntryPoint() {
return reinterpret_cast<const void*>(interpreter::ExecuteNterpImpl);
}
+const void* GetNterpWithClinitEntryPoint() {
+ return reinterpret_cast<const void*>(interpreter::ExecuteNterpWithClinitImpl);
+}
+
/*
* Verify some constants used by the nterp interpreter.
*/
@@ -89,13 +97,12 @@
}
template<typename T>
-inline void UpdateCache(Thread* self, uint16_t* dex_pc_ptr, T value) {
- DCHECK(kUseReadBarrier) << "Nterp only works with read barriers";
+inline void UpdateCache(Thread* self, const uint16_t* dex_pc_ptr, T value) {
self->GetInterpreterCache()->Set(self, dex_pc_ptr, value);
}
template<typename T>
-inline void UpdateCache(Thread* self, uint16_t* dex_pc_ptr, T* value) {
+inline void UpdateCache(Thread* self, const uint16_t* dex_pc_ptr, T* value) {
UpdateCache(self, dex_pc_ptr, reinterpret_cast<size_t>(value));
}
@@ -244,76 +251,57 @@
return dex_file->GetShorty(proto_idx);
}
+static constexpr uint8_t kInvalidInvokeType = 255u;
+static_assert(static_cast<uint8_t>(kMaxInvokeType) < kInvalidInvokeType);
+
+static constexpr uint8_t GetOpcodeInvokeType(uint8_t opcode) {
+ switch (opcode) {
+ case Instruction::INVOKE_DIRECT:
+ case Instruction::INVOKE_DIRECT_RANGE:
+ return static_cast<uint8_t>(kDirect);
+ case Instruction::INVOKE_INTERFACE:
+ case Instruction::INVOKE_INTERFACE_RANGE:
+ return static_cast<uint8_t>(kInterface);
+ case Instruction::INVOKE_STATIC:
+ case Instruction::INVOKE_STATIC_RANGE:
+ return static_cast<uint8_t>(kStatic);
+ case Instruction::INVOKE_SUPER:
+ case Instruction::INVOKE_SUPER_RANGE:
+ return static_cast<uint8_t>(kSuper);
+ case Instruction::INVOKE_VIRTUAL:
+ case Instruction::INVOKE_VIRTUAL_RANGE:
+ return static_cast<uint8_t>(kVirtual);
+
+ default:
+ return kInvalidInvokeType;
+ }
+}
+
+static constexpr std::array<uint8_t, 256u> GenerateOpcodeInvokeTypes() {
+ std::array<uint8_t, 256u> opcode_invoke_types{};
+ for (size_t opcode = 0u; opcode != opcode_invoke_types.size(); ++opcode) {
+ opcode_invoke_types[opcode] = GetOpcodeInvokeType(opcode);
+ }
+ return opcode_invoke_types;
+}
+
+static constexpr std::array<uint8_t, 256u> kOpcodeInvokeTypes = GenerateOpcodeInvokeTypes();
+
FLATTEN
-extern "C" size_t NterpGetMethod(Thread* self, ArtMethod* caller, uint16_t* dex_pc_ptr)
+extern "C" size_t NterpGetMethod(Thread* self, ArtMethod* caller, const uint16_t* dex_pc_ptr)
REQUIRES_SHARED(Locks::mutator_lock_) {
UpdateHotness(caller);
const Instruction* inst = Instruction::At(dex_pc_ptr);
- InvokeType invoke_type = kStatic;
- uint16_t method_index = 0;
- switch (inst->Opcode()) {
- case Instruction::INVOKE_DIRECT: {
- method_index = inst->VRegB_35c();
- invoke_type = kDirect;
- break;
- }
+ Instruction::Code opcode = inst->Opcode();
+ DCHECK(IsUint<8>(static_cast<std::underlying_type_t<Instruction::Code>>(opcode)));
+ uint8_t raw_invoke_type = kOpcodeInvokeTypes[opcode];
+ DCHECK_LE(raw_invoke_type, kMaxInvokeType);
+ InvokeType invoke_type = static_cast<InvokeType>(raw_invoke_type);
- case Instruction::INVOKE_INTERFACE: {
- method_index = inst->VRegB_35c();
- invoke_type = kInterface;
- break;
- }
-
- case Instruction::INVOKE_STATIC: {
- method_index = inst->VRegB_35c();
- invoke_type = kStatic;
- break;
- }
-
- case Instruction::INVOKE_SUPER: {
- method_index = inst->VRegB_35c();
- invoke_type = kSuper;
- break;
- }
- case Instruction::INVOKE_VIRTUAL: {
- method_index = inst->VRegB_35c();
- invoke_type = kVirtual;
- break;
- }
-
- case Instruction::INVOKE_DIRECT_RANGE: {
- method_index = inst->VRegB_3rc();
- invoke_type = kDirect;
- break;
- }
-
- case Instruction::INVOKE_INTERFACE_RANGE: {
- method_index = inst->VRegB_3rc();
- invoke_type = kInterface;
- break;
- }
-
- case Instruction::INVOKE_STATIC_RANGE: {
- method_index = inst->VRegB_3rc();
- invoke_type = kStatic;
- break;
- }
-
- case Instruction::INVOKE_SUPER_RANGE: {
- method_index = inst->VRegB_3rc();
- invoke_type = kSuper;
- break;
- }
-
- case Instruction::INVOKE_VIRTUAL_RANGE: {
- method_index = inst->VRegB_3rc();
- invoke_type = kVirtual;
- break;
- }
-
- default:
- LOG(FATAL) << "Unknown instruction " << inst->Opcode();
- }
+ // In release mode, this is just a simple load.
+ // In debug mode, this checks that we're using the correct instruction format.
+ uint16_t method_index =
+ (opcode >= Instruction::INVOKE_VIRTUAL_RANGE) ? inst->VRegB_3rc() : inst->VRegB_35c();
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
ArtMethod* resolved_method = caller->SkipAccessChecks()
@@ -355,9 +343,7 @@
}
UpdateCache(self, dex_pc_ptr, result);
return result;
- } else if (resolved_method->GetDeclaringClass()->IsStringClass()
- && !resolved_method->IsStatic()
- && resolved_method->IsConstructor()) {
+ } else if (resolved_method->IsStringConstructor()) {
CHECK_NE(invoke_type, kSuper);
resolved_method = WellKnownClasses::StringInitToStringFactory(resolved_method);
// Or the result with 1 to notify to nterp this is a string init method. We
@@ -374,71 +360,23 @@
}
}
-FLATTEN
-static ArtField* ResolveFieldWithAccessChecks(Thread* self,
- ClassLinker* class_linker,
- uint16_t field_index,
- ArtMethod* caller,
- bool is_static,
- bool is_put,
- size_t resolve_field_type) // Resolve if not zero
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (caller->SkipAccessChecks()) {
- return class_linker->ResolveField(field_index, caller, is_static);
- }
-
- caller = caller->GetInterfaceMethodIfProxy(kRuntimePointerSize);
-
- StackHandleScope<2> hs(self);
- Handle<mirror::DexCache> h_dex_cache(hs.NewHandle(caller->GetDexCache()));
- Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(caller->GetClassLoader()));
-
- ArtField* resolved_field = class_linker->ResolveFieldJLS(field_index,
- h_dex_cache,
- h_class_loader);
- if (resolved_field == nullptr) {
- return nullptr;
- }
-
- ObjPtr<mirror::Class> fields_class = resolved_field->GetDeclaringClass();
- if (UNLIKELY(resolved_field->IsStatic() != is_static)) {
- ThrowIncompatibleClassChangeErrorField(resolved_field, is_static, caller);
- return nullptr;
- }
- ObjPtr<mirror::Class> referring_class = caller->GetDeclaringClass();
- if (UNLIKELY(!referring_class->CheckResolvedFieldAccess(fields_class,
- resolved_field,
- caller->GetDexCache(),
- field_index))) {
- return nullptr;
- }
- if (UNLIKELY(is_put && resolved_field->IsFinal() && (fields_class != referring_class))) {
- ThrowIllegalAccessErrorFinalField(caller, resolved_field);
- return nullptr;
- }
- if (resolve_field_type != 0u && resolved_field->ResolveType() == nullptr) {
- DCHECK(self->IsExceptionPending());
- return nullptr;
- }
- return resolved_field;
-}
-
extern "C" size_t NterpGetStaticField(Thread* self,
ArtMethod* caller,
- uint16_t* dex_pc_ptr,
+ const uint16_t* dex_pc_ptr,
size_t resolve_field_type) // Resolve if not zero
REQUIRES_SHARED(Locks::mutator_lock_) {
UpdateHotness(caller);
const Instruction* inst = Instruction::At(dex_pc_ptr);
uint16_t field_index = inst->VRegB_21c();
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ Instruction::Code opcode = inst->Opcode();
ArtField* resolved_field = ResolveFieldWithAccessChecks(
self,
class_linker,
field_index,
caller,
- /* is_static */ true,
- /* is_put */ IsInstructionSPut(inst->Opcode()),
+ /*is_static=*/ true,
+ /*is_put=*/ IsInstructionSPut(opcode),
resolve_field_type);
if (resolved_field == nullptr) {
@@ -461,27 +399,39 @@
// check for it.
return reinterpret_cast<size_t>(resolved_field) | 1;
} else {
- UpdateCache(self, dex_pc_ptr, resolved_field);
+ // For sput-object, try to resolve the field type even if we were not requested to.
+ // Only if the field type is successfully resolved can we update the cache. If we
+ // fail to resolve the type, we clear the exception to keep interpreter
+ // semantics of not throwing when null is stored.
+ if (opcode == Instruction::SPUT_OBJECT &&
+ resolve_field_type == 0 &&
+ resolved_field->ResolveType() == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ self->ClearException();
+ } else {
+ UpdateCache(self, dex_pc_ptr, resolved_field);
+ }
return reinterpret_cast<size_t>(resolved_field);
}
}
extern "C" uint32_t NterpGetInstanceFieldOffset(Thread* self,
ArtMethod* caller,
- uint16_t* dex_pc_ptr,
+ const uint16_t* dex_pc_ptr,
size_t resolve_field_type) // Resolve if not zero
REQUIRES_SHARED(Locks::mutator_lock_) {
UpdateHotness(caller);
const Instruction* inst = Instruction::At(dex_pc_ptr);
uint16_t field_index = inst->VRegC_22c();
ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ Instruction::Code opcode = inst->Opcode();
ArtField* resolved_field = ResolveFieldWithAccessChecks(
self,
class_linker,
field_index,
caller,
- /* is_static */ false,
- /* is_put */ IsInstructionIPut(inst->Opcode()),
+ /*is_static=*/ false,
+ /*is_put=*/ IsInstructionIPut(opcode),
resolve_field_type);
if (resolved_field == nullptr) {
DCHECK(self->IsExceptionPending());
@@ -492,67 +442,86 @@
// of volatile.
return -resolved_field->GetOffset().Uint32Value();
}
- UpdateCache(self, dex_pc_ptr, resolved_field->GetOffset().Uint32Value());
+ // For iput-object, try to resolve the field type even if we were not requested to.
+ // Only if the field type is successfully resolved can we update the cache. If we
+ // fail to resolve the type, we clear the exception to keep interpreter
+ // semantics of not throwing when null is stored.
+ if (opcode == Instruction::IPUT_OBJECT &&
+ resolve_field_type == 0 &&
+ resolved_field->ResolveType() == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ self->ClearException();
+ } else {
+ UpdateCache(self, dex_pc_ptr, resolved_field->GetOffset().Uint32Value());
+ }
return resolved_field->GetOffset().Uint32Value();
}
-extern "C" mirror::Object* NterpGetClassOrAllocateObject(Thread* self,
- ArtMethod* caller,
- uint16_t* dex_pc_ptr)
+extern "C" mirror::Object* NterpGetClass(Thread* self, ArtMethod* caller, uint16_t* dex_pc_ptr)
REQUIRES_SHARED(Locks::mutator_lock_) {
UpdateHotness(caller);
const Instruction* inst = Instruction::At(dex_pc_ptr);
- dex::TypeIndex index;
- switch (inst->Opcode()) {
- case Instruction::NEW_INSTANCE:
- index = dex::TypeIndex(inst->VRegB_21c());
- break;
- case Instruction::CHECK_CAST:
- index = dex::TypeIndex(inst->VRegB_21c());
- break;
- case Instruction::INSTANCE_OF:
- index = dex::TypeIndex(inst->VRegC_22c());
- break;
- case Instruction::CONST_CLASS:
- index = dex::TypeIndex(inst->VRegB_21c());
- break;
- case Instruction::NEW_ARRAY:
- index = dex::TypeIndex(inst->VRegC_22c());
- break;
- default:
- LOG(FATAL) << "Unreachable";
- }
+ Instruction::Code opcode = inst->Opcode();
+ DCHECK(opcode == Instruction::CHECK_CAST ||
+ opcode == Instruction::INSTANCE_OF ||
+ opcode == Instruction::CONST_CLASS ||
+ opcode == Instruction::NEW_ARRAY);
+
+ // In release mode, this is just a simple load.
+ // In debug mode, this checks that we're using the correct instruction format.
+ dex::TypeIndex index = dex::TypeIndex(
+ (opcode == Instruction::CHECK_CAST || opcode == Instruction::CONST_CLASS)
+ ? inst->VRegB_21c()
+ : inst->VRegC_22c());
+
ObjPtr<mirror::Class> c =
ResolveVerifyAndClinit(index,
caller,
self,
/* can_run_clinit= */ false,
/* verify_access= */ !caller->SkipAccessChecks());
- if (c == nullptr) {
+ if (UNLIKELY(c == nullptr)) {
DCHECK(self->IsExceptionPending());
return nullptr;
}
- if (inst->Opcode() == Instruction::NEW_INSTANCE) {
- gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
- if (UNLIKELY(c->IsStringClass())) {
- // We don't cache the class for strings as we need to special case their
- // allocation.
- return mirror::String::AllocEmptyString(self, allocator_type).Ptr();
- } else {
- if (!c->IsFinalizable() && c->IsInstantiable()) {
- // Cache non-finalizable classes for next calls.
- UpdateCache(self, dex_pc_ptr, c.Ptr());
- }
- return AllocObjectFromCode(c, self, allocator_type).Ptr();
- }
- } else {
- // For all other cases, cache the class.
- UpdateCache(self, dex_pc_ptr, c.Ptr());
- }
+ UpdateCache(self, dex_pc_ptr, c.Ptr());
return c.Ptr();
}
+extern "C" mirror::Object* NterpAllocateObject(Thread* self,
+ ArtMethod* caller,
+ uint16_t* dex_pc_ptr)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ UpdateHotness(caller);
+ const Instruction* inst = Instruction::At(dex_pc_ptr);
+ DCHECK_EQ(inst->Opcode(), Instruction::NEW_INSTANCE);
+ dex::TypeIndex index = dex::TypeIndex(inst->VRegB_21c());
+ ObjPtr<mirror::Class> c =
+ ResolveVerifyAndClinit(index,
+ caller,
+ self,
+ /* can_run_clinit= */ false,
+ /* verify_access= */ !caller->SkipAccessChecks());
+ if (UNLIKELY(c == nullptr)) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+
+ gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
+ if (UNLIKELY(c->IsStringClass())) {
+ // We don't cache the class for strings as we need to special case their
+ // allocation.
+ return mirror::String::AllocEmptyString(self, allocator_type).Ptr();
+ } else {
+ if (!c->IsFinalizable() && c->IsInstantiable()) {
+ // Cache non-finalizable classes for next calls.
+ UpdateCache(self, dex_pc_ptr, c.Ptr());
+ }
+ return AllocObjectFromCode(c, self, allocator_type).Ptr();
+ }
+}
+
extern "C" mirror::Object* NterpLoadObject(Thread* self, ArtMethod* caller, uint16_t* dex_pc_ptr)
REQUIRES_SHARED(Locks::mutator_lock_) {
const Instruction* inst = Instruction::At(dex_pc_ptr);
diff --git a/runtime/interpreter/mterp/nterp.h b/runtime/interpreter/mterp/nterp.h
index 1590b28..4d5af39 100644
--- a/runtime/interpreter/mterp/nterp.h
+++ b/runtime/interpreter/mterp/nterp.h
@@ -32,6 +32,7 @@
bool IsNterpSupported();
bool CanRuntimeUseNterp();
const void* GetNterpEntryPoint();
+const void* GetNterpWithClinitEntryPoint();
constexpr uint16_t kNterpHotnessValue = 0;
diff --git a/runtime/interpreter/mterp/x86_64ng/main.S b/runtime/interpreter/mterp/x86_64ng/main.S
index bd191c0..a1890a1 100644
--- a/runtime/interpreter/mterp/x86_64ng/main.S
+++ b/runtime/interpreter/mterp/x86_64ng/main.S
@@ -237,7 +237,9 @@
SETUP_SAVE_REFS_ONLY_FRAME
call \helper
RESTORE_SAVE_REFS_ONLY_FRAME
- RETURN_OR_DELIVER_PENDING_EXCEPTION
+ cmpq LITERAL(0), %gs:THREAD_EXCEPTION_OFFSET
+ jne nterp_deliver_pending_exception
+ ret
END_FUNCTION \name
.endm
@@ -1694,6 +1696,21 @@
* rest method parameters
*/
+OAT_ENTRY ExecuteNterpWithClinitImpl, EndExecuteNterpWithClinitImpl
+ // For simplicity, we don't do a read barrier here, but instead rely
+ // on art_quick_resolution_trampoline to always have a suspend point before
+ // calling back here.
+ movl ART_METHOD_DECLARING_CLASS_OFFSET(%rdi), %r10d
+ cmpb $$(MIRROR_CLASS_IS_VISIBLY_INITIALIZED_VALUE), MIRROR_CLASS_IS_VISIBLY_INITIALIZED_OFFSET(%r10d)
+ jae ExecuteNterpImpl
+ cmpb $$(MIRROR_CLASS_IS_INITIALIZING_VALUE), MIRROR_CLASS_IS_VISIBLY_INITIALIZED_OFFSET(%r10d)
+ jb art_quick_resolution_trampoline
+ movl MIRROR_CLASS_CLINIT_THREAD_ID_OFFSET(%r10d), %r10d
+ cmpl %r10d, rSELF:THREAD_TID_OFFSET
+ je ExecuteNterpImpl
+ jmp art_quick_resolution_trampoline
+EndExecuteNterpWithClinitImpl:
+
OAT_ENTRY ExecuteNterpImpl, EndExecuteNterpImpl
.cfi_startproc
.cfi_def_cfa rsp, 8
@@ -1923,7 +1940,7 @@
movq rSELF:THREAD_SELF_OFFSET, %rdi
movq 0(%rsp), %rsi
movq rPC, %rdx
- call nterp_get_class_or_allocate_object
+ call nterp_allocate_object
jmp 1b
3:
// 07 is %rdi
@@ -1949,7 +1966,7 @@
movq rSELF:THREAD_SELF_OFFSET, %rdi
movq 0(%rsp), %rsi
movq rPC, %rdx
- call nterp_get_class_or_allocate_object
+ call nterp_get_class
movq %rax, %rdi
jmp 1b
3:
@@ -2295,11 +2312,16 @@
NTERP_TRAMPOLINE nterp_get_instance_field_offset, NterpGetInstanceFieldOffset
NTERP_TRAMPOLINE nterp_filled_new_array, NterpFilledNewArray
NTERP_TRAMPOLINE nterp_filled_new_array_range, NterpFilledNewArrayRange
-NTERP_TRAMPOLINE nterp_get_class_or_allocate_object, NterpGetClassOrAllocateObject
+NTERP_TRAMPOLINE nterp_get_class, NterpGetClass
+NTERP_TRAMPOLINE nterp_allocate_object, NterpAllocateObject
NTERP_TRAMPOLINE nterp_get_method, NterpGetMethod
NTERP_TRAMPOLINE nterp_hot_method, NterpHotMethod
NTERP_TRAMPOLINE nterp_load_object, NterpLoadObject
+DEFINE_FUNCTION nterp_deliver_pending_exception
+ DELIVER_PENDING_EXCEPTION
+END_FUNCTION nterp_deliver_pending_exception
+
// gen_mterp.py will inline the following definitions
// within [ExecuteNterpImpl, EndExecuteNterpImpl).
%def instruction_end():
diff --git a/runtime/interpreter/mterp/x86_64ng/object.S b/runtime/interpreter/mterp/x86_64ng/object.S
index 140ea75..21a6e67 100644
--- a/runtime/interpreter/mterp/x86_64ng/object.S
+++ b/runtime/interpreter/mterp/x86_64ng/object.S
@@ -16,7 +16,7 @@
movq rSELF:THREAD_SELF_OFFSET, %rdi
movq 0(%rsp), %rsi
movq rPC, %rdx
- call nterp_get_class_or_allocate_object
+ call nterp_get_class
movq %rax, %rsi
jmp 1b
@@ -149,7 +149,7 @@
movq rSELF:THREAD_SELF_OFFSET, %rdi
movq 0(%rsp), %rsi
movq rPC, %rdx
- call nterp_get_class_or_allocate_object
+ call nterp_get_class
movq %rax, %rsi
jmp .L${opcode}_start
diff --git a/runtime/interpreter/mterp/x86_64ng/other.S b/runtime/interpreter/mterp/x86_64ng/other.S
index a72ee58..f789086 100644
--- a/runtime/interpreter/mterp/x86_64ng/other.S
+++ b/runtime/interpreter/mterp/x86_64ng/other.S
@@ -53,7 +53,7 @@
jmp 1b
%def op_const_class():
-% op_const_object(jumbo="0", helper="nterp_get_class_or_allocate_object")
+% op_const_object(jumbo="0", helper="nterp_get_class")
%def op_const_method_handle():
% op_const_object(jumbo="0")
diff --git a/runtime/interpreter/mterp/x86ng/main.S b/runtime/interpreter/mterp/x86ng/main.S
index db8519b..5b0edd4 100644
--- a/runtime/interpreter/mterp/x86ng/main.S
+++ b/runtime/interpreter/mterp/x86ng/main.S
@@ -275,7 +275,9 @@
RESTORE_IBASE
FETCH_INST_CLEAR_OPCODE
RESTORE_SAVE_REFS_ONLY_FRAME
- RETURN_OR_DELIVER_PENDING_EXCEPTION
+ cmpl LITERAL(0), %fs:THREAD_EXCEPTION_OFFSET
+ jne nterp_deliver_pending_exception
+ ret
END_FUNCTION \name
.endm
@@ -1757,6 +1759,27 @@
* rest method parameters
*/
+OAT_ENTRY ExecuteNterpWithClinitImpl, EndExecuteNterpWithClinitImpl
+ push %esi
+ // For simplicity, we don't do a read barrier here, but instead rely
+ // on art_quick_resolution_trampoline to always have a suspend point before
+ // calling back here.
+ movl ART_METHOD_DECLARING_CLASS_OFFSET(%eax), %esi
+ cmpb $$(MIRROR_CLASS_IS_VISIBLY_INITIALIZED_VALUE), MIRROR_CLASS_IS_VISIBLY_INITIALIZED_OFFSET(%esi)
+ jae .Lcontinue_execute_nterp
+ cmpb $$(MIRROR_CLASS_IS_INITIALIZING_VALUE), MIRROR_CLASS_IS_VISIBLY_INITIALIZED_OFFSET(%esi)
+ jb .Linvoke_trampoline
+ movl MIRROR_CLASS_CLINIT_THREAD_ID_OFFSET(%esi), %esi
+ cmpl %esi, rSELF:THREAD_TID_OFFSET
+ je .Lcontinue_execute_nterp
+.Linvoke_trampoline:
+ pop %esi
+ jmp art_quick_resolution_trampoline
+.Lcontinue_execute_nterp:
+ pop %esi
+ jmp ExecuteNterpImpl
+EndExecuteNterpWithClinitImpl:
+
OAT_ENTRY ExecuteNterpImpl, EndExecuteNterpImpl
.cfi_startproc
.cfi_def_cfa esp, 4
@@ -1980,7 +2003,7 @@
movl rSELF:THREAD_SELF_OFFSET, ARG0
movl 0(%esp), ARG1
movl rPC, ARG2
- call nterp_get_class_or_allocate_object
+ call nterp_allocate_object
jmp 1b
3:
// 00 is %eax
@@ -2008,7 +2031,7 @@
movl rSELF:THREAD_SELF_OFFSET, ARG0
movl 0(%esp), ARG1
movl rPC, ARG2
- call nterp_get_class_or_allocate_object
+ call nterp_get_class
jmp 1b
3:
// 00 is %eax
@@ -2339,11 +2362,16 @@
NTERP_TRAMPOLINE nterp_get_instance_field_offset, NterpGetInstanceFieldOffset
NTERP_TRAMPOLINE nterp_filled_new_array, NterpFilledNewArray
NTERP_TRAMPOLINE nterp_filled_new_array_range, NterpFilledNewArrayRange
-NTERP_TRAMPOLINE nterp_get_class_or_allocate_object, NterpGetClassOrAllocateObject
+NTERP_TRAMPOLINE nterp_get_class, NterpGetClass
+NTERP_TRAMPOLINE nterp_allocate_object, NterpAllocateObject
NTERP_TRAMPOLINE nterp_get_method, NterpGetMethod
NTERP_TRAMPOLINE nterp_hot_method, NterpHotMethod
NTERP_TRAMPOLINE nterp_load_object, NterpLoadObject
+DEFINE_FUNCTION nterp_deliver_pending_exception
+ DELIVER_PENDING_EXCEPTION
+END_FUNCTION nterp_deliver_pending_exception
+
// gen_mterp.py will inline the following definitions
// within [ExecuteNterpImpl, EndExecuteNterpImpl).
%def instruction_end():
diff --git a/runtime/interpreter/mterp/x86ng/object.S b/runtime/interpreter/mterp/x86ng/object.S
index 1d11e10..39091ce 100644
--- a/runtime/interpreter/mterp/x86ng/object.S
+++ b/runtime/interpreter/mterp/x86ng/object.S
@@ -16,7 +16,7 @@
movl rSELF:THREAD_SELF_OFFSET, ARG0
movl 0(%esp), ARG1
movl rPC, ARG2
- call nterp_get_class_or_allocate_object
+ call nterp_get_class
movl %eax, %ecx
jmp 1b
@@ -58,7 +58,7 @@
movl rSELF:THREAD_SELF_OFFSET, ARG0
movl 0(%esp), ARG1
movl rPC, ARG2
- call nterp_get_class_or_allocate_object
+ call nterp_get_class
movl %eax, %ecx
jmp 1b
diff --git a/runtime/interpreter/mterp/x86ng/other.S b/runtime/interpreter/mterp/x86ng/other.S
index 4cf982c..6dd1ce3 100644
--- a/runtime/interpreter/mterp/x86ng/other.S
+++ b/runtime/interpreter/mterp/x86ng/other.S
@@ -53,7 +53,7 @@
jmp 1b
%def op_const_class():
-% op_const_object(jumbo="0", helper="nterp_get_class_or_allocate_object")
+% op_const_object(jumbo="0", helper="nterp_get_class")
%def op_const_method_handle():
% op_const_object(jumbo="0")
diff --git a/runtime/interpreter/shadow_frame.h b/runtime/interpreter/shadow_frame.h
index 8cb2b33..7ca2423 100644
--- a/runtime/interpreter/shadow_frame.h
+++ b/runtime/interpreter/shadow_frame.h
@@ -54,7 +54,7 @@
// We have been requested to notify when this frame gets popped.
kNotifyFramePop = 1 << 0,
// We have been asked to pop this frame off the stack as soon as possible.
- kForcePopFrame = 1 << 1,
+ kForcePopFrame = 1 << 1,
// We have been asked to re-execute the last instruction.
kForceRetryInst = 1 << 2,
// Mark that we expect the next frame to retry the last instruction (used by instrumentation and
@@ -62,6 +62,9 @@
kSkipMethodExitEvents = 1 << 3,
// Used to suppress exception events caused by other instrumentation events.
kSkipNextExceptionEvent = 1 << 4,
+ // Used to specify if DexPCMoveEvents have to be reported. These events will
+ // only be reported if the method has a breakpoint set.
+ kNotifyDexPcMoveEvents = 1 << 5,
};
public:
@@ -72,10 +75,11 @@
}
// Create ShadowFrame in heap for deoptimization.
- static ShadowFrame* CreateDeoptimizedFrame(uint32_t num_vregs, ShadowFrame* link,
- ArtMethod* method, uint32_t dex_pc) {
+ static ShadowFrame* CreateDeoptimizedFrame(uint32_t num_vregs,
+ ArtMethod* method,
+ uint32_t dex_pc) {
uint8_t* memory = new uint8_t[ComputeSize(num_vregs)];
- return CreateShadowFrameImpl(num_vregs, link, method, dex_pc, memory);
+ return CreateShadowFrameImpl(num_vregs, method, dex_pc, memory);
}
// Delete a ShadowFrame allocated on the heap for deoptimization.
@@ -87,12 +91,11 @@
// Create a shadow frame in a fresh alloca. This needs to be in the context of the caller.
// Inlining doesn't work, the compiler will still undo the alloca. So this needs to be a macro.
-#define CREATE_SHADOW_FRAME(num_vregs, link, method, dex_pc) ({ \
+#define CREATE_SHADOW_FRAME(num_vregs, method, dex_pc) ({ \
size_t frame_size = ShadowFrame::ComputeSize(num_vregs); \
void* alloca_mem = alloca(frame_size); \
ShadowFrameAllocaUniquePtr( \
- ShadowFrame::CreateShadowFrameImpl((num_vregs), (link), (method), (dex_pc), \
- (alloca_mem))); \
+ ShadowFrame::CreateShadowFrameImpl((num_vregs), (method), (dex_pc), (alloca_mem))); \
})
~ShadowFrame() {}
@@ -132,9 +135,14 @@
void SetLink(ShadowFrame* frame) {
DCHECK_NE(this, frame);
+ DCHECK_EQ(link_, nullptr);
link_ = frame;
}
+ void ClearLink() {
+ link_ = nullptr;
+ }
+
int32_t GetVReg(size_t i) const {
DCHECK_LT(i, NumberOfVRegs());
const uint32_t* vreg = &vregs_[i];
@@ -169,14 +177,14 @@
int64_t GetVRegLong(size_t i) const {
DCHECK_LT(i + 1, NumberOfVRegs());
const uint32_t* vreg = &vregs_[i];
- typedef const int64_t unaligned_int64 __attribute__ ((aligned (4)));
+ using unaligned_int64 __attribute__((aligned(4))) = const int64_t;
return *reinterpret_cast<unaligned_int64*>(vreg);
}
double GetVRegDouble(size_t i) const {
DCHECK_LT(i + 1, NumberOfVRegs());
const uint32_t* vreg = &vregs_[i];
- typedef const double unaligned_double __attribute__ ((aligned (4)));
+ using unaligned_double __attribute__((aligned(4))) = const double;
return *reinterpret_cast<unaligned_double*>(vreg);
}
@@ -221,7 +229,7 @@
void SetVRegLong(size_t i, int64_t val) {
DCHECK_LT(i + 1, NumberOfVRegs());
uint32_t* vreg = &vregs_[i];
- typedef int64_t unaligned_int64 __attribute__ ((aligned (4)));
+ using unaligned_int64 __attribute__((aligned(4))) = int64_t;
*reinterpret_cast<unaligned_int64*>(vreg) = val;
// This is needed for moving collectors since these can update the vreg references if they
// happen to agree with references in the reference array.
@@ -232,7 +240,7 @@
void SetVRegDouble(size_t i, double val) {
DCHECK_LT(i + 1, NumberOfVRegs());
uint32_t* vreg = &vregs_[i];
- typedef double unaligned_double __attribute__ ((aligned (4)));
+ using unaligned_double __attribute__((aligned(4))) = double;
*reinterpret_cast<unaligned_double*>(vreg) = val;
// This is needed for moving collectors since these can update the vreg references if they
// happen to agree with references in the reference array.
@@ -314,11 +322,10 @@
// Create ShadowFrame for interpreter using provided memory.
static ShadowFrame* CreateShadowFrameImpl(uint32_t num_vregs,
- ShadowFrame* link,
ArtMethod* method,
uint32_t dex_pc,
void* memory) {
- return new (memory) ShadowFrame(num_vregs, link, method, dex_pc);
+ return new (memory) ShadowFrame(num_vregs, method, dex_pc);
}
const uint16_t* GetDexPCPtr() {
@@ -373,6 +380,14 @@
UpdateFrameFlag(enable, FrameFlags::kSkipNextExceptionEvent);
}
+ bool GetNotifyDexPcMoveEvents() const {
+ return GetFrameFlag(FrameFlags::kNotifyDexPcMoveEvents);
+ }
+
+ void SetNotifyDexPcMoveEvents(bool enable) {
+ UpdateFrameFlag(enable, FrameFlags::kNotifyDexPcMoveEvents);
+ }
+
void CheckConsistentVRegs() const {
if (kIsDebugBuild) {
// A shadow frame visible to GC requires the following rule: for a given vreg,
@@ -385,8 +400,8 @@
}
private:
- ShadowFrame(uint32_t num_vregs, ShadowFrame* link, ArtMethod* method, uint32_t dex_pc)
- : link_(link),
+ ShadowFrame(uint32_t num_vregs, ArtMethod* method, uint32_t dex_pc)
+ : link_(nullptr),
method_(method),
result_register_(nullptr),
dex_pc_ptr_(nullptr),
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 62051ee..0d40e13 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -61,7 +61,7 @@
#include "thread-inl.h"
#include "transaction.h"
#include "unstarted_runtime_list.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
namespace interpreter {
@@ -231,8 +231,7 @@
class_loader = nullptr;
}
- ScopedObjectAccessUnchecked soa(self);
- if (class_loader != nullptr && !ClassLinker::IsBootClassLoader(soa, class_loader)) {
+ if (class_loader != nullptr && !ClassLinker::IsBootClassLoader(class_loader)) {
AbortTransactionOrFail(self,
"Only the boot classloader is supported: %s",
mirror::Object::PrettyTypeOf(class_loader).c_str());
@@ -659,8 +658,7 @@
StackHandleScope<1> hs(self);
Handle<mirror::Class> this_classloader_class(hs.NewHandle(this_obj->GetClass()));
- if (self->DecodeJObject(WellKnownClasses::java_lang_BootClassLoader) !=
- this_classloader_class.Get()) {
+ if (WellKnownClasses::java_lang_BootClassLoader != this_classloader_class.Get()) {
AbortTransactionOrFail(self,
"Unsupported classloader type %s for getResourceAsStream",
mirror::Class::PrettyClass(this_classloader_class.Get()).c_str());
@@ -1113,18 +1111,14 @@
// thread as unstarted to the ThreadGroup. A faked-up main thread peer is good enough for
// these purposes.
Runtime::Current()->InitThreadGroups(self);
- jobject main_peer =
- self->CreateCompileTimePeer(self->GetJniEnv(),
- "main",
- false,
- Runtime::Current()->GetMainThreadGroup());
+ ObjPtr<mirror::Object> main_peer = self->CreateCompileTimePeer(
+ "main", /*as_daemon=*/ false, Runtime::Current()->GetMainThreadGroup());
if (main_peer == nullptr) {
AbortTransactionOrFail(self, "Failed allocating peer");
return;
}
- result->SetL(self->DecodeJObject(main_peer));
- self->GetJniEnv()->DeleteLocalRef(main_peer);
+ result->SetL(main_peer);
} else {
AbortTransactionOrFail(self,
"Thread.currentThread() does not support %s",
@@ -1367,6 +1361,22 @@
}
// This allows creating the new style of String objects during compilation.
+void UnstartedRuntime::UnstartedStringFactoryNewStringFromBytes(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
+ jint high = shadow_frame->GetVReg(arg_offset + 1);
+ jint offset = shadow_frame->GetVReg(arg_offset + 2);
+ jint byte_count = shadow_frame->GetVReg(arg_offset + 3);
+ DCHECK_GE(byte_count, 0);
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ByteArray> h_byte_array(
+ hs.NewHandle(shadow_frame->GetVRegReference(arg_offset)->AsByteArray()));
+ Runtime* runtime = Runtime::Current();
+ gc::AllocatorType allocator = runtime->GetHeap()->GetCurrentAllocator();
+ result->SetL(
+ mirror::String::AllocFromByteArray(self, byte_count, h_byte_array, offset, high, allocator));
+}
+
+// This allows creating the new style of String objects during compilation.
void UnstartedRuntime::UnstartedStringFactoryNewStringFromChars(
Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
jint offset = shadow_frame->GetVReg(arg_offset);
@@ -1557,7 +1567,7 @@
mirror::Object* new_value = shadow_frame->GetVRegReference(arg_offset + 5);
// Must use non transactional mode.
- if (kUseReadBarrier) {
+ if (gUseReadBarrier) {
// Need to make sure the reference stored in the field is a to-space one before attempting the
// CAS or the CAS could fail incorrectly.
mirror::HeapReference<mirror::Object>* field_addr =
@@ -1921,6 +1931,30 @@
result->SetI(receiver->AsString()->CompareTo(rhs->AsString()));
}
+void UnstartedRuntime::UnstartedJNIStringFillBytesLatin1(
+ Thread* self, ArtMethod* method ATTRIBUTE_UNUSED,
+ mirror::Object* receiver, uint32_t* args, JValue* ATTRIBUTE_UNUSED) {
+ StackHandleScope<2> hs(self);
+ Handle<mirror::String> h_receiver(hs.NewHandle(
+ reinterpret_cast<mirror::String*>(receiver)->AsString()));
+ Handle<mirror::ByteArray> h_buffer(hs.NewHandle(
+ reinterpret_cast<mirror::ByteArray*>(args[0])->AsByteArray()));
+ int32_t index = static_cast<int32_t>(args[1]);
+ h_receiver->FillBytesLatin1(h_buffer, index);
+}
+
+void UnstartedRuntime::UnstartedJNIStringFillBytesUTF16(
+ Thread* self, ArtMethod* method ATTRIBUTE_UNUSED,
+ mirror::Object* receiver, uint32_t* args, JValue* ATTRIBUTE_UNUSED) {
+ StackHandleScope<2> hs(self);
+ Handle<mirror::String> h_receiver(hs.NewHandle(
+ reinterpret_cast<mirror::String*>(receiver)->AsString()));
+ Handle<mirror::ByteArray> h_buffer(hs.NewHandle(
+ reinterpret_cast<mirror::ByteArray*>(args[0])->AsByteArray()));
+ int32_t index = static_cast<int32_t>(args[1]);
+ h_receiver->FillBytesUTF16(h_buffer, index);
+}
+
void UnstartedRuntime::UnstartedJNIStringIntern(
Thread* self ATTRIBUTE_UNUSED, ArtMethod* method ATTRIBUTE_UNUSED, mirror::Object* receiver,
uint32_t* args ATTRIBUTE_UNUSED, JValue* result) {
@@ -2163,6 +2197,7 @@
uint32_t* args,
JValue* result);
+// NOLINTNEXTLINE
#define ONE_PLUS(ShortNameIgnored, DescriptorIgnored, NameIgnored, SignatureIgnored) 1 +
static constexpr size_t kInvokeHandlersSize = UNSTARTED_RUNTIME_DIRECT_LIST(ONE_PLUS) 0;
static constexpr size_t kJniHandlersSize = UNSTARTED_RUNTIME_JNI_LIST(ONE_PLUS) 0;
@@ -2262,6 +2297,9 @@
const auto& iter = invoke_handlers_.find(shadow_frame->GetMethod());
if (iter != invoke_handlers_.end()) {
+ // Note: When we special case the method, we do not ensure initialization.
+ // This has been the behavior since implementation of this feature.
+
// Clear out the result in case it's not zeroed out.
result->SetL(nullptr);
@@ -2272,6 +2310,9 @@
self->PopShadowFrame();
} else {
+ if (!EnsureInitialized(self, shadow_frame)) {
+ return;
+ }
// Not special, continue with regular interpreter execution.
ArtInterpreterToInterpreterBridge(self, accessor, shadow_frame, result);
}
diff --git a/runtime/interpreter/unstarted_runtime_list.h b/runtime/interpreter/unstarted_runtime_list.h
index 5f8add0..dd2028a 100644
--- a/runtime/interpreter/unstarted_runtime_list.h
+++ b/runtime/interpreter/unstarted_runtime_list.h
@@ -67,6 +67,7 @@
V(StringGetCharsNoCheck, "Ljava/lang/String;", "getCharsNoCheck", "(II[CI)V") \
V(StringCharAt, "Ljava/lang/String;", "charAt", "(I)C") \
V(StringDoReplace, "Ljava/lang/String;", "doReplace", "(CC)Ljava/lang/String;") \
+ V(StringFactoryNewStringFromBytes, "Ljava/lang/StringFactory;", "newStringFromBytes", "([BIII)Ljava/lang/String;") \
V(StringFactoryNewStringFromChars, "Ljava/lang/StringFactory;", "newStringFromChars", "(II[C)Ljava/lang/String;") \
V(StringFactoryNewStringFromString, "Ljava/lang/StringFactory;", "newStringFromString", "(Ljava/lang/String;)Ljava/lang/String;") \
V(StringFastSubstring, "Ljava/lang/String;", "fastSubstring", "(II)Ljava/lang/String;") \
@@ -105,6 +106,8 @@
V(ObjectInternalClone, "Ljava/lang/Object;", "internalClone", "()Ljava/lang/Object;") \
V(ObjectNotifyAll, "Ljava/lang/Object;", "notifyAll", "()V") \
V(StringCompareTo, "Ljava/lang/String;", "compareTo", "(Ljava/lang/String;)I") \
+ V(StringFillBytesLatin1, "Ljava/lang/String;", "fillBytesLatin1", "([BI)V") \
+ V(StringFillBytesUTF16, "Ljava/lang/String;", "fillBytesUTF16", "([BI)V") \
V(StringIntern, "Ljava/lang/String;", "intern", "()Ljava/lang/String;") \
V(ArrayCreateMultiArray, "Ljava/lang/reflect/Array;", "createMultiArray", "(Ljava/lang/Class;[I)Ljava/lang/Object;") \
V(ArrayCreateObjectArray, "Ljava/lang/reflect/Array;", "createObjectArray", "(Ljava/lang/Class;I)Ljava/lang/Object;") \
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index 75a692e..dfda20a 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -62,11 +62,6 @@
class UnstartedRuntimeTest : public CommonRuntimeTest {
protected:
- void SetUp() override {
- CommonRuntimeTest::SetUp();
- InitializeIntrinsics();
- }
-
// Re-expose all UnstartedRuntime implementations so we don't need to declare a million
// test friends.
@@ -96,11 +91,10 @@
#undef UNSTARTED_JNI
UniqueDeoptShadowFramePtr CreateShadowFrame(uint32_t num_vregs,
- ShadowFrame* link,
ArtMethod* method,
uint32_t dex_pc) {
return UniqueDeoptShadowFramePtr(
- ShadowFrame::CreateDeoptimizedFrame(num_vregs, link, method, dex_pc));
+ ShadowFrame::CreateDeoptimizedFrame(num_vregs, method, dex_pc));
}
// Helpers for ArrayCopy.
@@ -237,7 +231,7 @@
const uint8_t* base_ptr = base_array;
JValue result;
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
for (int32_t i = 0; i < kBaseLen; ++i) {
tmp->SetVRegLong(0, static_cast<int64_t>(reinterpret_cast<intptr_t>(base_ptr + i)));
@@ -257,7 +251,7 @@
const uint8_t* base_ptr = base_array;
JValue result;
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
int32_t adjusted_length = kBaseLen - sizeof(int16_t);
for (int32_t i = 0; i < adjusted_length; ++i) {
@@ -280,7 +274,7 @@
const uint8_t* base_ptr = base_array;
JValue result;
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
int32_t adjusted_length = kBaseLen - sizeof(int32_t);
for (int32_t i = 0; i < adjusted_length; ++i) {
@@ -303,7 +297,7 @@
const uint8_t* base_ptr = base_array;
JValue result;
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
int32_t adjusted_length = kBaseLen - sizeof(int64_t);
for (int32_t i = 0; i < adjusted_length; ++i) {
@@ -333,7 +327,7 @@
uint16_t buf[kBaseLen];
JValue result;
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
for (int32_t start_index = 0; start_index < kBaseLen; ++start_index) {
for (int32_t count = 0; count <= kBaseLen; ++count) {
@@ -385,7 +379,7 @@
ObjPtr<mirror::String> test_string = mirror::String::AllocFromModifiedUtf8(self, base_string);
JValue result;
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
for (int32_t i = 0; i < base_len; ++i) {
tmp->SetVRegReference(0, test_string);
@@ -410,7 +404,7 @@
uint16_t inst_data[3] = { 0x2070, 0x0000, 0x0010 };
JValue result;
- UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, method, 0);
+ UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, method, 0);
const char* base_string = "hello_world";
StackHandleScope<2> hs(self);
Handle<mirror::String> string_arg =
@@ -420,11 +414,13 @@
shadow_frame->SetVRegReference(0, reference_empty_string.Get());
shadow_frame->SetVRegReference(1, string_arg.Get());
- interpreter::DoCall<false, false>(method,
+ ArtMethod* factory = WellKnownClasses::StringInitToStringFactory(method);
+ interpreter::DoCall<false, false>(factory,
self,
*shadow_frame,
Instruction::At(inst_data),
inst_data[0],
+ /* string_init= */ true,
&result);
ObjPtr<mirror::String> string_result = down_cast<mirror::String*>(result.GetL());
EXPECT_EQ(string_arg->GetLength(), string_result->GetLength());
@@ -453,7 +449,7 @@
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
JValue result;
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
// Note: all tests are not GC safe. Assume there's no GC running here with the few objects we
// allocate.
@@ -485,7 +481,7 @@
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
JValue result;
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
StackHandleScope<1> hs_object(self);
Handle<mirror::Class> object_class(hs_object.NewHandle(GetClassRoot<mirror::Object>()));
@@ -588,7 +584,7 @@
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
// Test string. Should be valid, and between minimal values of LONG_MIN and LONG_MAX (for all
// suffixes).
@@ -634,7 +630,7 @@
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
// Test string. Should be valid, and between minimal values of LONG_MIN and LONG_MAX (for all
// suffixes).
@@ -679,7 +675,7 @@
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
constexpr double inf = std::numeric_limits<double>::infinity();
@@ -706,7 +702,7 @@
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
constexpr double inf = std::numeric_limits<double>::infinity();
@@ -733,7 +729,7 @@
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
std::locale c_locale("C");
@@ -828,7 +824,7 @@
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
// Test an important value, PI/6. That's the one we see in practice.
constexpr uint64_t lvalue = UINT64_C(0x3fe0c152382d7365);
@@ -845,7 +841,7 @@
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
// Test an important value, PI/6. That's the one we see in practice.
constexpr uint64_t lvalue = UINT64_C(0x3fe0c152382d7365);
@@ -862,7 +858,7 @@
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
// Test an important pair.
constexpr uint64_t lvalue1 = UINT64_C(0x4079000000000000);
@@ -883,7 +879,7 @@
ScopedObjectAccess soa(self);
JValue result;
- UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, 0);
ObjPtr<mirror::Class> class_klass = GetClassRoot<mirror::Class>();
shadow_frame->SetVRegReference(0, class_klass);
@@ -906,7 +902,7 @@
ScopedObjectAccess soa(self);
JValue result;
- UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, 0);
jobject class_loader = LoadDex("Nested");
StackHandleScope<4> hs(self);
@@ -938,7 +934,7 @@
ScopedObjectAccess soa(self);
JValue result;
- UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, 0);
StackHandleScope<1> hs(self);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
@@ -960,14 +956,14 @@
ASSERT_TRUE(caller_method != nullptr);
ASSERT_TRUE(caller_method->IsDirect());
ASSERT_TRUE(caller_method->GetDeclaringClass() == floating_decimal.Get());
- UniqueDeoptShadowFramePtr caller_frame = CreateShadowFrame(10, nullptr, caller_method, 0);
+ UniqueDeoptShadowFramePtr caller_frame = CreateShadowFrame(10, caller_method, 0);
shadow_frame->SetLink(caller_frame.get());
UnstartedThreadLocalGet(self, shadow_frame.get(), &result, 0);
EXPECT_TRUE(result.GetL() != nullptr);
EXPECT_FALSE(self->IsExceptionPending());
- shadow_frame->SetLink(nullptr);
+ shadow_frame->ClearLink();
}
// Negative test.
@@ -978,7 +974,7 @@
ObjPtr<mirror::Class> class_class = GetClassRoot<mirror::Class>();
ArtMethod* caller_method =
&*class_class->GetDeclaredMethods(class_linker->GetImagePointerSize()).begin();
- UniqueDeoptShadowFramePtr caller_frame = CreateShadowFrame(10, nullptr, caller_method, 0);
+ UniqueDeoptShadowFramePtr caller_frame = CreateShadowFrame(10, caller_method, 0);
shadow_frame->SetLink(caller_frame.get());
EnterTransactionMode();
@@ -988,7 +984,7 @@
ASSERT_TRUE(self->IsExceptionPending());
self->ClearException();
- shadow_frame->SetLink(nullptr);
+ shadow_frame->ClearLink();
}
}
@@ -1016,7 +1012,7 @@
uint16_t inst_data[3] = { 0x2070, 0x0000, 0x0010 };
JValue result;
- UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, method, 0);
+ UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, method, 0);
shadow_frame->SetVRegDouble(0, 1.23);
interpreter::DoCall<false, false>(method,
@@ -1024,6 +1020,7 @@
*shadow_frame,
Instruction::At(inst_data),
inst_data[0],
+ /* string_init= */ false,
&result);
ObjPtr<mirror::String> string_result = down_cast<mirror::String*>(result.GetL());
ASSERT_TRUE(string_result != nullptr);
@@ -1037,7 +1034,7 @@
ScopedObjectAccess soa(self);
JValue result;
- UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, 0);
StackHandleScope<1> hs(self);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
@@ -1114,13 +1111,15 @@
}
JValue result;
- UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, 0);
for (const char* name : kTestCases) {
ObjPtr<mirror::String> name_string = mirror::String::AllocFromModifiedUtf8(self, name);
CHECK(name_string != nullptr);
if (in_transaction) {
+ StackHandleScope<1> hs(self);
+ HandleWrapperObjPtr<mirror::String> h(hs.NewHandleWrapper(&name_string));
EnterTransactionMode();
}
CHECK(!self->IsExceptionPending());
@@ -1168,7 +1167,7 @@
CHECK(boot_cp_init != nullptr);
JValue result;
- UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, boot_cp_init, 0);
+ UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, boot_cp_init, 0);
shadow_frame->SetVRegReference(0, boot_cp.Get());
// create instruction data for invoke-direct {v0} of method with fake index
@@ -1179,6 +1178,7 @@
*shadow_frame,
Instruction::At(inst_data),
inst_data[0],
+ /* string_init= */ false,
&result);
CHECK(!self->IsExceptionPending());
}
@@ -1287,7 +1287,7 @@
ASSERT_TRUE(class_linker->EnsureInitialized(self, list_class, true, true));
JValue result;
- UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, 0);
shadow_frame->SetVRegReference(0, list_class.Get());
UnstartedClassGetSignatureAnnotation(self, shadow_frame.get(), &result, 0);
@@ -1339,7 +1339,7 @@
// OK, we're ready now.
JValue result;
- UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr shadow_frame = CreateShadowFrame(10, nullptr, 0);
shadow_frame->SetVRegReference(0, cons.Get());
shadow_frame->SetVRegReference(1, args.Get());
UnstartedConstructorNewInstance0(self, shadow_frame.get(), &result, 0);
@@ -1360,7 +1360,7 @@
TEST_F(UnstartedRuntimeTest, IdentityHashCode) {
Thread* self = Thread::Current();
ScopedObjectAccess soa(self);
- UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, nullptr, 0);
+ UniqueDeoptShadowFramePtr tmp = CreateShadowFrame(10, nullptr, 0);
JValue result;
UnstartedSystemIdentityHashCode(self, tmp.get(), &result, 0);
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 6d634ae..abade3a 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -208,7 +208,6 @@
// Jit GC for now (b/147208992).
if (code_cache->GetGarbageCollectCode()) {
code_cache->SetGarbageCollectCode(!jit_compiler_->GenerateDebugInfo() &&
- !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled() &&
!jit->JitAtFirstUse());
}
@@ -259,10 +258,14 @@
return true;
}
-bool Jit::CompileMethod(ArtMethod* method,
- Thread* self,
- CompilationKind compilation_kind,
- bool prejit) {
+bool Jit::CompileMethodInternal(ArtMethod* method,
+ Thread* self,
+ CompilationKind compilation_kind,
+ bool prejit) {
+ if (kIsDebugBuild) {
+ MutexLock mu(self, *Locks::jit_lock_);
+ CHECK(GetCodeCache()->IsMethodBeingCompiled(method, compilation_kind));
+ }
DCHECK(Runtime::Current()->UseJitCompilation());
DCHECK(!method->IsRuntimeMethod());
@@ -279,9 +282,8 @@
compilation_kind = CompilationKind::kOptimized;
}
- RuntimeCallbacks* cb = Runtime::Current()->GetRuntimeCallbacks();
// Don't compile the method if it has breakpoints.
- if (cb->IsMethodBeingInspected(method)) {
+ if (Runtime::Current()->GetInstrumentation()->IsDeoptimized(method)) {
VLOG(jit) << "JIT not compiling " << method->PrettyMethod()
<< " due to not being safe to jit according to runtime-callbacks. For example, there"
<< " could be breakpoints in this method.";
@@ -323,7 +325,7 @@
<< ArtMethod::PrettyMethod(method_to_compile)
<< " kind=" << compilation_kind;
bool success = jit_compiler_->CompileMethod(self, region, method_to_compile, compilation_kind);
- code_cache_->DoneCompiling(method_to_compile, self, compilation_kind);
+ code_cache_->DoneCompiling(method_to_compile, self);
if (!success) {
VLOG(jit) << "Failed to compile method "
<< ArtMethod::PrettyMethod(method_to_compile)
@@ -568,12 +570,11 @@
// Before allowing the jump, make sure no code is actively inspecting the method to avoid
// jumping from interpreter to OSR while e.g. single stepping. Note that we could selectively
// disable OSR when single stepping, but that's currently hard to know at this point.
- if (Runtime::Current()->GetInstrumentation()->InterpreterStubsInstalled() ||
- Runtime::Current()->GetInstrumentation()->IsDeoptimized(method) ||
- thread->IsForceInterpreter() ||
- method->GetDeclaringClass()->IsObsoleteObject() ||
- Dbg::IsForcedInterpreterNeededForUpcall(thread, method) ||
- Runtime::Current()->GetRuntimeCallbacks()->IsMethodBeingInspected(method)) {
+ // Currently, HaveLocalsChanged is not frame specific. It is possible to make it frame specific
+ // to allow OSR of frames that don't have any locals changed but it isn't worth the additional
+ // complexity.
+ if (Runtime::Current()->GetInstrumentation()->NeedsSlowInterpreterForMethod(thread, method) ||
+ Runtime::Current()->GetRuntimeCallbacks()->HaveLocalsChanged()) {
return false;
}
@@ -748,6 +749,51 @@
child_mapping_methods.Reset();
}
+class ScopedCompilation {
+ public:
+ ScopedCompilation(ScopedCompilation&& other) noexcept :
+ jit_(other.jit_),
+ method_(other.method_),
+ compilation_kind_(other.compilation_kind_),
+ owns_compilation_(other.owns_compilation_) {
+ other.owns_compilation_ = false;
+ }
+
+ ScopedCompilation(Jit* jit, ArtMethod* method, CompilationKind compilation_kind)
+ : jit_(jit),
+ method_(method),
+ compilation_kind_(compilation_kind),
+ owns_compilation_(true) {
+ MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ // We don't want to enqueue any new tasks when thread pool has stopped. This simplifies
+ // the implementation of redefinition feature in jvmti.
+ if (jit_->GetThreadPool() == nullptr ||
+ !jit_->GetThreadPool()->HasStarted(Thread::Current()) ||
+ jit_->GetCodeCache()->IsMethodBeingCompiled(method_, compilation_kind_)) {
+ owns_compilation_ = false;
+ return;
+ }
+ jit_->GetCodeCache()->AddMethodBeingCompiled(method_, compilation_kind_);
+ }
+
+ bool OwnsCompilation() const {
+ return owns_compilation_;
+ }
+
+ ~ScopedCompilation() {
+ if (owns_compilation_) {
+ MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ jit_->GetCodeCache()->RemoveMethodBeingCompiled(method_, compilation_kind_);
+ }
+ }
+
+ private:
+ Jit* const jit_;
+ ArtMethod* const method_;
+ const CompilationKind compilation_kind_;
+ bool owns_compilation_;
+};
+
class JitCompileTask final : public Task {
public:
enum class TaskKind {
@@ -755,25 +801,16 @@
kPreCompile,
};
- JitCompileTask(ArtMethod* method, TaskKind task_kind, CompilationKind compilation_kind)
- : method_(method), kind_(task_kind), compilation_kind_(compilation_kind), klass_(nullptr) {
- ScopedObjectAccess soa(Thread::Current());
- // For a non-bootclasspath class, add a global ref to the class to prevent class unloading
- // until compilation is done.
- // When we precompile, this is either with boot classpath methods, or main
- // class loader methods, so we don't need to keep a global reference.
- if (method->GetDeclaringClass()->GetClassLoader() != nullptr &&
- kind_ != TaskKind::kPreCompile) {
- klass_ = soa.Vm()->AddGlobalRef(soa.Self(), method_->GetDeclaringClass());
- CHECK(klass_ != nullptr);
- }
- }
-
- ~JitCompileTask() {
- if (klass_ != nullptr) {
- ScopedObjectAccess soa(Thread::Current());
- soa.Vm()->DeleteGlobalRef(soa.Self(), klass_);
- }
+ JitCompileTask(ArtMethod* method,
+ TaskKind task_kind,
+ CompilationKind compilation_kind,
+ ScopedCompilation&& sc)
+ : method_(method),
+ kind_(task_kind),
+ compilation_kind_(compilation_kind),
+ scoped_compilation_(std::move(sc)) {
+ DCHECK(scoped_compilation_.OwnsCompilation());
+ DCHECK(!sc.OwnsCompilation());
}
void Run(Thread* self) override {
@@ -782,7 +819,7 @@
switch (kind_) {
case TaskKind::kCompile:
case TaskKind::kPreCompile: {
- Runtime::Current()->GetJit()->CompileMethod(
+ Runtime::Current()->GetJit()->CompileMethodInternal(
method_,
self,
compilation_kind_,
@@ -802,7 +839,7 @@
ArtMethod* const method_;
const TaskKind kind_;
const CompilationKind compilation_kind_;
- jobject klass_;
+ ScopedCompilation scoped_compilation_;
DISALLOW_IMPLICIT_CONSTRUCTORS(JitCompileTask);
};
@@ -1280,16 +1317,40 @@
return;
}
Runtime* runtime = Runtime::Current();
- // If the runtime is debuggable, no need to precompile methods.
+ // If the runtime is debuggable, don't bother precompiling methods.
+ // If system server is being profiled, don't precompile as we are going to use
+ // the JIT to count hotness. Note that --count-hotness-in-compiled-code is
+ // only forced when we also profile the boot classpath, see
+ // AndroidRuntime.cpp.
if (runtime->IsSystemServer() &&
UseJitCompilation() &&
options_->UseProfiledJitCompilation() &&
runtime->HasImageWithProfile() &&
+ !runtime->IsSystemServerProfiled() &&
!runtime->IsJavaDebuggable()) {
+ // Note: this precompilation is currently not running in production because:
+ // - UseProfiledJitCompilation() is not set by default.
+ // - System server dex files are registered *before* we set the runtime as
+ // system server (though we are in the system server process).
thread_pool_->AddTask(Thread::Current(), new JitProfileTask(dex_files, class_loader));
}
}
+void Jit::AddCompileTask(Thread* self,
+ ArtMethod* method,
+ CompilationKind compilation_kind,
+ bool precompile) {
+ ScopedCompilation sc(this, method, compilation_kind);
+ if (!sc.OwnsCompilation()) {
+ return;
+ }
+ JitCompileTask::TaskKind task_kind = precompile
+ ? JitCompileTask::TaskKind::kPreCompile
+ : JitCompileTask::TaskKind::kCompile;
+ thread_pool_->AddTask(
+ self, new JitCompileTask(method, task_kind, compilation_kind, std::move(sc)));
+}
+
bool Jit::CompileMethodFromProfile(Thread* self,
ClassLinker* class_linker,
uint32_t method_idx,
@@ -1310,21 +1371,27 @@
// Already seen by another profile.
return false;
}
+ CompilationKind compilation_kind = CompilationKind::kOptimized;
const void* entry_point = method->GetEntryPointFromQuickCompiledCode();
if (class_linker->IsQuickToInterpreterBridge(entry_point) ||
class_linker->IsQuickGenericJniStub(entry_point) ||
- (entry_point == interpreter::GetNterpEntryPoint()) ||
- // We explicitly check for the stub. The trampoline is for methods backed by
- // a .oat file that has a compiled version of the method.
+ class_linker->IsNterpEntryPoint(entry_point) ||
+ // We explicitly check for the resolution stub, and not the resolution trampoline.
+ // The trampoline is for methods backed by a .oat file that has a compiled version of
+ // the method.
(entry_point == GetQuickResolutionStub())) {
VLOG(jit) << "JIT Zygote processing method " << ArtMethod::PrettyMethod(method)
<< " from profile";
method->SetPreCompiled();
+ ScopedCompilation sc(this, method, compilation_kind);
+ if (!sc.OwnsCompilation()) {
+ return false;
+ }
if (!add_to_queue) {
- CompileMethod(method, self, CompilationKind::kOptimized, /* prejit= */ true);
+ CompileMethodInternal(method, self, compilation_kind, /* prejit= */ true);
} else {
Task* task = new JitCompileTask(
- method, JitCompileTask::TaskKind::kPreCompile, CompilationKind::kOptimized);
+ method, JitCompileTask::TaskKind::kPreCompile, compilation_kind, std::move(sc));
if (compile_after_boot) {
AddPostBootTask(self, task);
} else {
@@ -1475,11 +1542,7 @@
// hotness threshold. If we're not only using the baseline compiler, enqueue a compilation
// task that will compile optimize the method.
if (!options_->UseBaselineCompiler()) {
- thread_pool_->AddTask(
- self,
- new JitCompileTask(method,
- JitCompileTask::TaskKind::kCompile,
- CompilationKind::kOptimized));
+ AddCompileTask(self, method, CompilationKind::kOptimized);
}
}
@@ -1499,23 +1562,17 @@
bool was_runtime_thread_;
};
-void Jit::MethodEntered(Thread* thread, ArtMethod* method) {
+void Jit::MethodEntered(Thread* self, ArtMethod* method) {
Runtime* runtime = Runtime::Current();
if (UNLIKELY(runtime->UseJitCompilation() && JitAtFirstUse())) {
ArtMethod* np_method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize);
if (np_method->IsCompilable()) {
- // TODO(ngeoffray): For JIT at first use, use kPreCompile. Currently we don't due to
- // conflicts with jitzygote optimizations.
- JitCompileTask compile_task(
- method, JitCompileTask::TaskKind::kCompile, CompilationKind::kOptimized);
- // Fake being in a runtime thread so that class-load behavior will be the same as normal jit.
- ScopedSetRuntimeThread ssrt(thread);
- compile_task.Run(thread);
+ CompileMethod(method, self, CompilationKind::kOptimized, /* prejit= */ false);
}
return;
}
- AddSamples(thread, method);
+ AddSamples(self, method);
}
void Jit::WaitForCompilationToFinish(Thread* self) {
@@ -1620,7 +1677,6 @@
// Jit GC for now (b/147208992).
code_cache_->SetGarbageCollectCode(
!jit_compiler_->GenerateDebugInfo() &&
- !runtime->GetInstrumentation()->AreExitStubsInstalled() &&
!JitAtFirstUse());
if (is_system_server && runtime->HasImageWithProfile()) {
@@ -1745,17 +1801,14 @@
if (!method->IsNative() && !code_cache_->IsOsrCompiled(method)) {
// If we already have compiled code for it, nterp may be stuck in a loop.
// Compile OSR.
- thread_pool_->AddTask(
- self,
- new JitCompileTask(method, JitCompileTask::TaskKind::kCompile, CompilationKind::kOsr));
+ AddCompileTask(self, method, CompilationKind::kOsr);
}
return;
}
// Check if we have precompiled this method.
if (UNLIKELY(method->IsPreCompiled())) {
- if (!NeedsClinitCheckBeforeCall(method) ||
- method->GetDeclaringClass()->IsVisiblyInitialized()) {
+ if (!method->StillNeedsClinitCheck()) {
const void* entry_point = code_cache_->GetSavedEntryPointOfPreCompiledMethod(method);
if (entry_point != nullptr) {
Runtime::Current()->GetInstrumentation()->UpdateMethodsCode(method, entry_point);
@@ -1764,7 +1817,7 @@
return;
}
- static constexpr size_t kIndividualSharedMethodHotnessThreshold = 0xff;
+ static constexpr size_t kIndividualSharedMethodHotnessThreshold = 0x3f;
if (method->IsMemorySharedMethod()) {
MutexLock mu(self, lock_);
auto it = shared_method_counters_.find(method);
@@ -1781,17 +1834,27 @@
}
if (!method->IsNative() && GetCodeCache()->CanAllocateProfilingInfo()) {
- thread_pool_->AddTask(
- self,
- new JitCompileTask(method, JitCompileTask::TaskKind::kCompile, CompilationKind::kBaseline));
+ AddCompileTask(self, method, CompilationKind::kBaseline);
} else {
- thread_pool_->AddTask(
- self,
- new JitCompileTask(method,
- JitCompileTask::TaskKind::kCompile,
- CompilationKind::kOptimized));
+ AddCompileTask(self, method, CompilationKind::kOptimized);
}
}
+bool Jit::CompileMethod(ArtMethod* method,
+ Thread* self,
+ CompilationKind compilation_kind,
+ bool prejit) {
+ ScopedCompilation sc(this, method, compilation_kind);
+ // TODO: all current users of this method expect us to wait if it is being compiled.
+ if (!sc.OwnsCompilation()) {
+ return false;
+ }
+ // Fake being in a runtime thread so that class-load behavior will be the same as normal jit.
+ ScopedSetRuntimeThread ssrt(self);
+ // TODO(ngeoffray): For JIT at first use, use kPreCompile. Currently we don't due to
+ // conflicts with jitzygote optimizations.
+ return CompileMethodInternal(method, self, compilation_kind, prejit);
+}
+
} // namespace jit
} // namespace art
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index b439c8e..c95fd9d 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -53,6 +53,7 @@
namespace jit {
class JitCodeCache;
+class JitCompileTask;
class JitMemoryRegion;
class JitOptions;
@@ -195,6 +196,7 @@
virtual bool GenerateDebugInfo() = 0;
virtual void ParseCompilerOptions() = 0;
virtual bool IsBaselineCompiler() const = 0;
+ virtual void SetDebuggableCompilerOption(bool value) = 0;
virtual std::vector<uint8_t> PackElfFileForJIT(ArrayRef<const JITCodeEntry*> elf_files,
ArrayRef<const void*> removed_symbols,
@@ -461,6 +463,17 @@
static bool BindCompilerMethods(std::string* error_msg);
+ void AddCompileTask(Thread* self,
+ ArtMethod* method,
+ CompilationKind compilation_kind,
+ bool precompile = false);
+
+ bool CompileMethodInternal(ArtMethod* method,
+ Thread* self,
+ CompilationKind compilation_kind,
+ bool prejit)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// JIT compiler
static void* jit_library_handle_;
static JitCompilerInterface* jit_compiler_;
@@ -507,6 +520,8 @@
// between the zygote and apps.
std::map<ArtMethod*, uint16_t> shared_method_counters_;
+ friend class art::jit::JitCompileTask;
+
DISALLOW_COPY_AND_ASSIGN(Jit);
};
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 0b34688..1ab1c77 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -40,7 +40,7 @@
#include "entrypoints/entrypoint_utils-inl.h"
#include "entrypoints/runtime_asm_entrypoints.h"
#include "gc/accounting/bitmap-inl.h"
-#include "gc/allocator/dlmalloc.h"
+#include "gc/allocator/art-dlmalloc.h"
#include "gc/scoped_gc_critical_section.h"
#include "handle.h"
#include "handle_scope-inl.h"
@@ -127,31 +127,19 @@
DCHECK(entrypoint == OatQuickMethodHeader::FromCodePointer(GetCode())->GetEntryPoint());
instrumentation::Instrumentation* instrum = Runtime::Current()->GetInstrumentation();
for (ArtMethod* m : GetMethods()) {
- // Because `m` might be in the process of being deleted:
- // - Call the dedicated method instead of the more generic UpdateMethodsCode
- // - Check the class status without a full read barrier; use ReadBarrier::IsMarked().
- bool can_set_entrypoint = true;
- if (NeedsClinitCheckBeforeCall(m)) {
- // To avoid resurrecting an unreachable object, we must not use a full read
- // barrier but we do not want to miss updating an entrypoint under common
- // circumstances, i.e. during a GC the class becomes visibly initialized,
- // the method becomes hot, we compile the thunk and want to update the
- // entrypoint while the method's declaring class field still points to the
- // from-space class object with the old status. Therefore we read the
- // declaring class without a read barrier and check if it's already marked.
- // If yes, we check the status of the to-space class object as intended.
- // Otherwise, there is no to-space object and the from-space class object
- // contains the most recent value of the status field; even if this races
- // with another thread doing a read barrier and updating the status, that's
- // no different from a race with a thread that just updates the status.
- // Such race can happen only for the zygote method pre-compilation, as we
- // otherwise compile only thunks for methods of visibly initialized classes.
- ObjPtr<mirror::Class> klass = m->GetDeclaringClass<kWithoutReadBarrier>();
- ObjPtr<mirror::Class> marked = ReadBarrier::IsMarked(klass.Ptr());
- ObjPtr<mirror::Class> checked_klass = (marked != nullptr) ? marked : klass;
- can_set_entrypoint = checked_klass->IsVisiblyInitialized();
- }
- if (can_set_entrypoint) {
+ // Because `m` might be in the process of being deleted,
+ // - use the `ArtMethod::StillNeedsClinitCheckMayBeDead()` to check if
+ // we can update the entrypoint, and
+ // - call `Instrumentation::UpdateNativeMethodsCodeToJitCode` instead of the
+ // more generic function `Instrumentation::UpdateMethodsCode()`.
+ // The `ArtMethod::StillNeedsClinitCheckMayBeDead()` checks the class status
+ // in the to-space object if any even if the method's declaring class points to
+ // the from-space class object. This way we do not miss updating an entrypoint
+ // even under uncommon circumstances, when during a GC the class becomes visibly
+ // initialized, the method becomes hot, we compile the thunk and want to update
+ // the entrypoint while the method's declaring class field still points to the
+ // from-space class object with the old status.
+ if (!m->StillNeedsClinitCheckMayBeDead()) {
instrum->UpdateNativeMethodsCodeToJitCode(m, entrypoint);
}
}
@@ -220,9 +208,10 @@
}
}
- size_t initial_capacity = Runtime::Current()->GetJITOptions()->GetCodeCacheInitialCapacity();
+ Runtime* runtime = Runtime::Current();
+ size_t initial_capacity = runtime->GetJITOptions()->GetCodeCacheInitialCapacity();
// Check whether the provided max capacity in options is below 1GB.
- size_t max_capacity = Runtime::Current()->GetJITOptions()->GetCodeCacheMaxCapacity();
+ size_t max_capacity = runtime->GetJITOptions()->GetCodeCacheMaxCapacity();
// We need to have 32 bit offsets from method headers in code cache which point to things
// in the data cache. If the maps are more than 4G apart, having multiple maps wouldn't work.
// Ensure we're below 1 GB to be safe.
@@ -244,6 +233,11 @@
return nullptr;
}
+ if (region.HasCodeMapping()) {
+ const MemMap* exec_pages = region.GetExecPages();
+ runtime->AddGeneratedCodeRange(exec_pages->Begin(), exec_pages->Size());
+ }
+
std::unique_ptr<JitCodeCache> jit_code_cache(new JitCodeCache());
if (is_zygote) {
// Zygote should never collect code to share the memory with the children.
@@ -278,7 +272,16 @@
histogram_profiling_info_memory_use_("Memory used for profiling info", 16) {
}
-JitCodeCache::~JitCodeCache() {}
+JitCodeCache::~JitCodeCache() {
+ if (private_region_.HasCodeMapping()) {
+ const MemMap* exec_pages = private_region_.GetExecPages();
+ Runtime::Current()->RemoveGeneratedCodeRange(exec_pages->Begin(), exec_pages->Size());
+ }
+ if (shared_region_.HasCodeMapping()) {
+ const MemMap* exec_pages = shared_region_.GetExecPages();
+ Runtime::Current()->RemoveGeneratedCodeRange(exec_pages->Begin(), exec_pages->Size());
+ }
+}
bool JitCodeCache::PrivateRegionContainsPc(const void* ptr) const {
return private_region_.IsInExecSpace(ptr);
@@ -289,7 +292,9 @@
}
bool JitCodeCache::ContainsMethod(ArtMethod* method) {
- MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ MutexLock mu(self, *Locks::jit_lock_);
if (UNLIKELY(method->IsNative())) {
auto it = jni_stubs_map_.find(JniStubKey(method));
if (it != jni_stubs_map_.end() &&
@@ -312,7 +317,9 @@
const void* JitCodeCache::GetJniStubCode(ArtMethod* method) {
DCHECK(method->IsNative());
- MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ MutexLock mu(self, *Locks::jit_lock_);
auto it = jni_stubs_map_.find(JniStubKey(method));
if (it != jni_stubs_map_.end()) {
JniStubData& data = it->second;
@@ -324,12 +331,14 @@
}
const void* JitCodeCache::GetSavedEntryPointOfPreCompiledMethod(ArtMethod* method) {
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
if (method->IsPreCompiled()) {
const void* code_ptr = nullptr;
if (method->GetDeclaringClass()->IsBootStrapClassLoaded()) {
code_ptr = zygote_map_.GetCodeFor(method);
} else {
- MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ MutexLock mu(self, *Locks::jit_lock_);
auto it = saved_compiled_methods_map_.find(method);
if (it != saved_compiled_methods_map_.end()) {
code_ptr = it->second;
@@ -353,12 +362,12 @@
}
static uintptr_t FromCodeToAllocation(const void* code) {
- size_t alignment = GetInstructionSetAlignment(kRuntimeISA);
+ size_t alignment = GetInstructionSetCodeAlignment(kRuntimeISA);
return reinterpret_cast<uintptr_t>(code) - RoundUp(sizeof(OatQuickMethodHeader), alignment);
}
static const void* FromAllocationToCode(const uint8_t* alloc) {
- size_t alignment = GetInstructionSetAlignment(kRuntimeISA);
+ size_t alignment = GetInstructionSetCodeAlignment(kRuntimeISA);
return reinterpret_cast<const void*>(alloc + RoundUp(sizeof(OatQuickMethodHeader), alignment));
}
@@ -400,7 +409,9 @@
}
void JitCodeCache::SweepRootTables(IsMarkedVisitor* visitor) {
- MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ MutexLock mu(self, *Locks::jit_lock_);
for (const auto& entry : method_code_map_) {
uint32_t number_of_roots = 0;
const uint8_t* root_table = GetRootTable(entry.first, &number_of_roots);
@@ -422,7 +433,6 @@
// TODO: Do not use IsMarked for j.l.Class, and adjust once we move this method
// out of the weak access/creation pause. b/32167580
if (new_object != nullptr && new_object != object) {
- DCHECK(new_object->IsString());
roots[i] = GcRoot<mirror::Object>(new_object);
}
} else {
@@ -506,6 +516,7 @@
void JitCodeCache::RemoveMethodsIn(Thread* self, const LinearAlloc& alloc) {
ScopedTrace trace(__PRETTY_FUNCTION__);
+ ScopedDebugDisallowReadBarriers sddrb(self);
// We use a set to first collect all method_headers whose code need to be
// removed. We need to free the underlying code after we remove CHA dependencies
// for entries in this set. And it's more efficient to iterate through
@@ -560,7 +571,7 @@
}
bool JitCodeCache::IsWeakAccessEnabled(Thread* self) const {
- return kUseReadBarrier
+ return gUseReadBarrier
? self->GetWeakRefAccessEnabled()
: is_weak_access_enabled_.load(std::memory_order_seq_cst);
}
@@ -583,13 +594,13 @@
}
void JitCodeCache::AllowInlineCacheAccess() {
- DCHECK(!kUseReadBarrier);
+ DCHECK(!gUseReadBarrier);
is_weak_access_enabled_.store(true, std::memory_order_seq_cst);
BroadcastForInlineCacheAccess();
}
void JitCodeCache::DisallowInlineCacheAccess() {
- DCHECK(!kUseReadBarrier);
+ DCHECK(!gUseReadBarrier);
is_weak_access_enabled_.store(false, std::memory_order_seq_cst);
}
@@ -700,32 +711,37 @@
// compiled code is considered invalidated by some class linking, but below we still make the
// compiled code valid for the method. Need cha_lock_ for checking all single-implementation
// flags and register dependencies.
- MutexLock cha_mu(self, *Locks::cha_lock_);
- bool single_impl_still_valid = true;
- for (ArtMethod* single_impl : cha_single_implementation_list) {
- if (!single_impl->HasSingleImplementation()) {
- // Simply discard the compiled code. Clear the counter so that it may be recompiled later.
- // Hopefully the class hierarchy will be more stable when compilation is retried.
- single_impl_still_valid = false;
- ClearMethodCounter(method, /*was_warm=*/ false);
- break;
+ {
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ MutexLock cha_mu(self, *Locks::cha_lock_);
+ bool single_impl_still_valid = true;
+ for (ArtMethod* single_impl : cha_single_implementation_list) {
+ if (!single_impl->HasSingleImplementation()) {
+ // Simply discard the compiled code. Clear the counter so that it may be recompiled later.
+ // Hopefully the class hierarchy will be more stable when compilation is retried.
+ single_impl_still_valid = false;
+ ClearMethodCounter(method, /*was_warm=*/ false);
+ break;
+ }
+ }
+
+ // Discard the code if any single-implementation assumptions are now invalid.
+ if (UNLIKELY(!single_impl_still_valid)) {
+ VLOG(jit) << "JIT discarded jitted code due to invalid single-implementation assumptions.";
+ return false;
+ }
+ DCHECK(cha_single_implementation_list.empty() || !Runtime::Current()->IsJavaDebuggable())
+ << "Should not be using cha on debuggable apps/runs!";
+
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ for (ArtMethod* single_impl : cha_single_implementation_list) {
+ class_linker->GetClassHierarchyAnalysis()->AddDependency(
+ single_impl, method, method_header);
}
}
- // Discard the code if any single-implementation assumptions are now invalid.
- if (UNLIKELY(!single_impl_still_valid)) {
- VLOG(jit) << "JIT discarded jitted code due to invalid single-implementation assumptions.";
- return false;
- }
- DCHECK(cha_single_implementation_list.empty() || !Runtime::Current()->IsJavaDebuggable())
- << "Should not be using cha on debuggable apps/runs!";
-
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- for (ArtMethod* single_impl : cha_single_implementation_list) {
- class_linker->GetClassHierarchyAnalysis()->AddDependency(single_impl, method, method_header);
- }
-
if (UNLIKELY(method->IsNative())) {
+ ScopedDebugDisallowReadBarriers sddrb(self);
auto it = jni_stubs_map_.find(JniStubKey(method));
DCHECK(it != jni_stubs_map_.end())
<< "Entry inserted in NotifyCompilationOf() should be alive.";
@@ -736,14 +752,17 @@
data->UpdateEntryPoints(method_header->GetEntryPoint());
} else {
if (method->IsPreCompiled() && IsSharedRegion(*region)) {
+ ScopedDebugDisallowReadBarriers sddrb(self);
zygote_map_.Put(code_ptr, method);
} else {
+ ScopedDebugDisallowReadBarriers sddrb(self);
method_code_map_.Put(code_ptr, method);
}
if (compilation_kind == CompilationKind::kOsr) {
+ ScopedDebugDisallowReadBarriers sddrb(self);
osr_code_map_.Put(method, code_ptr);
- } else if (NeedsClinitCheckBeforeCall(method) &&
- !method->GetDeclaringClass()->IsVisiblyInitialized()) {
+ } else if (method->StillNeedsClinitCheck()) {
+ ScopedDebugDisallowReadBarriers sddrb(self);
// This situation currently only occurs in the jit-zygote mode.
DCHECK(!garbage_collect_code_);
DCHECK(method->IsPreCompiled());
@@ -784,7 +803,9 @@
// This function is used only for testing and only with non-native methods.
CHECK(!method->IsNative());
- MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ MutexLock mu(self, *Locks::jit_lock_);
bool osr = osr_code_map_.find(method) != osr_code_map_.end();
bool in_cache = RemoveMethodLocked(method, release_memory);
@@ -853,7 +874,9 @@
// any cached information it has on the method. All threads must be suspended before calling this
// method. The compiled code for the method (if there is any) must not be in any threads call stack.
void JitCodeCache::NotifyMethodRedefined(ArtMethod* method) {
- MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ MutexLock mu(self, *Locks::jit_lock_);
RemoveMethodLocked(method, /* release_memory= */ true);
}
@@ -864,7 +887,9 @@
// shouldn't be used since it is no longer logically in the jit code cache.
// TODO We should add DCHECKS that validate that the JIT is paused when this method is entered.
void JitCodeCache::MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method) {
- MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ MutexLock mu(self, *Locks::jit_lock_);
if (old_method->IsNative()) {
// Update methods in jni_stubs_map_.
for (auto& entry : jni_stubs_map_) {
@@ -891,11 +916,14 @@
// Check that none of our methods have an entrypoint in the zygote exec
// space (this should be taken care of by
// ClassLinker::UpdateEntryPointsClassVisitor.
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
{
- MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ MutexLock mu(self, *Locks::jit_lock_);
if (kIsDebugBuild) {
- for (const auto& it : method_code_map_) {
- ArtMethod* method = it.second;
+ // TODO: Check `jni_stubs_map_`?
+ for (const auto& entry : method_code_map_) {
+ ArtMethod* method = entry.second;
DCHECK(!method->IsPreCompiled());
DCHECK(!IsInZygoteExecSpace(method->GetEntryPointFromQuickCompiledCode()));
}
@@ -1156,6 +1184,7 @@
// Start polling the liveness of compiled code to prepare for the next full collection.
if (next_collection_will_be_full) {
+ ScopedDebugDisallowReadBarriers sddrb(self);
for (auto it : profiling_infos_) {
it.second->ResetCounter();
}
@@ -1187,6 +1216,7 @@
void JitCodeCache::RemoveUnmarkedCode(Thread* self) {
ScopedTrace trace(__FUNCTION__);
+ ScopedDebugDisallowReadBarriers sddrb(self);
std::unordered_set<OatQuickMethodHeader*> method_headers;
{
MutexLock mu(self, *Locks::jit_lock_);
@@ -1234,6 +1264,7 @@
}
void JitCodeCache::RemoveMethodBeingCompiled(ArtMethod* method, CompilationKind kind) {
+ ScopedDebugDisallowReadBarriers sddrb(Thread::Current());
DCHECK(IsMethodBeingCompiled(method, kind));
switch (kind) {
case CompilationKind::kOsr:
@@ -1249,6 +1280,7 @@
}
void JitCodeCache::AddMethodBeingCompiled(ArtMethod* method, CompilationKind kind) {
+ ScopedDebugDisallowReadBarriers sddrb(Thread::Current());
DCHECK(!IsMethodBeingCompiled(method, kind));
switch (kind) {
case CompilationKind::kOsr:
@@ -1264,6 +1296,7 @@
}
bool JitCodeCache::IsMethodBeingCompiled(ArtMethod* method, CompilationKind kind) {
+ ScopedDebugDisallowReadBarriers sddrb(Thread::Current());
switch (kind) {
case CompilationKind::kOsr:
return ContainsElement(current_osr_compilations_, method);
@@ -1275,12 +1308,14 @@
}
bool JitCodeCache::IsMethodBeingCompiled(ArtMethod* method) {
+ ScopedDebugDisallowReadBarriers sddrb(Thread::Current());
return ContainsElement(current_optimized_compilations_, method) ||
ContainsElement(current_osr_compilations_, method) ||
ContainsElement(current_baseline_compilations_, method);
}
ProfilingInfo* JitCodeCache::GetProfilingInfo(ArtMethod* method, Thread* self) {
+ ScopedDebugDisallowReadBarriers sddrb(self);
MutexLock mu(self, *Locks::jit_lock_);
DCHECK(IsMethodBeingCompiled(method))
<< "GetProfilingInfo should only be called when the method is being compiled";
@@ -1292,6 +1327,7 @@
}
void JitCodeCache::ResetHotnessCounter(ArtMethod* method, Thread* self) {
+ ScopedDebugDisallowReadBarriers sddrb(self);
MutexLock mu(self, *Locks::jit_lock_);
auto it = profiling_infos_.find(method);
DCHECK(it != profiling_infos_.end());
@@ -1302,6 +1338,7 @@
void JitCodeCache::DoCollection(Thread* self, bool collect_profiling_info) {
ScopedTrace trace(__FUNCTION__);
{
+ ScopedDebugDisallowReadBarriers sddrb(self);
MutexLock mu(self, *Locks::jit_lock_);
// Update to interpreter the methods that have baseline entrypoints and whose baseline
@@ -1390,7 +1427,9 @@
CHECK(method != nullptr);
}
- MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ MutexLock mu(self, *Locks::jit_lock_);
OatQuickMethodHeader* method_header = nullptr;
ArtMethod* found_method = nullptr; // Only for DCHECK(), not for JNI stubs.
if (method != nullptr && UNLIKELY(method->IsNative())) {
@@ -1445,7 +1484,9 @@
}
OatQuickMethodHeader* JitCodeCache::LookupOsrMethodHeader(ArtMethod* method) {
- MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ MutexLock mu(self, *Locks::jit_lock_);
auto it = osr_code_map_.find(method);
if (it == osr_code_map_.end()) {
return nullptr;
@@ -1471,9 +1512,10 @@
return info;
}
-ProfilingInfo* JitCodeCache::AddProfilingInfoInternal(Thread* self ATTRIBUTE_UNUSED,
+ProfilingInfo* JitCodeCache::AddProfilingInfoInternal(Thread* self,
ArtMethod* method,
const std::vector<uint32_t>& entries) {
+ ScopedDebugDisallowReadBarriers sddrb(self);
// Check whether some other thread has concurrently created it.
auto it = profiling_infos_.find(method);
if (it != profiling_infos_.end()) {
@@ -1506,11 +1548,14 @@
std::vector<ProfileMethodInfo>& methods) {
Thread* self = Thread::Current();
WaitUntilInlineCacheAccessible(self);
+ // TODO: Avoid read barriers for potentially dead methods.
+ // ScopedDebugDisallowReadBarriers sddrb(self);
MutexLock mu(self, *Locks::jit_lock_);
ScopedTrace trace(__FUNCTION__);
- for (auto it : profiling_infos_) {
- ProfilingInfo* info = it.second;
- ArtMethod* method = info->GetMethod();
+ for (const auto& entry : profiling_infos_) {
+ ArtMethod* method = entry.first;
+ ProfilingInfo* info = entry.second;
+ DCHECK_EQ(method, info->GetMethod());
const DexFile* dex_file = method->GetDexFile();
const std::string base_location = DexFileLoader::GetBaseLocation(dex_file->GetLocation());
if (!ContainsElement(dex_base_locations, base_location)) {
@@ -1590,14 +1635,41 @@
}
bool JitCodeCache::IsOsrCompiled(ArtMethod* method) {
- MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ MutexLock mu(self, *Locks::jit_lock_);
return osr_code_map_.find(method) != osr_code_map_.end();
}
+void JitCodeCache::VisitRoots(RootVisitor* visitor) {
+ Thread* self = Thread::Current();
+ gc::Heap* const heap = Runtime::Current()->GetHeap();
+ if (heap->CurrentCollectorType() != gc::CollectorType::kCollectorTypeCMC
+ || !heap->MarkCompactCollector()->IsCompacting(self)) {
+ MutexLock mu(self, *Locks::jit_lock_);
+ UnbufferedRootVisitor root_visitor(visitor, RootInfo(kRootStickyClass));
+ for (ArtMethod* method : current_optimized_compilations_) {
+ method->VisitRoots(root_visitor, kRuntimePointerSize);
+ }
+ for (ArtMethod* method : current_baseline_compilations_) {
+ method->VisitRoots(root_visitor, kRuntimePointerSize);
+ }
+ for (ArtMethod* method : current_osr_compilations_) {
+ method->VisitRoots(root_visitor, kRuntimePointerSize);
+ }
+ }
+}
+
bool JitCodeCache::NotifyCompilationOf(ArtMethod* method,
Thread* self,
CompilationKind compilation_kind,
bool prejit) {
+ if (kIsDebugBuild) {
+ MutexLock mu(self, *Locks::jit_lock_);
+ // Note: the compilation kind may have been adjusted after what was passed initially.
+ // We really just want to check that the method is indeed being compiled.
+ CHECK(IsMethodBeingCompiled(method));
+ }
const void* existing_entry_point = method->GetEntryPointFromQuickCompiledCode();
if (compilation_kind != CompilationKind::kOsr && ContainsPc(existing_entry_point)) {
OatQuickMethodHeader* method_header =
@@ -1612,7 +1684,7 @@
}
}
- if (NeedsClinitCheckBeforeCall(method) && !prejit) {
+ if (method->NeedsClinitCheckBeforeCall() && !prejit) {
// We do not need a synchronization barrier for checking the visibly initialized status
// or checking the initialized status just for requesting visible initialization.
ClassStatus status = method->GetDeclaringClass()
@@ -1635,6 +1707,7 @@
}
}
+ ScopedDebugDisallowReadBarriers sddrb(self);
if (compilation_kind == CompilationKind::kOsr) {
MutexLock mu(self, *Locks::jit_lock_);
if (osr_code_map_.find(method) != osr_code_map_.end()) {
@@ -1686,16 +1759,12 @@
}
}
}
- MutexLock mu(self, *Locks::jit_lock_);
- if (IsMethodBeingCompiled(method, compilation_kind)) {
- return false;
- }
- AddMethodBeingCompiled(method, compilation_kind);
- return true;
}
+ return true;
}
ProfilingInfo* JitCodeCache::NotifyCompilerUse(ArtMethod* method, Thread* self) {
+ ScopedDebugDisallowReadBarriers sddrb(self);
MutexLock mu(self, *Locks::jit_lock_);
auto it = profiling_infos_.find(method);
if (it == profiling_infos_.end()) {
@@ -1709,16 +1778,16 @@
}
void JitCodeCache::DoneCompilerUse(ArtMethod* method, Thread* self) {
+ ScopedDebugDisallowReadBarriers sddrb(self);
MutexLock mu(self, *Locks::jit_lock_);
auto it = profiling_infos_.find(method);
DCHECK(it != profiling_infos_.end());
it->second->DecrementInlineUse();
}
-void JitCodeCache::DoneCompiling(ArtMethod* method,
- Thread* self,
- CompilationKind compilation_kind) {
+void JitCodeCache::DoneCompiling(ArtMethod* method, Thread* self) {
DCHECK_EQ(Thread::Current(), self);
+ ScopedDebugDisallowReadBarriers sddrb(self);
MutexLock mu(self, *Locks::jit_lock_);
if (UNLIKELY(method->IsNative())) {
auto it = jni_stubs_map_.find(JniStubKey(method));
@@ -1729,25 +1798,40 @@
// Failed to compile; the JNI compiler never fails, but the cache may be full.
jni_stubs_map_.erase(it); // Remove the entry added in NotifyCompilationOf().
} // else Commit() updated entrypoints of all methods in the JniStubData.
- } else {
- RemoveMethodBeingCompiled(method, compilation_kind);
}
}
void JitCodeCache::InvalidateAllCompiledCode() {
- art::MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ art::MutexLock mu(self, *Locks::jit_lock_);
VLOG(jit) << "Invalidating all compiled code";
- ClassLinker* linker = Runtime::Current()->GetClassLinker();
- for (auto it : method_code_map_) {
- ArtMethod* meth = it.second;
+ Runtime* runtime = Runtime::Current();
+ ClassLinker* linker = runtime->GetClassLinker();
+ instrumentation::Instrumentation* instr = runtime->GetInstrumentation();
+ // TODO: Clear `jni_stubs_map_`?
+ for (const auto& entry : method_code_map_) {
+ ArtMethod* meth = entry.second;
// We were compiled, so we must be warm.
ClearMethodCounter(meth, /*was_warm=*/true);
- if (meth->IsObsolete()) {
+ if (UNLIKELY(meth->IsObsolete())) {
linker->SetEntryPointsForObsoleteMethod(meth);
} else {
- Runtime::Current()->GetInstrumentation()->InitializeMethodsCode(meth, /*aot_code=*/ nullptr);
+ instr->InitializeMethodsCode(meth, /*aot_code=*/ nullptr);
}
}
+
+ for (const auto& entry : zygote_map_) {
+ if (entry.method == nullptr) {
+ continue;
+ }
+ if (entry.method->IsPreCompiled()) {
+ entry.method->ClearPreCompiled();
+ }
+ Runtime::Current()->GetInstrumentation()->InitializeMethodsCode(entry.method,
+ /*aot_code=*/nullptr);
+ }
+
saved_compiled_methods_map_.clear();
osr_code_map_.clear();
}
@@ -1765,7 +1849,9 @@
Runtime::Current()->GetInstrumentation()->InitializeMethodsCode(method, /*aot_code=*/ nullptr);
ClearMethodCounter(method, /*was_warm=*/ true);
} else {
- MutexLock mu(Thread::Current(), *Locks::jit_lock_);
+ Thread* self = Thread::Current();
+ ScopedDebugDisallowReadBarriers sddrb(self);
+ MutexLock mu(self, *Locks::jit_lock_);
auto it = osr_code_map_.find(method);
if (it != osr_code_map_.end() && OatQuickMethodHeader::FromCodePointer(it->second) == header) {
// Remove the OSR method, to avoid using it again.
@@ -1817,7 +1903,8 @@
// We do this now and not in Jit::PostForkChildAction, as system server calls
// JitCodeCache::PostForkChildAction first, and then does some code loading
// that may result in new JIT tasks that we want to keep.
- ThreadPool* pool = Runtime::Current()->GetJit()->GetThreadPool();
+ Runtime* runtime = Runtime::Current();
+ ThreadPool* pool = runtime->GetJit()->GetThreadPool();
if (pool != nullptr) {
pool->RemoveAllTasks(self);
}
@@ -1828,7 +1915,7 @@
// to write to them.
shared_region_.ResetWritableMappings();
- if (is_zygote || Runtime::Current()->IsSafeMode()) {
+ if (is_zygote || runtime->IsSafeMode()) {
// Don't create a private region for a child zygote. Regions are usually map shared
// (to satisfy dual-view), and we don't want children of a child zygote to inherit it.
return;
@@ -1843,8 +1930,8 @@
histogram_code_memory_use_.Reset();
histogram_profiling_info_memory_use_.Reset();
- size_t initial_capacity = Runtime::Current()->GetJITOptions()->GetCodeCacheInitialCapacity();
- size_t max_capacity = Runtime::Current()->GetJITOptions()->GetCodeCacheMaxCapacity();
+ size_t initial_capacity = runtime->GetJITOptions()->GetCodeCacheInitialCapacity();
+ size_t max_capacity = runtime->GetJITOptions()->GetCodeCacheMaxCapacity();
std::string error_msg;
if (!private_region_.Initialize(initial_capacity,
max_capacity,
@@ -1853,6 +1940,10 @@
&error_msg)) {
LOG(WARNING) << "Could not create private region after zygote fork: " << error_msg;
}
+ if (private_region_.HasCodeMapping()) {
+ const MemMap* exec_pages = private_region_.GetExecPages();
+ runtime->AddGeneratedCodeRange(exec_pages->Begin(), exec_pages->Size());
+ }
}
JitMemoryRegion* JitCodeCache::GetCurrentRegion() {
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index fb861a4..3112d27 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -215,7 +215,7 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::jit_lock_);
- void DoneCompiling(ArtMethod* method, Thread* self, CompilationKind compilation_kind)
+ void DoneCompiling(ArtMethod* method, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::jit_lock_);
@@ -403,6 +403,20 @@
ProfilingInfo* GetProfilingInfo(ArtMethod* method, Thread* self);
void ResetHotnessCounter(ArtMethod* method, Thread* self);
+ void VisitRoots(RootVisitor* visitor);
+
+ // Return whether `method` is being compiled with the given mode.
+ bool IsMethodBeingCompiled(ArtMethod* method, CompilationKind compilation_kind)
+ REQUIRES(Locks::jit_lock_);
+
+ // Remove `method` from the list of methods meing compiled with the given mode.
+ void RemoveMethodBeingCompiled(ArtMethod* method, CompilationKind compilation_kind)
+ REQUIRES(Locks::jit_lock_);
+
+ // Record that `method` is being compiled with the given mode.
+ void AddMethodBeingCompiled(ArtMethod* method, CompilationKind compilation_kind)
+ REQUIRES(Locks::jit_lock_);
+
private:
JitCodeCache();
@@ -492,18 +506,6 @@
REQUIRES(!Locks::jit_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- // Record that `method` is being compiled with the given mode.
- void AddMethodBeingCompiled(ArtMethod* method, CompilationKind compilation_kind)
- REQUIRES(Locks::jit_lock_);
-
- // Remove `method` from the list of methods meing compiled with the given mode.
- void RemoveMethodBeingCompiled(ArtMethod* method, CompilationKind compilation_kind)
- REQUIRES(Locks::jit_lock_);
-
- // Return whether `method` is being compiled with the given mode.
- bool IsMethodBeingCompiled(ArtMethod* method, CompilationKind compilation_kind)
- REQUIRES(Locks::jit_lock_);
-
// Return whether `method` is being compiled in any mode.
bool IsMethodBeingCompiled(ArtMethod* method) REQUIRES(Locks::jit_lock_);
@@ -528,6 +530,14 @@
// -------------- Global JIT maps --------------------------------------- //
+ // Note: The methods held in these maps may be dead, so we must ensure that we do not use
+ // read barriers on their declaring classes as that could unnecessarily keep them alive or
+ // crash the GC, depending on the GC phase and particular GC's details. Asserting that we
+ // do not emit read barriers for these methods can be tricky as we're allowed to emit read
+ // barriers for other methods that are known to be alive, such as the method being compiled.
+ // The GC must ensure that methods in these maps are cleaned up with `RemoveMethodsIn()`
+ // before the declaring class memory is freed.
+
// Holds compiled code associated with the shorty for a JNI stub.
SafeMap<JniStubKey, JniStubData> jni_stubs_map_ GUARDED_BY(Locks::jit_lock_);
diff --git a/runtime/jit/jit_load_test.cc b/runtime/jit/jit_load_test.cc
new file mode 100644
index 0000000..4b080a5
--- /dev/null
+++ b/runtime/jit/jit_load_test.cc
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common_runtime_test.h"
+#include "compiler_callbacks.h"
+
+namespace art {
+
+class JitLoadTest : public CommonRuntimeTest {
+ protected:
+ void SetUpRuntimeOptions(RuntimeOptions *options) override {
+ callbacks_.reset();
+ CommonRuntimeTest::SetUpRuntimeOptions(options);
+ options->push_back(std::make_pair("-Xusejit:true", nullptr));
+ }
+};
+
+
+TEST_F(JitLoadTest, JitLoad) {
+ Thread::Current()->TransitionFromSuspendedToRunnable();
+ runtime_->Start();
+ ASSERT_NE(runtime_->GetJit(), nullptr);
+}
+
+} // namespace art
diff --git a/runtime/jit/jit_memory_region.cc b/runtime/jit/jit_memory_region.cc
index 56407f5..410bf70 100644
--- a/runtime/jit/jit_memory_region.cc
+++ b/runtime/jit/jit_memory_region.cc
@@ -27,7 +27,7 @@
#include "base/membarrier.h"
#include "base/memfd.h"
#include "base/systrace.h"
-#include "gc/allocator/dlmalloc.h"
+#include "gc/allocator/art-dlmalloc.h"
#include "jit/jit_scoped_code_cache_write.h"
#include "oat_quick_method_header.h"
#include "palette/palette.h"
@@ -360,7 +360,7 @@
DCHECK(IsInExecSpace(reserved_code.data()));
ScopedCodeCacheWrite scc(*this);
- size_t alignment = GetInstructionSetAlignment(kRuntimeISA);
+ size_t alignment = GetInstructionSetCodeAlignment(kRuntimeISA);
size_t header_size = OatQuickMethodHeader::InstructionAlignedSize();
size_t total_size = header_size + code.size();
@@ -468,7 +468,7 @@
}
const uint8_t* JitMemoryRegion::AllocateCode(size_t size) {
- size_t alignment = GetInstructionSetAlignment(kRuntimeISA);
+ size_t alignment = GetInstructionSetCodeAlignment(kRuntimeISA);
void* result = mspace_memalign(exec_mspace_, alignment, size);
if (UNLIKELY(result == nullptr)) {
return nullptr;
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index cea654f..d746ade 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -136,9 +136,10 @@
{
MutexLock mu(self, wait_lock_);
- const uint64_t end_time = NanoTime() + MsToNs(force_early_first_save
+ const uint64_t sleep_time = MsToNs(force_early_first_save
? options_.GetMinFirstSaveMs()
: options_.GetSaveResolvedClassesDelayMs());
+ const uint64_t end_time = NanoTime() + sleep_time;
while (!Runtime::Current()->GetStartupCompleted()) {
const uint64_t current_time = NanoTime();
if (current_time >= end_time) {
@@ -146,7 +147,7 @@
}
period_condition_.TimedWait(self, NsToMs(end_time - current_time), 0);
}
- total_ms_of_sleep_ += options_.GetSaveResolvedClassesDelayMs();
+ total_ms_of_sleep_ += sleep_time;
}
// Tell the runtime that startup is completed if it has not already been notified.
// TODO: We should use another thread to do this in case the profile saver is not running.
diff --git a/runtime/jni/check_jni.cc b/runtime/jni/check_jni.cc
index 4c7b1aa..eb54f98 100644
--- a/runtime/jni/check_jni.cc
+++ b/runtime/jni/check_jni.cc
@@ -38,6 +38,7 @@
#include "indirect_reference_table-inl.h"
#include "java_vm_ext.h"
#include "jni_internal.h"
+#include "local_reference_table-inl.h"
#include "mirror/class-inl.h"
#include "mirror/field.h"
#include "mirror/method.h"
@@ -56,15 +57,20 @@
// declared as a friend by JniVmExt and JniEnvExt.
inline IndirectReferenceTable* GetIndirectReferenceTable(ScopedObjectAccess& soa,
IndirectRefKind kind) {
- DCHECK_NE(kind, kJniTransitionOrInvalid);
- JNIEnvExt* env = soa.Env();
- IndirectReferenceTable* irt =
- (kind == kLocal) ? &env->locals_
- : ((kind == kGlobal) ? &env->vm_->globals_ : &env->vm_->weak_globals_);
+ DCHECK_NE(kind, kJniTransition);
+ DCHECK_NE(kind, kLocal);
+ JavaVMExt* vm = soa.Env()->GetVm();
+ IndirectReferenceTable* irt = (kind == kGlobal) ? &vm->globals_ : &vm->weak_globals_;
DCHECK_EQ(irt->GetKind(), kind);
return irt;
}
+// This helper cannot be in the anonymous namespace because it needs to be
+// declared as a friend by JniEnvExt.
+inline jni::LocalReferenceTable* GetLocalReferenceTable(ScopedObjectAccess& soa) {
+ return &soa.Env()->locals_;
+}
+
namespace {
using android::base::StringAppendF;
@@ -723,7 +729,7 @@
IndirectRefKind found_kind;
if (expected_kind == kLocal) {
found_kind = IndirectReferenceTable::GetIndirectRefKind(obj);
- if (found_kind == kJniTransitionOrInvalid &&
+ if (found_kind == kJniTransition &&
obj != nullptr &&
self->IsJniTransitionReference(obj)) {
found_kind = kLocal;
@@ -866,13 +872,19 @@
bool expect_null = false;
bool okay = true;
std::string error_msg;
- if (ref_kind == kJniTransitionOrInvalid) {
+ if (ref_kind == kJniTransition) {
if (!soa.Self()->IsJniTransitionReference(java_object)) {
okay = false;
error_msg = "use of invalid jobject";
} else {
obj = soa.Decode<mirror::Object>(java_object);
}
+ } else if (ref_kind == kLocal) {
+ jni::LocalReferenceTable* lrt = GetLocalReferenceTable(soa);
+ okay = lrt->IsValidReference(java_object, &error_msg);
+ if (okay) {
+ obj = lrt->Get(ref);
+ }
} else {
IndirectReferenceTable* irt = GetIndirectReferenceTable(soa, ref_kind);
okay = irt->IsValidReference(java_object, &error_msg);
@@ -881,10 +893,7 @@
// Note: The `IsValidReference()` checks for null but we do not prevent races,
// so the null check below can still fail. Even if it succeeds, another thread
// could delete the global or weak global before it's used by JNI.
- if (ref_kind == kLocal) {
- // Local references do not need a read barrier.
- obj = irt->Get<kWithoutReadBarrier>(ref);
- } else if (ref_kind == kGlobal) {
+ if (ref_kind == kGlobal) {
obj = soa.Env()->GetVm()->DecodeGlobal(ref);
} else {
obj = soa.Env()->GetVm()->DecodeWeakGlobal(soa.Self(), ref);
@@ -2340,6 +2349,7 @@
CallMethodV(__FUNCTION__, env, obj, c, mid, vargs, Primitive::kPrimVoid, kDirect);
}
+ NO_STACK_PROTECTOR
static void CallStaticVoidMethodV(JNIEnv* env, jclass c, jmethodID mid, va_list vargs) {
CallMethodV(__FUNCTION__, env, nullptr, c, mid, vargs, Primitive::kPrimVoid, kStatic);
}
@@ -3304,6 +3314,7 @@
return result;
}
+ NO_STACK_PROTECTOR
static JniValueType CallMethodV(const char* function_name, JNIEnv* env, jobject obj, jclass c,
jmethodID mid, va_list vargs, Primitive::Type type,
InvokeType invoke) {
diff --git a/runtime/jni/java_vm_ext-inl.h b/runtime/jni/java_vm_ext-inl.h
index 29cdf1b..c98a553 100644
--- a/runtime/jni/java_vm_ext-inl.h
+++ b/runtime/jni/java_vm_ext-inl.h
@@ -26,7 +26,7 @@
inline bool JavaVMExt::MayAccessWeakGlobals(Thread* self) const {
DCHECK(self != nullptr);
- return kUseReadBarrier
+ return gUseReadBarrier
? self->GetWeakRefAccessEnabled()
: allow_accessing_weak_globals_.load(std::memory_order_seq_cst);
}
diff --git a/runtime/jni/java_vm_ext.cc b/runtime/jni/java_vm_ext.cc
index f41b6c0..b4e7938 100644
--- a/runtime/jni/java_vm_ext.cc
+++ b/runtime/jni/java_vm_ext.cc
@@ -44,7 +44,6 @@
#include "nativehelper/scoped_local_ref.h"
#include "nativehelper/scoped_utf_chars.h"
#include "nativeloader/native_loader.h"
-#include "object_callbacks.h"
#include "parsed_options.h"
#include "runtime-inl.h"
#include "runtime_options.h"
@@ -53,7 +52,7 @@
#include "thread-inl.h"
#include "thread_list.h"
#include "ti/agent.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -306,6 +305,7 @@
*detail += "No implementation found for ";
*detail += m->PrettyMethod();
*detail += " (tried " + jni_short_name + " and " + jni_long_name + ")";
+ *detail += " - is the library loaded, e.g. System.loadLibrary?";
}
return nullptr;
}
@@ -495,9 +495,7 @@
JII::AttachCurrentThreadAsDaemon
};
-JavaVMExt::JavaVMExt(Runtime* runtime,
- const RuntimeArgumentMap& runtime_options,
- std::string* error_msg)
+JavaVMExt::JavaVMExt(Runtime* runtime, const RuntimeArgumentMap& runtime_options)
: runtime_(runtime),
check_jni_abort_hook_(nullptr),
check_jni_abort_hook_data_(nullptr),
@@ -506,13 +504,10 @@
tracing_enabled_(runtime_options.Exists(RuntimeArgumentMap::JniTrace)
|| VLOG_IS_ON(third_party_jni)),
trace_(runtime_options.GetOrDefault(RuntimeArgumentMap::JniTrace)),
- globals_(kGlobalsMax, kGlobal, IndirectReferenceTable::ResizableCapacity::kNo, error_msg),
+ globals_(kGlobal),
libraries_(new Libraries),
unchecked_functions_(&gJniInvokeInterface),
- weak_globals_(kWeakGlobalsMax,
- kWeakGlobal,
- IndirectReferenceTable::ResizableCapacity::kNo,
- error_msg),
+ weak_globals_(kWeakGlobal),
allow_accessing_weak_globals_(true),
weak_globals_add_condition_("weak globals add condition",
(CHECK(Locks::jni_weak_globals_lock_ != nullptr),
@@ -527,21 +522,23 @@
SetCheckJniEnabled(runtime_options.Exists(RuntimeArgumentMap::CheckJni) || kIsDebugBuild);
}
+bool JavaVMExt::Initialize(std::string* error_msg) {
+ return globals_.Initialize(kGlobalsMax, error_msg) &&
+ weak_globals_.Initialize(kWeakGlobalsMax, error_msg);
+}
+
JavaVMExt::~JavaVMExt() {
UnloadBootNativeLibraries();
}
-// Checking "globals" and "weak_globals" usually requires locks, but we
-// don't need the locks to check for validity when constructing the
-// object. Use NO_THREAD_SAFETY_ANALYSIS for this.
std::unique_ptr<JavaVMExt> JavaVMExt::Create(Runtime* runtime,
const RuntimeArgumentMap& runtime_options,
- std::string* error_msg) NO_THREAD_SAFETY_ANALYSIS {
- std::unique_ptr<JavaVMExt> java_vm(new JavaVMExt(runtime, runtime_options, error_msg));
- if (java_vm && java_vm->globals_.IsValid() && java_vm->weak_globals_.IsValid()) {
- return java_vm;
+ std::string* error_msg) {
+ std::unique_ptr<JavaVMExt> java_vm(new JavaVMExt(runtime, runtime_options));
+ if (!java_vm->Initialize(error_msg)) {
+ return nullptr;
}
- return nullptr;
+ return java_vm;
}
jint JavaVMExt::HandleGetEnv(/*out*/void** env, jint version) {
@@ -698,7 +695,7 @@
std::string error_msg;
{
WriterMutexLock mu(self, *Locks::jni_globals_lock_);
- ref = globals_.Add(kIRTFirstSegment, obj, &error_msg);
+ ref = globals_.Add(obj, &error_msg);
MaybeTraceGlobals();
}
if (UNLIKELY(ref == nullptr)) {
@@ -729,12 +726,12 @@
MutexLock mu(self, *Locks::jni_weak_globals_lock_);
// CMS needs this to block for concurrent reference processing because an object allocated during
// the GC won't be marked and concurrent reference processing would incorrectly clear the JNI weak
- // ref. But CC (kUseReadBarrier == true) doesn't because of the to-space invariant.
- if (!kUseReadBarrier) {
+ // ref. But CC (gUseReadBarrier == true) doesn't because of the to-space invariant.
+ if (!gUseReadBarrier) {
WaitForWeakGlobalsAccess(self);
}
std::string error_msg;
- IndirectRef ref = weak_globals_.Add(kIRTFirstSegment, obj, &error_msg);
+ IndirectRef ref = weak_globals_.Add(obj, &error_msg);
MaybeTraceWeakGlobals();
if (UNLIKELY(ref == nullptr)) {
LOG(FATAL) << error_msg;
@@ -749,7 +746,7 @@
}
{
WriterMutexLock mu(self, *Locks::jni_globals_lock_);
- if (!globals_.Remove(kIRTFirstSegment, obj)) {
+ if (!globals_.Remove(obj)) {
LOG(WARNING) << "JNI WARNING: DeleteGlobalRef(" << obj << ") "
<< "failed to find entry";
}
@@ -763,7 +760,7 @@
return;
}
MutexLock mu(self, *Locks::jni_weak_globals_lock_);
- if (!weak_globals_.Remove(kIRTFirstSegment, obj)) {
+ if (!weak_globals_.Remove(obj)) {
LOG(WARNING) << "JNI WARNING: DeleteWeakGlobalRef(" << obj << ") "
<< "failed to find entry";
}
@@ -809,7 +806,7 @@
}
void JavaVMExt::DisallowNewWeakGlobals() {
- CHECK(!kUseReadBarrier);
+ CHECK(!gUseReadBarrier);
Thread* const self = Thread::Current();
MutexLock mu(self, *Locks::jni_weak_globals_lock_);
// DisallowNewWeakGlobals is only called by CMS during the pause. It is required to have the
@@ -820,7 +817,7 @@
}
void JavaVMExt::AllowNewWeakGlobals() {
- CHECK(!kUseReadBarrier);
+ CHECK(!gUseReadBarrier);
Thread* self = Thread::Current();
MutexLock mu(self, *Locks::jni_weak_globals_lock_);
allow_accessing_weak_globals_.store(true, std::memory_order_seq_cst);
@@ -834,7 +831,7 @@
}
ObjPtr<mirror::Object> JavaVMExt::DecodeGlobal(IndirectRef ref) {
- return globals_.SynchronizedGet(ref);
+ return globals_.Get(ref);
}
void JavaVMExt::UpdateGlobal(Thread* self, IndirectRef ref, ObjPtr<mirror::Object> result) {
@@ -851,7 +848,7 @@
// if MayAccessWeakGlobals is false.
DCHECK_EQ(IndirectReferenceTable::GetIndirectRefKind(ref), kWeakGlobal);
if (LIKELY(MayAccessWeakGlobals(self))) {
- return weak_globals_.SynchronizedGet(ref);
+ return weak_globals_.Get(ref);
}
MutexLock mu(self, *Locks::jni_weak_globals_lock_);
return DecodeWeakGlobalLocked(self, ref);
@@ -876,10 +873,10 @@
return DecodeWeakGlobal(self, ref);
}
// self can be null during a runtime shutdown. ~Runtime()->~ClassLinker()->DecodeWeakGlobal().
- if (!kUseReadBarrier) {
+ if (!gUseReadBarrier) {
DCHECK(allow_accessing_weak_globals_.load(std::memory_order_seq_cst));
}
- return weak_globals_.SynchronizedGet(ref);
+ return weak_globals_.Get(ref);
}
bool JavaVMExt::IsWeakGlobalCleared(Thread* self, IndirectRef ref) {
@@ -945,7 +942,7 @@
ObjPtr<mirror::ClassLoader> loader = soa.Decode<mirror::ClassLoader>(class_loader);
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- if (class_linker->IsBootClassLoader(soa, loader.Ptr())) {
+ if (class_linker->IsBootClassLoader(loader)) {
loader = nullptr;
class_loader = nullptr;
if (caller_class != nullptr) {
@@ -957,7 +954,7 @@
}
}
- class_loader_allocator = class_linker->GetAllocatorForClassLoader(loader.Ptr());
+ class_loader_allocator = class_linker->GetAllocatorForClassLoader(loader);
CHECK(class_loader_allocator != nullptr);
}
if (library != nullptr) {
@@ -1160,7 +1157,7 @@
CHECK(m->IsNative());
ObjPtr<mirror::Class> c = m->GetDeclaringClass();
// If this is a static method, it could be called before the class has been initialized.
- CHECK(c->IsInitializing() || !NeedsClinitCheckBeforeCall(m))
+ CHECK(c->IsInitializing() || !m->NeedsClinitCheckBeforeCall())
<< c->GetStatus() << " " << m->PrettyMethod();
Thread* const self = Thread::Current();
void* native_method = libraries_->FindNativeMethod(self, m, error_msg, can_suspend);
@@ -1172,23 +1169,6 @@
return native_method;
}
-void JavaVMExt::SweepJniWeakGlobals(IsMarkedVisitor* visitor) {
- MutexLock mu(Thread::Current(), *Locks::jni_weak_globals_lock_);
- Runtime* const runtime = Runtime::Current();
- for (auto* entry : weak_globals_) {
- // Need to skip null here to distinguish between null entries and cleared weak ref entries.
- if (!entry->IsNull()) {
- // Since this is called by the GC, we don't need a read barrier.
- mirror::Object* obj = entry->Read<kWithoutReadBarrier>();
- mirror::Object* new_obj = visitor->IsMarked(obj);
- if (new_obj == nullptr) {
- new_obj = runtime->GetClearedJniWeakGlobal();
- }
- *entry = GcRoot<mirror::Object>(new_obj);
- }
- }
-}
-
void JavaVMExt::TrimGlobals() {
WriterMutexLock mu(Thread::Current(), *Locks::jni_globals_lock_);
globals_.Trim();
@@ -1205,12 +1185,14 @@
if (class_loader == nullptr) {
return nullptr;
}
- if (!env->IsInstanceOf(class_loader, WellKnownClasses::dalvik_system_BaseDexClassLoader)) {
+ ScopedObjectAccess soa(env);
+ ObjPtr<mirror::Object> mirror_class_loader = soa.Decode<mirror::Object>(class_loader);
+ if (!mirror_class_loader->InstanceOf(WellKnownClasses::dalvik_system_BaseDexClassLoader.Get())) {
return nullptr;
}
- return reinterpret_cast<jstring>(env->CallObjectMethod(
- class_loader,
- WellKnownClasses::dalvik_system_BaseDexClassLoader_getLdLibraryPath));
+ return soa.AddLocalReference<jstring>(
+ WellKnownClasses::dalvik_system_BaseDexClassLoader_getLdLibraryPath->InvokeVirtual<'L'>(
+ soa.Self(), mirror_class_loader));
}
// JNI Invocation interface.
diff --git a/runtime/jni/java_vm_ext.h b/runtime/jni/java_vm_ext.h
index 08de18b..8c8b1b3 100644
--- a/runtime/jni/java_vm_ext.h
+++ b/runtime/jni/java_vm_ext.h
@@ -165,7 +165,9 @@
void SweepJniWeakGlobals(IsMarkedVisitor* visitor)
REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!Locks::jni_weak_globals_lock_);
+ REQUIRES(!Locks::jni_weak_globals_lock_) {
+ weak_globals_.SweepJniWeakGlobals(visitor);
+ }
ObjPtr<mirror::Object> DecodeGlobal(IndirectRef ref)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -218,9 +220,12 @@
static jstring GetLibrarySearchPath(JNIEnv* env, jobject class_loader);
private:
- // The constructor should not be called directly. It may leave the object in
- // an erroneous state, and the result needs to be checked.
- JavaVMExt(Runtime* runtime, const RuntimeArgumentMap& runtime_options, std::string* error_msg);
+ // The constructor should not be called directly. Use `Create()` that initializes
+ // the new `JavaVMExt` object by calling `Initialize()`.
+ JavaVMExt(Runtime* runtime, const RuntimeArgumentMap& runtime_options);
+
+ // Initialize the `JavaVMExt` object.
+ bool Initialize(std::string* error_msg);
// Return true if self can currently access weak globals.
bool MayAccessWeakGlobals(Thread* self) const REQUIRES_SHARED(Locks::mutator_lock_);
@@ -248,7 +253,6 @@
// Extra diagnostics.
const std::string trace_;
- // Not guarded by globals_lock since we sometimes use SynchronizedGet in Thread::DecodeJObject.
IndirectReferenceTable globals_;
// No lock annotation since UnloadNativeLibraries is called on libraries_ but locks the
@@ -260,10 +264,8 @@
// Since weak_globals_ contain weak roots, be careful not to
// directly access the object references in it. Use Get() with the
- // read barrier enabled.
- // Not guarded by weak_globals_lock since we may use SynchronizedGet in DecodeWeakGlobal.
+ // read barrier enabled or disabled based on the use case.
IndirectReferenceTable weak_globals_;
- // Not guarded by weak_globals_lock since we may use SynchronizedGet in DecodeWeakGlobal.
Atomic<bool> allow_accessing_weak_globals_;
ConditionVariable weak_globals_add_condition_ GUARDED_BY(Locks::jni_weak_globals_lock_);
diff --git a/runtime/jni/jni_env_ext-inl.h b/runtime/jni/jni_env_ext-inl.h
index 7609a9e..0c04192 100644
--- a/runtime/jni/jni_env_ext-inl.h
+++ b/runtime/jni/jni_env_ext-inl.h
@@ -19,6 +19,7 @@
#include "jni_env_ext.h"
+#include "local_reference_table-inl.h"
#include "mirror/object.h"
namespace art {
@@ -49,6 +50,10 @@
return reinterpret_cast<T>(ref);
}
+inline void JNIEnvExt::UpdateLocal(IndirectRef iref, ObjPtr<mirror::Object> obj) {
+ locals_.Update(iref, obj);
+}
+
} // namespace art
#endif // ART_RUNTIME_JNI_JNI_ENV_EXT_INL_H_
diff --git a/runtime/jni/jni_env_ext.cc b/runtime/jni/jni_env_ext.cc
index 4510b37..619e1de 100644
--- a/runtime/jni/jni_env_ext.cc
+++ b/runtime/jni/jni_env_ext.cc
@@ -44,13 +44,6 @@
const JNINativeInterface* JNIEnvExt::table_override_ = nullptr;
-bool JNIEnvExt::CheckLocalsValid(JNIEnvExt* in) NO_THREAD_SAFETY_ANALYSIS {
- if (in == nullptr) {
- return false;
- }
- return in->locals_.IsValid();
-}
-
jint JNIEnvExt::GetEnvHandler(JavaVMExt* vm, /*out*/void** env, jint version) {
UNUSED(vm);
// GetEnv always returns a JNIEnv* for the most current supported JNI version,
@@ -66,18 +59,18 @@
}
JNIEnvExt* JNIEnvExt::Create(Thread* self_in, JavaVMExt* vm_in, std::string* error_msg) {
- std::unique_ptr<JNIEnvExt> ret(new JNIEnvExt(self_in, vm_in, error_msg));
- if (CheckLocalsValid(ret.get())) {
- return ret.release();
+ std::unique_ptr<JNIEnvExt> ret(new JNIEnvExt(self_in, vm_in));
+ if (!ret->Initialize(error_msg)) {
+ return nullptr;
}
- return nullptr;
+ return ret.release();
}
-JNIEnvExt::JNIEnvExt(Thread* self_in, JavaVMExt* vm_in, std::string* error_msg)
+JNIEnvExt::JNIEnvExt(Thread* self_in, JavaVMExt* vm_in)
: self_(self_in),
vm_(vm_in),
- local_ref_cookie_(kIRTFirstSegment),
- locals_(1, kLocal, IndirectReferenceTable::ResizableCapacity::kYes, error_msg),
+ local_ref_cookie_(jni::kLRTFirstSegment),
+ locals_(),
monitors_("monitors", kMonitorsInitial, kMonitorsMax),
critical_(0),
check_jni_(false),
@@ -88,6 +81,10 @@
unchecked_functions_ = GetJniNativeInterface();
}
+bool JNIEnvExt::Initialize(std::string* error_msg) {
+ return locals_.Initialize(/*max_count=*/ 1u, error_msg);
+}
+
void JNIEnvExt::SetFunctionsToRuntimeShutdownFunctions() {
functions = GetRuntimeShutdownNativeInterface();
}
@@ -157,7 +154,7 @@
4 + // local_ref_cookie.
(pointer_size - 4); // Padding.
size_t irt_segment_state_offset =
- IndirectReferenceTable::SegmentStateOffset(pointer_size).Int32Value();
+ jni::LocalReferenceTable::SegmentStateOffset(pointer_size).Int32Value();
return MemberOffset(locals_offset + irt_segment_state_offset);
}
diff --git a/runtime/jni/jni_env_ext.h b/runtime/jni/jni_env_ext.h
index bdde5f8..1f57658 100644
--- a/runtime/jni/jni_env_ext.h
+++ b/runtime/jni/jni_env_ext.h
@@ -21,7 +21,7 @@
#include "base/locks.h"
#include "base/macros.h"
-#include "indirect_reference_table.h"
+#include "local_reference_table.h"
#include "obj_ptr.h"
#include "reference_table.h"
@@ -63,9 +63,8 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::alloc_tracker_lock_);
- void UpdateLocal(IndirectRef iref, ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_) {
- locals_.Update(iref, obj);
- }
+ void UpdateLocal(IndirectRef iref, ObjPtr<mirror::Object> obj)
+ REQUIRES_SHARED(Locks::mutator_lock_);
jobject NewLocalRef(mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_);
void DeleteLocalRef(jobject obj) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -80,13 +79,13 @@
return locals_.Capacity();
}
- IRTSegmentState GetLocalRefCookie() const { return local_ref_cookie_; }
- void SetLocalRefCookie(IRTSegmentState new_cookie) { local_ref_cookie_ = new_cookie; }
+ jni::LRTSegmentState GetLocalRefCookie() const { return local_ref_cookie_; }
+ void SetLocalRefCookie(jni::LRTSegmentState new_cookie) { local_ref_cookie_ = new_cookie; }
- IRTSegmentState GetLocalsSegmentState() const REQUIRES_SHARED(Locks::mutator_lock_) {
+ jni::LRTSegmentState GetLocalsSegmentState() const REQUIRES_SHARED(Locks::mutator_lock_) {
return locals_.GetSegmentState();
}
- void SetLocalSegmentState(IRTSegmentState new_state) REQUIRES_SHARED(Locks::mutator_lock_) {
+ void SetLocalSegmentState(jni::LRTSegmentState new_state) REQUIRES_SHARED(Locks::mutator_lock_) {
locals_.SetSegmentState(new_state);
}
@@ -151,20 +150,18 @@
REQUIRES(!Locks::thread_list_lock_, !Locks::jni_function_table_lock_);
private:
- // Checking "locals" requires the mutator lock, but at creation time we're
- // really only interested in validity, which isn't changing. To avoid grabbing
- // the mutator lock, factored out and tagged with NO_THREAD_SAFETY_ANALYSIS.
- static bool CheckLocalsValid(JNIEnvExt* in) NO_THREAD_SAFETY_ANALYSIS;
-
// Override of function tables. This applies to both default as well as instrumented (CheckJNI)
// function tables.
static const JNINativeInterface* table_override_ GUARDED_BY(Locks::jni_function_table_lock_);
- // The constructor should not be called directly. It may leave the object in an erroneous state,
- // and the result needs to be checked.
- JNIEnvExt(Thread* self, JavaVMExt* vm, std::string* error_msg)
+ // The constructor should not be called directly. Use `Create()` that initializes
+ // the new `JNIEnvExt` object by calling `Initialize()`.
+ JNIEnvExt(Thread* self, JavaVMExt* vm)
REQUIRES(!Locks::jni_function_table_lock_);
+ // Initialize the `JNIEnvExt` object.
+ bool Initialize(std::string* error_msg);
+
// Link to Thread::Current().
Thread* const self_;
@@ -172,15 +169,15 @@
JavaVMExt* const vm_;
// Cookie used when using the local indirect reference table.
- IRTSegmentState local_ref_cookie_;
+ jni::LRTSegmentState local_ref_cookie_;
// JNI local references.
- IndirectReferenceTable locals_ GUARDED_BY(Locks::mutator_lock_);
+ jni::LocalReferenceTable locals_;
// Stack of cookies corresponding to PushLocalFrame/PopLocalFrame calls.
// TODO: to avoid leaks (and bugs), we need to clear this vector on entry (or return)
// to a native method.
- std::vector<IRTSegmentState> stacked_local_ref_cookies_;
+ std::vector<jni::LRTSegmentState> stacked_local_ref_cookies_;
// Entered JNI monitors, for bulk exit on thread detach.
ReferenceTable monitors_;
@@ -211,6 +208,7 @@
friend class Thread;
friend IndirectReferenceTable* GetIndirectReferenceTable(ScopedObjectAccess& soa,
IndirectRefKind kind);
+ friend jni::LocalReferenceTable* GetLocalReferenceTable(ScopedObjectAccess& soa);
friend void ThreadResetFunctionTable(Thread* thread, void* arg);
ART_FRIEND_TEST(JniInternalTest, JNIEnvExtOffsets);
};
@@ -232,7 +230,7 @@
private:
JNIEnvExt* const env_;
- const IRTSegmentState saved_local_ref_cookie_;
+ const jni::LRTSegmentState saved_local_ref_cookie_;
DISALLOW_COPY_AND_ASSIGN(ScopedJniEnvLocalRefState);
};
diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc
index e3153fd..ad2efc5 100644
--- a/runtime/jni/jni_internal.cc
+++ b/runtime/jni/jni_internal.cc
@@ -22,7 +22,7 @@
#include <utility>
#include "art_field-inl.h"
-#include "art_method-inl.h"
+#include "art_method-alloc-inl.h"
#include "base/allocator.h"
#include "base/atomic.h"
#include "base/casts.h"
@@ -37,6 +37,7 @@
#include "dex/dex_file-inl.h"
#include "dex/utf-inl.h"
#include "fault_handler.h"
+#include "handle_scope.h"
#include "hidden_api.h"
#include "gc/accounting/card_table-inl.h"
#include "gc_root.h"
@@ -63,7 +64,7 @@
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -392,8 +393,7 @@
REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* method = soa.Self()->GetCurrentMethod(nullptr);
// If we are running Runtime.nativeLoad, use the overriding ClassLoader it set.
- if (method ==
- jni::DecodeArtMethod<kEnableIndexIds>(WellKnownClasses::java_lang_Runtime_nativeLoad)) {
+ if (method == WellKnownClasses::java_lang_Runtime_nativeLoad) {
return soa.Decode<mirror::ClassLoader>(soa.Self()->GetClassLoaderOverride());
}
// If we have a method, use its ClassLoader for context.
@@ -954,16 +954,15 @@
WellKnownClasses::StringInitToStringFactory(jni::DecodeArtMethod(mid)));
return CallStaticObjectMethodV(env, WellKnownClasses::java_lang_StringFactory, sf_mid, args);
}
- ObjPtr<mirror::Object> result = c->AllocObject(soa.Self());
+ ScopedLocalRef<jobject> result(env, soa.AddLocalReference<jobject>(c->AllocObject(soa.Self())));
if (result == nullptr) {
return nullptr;
}
- jobject local_result = soa.AddLocalReference<jobject>(result);
- CallNonvirtualVoidMethodV(env, local_result, java_class, mid, args);
+ CallNonvirtualVoidMethodV(env, result.get(), java_class, mid, args);
if (soa.Self()->IsExceptionPending()) {
return nullptr;
}
- return local_result;
+ return result.release();
}
static jobject NewObjectA(JNIEnv* env, jclass java_class, jmethodID mid, const jvalue* args) {
@@ -981,16 +980,15 @@
WellKnownClasses::StringInitToStringFactory(jni::DecodeArtMethod(mid)));
return CallStaticObjectMethodA(env, WellKnownClasses::java_lang_StringFactory, sf_mid, args);
}
- ObjPtr<mirror::Object> result = c->AllocObject(soa.Self());
+ ScopedLocalRef<jobject> result(env, soa.AddLocalReference<jobject>(c->AllocObject(soa.Self())));
if (result == nullptr) {
return nullptr;
}
- jobject local_result = soa.AddLocalReference<jobjectArray>(result);
- CallNonvirtualVoidMethodA(env, local_result, java_class, mid, args);
+ CallNonvirtualVoidMethodA(env, result.get(), java_class, mid, args);
if (soa.Self()->IsExceptionPending()) {
return nullptr;
}
- return local_result;
+ return result.release();
}
static jmethodID GetMethodID(JNIEnv* env, jclass java_class, const char* name, const char* sig) {
@@ -1269,8 +1267,7 @@
CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithVarArgs(soa, obj, mid, ap));
- jobject local_result = soa.AddLocalReference<jobject>(result.GetL());
- return local_result;
+ return soa.AddLocalReference<jobject>(result.GetL());
}
static jobject CallNonvirtualObjectMethodV(JNIEnv* env, jobject obj, jclass, jmethodID mid,
@@ -1756,8 +1753,7 @@
CHECK_NON_NULL_ARGUMENT(mid);
ScopedObjectAccess soa(env);
JValue result(InvokeWithVarArgs(soa, nullptr, mid, ap));
- jobject local_result = soa.AddLocalReference<jobject>(result.GetL());
- return local_result;
+ return soa.AddLocalReference<jobject>(result.GetL());
}
static jobject CallStaticObjectMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) {
@@ -1950,6 +1946,7 @@
return InvokeWithJValues(soa, nullptr, mid, args).GetD();
}
+ NO_STACK_PROTECTOR
static void CallStaticVoidMethod(JNIEnv* env, jclass, jmethodID mid, ...) {
va_list ap;
va_start(ap, mid);
@@ -1959,6 +1956,7 @@
InvokeWithVarArgs(soa, nullptr, mid, ap);
}
+ NO_STACK_PROTECTOR
static void CallStaticVoidMethodV(JNIEnv* env, jclass, jmethodID mid, va_list args) {
CHECK_NON_NULL_ARGUMENT_RETURN_VOID(mid);
ScopedObjectAccess soa(env);
@@ -2176,14 +2174,17 @@
if (heap->IsMovableObject(s)) {
StackHandleScope<1> hs(soa.Self());
HandleWrapperObjPtr<mirror::String> h(hs.NewHandleWrapper(&s));
- if (!kUseReadBarrier) {
+ if (!gUseReadBarrier && !gUseUserfaultfd) {
heap->IncrementDisableMovingGC(soa.Self());
} else {
- // For the CC collector, we only need to wait for the thread flip rather
+ // For the CC and CMC collector, we only need to wait for the thread flip rather
// than the whole GC to occur thanks to the to-space invariant.
heap->IncrementDisableThreadFlip(soa.Self());
}
}
+ // Ensure that the string doesn't cause userfaults in case passed on to
+ // the kernel.
+ heap->EnsureObjectUserfaulted(s);
if (is_copy != nullptr) {
*is_copy = JNI_FALSE;
}
@@ -2199,7 +2200,7 @@
gc::Heap* heap = Runtime::Current()->GetHeap();
ObjPtr<mirror::String> s = soa.Decode<mirror::String>(java_string);
if (!s->IsCompressed() && heap->IsMovableObject(s)) {
- if (!kUseReadBarrier) {
+ if (!gUseReadBarrier && !gUseUserfaultfd) {
heap->DecrementDisableMovingGC(soa.Self());
} else {
heap->DecrementDisableThreadFlip(soa.Self());
@@ -2366,16 +2367,18 @@
}
gc::Heap* heap = Runtime::Current()->GetHeap();
if (heap->IsMovableObject(array)) {
- if (!kUseReadBarrier) {
+ if (!gUseReadBarrier && !gUseUserfaultfd) {
heap->IncrementDisableMovingGC(soa.Self());
} else {
- // For the CC collector, we only need to wait for the thread flip rather than the whole GC
- // to occur thanks to the to-space invariant.
+ // For the CC and CMC collector, we only need to wait for the thread flip rather
+ // than the whole GC to occur thanks to the to-space invariant.
heap->IncrementDisableThreadFlip(soa.Self());
}
// Re-decode in case the object moved since IncrementDisableGC waits for GC to complete.
array = soa.Decode<mirror::Array>(java_array);
}
+ // Ensure that the array doesn't cause userfaults in case passed on to the kernel.
+ heap->EnsureObjectUserfaulted(array);
if (is_copy != nullptr) {
*is_copy = JNI_FALSE;
}
@@ -2772,10 +2775,10 @@
jlong address_arg = reinterpret_cast<jlong>(address);
jint capacity_arg = static_cast<jint>(capacity);
- jobject result = env->NewObject(WellKnownClasses::java_nio_DirectByteBuffer,
- WellKnownClasses::java_nio_DirectByteBuffer_init,
- address_arg, capacity_arg);
- return static_cast<JNIEnvExt*>(env)->self_->IsExceptionPending() ? nullptr : result;
+ ScopedObjectAccess soa(env);
+ return soa.AddLocalReference<jobject>(
+ WellKnownClasses::java_nio_DirectByteBuffer_init->NewObject<'J', 'I'>(
+ soa.Self(), address_arg, capacity_arg));
}
static void* GetDirectBufferAddress(JNIEnv* env, jobject java_buffer) {
@@ -2784,14 +2787,16 @@
return nullptr;
}
+ ScopedObjectAccess soa(env);
+ ObjPtr<mirror::Object> buffer = soa.Decode<mirror::Object>(java_buffer);
+
// Return null if |java_buffer| is not a java.nio.Buffer instance.
- if (!IsInstanceOf(env, java_buffer, WellKnownClasses::java_nio_Buffer)) {
+ if (!buffer->InstanceOf(WellKnownClasses::java_nio_Buffer.Get())) {
return nullptr;
}
// Buffer.address is non-null when the |java_buffer| is direct.
- return reinterpret_cast<void*>(env->GetLongField(
- java_buffer, WellKnownClasses::java_nio_Buffer_address));
+ return reinterpret_cast<void*>(WellKnownClasses::java_nio_Buffer_address->GetLong(buffer));
}
static jlong GetDirectBufferCapacity(JNIEnv* env, jobject java_buffer) {
@@ -2799,7 +2804,10 @@
return -1;
}
- if (!IsInstanceOf(env, java_buffer, WellKnownClasses::java_nio_Buffer)) {
+ ScopedObjectAccess soa(env);
+ StackHandleScope<1u> hs(soa.Self());
+ Handle<mirror::Object> buffer = hs.NewHandle(soa.Decode<mirror::Object>(java_buffer));
+ if (!buffer->InstanceOf(WellKnownClasses::java_nio_Buffer.Get())) {
return -1;
}
@@ -2808,16 +2816,18 @@
// We therefore call Buffer.isDirect(). One path that creates such a buffer is
// FileChannel.map() if the file size is zero.
//
- // NB GetDirectBufferAddress() does not need to call Buffer.isDirect() since it is only
+ // NB GetDirectBufferAddress() does not need to call `Buffer.isDirect()` since it is only
// able return a valid address if the Buffer address field is not-null.
- jboolean direct = env->CallBooleanMethod(java_buffer,
- WellKnownClasses::java_nio_Buffer_isDirect);
- if (!direct) {
+ //
+ // Note: We can hit a `StackOverflowError` during the invocation but `Buffer.isDirect()`
+ // implementations should not otherwise throw any exceptions.
+ bool direct = WellKnownClasses::java_nio_Buffer_isDirect->InvokeVirtual<'Z'>(
+ soa.Self(), buffer.Get());
+ if (UNLIKELY(soa.Self()->IsExceptionPending()) || !direct) {
return -1;
}
- return static_cast<jlong>(env->GetIntField(
- java_buffer, WellKnownClasses::java_nio_Buffer_capacity));
+ return static_cast<jlong>(WellKnownClasses::java_nio_Buffer_capacity->GetInt(buffer.Get()));
}
static jobjectRefType GetObjectRefType(JNIEnv* env ATTRIBUTE_UNUSED, jobject java_object) {
@@ -2835,7 +2845,7 @@
return JNIGlobalRefType;
case kWeakGlobal:
return JNIWeakGlobalRefType;
- case kJniTransitionOrInvalid:
+ case kJniTransition:
// Assume value is in a JNI transition frame.
return JNILocalRefType;
}
@@ -2967,7 +2977,7 @@
delete[] reinterpret_cast<uint64_t*>(elements);
} else if (heap->IsMovableObject(array)) {
// Non copy to a movable object must means that we had disabled the moving GC.
- if (!kUseReadBarrier) {
+ if (!gUseReadBarrier && !gUseUserfaultfd) {
heap->DecrementDisableMovingGC(soa.Self());
} else {
heap->DecrementDisableThreadFlip(soa.Self());
diff --git a/runtime/jni/jni_internal_test.cc b/runtime/jni/jni_internal_test.cc
index eca5aec..5abedb9 100644
--- a/runtime/jni/jni_internal_test.cc
+++ b/runtime/jni/jni_internal_test.cc
@@ -21,7 +21,7 @@
#include "art_method-inl.h"
#include "base/mem_map.h"
#include "common_runtime_test.h"
-#include "indirect_reference_table.h"
+#include "local_reference_table.h"
#include "java_vm_ext.h"
#include "jni_env_ext.h"
#include "mirror/string-inl.h"
@@ -2580,25 +2580,23 @@
// by modifying memory.
// The parameters don't really matter here.
std::string error_msg;
- IndirectReferenceTable irt(5,
- IndirectRefKind::kGlobal,
- IndirectReferenceTable::ResizableCapacity::kNo,
- &error_msg);
- ASSERT_TRUE(irt.IsValid()) << error_msg;
- IRTSegmentState old_state = irt.GetSegmentState();
+ jni::LocalReferenceTable lrt;
+ bool success = lrt.Initialize(/*max_count=*/ 5, &error_msg);
+ ASSERT_TRUE(success) << error_msg;
+ jni::LRTSegmentState old_state = lrt.GetSegmentState();
// Write some new state directly. We invert parts of old_state to ensure a new value.
- IRTSegmentState new_state;
+ jni::LRTSegmentState new_state;
new_state.top_index = old_state.top_index ^ 0x07705005;
ASSERT_NE(old_state.top_index, new_state.top_index);
- uint8_t* base = reinterpret_cast<uint8_t*>(&irt);
+ uint8_t* base = reinterpret_cast<uint8_t*>(&lrt);
int32_t segment_state_offset =
- IndirectReferenceTable::SegmentStateOffset(sizeof(void*)).Int32Value();
- *reinterpret_cast<IRTSegmentState*>(base + segment_state_offset) = new_state;
+ jni::LocalReferenceTable::SegmentStateOffset(sizeof(void*)).Int32Value();
+ *reinterpret_cast<jni::LRTSegmentState*>(base + segment_state_offset) = new_state;
// Read and compare.
- EXPECT_EQ(new_state.top_index, irt.GetSegmentState().top_index);
+ EXPECT_EQ(new_state.top_index, lrt.GetSegmentState().top_index);
}
// Test the offset computation of JNIEnvExt offsets. b/26071368.
@@ -2612,7 +2610,7 @@
// hope it to be.
uint32_t segment_state_now =
OFFSETOF_MEMBER(JNIEnvExt, locals_) +
- IndirectReferenceTable::SegmentStateOffset(sizeof(void*)).Uint32Value();
+ jni::LocalReferenceTable::SegmentStateOffset(sizeof(void*)).Uint32Value();
uint32_t segment_state_computed = JNIEnvExt::SegmentStateOffset(sizeof(void*)).Uint32Value();
EXPECT_EQ(segment_state_now, segment_state_computed);
}
diff --git a/runtime/jni/local_reference_table-inl.h b/runtime/jni/local_reference_table-inl.h
new file mode 100644
index 0000000..b65dea7
--- /dev/null
+++ b/runtime/jni/local_reference_table-inl.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_JNI_LOCAL_REFERENCE_TABLE_INL_H_
+#define ART_RUNTIME_JNI_LOCAL_REFERENCE_TABLE_INL_H_
+
+#include "local_reference_table.h"
+
+#include "android-base/stringprintf.h"
+
+#include "base/dumpable.h"
+#include "gc_root-inl.h"
+#include "obj_ptr-inl.h"
+#include "verify_object.h"
+
+namespace art {
+namespace mirror {
+class Object;
+} // namespace mirror
+
+namespace jni {
+
+// Verifies that the indirect table lookup is valid.
+// Returns "false" if something looks bad.
+inline bool LocalReferenceTable::IsValidReference(IndirectRef iref,
+ /*out*/std::string* error_msg) const {
+ DCHECK(iref != nullptr);
+ DCHECK_EQ(GetIndirectRefKind(iref), kLocal);
+ const uint32_t top_index = segment_state_.top_index;
+ uint32_t idx = ExtractIndex(iref);
+ if (UNLIKELY(idx >= top_index)) {
+ *error_msg = android::base::StringPrintf("deleted reference at index %u in a table of size %u",
+ idx,
+ top_index);
+ return false;
+ }
+ if (UNLIKELY(table_[idx].GetReference()->IsNull())) {
+ *error_msg = android::base::StringPrintf("deleted reference at index %u", idx);
+ return false;
+ }
+ uint32_t iref_serial = DecodeSerial(reinterpret_cast<uintptr_t>(iref));
+ uint32_t entry_serial = table_[idx].GetSerial();
+ if (UNLIKELY(iref_serial != entry_serial)) {
+ *error_msg = android::base::StringPrintf("stale reference with serial number %u v. current %u",
+ iref_serial,
+ entry_serial);
+ return false;
+ }
+ return true;
+}
+
+// Make sure that the entry at "idx" is correctly paired with "iref".
+inline bool LocalReferenceTable::CheckEntry(const char* what,
+ IndirectRef iref,
+ uint32_t idx) const {
+ IndirectRef checkRef = ToIndirectRef(idx);
+ if (UNLIKELY(checkRef != iref)) {
+ std::string msg = android::base::StringPrintf(
+ "JNI ERROR (app bug): attempt to %s stale %s %p (should be %p)",
+ what,
+ GetIndirectRefKindString(kLocal),
+ iref,
+ checkRef);
+ AbortIfNoCheckJNI(msg);
+ return false;
+ }
+ return true;
+}
+
+template<ReadBarrierOption kReadBarrierOption>
+inline ObjPtr<mirror::Object> LocalReferenceTable::Get(IndirectRef iref) const {
+ DCHECK_EQ(GetIndirectRefKind(iref), kLocal);
+ uint32_t idx = ExtractIndex(iref);
+ DCHECK_LT(idx, segment_state_.top_index);
+ DCHECK_EQ(DecodeSerial(reinterpret_cast<uintptr_t>(iref)), table_[idx].GetSerial());
+ DCHECK(!table_[idx].GetReference()->IsNull());
+ ObjPtr<mirror::Object> obj = table_[idx].GetReference()->Read<kReadBarrierOption>();
+ VerifyObject(obj);
+ return obj;
+}
+
+inline void LocalReferenceTable::Update(IndirectRef iref, ObjPtr<mirror::Object> obj) {
+ DCHECK_EQ(GetIndirectRefKind(iref), kLocal);
+ uint32_t idx = ExtractIndex(iref);
+ DCHECK_LT(idx, segment_state_.top_index);
+ DCHECK_EQ(DecodeSerial(reinterpret_cast<uintptr_t>(iref)), table_[idx].GetSerial());
+ DCHECK(!table_[idx].GetReference()->IsNull());
+ table_[idx].SetReference(obj);
+}
+
+inline void LrtEntry::Add(ObjPtr<mirror::Object> obj) {
+ ++serial_;
+ if (serial_ == kLRTMaxSerial) {
+ serial_ = 0;
+ }
+ reference_ = GcRoot<mirror::Object>(obj);
+}
+
+inline void LrtEntry::SetReference(ObjPtr<mirror::Object> obj) {
+ DCHECK_LT(serial_, kLRTMaxSerial);
+ reference_ = GcRoot<mirror::Object>(obj);
+}
+
+} // namespace jni
+} // namespace art
+
+#endif // ART_RUNTIME_JNI_LOCAL_REFERENCE_TABLE_INL_H_
diff --git a/runtime/jni/local_reference_table.cc b/runtime/jni/local_reference_table.cc
new file mode 100644
index 0000000..6cbbde7
--- /dev/null
+++ b/runtime/jni/local_reference_table.cc
@@ -0,0 +1,564 @@
+/*
+ * Copyright (C) 2022 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 "local_reference_table-inl.h"
+
+#include "base/globals.h"
+#include "base/mutator_locked_dumpable.h"
+#include "base/systrace.h"
+#include "base/utils.h"
+#include "indirect_reference_table.h"
+#include "jni/java_vm_ext.h"
+#include "jni/jni_internal.h"
+#include "mirror/object-inl.h"
+#include "nth_caller_visitor.h"
+#include "reference_table.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread.h"
+
+#include <cstdlib>
+
+namespace art {
+namespace jni {
+
+static constexpr bool kDumpStackOnNonLocalReference = false;
+static constexpr bool kDebugLRT = false;
+
+// Maximum table size we allow.
+static constexpr size_t kMaxTableSizeInBytes = 128 * MB;
+
+void LocalReferenceTable::AbortIfNoCheckJNI(const std::string& msg) {
+ // If -Xcheck:jni is on, it'll give a more detailed error before aborting.
+ JavaVMExt* vm = Runtime::Current()->GetJavaVM();
+ if (!vm->IsCheckJniEnabled()) {
+ // Otherwise, we want to abort rather than hand back a bad reference.
+ LOG(FATAL) << msg;
+ } else {
+ LOG(ERROR) << msg;
+ }
+}
+
+// Mmap an "indirect ref table region. Table_bytes is a multiple of a page size.
+static inline MemMap NewLRTMap(size_t table_bytes, std::string* error_msg) {
+ MemMap result = MemMap::MapAnonymous("local ref table",
+ table_bytes,
+ PROT_READ | PROT_WRITE,
+ /*low_4gb=*/ false,
+ error_msg);
+ if (!result.IsValid() && error_msg->empty()) {
+ *error_msg = "Unable to map memory for indirect ref table";
+ }
+ return result;
+}
+
+SmallLrtAllocator::SmallLrtAllocator()
+ : small_lrt_freelist_(nullptr), lock_("Small LRT table lock", LockLevel::kGenericBottomLock) {
+}
+
+// Allocate a LRT table for kSmallLrtEntries.
+LrtEntry* SmallLrtAllocator::Allocate(std::string* error_msg) {
+ MutexLock lock(Thread::Current(), lock_);
+ if (small_lrt_freelist_ == nullptr) {
+ // Refill.
+ MemMap map = NewLRTMap(kPageSize, error_msg);
+ if (map.IsValid()) {
+ small_lrt_freelist_ = reinterpret_cast<LrtEntry*>(map.Begin());
+ for (uint8_t* p = map.Begin(); p + kInitialLrtBytes < map.End(); p += kInitialLrtBytes) {
+ *reinterpret_cast<LrtEntry**>(p) = reinterpret_cast<LrtEntry*>(p + kInitialLrtBytes);
+ }
+ shared_lrt_maps_.emplace_back(std::move(map));
+ }
+ }
+ if (small_lrt_freelist_ == nullptr) {
+ return nullptr;
+ }
+ LrtEntry* result = small_lrt_freelist_;
+ small_lrt_freelist_ = *reinterpret_cast<LrtEntry**>(small_lrt_freelist_);
+ // Clear pointer in first entry.
+ new(result) LrtEntry();
+ return result;
+}
+
+void SmallLrtAllocator::Deallocate(LrtEntry* unneeded) {
+ MutexLock lock(Thread::Current(), lock_);
+ *reinterpret_cast<LrtEntry**>(unneeded) = small_lrt_freelist_;
+ small_lrt_freelist_ = unneeded;
+}
+
+LocalReferenceTable::LocalReferenceTable()
+ : segment_state_(kLRTFirstSegment),
+ table_(nullptr),
+ max_entries_(0u),
+ current_num_holes_(0) {
+}
+
+bool LocalReferenceTable::Initialize(size_t max_count, std::string* error_msg) {
+ CHECK(error_msg != nullptr);
+
+ // Overflow and maximum check.
+ CHECK_LE(max_count, kMaxTableSizeInBytes / sizeof(LrtEntry));
+
+ if (max_count <= kSmallLrtEntries) {
+ table_ = Runtime::Current()->GetSmallLrtAllocator()->Allocate(error_msg);
+ if (table_ != nullptr) {
+ max_entries_ = kSmallLrtEntries;
+ // table_mem_map_ remains invalid.
+ }
+ }
+ if (table_ == nullptr) {
+ const size_t table_bytes = RoundUp(max_count * sizeof(LrtEntry), kPageSize);
+ table_mem_map_ = NewLRTMap(table_bytes, error_msg);
+ if (!table_mem_map_.IsValid()) {
+ DCHECK(!error_msg->empty());
+ return false;
+ }
+
+ table_ = reinterpret_cast<LrtEntry*>(table_mem_map_.Begin());
+ // Take into account the actual length.
+ max_entries_ = table_bytes / sizeof(LrtEntry);
+ }
+ segment_state_ = kLRTFirstSegment;
+ last_known_previous_state_ = kLRTFirstSegment;
+ return true;
+}
+
+LocalReferenceTable::~LocalReferenceTable() {
+ if (table_ != nullptr && !table_mem_map_.IsValid()) {
+ Runtime::Current()->GetSmallLrtAllocator()->Deallocate(table_);
+ }
+}
+
+void LocalReferenceTable::ConstexprChecks() {
+ // Use this for some assertions. They can't be put into the header as C++ wants the class
+ // to be complete.
+
+ // Check kind.
+ static_assert((EncodeIndirectRefKind(kLocal) & (~kKindMask)) == 0, "Kind encoding error");
+ static_assert((EncodeIndirectRefKind(kGlobal) & (~kKindMask)) == 0, "Kind encoding error");
+ static_assert((EncodeIndirectRefKind(kWeakGlobal) & (~kKindMask)) == 0, "Kind encoding error");
+ static_assert(DecodeIndirectRefKind(EncodeIndirectRefKind(kLocal)) == kLocal,
+ "Kind encoding error");
+ static_assert(DecodeIndirectRefKind(EncodeIndirectRefKind(kGlobal)) == kGlobal,
+ "Kind encoding error");
+ static_assert(DecodeIndirectRefKind(EncodeIndirectRefKind(kWeakGlobal)) == kWeakGlobal,
+ "Kind encoding error");
+
+ // Check serial.
+ static_assert(DecodeSerial(EncodeSerial(0u)) == 0u, "Serial encoding error");
+ static_assert(DecodeSerial(EncodeSerial(1u)) == 1u, "Serial encoding error");
+ static_assert(DecodeSerial(EncodeSerial(2u)) == 2u, "Serial encoding error");
+ static_assert(DecodeSerial(EncodeSerial(3u)) == 3u, "Serial encoding error");
+
+ // Table index.
+ static_assert(DecodeIndex(EncodeIndex(0u)) == 0u, "Index encoding error");
+ static_assert(DecodeIndex(EncodeIndex(1u)) == 1u, "Index encoding error");
+ static_assert(DecodeIndex(EncodeIndex(2u)) == 2u, "Index encoding error");
+ static_assert(DecodeIndex(EncodeIndex(3u)) == 3u, "Index encoding error");
+}
+
+bool LocalReferenceTable::IsValid() const {
+ return table_ != nullptr;
+}
+
+// Holes:
+//
+// To keep the LRT compact, we want to fill "holes" created by non-stack-discipline Add & Remove
+// operation sequences. For simplicity and lower memory overhead, we do not use a free list or
+// similar. Instead, we scan for holes, with the expectation that we will find holes fast as they
+// are usually near the end of the table (see the header, TODO: verify this assumption). To avoid
+// scans when there are no holes, the number of known holes should be tracked.
+//
+// A previous implementation stored the top index and the number of holes as the segment state.
+// This constraints the maximum number of references to 16-bit. We want to relax this, as it
+// is easy to require more references (e.g., to list all classes in large applications). Thus,
+// the implicitly stack-stored state, the LRTSegmentState, is only the top index.
+//
+// Thus, hole count is a local property of the current segment, and needs to be recovered when
+// (or after) a frame is pushed or popped. To keep JNI transitions simple (and inlineable), we
+// cannot do work when the segment changes. Thus, Add and Remove need to ensure the current
+// hole count is correct.
+//
+// To be able to detect segment changes, we require an additional local field that can describe
+// the known segment. This is last_known_previous_state_. The requirement will become clear with
+// the following (some non-trivial) cases that have to be supported:
+//
+// 1) Segment with holes (current_num_holes_ > 0), push new segment, add/remove reference
+// 2) Segment with holes (current_num_holes_ > 0), pop segment, add/remove reference
+// 3) Segment with holes (current_num_holes_ > 0), push new segment, pop segment, add/remove
+// reference
+// 4) Empty segment, push new segment, create a hole, pop a segment, add/remove a reference
+// 5) Base segment, push new segment, create a hole, pop a segment, push new segment, add/remove
+// reference
+//
+// Storing the last known *previous* state (bottom index) allows conservatively detecting all the
+// segment changes above. The condition is simply that the last known state is greater than or
+// equal to the current previous state, and smaller than the current state (top index). The
+// condition is conservative as it adds O(1) overhead to operations on an empty segment.
+
+static size_t CountNullEntries(const LrtEntry* table, size_t from, size_t to) {
+ size_t count = 0;
+ for (size_t index = from; index != to; ++index) {
+ if (table[index].GetReference()->IsNull()) {
+ count++;
+ }
+ }
+ return count;
+}
+
+void LocalReferenceTable::RecoverHoles(LRTSegmentState prev_state) {
+ if (last_known_previous_state_.top_index >= segment_state_.top_index ||
+ last_known_previous_state_.top_index < prev_state.top_index) {
+ const size_t top_index = segment_state_.top_index;
+ size_t count = CountNullEntries(table_, prev_state.top_index, top_index);
+
+ if (kDebugLRT) {
+ LOG(INFO) << "+++ Recovered holes: "
+ << " Current prev=" << prev_state.top_index
+ << " Current top_index=" << top_index
+ << " Old num_holes=" << current_num_holes_
+ << " New num_holes=" << count;
+ }
+
+ current_num_holes_ = count;
+ last_known_previous_state_ = prev_state;
+ } else if (kDebugLRT) {
+ LOG(INFO) << "No need to recover holes";
+ }
+}
+
+ALWAYS_INLINE
+static inline void CheckHoleCount(LrtEntry* table,
+ size_t exp_num_holes,
+ LRTSegmentState prev_state,
+ LRTSegmentState cur_state) {
+ if (kIsDebugBuild) {
+ size_t count = CountNullEntries(table, prev_state.top_index, cur_state.top_index);
+ CHECK_EQ(exp_num_holes, count) << "prevState=" << prev_state.top_index
+ << " topIndex=" << cur_state.top_index;
+ }
+}
+
+bool LocalReferenceTable::Resize(size_t new_size, std::string* error_msg) {
+ CHECK_GT(new_size, max_entries_);
+
+ constexpr size_t kMaxEntries = kMaxTableSizeInBytes / sizeof(LrtEntry);
+ if (new_size > kMaxEntries) {
+ *error_msg = android::base::StringPrintf("Requested size exceeds maximum: %zu", new_size);
+ return false;
+ }
+ // Note: the above check also ensures that there is no overflow below.
+
+ const size_t table_bytes = RoundUp(new_size * sizeof(LrtEntry), kPageSize);
+
+ MemMap new_map = NewLRTMap(table_bytes, error_msg);
+ if (!new_map.IsValid()) {
+ return false;
+ }
+
+ memcpy(new_map.Begin(), table_, max_entries_ * sizeof(LrtEntry));
+ if (!table_mem_map_.IsValid()) {
+ // Didn't have its own map; deallocate old table.
+ Runtime::Current()->GetSmallLrtAllocator()->Deallocate(table_);
+ }
+ table_mem_map_ = std::move(new_map);
+ table_ = reinterpret_cast<LrtEntry*>(table_mem_map_.Begin());
+ const size_t real_new_size = table_bytes / sizeof(LrtEntry);
+ DCHECK_GE(real_new_size, new_size);
+ max_entries_ = real_new_size;
+
+ return true;
+}
+
+IndirectRef LocalReferenceTable::Add(LRTSegmentState previous_state,
+ ObjPtr<mirror::Object> obj,
+ std::string* error_msg) {
+ if (kDebugLRT) {
+ LOG(INFO) << "+++ Add: previous_state=" << previous_state.top_index
+ << " top_index=" << segment_state_.top_index
+ << " last_known_prev_top_index=" << last_known_previous_state_.top_index
+ << " holes=" << current_num_holes_;
+ }
+
+ size_t top_index = segment_state_.top_index;
+
+ CHECK(obj != nullptr);
+ VerifyObject(obj);
+ DCHECK(table_ != nullptr);
+
+ if (top_index == max_entries_) {
+ // Try to double space.
+ if (std::numeric_limits<size_t>::max() / 2 < max_entries_) {
+ std::ostringstream oss;
+ oss << "JNI ERROR (app bug): " << kLocal << " table overflow "
+ << "(max=" << max_entries_ << ")" << std::endl
+ << MutatorLockedDumpable<LocalReferenceTable>(*this)
+ << " Resizing failed: exceeds size_t";
+ *error_msg = oss.str();
+ return nullptr;
+ }
+
+ std::string inner_error_msg;
+ if (!Resize(max_entries_ * 2, &inner_error_msg)) {
+ std::ostringstream oss;
+ oss << "JNI ERROR (app bug): " << kLocal << " table overflow "
+ << "(max=" << max_entries_ << ")" << std::endl
+ << MutatorLockedDumpable<LocalReferenceTable>(*this)
+ << " Resizing failed: " << inner_error_msg;
+ *error_msg = oss.str();
+ return nullptr;
+ }
+ }
+
+ RecoverHoles(previous_state);
+ CheckHoleCount(table_, current_num_holes_, previous_state, segment_state_);
+
+ // We know there's enough room in the table. Now we just need to find
+ // the right spot. If there's a hole, find it and fill it; otherwise,
+ // add to the end of the list.
+ IndirectRef result;
+ size_t index;
+ if (current_num_holes_ > 0) {
+ DCHECK_GT(top_index, 1U);
+ // Find the first hole; likely to be near the end of the list.
+ LrtEntry* p_scan = &table_[top_index - 1];
+ DCHECK(!p_scan->GetReference()->IsNull());
+ --p_scan;
+ while (!p_scan->GetReference()->IsNull()) {
+ DCHECK_GE(p_scan, table_ + previous_state.top_index);
+ --p_scan;
+ }
+ index = p_scan - table_;
+ current_num_holes_--;
+ } else {
+ // Add to the end.
+ index = top_index++;
+ segment_state_.top_index = top_index;
+ }
+ table_[index].Add(obj);
+ result = ToIndirectRef(index);
+ if (kDebugLRT) {
+ LOG(INFO) << "+++ added at " << ExtractIndex(result) << " top=" << segment_state_.top_index
+ << " holes=" << current_num_holes_;
+ }
+
+ DCHECK(result != nullptr);
+ return result;
+}
+
+void LocalReferenceTable::AssertEmpty() {
+ for (size_t i = 0; i < Capacity(); ++i) {
+ if (!table_[i].GetReference()->IsNull()) {
+ LOG(FATAL) << "Internal Error: non-empty local reference table\n"
+ << MutatorLockedDumpable<LocalReferenceTable>(*this);
+ UNREACHABLE();
+ }
+ }
+}
+
+// Removes an object. We extract the table offset bits from "iref"
+// and zap the corresponding entry, leaving a hole if it's not at the top.
+// If the entry is not between the current top index and the bottom index
+// specified by the cookie, we don't remove anything. This is the behavior
+// required by JNI's DeleteLocalRef function.
+// This method is not called when a local frame is popped; this is only used
+// for explicit single removals.
+// Returns "false" if nothing was removed.
+bool LocalReferenceTable::Remove(LRTSegmentState previous_state, IndirectRef iref) {
+ if (kDebugLRT) {
+ LOG(INFO) << "+++ Remove: previous_state=" << previous_state.top_index
+ << " top_index=" << segment_state_.top_index
+ << " last_known_prev_top_index=" << last_known_previous_state_.top_index
+ << " holes=" << current_num_holes_;
+ }
+
+ const uint32_t top_index = segment_state_.top_index;
+ const uint32_t bottom_index = previous_state.top_index;
+
+ DCHECK(table_ != nullptr);
+
+ // TODO: We should eagerly check the ref kind against the `kLocal` kind instead of
+ // relying on this weak check and postponing the rest until `CheckEntry()` below.
+ // Passing the wrong kind shall currently result in misleading warnings.
+ if (GetIndirectRefKind(iref) == kJniTransition) {
+ auto* self = Thread::Current();
+ ScopedObjectAccess soa(self);
+ if (self->IsJniTransitionReference(reinterpret_cast<jobject>(iref))) {
+ auto* env = self->GetJniEnv();
+ DCHECK(env != nullptr);
+ if (env->IsCheckJniEnabled()) {
+ LOG(WARNING) << "Attempt to remove non-JNI local reference, dumping thread";
+ if (kDumpStackOnNonLocalReference) {
+ self->Dump(LOG_STREAM(WARNING));
+ }
+ }
+ return true;
+ }
+ }
+
+ const uint32_t idx = ExtractIndex(iref);
+ if (idx < bottom_index) {
+ // Wrong segment.
+ LOG(WARNING) << "Attempt to remove index outside index area (" << idx
+ << " vs " << bottom_index << "-" << top_index << ")";
+ return false;
+ }
+ if (idx >= top_index) {
+ // Bad --- stale reference?
+ LOG(WARNING) << "Attempt to remove invalid index " << idx
+ << " (bottom=" << bottom_index << " top=" << top_index << ")";
+ return false;
+ }
+
+ RecoverHoles(previous_state);
+ CheckHoleCount(table_, current_num_holes_, previous_state, segment_state_);
+
+ if (idx == top_index - 1) {
+ // Top-most entry. Scan up and consume holes.
+
+ if (!CheckEntry("remove", iref, idx)) {
+ return false;
+ }
+
+ *table_[idx].GetReference() = GcRoot<mirror::Object>(nullptr);
+ if (current_num_holes_ != 0) {
+ uint32_t collapse_top_index = top_index;
+ while (--collapse_top_index > bottom_index && current_num_holes_ != 0) {
+ if (kDebugLRT) {
+ ScopedObjectAccess soa(Thread::Current());
+ LOG(INFO) << "+++ checking for hole at " << collapse_top_index - 1
+ << " (previous_state=" << bottom_index << ") val="
+ << table_[collapse_top_index - 1].GetReference()->Read<kWithoutReadBarrier>();
+ }
+ if (!table_[collapse_top_index - 1].GetReference()->IsNull()) {
+ break;
+ }
+ if (kDebugLRT) {
+ LOG(INFO) << "+++ ate hole at " << (collapse_top_index - 1);
+ }
+ current_num_holes_--;
+ }
+ segment_state_.top_index = collapse_top_index;
+
+ CheckHoleCount(table_, current_num_holes_, previous_state, segment_state_);
+ } else {
+ segment_state_.top_index = top_index - 1;
+ if (kDebugLRT) {
+ LOG(INFO) << "+++ ate last entry " << top_index - 1;
+ }
+ }
+ } else {
+ // Not the top-most entry. This creates a hole. We null out the entry to prevent somebody
+ // from deleting it twice and screwing up the hole count.
+ if (table_[idx].GetReference()->IsNull()) {
+ LOG(INFO) << "--- WEIRD: removing null entry " << idx;
+ return false;
+ }
+ if (!CheckEntry("remove", iref, idx)) {
+ return false;
+ }
+
+ *table_[idx].GetReference() = GcRoot<mirror::Object>(nullptr);
+ current_num_holes_++;
+ CheckHoleCount(table_, current_num_holes_, previous_state, segment_state_);
+ if (kDebugLRT) {
+ LOG(INFO) << "+++ left hole at " << idx << ", holes=" << current_num_holes_;
+ }
+ }
+
+ return true;
+}
+
+void LocalReferenceTable::Trim() {
+ ScopedTrace trace(__PRETTY_FUNCTION__);
+ if (!table_mem_map_.IsValid()) {
+ // Small table; nothing to do here.
+ return;
+ }
+ const size_t top_index = Capacity();
+ uint8_t* release_start = AlignUp(reinterpret_cast<uint8_t*>(&table_[top_index]), kPageSize);
+ uint8_t* release_end = static_cast<uint8_t*>(table_mem_map_.BaseEnd());
+ DCHECK_GE(reinterpret_cast<uintptr_t>(release_end), reinterpret_cast<uintptr_t>(release_start));
+ DCHECK_ALIGNED(release_end, kPageSize);
+ DCHECK_ALIGNED(release_end - release_start, kPageSize);
+ if (release_start != release_end) {
+ madvise(release_start, release_end - release_start, MADV_DONTNEED);
+ }
+}
+
+void LocalReferenceTable::VisitRoots(RootVisitor* visitor, const RootInfo& root_info) {
+ BufferedRootVisitor<kDefaultBufferedRootCount> root_visitor(visitor, root_info);
+ for (size_t i = 0, capacity = Capacity(); i != capacity; ++i) {
+ GcRoot<mirror::Object>* ref = table_[i].GetReference();
+ if (!ref->IsNull()) {
+ root_visitor.VisitRoot(*ref);
+ DCHECK(!ref->IsNull());
+ }
+ }
+}
+
+void LocalReferenceTable::Dump(std::ostream& os) const {
+ os << kLocal << " table dump:\n";
+ ReferenceTable::Table entries;
+ for (size_t i = 0; i < Capacity(); ++i) {
+ ObjPtr<mirror::Object> obj = table_[i].GetReference()->Read<kWithoutReadBarrier>();
+ if (obj != nullptr) {
+ obj = table_[i].GetReference()->Read();
+ entries.push_back(GcRoot<mirror::Object>(obj));
+ }
+ }
+ ReferenceTable::Dump(os, entries);
+}
+
+void LocalReferenceTable::SetSegmentState(LRTSegmentState new_state) {
+ if (kDebugLRT) {
+ LOG(INFO) << "Setting segment state: "
+ << segment_state_.top_index
+ << " -> "
+ << new_state.top_index;
+ }
+ segment_state_ = new_state;
+}
+
+bool LocalReferenceTable::EnsureFreeCapacity(size_t free_capacity, std::string* error_msg) {
+ DCHECK_GE(free_capacity, static_cast<size_t>(1));
+ if (free_capacity > kMaxTableSizeInBytes) {
+ // Arithmetic might even overflow.
+ *error_msg = "Requested table size implausibly large";
+ return false;
+ }
+ size_t top_index = segment_state_.top_index;
+ if (top_index + free_capacity <= max_entries_) {
+ return true;
+ }
+
+ // Try to increase the table size.
+ if (!Resize(top_index + free_capacity, error_msg)) {
+ LOG(WARNING) << "JNI ERROR: Unable to reserve space in EnsureFreeCapacity (" << free_capacity
+ << "): " << std::endl
+ << MutatorLockedDumpable<LocalReferenceTable>(*this)
+ << " Resizing failed: " << *error_msg;
+ return false;
+ }
+ return true;
+}
+
+size_t LocalReferenceTable::FreeCapacity() const {
+ return max_entries_ - segment_state_.top_index;
+}
+
+} // namespace jni
+} // namespace art
diff --git a/runtime/jni/local_reference_table.h b/runtime/jni/local_reference_table.h
new file mode 100644
index 0000000..debaa8b
--- /dev/null
+++ b/runtime/jni/local_reference_table.h
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_JNI_LOCAL_REFERENCE_TABLE_H_
+#define ART_RUNTIME_JNI_LOCAL_REFERENCE_TABLE_H_
+
+#include <stdint.h>
+
+#include <iosfwd>
+#include <limits>
+#include <string>
+
+#include <android-base/logging.h>
+
+#include "base/bit_utils.h"
+#include "base/locks.h"
+#include "base/macros.h"
+#include "base/mem_map.h"
+#include "base/mutex.h"
+#include "gc_root.h"
+#include "indirect_reference_table.h"
+#include "obj_ptr.h"
+#include "offsets.h"
+#include "read_barrier_option.h"
+
+namespace art {
+
+class RootInfo;
+
+namespace mirror {
+class Object;
+} // namespace mirror
+
+namespace jni {
+
+// Maintain a table of local references. Used for local JNI references.
+// TODO: Rewrite the implementation, so that valid local references are effectively
+// `CompressedReference<Object>*`, so that it can be decoded very quickly.
+//
+// The table contains object references, where the strong (local/global) references are part of the
+// GC root set (but not the weak global references). When an object is added we return an
+// IndirectRef that is not a valid pointer but can be used to find the original value in O(1) time.
+// Conversions to and from indirect references are performed on upcalls and downcalls, so they need
+// to be very fast.
+//
+// To be efficient for JNI local variable storage, we need to provide operations that allow us to
+// operate on segments of the table, where segments are pushed and popped as if on a stack. For
+// example, deletion of an entry should only succeed if it appears in the current segment, and we
+// want to be able to strip off the current segment quickly when a method returns. Additions to the
+// table must be made in the current segment even if space is available in an earlier area.
+//
+// A new segment is created when we call into native code from interpreted code, or when we handle
+// the JNI PushLocalFrame function.
+//
+// The GC must be able to scan the entire table quickly.
+//
+// In summary, these must be very fast:
+// - adding or removing a segment
+// - adding references to a new segment
+// - converting an indirect reference back to an Object
+// These can be a little slower, but must still be pretty quick:
+// - adding references to a "mature" segment
+// - removing individual references
+// - scanning the entire table straight through
+//
+// If there's more than one segment, we don't guarantee that the table will fill completely before
+// we fail due to lack of space. We do ensure that the current segment will pack tightly, which
+// should satisfy JNI requirements (e.g. EnsureLocalCapacity).
+
+// Indirect reference definition. This must be interchangeable with JNI's jobject, and it's
+// convenient to let null be null, so we use void*.
+//
+// We need a (potentially) large table index and a 2-bit reference type (global, local, weak
+// global). We also reserve some bits to be used to detect stale indirect references: we put a
+// serial number in the extra bits, and keep a copy of the serial number in the table. This requires
+// more memory and additional memory accesses on add/get, but is moving-GC safe. It will catch
+// additional problems, e.g.: create iref1 for obj, delete iref1, create iref2 for same obj,
+// lookup iref1. A pattern based on object bits will miss this.
+
+// Table definition.
+//
+// For the global reference table, the expected common operations are adding a new entry and
+// removing a recently-added entry (usually the most-recently-added entry). For JNI local
+// references, the common operations are adding a new entry and removing an entire table segment.
+//
+// If we delete entries from the middle of the list, we will be left with "holes". We track the
+// number of holes so that, when adding new elements, we can quickly decide to do a trivial append
+// or go slot-hunting.
+//
+// When the top-most entry is removed, any holes immediately below it are also removed. Thus,
+// deletion of an entry may reduce "top_index" by more than one.
+//
+// To get the desired behavior for JNI locals, we need to know the bottom and top of the current
+// "segment". The top is managed internally, and the bottom is passed in as a function argument.
+// When we call a native method or push a local frame, the current top index gets pushed on, and
+// serves as the new bottom. When we pop a frame off, the value from the stack becomes the new top
+// index, and the value stored in the previous frame becomes the new bottom.
+//
+// Holes are being locally cached for the segment. Otherwise we'd have to pass bottom index and
+// number of holes, which restricts us to 16 bits for the top index. The value is cached within the
+// table. To avoid code in generated JNI transitions, which implicitly form segments, the code for
+// adding and removing references needs to detect the change of a segment. Helper fields are used
+// for this detection.
+//
+// Common alternative implementation: make IndirectRef a pointer to the actual reference slot.
+// Instead of getting a table and doing a lookup, the lookup can be done instantly. Operations like
+// determining the type and deleting the reference are more expensive because the table must be
+// hunted for (i.e. you have to do a pointer comparison to see which table it's in), you can't move
+// the table when expanding it (so realloc() is out), and tricks like serial number checking to
+// detect stale references aren't possible (though we may be able to get similar benefits with other
+// approaches).
+//
+// TODO: consider a "lastDeleteIndex" for quick hole-filling when an add immediately follows a
+// delete; must invalidate after segment pop might be worth only using it for JNI globals.
+//
+// TODO: may want completely different add/remove algorithms for global and local refs to improve
+// performance. A large circular buffer might reduce the amortized cost of adding global
+// references.
+
+// The state of the current segment. We only store the index. Splitting it for index and hole
+// count restricts the range too much.
+struct LRTSegmentState {
+ uint32_t top_index;
+};
+
+// Use as initial value for "cookie", and when table has only one segment.
+static constexpr LRTSegmentState kLRTFirstSegment = { 0 };
+
+// We associate a few bits of serial number with each reference, for error checking.
+static constexpr unsigned int kLRTSerialBits = 3;
+static constexpr uint32_t kLRTMaxSerial = ((1 << kLRTSerialBits) - 1);
+
+class LrtEntry {
+ public:
+ void Add(ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ GcRoot<mirror::Object>* GetReference() {
+ DCHECK_LE(serial_, kLRTMaxSerial);
+ return &reference_;
+ }
+
+ const GcRoot<mirror::Object>* GetReference() const {
+ DCHECK_LE(serial_, kLRTMaxSerial);
+ return &reference_;
+ }
+
+ uint32_t GetSerial() const {
+ return serial_;
+ }
+
+ void SetReference(ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+ uint32_t serial_; // Incremented for each reuse; checked against reference.
+ GcRoot<mirror::Object> reference_;
+};
+static_assert(sizeof(LrtEntry) == 2 * sizeof(uint32_t), "Unexpected sizeof(LrtEntry)");
+static_assert(IsPowerOfTwo(sizeof(LrtEntry)), "Unexpected sizeof(LrtEntry)");
+
+// We initially allocate local reference tables with a very small number of entries, packing
+// multiple tables into a single page. If we need to expand one, we allocate them in units of
+// pages.
+// TODO: We should allocate all LRT tables as nonmovable Java objects, That in turn works better
+// if we break up each table into 2 parallel arrays, one for the Java reference, and one for the
+// serial number. The current scheme page-aligns regions containing LRT tables, and so allows them
+// to be identified and page-protected in the future.
+constexpr size_t kInitialLrtBytes = 512; // Number of bytes in an initial local table.
+constexpr size_t kSmallLrtEntries = kInitialLrtBytes / sizeof(LrtEntry);
+static_assert(kPageSize % kInitialLrtBytes == 0);
+static_assert(kInitialLrtBytes % sizeof(LrtEntry) == 0);
+static_assert(kInitialLrtBytes % sizeof(void *) == 0);
+
+// A minimal stopgap allocator for initial small local LRT tables.
+class SmallLrtAllocator {
+ public:
+ SmallLrtAllocator();
+
+ // Allocate a LRT table for kSmallLrtEntries.
+ LrtEntry* Allocate(std::string* error_msg) REQUIRES(!lock_);
+
+ void Deallocate(LrtEntry* unneeded) REQUIRES(!lock_);
+
+ private:
+ // A free list of kInitialLrtBytes chunks linked through the first word.
+ LrtEntry* small_lrt_freelist_;
+
+ // Repository of MemMaps used for small LRT tables.
+ std::vector<MemMap> shared_lrt_maps_;
+
+ Mutex lock_; // Level kGenericBottomLock; acquired before mem_map_lock_, which is a C++ mutex.
+};
+
+class LocalReferenceTable {
+ public:
+ // Constructs an uninitialized indirect reference table. Use `Initialize()` to initialize it.
+ LocalReferenceTable();
+
+ // Initialize the indirect reference table.
+ //
+ // Max_count is the minimum initial capacity (resizable).
+ // A value of 1 indicates an implementation-convenient small size.
+ bool Initialize(size_t max_count, std::string* error_msg);
+
+ ~LocalReferenceTable();
+
+ /*
+ * Checks whether construction of the LocalReferenceTable succeeded.
+ *
+ * This object must only be used if IsValid() returns true. It is safe to
+ * call IsValid from multiple threads without locking or other explicit
+ * synchronization.
+ */
+ bool IsValid() const;
+
+ // Add a new entry. "obj" must be a valid non-null object reference. This function will
+ // return null if an error happened (with an appropriate error message set).
+ IndirectRef Add(LRTSegmentState previous_state,
+ ObjPtr<mirror::Object> obj,
+ std::string* error_msg)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Given an IndirectRef in the table, return the Object it refers to.
+ //
+ // This function may abort under error conditions.
+ template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+ ObjPtr<mirror::Object> Get(IndirectRef iref) const REQUIRES_SHARED(Locks::mutator_lock_)
+ ALWAYS_INLINE;
+
+ // Updates an existing indirect reference to point to a new object.
+ void Update(IndirectRef iref, ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Remove an existing entry.
+ //
+ // If the entry is not between the current top index and the bottom index
+ // specified by the cookie, we don't remove anything. This is the behavior
+ // required by JNI's DeleteLocalRef function.
+ //
+ // Returns "false" if nothing was removed.
+ bool Remove(LRTSegmentState previous_state, IndirectRef iref);
+
+ void AssertEmpty() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void Dump(std::ostream& os) const
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::alloc_tracker_lock_);
+
+ IndirectRefKind GetKind() const {
+ return kLocal;
+ }
+
+ // Return the #of entries in the entire table. This includes holes, and
+ // so may be larger than the actual number of "live" entries.
+ size_t Capacity() const {
+ return segment_state_.top_index;
+ }
+
+ // Return the number of non-null entries in the table. Only reliable for a
+ // single segment table.
+ int32_t NEntriesForGlobal() {
+ return segment_state_.top_index - current_num_holes_;
+ }
+
+ // Ensure that at least free_capacity elements are available, or return false.
+ // Caller ensures free_capacity > 0.
+ bool EnsureFreeCapacity(size_t free_capacity, std::string* error_msg)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ // See implementation of EnsureFreeCapacity. We'll only state here how much is trivially free,
+ // without recovering holes. Thus this is a conservative estimate.
+ size_t FreeCapacity() const;
+
+ void VisitRoots(RootVisitor* visitor, const RootInfo& root_info)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ LRTSegmentState GetSegmentState() const {
+ return segment_state_;
+ }
+
+ void SetSegmentState(LRTSegmentState new_state);
+
+ static Offset SegmentStateOffset(size_t pointer_size ATTRIBUTE_UNUSED) {
+ // Note: Currently segment_state_ is at offset 0. We're testing the expected value in
+ // jni_internal_test to make sure it stays correct. It is not OFFSETOF_MEMBER, as that
+ // is not pointer-size-safe.
+ return Offset(0);
+ }
+
+ // Release pages past the end of the table that may have previously held references.
+ void Trim() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Determine what kind of indirect reference this is. Opposite of EncodeIndirectRefKind.
+ ALWAYS_INLINE static inline IndirectRefKind GetIndirectRefKind(IndirectRef iref) {
+ return DecodeIndirectRefKind(reinterpret_cast<uintptr_t>(iref));
+ }
+
+ /* Reference validation for CheckJNI. */
+ bool IsValidReference(IndirectRef, /*out*/std::string* error_msg) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+ static constexpr uint32_t kShiftedSerialMask = (1u << kLRTSerialBits) - 1;
+
+ static constexpr size_t kKindBits = MinimumBitsToStore(
+ static_cast<uint32_t>(IndirectRefKind::kLastKind));
+ static constexpr uint32_t kKindMask = (1u << kKindBits) - 1;
+
+ static constexpr uintptr_t EncodeIndex(uint32_t table_index) {
+ static_assert(sizeof(IndirectRef) == sizeof(uintptr_t), "Unexpected IndirectRef size");
+ DCHECK_LE(MinimumBitsToStore(table_index), BitSizeOf<uintptr_t>() - kLRTSerialBits - kKindBits);
+ return (static_cast<uintptr_t>(table_index) << kKindBits << kLRTSerialBits);
+ }
+ static constexpr uint32_t DecodeIndex(uintptr_t uref) {
+ return static_cast<uint32_t>((uref >> kKindBits) >> kLRTSerialBits);
+ }
+
+ static constexpr uintptr_t EncodeIndirectRefKind(IndirectRefKind kind) {
+ return static_cast<uintptr_t>(kind);
+ }
+ static constexpr IndirectRefKind DecodeIndirectRefKind(uintptr_t uref) {
+ return static_cast<IndirectRefKind>(uref & kKindMask);
+ }
+
+ static constexpr uintptr_t EncodeSerial(uint32_t serial) {
+ DCHECK_LE(MinimumBitsToStore(serial), kLRTSerialBits);
+ return serial << kKindBits;
+ }
+ static constexpr uint32_t DecodeSerial(uintptr_t uref) {
+ return static_cast<uint32_t>(uref >> kKindBits) & kShiftedSerialMask;
+ }
+
+ constexpr uintptr_t EncodeIndirectRef(uint32_t table_index, uint32_t serial) const {
+ DCHECK_LT(table_index, max_entries_);
+ return EncodeIndex(table_index) | EncodeSerial(serial) | EncodeIndirectRefKind(kLocal);
+ }
+
+ static void ConstexprChecks();
+
+ // Extract the table index from an indirect reference.
+ ALWAYS_INLINE static uint32_t ExtractIndex(IndirectRef iref) {
+ return DecodeIndex(reinterpret_cast<uintptr_t>(iref));
+ }
+
+ IndirectRef ToIndirectRef(uint32_t table_index) const {
+ DCHECK_LT(table_index, max_entries_);
+ uint32_t serial = table_[table_index].GetSerial();
+ return reinterpret_cast<IndirectRef>(EncodeIndirectRef(table_index, serial));
+ }
+
+ // Resize the backing table to be at least new_size elements long. Currently
+ // must be larger than the current size. After return max_entries_ >= new_size.
+ bool Resize(size_t new_size, std::string* error_msg);
+
+ void RecoverHoles(LRTSegmentState from);
+
+ // Abort if check_jni is not enabled. Otherwise, just log as an error.
+ static void AbortIfNoCheckJNI(const std::string& msg);
+
+ /* extra debugging checks */
+ bool CheckEntry(const char*, IndirectRef, uint32_t) const;
+
+ /// semi-public - read/write by jni down calls.
+ LRTSegmentState segment_state_;
+
+ // Mem map where we store the indirect refs. If it's invalid, and table_ is non-null, then
+ // table_ is valid, but was allocated via `SmallLrtAllocator`;
+ MemMap table_mem_map_;
+ // bottom of the stack. Do not directly access the object references
+ // in this as they are roots. Use Get() that has a read barrier.
+ LrtEntry* table_;
+
+ // max #of entries allowed (modulo resizing).
+ size_t max_entries_;
+
+ // Some values to retain old behavior with holes. Description of the algorithm is in the .cc
+ // file.
+ // TODO: Consider other data structures for compact tables, e.g., free lists.
+ size_t current_num_holes_; // Number of holes in the current / top segment.
+ LRTSegmentState last_known_previous_state_;
+};
+
+} // namespace jni
+} // namespace art
+
+#endif // ART_RUNTIME_JNI_LOCAL_REFERENCE_TABLE_H_
diff --git a/runtime/jni/local_reference_table_test.cc b/runtime/jni/local_reference_table_test.cc
new file mode 100644
index 0000000..84bb189
--- /dev/null
+++ b/runtime/jni/local_reference_table_test.cc
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2022 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 "indirect_reference_table-inl.h"
+
+#include "android-base/stringprintf.h"
+
+#include "class_linker-inl.h"
+#include "common_runtime_test.h"
+#include "mirror/class-alloc-inl.h"
+#include "mirror/object-inl.h"
+#include "scoped_thread_state_change-inl.h"
+
+namespace art {
+namespace jni {
+
+using android::base::StringPrintf;
+
+class LocalReferenceTableTest : public CommonRuntimeTest {
+ protected:
+ LocalReferenceTableTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+};
+
+static void CheckDump(LocalReferenceTable* lrt, size_t num_objects, size_t num_unique)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ std::ostringstream oss;
+ lrt->Dump(oss);
+ if (num_objects == 0) {
+ EXPECT_EQ(oss.str().find("java.lang.Object"), std::string::npos) << oss.str();
+ } else if (num_objects == 1) {
+ EXPECT_NE(oss.str().find("1 of java.lang.Object"), std::string::npos) << oss.str();
+ } else {
+ EXPECT_NE(oss.str().find(StringPrintf("%zd of java.lang.Object (%zd unique instances)",
+ num_objects, num_unique)),
+ std::string::npos)
+ << "\n Expected number of objects: " << num_objects
+ << "\n Expected unique objects: " << num_unique << "\n"
+ << oss.str();
+ }
+}
+
+TEST_F(LocalReferenceTableTest, BasicTest) {
+ // This will lead to error messages in the log.
+ ScopedLogSeverity sls(LogSeverity::FATAL);
+
+ ScopedObjectAccess soa(Thread::Current());
+ static const size_t kTableMax = 20;
+ std::string error_msg;
+ LocalReferenceTable lrt;
+ bool success = lrt.Initialize(kTableMax, &error_msg);
+ ASSERT_TRUE(success) << error_msg;
+
+ StackHandleScope<5> hs(soa.Self());
+ Handle<mirror::Class> c =
+ hs.NewHandle(class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"));
+ ASSERT_TRUE(c != nullptr);
+ Handle<mirror::Object> obj0 = hs.NewHandle(c->AllocObject(soa.Self()));
+ ASSERT_TRUE(obj0 != nullptr);
+ Handle<mirror::Object> obj1 = hs.NewHandle(c->AllocObject(soa.Self()));
+ ASSERT_TRUE(obj1 != nullptr);
+ Handle<mirror::Object> obj2 = hs.NewHandle(c->AllocObject(soa.Self()));
+ ASSERT_TRUE(obj2 != nullptr);
+ Handle<mirror::Object> obj3 = hs.NewHandle(c->AllocObject(soa.Self()));
+ ASSERT_TRUE(obj3 != nullptr);
+
+ const LRTSegmentState cookie = kLRTFirstSegment;
+
+ CheckDump(&lrt, 0, 0);
+
+ IndirectRef iref0 = (IndirectRef) 0x11110;
+ EXPECT_FALSE(lrt.Remove(cookie, iref0)) << "unexpectedly successful removal";
+
+ // Add three, check, remove in the order in which they were added.
+ iref0 = lrt.Add(cookie, obj0.Get(), &error_msg);
+ EXPECT_TRUE(iref0 != nullptr);
+ CheckDump(&lrt, 1, 1);
+ IndirectRef iref1 = lrt.Add(cookie, obj1.Get(), &error_msg);
+ EXPECT_TRUE(iref1 != nullptr);
+ CheckDump(&lrt, 2, 2);
+ IndirectRef iref2 = lrt.Add(cookie, obj2.Get(), &error_msg);
+ EXPECT_TRUE(iref2 != nullptr);
+ CheckDump(&lrt, 3, 3);
+
+ EXPECT_OBJ_PTR_EQ(obj0.Get(), lrt.Get(iref0));
+ EXPECT_OBJ_PTR_EQ(obj1.Get(), lrt.Get(iref1));
+ EXPECT_OBJ_PTR_EQ(obj2.Get(), lrt.Get(iref2));
+
+ EXPECT_TRUE(lrt.Remove(cookie, iref0));
+ CheckDump(&lrt, 2, 2);
+ EXPECT_TRUE(lrt.Remove(cookie, iref1));
+ CheckDump(&lrt, 1, 1);
+ EXPECT_TRUE(lrt.Remove(cookie, iref2));
+ CheckDump(&lrt, 0, 0);
+
+ // Table should be empty now.
+ EXPECT_EQ(0U, lrt.Capacity());
+
+ // Check that the entry off the end of the list is not valid.
+ // (CheckJNI shall abort for such entries.)
+ EXPECT_FALSE(lrt.IsValidReference(iref0, &error_msg));
+
+ // Add three, remove in the opposite order.
+ iref0 = lrt.Add(cookie, obj0.Get(), &error_msg);
+ EXPECT_TRUE(iref0 != nullptr);
+ iref1 = lrt.Add(cookie, obj1.Get(), &error_msg);
+ EXPECT_TRUE(iref1 != nullptr);
+ iref2 = lrt.Add(cookie, obj2.Get(), &error_msg);
+ EXPECT_TRUE(iref2 != nullptr);
+ CheckDump(&lrt, 3, 3);
+
+ ASSERT_TRUE(lrt.Remove(cookie, iref2));
+ CheckDump(&lrt, 2, 2);
+ ASSERT_TRUE(lrt.Remove(cookie, iref1));
+ CheckDump(&lrt, 1, 1);
+ ASSERT_TRUE(lrt.Remove(cookie, iref0));
+ CheckDump(&lrt, 0, 0);
+
+ // Table should be empty now.
+ ASSERT_EQ(0U, lrt.Capacity());
+
+ // Add three, remove middle / middle / bottom / top. (Second attempt
+ // to remove middle should fail.)
+ iref0 = lrt.Add(cookie, obj0.Get(), &error_msg);
+ EXPECT_TRUE(iref0 != nullptr);
+ iref1 = lrt.Add(cookie, obj1.Get(), &error_msg);
+ EXPECT_TRUE(iref1 != nullptr);
+ iref2 = lrt.Add(cookie, obj2.Get(), &error_msg);
+ EXPECT_TRUE(iref2 != nullptr);
+ CheckDump(&lrt, 3, 3);
+
+ ASSERT_EQ(3U, lrt.Capacity());
+
+ ASSERT_TRUE(lrt.Remove(cookie, iref1));
+ CheckDump(&lrt, 2, 2);
+ ASSERT_FALSE(lrt.Remove(cookie, iref1));
+ CheckDump(&lrt, 2, 2);
+
+ // Check that the reference to the hole is not valid.
+ EXPECT_FALSE(lrt.IsValidReference(iref1, &error_msg));
+
+ ASSERT_TRUE(lrt.Remove(cookie, iref2));
+ CheckDump(&lrt, 1, 1);
+ ASSERT_TRUE(lrt.Remove(cookie, iref0));
+ CheckDump(&lrt, 0, 0);
+
+ // Table should be empty now.
+ ASSERT_EQ(0U, lrt.Capacity());
+
+ // Add four entries. Remove #1, add new entry, verify that table size
+ // is still 4 (i.e. holes are getting filled). Remove #1 and #3, verify
+ // that we delete one and don't hole-compact the other.
+ iref0 = lrt.Add(cookie, obj0.Get(), &error_msg);
+ EXPECT_TRUE(iref0 != nullptr);
+ iref1 = lrt.Add(cookie, obj1.Get(), &error_msg);
+ EXPECT_TRUE(iref1 != nullptr);
+ iref2 = lrt.Add(cookie, obj2.Get(), &error_msg);
+ EXPECT_TRUE(iref2 != nullptr);
+ IndirectRef iref3 = lrt.Add(cookie, obj3.Get(), &error_msg);
+ EXPECT_TRUE(iref3 != nullptr);
+ CheckDump(&lrt, 4, 4);
+
+ ASSERT_TRUE(lrt.Remove(cookie, iref1));
+ CheckDump(&lrt, 3, 3);
+
+ iref1 = lrt.Add(cookie, obj1.Get(), &error_msg);
+ EXPECT_TRUE(iref1 != nullptr);
+
+ ASSERT_EQ(4U, lrt.Capacity()) << "hole not filled";
+ CheckDump(&lrt, 4, 4);
+
+ ASSERT_TRUE(lrt.Remove(cookie, iref1));
+ CheckDump(&lrt, 3, 3);
+ ASSERT_TRUE(lrt.Remove(cookie, iref3));
+ CheckDump(&lrt, 2, 2);
+
+ ASSERT_EQ(3U, lrt.Capacity()) << "should be 3 after two deletions";
+
+ ASSERT_TRUE(lrt.Remove(cookie, iref2));
+ CheckDump(&lrt, 1, 1);
+ ASSERT_TRUE(lrt.Remove(cookie, iref0));
+ CheckDump(&lrt, 0, 0);
+
+ ASSERT_EQ(0U, lrt.Capacity()) << "not empty after split remove";
+
+ // Add an entry, remove it, add a new entry, and try to use the original
+ // iref. They have the same slot number but are for different objects.
+ // With the extended checks in place, this should fail.
+ iref0 = lrt.Add(cookie, obj0.Get(), &error_msg);
+ EXPECT_TRUE(iref0 != nullptr);
+ CheckDump(&lrt, 1, 1);
+ ASSERT_TRUE(lrt.Remove(cookie, iref0));
+ CheckDump(&lrt, 0, 0);
+ iref1 = lrt.Add(cookie, obj1.Get(), &error_msg);
+ EXPECT_TRUE(iref1 != nullptr);
+ CheckDump(&lrt, 1, 1);
+ ASSERT_FALSE(lrt.Remove(cookie, iref0)) << "mismatched del succeeded";
+ CheckDump(&lrt, 1, 1);
+ ASSERT_TRUE(lrt.Remove(cookie, iref1)) << "switched del failed";
+ ASSERT_EQ(0U, lrt.Capacity()) << "switching del not empty";
+ CheckDump(&lrt, 0, 0);
+
+ // Same as above, but with the same object. A more rigorous checker
+ // (e.g. with slot serialization) will catch this.
+ iref0 = lrt.Add(cookie, obj0.Get(), &error_msg);
+ EXPECT_TRUE(iref0 != nullptr);
+ CheckDump(&lrt, 1, 1);
+ ASSERT_TRUE(lrt.Remove(cookie, iref0));
+ CheckDump(&lrt, 0, 0);
+ iref1 = lrt.Add(cookie, obj0.Get(), &error_msg);
+ EXPECT_TRUE(iref1 != nullptr);
+ CheckDump(&lrt, 1, 1);
+ if (iref0 != iref1) {
+ // Try 0, should not work.
+ ASSERT_FALSE(lrt.Remove(cookie, iref0)) << "temporal del succeeded";
+ }
+ ASSERT_TRUE(lrt.Remove(cookie, iref1)) << "temporal cleanup failed";
+ ASSERT_EQ(0U, lrt.Capacity()) << "temporal del not empty";
+ CheckDump(&lrt, 0, 0);
+
+ // Stale reference is not valid.
+ iref0 = lrt.Add(cookie, obj0.Get(), &error_msg);
+ EXPECT_TRUE(iref0 != nullptr);
+ CheckDump(&lrt, 1, 1);
+ ASSERT_TRUE(lrt.Remove(cookie, iref0));
+ EXPECT_FALSE(lrt.IsValidReference(iref0, &error_msg)) << "stale lookup succeeded";
+ CheckDump(&lrt, 0, 0);
+
+ // Test table resizing.
+ // These ones fit...
+ static const size_t kTableInitial = kTableMax / 2;
+ IndirectRef manyRefs[kTableInitial];
+ for (size_t i = 0; i < kTableInitial; i++) {
+ manyRefs[i] = lrt.Add(cookie, obj0.Get(), &error_msg);
+ ASSERT_TRUE(manyRefs[i] != nullptr) << "Failed adding " << i;
+ CheckDump(&lrt, i + 1, 1);
+ }
+ // ...this one causes overflow.
+ iref0 = lrt.Add(cookie, obj0.Get(), &error_msg);
+ ASSERT_TRUE(iref0 != nullptr);
+ ASSERT_EQ(kTableInitial + 1, lrt.Capacity());
+ CheckDump(&lrt, kTableInitial + 1, 1);
+
+ for (size_t i = 0; i < kTableInitial; i++) {
+ ASSERT_TRUE(lrt.Remove(cookie, manyRefs[i])) << "failed removing " << i;
+ CheckDump(&lrt, kTableInitial - i, 1);
+ }
+ // Because of removal order, should have 11 entries, 10 of them holes.
+ ASSERT_EQ(kTableInitial + 1, lrt.Capacity());
+
+ ASSERT_TRUE(lrt.Remove(cookie, iref0)) << "multi-remove final failed";
+
+ ASSERT_EQ(0U, lrt.Capacity()) << "multi-del not empty";
+ CheckDump(&lrt, 0, 0);
+}
+
+TEST_F(LocalReferenceTableTest, Holes) {
+ // Test the explicitly named cases from the LRT implementation:
+ //
+ // 1) Segment with holes (current_num_holes_ > 0), push new segment, add/remove reference
+ // 2) Segment with holes (current_num_holes_ > 0), pop segment, add/remove reference
+ // 3) Segment with holes (current_num_holes_ > 0), push new segment, pop segment, add/remove
+ // reference
+ // 4) Empty segment, push new segment, create a hole, pop a segment, add/remove a reference
+ // 5) Base segment, push new segment, create a hole, pop a segment, push new segment, add/remove
+ // reference
+
+ ScopedObjectAccess soa(Thread::Current());
+ static const size_t kTableMax = 10;
+
+ StackHandleScope<6> hs(soa.Self());
+ Handle<mirror::Class> c = hs.NewHandle(
+ class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"));
+ ASSERT_TRUE(c != nullptr);
+ Handle<mirror::Object> obj0 = hs.NewHandle(c->AllocObject(soa.Self()));
+ ASSERT_TRUE(obj0 != nullptr);
+ Handle<mirror::Object> obj1 = hs.NewHandle(c->AllocObject(soa.Self()));
+ ASSERT_TRUE(obj1 != nullptr);
+ Handle<mirror::Object> obj2 = hs.NewHandle(c->AllocObject(soa.Self()));
+ ASSERT_TRUE(obj2 != nullptr);
+ Handle<mirror::Object> obj3 = hs.NewHandle(c->AllocObject(soa.Self()));
+ ASSERT_TRUE(obj3 != nullptr);
+ Handle<mirror::Object> obj4 = hs.NewHandle(c->AllocObject(soa.Self()));
+ ASSERT_TRUE(obj4 != nullptr);
+
+ std::string error_msg;
+
+ // 1) Segment with holes (current_num_holes_ > 0), push new segment, add/remove reference.
+ {
+ LocalReferenceTable lrt;
+ bool success = lrt.Initialize(kTableMax, &error_msg);
+ ASSERT_TRUE(success) << error_msg;
+
+ const LRTSegmentState cookie0 = kLRTFirstSegment;
+
+ CheckDump(&lrt, 0, 0);
+
+ IndirectRef iref0 = lrt.Add(cookie0, obj0.Get(), &error_msg);
+ IndirectRef iref1 = lrt.Add(cookie0, obj1.Get(), &error_msg);
+ IndirectRef iref2 = lrt.Add(cookie0, obj2.Get(), &error_msg);
+
+ EXPECT_TRUE(lrt.Remove(cookie0, iref1));
+
+ // New segment.
+ const LRTSegmentState cookie1 = lrt.GetSegmentState();
+
+ IndirectRef iref3 = lrt.Add(cookie1, obj3.Get(), &error_msg);
+
+ // Must not have filled the previous hole.
+ EXPECT_EQ(lrt.Capacity(), 4u);
+ EXPECT_FALSE(lrt.IsValidReference(iref1, &error_msg));
+ CheckDump(&lrt, 3, 3);
+
+ UNUSED(iref0, iref1, iref2, iref3);
+ }
+
+ // 2) Segment with holes (current_num_holes_ > 0), pop segment, add/remove reference
+ {
+ LocalReferenceTable lrt;
+ bool success = lrt.Initialize(kTableMax, &error_msg);
+ ASSERT_TRUE(success) << error_msg;
+
+ const LRTSegmentState cookie0 = kLRTFirstSegment;
+
+ CheckDump(&lrt, 0, 0);
+
+ IndirectRef iref0 = lrt.Add(cookie0, obj0.Get(), &error_msg);
+
+ // New segment.
+ const LRTSegmentState cookie1 = lrt.GetSegmentState();
+
+ IndirectRef iref1 = lrt.Add(cookie1, obj1.Get(), &error_msg);
+ IndirectRef iref2 = lrt.Add(cookie1, obj2.Get(), &error_msg);
+ IndirectRef iref3 = lrt.Add(cookie1, obj3.Get(), &error_msg);
+
+ EXPECT_TRUE(lrt.Remove(cookie1, iref2));
+
+ // Pop segment.
+ lrt.SetSegmentState(cookie1);
+
+ IndirectRef iref4 = lrt.Add(cookie1, obj4.Get(), &error_msg);
+
+ EXPECT_EQ(lrt.Capacity(), 2u);
+ EXPECT_FALSE(lrt.IsValidReference(iref2, &error_msg));
+ CheckDump(&lrt, 2, 2);
+
+ UNUSED(iref0, iref1, iref2, iref3, iref4);
+ }
+
+ // 3) Segment with holes (current_num_holes_ > 0), push new segment, pop segment, add/remove
+ // reference.
+ {
+ LocalReferenceTable lrt;
+ bool success = lrt.Initialize(kTableMax, &error_msg);
+ ASSERT_TRUE(success) << error_msg;
+
+ const LRTSegmentState cookie0 = kLRTFirstSegment;
+
+ CheckDump(&lrt, 0, 0);
+
+ IndirectRef iref0 = lrt.Add(cookie0, obj0.Get(), &error_msg);
+
+ // New segment.
+ const LRTSegmentState cookie1 = lrt.GetSegmentState();
+
+ IndirectRef iref1 = lrt.Add(cookie1, obj1.Get(), &error_msg);
+ IndirectRef iref2 = lrt.Add(cookie1, obj2.Get(), &error_msg);
+
+ EXPECT_TRUE(lrt.Remove(cookie1, iref1));
+
+ // New segment.
+ const LRTSegmentState cookie2 = lrt.GetSegmentState();
+
+ IndirectRef iref3 = lrt.Add(cookie2, obj3.Get(), &error_msg);
+
+ // Pop segment.
+ lrt.SetSegmentState(cookie2);
+
+ IndirectRef iref4 = lrt.Add(cookie1, obj4.Get(), &error_msg);
+
+ EXPECT_EQ(lrt.Capacity(), 3u);
+ EXPECT_FALSE(lrt.IsValidReference(iref1, &error_msg));
+ CheckDump(&lrt, 3, 3);
+
+ UNUSED(iref0, iref1, iref2, iref3, iref4);
+ }
+
+ // 4) Empty segment, push new segment, create a hole, pop a segment, add/remove a reference.
+ {
+ LocalReferenceTable lrt;
+ bool success = lrt.Initialize(kTableMax, &error_msg);
+ ASSERT_TRUE(success) << error_msg;
+
+ const LRTSegmentState cookie0 = kLRTFirstSegment;
+
+ CheckDump(&lrt, 0, 0);
+
+ IndirectRef iref0 = lrt.Add(cookie0, obj0.Get(), &error_msg);
+
+ // New segment.
+ const LRTSegmentState cookie1 = lrt.GetSegmentState();
+
+ IndirectRef iref1 = lrt.Add(cookie1, obj1.Get(), &error_msg);
+ EXPECT_TRUE(lrt.Remove(cookie1, iref1));
+
+ // Emptied segment, push new one.
+ const LRTSegmentState cookie2 = lrt.GetSegmentState();
+
+ IndirectRef iref2 = lrt.Add(cookie1, obj1.Get(), &error_msg);
+ IndirectRef iref3 = lrt.Add(cookie1, obj2.Get(), &error_msg);
+ IndirectRef iref4 = lrt.Add(cookie1, obj3.Get(), &error_msg);
+
+ EXPECT_TRUE(lrt.Remove(cookie1, iref3));
+
+ // Pop segment.
+ UNUSED(cookie2);
+ lrt.SetSegmentState(cookie1);
+
+ IndirectRef iref5 = lrt.Add(cookie1, obj4.Get(), &error_msg);
+
+ EXPECT_EQ(lrt.Capacity(), 2u);
+ EXPECT_FALSE(lrt.IsValidReference(iref3, &error_msg));
+ CheckDump(&lrt, 2, 2);
+
+ UNUSED(iref0, iref1, iref2, iref3, iref4, iref5);
+ }
+
+ // 5) Base segment, push new segment, create a hole, pop a segment, push new segment, add/remove
+ // reference
+ {
+ LocalReferenceTable lrt;
+ bool success = lrt.Initialize(kTableMax, &error_msg);
+ ASSERT_TRUE(success) << error_msg;
+
+ const LRTSegmentState cookie0 = kLRTFirstSegment;
+
+ CheckDump(&lrt, 0, 0);
+
+ IndirectRef iref0 = lrt.Add(cookie0, obj0.Get(), &error_msg);
+
+ // New segment.
+ const LRTSegmentState cookie1 = lrt.GetSegmentState();
+
+ IndirectRef iref1 = lrt.Add(cookie1, obj1.Get(), &error_msg);
+ IndirectRef iref2 = lrt.Add(cookie1, obj1.Get(), &error_msg);
+ IndirectRef iref3 = lrt.Add(cookie1, obj2.Get(), &error_msg);
+
+ EXPECT_TRUE(lrt.Remove(cookie1, iref2));
+
+ // Pop segment.
+ lrt.SetSegmentState(cookie1);
+
+ // Push segment.
+ const LRTSegmentState cookie1_second = lrt.GetSegmentState();
+ UNUSED(cookie1_second);
+
+ IndirectRef iref4 = lrt.Add(cookie1, obj3.Get(), &error_msg);
+
+ EXPECT_EQ(lrt.Capacity(), 2u);
+ EXPECT_FALSE(lrt.IsValidReference(iref3, &error_msg));
+ CheckDump(&lrt, 2, 2);
+
+ UNUSED(iref0, iref1, iref2, iref3, iref4);
+ }
+}
+
+TEST_F(LocalReferenceTableTest, Resize) {
+ ScopedObjectAccess soa(Thread::Current());
+ static const size_t kTableMax = 512;
+
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::Class> c = hs.NewHandle(
+ class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"));
+ ASSERT_TRUE(c != nullptr);
+ Handle<mirror::Object> obj0 = hs.NewHandle(c->AllocObject(soa.Self()));
+ ASSERT_TRUE(obj0 != nullptr);
+
+ std::string error_msg;
+ LocalReferenceTable lrt;
+ bool success = lrt.Initialize(kTableMax, &error_msg);
+ ASSERT_TRUE(success) << error_msg;
+
+ CheckDump(&lrt, 0, 0);
+ const LRTSegmentState cookie = kLRTFirstSegment;
+
+ for (size_t i = 0; i != kTableMax + 1; ++i) {
+ lrt.Add(cookie, obj0.Get(), &error_msg);
+ }
+
+ EXPECT_EQ(lrt.Capacity(), kTableMax + 1);
+}
+
+} // namespace jni
+} // namespace art
diff --git a/runtime/linear_alloc-inl.h b/runtime/linear_alloc-inl.h
new file mode 100644
index 0000000..13dbea1
--- /dev/null
+++ b/runtime/linear_alloc-inl.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_LINEAR_ALLOC_INL_H_
+#define ART_RUNTIME_LINEAR_ALLOC_INL_H_
+
+#include "linear_alloc.h"
+
+#include "base/gc_visited_arena_pool.h"
+#include "thread-current-inl.h"
+
+namespace art {
+
+inline void LinearAlloc::SetFirstObject(void* begin, size_t bytes) const {
+ DCHECK(track_allocations_);
+ if (ArenaAllocator::IsRunningOnMemoryTool()) {
+ bytes += ArenaAllocator::kMemoryToolRedZoneBytes;
+ }
+ uint8_t* end = static_cast<uint8_t*>(begin) + bytes;
+ Arena* arena = allocator_.GetHeadArena();
+ DCHECK_NE(arena, nullptr);
+ // The object would either be in the head arena or the next one.
+ if (UNLIKELY(begin < arena->Begin() || begin >= arena->End())) {
+ arena = arena->Next();
+ }
+ DCHECK(begin >= arena->Begin() && end <= arena->End());
+ down_cast<TrackedArena*>(arena)->SetFirstObject(static_cast<uint8_t*>(begin), end);
+}
+
+inline void LinearAlloc::SetupForPostZygoteFork(Thread* self) {
+ MutexLock mu(self, lock_);
+ DCHECK(track_allocations_);
+ allocator_.ResetCurrentArena();
+}
+
+inline void* LinearAlloc::Realloc(Thread* self,
+ void* ptr,
+ size_t old_size,
+ size_t new_size,
+ LinearAllocKind kind) {
+ MutexLock mu(self, lock_);
+ if (track_allocations_) {
+ if (ptr != nullptr) {
+ // Realloc cannot be called on 16-byte aligned as Realloc doesn't guarantee
+ // that. So the header must be immediately prior to ptr.
+ TrackingHeader* header = reinterpret_cast<TrackingHeader*>(ptr) - 1;
+ DCHECK_EQ(header->GetKind(), kind);
+ old_size += sizeof(TrackingHeader);
+ DCHECK_EQ(header->GetSize(), old_size);
+ ptr = header;
+ } else {
+ DCHECK_EQ(old_size, 0u);
+ }
+ new_size += sizeof(TrackingHeader);
+ void* ret = allocator_.Realloc(ptr, old_size, new_size);
+ new (ret) TrackingHeader(new_size, kind);
+ SetFirstObject(ret, new_size);
+ return static_cast<TrackingHeader*>(ret) + 1;
+ } else {
+ return allocator_.Realloc(ptr, old_size, new_size);
+ }
+}
+
+inline void* LinearAlloc::Alloc(Thread* self, size_t size, LinearAllocKind kind) {
+ MutexLock mu(self, lock_);
+ if (track_allocations_) {
+ size += sizeof(TrackingHeader);
+ TrackingHeader* storage = new (allocator_.Alloc(size)) TrackingHeader(size, kind);
+ SetFirstObject(storage, size);
+ return storage + 1;
+ } else {
+ return allocator_.Alloc(size);
+ }
+}
+
+inline void* LinearAlloc::AllocAlign16(Thread* self, size_t size, LinearAllocKind kind) {
+ MutexLock mu(self, lock_);
+ DCHECK_ALIGNED(size, 16);
+ if (track_allocations_) {
+ size_t mem_tool_bytes = ArenaAllocator::IsRunningOnMemoryTool()
+ ? ArenaAllocator::kMemoryToolRedZoneBytes : 0;
+ uint8_t* ptr = allocator_.CurrentPtr() + sizeof(TrackingHeader);
+ uintptr_t padding =
+ RoundUp(reinterpret_cast<uintptr_t>(ptr), 16) - reinterpret_cast<uintptr_t>(ptr);
+ DCHECK_LT(padding, 16u);
+ size_t required_size = size + sizeof(TrackingHeader) + padding;
+
+ if (allocator_.CurrentArenaUnusedBytes() < required_size + mem_tool_bytes) {
+ // The allocator will require a new arena, which is expected to be
+ // 16-byte aligned.
+ static_assert(ArenaAllocator::kArenaAlignment >= 16,
+ "Expecting sufficient alignment for new Arena.");
+ required_size = size + RoundUp(sizeof(TrackingHeader), 16);
+ }
+ // Using ArenaAllocator's AllocAlign16 now would disturb the alignment by
+ // trying to make header 16-byte aligned. The alignment requirements are
+ // already addressed here. Now we want allocator to just bump the pointer.
+ ptr = static_cast<uint8_t*>(allocator_.Alloc(required_size));
+ new (ptr) TrackingHeader(required_size, kind, /*is_16_aligned=*/true);
+ SetFirstObject(ptr, required_size);
+ return AlignUp(ptr + sizeof(TrackingHeader), 16);
+ } else {
+ return allocator_.AllocAlign16(size);
+ }
+}
+
+inline size_t LinearAlloc::GetUsedMemory() const {
+ MutexLock mu(Thread::Current(), lock_);
+ return allocator_.BytesUsed();
+}
+
+inline ArenaPool* LinearAlloc::GetArenaPool() {
+ MutexLock mu(Thread::Current(), lock_);
+ return allocator_.GetArenaPool();
+}
+
+inline bool LinearAlloc::Contains(void* ptr) const {
+ MutexLock mu(Thread::Current(), lock_);
+ return allocator_.Contains(ptr);
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_LINEAR_ALLOC_INL_H_
diff --git a/runtime/linear_alloc.cc b/runtime/linear_alloc.cc
deleted file mode 100644
index 3f01fc3..0000000
--- a/runtime/linear_alloc.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2015 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 "linear_alloc.h"
-
-#include "thread-current-inl.h"
-
-namespace art {
-
-LinearAlloc::LinearAlloc(ArenaPool* pool) : lock_("linear alloc"), allocator_(pool) {
-}
-
-void* LinearAlloc::Realloc(Thread* self, void* ptr, size_t old_size, size_t new_size) {
- MutexLock mu(self, lock_);
- return allocator_.Realloc(ptr, old_size, new_size);
-}
-
-void* LinearAlloc::Alloc(Thread* self, size_t size) {
- MutexLock mu(self, lock_);
- return allocator_.Alloc(size);
-}
-
-void* LinearAlloc::AllocAlign16(Thread* self, size_t size) {
- MutexLock mu(self, lock_);
- return allocator_.AllocAlign16(size);
-}
-
-size_t LinearAlloc::GetUsedMemory() const {
- MutexLock mu(Thread::Current(), lock_);
- return allocator_.BytesUsed();
-}
-
-ArenaPool* LinearAlloc::GetArenaPool() {
- MutexLock mu(Thread::Current(), lock_);
- return allocator_.GetArenaPool();
-}
-
-bool LinearAlloc::Contains(void* ptr) const {
- MutexLock mu(Thread::Current(), lock_);
- return allocator_.Contains(ptr);
-}
-
-bool LinearAlloc::ContainsUnsafe(void* ptr) const {
- return allocator_.Contains(ptr);
-}
-
-} // namespace art
diff --git a/runtime/linear_alloc.h b/runtime/linear_alloc.h
index 1d01f84..c40af8a 100644
--- a/runtime/linear_alloc.h
+++ b/runtime/linear_alloc.h
@@ -18,44 +18,99 @@
#define ART_RUNTIME_LINEAR_ALLOC_H_
#include "base/arena_allocator.h"
+#include "base/casts.h"
#include "base/mutex.h"
namespace art {
class ArenaPool;
-// TODO: Support freeing if we add class unloading.
+enum class LinearAllocKind : uint32_t {
+ kNoGCRoots = 0, // No GC-root kind should always be 0.
+ kGCRootArray,
+ kArtMethodArray,
+ kArtFieldArray,
+ kDexCacheArray,
+ kArtMethod
+};
+
+// Header for every allocation in LinearAlloc. The header provides the type
+// and size information to the GC for invoking the right visitor.
+class TrackingHeader final {
+ public:
+ static constexpr uint32_t kIs16Aligned = 1;
+ TrackingHeader(size_t size, LinearAllocKind kind, bool is_16_aligned = false)
+ : kind_(kind), size_(dchecked_integral_cast<uint32_t>(size)) {
+ // We need the last bit to store 16-byte alignment flag.
+ CHECK_EQ(size_ & kIs16Aligned, 0u);
+ if (is_16_aligned) {
+ size_ |= kIs16Aligned;
+ }
+ }
+
+ LinearAllocKind GetKind() const { return kind_; }
+ // Since we are linearly allocating and hop from one object to the next during
+ // visits, reading 'size_ == 0' indicates that there are no more objects to
+ // visit in the given page. But ASAN detects it as use-after-poison access.
+ ATTRIBUTE_NO_SANITIZE_ADDRESS size_t GetSize() const { return size_ & ~kIs16Aligned; }
+ bool Is16Aligned() const { return size_ & kIs16Aligned; }
+
+ private:
+ LinearAllocKind kind_;
+ uint32_t size_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(TrackingHeader);
+};
+
+std::ostream& operator<<(std::ostream& os, LinearAllocKind value);
+
class LinearAlloc {
public:
- explicit LinearAlloc(ArenaPool* pool);
+ static constexpr size_t kAlignment = 8u;
+ static_assert(kAlignment >= ArenaAllocator::kAlignment);
+ static_assert(sizeof(TrackingHeader) == ArenaAllocator::kAlignment);
- void* Alloc(Thread* self, size_t size) REQUIRES(!lock_);
- void* AllocAlign16(Thread* self, size_t size) REQUIRES(!lock_);
+ explicit LinearAlloc(ArenaPool* pool, bool track_allocs)
+ : lock_("linear alloc"), allocator_(pool), track_allocations_(track_allocs) {}
+
+ void* Alloc(Thread* self, size_t size, LinearAllocKind kind) REQUIRES(!lock_);
+ void* AllocAlign16(Thread* self, size_t size, LinearAllocKind kind) REQUIRES(!lock_);
// Realloc never frees the input pointer, it is the caller's job to do this if necessary.
- void* Realloc(Thread* self, void* ptr, size_t old_size, size_t new_size) REQUIRES(!lock_);
+ void* Realloc(Thread* self, void* ptr, size_t old_size, size_t new_size, LinearAllocKind kind)
+ REQUIRES(!lock_);
// Allocate an array of structs of type T.
template<class T>
- T* AllocArray(Thread* self, size_t elements) REQUIRES(!lock_) {
- return reinterpret_cast<T*>(Alloc(self, elements * sizeof(T)));
+ T* AllocArray(Thread* self, size_t elements, LinearAllocKind kind) REQUIRES(!lock_) {
+ return reinterpret_cast<T*>(Alloc(self, elements * sizeof(T), kind));
}
// Return the number of bytes used in the allocator.
size_t GetUsedMemory() const REQUIRES(!lock_);
ArenaPool* GetArenaPool() REQUIRES(!lock_);
+ // Force arena allocator to ask for a new arena on next allocation. This
+ // is to preserve private/shared clean pages across zygote fork.
+ void SetupForPostZygoteFork(Thread* self) REQUIRES(!lock_);
- // Return true if the linear alloc contrains an address.
+ // Return true if the linear alloc contains an address.
bool Contains(void* ptr) const REQUIRES(!lock_);
// Unsafe version of 'Contains' only to be used when the allocator is going
// to be deleted.
- bool ContainsUnsafe(void* ptr) const NO_THREAD_SAFETY_ANALYSIS;
+ bool ContainsUnsafe(void* ptr) const NO_THREAD_SAFETY_ANALYSIS {
+ return allocator_.Contains(ptr);
+ }
+
+ // Set the given object as the first object for all the pages where the
+ // page-beginning overlaps with the object.
+ void SetFirstObject(void* begin, size_t bytes) const REQUIRES(lock_);
private:
mutable Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
ArenaAllocator allocator_ GUARDED_BY(lock_);
+ const bool track_allocations_;
DISALLOW_IMPLICIT_CONSTRUCTORS(LinearAlloc);
};
diff --git a/runtime/lock_word.h b/runtime/lock_word.h
index 84f45c2..21b40b7 100644
--- a/runtime/lock_word.h
+++ b/runtime/lock_word.h
@@ -183,22 +183,24 @@
LockState GetState() const {
CheckReadBarrierState();
- if ((!kUseReadBarrier && UNLIKELY(value_ == 0)) ||
- (kUseReadBarrier && UNLIKELY((value_ & kGCStateMaskShiftedToggled) == 0))) {
- return kUnlocked;
- } else {
- uint32_t internal_state = (value_ >> kStateShift) & kStateMask;
- switch (internal_state) {
- case kStateThinOrUnlocked:
- return kThinLocked;
- case kStateHash:
- return kHashCode;
- case kStateForwardingAddress:
- return kForwardingAddress;
- default:
- DCHECK_EQ(internal_state, static_cast<uint32_t>(kStateFat));
- return kFatLocked;
+ if (gUseReadBarrier || gUseUserfaultfd) {
+ if ((value_ & kGCStateMaskShiftedToggled) == 0) {
+ return kUnlocked;
}
+ } else if (value_ == 0) {
+ return kUnlocked;
+ }
+ uint32_t internal_state = (value_ >> kStateShift) & kStateMask;
+ switch (internal_state) {
+ case kStateThinOrUnlocked:
+ return kThinLocked;
+ case kStateHash:
+ return kHashCode;
+ case kStateForwardingAddress:
+ return kForwardingAddress;
+ default:
+ DCHECK_EQ(internal_state, static_cast<uint32_t>(kStateFat));
+ return kFatLocked;
}
}
@@ -288,7 +290,7 @@
void CheckReadBarrierState() const {
if (kIsDebugBuild && ((value_ >> kStateShift) & kStateMask) != kStateForwardingAddress) {
uint32_t rb_state = ReadBarrierState();
- if (!kUseReadBarrier) {
+ if (!gUseReadBarrier) {
DCHECK_EQ(rb_state, 0U);
} else {
DCHECK(rb_state == ReadBarrier::NonGrayState() ||
diff --git a/runtime/managed_stack-inl.h b/runtime/managed_stack-inl.h
index 678be8e..ca983ea 100644
--- a/runtime/managed_stack-inl.h
+++ b/runtime/managed_stack-inl.h
@@ -36,6 +36,7 @@
CHECK(top_shadow_frame_ != nullptr);
ShadowFrame* frame = top_shadow_frame_;
top_shadow_frame_ = frame->GetLink();
+ frame->ClearLink();
return frame;
}
diff --git a/runtime/managed_stack.h b/runtime/managed_stack.h
index 04a27fe..0e7dfe3 100644
--- a/runtime/managed_stack.h
+++ b/runtime/managed_stack.h
@@ -43,6 +43,8 @@
// code.
class PACKED(4) ManagedStack {
public:
+ static size_t constexpr kTaggedJniSpMask = 0x3;
+
ManagedStack()
: tagged_top_quick_frame_(TaggedTopQuickFrame::CreateNotTagged(nullptr)),
link_(nullptr),
@@ -75,8 +77,12 @@
return tagged_top_quick_frame_.GetSp();
}
- bool GetTopQuickFrameTag() const {
- return tagged_top_quick_frame_.GetTag();
+ bool GetTopQuickFrameGenericJniTag() const {
+ return tagged_top_quick_frame_.GetGenericJniTag();
+ }
+
+ bool GetTopQuickFrameJitJniTag() const {
+ return tagged_top_quick_frame_.GetJitJniTag();
}
bool HasTopQuickFrame() const {
@@ -89,10 +95,10 @@
tagged_top_quick_frame_ = TaggedTopQuickFrame::CreateNotTagged(top);
}
- void SetTopQuickFrameTagged(ArtMethod** top) {
+ void SetTopQuickFrameGenericJniTagged(ArtMethod** top) {
DCHECK(top_shadow_frame_ == nullptr);
DCHECK_ALIGNED(top, 4u);
- tagged_top_quick_frame_ = TaggedTopQuickFrame::CreateTagged(top);
+ tagged_top_quick_frame_ = TaggedTopQuickFrame::CreateGenericJniTagged(top);
}
static constexpr size_t TaggedTopQuickFrameOffset() {
@@ -129,26 +135,30 @@
return TaggedTopQuickFrame(reinterpret_cast<uintptr_t>(sp));
}
- static TaggedTopQuickFrame CreateTagged(ArtMethod** sp) {
+ static TaggedTopQuickFrame CreateGenericJniTagged(ArtMethod** sp) {
DCHECK_ALIGNED(sp, 4u);
return TaggedTopQuickFrame(reinterpret_cast<uintptr_t>(sp) | 1u);
}
// Get SP known to be not tagged and non-null.
ArtMethod** GetSpKnownNotTagged() const {
- DCHECK(!GetTag());
+ DCHECK(!GetGenericJniTag() && !GetJitJniTag());
DCHECK_NE(tagged_sp_, 0u);
return reinterpret_cast<ArtMethod**>(tagged_sp_);
}
ArtMethod** GetSp() const {
- return reinterpret_cast<ArtMethod**>(tagged_sp_ & ~static_cast<uintptr_t>(1u));
+ return reinterpret_cast<ArtMethod**>(tagged_sp_ & ~static_cast<uintptr_t>(kTaggedJniSpMask));
}
- bool GetTag() const {
+ bool GetGenericJniTag() const {
return (tagged_sp_ & 1u) != 0u;
}
+ bool GetJitJniTag() const {
+ return (tagged_sp_ & 2u) != 0u;
+ }
+
uintptr_t GetTaggedSp() const {
return tagged_sp_;
}
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 1327a24..059a33f 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -81,7 +81,7 @@
ObjPtr<mirror::Class> GetBoxedPrimitiveClass(Primitive::Type type)
REQUIRES_SHARED(Locks::mutator_lock_) {
ScopedAssertNoThreadSuspension ants(__FUNCTION__);
- jmethodID m = nullptr;
+ ArtMethod* m = nullptr;
switch (type) {
#define CASE_PRIMITIVE(primitive, _, java_name, __) \
case primitive: \
@@ -93,7 +93,7 @@
case Primitive::Type::kPrimVoid:
return nullptr;
}
- return jni::DecodeArtMethod(m)->GetDeclaringClass();
+ return m->GetDeclaringClass();
}
bool GetUnboxedTypeAndValue(ObjPtr<mirror::Object> o, Primitive::Type* type, JValue* value)
@@ -290,6 +290,13 @@
return false;
}
+ ObjPtr<mirror::Class> from_obj_type = from_obj->GetClass();
+ Primitive::Type from_primitive_type;
+ if (!GetUnboxedPrimitiveType(from_obj_type, &from_primitive_type)) {
+ ThrowClassCastException(from, to);
+ return false;
+ }
+
Primitive::Type unboxed_type;
JValue unboxed_value;
if (UNLIKELY(!GetUnboxedTypeAndValue(from_obj, &unboxed_type, &unboxed_value))) {
@@ -393,7 +400,7 @@
const char* old_cause = self->StartAssertNoThreadSuspension("MethodHandleInvokeTransform");
ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
- CREATE_SHADOW_FRAME(kNumRegsForTransform, &shadow_frame, called_method, /* dex pc */ 0);
+ CREATE_SHADOW_FRAME(kNumRegsForTransform, called_method, /* dex pc */ 0);
ShadowFrame* new_shadow_frame = shadow_frame_unique_ptr.get();
new_shadow_frame->SetVRegReference(0, method_handle.Get());
new_shadow_frame->SetVRegReference(1, sf.Get());
@@ -459,7 +466,7 @@
} else if (handle_kind == mirror::MethodHandle::Kind::kInvokeDirect) {
// String constructors are a special case, they are replaced with
// StringFactory methods.
- if (target_method->IsConstructor() && target_method->GetDeclaringClass()->IsStringClass()) {
+ if (target_method->IsStringConstructor()) {
DCHECK(handle_type->GetRType()->IsStringClass());
return WellKnownClasses::StringInitToStringFactory(target_method);
}
@@ -625,6 +632,10 @@
case mirror::MethodHandle::kInstanceGet: {
size_t obj_reg = operands->GetOperand(0);
ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(obj_reg);
+ if (obj == nullptr) {
+ ThrowNullPointerException("Receiver is null");
+ return false;
+ }
MethodHandleFieldGet(self, shadow_frame, obj, field, field_type, result);
return true;
}
@@ -648,6 +659,10 @@
callsite_type->GetPTypes()->Get(kPTypeIndex)->GetPrimitiveType(),
value_reg);
ObjPtr<mirror::Object> obj = shadow_frame.GetVRegReference(obj_reg);
+ if (obj == nullptr) {
+ ThrowNullPointerException("Receiver is null");
+ return false;
+ }
return MethodHandleFieldPut(self, shadow_frame, obj, field, field_type, value);
}
case mirror::MethodHandle::kStaticPut: {
@@ -769,7 +784,7 @@
const char* old_cause = self->StartAssertNoThreadSuspension("DoMethodHandleInvokeMethod");
ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
- CREATE_SHADOW_FRAME(num_regs, &shadow_frame, called_method, /* dex pc */ 0);
+ CREATE_SHADOW_FRAME(num_regs, called_method, /* dex pc */ 0);
ShadowFrame* new_shadow_frame = shadow_frame_unique_ptr.get();
CopyArgumentsFromCallerFrame(shadow_frame, new_shadow_frame, operands, first_dest_reg);
self->EndAssertNoThreadSuspension(old_cause);
@@ -856,19 +871,13 @@
if (atc == nullptr || !callsite_type->IsExactMatch(atc->GetMethodType())) {
// Cached asType adapter does not exist or is for another call site. Call
// MethodHandle::asType() to get an appropriate adapter.
- ArtMethod* as_type =
- jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_asType);
- uint32_t as_type_args[] = {
- static_cast<uint32_t>(reinterpret_cast<uintptr_t>(method_handle.Get())),
- static_cast<uint32_t>(reinterpret_cast<uintptr_t>(callsite_type.Get()))};
- JValue atc_result;
- as_type->Invoke(self, as_type_args, sizeof(as_type_args), &atc_result, "LL");
- if (atc_result.GetL() == nullptr) {
+ ArtMethod* as_type = WellKnownClasses::java_lang_invoke_MethodHandle_asType;
+ ObjPtr<mirror::MethodHandle> atc_method_handle = ObjPtr<mirror::MethodHandle>::DownCast(
+ as_type->InvokeVirtual<'L', 'L'>(self, method_handle.Get(), callsite_type.Get()));
+ if (atc_method_handle == nullptr) {
DCHECK(self->IsExceptionPending());
return false;
}
- ObjPtr<mirror::MethodHandle> atc_method_handle =
- down_cast<mirror::MethodHandle*>(atc_result.GetL());
atc.Assign(atc_method_handle);
DCHECK(!atc.IsNull());
}
@@ -909,10 +918,9 @@
const uint16_t num_vregs = callsite_type->NumberOfVRegs();
const char* old_cause = self->StartAssertNoThreadSuspension("EmulatedStackFrame to ShadowFrame");
- ArtMethod* invoke_exact =
- jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact);
+ ArtMethod* invoke_exact = WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact;
ShadowFrameAllocaUniquePtr shadow_frame =
- CREATE_SHADOW_FRAME(num_vregs, /*link*/ nullptr, invoke_exact, /*dex_pc*/ 0);
+ CREATE_SHADOW_FRAME(num_vregs, invoke_exact, /*dex_pc*/ 0);
emulated_frame->WriteToShadowFrame(self, callsite_type, 0, shadow_frame.get());
self->EndAssertNoThreadSuspension(old_cause);
diff --git a/runtime/method_handles_test.cc b/runtime/method_handles_test.cc
index eb3f2ad..588f861 100644
--- a/runtime/method_handles_test.cc
+++ b/runtime/method_handles_test.cc
@@ -71,7 +71,12 @@
}
} // namespace
-class MethodHandlesTest : public CommonRuntimeTest {};
+class MethodHandlesTest : public CommonRuntimeTest {
+ protected:
+ MethodHandlesTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+};
//
// Primitive -> Primitive Conversions
@@ -342,7 +347,7 @@
value.SetL(cl->FindPrimitiveClass('V'));
ASSERT_FALSE(TryConversion(soa.Self(), from, to, &value));
ASSERT_TRUE(soa.Self()->IsExceptionPending());
- ASSERT_TRUE(IsWrongMethodTypeException(soa.Self()->GetException()));
+ ASSERT_TRUE(IsClassCastException(soa.Self()->GetException()));
soa.Self()->ClearException();
}
diff --git a/runtime/metrics/reporter.cc b/runtime/metrics/reporter.cc
index a44066e..6fc1a14 100644
--- a/runtime/metrics/reporter.cc
+++ b/runtime/metrics/reporter.cc
@@ -16,11 +16,12 @@
#include "reporter.h"
-#include <algorithm>
-
#include <android-base/parseint.h>
+#include <algorithm>
+
#include "base/flags.h"
+#include "base/stl_util.h"
#include "oat_file_manager.h"
#include "runtime.h"
#include "runtime_options.h"
@@ -126,10 +127,17 @@
// Configure the backends
if (config_.dump_to_logcat) {
- backends_.emplace_back(new LogBackend(LogSeverity::INFO));
+ backends_.emplace_back(new LogBackend(std::make_unique<TextFormatter>(), LogSeverity::INFO));
}
if (config_.dump_to_file.has_value()) {
- backends_.emplace_back(new FileBackend(config_.dump_to_file.value()));
+ std::unique_ptr<MetricsFormatter> formatter;
+ if (config_.metrics_format == "xml") {
+ formatter = std::make_unique<XmlFormatter>();
+ } else {
+ formatter = std::make_unique<TextFormatter>();
+ }
+
+ backends_.emplace_back(new FileBackend(std::move(formatter), config_.dump_to_file.value()));
}
if (config_.dump_to_statsd) {
auto backend = CreateStatsdBackend();
@@ -189,12 +197,10 @@
}
}
-const ArtMetrics* MetricsReporter::GetMetrics() {
- return runtime_->GetMetrics();
-}
+ArtMetrics* MetricsReporter::GetMetrics() { return runtime_->GetMetrics(); }
void MetricsReporter::ReportMetrics() {
- const ArtMetrics* metrics = GetMetrics();
+ ArtMetrics* metrics = GetMetrics();
if (!session_started_) {
for (auto& backend : backends_) {
@@ -203,9 +209,7 @@
session_started_ = true;
}
- for (auto& backend : backends_) {
- metrics->ReportAllMetrics(backend.get());
- }
+ metrics->ReportAllMetricsAndResetValueMetrics(MakeNonOwningPointerVector(backends_));
}
void MetricsReporter::UpdateSessionInBackends() {
@@ -291,6 +295,7 @@
.dump_to_logcat = gFlags.MetricsWriteToLogcat(),
.dump_to_file = gFlags.MetricsWriteToFile.GetValueOptional(),
.dump_to_statsd = gFlags.MetricsWriteToStatsd(),
+ .metrics_format = gFlags.MetricsFormat(),
.period_spec = period_spec,
.reporting_num_mods = reporting_num_mods,
.reporting_mods = reporting_mods,
diff --git a/runtime/metrics/reporter.h b/runtime/metrics/reporter.h
index daeaf1f..865815e 100644
--- a/runtime/metrics/reporter.h
+++ b/runtime/metrics/reporter.h
@@ -78,6 +78,9 @@
// If set, provides a file name to enable metrics logging to a file.
std::optional<std::string> dump_to_file;
+ // Provides the desired output format for metrics written to a file.
+ std::string metrics_format;
+
// The reporting period configuration.
std::optional<ReportingPeriodSpec> period_spec;
@@ -133,7 +136,7 @@
// Returns the metrics to be reported.
// This exists only for testing purposes so that we can verify reporting with minimum
// runtime interference.
- virtual const ArtMetrics* GetMetrics();
+ virtual ArtMetrics* GetMetrics();
MetricsReporter(const ReportingConfig& config, Runtime* runtime);
diff --git a/runtime/metrics/reporter_test.cc b/runtime/metrics/reporter_test.cc
index 3807c77..848a74e 100644
--- a/runtime/metrics/reporter_test.cc
+++ b/runtime/metrics/reporter_test.cc
@@ -34,13 +34,10 @@
// other runtime setup logic.
class MockMetricsReporter : public MetricsReporter {
protected:
- MockMetricsReporter(const ReportingConfig& config, Runtime* runtime) :
- MetricsReporter(config, runtime),
- art_metrics_(new ArtMetrics()) {}
+ MockMetricsReporter(const ReportingConfig& config, Runtime* runtime)
+ : MetricsReporter(config, runtime), art_metrics_(std::make_unique<ArtMetrics>()) {}
- const ArtMetrics* GetMetrics() override {
- return art_metrics_.get();
- }
+ ArtMetrics* GetMetrics() override { return art_metrics_.get(); }
std::unique_ptr<ArtMetrics> art_metrics_;
@@ -174,9 +171,9 @@
CompilationReason reason = CompilationReason::kUnknown) {
// TODO: we should iterate through all the other metrics to make sure they were not
// reported. However, we don't have an easy to use iteration mechanism over metrics yet.
- // We should ads one
+ // We should add one
ASSERT_EQ(backend_->GetReports().size(), size);
- for (auto report : backend_->GetReports()) {
+ for (const TestBackend::Report& report : backend_->GetReports()) {
ASSERT_EQ(report.data.Get(DatumId::kClassVerificationCount), with_metrics ? 2u : 0u);
ASSERT_EQ(report.data.Get(DatumId::kJitMethodCompileCount), with_metrics ? 1u : 0u);
}
@@ -220,7 +217,7 @@
WaitForReport(/*report_count=*/ 1, /*sleep_period_ms=*/ 50);
VerifyReports(/*size=*/ 1, /*with_metrics*/ true);
- // We still should not report at period.
+ // We should still not report continuously.
ASSERT_FALSE(ShouldContinueReporting());
}
@@ -241,11 +238,11 @@
WaitForReport(/*report_count=*/ 2, /*sleep_period_ms=*/ 500);
VerifyReports(/*size=*/ 2, /*with_metrics*/ true);
- // We should not longer report at period.
+ // We should no longer report continuously.
ASSERT_FALSE(ShouldContinueReporting());
}
-// LARGE TEST: This takes take 2s to run.
+// LARGE TEST: This test take 2s to run.
// Verifies startup reporting, followed by continuous reporting.
TEST_F(MetricsReporterTest, StartupAndPeriodContinuous) {
SetupReporter("S,1,*");
@@ -262,7 +259,7 @@
WaitForReport(/*report_count=*/ 3, /*sleep_period_ms=*/ 500);
VerifyReports(/*size=*/ 3, /*with_metrics*/ true);
- // We should keep reporting at period.
+ // We should keep reporting continuously.
ASSERT_TRUE(ShouldContinueReporting());
}
@@ -282,11 +279,11 @@
WaitForReport(/*report_count=*/ 1, /*sleep_period_ms=*/ 500);
VerifyReports(/*size=*/ 1, /*with_metrics*/ true);
- // We should not longer report at period.
+ // We should no longer report continuously.
ASSERT_FALSE(ShouldContinueReporting());
}
-// LARGE TEST: This takes take 5s to run.
+// LARGE TEST: This test takes 5s to run.
// Verifies a sequence of reporting, at different interval of times.
TEST_F(MetricsReporterTest, PeriodContinuous) {
SetupReporter("1,2,*");
@@ -303,7 +300,7 @@
WaitForReport(/*report_count=*/ 3, /*sleep_period_ms=*/ 500);
VerifyReports(/*size=*/ 3, /*with_metrics*/ true);
- // We should keep reporting at period.
+ // We should keep reporting continuously.
ASSERT_TRUE(ShouldContinueReporting());
}
@@ -323,13 +320,13 @@
WaitForReport(/*report_count=*/ 1, /*sleep_period_ms=*/ 500);
VerifyReports(/*size=*/ 1, /*with_metrics*/ false);
- // We should not longer report at period.
+ // We should no longer report continuously.
ASSERT_FALSE(ShouldContinueReporting());
}
// Verify we don't start reporting if the sample rate is set to 0.
TEST_F(MetricsReporterTest, SampleRateDisable) {
- SetupReporter("1", /*session_id=*/ 1, /*reporting_mods=*/ 0);
+ SetupReporter("1,*", /*session_id=*/ 1, /*reporting_mods=*/ 0);
// The background thread should not start.
ASSERT_FALSE(MaybeStartBackgroundThread(/*add_metrics=*/ false));
@@ -341,7 +338,7 @@
// Verify we don't start reporting if the sample rate is low and the session does
// not meet conditions.
TEST_F(MetricsReporterTest, SampleRateDisable24) {
- SetupReporter("1", /*session_id=*/ 125, /*reporting_mods=*/ 24);
+ SetupReporter("1,*", /*session_id=*/ 125, /*reporting_mods=*/ 24);
// The background thread should not start.
ASSERT_FALSE(MaybeStartBackgroundThread(/*add_metrics=*/ false));
@@ -353,9 +350,9 @@
// Verify we start reporting if the sample rate and the session meet
// reporting conditions
TEST_F(MetricsReporterTest, SampleRateEnable50) {
- SetupReporter("1", /*session_id=*/ 125, /*reporting_mods=*/ 50);
+ SetupReporter("1,*", /*session_id=*/ 125, /*reporting_mods=*/ 50);
- // The background thread should not start.
+ // The background thread should start.
ASSERT_TRUE(MaybeStartBackgroundThread(/*add_metrics=*/ false));
ASSERT_FALSE(ShouldReportAtStartup());
@@ -365,7 +362,7 @@
// Verify we start reporting if the sample rate and the session meet
// reporting conditions
TEST_F(MetricsReporterTest, SampleRateEnableAll) {
- SetupReporter("1", /*session_id=*/ 1099, /*reporting_mods=*/ 100);
+ SetupReporter("1,*", /*session_id=*/ 1099, /*reporting_mods=*/ 100);
// The background thread should start.
ASSERT_TRUE(MaybeStartBackgroundThread(/*add_metrics=*/ false));
@@ -411,7 +408,7 @@
const std::string& spec_str,
bool startup_first,
bool continuous,
- std::vector<uint32_t> periods) {
+ const std::vector<uint32_t>& periods) {
Verify(spec_str, true, startup_first, continuous, periods);
}
@@ -420,7 +417,7 @@
bool valid,
bool startup_first,
bool continuous,
- std::vector<uint32_t> periods) {
+ const std::vector<uint32_t>& periods) {
std::string error_msg;
std::optional<ReportingPeriodSpec> spec = ReportingPeriodSpec::Parse(spec_str, &error_msg);
diff --git a/runtime/metrics/statsd.cc b/runtime/metrics/statsd.cc
index f68d507..9a11530 100644
--- a/runtime/metrics/statsd.cc
+++ b/runtime/metrics/statsd.cc
@@ -19,6 +19,10 @@
#include "arch/instruction_set.h"
#include "base/compiler_filter.h"
#include "base/metrics/metrics.h"
+#include "gc/collector/mark_compact.h"
+#include "gc/heap.h"
+#include "gc/space/image_space.h"
+#include "runtime.h"
#include "statslog_art.h"
#pragma clang diagnostic push
@@ -44,27 +48,49 @@
case DatumId::kClassVerificationTotalTime:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_CLASS_VERIFICATION_TIME_COUNTER_MICROS);
+ case DatumId::kClassVerificationTotalTimeDelta:
+ return std::make_optional(
+ statsd::ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_CLASS_VERIFICATION_TIME_MICROS);
case DatumId::kJitMethodCompileTotalTime:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_JIT_METHOD_COMPILE_TIME_MICROS);
+ case DatumId::kJitMethodCompileTotalTimeDelta:
+ return std::make_optional(
+ statsd::ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_JIT_METHOD_COMPILE_TIME_MICROS);
case DatumId::kClassLoadingTotalTime:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_CLASS_LOADING_TIME_COUNTER_MICROS);
+ case DatumId::kClassLoadingTotalTimeDelta:
+ return std::make_optional(
+ statsd::ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_CLASS_LOADING_TIME_MICROS);
case DatumId::kClassVerificationCount:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_CLASS_VERIFICATION_COUNT);
+ case DatumId::kClassVerificationCountDelta:
+ return std::make_optional(
+ statsd::ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_CLASS_VERIFICATION_COUNT);
case DatumId::kWorldStopTimeDuringGCAvg:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_WORLD_STOP_TIME_AVG_MICROS);
case DatumId::kYoungGcCount:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_YOUNG_GENERATION_COLLECTION_COUNT);
+ case DatumId::kYoungGcCountDelta:
+ return std::make_optional(
+ statsd::
+ ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_GC_YOUNG_GENERATION_COLLECTION_COUNT);
case DatumId::kFullGcCount:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_FULL_HEAP_COLLECTION_COUNT);
+ case DatumId::kFullGcCountDelta:
+ return std::make_optional(
+ statsd::ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_GC_FULL_HEAP_COLLECTION_COUNT);
case DatumId::kTotalBytesAllocated:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_TOTAL_BYTES_ALLOCATED);
+ case DatumId::kTotalBytesAllocatedDelta:
+ return std::make_optional(
+ statsd::ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_GC_TOTAL_BYTES_ALLOCATED);
case DatumId::kYoungGcCollectionTime:
return std::make_optional(
statsd::
@@ -83,6 +109,9 @@
case DatumId::kJitMethodCompileCount:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_JIT_METHOD_COMPILE_COUNT);
+ case DatumId::kJitMethodCompileCountDelta:
+ return std::make_optional(
+ statsd::ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_JIT_METHOD_COMPILE_COUNT);
case DatumId::kYoungGcTracingThroughput:
return std::make_optional(
statsd::
@@ -94,6 +123,9 @@
case DatumId::kTotalGcCollectionTime:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_TOTAL_COLLECTION_TIME_MS);
+ case DatumId::kTotalGcCollectionTimeDelta:
+ return std::make_optional(
+ statsd::ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_GC_TOTAL_COLLECTION_TIME_MS);
case DatumId::kYoungGcThroughputAvg:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_YOUNG_GENERATION_COLLECTION_THROUGHPUT_AVG_MB_PER_SEC);
@@ -106,6 +138,60 @@
case DatumId::kFullGcTracingThroughputAvg:
return std::make_optional(
statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_FULL_HEAP_TRACING_THROUGHPUT_AVG_MB_PER_SEC);
+ case DatumId::kGcWorldStopTime:
+ return std::make_optional(
+ statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_WORLD_STOP_TIME_US);
+ case DatumId::kGcWorldStopTimeDelta:
+ return std::make_optional(
+ statsd::ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_GC_WORLD_STOP_TIME_US);
+ case DatumId::kGcWorldStopCount:
+ return std::make_optional(
+ statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_WORLD_STOP_COUNT);
+ case DatumId::kGcWorldStopCountDelta:
+ return std::make_optional(
+ statsd::ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_GC_WORLD_STOP_COUNT);
+ case DatumId::kYoungGcScannedBytes:
+ return std::make_optional(
+ statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_YOUNG_GENERATION_COLLECTION_SCANNED_BYTES);
+ case DatumId::kYoungGcScannedBytesDelta:
+ return std::make_optional(
+ statsd::
+ ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_GC_YOUNG_GENERATION_COLLECTION_SCANNED_BYTES);
+ case DatumId::kYoungGcFreedBytes:
+ return std::make_optional(
+ statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_YOUNG_GENERATION_COLLECTION_FREED_BYTES);
+ case DatumId::kYoungGcFreedBytesDelta:
+ return std::make_optional(
+ statsd::
+ ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_GC_YOUNG_GENERATION_COLLECTION_FREED_BYTES);
+ case DatumId::kYoungGcDuration:
+ return std::make_optional(
+ statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_YOUNG_GENERATION_COLLECTION_DURATION_MS);
+ case DatumId::kYoungGcDurationDelta:
+ return std::make_optional(
+ statsd::
+ ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_GC_YOUNG_GENERATION_COLLECTION_DURATION_MS);
+ case DatumId::kFullGcScannedBytes:
+ return std::make_optional(
+ statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_FULL_HEAP_COLLECTION_SCANNED_BYTES);
+ case DatumId::kFullGcScannedBytesDelta:
+ return std::make_optional(
+ statsd::
+ ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_GC_FULL_HEAP_COLLECTION_SCANNED_BYTES);
+ case DatumId::kFullGcFreedBytes:
+ return std::make_optional(
+ statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_FULL_HEAP_COLLECTION_FREED_BYTES);
+ case DatumId::kFullGcFreedBytesDelta:
+ return std::make_optional(
+ statsd::
+ ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_GC_FULL_HEAP_COLLECTION_FREED_BYTES);
+ case DatumId::kFullGcDuration:
+ return std::make_optional(
+ statsd::ART_DATUM_REPORTED__KIND__ART_DATUM_GC_FULL_HEAP_COLLECTION_DURATION_MS);
+ case DatumId::kFullGcDurationDelta:
+ return std::make_optional(
+ statsd::
+ ART_DATUM_DELTA_REPORTED__KIND__ART_DATUM_DELTA_GC_FULL_HEAP_COLLECTION_DURATION_MS);
}
}
@@ -196,6 +282,8 @@
return statsd::ART_DATUM_REPORTED__ISA__ART_ISA_ARM;
case InstructionSet::kArm64:
return statsd::ART_DATUM_REPORTED__ISA__ART_ISA_ARM64;
+ case InstructionSet::kRiscv64:
+ return statsd::ART_DATUM_REPORTED__ISA__ART_ISA_RISCV64;
case InstructionSet::kX86:
return statsd::ART_DATUM_REPORTED__ISA__ART_ISA_X86;
case InstructionSet::kX86_64:
@@ -205,6 +293,51 @@
}
}
+constexpr int32_t EncodeGcCollectorType(gc::CollectorType collector_type) {
+ switch (collector_type) {
+ case gc::CollectorType::kCollectorTypeMS:
+ return statsd::ART_DATUM_REPORTED__GC__ART_GC_COLLECTOR_TYPE_MARK_SWEEP;
+ case gc::CollectorType::kCollectorTypeCMS:
+ return statsd::ART_DATUM_REPORTED__GC__ART_GC_COLLECTOR_TYPE_CONCURRENT_MARK_SWEEP;
+ case gc::CollectorType::kCollectorTypeCMC:
+ return statsd::ART_DATUM_REPORTED__GC__ART_GC_COLLECTOR_TYPE_CONCURRENT_MARK_COMPACT;
+ case gc::CollectorType::kCollectorTypeSS:
+ return statsd::ART_DATUM_REPORTED__GC__ART_GC_COLLECTOR_TYPE_SEMI_SPACE;
+ case gc::kCollectorTypeCC:
+ return statsd::ART_DATUM_REPORTED__GC__ART_GC_COLLECTOR_TYPE_CONCURRENT_COPYING;
+ case gc::kCollectorTypeCCBackground:
+ return statsd::ART_DATUM_REPORTED__GC__ART_GC_COLLECTOR_TYPE_CONCURRENT_COPYING_BACKGROUND;
+ case gc::kCollectorTypeNone:
+ case gc::kCollectorTypeInstrumentation:
+ case gc::kCollectorTypeAddRemoveAppImageSpace:
+ case gc::kCollectorTypeDebugger:
+ case gc::kCollectorTypeHomogeneousSpaceCompact:
+ case gc::kCollectorTypeClassLinker:
+ case gc::kCollectorTypeJitCodeCache:
+ case gc::kCollectorTypeHprof:
+ case gc::kCollectorTypeAddRemoveSystemWeakHolder:
+ case gc::kCollectorTypeGetObjectsAllocated:
+ case gc::kCollectorTypeCriticalSection:
+ case gc::kCollectorTypeHeapTrim:
+ return statsd::ART_DATUM_REPORTED__GC__ART_GC_COLLECTOR_TYPE_UNKNOWN;
+ }
+}
+
+int32_t EncodeUffdMinorFaultSupport() {
+ auto [uffd_supported, minor_fault_supported] = gc::collector::MarkCompact::GetUffdAndMinorFault();
+
+ if (uffd_supported) {
+ if (minor_fault_supported) {
+ return statsd::ART_DATUM_REPORTED__UFFD_SUPPORT__ART_UFFD_SUPPORT_MINOR_FAULT_MODE_SUPPORTED;
+ } else {
+ return statsd::
+ ART_DATUM_REPORTED__UFFD_SUPPORT__ART_UFFD_SUPPORT_MINOR_FAULT_MODE_NOT_SUPPORTED;
+ }
+ } else {
+ return statsd::ART_DATUM_REPORTED__UFFD_SUPPORT__ART_UFFD_SUPPORT_UFFD_NOT_SUPPORTED;
+ }
+}
+
class StatsdBackend : public MetricsBackend {
public:
void BeginOrUpdateSession(const SessionData& session_data) override {
@@ -218,22 +351,41 @@
void ReportCounter(DatumId counter_type, uint64_t value) override {
std::optional<int32_t> datum_id = EncodeDatumId(counter_type);
- if (datum_id.has_value()) {
- statsd::stats_write(
- statsd::ART_DATUM_REPORTED,
- session_data_.session_id,
- session_data_.uid,
- EncodeCompileFilter(session_data_.compiler_filter),
- EncodeCompilationReason(session_data_.compilation_reason),
- current_timestamp_,
- /*thread_type=*/0, // TODO: collect and report thread type (0 means UNKNOWN, but that
- // constant is not present in all branches)
- datum_id.value(),
- static_cast<int64_t>(value),
- statsd::ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_UNKNOWN,
- statsd::ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_UNKNOWN,
- EncodeInstructionSet(kRuntimeISA));
+ if (!datum_id.has_value()) {
+ return;
}
+
+ int32_t atom;
+ switch (counter_type) {
+#define EVENT_METRIC_CASE(name, ...) case DatumId::k##name:
+ ART_EVENT_METRICS(EVENT_METRIC_CASE)
+#undef EVENT_METRIC_CASE
+ atom = statsd::ART_DATUM_REPORTED;
+ break;
+
+#define VALUE_METRIC_CASE(name, type, ...) case DatumId::k##name:
+ ART_VALUE_METRICS(VALUE_METRIC_CASE)
+#undef VALUE_METRIC_CASE
+ atom = statsd::ART_DATUM_DELTA_REPORTED;
+ break;
+ }
+
+ statsd::stats_write(
+ atom,
+ session_data_.session_id,
+ session_data_.uid,
+ EncodeCompileFilter(session_data_.compiler_filter),
+ EncodeCompilationReason(session_data_.compilation_reason),
+ current_timestamp_,
+ 0, // TODO: collect and report thread type (0 means UNKNOWN, but that
+ // constant is not present in all branches)
+ datum_id.value(),
+ static_cast<int64_t>(value),
+ statsd::ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_UNKNOWN,
+ statsd::ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_UNKNOWN,
+ EncodeInstructionSet(kRuntimeISA),
+ EncodeGcCollectorType(Runtime::Current()->GetHeap()->GetForegroundCollectorType()),
+ EncodeUffdMinorFaultSupport());
}
void ReportHistogram(DatumId /*histogram_type*/,
@@ -256,6 +408,20 @@
std::unique_ptr<MetricsBackend> CreateStatsdBackend() { return std::make_unique<StatsdBackend>(); }
+void ReportDeviceMetrics() {
+ Runtime* runtime = Runtime::Current();
+ int32_t boot_image_status;
+ if (runtime->GetHeap()->HasBootImageSpace() && !runtime->HasImageWithProfile()) {
+ boot_image_status = statsd::ART_DEVICE_DATUM_REPORTED__BOOT_IMAGE_STATUS__STATUS_FULL;
+ } else if (runtime->GetHeap()->HasBootImageSpace() &&
+ runtime->GetHeap()->GetBootImageSpaces()[0]->GetProfileFiles().empty()) {
+ boot_image_status = statsd::ART_DEVICE_DATUM_REPORTED__BOOT_IMAGE_STATUS__STATUS_MINIMAL;
+ } else {
+ boot_image_status = statsd::ART_DEVICE_DATUM_REPORTED__BOOT_IMAGE_STATUS__STATUS_NONE;
+ }
+ statsd::stats_write(statsd::ART_DEVICE_DATUM_REPORTED, boot_image_status);
+}
+
} // namespace metrics
} // namespace art
diff --git a/runtime/metrics/statsd.h b/runtime/metrics/statsd.h
index a99d510..cb84825 100644
--- a/runtime/metrics/statsd.h
+++ b/runtime/metrics/statsd.h
@@ -27,8 +27,10 @@
// Statsd is only supported on Android
#ifdef __ANDROID__
std::unique_ptr<MetricsBackend> CreateStatsdBackend();
+void ReportDeviceMetrics();
#else
inline std::unique_ptr<MetricsBackend> CreateStatsdBackend() { return nullptr; }
+inline void ReportDeviceMetrics() {}
#endif
} // namespace metrics
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index b0e77b4..a7faa37 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -36,12 +36,11 @@
return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0, pointer_size);
}
-template<VerifyObjectFlags kVerifyFlags>
+template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
inline size_t Array::SizeOf() {
- // No read barrier is needed for reading a constant primitive field through
- // constant reference field chain. See ReadBarrierOption.
size_t component_size_shift =
- GetClass<kVerifyFlags, kWithoutReadBarrier>()->GetComponentSizeShift();
+ GetClass<kVerifyFlags, kReadBarrierOption>()
+ ->template GetComponentSizeShift<kReadBarrierOption>();
// Don't need to check this since we already check this in GetClass.
int32_t component_count =
GetLength<static_cast<VerifyObjectFlags>(kVerifyFlags & ~kVerifyThis)>();
@@ -98,7 +97,7 @@
if (kTransactionActive) {
Runtime::Current()->RecordWriteArray(this, i, GetWithoutChecks(i));
}
- DCHECK(CheckIsValidIndex<kVerifyFlags>(i));
+ DCHECK(CheckIsValidIndex<kVerifyFlags>(i)) << i << " " << GetLength<kVerifyFlags>();
GetData()[i] = value;
}
// Backward copy where elements are of aligned appropriately for T. Count is in T sized units.
diff --git a/runtime/mirror/array.h b/runtime/mirror/array.h
index 4bf9dee..dfe7d47 100644
--- a/runtime/mirror/array.h
+++ b/runtime/mirror/array.h
@@ -58,7 +58,8 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Roles::uninterruptible_);
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
+ ReadBarrierOption kReadBarrierOption = kWithoutReadBarrier>
size_t SizeOf() REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
ALWAYS_INLINE int32_t GetLength() REQUIRES_SHARED(Locks::mutator_lock_) {
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index b6bd22e..77f78c5 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -1077,10 +1077,9 @@
return 1U << GetComponentSizeShift();
}
+template <ReadBarrierOption kReadBarrierOption>
inline size_t Class::GetComponentSizeShift() {
- // No read barrier is needed for reading a constant primitive field through
- // constant reference field. See ReadBarrierOption.
- return GetComponentType<kDefaultVerifyFlags, kWithoutReadBarrier>()->GetPrimitiveTypeSizeShift();
+ return GetComponentType<kDefaultVerifyFlags, kReadBarrierOption>()->GetPrimitiveTypeSizeShift();
}
inline bool Class::IsObjectClass() {
@@ -1106,11 +1105,9 @@
return GetComponentType<kVerifyFlags, kWithoutReadBarrier>() != nullptr;
}
-template<VerifyObjectFlags kVerifyFlags>
+template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
inline bool Class::IsObjectArrayClass() {
- // We do not need a read barrier here as the primitive type is constant,
- // both from-space and to-space component type classes shall yield the same result.
- const ObjPtr<Class> component_type = GetComponentType<kVerifyFlags, kWithoutReadBarrier>();
+ const ObjPtr<Class> component_type = GetComponentType<kVerifyFlags, kReadBarrierOption>();
constexpr VerifyObjectFlags kNewFlags = RemoveThisFlags(kVerifyFlags);
return component_type != nullptr && !component_type->IsPrimitive<kNewFlags>();
}
diff --git a/runtime/mirror/class-refvisitor-inl.h b/runtime/mirror/class-refvisitor-inl.h
index 8c85387..ee5c11f 100644
--- a/runtime/mirror/class-refvisitor-inl.h
+++ b/runtime/mirror/class-refvisitor-inl.h
@@ -51,22 +51,39 @@
}
}
-template<ReadBarrierOption kReadBarrierOption, class Visitor>
+template<ReadBarrierOption kReadBarrierOption, bool kVisitProxyMethod, class Visitor>
void Class::VisitNativeRoots(Visitor& visitor, PointerSize pointer_size) {
VisitFields<kReadBarrierOption>([&](ArtField* field) REQUIRES_SHARED(art::Locks::mutator_lock_) {
field->VisitRoots(visitor);
- if (kIsDebugBuild && IsResolved()) {
+ if (kIsDebugBuild && !gUseUserfaultfd && IsResolved()) {
CHECK_EQ(field->GetDeclaringClass<kReadBarrierOption>(), this)
<< GetStatus() << field->GetDeclaringClass()->PrettyClass() << " != " << PrettyClass();
}
});
// Don't use VisitMethods because we don't want to hit the class-ext methods twice.
for (ArtMethod& method : GetMethods(pointer_size)) {
- method.VisitRoots<kReadBarrierOption>(visitor, pointer_size);
+ method.VisitRoots<kReadBarrierOption, kVisitProxyMethod>(visitor, pointer_size);
}
ObjPtr<ClassExt> ext(GetExtData<kDefaultVerifyFlags, kReadBarrierOption>());
if (!ext.IsNull()) {
- ext->VisitNativeRoots<kReadBarrierOption, Visitor>(visitor, pointer_size);
+ ext->VisitNativeRoots<kReadBarrierOption, kVisitProxyMethod>(visitor, pointer_size);
+ }
+}
+
+template<ReadBarrierOption kReadBarrierOption>
+void Class::VisitObsoleteDexCaches(DexCacheVisitor& visitor) {
+ ObjPtr<ClassExt> ext(GetExtData<kDefaultVerifyFlags, kReadBarrierOption>());
+ if (!ext.IsNull()) {
+ ext->VisitDexCaches<kDefaultVerifyFlags, kReadBarrierOption>(visitor);
+ }
+}
+
+template<ReadBarrierOption kReadBarrierOption, class Visitor>
+void Class::VisitObsoleteClass(Visitor& visitor) {
+ ObjPtr<ClassExt> ext(GetExtData<kDefaultVerifyFlags, kReadBarrierOption>());
+ if (!ext.IsNull()) {
+ ObjPtr<Class> klass = ext->GetObsoleteClass<kDefaultVerifyFlags, kReadBarrierOption>();
+ visitor(klass);
}
}
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 37a4197..7967600 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -59,10 +59,6 @@
namespace art {
-// TODO: move to own CC file?
-constexpr size_t BitString::kBitSizeAtPosition[BitString::kCapacity];
-constexpr size_t BitString::kCapacity;
-
namespace mirror {
using android::base::StringPrintf;
@@ -1436,7 +1432,7 @@
void Class::ClearSkipAccessChecksFlagOnAllMethods(PointerSize pointer_size) {
DCHECK(IsVerified());
for (auto& m : GetMethods(pointer_size)) {
- if (!m.IsNative() && m.IsInvokable()) {
+ if (m.IsManagedAndInvokable()) {
m.ClearSkipAccessChecks();
}
}
@@ -1445,7 +1441,7 @@
void Class::ClearMustCountLocksFlagOnAllMethods(PointerSize pointer_size) {
DCHECK(IsVerified());
for (auto& m : GetMethods(pointer_size)) {
- if (!m.IsNative() && m.IsInvokable()) {
+ if (m.IsManagedAndInvokable()) {
m.ClearMustCountLocks();
}
}
@@ -1454,7 +1450,7 @@
void Class::ClearDontCompileFlagOnAllMethods(PointerSize pointer_size) {
DCHECK(IsVerified());
for (auto& m : GetMethods(pointer_size)) {
- if (!m.IsNative() && m.IsInvokable()) {
+ if (m.IsManagedAndInvokable()) {
m.ClearDontCompile();
}
}
@@ -1463,7 +1459,7 @@
void Class::SetSkipAccessChecksFlagOnAllMethods(PointerSize pointer_size) {
DCHECK(IsVerified());
for (auto& m : GetMethods(pointer_size)) {
- if (!m.IsNative() && m.IsInvokable()) {
+ if (m.IsManagedAndInvokable()) {
m.SetSkipAccessChecks();
}
}
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 90efce5..67b9559 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -64,6 +64,7 @@
template<typename T> class StrideIterator;
template<size_t kNumReferences> class PACKED(4) StackHandleScope;
class Thread;
+class DexCacheVisitor;
namespace mirror {
@@ -236,6 +237,15 @@
// Set access flags, recording the change if running inside a Transaction.
void SetAccessFlags(uint32_t new_access_flags) REQUIRES_SHARED(Locks::mutator_lock_);
+ void SetInBootImageAndNotInPreloadedClasses() REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint32_t flags = GetAccessFlags();
+ SetAccessFlags(flags | kAccInBootImageAndNotInPreloadedClasses);
+ }
+
+ ALWAYS_INLINE bool IsInBootImageAndNotInPreloadedClasses() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return (GetAccessFlags() & kAccInBootImageAndNotInPreloadedClasses) != 0;
+ }
+
// Returns true if the class is an enum.
ALWAYS_INLINE bool IsEnum() REQUIRES_SHARED(Locks::mutator_lock_) {
return (GetAccessFlags() & kAccEnum) != 0;
@@ -486,6 +496,7 @@
size_t GetComponentSize() REQUIRES_SHARED(Locks::mutator_lock_);
+ template<ReadBarrierOption kReadBarrierOption = kWithoutReadBarrier>
size_t GetComponentSizeShift() REQUIRES_SHARED(Locks::mutator_lock_);
bool IsObjectClass() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -495,7 +506,8 @@
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsInstantiable() REQUIRES_SHARED(Locks::mutator_lock_);
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
+ ReadBarrierOption kReadBarrierOption = kWithoutReadBarrier>
ALWAYS_INLINE bool IsObjectArrayClass() REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
@@ -553,7 +565,7 @@
// The size of java.lang.Class.class.
static uint32_t ClassClassSize(PointerSize pointer_size) {
// The number of vtable entries in java.lang.Class.
- uint32_t vtable_entries = Object::kVTableLength + 67;
+ uint32_t vtable_entries = Object::kVTableLength + 73;
return ComputeClassSize(true, vtable_entries, 0, 0, 4, 1, 0, pointer_size);
}
@@ -570,6 +582,9 @@
static constexpr MemberOffset ObjectSizeAllocFastPathOffset() {
return OFFSET_OF_OBJECT_MEMBER(Class, object_size_alloc_fast_path_);
}
+ static constexpr MemberOffset ClinitThreadIdOffset() {
+ return OFFSET_OF_OBJECT_MEMBER(Class, clinit_thread_id_);
+ }
ALWAYS_INLINE void SetObjectSize(uint32_t new_object_size) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -1170,10 +1185,19 @@
// Visit native roots visits roots which are keyed off the native pointers such as ArtFields and
// ArtMethods.
- template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier, class Visitor>
+ template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier,
+ bool kVisitProxyMethod = true,
+ class Visitor>
void VisitNativeRoots(Visitor& visitor, PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Visit obsolete dex caches possibly stored in ext_data_
+ template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+ void VisitObsoleteDexCaches(DexCacheVisitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier, class Visitor>
+ void VisitObsoleteClass(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
// Visit ArtMethods directly owned by this class.
template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier, class Visitor>
void VisitMethods(Visitor visitor, PointerSize pointer_size)
@@ -1417,7 +1441,7 @@
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
// 'Class' Object Fields
- // Order governed by java field ordering. See art::ClassLinker::LinkFields.
+ // Order governed by java field ordering. See art::ClassLinker::LinkFieldsHelper::LinkFields.
// Defining class loader, or null for the "bootstrap" system loader.
HeapReference<ClassLoader> class_loader_;
diff --git a/runtime/mirror/class_ext-inl.h b/runtime/mirror/class_ext-inl.h
index ddd46b9..9d6ac43 100644
--- a/runtime/mirror/class_ext-inl.h
+++ b/runtime/mirror/class_ext-inl.h
@@ -23,6 +23,7 @@
#include "art_method-inl.h"
#include "base/enums.h"
#include "base/globals.h"
+#include "class_linker.h"
#include "handle_scope.h"
#include "jni/jni_internal.h"
#include "jni_id_type.h"
@@ -148,8 +149,9 @@
return GetFieldObject<Throwable>(OFFSET_OF_OBJECT_MEMBER(ClassExt, erroneous_state_error_));
}
+template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
inline ObjPtr<ObjectArray<DexCache>> ClassExt::GetObsoleteDexCaches() {
- return GetFieldObject<ObjectArray<DexCache>>(
+ return GetFieldObject<ObjectArray<DexCache>, kVerifyFlags, kReadBarrierOption>(
OFFSET_OF_OBJECT_MEMBER(ClassExt, obsolete_dex_caches_));
}
@@ -164,13 +166,25 @@
return GetFieldObject<Object>(OFFSET_OF_OBJECT_MEMBER(ClassExt, original_dex_file_));
}
-template<ReadBarrierOption kReadBarrierOption, class Visitor>
+template<ReadBarrierOption kReadBarrierOption, bool kVisitProxyMethod, class Visitor>
void ClassExt::VisitNativeRoots(Visitor& visitor, PointerSize pointer_size) {
VisitMethods<kReadBarrierOption>([&](ArtMethod* method) {
- method->VisitRoots<kReadBarrierOption>(visitor, pointer_size);
+ method->VisitRoots<kReadBarrierOption, kVisitProxyMethod>(visitor, pointer_size);
}, pointer_size);
}
+template<VerifyObjectFlags kVerifyFlags, ReadBarrierOption kReadBarrierOption>
+void ClassExt::VisitDexCaches(DexCacheVisitor& visitor) {
+ ObjPtr<ObjectArray<DexCache>> arr(GetObsoleteDexCaches<kVerifyFlags, kReadBarrierOption>());
+ if (!arr.IsNull()) {
+ int32_t len = arr->GetLength();
+ for (int32_t i = 0; i < len; i++) {
+ ObjPtr<mirror::DexCache> dex_cache = arr->Get<kVerifyFlags, kReadBarrierOption>(i);
+ visitor.Visit(dex_cache);
+ }
+ }
+}
+
template<ReadBarrierOption kReadBarrierOption, class Visitor>
void ClassExt::VisitMethods(Visitor visitor, PointerSize pointer_size) {
ObjPtr<PointerArray> arr(GetObsoleteMethods<kDefaultVerifyFlags, kReadBarrierOption>());
diff --git a/runtime/mirror/class_ext.h b/runtime/mirror/class_ext.h
index 4ce3b10..b025eb2 100644
--- a/runtime/mirror/class_ext.h
+++ b/runtime/mirror/class_ext.h
@@ -27,6 +27,7 @@
namespace art {
struct ClassExtOffsets;
+class DexCacheVisitor;
namespace mirror {
@@ -46,6 +47,8 @@
ObjPtr<Throwable> GetErroneousStateError() REQUIRES_SHARED(Locks::mutator_lock_);
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
+ ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
ObjPtr<ObjectArray<DexCache>> GetObsoleteDexCaches() REQUIRES_SHARED(Locks::mutator_lock_);
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
@@ -126,10 +129,21 @@
static bool ExtendObsoleteArrays(Handle<ClassExt> h_this, Thread* self, uint32_t increase)
REQUIRES_SHARED(Locks::mutator_lock_);
- template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier, class Visitor>
+ template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier,
+ bool kVisitProxyMethod = true,
+ class Visitor>
inline void VisitNativeRoots(Visitor& visitor, PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // NO_THREAD_SAFETY_ANALYSIS for dex_lock and heap_bitmap_lock_ as both are at
+ // higher lock-level than class-table's lock, which is already acquired and
+ // is at lower (kClassLoaderClassesLock) level.
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
+ ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+ inline void VisitDexCaches(DexCacheVisitor& visitor)
+ NO_THREAD_SAFETY_ANALYSIS
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier, class Visitor>
inline void VisitMethods(Visitor visitor, PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -156,6 +170,10 @@
bool EnsureJniIdsArrayPresent(MemberOffset off, size_t count)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Backing store of user-defined values pertaining to a class.
+ // Maintained by the ClassValue class.
+ HeapReference<Object> class_value_map_;
+
// The saved error for this class being erroneous.
HeapReference<Throwable> erroneous_state_error_;
@@ -181,9 +199,10 @@
// classes sfields_ array or '0' if no id has been assigned to that field yet.
HeapReference<PointerArray> static_jfield_ids_;
+ int32_t pre_redefine_class_def_index_;
+
// Native pointer to DexFile and ClassDef index of this class before it was JVMTI-redefined.
int64_t pre_redefine_dex_file_ptr_;
- int32_t pre_redefine_class_def_index_;
friend struct art::ClassExtOffsets; // for verifying offset information
DISALLOW_IMPLICIT_CONSTRUCTORS(ClassExt);
diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h
index 2791fe3..3452f17 100644
--- a/runtime/mirror/dex_cache-inl.h
+++ b/runtime/mirror/dex_cache-inl.h
@@ -29,7 +29,7 @@
#include "class_linker.h"
#include "dex/dex_file.h"
#include "gc_root-inl.h"
-#include "linear_alloc.h"
+#include "linear_alloc-inl.h"
#include "mirror/call_site.h"
#include "mirror/class.h"
#include "mirror/method_type.h"
@@ -49,34 +49,36 @@
}
template<typename T>
-static void InitializeArray(GcRoot<T>*) {
- // No special initialization is needed.
+static void InitializeArray(T*) {
+ // Nothing to do.
}
-template<typename T, size_t kMaxCacheSize>
-T* DexCache::AllocArray(MemberOffset obj_offset, MemberOffset num_offset, size_t num) {
- num = std::min<size_t>(num, kMaxCacheSize);
- if (num == 0) {
- return nullptr;
- }
+template<typename T>
+T* DexCache::AllocArray(MemberOffset obj_offset, size_t num, LinearAllocKind kind, bool startup) {
+ Thread* self = Thread::Current();
mirror::DexCache* dex_cache = this;
- if (kUseReadBarrier && Thread::Current()->GetIsGcMarking()) {
+ if (gUseReadBarrier && self->GetIsGcMarking()) {
// Several code paths use DexCache without read-barrier for performance.
// We have to check the "to-space" object here to avoid allocating twice.
- dex_cache = reinterpret_cast<DexCache*>(ReadBarrier::Mark(dex_cache));
+ dex_cache = reinterpret_cast<DexCache*>(ReadBarrier::Mark(this));
}
- Thread* self = Thread::Current();
- ClassLinker* linker = Runtime::Current()->GetClassLinker();
- LinearAlloc* alloc = linker->GetOrCreateAllocatorForClassLoader(GetClassLoader());
+ // DON'T USE 'this' from now on.
+ Runtime* runtime = Runtime::Current();
+ // Note: in the 1002-notify-startup test, the startup linear alloc can become null
+ // concurrently, even if the runtime is marked at startup. Therefore we should only
+ // fetch it once here.
+ LinearAlloc* startup_linear_alloc = runtime->GetStartupLinearAlloc();
+ LinearAlloc* alloc = (startup && startup_linear_alloc != nullptr)
+ ? startup_linear_alloc
+ : runtime->GetClassLinker()->GetOrCreateAllocatorForClassLoader(GetClassLoader());
MutexLock mu(self, *Locks::dex_cache_lock_); // Avoid allocation by multiple threads.
T* array = dex_cache->GetFieldPtr64<T*>(obj_offset);
if (array != nullptr) {
DCHECK(alloc->Contains(array));
return array; // Other thread just allocated the array.
}
- array = reinterpret_cast<T*>(alloc->AllocAlign16(self, RoundUp(num * sizeof(T), 16)));
+ array = reinterpret_cast<T*>(alloc->AllocAlign16(self, RoundUp(num * sizeof(T), 16), kind));
InitializeArray(array); // Ensure other threads see the array initialized.
- dex_cache->SetField32Volatile<false, false>(num_offset, num);
dex_cache->SetField64Volatile<false, false>(obj_offset, reinterpret_cast64<uint64_t>(array));
return array;
}
@@ -86,14 +88,6 @@
: object(object), index(index) {}
template <typename T>
-inline void DexCachePair<T>::Initialize(std::atomic<DexCachePair<T>>* dex_cache) {
- DexCachePair<T> first_elem;
- first_elem.object = GcRoot<T>(nullptr);
- first_elem.index = InvalidIndexForSlot(0);
- dex_cache[0].store(first_elem, std::memory_order_relaxed);
-}
-
-template <typename T>
inline T* DexCachePair<T>::GetObjectForIndex(uint32_t idx) {
if (idx != index) {
return nullptr;
@@ -103,11 +97,33 @@
}
template <typename T>
+inline void DexCachePair<T>::Initialize(std::atomic<DexCachePair<T>>* dex_cache) {
+ DexCachePair<T> first_elem;
+ first_elem.object = GcRoot<T>(nullptr);
+ first_elem.index = InvalidIndexForSlot(0);
+ dex_cache[0].store(first_elem, std::memory_order_relaxed);
+}
+
+template <typename T>
inline void NativeDexCachePair<T>::Initialize(std::atomic<NativeDexCachePair<T>>* dex_cache) {
NativeDexCachePair<T> first_elem;
first_elem.object = nullptr;
first_elem.index = InvalidIndexForSlot(0);
- DexCache::SetNativePair(dex_cache, 0, first_elem);
+
+ auto* array = reinterpret_cast<std::atomic<AtomicPair<uintptr_t>>*>(dex_cache);
+ AtomicPair<uintptr_t> v(reinterpret_cast<size_t>(first_elem.object), first_elem.index);
+ AtomicPairStoreRelease(&array[0], v);
+}
+
+template <typename T>
+inline void GcRootArray<T>::Set(uint32_t index, T* value) {
+ GcRoot<T> root(value);
+ entries_[index] = root;
+}
+
+template <typename T>
+inline T* GcRootArray<T>::Get(uint32_t index) {
+ return entries_[index].Read();
}
inline uint32_t DexCache::ClassSize(PointerSize pointer_size) {
@@ -115,31 +131,13 @@
return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 0, 0, pointer_size);
}
-inline uint32_t DexCache::StringSlotIndex(dex::StringIndex string_idx) {
- DCHECK_LT(string_idx.index_, GetDexFile()->NumStringIds());
- const uint32_t slot_idx = string_idx.index_ % kDexCacheStringCacheSize;
- DCHECK_LT(slot_idx, NumStrings());
- return slot_idx;
-}
-
inline String* DexCache::GetResolvedString(dex::StringIndex string_idx) {
- StringDexCacheType* strings = GetStrings();
- if (UNLIKELY(strings == nullptr)) {
- return nullptr;
- }
- return strings[StringSlotIndex(string_idx)].load(
- std::memory_order_relaxed).GetObjectForIndex(string_idx.index_);
+ return GetStringsEntry(string_idx.index_);
}
inline void DexCache::SetResolvedString(dex::StringIndex string_idx, ObjPtr<String> resolved) {
DCHECK(resolved != nullptr);
- StringDexCacheType* strings = GetStrings();
- if (UNLIKELY(strings == nullptr)) {
- strings = AllocArray<StringDexCacheType, kDexCacheStringCacheSize>(
- StringsOffset(), NumStringsOffset(), GetDexFile()->NumStringIds());
- }
- strings[StringSlotIndex(string_idx)].store(
- StringDexCachePair(resolved, string_idx.index_), std::memory_order_relaxed);
+ SetStringsEntry(string_idx.index_, resolved.Ptr());
Runtime* const runtime = Runtime::Current();
if (UNLIKELY(runtime->IsActiveTransaction())) {
DCHECK(runtime->IsAotCompiler());
@@ -151,96 +149,42 @@
inline void DexCache::ClearString(dex::StringIndex string_idx) {
DCHECK(Runtime::Current()->IsAotCompiler());
- uint32_t slot_idx = StringSlotIndex(string_idx);
- StringDexCacheType* strings = GetStrings();
+ auto* array = GetStringsArray();
+ if (array != nullptr) {
+ array->Set(string_idx.index_, nullptr);
+ }
+ auto* strings = GetStrings();
if (UNLIKELY(strings == nullptr)) {
return;
}
- StringDexCacheType* slot = &strings[slot_idx];
- // This is racy but should only be called from the transactional interpreter.
- if (slot->load(std::memory_order_relaxed).index == string_idx.index_) {
- StringDexCachePair cleared(nullptr, StringDexCachePair::InvalidIndexForSlot(slot_idx));
- slot->store(cleared, std::memory_order_relaxed);
- }
-}
-
-inline uint32_t DexCache::TypeSlotIndex(dex::TypeIndex type_idx) {
- DCHECK_LT(type_idx.index_, GetDexFile()->NumTypeIds());
- const uint32_t slot_idx = type_idx.index_ % kDexCacheTypeCacheSize;
- DCHECK_LT(slot_idx, NumResolvedTypes());
- return slot_idx;
+ strings->Clear(string_idx.index_);
}
inline Class* DexCache::GetResolvedType(dex::TypeIndex type_idx) {
- // It is theorized that a load acquire is not required since obtaining the resolved class will
- // always have an address dependency or a lock.
- TypeDexCacheType* resolved_types = GetResolvedTypes();
- if (UNLIKELY(resolved_types == nullptr)) {
- return nullptr;
- }
- return resolved_types[TypeSlotIndex(type_idx)].load(
- std::memory_order_relaxed).GetObjectForIndex(type_idx.index_);
-}
-
-inline void DexCache::SetResolvedType(dex::TypeIndex type_idx, ObjPtr<Class> resolved) {
- DCHECK(resolved != nullptr);
- DCHECK(resolved->IsResolved()) << resolved->GetStatus();
- TypeDexCacheType* resolved_types = GetResolvedTypes();
- if (UNLIKELY(resolved_types == nullptr)) {
- resolved_types = AllocArray<TypeDexCacheType, kDexCacheTypeCacheSize>(
- ResolvedTypesOffset(), NumResolvedTypesOffset(), GetDexFile()->NumTypeIds());
- }
- // TODO default transaction support.
- // Use a release store for SetResolvedType. This is done to prevent other threads from seeing a
- // class but not necessarily seeing the loaded members like the static fields array.
- // See b/32075261.
- resolved_types[TypeSlotIndex(type_idx)].store(
- TypeDexCachePair(resolved, type_idx.index_), std::memory_order_release);
- // TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
- WriteBarrier::ForEveryFieldWrite(this);
+ return GetResolvedTypesEntry(type_idx.index_);
}
inline void DexCache::ClearResolvedType(dex::TypeIndex type_idx) {
DCHECK(Runtime::Current()->IsAotCompiler());
- TypeDexCacheType* resolved_types = GetResolvedTypes();
+ auto* array = GetResolvedTypesArray();
+ if (array != nullptr) {
+ array->Set(type_idx.index_, nullptr);
+ }
+ auto* resolved_types = GetResolvedTypes();
if (UNLIKELY(resolved_types == nullptr)) {
return;
}
- uint32_t slot_idx = TypeSlotIndex(type_idx);
- TypeDexCacheType* slot = &resolved_types[slot_idx];
- // This is racy but should only be called from the single-threaded ImageWriter and tests.
- if (slot->load(std::memory_order_relaxed).index == type_idx.index_) {
- TypeDexCachePair cleared(nullptr, TypeDexCachePair::InvalidIndexForSlot(slot_idx));
- slot->store(cleared, std::memory_order_relaxed);
- }
-}
-
-inline uint32_t DexCache::MethodTypeSlotIndex(dex::ProtoIndex proto_idx) {
- DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
- DCHECK_LT(proto_idx.index_, GetDexFile()->NumProtoIds());
- const uint32_t slot_idx = proto_idx.index_ % kDexCacheMethodTypeCacheSize;
- DCHECK_LT(slot_idx, NumResolvedMethodTypes());
- return slot_idx;
+ resolved_types->Clear(type_idx.index_);
}
inline MethodType* DexCache::GetResolvedMethodType(dex::ProtoIndex proto_idx) {
- MethodTypeDexCacheType* methods = GetResolvedMethodTypes();
- if (UNLIKELY(methods == nullptr)) {
- return nullptr;
- }
- return methods[MethodTypeSlotIndex(proto_idx)].load(
- std::memory_order_relaxed).GetObjectForIndex(proto_idx.index_);
+ return GetResolvedMethodTypesEntry(proto_idx.index_);
}
inline void DexCache::SetResolvedMethodType(dex::ProtoIndex proto_idx, MethodType* resolved) {
DCHECK(resolved != nullptr);
- MethodTypeDexCacheType* methods = GetResolvedMethodTypes();
- if (UNLIKELY(methods == nullptr)) {
- methods = AllocArray<MethodTypeDexCacheType, kDexCacheMethodTypeCacheSize>(
- ResolvedMethodTypesOffset(), NumResolvedMethodTypesOffset(), GetDexFile()->NumProtoIds());
- }
- methods[MethodTypeSlotIndex(proto_idx)].store(
- MethodTypeDexCachePair(resolved, proto_idx.index_), std::memory_order_relaxed);
+ SetResolvedMethodTypesEntry(proto_idx.index_, resolved);
+
Runtime* const runtime = Runtime::Current();
if (UNLIKELY(runtime->IsActiveTransaction())) {
DCHECK(runtime->IsAotCompiler());
@@ -252,24 +196,25 @@
inline void DexCache::ClearMethodType(dex::ProtoIndex proto_idx) {
DCHECK(Runtime::Current()->IsAotCompiler());
- uint32_t slot_idx = MethodTypeSlotIndex(proto_idx);
- MethodTypeDexCacheType* slot = &GetResolvedMethodTypes()[slot_idx];
- // This is racy but should only be called from the transactional interpreter.
- if (slot->load(std::memory_order_relaxed).index == proto_idx.index_) {
- MethodTypeDexCachePair cleared(nullptr,
- MethodTypeDexCachePair::InvalidIndexForSlot(proto_idx.index_));
- slot->store(cleared, std::memory_order_relaxed);
+ auto* array = GetResolvedMethodTypesArray();
+ if (array != nullptr) {
+ array->Set(proto_idx.index_, nullptr);
}
+ auto* methods = GetResolvedMethodTypes();
+ if (methods == nullptr) {
+ return;
+ }
+ methods->Clear(proto_idx.index_);
}
inline CallSite* DexCache::GetResolvedCallSite(uint32_t call_site_idx) {
DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
DCHECK_LT(call_site_idx, GetDexFile()->NumCallSiteIds());
- GcRoot<CallSite>* call_sites = GetResolvedCallSites();
+ GcRootArray<CallSite>* call_sites = GetResolvedCallSites();
if (UNLIKELY(call_sites == nullptr)) {
return nullptr;
}
- GcRoot<mirror::CallSite>& target = call_sites[call_site_idx];
+ GcRoot<mirror::CallSite>& target = call_sites->GetGcRoot(call_site_idx);
Atomic<GcRoot<mirror::CallSite>>& ref =
reinterpret_cast<Atomic<GcRoot<mirror::CallSite>>&>(target);
return ref.load(std::memory_order_seq_cst).Read();
@@ -282,12 +227,11 @@
GcRoot<mirror::CallSite> null_call_site(nullptr);
GcRoot<mirror::CallSite> candidate(call_site);
- GcRoot<CallSite>* call_sites = GetResolvedCallSites();
+ GcRootArray<CallSite>* call_sites = GetResolvedCallSites();
if (UNLIKELY(call_sites == nullptr)) {
- call_sites = AllocArray<GcRoot<CallSite>, std::numeric_limits<size_t>::max()>(
- ResolvedCallSitesOffset(), NumResolvedCallSitesOffset(), GetDexFile()->NumCallSiteIds());
+ call_sites = AllocateResolvedCallSites();
}
- GcRoot<mirror::CallSite>& target = call_sites[call_site_idx];
+ GcRoot<mirror::CallSite>& target = call_sites->GetGcRoot(call_site_idx);
// The first assignment for a given call site wins.
Atomic<GcRoot<mirror::CallSite>>& ref =
@@ -301,101 +245,55 @@
}
}
-inline uint32_t DexCache::FieldSlotIndex(uint32_t field_idx) {
- DCHECK_LT(field_idx, GetDexFile()->NumFieldIds());
- const uint32_t slot_idx = field_idx % kDexCacheFieldCacheSize;
- DCHECK_LT(slot_idx, NumResolvedFields());
- return slot_idx;
-}
-
inline ArtField* DexCache::GetResolvedField(uint32_t field_idx) {
- FieldDexCacheType* fields = GetResolvedFields();
- if (UNLIKELY(fields == nullptr)) {
- return nullptr;
- }
- auto pair = GetNativePair(fields, FieldSlotIndex(field_idx));
- return pair.GetObjectForIndex(field_idx);
+ return GetResolvedFieldsEntry(field_idx);
}
inline void DexCache::SetResolvedField(uint32_t field_idx, ArtField* field) {
- DCHECK(field != nullptr);
- FieldDexCachePair pair(field, field_idx);
- FieldDexCacheType* fields = GetResolvedFields();
- if (UNLIKELY(fields == nullptr)) {
- fields = AllocArray<FieldDexCacheType, kDexCacheFieldCacheSize>(
- ResolvedFieldsOffset(), NumResolvedFieldsOffset(), GetDexFile()->NumFieldIds());
- }
- SetNativePair(fields, FieldSlotIndex(field_idx), pair);
-}
-
-inline uint32_t DexCache::MethodSlotIndex(uint32_t method_idx) {
- DCHECK_LT(method_idx, GetDexFile()->NumMethodIds());
- const uint32_t slot_idx = method_idx % kDexCacheMethodCacheSize;
- DCHECK_LT(slot_idx, NumResolvedMethods());
- return slot_idx;
+ SetResolvedFieldsEntry(field_idx, field);
}
inline ArtMethod* DexCache::GetResolvedMethod(uint32_t method_idx) {
- MethodDexCacheType* methods = GetResolvedMethods();
- if (UNLIKELY(methods == nullptr)) {
- return nullptr;
- }
- auto pair = GetNativePair(methods, MethodSlotIndex(method_idx));
- return pair.GetObjectForIndex(method_idx);
+ return GetResolvedMethodsEntry(method_idx);
}
inline void DexCache::SetResolvedMethod(uint32_t method_idx, ArtMethod* method) {
- DCHECK(method != nullptr);
- MethodDexCachePair pair(method, method_idx);
- MethodDexCacheType* methods = GetResolvedMethods();
- if (UNLIKELY(methods == nullptr)) {
- methods = AllocArray<MethodDexCacheType, kDexCacheMethodCacheSize>(
- ResolvedMethodsOffset(), NumResolvedMethodsOffset(), GetDexFile()->NumMethodIds());
- }
- SetNativePair(methods, MethodSlotIndex(method_idx), pair);
+ SetResolvedMethodsEntry(method_idx, method);
}
-template <typename T>
-NativeDexCachePair<T> DexCache::GetNativePair(std::atomic<NativeDexCachePair<T>>* pair_array,
- size_t idx) {
- auto* array = reinterpret_cast<std::atomic<AtomicPair<uintptr_t>>*>(pair_array);
- AtomicPair<uintptr_t> value = AtomicPairLoadAcquire(&array[idx]);
- return NativeDexCachePair<T>(reinterpret_cast<T*>(value.first), value.second);
-}
-
-template <typename T>
-void DexCache::SetNativePair(std::atomic<NativeDexCachePair<T>>* pair_array,
- size_t idx,
- NativeDexCachePair<T> pair) {
- auto* array = reinterpret_cast<std::atomic<AtomicPair<uintptr_t>>*>(pair_array);
- AtomicPair<uintptr_t> v(reinterpret_cast<size_t>(pair.object), pair.index);
- AtomicPairStoreRelease(&array[idx], v);
-}
-
-template <typename T,
- ReadBarrierOption kReadBarrierOption,
- typename Visitor>
-inline void VisitDexCachePairs(std::atomic<DexCachePair<T>>* pairs,
+template <ReadBarrierOption kReadBarrierOption,
+ typename Visitor,
+ typename T>
+inline void VisitDexCachePairs(T* array,
size_t num_pairs,
const Visitor& visitor)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_) {
// Check both the data pointer and count since the array might be initialized
// concurrently on other thread, and we might observe just one of the values.
- for (size_t i = 0; pairs != nullptr && i < num_pairs; ++i) {
- DexCachePair<T> source = pairs[i].load(std::memory_order_relaxed);
+ for (size_t i = 0; array != nullptr && i < num_pairs; ++i) {
+ auto source = array->GetPair(i);
// NOTE: We need the "template" keyword here to avoid a compilation
// failure. GcRoot<T> is a template argument-dependent type and we need to
// tell the compiler to treat "Read" as a template rather than a field or
// function. Otherwise, on encountering the "<" token, the compiler would
// treat "Read" as a field.
- T* const before = source.object.template Read<kReadBarrierOption>();
+ auto const before = source.object.template Read<kReadBarrierOption>();
visitor.VisitRootIfNonNull(source.object.AddressWithoutBarrier());
if (source.object.template Read<kReadBarrierOption>() != before) {
- pairs[i].store(source, std::memory_order_relaxed);
+ array->SetPair(i, source);
}
}
}
+template <typename Visitor>
+void DexCache::VisitDexCachePairRoots(Visitor& visitor,
+ DexCachePair<Object>* pairs_begin,
+ DexCachePair<Object>* pairs_end) {
+ for (; pairs_begin < pairs_end; pairs_begin++) {
+ visitor.VisitRootIfNonNull(pairs_begin->object.AddressWithoutBarrier());
+ }
+}
+
template <bool kVisitNativeRoots,
VerifyObjectFlags kVerifyFlags,
ReadBarrierOption kReadBarrierOption,
@@ -405,20 +303,46 @@
VisitInstanceFieldsReferences<kVerifyFlags, kReadBarrierOption>(klass, visitor);
// Visit arrays after.
if (kVisitNativeRoots) {
- VisitDexCachePairs<String, kReadBarrierOption, Visitor>(
- GetStrings<kVerifyFlags>(), NumStrings<kVerifyFlags>(), visitor);
+ VisitNativeRoots<kVerifyFlags, kReadBarrierOption>(visitor);
+ }
+}
- VisitDexCachePairs<Class, kReadBarrierOption, Visitor>(
- GetResolvedTypes<kVerifyFlags>(), NumResolvedTypes<kVerifyFlags>(), visitor);
+template <VerifyObjectFlags kVerifyFlags,
+ ReadBarrierOption kReadBarrierOption,
+ typename Visitor>
+inline void DexCache::VisitNativeRoots(const Visitor& visitor) {
+ VisitDexCachePairs<kReadBarrierOption, Visitor>(
+ GetStrings<kVerifyFlags>(), NumStrings<kVerifyFlags>(), visitor);
- VisitDexCachePairs<MethodType, kReadBarrierOption, Visitor>(
- GetResolvedMethodTypes<kVerifyFlags>(), NumResolvedMethodTypes<kVerifyFlags>(), visitor);
+ VisitDexCachePairs<kReadBarrierOption, Visitor>(
+ GetResolvedTypes<kVerifyFlags>(), NumResolvedTypes<kVerifyFlags>(), visitor);
- GcRoot<mirror::CallSite>* resolved_call_sites = GetResolvedCallSites<kVerifyFlags>();
- size_t num_call_sites = NumResolvedCallSites<kVerifyFlags>();
- for (size_t i = 0; resolved_call_sites != nullptr && i != num_call_sites; ++i) {
- visitor.VisitRootIfNonNull(resolved_call_sites[i].AddressWithoutBarrier());
- }
+ VisitDexCachePairs<kReadBarrierOption, Visitor>(
+ GetResolvedMethodTypes<kVerifyFlags>(), NumResolvedMethodTypes<kVerifyFlags>(), visitor);
+
+ GcRootArray<mirror::CallSite>* resolved_call_sites = GetResolvedCallSites<kVerifyFlags>();
+ size_t num_call_sites = NumResolvedCallSites<kVerifyFlags>();
+ for (size_t i = 0; resolved_call_sites != nullptr && i != num_call_sites; ++i) {
+ visitor.VisitRootIfNonNull(resolved_call_sites->GetGcRoot(i).AddressWithoutBarrier());
+ }
+
+ GcRootArray<mirror::Class>* resolved_types = GetResolvedTypesArray<kVerifyFlags>();
+ size_t num_resolved_types = NumResolvedTypesArray<kVerifyFlags>();
+ for (size_t i = 0; resolved_types != nullptr && i != num_resolved_types; ++i) {
+ visitor.VisitRootIfNonNull(resolved_types->GetGcRoot(i).AddressWithoutBarrier());
+ }
+
+ GcRootArray<mirror::String>* resolved_strings = GetStringsArray<kVerifyFlags>();
+ size_t num_resolved_strings = NumStringsArray<kVerifyFlags>();
+ for (size_t i = 0; resolved_strings != nullptr && i != num_resolved_strings; ++i) {
+ visitor.VisitRootIfNonNull(resolved_strings->GetGcRoot(i).AddressWithoutBarrier());
+ }
+
+ GcRootArray<mirror::MethodType>* resolved_method_types =
+ GetResolvedMethodTypesArray<kVerifyFlags>();
+ size_t num_resolved_method_types = NumResolvedMethodTypesArray<kVerifyFlags>();
+ for (size_t i = 0; resolved_method_types != nullptr && i != num_resolved_method_types; ++i) {
+ visitor.VisitRootIfNonNull(resolved_method_types->GetGcRoot(i).AddressWithoutBarrier());
}
}
diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc
index b7f8ee7..b0b6012 100644
--- a/runtime/mirror/dex_cache.cc
+++ b/runtime/mirror/dex_cache.cc
@@ -20,6 +20,7 @@
#include "class_linker.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/heap.h"
+#include "jit/profile_saver.h"
#include "linear_alloc.h"
#include "oat_file.h"
#include "object-inl.h"
@@ -44,6 +45,12 @@
DCHECK(GetResolvedMethodTypes() == nullptr);
DCHECK(GetResolvedCallSites() == nullptr);
+ DCHECK(GetStringsArray() == nullptr);
+ DCHECK(GetResolvedTypesArray() == nullptr);
+ DCHECK(GetResolvedMethodsArray() == nullptr);
+ DCHECK(GetResolvedFieldsArray() == nullptr);
+ DCHECK(GetResolvedMethodTypesArray() == nullptr);
+
ScopedAssertNoThreadSuspension sants(__FUNCTION__);
SetDexFile(dex_file);
@@ -52,48 +59,81 @@
void DexCache::VisitReflectiveTargets(ReflectiveValueVisitor* visitor) {
bool wrote = false;
- FieldDexCacheType* fields = GetResolvedFields();
+ auto* fields = GetResolvedFields();
size_t num_fields = NumResolvedFields();
// Check both the data pointer and count since the array might be initialized
// concurrently on other thread, and we might observe just one of the values.
for (size_t i = 0; fields != nullptr && i < num_fields; i++) {
- auto pair(GetNativePair(fields, i));
- if (pair.index == FieldDexCachePair::InvalidIndexForSlot(i)) {
+ auto pair(fields->GetNativePair(i));
+ if (pair.index == NativeDexCachePair<ArtField>::InvalidIndexForSlot(i)) {
continue;
}
ArtField* new_val = visitor->VisitField(
pair.object, DexCacheSourceInfo(kSourceDexCacheResolvedField, pair.index, this));
if (UNLIKELY(new_val != pair.object)) {
if (new_val == nullptr) {
- pair = FieldDexCachePair(nullptr, FieldDexCachePair::InvalidIndexForSlot(i));
+ pair = NativeDexCachePair<ArtField>(
+ nullptr, NativeDexCachePair<ArtField>::InvalidIndexForSlot(i));
} else {
pair.object = new_val;
}
- SetNativePair(fields, i, pair);
+ fields->SetNativePair(i, pair);
wrote = true;
}
}
- MethodDexCacheType* methods = GetResolvedMethods();
+ auto* methods = GetResolvedMethods();
size_t num_methods = NumResolvedMethods();
// Check both the data pointer and count since the array might be initialized
// concurrently on other thread, and we might observe just one of the values.
for (size_t i = 0; methods != nullptr && i < num_methods; i++) {
- auto pair(GetNativePair(methods, i));
- if (pair.index == MethodDexCachePair::InvalidIndexForSlot(i)) {
+ auto pair(methods->GetNativePair(i));
+ if (pair.index == NativeDexCachePair<ArtMethod>::InvalidIndexForSlot(i)) {
continue;
}
ArtMethod* new_val = visitor->VisitMethod(
pair.object, DexCacheSourceInfo(kSourceDexCacheResolvedMethod, pair.index, this));
if (UNLIKELY(new_val != pair.object)) {
if (new_val == nullptr) {
- pair = MethodDexCachePair(nullptr, MethodDexCachePair::InvalidIndexForSlot(i));
+ pair = NativeDexCachePair<ArtMethod>(
+ nullptr, NativeDexCachePair<ArtMethod>::InvalidIndexForSlot(i));
} else {
pair.object = new_val;
}
- SetNativePair(methods, i, pair);
+ methods->SetNativePair(i, pair);
wrote = true;
}
}
+
+ auto* fields_array = GetResolvedFieldsArray();
+ num_fields = NumResolvedFieldsArray();
+ for (size_t i = 0; fields_array != nullptr && i < num_fields; i++) {
+ ArtField* old_val = fields_array->Get(i);
+ if (old_val == nullptr) {
+ continue;
+ }
+ ArtField* new_val = visitor->VisitField(
+ old_val, DexCacheSourceInfo(kSourceDexCacheResolvedField, i, this));
+ if (new_val != old_val) {
+ fields_array->Set(i, new_val);
+ wrote = true;
+ }
+ }
+
+ auto* methods_array = GetResolvedMethodsArray();
+ num_methods = NumResolvedMethodsArray();
+ for (size_t i = 0; methods_array != nullptr && i < num_methods; i++) {
+ ArtMethod* old_val = methods_array->Get(i);
+ if (old_val == nullptr) {
+ continue;
+ }
+ ArtMethod* new_val = visitor->VisitMethod(
+ old_val, DexCacheSourceInfo(kSourceDexCacheResolvedMethod, i, this));
+ if (new_val != old_val) {
+ methods_array->Set(i, new_val);
+ wrote = true;
+ }
+ }
+
if (wrote) {
WriteBarrier::ForEveryFieldWrite(this);
}
@@ -106,12 +146,12 @@
SetResolvedFields(nullptr);
SetResolvedMethodTypes(nullptr);
SetResolvedCallSites(nullptr);
- SetField32<false>(NumStringsOffset(), 0);
- SetField32<false>(NumResolvedTypesOffset(), 0);
- SetField32<false>(NumResolvedMethodsOffset(), 0);
- SetField32<false>(NumResolvedFieldsOffset(), 0);
- SetField32<false>(NumResolvedMethodTypesOffset(), 0);
- SetField32<false>(NumResolvedCallSitesOffset(), 0);
+
+ SetStringsArray(nullptr);
+ SetResolvedTypesArray(nullptr);
+ SetResolvedMethodsArray(nullptr);
+ SetResolvedFieldsArray(nullptr);
+ SetResolvedMethodTypesArray(nullptr);
}
void DexCache::SetLocation(ObjPtr<mirror::String> location) {
@@ -126,5 +166,95 @@
return GetFieldObject<ClassLoader>(OFFSET_OF_OBJECT_MEMBER(DexCache, class_loader_));
}
+bool DexCache::ShouldAllocateFullArrayAtStartup() {
+ Runtime* runtime = Runtime::Current();
+ if (runtime->IsAotCompiler()) {
+ // To save on memory in dex2oat, we don't allocate full arrays by default.
+ return false;
+ }
+
+ if (runtime->GetStartupCompleted()) {
+ // We only allocate full arrays during app startup.
+ return false;
+ }
+
+ if (GetClassLoader() == nullptr) {
+ // Only allocate full array for app dex files (also note that for
+ // multi-image, the `GetCompilerFilter` call below does not work for
+ // non-primary oat files).
+ return false;
+ }
+
+ const OatDexFile* oat_dex_file = GetDexFile()->GetOatDexFile();
+ if (oat_dex_file != nullptr &&
+ CompilerFilter::IsAotCompilationEnabled(oat_dex_file->GetOatFile()->GetCompilerFilter())) {
+ // We only allocate full arrays for dex files where we do not have
+ // compilation.
+ return false;
+ }
+
+ if (!ProfileSaver::IsStarted()) {
+ // Only allocate full arrays if the profile saver is running: if the app
+ // does not call `reportFullyDrawn`, then only the profile saver will notify
+ // that the app has eventually started.
+ return false;
+ }
+
+ return true;
+}
+
+void DexCache::UnlinkStartupCaches() {
+ if (GetDexFile() == nullptr) {
+ // Unused dex cache.
+ return;
+ }
+ UnlinkStringsArrayIfStartup();
+ UnlinkResolvedFieldsArrayIfStartup();
+ UnlinkResolvedMethodsArrayIfStartup();
+ UnlinkResolvedTypesArrayIfStartup();
+ UnlinkResolvedMethodTypesArrayIfStartup();
+}
+
+void DexCache::SetResolvedType(dex::TypeIndex type_idx, ObjPtr<Class> resolved) {
+ DCHECK(resolved != nullptr);
+ DCHECK(resolved->IsResolved()) << resolved->GetStatus();
+ // TODO default transaction support.
+ // Use a release store for SetResolvedType. This is done to prevent other threads from seeing a
+ // class but not necessarily seeing the loaded members like the static fields array.
+ // See b/32075261.
+ SetResolvedTypesEntry(type_idx.index_, resolved.Ptr());
+ // TODO: Fine-grained marking, so that we don't need to go through all arrays in full.
+ WriteBarrier::ForEveryFieldWrite(this);
+
+ if (this == resolved->GetDexCache()) {
+ // If we're updating the dex cache of the class, optimistically update the cache for methods and
+ // fields if the caches are full arrays.
+ auto* resolved_methods = GetResolvedMethodsArray();
+ if (resolved_methods != nullptr) {
+ PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ // Because there could be duplicate method entries, we make sure we only
+ // update the cache with the first one found to be consistent with method
+ // resolution.
+ uint32_t previous_method_index = dex::kDexNoIndex;
+ for (ArtMethod& current_method : resolved->GetDeclaredMethods(pointer_size)) {
+ uint32_t new_index = current_method.GetDexMethodIndex();
+ if (new_index != previous_method_index) {
+ resolved_methods->Set(new_index, ¤t_method);
+ previous_method_index = new_index;
+ }
+ }
+ }
+ auto* resolved_fields = GetResolvedFieldsArray();
+ if (resolved_fields != nullptr) {
+ for (ArtField& current_field : resolved->GetSFields()) {
+ resolved_fields->Set(current_field.GetDexFieldIndex(), ¤t_field);
+ }
+ for (ArtField& current_field : resolved->GetIFields()) {
+ resolved_fields->Set(current_field.GetDexFieldIndex(), ¤t_field);
+ }
+ }
+ }
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h
index 6701405..cc01b6b 100644
--- a/runtime/mirror/dex_cache.h
+++ b/runtime/mirror/dex_cache.h
@@ -19,10 +19,13 @@
#include "array.h"
#include "base/array_ref.h"
+#include "base/atomic_pair.h"
#include "base/bit_utils.h"
#include "base/locks.h"
+#include "dex/dex_file.h"
#include "dex/dex_file_types.h"
#include "gc_root.h" // Note: must not use -inl here to avoid circular dependency.
+#include "linear_alloc.h"
#include "object.h"
#include "object_array.h"
@@ -37,7 +40,6 @@
struct DexCacheOffsets;
class DexFile;
union JValue;
-class LinearAlloc;
class ReflectiveValueVisitor;
class Thread;
@@ -46,6 +48,7 @@
class CallSite;
class Class;
class ClassLoader;
+class DexCache;
class MethodType;
class String;
@@ -115,20 +118,124 @@
}
};
-using TypeDexCachePair = DexCachePair<Class>;
-using TypeDexCacheType = std::atomic<TypeDexCachePair>;
+template <typename T, size_t size> class NativeDexCachePairArray {
+ public:
+ NativeDexCachePairArray() {}
-using StringDexCachePair = DexCachePair<String>;
-using StringDexCacheType = std::atomic<StringDexCachePair>;
+ T* Get(uint32_t index) REQUIRES_SHARED(Locks::mutator_lock_) {
+ auto pair = GetNativePair(entries_, SlotIndex(index));
+ return pair.GetObjectForIndex(index);
+ }
-using FieldDexCachePair = NativeDexCachePair<ArtField>;
-using FieldDexCacheType = std::atomic<FieldDexCachePair>;
+ void Set(uint32_t index, T* value) {
+ NativeDexCachePair<T> pair(value, index);
+ SetNativePair(entries_, SlotIndex(index), pair);
+ }
-using MethodDexCachePair = NativeDexCachePair<ArtMethod>;
-using MethodDexCacheType = std::atomic<MethodDexCachePair>;
+ NativeDexCachePair<T> GetNativePair(uint32_t index) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetNativePair(entries_, SlotIndex(index));
+ }
-using MethodTypeDexCachePair = DexCachePair<MethodType>;
-using MethodTypeDexCacheType = std::atomic<MethodTypeDexCachePair>;
+ void SetNativePair(uint32_t index, NativeDexCachePair<T> value) {
+ SetNativePair(entries_, SlotIndex(index), value);
+ }
+
+ private:
+ NativeDexCachePair<T> GetNativePair(std::atomic<NativeDexCachePair<T>>* pair_array, size_t idx) {
+ auto* array = reinterpret_cast<std::atomic<AtomicPair<uintptr_t>>*>(pair_array);
+ AtomicPair<uintptr_t> value = AtomicPairLoadAcquire(&array[idx]);
+ return NativeDexCachePair<T>(reinterpret_cast<T*>(value.first), value.second);
+ }
+
+ void SetNativePair(std::atomic<NativeDexCachePair<T>>* pair_array,
+ size_t idx,
+ NativeDexCachePair<T> pair) {
+ auto* array = reinterpret_cast<std::atomic<AtomicPair<uintptr_t>>*>(pair_array);
+ AtomicPair<uintptr_t> v(reinterpret_cast<size_t>(pair.object), pair.index);
+ AtomicPairStoreRelease(&array[idx], v);
+ }
+
+ uint32_t SlotIndex(uint32_t index) {
+ return index % size;
+ }
+
+ std::atomic<NativeDexCachePair<T>> entries_[0];
+
+ NativeDexCachePairArray(const NativeDexCachePairArray<T, size>&) = delete;
+ NativeDexCachePairArray& operator=(const NativeDexCachePairArray<T, size>&) = delete;
+};
+
+template <typename T, size_t size> class DexCachePairArray {
+ public:
+ DexCachePairArray() {}
+
+ T* Get(uint32_t index) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return GetPair(index).GetObjectForIndex(index);
+ }
+
+ void Set(uint32_t index, T* value) REQUIRES_SHARED(Locks::mutator_lock_) {
+ SetPair(index, DexCachePair<T>(value, index));
+ }
+
+ DexCachePair<T> GetPair(uint32_t index) {
+ return entries_[SlotIndex(index)].load(std::memory_order_relaxed);
+ }
+
+ void SetPair(uint32_t index, DexCachePair<T> value) {
+ entries_[SlotIndex(index)].store(value, std::memory_order_relaxed);
+ }
+
+ void Clear(uint32_t index) {
+ uint32_t slot = SlotIndex(index);
+ // This is racy but should only be called from the transactional interpreter.
+ if (entries_[slot].load(std::memory_order_relaxed).index == index) {
+ DexCachePair<T> cleared(nullptr, DexCachePair<T>::InvalidIndexForSlot(slot));
+ entries_[slot].store(cleared, std::memory_order_relaxed);
+ }
+ }
+
+ private:
+ uint32_t SlotIndex(uint32_t index) {
+ return index % size;
+ }
+
+ std::atomic<DexCachePair<T>> entries_[0];
+
+ DexCachePairArray(const DexCachePairArray<T, size>&) = delete;
+ DexCachePairArray& operator=(const DexCachePairArray<T, size>&) = delete;
+};
+
+template <typename T> class GcRootArray {
+ public:
+ GcRootArray() {}
+
+ T* Get(uint32_t index) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ GcRoot<T>& GetGcRoot(uint32_t index) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return entries_[index];
+ }
+
+ void Set(uint32_t index, T* value) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+ GcRoot<T> entries_[0];
+};
+
+template <typename T> class NativeArray {
+ public:
+ NativeArray() {}
+
+ T* Get(uint32_t index) {
+ return entries_[index];
+ }
+
+ void Set(uint32_t index, T* value) {
+ entries_[index] = value;
+ }
+
+ private:
+ T* entries_[0];
+};
// C++ mirror of java.lang.DexCache.
class MANAGED DexCache final : public Object {
@@ -164,31 +271,18 @@
static_assert(IsPowerOfTwo(kDexCacheMethodTypeCacheSize),
"MethodType dex cache size is not a power of 2.");
- static constexpr size_t StaticTypeSize() {
- return kDexCacheTypeCacheSize;
- }
-
- static constexpr size_t StaticStringSize() {
- return kDexCacheStringCacheSize;
- }
-
- static constexpr size_t StaticArtFieldSize() {
- return kDexCacheFieldCacheSize;
- }
-
- static constexpr size_t StaticMethodSize() {
- return kDexCacheMethodCacheSize;
- }
-
- static constexpr size_t StaticMethodTypeSize() {
- return kDexCacheMethodTypeCacheSize;
- }
-
// Size of an instance of java.lang.DexCache not including referenced values.
static constexpr uint32_t InstanceSize() {
return sizeof(DexCache);
}
+ // Visit gc-roots in DexCachePair array in [pairs_begin, pairs_end) range.
+ template <typename Visitor>
+ static void VisitDexCachePairRoots(Visitor& visitor,
+ DexCachePair<Object>* pairs_begin,
+ DexCachePair<Object>* pairs_end)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
void Initialize(const DexFile* dex_file, ObjPtr<ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(Locks::dex_lock_);
@@ -201,66 +295,6 @@
ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
ObjPtr<String> GetLocation() REQUIRES_SHARED(Locks::mutator_lock_);
- static constexpr MemberOffset StringsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, strings_);
- }
-
- static constexpr MemberOffset PreResolvedStringsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, preresolved_strings_);
- }
-
- static constexpr MemberOffset ResolvedTypesOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_);
- }
-
- static constexpr MemberOffset ResolvedFieldsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_fields_);
- }
-
- static constexpr MemberOffset ResolvedMethodsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_methods_);
- }
-
- static constexpr MemberOffset ResolvedMethodTypesOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_method_types_);
- }
-
- static constexpr MemberOffset ResolvedCallSitesOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_call_sites_);
- }
-
- static constexpr MemberOffset NumStringsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, num_strings_);
- }
-
- static constexpr MemberOffset NumPreResolvedStringsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, num_preresolved_strings_);
- }
-
- static constexpr MemberOffset NumResolvedTypesOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_types_);
- }
-
- static constexpr MemberOffset NumResolvedFieldsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_fields_);
- }
-
- static constexpr MemberOffset NumResolvedMethodsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_methods_);
- }
-
- static constexpr MemberOffset NumResolvedMethodTypesOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_method_types_);
- }
-
- static constexpr MemberOffset NumResolvedCallSitesOffset() {
- return OFFSET_OF_OBJECT_MEMBER(DexCache, num_resolved_call_sites_);
- }
-
- static constexpr size_t PreResolvedStringsAlignment() {
- return alignof(GcRoot<mirror::String>);
- }
-
String* GetResolvedString(dex::StringIndex string_idx) ALWAYS_INLINE
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -309,106 +343,6 @@
ObjPtr<CallSite> SetResolvedCallSite(uint32_t call_site_idx, ObjPtr<CallSite> resolved)
REQUIRES_SHARED(Locks::mutator_lock_) WARN_UNUSED;
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- StringDexCacheType* GetStrings() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetFieldPtr64<StringDexCacheType*, kVerifyFlags>(StringsOffset());
- }
-
- void SetStrings(StringDexCacheType* strings) ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- SetFieldPtr<false>(StringsOffset(), strings);
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- TypeDexCacheType* GetResolvedTypes() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetFieldPtr<TypeDexCacheType*, kVerifyFlags>(ResolvedTypesOffset());
- }
-
- void SetResolvedTypes(TypeDexCacheType* resolved_types)
- ALWAYS_INLINE
- REQUIRES_SHARED(Locks::mutator_lock_) {
- SetFieldPtr<false>(ResolvedTypesOffset(), resolved_types);
- }
-
- MethodDexCacheType* GetResolvedMethods() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetFieldPtr<MethodDexCacheType*>(ResolvedMethodsOffset());
- }
-
- void SetResolvedMethods(MethodDexCacheType* resolved_methods)
- ALWAYS_INLINE
- REQUIRES_SHARED(Locks::mutator_lock_) {
- SetFieldPtr<false>(ResolvedMethodsOffset(), resolved_methods);
- }
-
- FieldDexCacheType* GetResolvedFields() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetFieldPtr<FieldDexCacheType*>(ResolvedFieldsOffset());
- }
-
- void SetResolvedFields(FieldDexCacheType* resolved_fields)
- ALWAYS_INLINE
- REQUIRES_SHARED(Locks::mutator_lock_) {
- SetFieldPtr<false>(ResolvedFieldsOffset(), resolved_fields);
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- MethodTypeDexCacheType* GetResolvedMethodTypes()
- ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetFieldPtr64<MethodTypeDexCacheType*, kVerifyFlags>(ResolvedMethodTypesOffset());
- }
-
- void SetResolvedMethodTypes(MethodTypeDexCacheType* resolved_method_types)
- ALWAYS_INLINE
- REQUIRES_SHARED(Locks::mutator_lock_) {
- SetFieldPtr<false>(ResolvedMethodTypesOffset(), resolved_method_types);
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- GcRoot<CallSite>* GetResolvedCallSites()
- ALWAYS_INLINE
- REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetFieldPtr<GcRoot<CallSite>*, kVerifyFlags>(ResolvedCallSitesOffset());
- }
-
- void SetResolvedCallSites(GcRoot<CallSite>* resolved_call_sites)
- ALWAYS_INLINE
- REQUIRES_SHARED(Locks::mutator_lock_) {
- SetFieldPtr<false>(ResolvedCallSitesOffset(), resolved_call_sites);
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- size_t NumStrings() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetField32<kVerifyFlags>(NumStringsOffset());
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- size_t NumPreResolvedStrings() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetField32<kVerifyFlags>(NumPreResolvedStringsOffset());
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- size_t NumResolvedTypes() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetField32<kVerifyFlags>(NumResolvedTypesOffset());
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- size_t NumResolvedMethods() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetField32<kVerifyFlags>(NumResolvedMethodsOffset());
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- size_t NumResolvedFields() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetField32<kVerifyFlags>(NumResolvedFieldsOffset());
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- size_t NumResolvedMethodTypes() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetField32<kVerifyFlags>(NumResolvedMethodTypesOffset());
- }
-
- template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
- size_t NumResolvedCallSites() REQUIRES_SHARED(Locks::mutator_lock_) {
- return GetField32<kVerifyFlags>(NumResolvedCallSitesOffset());
- }
-
const DexFile* GetDexFile() ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
return GetFieldPtr<const DexFile*>(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_));
}
@@ -419,35 +353,188 @@
void SetLocation(ObjPtr<String> location) REQUIRES_SHARED(Locks::mutator_lock_);
- template <typename T>
- static NativeDexCachePair<T> GetNativePair(std::atomic<NativeDexCachePair<T>>* pair_array,
- size_t idx);
-
- template <typename T>
- static void SetNativePair(std::atomic<NativeDexCachePair<T>>* pair_array,
- size_t idx,
- NativeDexCachePair<T> pair);
-
- static size_t PreResolvedStringsSize(size_t num_strings) {
- return sizeof(GcRoot<mirror::String>) * num_strings;
- }
-
- uint32_t StringSlotIndex(dex::StringIndex string_idx) REQUIRES_SHARED(Locks::mutator_lock_);
- uint32_t TypeSlotIndex(dex::TypeIndex type_idx) REQUIRES_SHARED(Locks::mutator_lock_);
- uint32_t FieldSlotIndex(uint32_t field_idx) REQUIRES_SHARED(Locks::mutator_lock_);
- uint32_t MethodSlotIndex(uint32_t method_idx) REQUIRES_SHARED(Locks::mutator_lock_);
- uint32_t MethodTypeSlotIndex(dex::ProtoIndex proto_idx) REQUIRES_SHARED(Locks::mutator_lock_);
-
void VisitReflectiveTargets(ReflectiveValueVisitor* visitor) REQUIRES(Locks::mutator_lock_);
void SetClassLoader(ObjPtr<ClassLoader> class_loader) REQUIRES_SHARED(Locks::mutator_lock_);
ObjPtr<ClassLoader> GetClassLoader() REQUIRES_SHARED(Locks::mutator_lock_);
+ template <VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
+ ReadBarrierOption kReadBarrierOption = kWithReadBarrier,
+ typename Visitor>
+ void VisitNativeRoots(const Visitor& visitor)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);
+
+ // Sets null to dex cache array fields which were allocated with the startup
+ // allocator.
+ void UnlinkStartupCaches() REQUIRES_SHARED(Locks::mutator_lock_);
+
+// NOLINTBEGIN(bugprone-macro-parentheses)
+#define DEFINE_ARRAY(name, array_kind, getter_setter, type, ids, alloc_kind) \
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> \
+ array_kind* Get ##getter_setter() \
+ ALWAYS_INLINE \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ return GetFieldPtr<array_kind*, kVerifyFlags>(getter_setter ##Offset()); \
+ } \
+ void Set ##getter_setter(array_kind* value) \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ SetFieldPtr<false>(getter_setter ##Offset(), value); \
+ } \
+ static constexpr MemberOffset getter_setter ##Offset() { \
+ return OFFSET_OF_OBJECT_MEMBER(DexCache, name); \
+ } \
+ array_kind* Allocate ##getter_setter(bool startup = false) \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ return reinterpret_cast<array_kind*>(AllocArray<type>( \
+ getter_setter ##Offset(), GetDexFile()->ids(), alloc_kind, startup)); \
+ } \
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> \
+ size_t Num ##getter_setter() REQUIRES_SHARED(Locks::mutator_lock_) { \
+ return Get ##getter_setter() == nullptr ? 0u : GetDexFile()->ids(); \
+ } \
+
+#define DEFINE_PAIR_ARRAY(name, pair_kind, getter_setter, type, size, alloc_kind) \
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> \
+ pair_kind ##Array<type, size>* Get ##getter_setter() \
+ ALWAYS_INLINE \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ return GetFieldPtr<pair_kind ##Array<type, size>*, kVerifyFlags>(getter_setter ##Offset()); \
+ } \
+ void Set ##getter_setter(pair_kind ##Array<type, size>* value) \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ SetFieldPtr<false>(getter_setter ##Offset(), value); \
+ } \
+ static constexpr MemberOffset getter_setter ##Offset() { \
+ return OFFSET_OF_OBJECT_MEMBER(DexCache, name); \
+ } \
+ pair_kind ##Array<type, size>* Allocate ##getter_setter() \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ return reinterpret_cast<pair_kind ##Array<type, size>*>( \
+ AllocArray<std::atomic<pair_kind<type>>>( \
+ getter_setter ##Offset(), size, alloc_kind)); \
+ } \
+ template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags> \
+ size_t Num ##getter_setter() REQUIRES_SHARED(Locks::mutator_lock_) { \
+ return Get ##getter_setter() == nullptr ? 0u : size; \
+ } \
+
+#define DEFINE_DUAL_CACHE( \
+ name, pair_kind, getter_setter, type, pair_size, alloc_pair_kind, \
+ array_kind, component_type, ids, alloc_array_kind) \
+ DEFINE_PAIR_ARRAY( \
+ name, pair_kind, getter_setter, type, pair_size, alloc_pair_kind) \
+ DEFINE_ARRAY( \
+ name ##array_, array_kind, getter_setter ##Array, component_type, ids, alloc_array_kind) \
+ type* Get ##getter_setter ##Entry(uint32_t index) REQUIRES_SHARED(Locks::mutator_lock_) { \
+ DCHECK_LT(index, GetDexFile()->ids()); \
+ auto* array = Get ##getter_setter ##Array(); \
+ if (array != nullptr) { \
+ return array->Get(index); \
+ } \
+ auto* pairs = Get ##getter_setter(); \
+ if (pairs != nullptr) { \
+ return pairs->Get(index); \
+ } \
+ return nullptr; \
+ } \
+ void Set ##getter_setter ##Entry(uint32_t index, type* resolved) \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ DCHECK_LT(index, GetDexFile()->ids()); \
+ auto* array = Get ##getter_setter ##Array(); \
+ if (array != nullptr) { \
+ array->Set(index, resolved); \
+ } else { \
+ auto* pairs = Get ##getter_setter(); \
+ if (pairs == nullptr) { \
+ bool should_allocate_full_array = ShouldAllocateFullArray(GetDexFile()->ids(), pair_size); \
+ if (ShouldAllocateFullArrayAtStartup() || should_allocate_full_array) { \
+ array = Allocate ##getter_setter ##Array(!should_allocate_full_array); \
+ array->Set(index, resolved); \
+ } else { \
+ pairs = Allocate ##getter_setter(); \
+ pairs->Set(index, resolved); \
+ } \
+ } else { \
+ pairs->Set(index, resolved); \
+ } \
+ } \
+ } \
+ void Unlink ##getter_setter ##ArrayIfStartup() \
+ REQUIRES_SHARED(Locks::mutator_lock_) { \
+ if (!ShouldAllocateFullArray(GetDexFile()->ids(), pair_size)) { \
+ Set ##getter_setter ##Array(nullptr) ; \
+ } \
+ }
+
+ DEFINE_ARRAY(resolved_call_sites_,
+ GcRootArray<CallSite>,
+ ResolvedCallSites,
+ GcRoot<CallSite>,
+ NumCallSiteIds,
+ LinearAllocKind::kGCRootArray)
+
+ DEFINE_DUAL_CACHE(resolved_fields_,
+ NativeDexCachePair,
+ ResolvedFields,
+ ArtField,
+ kDexCacheFieldCacheSize,
+ LinearAllocKind::kNoGCRoots,
+ NativeArray<ArtField>,
+ ArtField,
+ NumFieldIds,
+ LinearAllocKind::kNoGCRoots)
+
+ DEFINE_DUAL_CACHE(resolved_method_types_,
+ DexCachePair,
+ ResolvedMethodTypes,
+ mirror::MethodType,
+ kDexCacheMethodTypeCacheSize,
+ LinearAllocKind::kDexCacheArray,
+ GcRootArray<mirror::MethodType>,
+ GcRoot<mirror::MethodType>,
+ NumProtoIds,
+ LinearAllocKind::kGCRootArray);
+
+ DEFINE_DUAL_CACHE(resolved_methods_,
+ NativeDexCachePair,
+ ResolvedMethods,
+ ArtMethod,
+ kDexCacheMethodCacheSize,
+ LinearAllocKind::kNoGCRoots,
+ NativeArray<ArtMethod>,
+ ArtMethod,
+ NumMethodIds,
+ LinearAllocKind::kNoGCRoots)
+
+ DEFINE_DUAL_CACHE(resolved_types_,
+ DexCachePair,
+ ResolvedTypes,
+ mirror::Class,
+ kDexCacheTypeCacheSize,
+ LinearAllocKind::kDexCacheArray,
+ GcRootArray<mirror::Class>,
+ GcRoot<mirror::Class>,
+ NumTypeIds,
+ LinearAllocKind::kGCRootArray);
+
+ DEFINE_DUAL_CACHE(strings_,
+ DexCachePair,
+ Strings,
+ mirror::String,
+ kDexCacheStringCacheSize,
+ LinearAllocKind::kDexCacheArray,
+ GcRootArray<mirror::String>,
+ GcRoot<mirror::String>,
+ NumStringIds,
+ LinearAllocKind::kGCRootArray);
+
+// NOLINTEND(bugprone-macro-parentheses)
+
private:
// Allocate new array in linear alloc and save it in the given fields.
- template<typename T, size_t kMaxCacheSize>
- T* AllocArray(MemberOffset obj_offset, MemberOffset num_offset, size_t num)
+ template<typename T>
+ T* AllocArray(MemberOffset obj_offset, size_t num, LinearAllocKind kind, bool startup = false)
REQUIRES_SHARED(Locks::mutator_lock_);
// Visit instance fields of the dex cache as well as its associated arrays.
@@ -458,30 +545,32 @@
void VisitReferences(ObjPtr<Class> klass, const Visitor& visitor)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Locks::heap_bitmap_lock_);
+ // Returns whether we should allocate a full array given the current state of
+ // the runtime and oat files.
+ bool ShouldAllocateFullArrayAtStartup() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Returns whether we should allocate a full array given the number of
+ // elements.
+ static bool ShouldAllocateFullArray(size_t number_of_elements, size_t dex_cache_size) {
+ return number_of_elements <= dex_cache_size;
+ }
+
HeapReference<ClassLoader> class_loader_;
HeapReference<String> location_;
- uint64_t dex_file_; // const DexFile*
- uint64_t preresolved_strings_; // GcRoot<mirror::String*> array with num_preresolved_strings
- // elements.
- uint64_t resolved_call_sites_; // GcRoot<CallSite>* array with num_resolved_call_sites_
- // elements.
- uint64_t resolved_fields_; // std::atomic<FieldDexCachePair>*, array with
- // num_resolved_fields_ elements.
- uint64_t resolved_method_types_; // std::atomic<MethodTypeDexCachePair>* array with
- // num_resolved_method_types_ elements.
- uint64_t resolved_methods_; // ArtMethod*, array with num_resolved_methods_ elements.
- uint64_t resolved_types_; // TypeDexCacheType*, array with num_resolved_types_ elements.
- uint64_t strings_; // std::atomic<StringDexCachePair>*, array with num_strings_
- // elements.
-
- uint32_t num_preresolved_strings_; // Number of elements in the preresolved_strings_ array.
- uint32_t num_resolved_call_sites_; // Number of elements in the call_sites_ array.
- uint32_t num_resolved_fields_; // Number of elements in the resolved_fields_ array.
- uint32_t num_resolved_method_types_; // Number of elements in the resolved_method_types_ array.
- uint32_t num_resolved_methods_; // Number of elements in the resolved_methods_ array.
- uint32_t num_resolved_types_; // Number of elements in the resolved_types_ array.
- uint32_t num_strings_; // Number of elements in the strings_ array.
+ uint64_t dex_file_; // const DexFile*
+ //
+ uint64_t resolved_call_sites_; // Array of call sites
+ uint64_t resolved_fields_; // NativeDexCacheArray holding ArtField's
+ uint64_t resolved_fields_array_; // Array of ArtField's.
+ uint64_t resolved_method_types_; // DexCacheArray holding mirror::MethodType's
+ uint64_t resolved_method_types_array_; // Array of mirror::MethodType's
+ uint64_t resolved_methods_; // NativeDexCacheArray holding ArtMethod's
+ uint64_t resolved_methods_array_; // Array of ArtMethod's
+ uint64_t resolved_types_; // DexCacheArray holding mirror::Class's
+ uint64_t resolved_types_array_; // Array of resolved types.
+ uint64_t strings_; // DexCacheArray holding mirror::String's
+ uint64_t strings_array_; // Array of String's.
friend struct art::DexCacheOffsets; // for verifying offset information
friend class linker::ImageWriter;
diff --git a/runtime/mirror/dex_cache_test.cc b/runtime/mirror/dex_cache_test.cc
index a0e8fda..bc521f5 100644
--- a/runtime/mirror/dex_cache_test.cc
+++ b/runtime/mirror/dex_cache_test.cc
@@ -146,15 +146,16 @@
// The MethodTypes dex file contains a single interface with two abstract
// methods. It must therefore contain precisely two method IDs.
ASSERT_EQ(2u, dex_file.NumProtoIds());
- ASSERT_EQ(dex_file.NumProtoIds(), dex_cache->NumResolvedMethodTypes());
- MethodTypeDexCacheType* method_types_cache = dex_cache->GetResolvedMethodTypes();
+ ASSERT_EQ(dex_file.NumProtoIds(), dex_cache->NumResolvedMethodTypesArray());
+ ASSERT_EQ(0u, dex_cache->NumResolvedMethodTypes());
+ auto* method_types_cache = dex_cache->GetResolvedMethodTypesArray();
for (size_t i = 0; i < dex_file.NumProtoIds(); ++i) {
- const MethodTypeDexCachePair pair = method_types_cache[i].load(std::memory_order_relaxed);
- if (dex::ProtoIndex(pair.index) == method1_id.proto_idx_) {
- ASSERT_EQ(method1_type.Get(), pair.object.Read());
- } else if (dex::ProtoIndex(pair.index) == method2_id.proto_idx_) {
- ASSERT_EQ(method2_type.Get(), pair.object.Read());
+ auto* method_type = method_types_cache->Get(i);
+ if (dex::ProtoIndex(i) == method1_id.proto_idx_) {
+ ASSERT_EQ(method1_type.Get(), method_type);
+ } else if (dex::ProtoIndex(i) == method2_id.proto_idx_) {
+ ASSERT_EQ(method2_type.Get(), method_type);
} else {
ASSERT_TRUE(false);
}
diff --git a/runtime/mirror/method_handles_lookup.cc b/runtime/mirror/method_handles_lookup.cc
index e9c41f9..62c35df 100644
--- a/runtime/mirror/method_handles_lookup.cc
+++ b/runtime/mirror/method_handles_lookup.cc
@@ -20,7 +20,6 @@
#include "class_root-inl.h"
#include "dex/modifiers.h"
#include "handle_scope.h"
-#include "jni/jni_internal.h"
#include "mirror/method_handle_impl.h"
#include "obj_ptr-inl.h"
#include "object-inl.h"
@@ -42,25 +41,17 @@
}
ObjPtr<MethodHandlesLookup> MethodHandlesLookup::GetDefault(Thread* const self) {
- ArtMethod* lookup = jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandles_lookup);
- JValue result;
- lookup->Invoke(self, nullptr, 0, &result, "L");
- return ObjPtr<MethodHandlesLookup>::DownCast(result.GetL());
+ ArtMethod* lookup = WellKnownClasses::java_lang_invoke_MethodHandles_lookup;
+ return ObjPtr<MethodHandlesLookup>::DownCast(lookup->InvokeStatic<'L'>(self));
}
ObjPtr<MethodHandle> MethodHandlesLookup::FindConstructor(Thread* const self,
Handle<Class> klass,
Handle<MethodType> method_type) {
- ArtMethod* findConstructor =
- jni::DecodeArtMethod(WellKnownClasses::java_lang_invoke_MethodHandles_Lookup_findConstructor);
- uint32_t args[] = {
- static_cast<uint32_t>(reinterpret_cast<uintptr_t>(this)),
- static_cast<uint32_t>(reinterpret_cast<uintptr_t>(klass.Get())),
- static_cast<uint32_t>(reinterpret_cast<uintptr_t>(method_type.Get()))
- };
- JValue result;
- findConstructor->Invoke(self, args, sizeof(args), &result, "LLL");
- return ObjPtr<MethodHandle>::DownCast(result.GetL());
+ ArtMethod* find_constructor =
+ WellKnownClasses::java_lang_invoke_MethodHandles_Lookup_findConstructor;
+ return ObjPtr<MethodHandle>::DownCast(
+ find_constructor->InvokeFinal<'L', 'L', 'L'>(self, this, klass.Get(), method_type.Get()));
}
} // namespace mirror
diff --git a/runtime/mirror/object-inl.h b/runtime/mirror/object-inl.h
index c679fde..318a811 100644
--- a/runtime/mirror/object-inl.h
+++ b/runtime/mirror/object-inl.h
@@ -104,7 +104,7 @@
}
inline uint32_t Object::GetMarkBit() {
- CHECK(kUseReadBarrier);
+ CHECK(gUseReadBarrier);
return GetLockWord(false).MarkBitState();
}
@@ -880,7 +880,7 @@
// inheritance hierarchy and find reference offsets the hard way. In the static case, just
// consider this class.
for (ObjPtr<Class> klass = kIsStatic
- ? AsClass<kVerifyFlags>()
+ ? ObjPtr<Class>::DownCast(this)
: GetClass<kVerifyFlags, kReadBarrierOption>();
klass != nullptr;
klass = kIsStatic ? nullptr : klass->GetSuperClass<kVerifyFlags, kReadBarrierOption>()) {
diff --git a/runtime/mirror/object-refvisitor-inl.h b/runtime/mirror/object-refvisitor-inl.h
index f98c433..5251953 100644
--- a/runtime/mirror/object-refvisitor-inl.h
+++ b/runtime/mirror/object-refvisitor-inl.h
@@ -90,6 +90,104 @@
}
}
+// Could be called with from-space address of the object as we access klass and
+// length (in case of arrays/strings) and we don't want to cause cascading faults.
+template <bool kFetchObjSize,
+ bool kVisitNativeRoots,
+ VerifyObjectFlags kVerifyFlags,
+ ReadBarrierOption kReadBarrierOption,
+ typename Visitor>
+inline size_t Object::VisitRefsForCompaction(const Visitor& visitor,
+ MemberOffset begin,
+ MemberOffset end) {
+ constexpr VerifyObjectFlags kSizeOfFlags = RemoveThisFlags(kVerifyFlags);
+ size_t size;
+ // We want to continue using pre-compact klass to avoid cascading faults.
+ ObjPtr<Class> klass = GetClass<kVerifyFlags, kReadBarrierOption>();
+ DCHECK(klass != nullptr) << "obj=" << this;
+ const uint32_t class_flags = klass->GetClassFlags<kVerifyNone>();
+ if (LIKELY(class_flags == kClassFlagNormal)) {
+ DCHECK((!klass->IsVariableSize<kVerifyFlags>()));
+ VisitInstanceFieldsReferences<kVerifyFlags, kReadBarrierOption>(klass, visitor);
+ size = kFetchObjSize ? klass->GetObjectSize<kSizeOfFlags>() : 0;
+ DCHECK((!klass->IsClassClass<kVerifyFlags>()));
+ DCHECK(!klass->IsStringClass<kVerifyFlags>());
+ DCHECK(!klass->IsClassLoaderClass<kVerifyFlags>());
+ DCHECK((!klass->IsArrayClass<kVerifyFlags>()));
+ } else {
+ if ((class_flags & kClassFlagNoReferenceFields) == 0) {
+ DCHECK(!klass->IsStringClass<kVerifyFlags>());
+ if (class_flags == kClassFlagClass) {
+ DCHECK((klass->IsClassClass<kVerifyFlags>()));
+ ObjPtr<Class> as_klass = ObjPtr<Class>::DownCast(this);
+ as_klass->VisitReferences<kVisitNativeRoots, kVerifyFlags, kReadBarrierOption>(klass,
+ visitor);
+ size = kFetchObjSize ? as_klass->SizeOf<kSizeOfFlags>() : 0;
+ } else if (class_flags == kClassFlagObjectArray) {
+ DCHECK((klass->IsObjectArrayClass<kVerifyFlags, kReadBarrierOption>()));
+ ObjPtr<ObjectArray<Object>> obj_arr = ObjPtr<ObjectArray<Object>>::DownCast(this);
+ obj_arr->VisitReferences(visitor, begin, end);
+ size = kFetchObjSize ? obj_arr->SizeOf<kSizeOfFlags, kReadBarrierOption>() : 0;
+ } else if ((class_flags & kClassFlagReference) != 0) {
+ VisitInstanceFieldsReferences<kVerifyFlags, kReadBarrierOption>(klass, visitor);
+ // Visit referent also as this is about updating the reference only.
+ // There is no reference processing happening here.
+ visitor(this, mirror::Reference::ReferentOffset(), /* is_static= */ false);
+ size = kFetchObjSize ? klass->GetObjectSize<kSizeOfFlags>() : 0;
+ } else if (class_flags == kClassFlagDexCache) {
+ ObjPtr<DexCache> const dex_cache = ObjPtr<DexCache>::DownCast(this);
+ dex_cache->VisitReferences<kVisitNativeRoots,
+ kVerifyFlags,
+ kReadBarrierOption>(klass, visitor);
+ size = kFetchObjSize ? klass->GetObjectSize<kSizeOfFlags>() : 0;
+ } else {
+ ObjPtr<ClassLoader> const class_loader = ObjPtr<ClassLoader>::DownCast(this);
+ class_loader->VisitReferences<kVisitNativeRoots,
+ kVerifyFlags,
+ kReadBarrierOption>(klass, visitor);
+ size = kFetchObjSize ? klass->GetObjectSize<kSizeOfFlags>() : 0;
+ }
+ } else {
+ DCHECK((!klass->IsClassClass<kVerifyFlags>()));
+ DCHECK((!klass->IsObjectArrayClass<kVerifyFlags, kReadBarrierOption>()));
+ if ((class_flags & kClassFlagString) != 0) {
+ size = kFetchObjSize ? static_cast<String*>(this)->SizeOf<kSizeOfFlags>() : 0;
+ } else if (klass->IsArrayClass<kVerifyFlags>()) {
+ // TODO: We can optimize this by implementing a SizeOf() version which takes
+ // component-size-shift as an argument, thereby avoiding multiple loads of
+ // component_type.
+ size = kFetchObjSize
+ ? static_cast<Array*>(this)->SizeOf<kSizeOfFlags, kReadBarrierOption>()
+ : 0;
+ } else {
+ DCHECK_EQ(class_flags, kClassFlagNoReferenceFields)
+ << "class_flags: " << std::hex << class_flags;
+ // Only possibility left is of a normal klass instance with no references.
+ size = kFetchObjSize ? klass->GetObjectSize<kSizeOfFlags>() : 0;
+ }
+
+ if (kIsDebugBuild) {
+ // String still has instance fields for reflection purposes but these don't exist in
+ // actual string instances.
+ if (!klass->IsStringClass<kVerifyFlags>()) {
+ size_t total_reference_instance_fields = 0;
+ ObjPtr<Class> super_class = klass;
+ do {
+ total_reference_instance_fields +=
+ super_class->NumReferenceInstanceFields<kVerifyFlags>();
+ super_class = super_class->GetSuperClass<kVerifyFlags, kReadBarrierOption>();
+ } while (super_class != nullptr);
+ // The only reference field should be the object's class. This field is handled at the
+ // beginning of the function.
+ CHECK_EQ(total_reference_instance_fields, 1u);
+ }
+ }
+ }
+ }
+ visitor(this, ClassOffset(), /* is_static= */ false);
+ return size;
+}
+
} // namespace mirror
} // namespace art
diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc
index ede1c66..bb9e85d 100644
--- a/runtime/mirror/object.cc
+++ b/runtime/mirror/object.cc
@@ -115,7 +115,7 @@
}
}
- if (kUseReadBarrier) {
+ if (gUseReadBarrier) {
// We need a RB here. After copying the whole object above, copy references fields one by one
// again with a RB to make sure there are no from space refs. TODO: Optimize this later?
CopyReferenceFieldsWithReadBarrierVisitor visitor(dest);
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index ac72745..0ba545b 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -647,6 +647,17 @@
typename JavaLangRefVisitor = VoidFunctor>
void VisitReferences(const Visitor& visitor, const JavaLangRefVisitor& ref_visitor)
NO_THREAD_SAFETY_ANALYSIS;
+ // VisitReferences version for compaction. It is invoked with from-space
+ // object so that portions of the object, like klass and length (for arrays),
+ // can be accessed without causing cascading faults.
+ template <bool kFetchObjSize = true,
+ bool kVisitNativeRoots = false,
+ VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags,
+ ReadBarrierOption kReadBarrierOption = kWithFromSpaceBarrier,
+ typename Visitor>
+ size_t VisitRefsForCompaction(const Visitor& visitor,
+ MemberOffset begin,
+ MemberOffset end) NO_THREAD_SAFETY_ANALYSIS;
ArtField* FindFieldByOffset(MemberOffset offset) REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h
index e4fe03b..87f24eb 100644
--- a/runtime/mirror/object_array-inl.h
+++ b/runtime/mirror/object_array-inl.h
@@ -121,7 +121,7 @@
if (copy_forward) {
// Forward copy.
bool baker_non_gray_case = false;
- if (kUseReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
uintptr_t fake_address_dependency;
if (!ReadBarrier::IsGray(src.Ptr(), &fake_address_dependency)) {
baker_non_gray_case = true;
@@ -146,7 +146,7 @@
} else {
// Backward copy.
bool baker_non_gray_case = false;
- if (kUseReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
uintptr_t fake_address_dependency;
if (!ReadBarrier::IsGray(src.Ptr(), &fake_address_dependency)) {
baker_non_gray_case = true;
@@ -196,7 +196,7 @@
// We can't use memmove since it does not handle read barriers and may do by per byte copying.
// See b/32012820.
bool baker_non_gray_case = false;
- if (kUseReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
uintptr_t fake_address_dependency;
if (!ReadBarrier::IsGray(src.Ptr(), &fake_address_dependency)) {
baker_non_gray_case = true;
@@ -244,7 +244,7 @@
ObjPtr<T> o = nullptr;
int i = 0;
bool baker_non_gray_case = false;
- if (kUseReadBarrier && kUseBakerReadBarrier) {
+ if (gUseReadBarrier && kUseBakerReadBarrier) {
uintptr_t fake_address_dependency;
if (!ReadBarrier::IsGray(src.Ptr(), &fake_address_dependency)) {
baker_non_gray_case = true;
@@ -327,7 +327,20 @@
inline void ObjectArray<T>::VisitReferences(const Visitor& visitor) {
const size_t length = static_cast<size_t>(GetLength());
for (size_t i = 0; i < length; ++i) {
- visitor(this, OffsetOfElement(i), false);
+ visitor(this, OffsetOfElement(i), /* is_static= */ false);
+ }
+}
+
+template<class T> template<typename Visitor>
+inline void ObjectArray<T>::VisitReferences(const Visitor& visitor,
+ MemberOffset begin,
+ MemberOffset end) {
+ const size_t length = static_cast<size_t>(GetLength());
+ begin = std::max(begin, OffsetOfElement(0));
+ end = std::min(end, OffsetOfElement(length));
+ while (begin < end) {
+ visitor(this, begin, /* is_static= */ false, /*is_obj_array*/ true);
+ begin += kHeapReferenceSize;
}
}
diff --git a/runtime/mirror/object_array.h b/runtime/mirror/object_array.h
index a20c86b..9a53708 100644
--- a/runtime/mirror/object_array.h
+++ b/runtime/mirror/object_array.h
@@ -150,6 +150,10 @@
// REQUIRES_SHARED(Locks::mutator_lock_).
template<typename Visitor>
void VisitReferences(const Visitor& visitor) NO_THREAD_SAFETY_ANALYSIS;
+ template<typename Visitor>
+ void VisitReferences(const Visitor& visitor,
+ MemberOffset begin,
+ MemberOffset end) NO_THREAD_SAFETY_ANALYSIS;
friend class Object; // For VisitReferences
DISALLOW_IMPLICIT_CONSTRUCTORS(ObjectArray);
diff --git a/runtime/mirror/object_reference.h b/runtime/mirror/object_reference.h
index 386244d..4c3a5dc 100644
--- a/runtime/mirror/object_reference.h
+++ b/runtime/mirror/object_reference.h
@@ -50,6 +50,7 @@
vis("Ljava/lang/ClassNotFoundException;") \
vis("Ljava/lang/DexCache;") \
vis("Ljava/lang/Object;") \
+ vis("Ljava/lang/StackFrameInfo;") \
vis("Ljava/lang/StackTraceElement;") \
vis("Ljava/lang/String;") \
vis("Ljava/lang/Throwable;") \
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index e26cf42..6f42c5b 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -50,6 +50,10 @@
class ObjectTest : public CommonRuntimeTest {
protected:
+ ObjectTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+
void AssertString(int32_t expected_utf16_length,
const char* utf8_in,
const char* utf16_expected_le,
@@ -420,10 +424,12 @@
ASSERT_TRUE(field_id != nullptr);
uint32_t field_idx = dex_file->GetIndexForFieldId(*field_id);
- ArtField* field = FindFieldFromCode<StaticObjectRead, true>(field_idx, clinit, Thread::Current(),
- sizeof(HeapReference<Object>));
+ ArtField* field = FindFieldFromCode<StaticObjectRead>(field_idx,
+ clinit,
+ Thread::Current(),
+ sizeof(HeapReference<Object>));
ObjPtr<Object> s0 = field->GetObj(klass.Get());
- EXPECT_TRUE(s0 != nullptr);
+ EXPECT_TRUE(s0 != nullptr) << field->PrettyField();
Handle<CharArray> char_array(hs.NewHandle(CharArray::Alloc(soa.Self(), 0)));
field->SetObj<false>(field->GetDeclaringClass(), char_array.Get());
@@ -522,7 +528,7 @@
StackHandleScope<1> hs(soa.Self());
Handle<String> string(hs.NewHandle(String::AllocFromModifiedUtf8(soa.Self(), "android")));
EXPECT_EQ(string->GetLength(), 7);
- EXPECT_EQ(string->GetUtfLength(), 7);
+ EXPECT_EQ(string->GetModifiedUtf8Length(), 7);
}
TEST_F(ObjectTest, DescriptorCompare) {
diff --git a/runtime/mirror/stack_frame_info.cc b/runtime/mirror/stack_frame_info.cc
new file mode 100644
index 0000000..dd3e8f7
--- /dev/null
+++ b/runtime/mirror/stack_frame_info.cc
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 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 "stack_frame_info.h"
+
+#include "class-alloc-inl.h"
+#include "class.h"
+#include "class_root-inl.h"
+#include "gc/accounting/card_table-inl.h"
+#include "handle_scope-inl.h"
+#include "object-inl.h"
+#include "string.h"
+
+namespace art {
+namespace mirror {
+
+void StackFrameInfo::AssignFields(Handle<Class> declaring_class,
+ Handle<MethodType> method_type,
+ Handle<String> method_name,
+ Handle<String> file_name,
+ int32_t line_number,
+ int32_t dex_pc) {
+ if (Runtime::Current()->IsActiveTransaction()) {
+ SetFields<true>(declaring_class.Get(), method_type.Get(), method_name.Get(),
+ file_name.Get(), line_number, dex_pc);
+ } else {
+ SetFields<false>(declaring_class.Get(), method_type.Get(), method_name.Get(),
+ file_name.Get(), line_number, dex_pc);
+ }
+}
+
+template<bool kTransactionActive>
+void StackFrameInfo::SetFields(ObjPtr<Class> declaring_class,
+ ObjPtr<MethodType> method_type,
+ ObjPtr<String> method_name,
+ ObjPtr<String> file_name,
+ int32_t line_number,
+ int32_t bci) {
+ SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackFrameInfo, declaring_class_),
+ declaring_class);
+ SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackFrameInfo, method_type_),
+ method_type);
+ SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackFrameInfo, method_name_),
+ method_name);
+ SetFieldObject<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackFrameInfo, file_name_),
+ file_name);
+ SetField32<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackFrameInfo, line_number_),
+ line_number);
+ SetField32<kTransactionActive>(OFFSET_OF_OBJECT_MEMBER(StackFrameInfo, bci_),
+ bci);
+}
+
+} // namespace mirror
+} // namespace art
diff --git a/runtime/mirror/stack_frame_info.h b/runtime/mirror/stack_frame_info.h
new file mode 100644
index 0000000..24f8c8f
--- /dev/null
+++ b/runtime/mirror/stack_frame_info.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_MIRROR_STACK_FRAME_INFO_H_
+#define ART_RUNTIME_MIRROR_STACK_FRAME_INFO_H_
+
+#include "method_type.h"
+#include "object.h"
+#include "stack_trace_element.h"
+
+namespace art {
+
+template<class T> class Handle;
+struct StackFrameInfoOffsets;
+
+namespace mirror {
+
+// C++ mirror of java.lang.StackFrameInfo
+class MANAGED StackFrameInfo final : public Object {
+ public:
+ MIRROR_CLASS("Ljava/lang/StackFrameInfo;");
+
+ void AssignFields(Handle<Class> declaring_class,
+ Handle<MethodType> method_type,
+ Handle<String> method_name,
+ Handle<String> file_name,
+ int32_t line_number,
+ int32_t dex_pc)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ private:
+ // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
+ HeapReference<Class> declaring_class_;
+ HeapReference<String> file_name_;
+ HeapReference<String> method_name_;
+ HeapReference<Class> method_type_;
+ HeapReference<StackTraceElement> ste_;
+ int32_t bci_;
+ int32_t line_number_;
+ bool retain_class_ref_;
+
+ template<bool kTransactionActive>
+ void SetFields(ObjPtr<Class> declaring_class,
+ ObjPtr<MethodType> method_type,
+ ObjPtr<String> method_name,
+ ObjPtr<String> file_name,
+ int32_t line_number,
+ int32_t bci)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ friend struct art::StackFrameInfoOffsets; // for verifying offset information
+ DISALLOW_IMPLICIT_CONSTRUCTORS(StackFrameInfo);
+};
+
+} // namespace mirror
+} // namespace art
+
+#endif // ART_RUNTIME_MIRROR_STACK_FRAME_INFO_H_
diff --git a/runtime/mirror/string-alloc-inl.h b/runtime/mirror/string-alloc-inl.h
index 533053d..cb2dcb2 100644
--- a/runtime/mirror/string-alloc-inl.h
+++ b/runtime/mirror/string-alloc-inl.h
@@ -18,6 +18,7 @@
#include "string-inl.h"
+#include "android-base/endian.h"
#include "android-base/stringprintf.h"
#include "array.h"
@@ -89,6 +90,41 @@
};
// Sets string count and value in the allocation code path to ensure it is guarded by a CAS.
+class SetStringCountAndUtf16BytesVisitor {
+ public:
+ SetStringCountAndUtf16BytesVisitor(int32_t count, Handle<ByteArray> src_array, int32_t offset)
+ : count_(count), src_array_(src_array), offset_(offset) {
+ }
+
+ void operator()(ObjPtr<Object> obj, size_t usable_size ATTRIBUTE_UNUSED) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Avoid AsString as object is not yet in live bitmap or allocation stack.
+ ObjPtr<String> string = ObjPtr<String>::DownCast(obj);
+ string->SetCount(count_);
+ DCHECK_IMPLIES(string->IsCompressed(), kUseStringCompression);
+ uint32_t length = String::GetLengthFromCount(count_);
+ const uint8_t* const src = reinterpret_cast<uint8_t*>(src_array_->GetData()) + offset_;
+ if (UNLIKELY(string->IsCompressed())) {
+ uint8_t* valueCompressed = string->GetValueCompressed();
+ for (uint32_t i = 0; i < length; i++) {
+ valueCompressed[i] = (src[i << 1] & 0xFF);
+ }
+ } else {
+ uint16_t* value = string->GetValue();
+ for (uint32_t i = 0; i < length; i++) {
+ uint32_t index = (i << 1);
+ value[i] = (src[index] & 0xFF) + ((src[index + 1] & 0xFF) << 8);
+ }
+ }
+ }
+
+ private:
+ const int32_t count_;
+ Handle<ByteArray> src_array_;
+ const int32_t offset_;
+};
+
+// Sets string count and value in the allocation code path to ensure it is guarded by a CAS.
class SetStringCountAndValueVisitorFromCharArray {
public:
SetStringCountAndValueVisitorFromCharArray(int32_t count, Handle<CharArray> src_array,
@@ -216,13 +252,39 @@
const uint8_t* const src = reinterpret_cast<uint8_t*>(array->GetData()) + offset;
high_byte &= 0xff; // Extract the relevant bits before determining `compressible`.
const bool compressible =
- kUseStringCompression && String::AllASCII<uint8_t>(src, byte_length) && (high_byte == 0);
+ kUseStringCompression &&
+ String::AllASCII<uint8_t>(src, byte_length) &&
+ (high_byte == 0 || byte_length == 0);
const int32_t length_with_flag = String::GetFlaggedCount(byte_length, compressible);
SetStringCountAndBytesVisitor visitor(length_with_flag, array, offset, high_byte << 8);
return Alloc<kIsInstrumented>(self, length_with_flag, allocator_type, visitor);
}
template <bool kIsInstrumented>
+inline ObjPtr<String> String::AllocFromUtf16ByteArray(Thread* self,
+ int32_t char_count,
+ Handle<ByteArray> array,
+ int32_t offset,
+ gc::AllocatorType allocator_type) {
+ static_assert(__BYTE_ORDER == __LITTLE_ENDIAN,
+ "Please update this function and java-side callers to support big endian.");
+ const uint8_t* const src = reinterpret_cast<uint8_t*>(array->GetData()) + offset;
+ bool compressible = kUseStringCompression;
+ if (compressible) {
+ uint32_t byte_count = (static_cast<uint32_t>(char_count) << 1);
+ for (uint32_t i = 0; i < byte_count; i += 2) {
+ if (!IsASCII((src[i] & 0xff) + ((src[i + 1] & 0xff) << 8))) {
+ compressible = false;
+ break;
+ }
+ }
+ }
+ const int32_t length_with_flag = String::GetFlaggedCount(char_count, compressible);
+ SetStringCountAndUtf16BytesVisitor visitor(length_with_flag, array, offset);
+ return Alloc<kIsInstrumented>(self, length_with_flag, allocator_type, visitor);
+}
+
+template <bool kIsInstrumented>
inline ObjPtr<String> String::AllocFromCharArray(Thread* self,
int32_t count,
Handle<CharArray> array,
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index 5903872..883a45c 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -35,11 +35,11 @@
// lambda$codePoints$1$CharSequence
// which were virtual functions in standalone desugar, becomes
// direct functions with D8 desugaring.
- uint32_t vtable_entries = Object::kVTableLength + 60;
+ uint32_t vtable_entries = Object::kVTableLength + 67;
#else
- uint32_t vtable_entries = Object::kVTableLength + 62;
+ uint32_t vtable_entries = Object::kVTableLength + 69;
#endif
- return Class::ComputeClassSize(true, vtable_entries, 0, 0, 0, 1, 2, pointer_size);
+ return Class::ComputeClassSize(true, vtable_entries, 3, 0, 0, 1, 2, pointer_size);
}
inline uint16_t String::CharAt(int32_t index) {
@@ -83,11 +83,11 @@
return result;
}
-inline int32_t String::GetUtfLength() {
+inline int32_t String::GetModifiedUtf8Length() {
if (IsCompressed()) {
return GetLength();
} else {
- return CountUtf8Bytes(GetValue(), GetLength());
+ return CountModifiedUtf8BytesInUtf16(GetValue(), GetLength());
}
}
diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc
index d059278..839e01a 100644
--- a/runtime/mirror/string.cc
+++ b/runtime/mirror/string.cc
@@ -19,6 +19,7 @@
#include "arch/memcmp16.h"
#include "array-alloc-inl.h"
#include "base/array_ref.h"
+#include "base/casts.h"
#include "base/stl_util.h"
#include "class-inl.h"
#include "dex/descriptors_names.h"
@@ -254,7 +255,7 @@
return Alloc(self, length_with_flag, allocator_type, visitor);
}
-bool String::Equals(ObjPtr<String> that) {
+bool String::Equals(mirror::String* that) {
if (this == that) {
// Quick reference equality test
return true;
@@ -312,7 +313,7 @@
if (IsCompressed()) {
return std::string(reinterpret_cast<const char*>(GetValueCompressed()), GetLength());
} else {
- size_t byte_count = GetUtfLength();
+ size_t byte_count = GetModifiedUtf8Length();
std::string result(byte_count, static_cast<char>(0));
ConvertUtf16ToModifiedUtf8(&result[0], byte_count, GetValue(), GetLength());
return result;
@@ -396,6 +397,38 @@
}
}
+void String::FillBytesLatin1(Handle<ByteArray> array, int32_t index) {
+ int8_t* data = array->GetData() + index;
+ int32_t length = GetLength();
+ if (IsCompressed()) {
+ const uint8_t* value = GetValueCompressed();
+ memcpy(data, value, length * sizeof(uint8_t));
+ } else {
+ // Drop the high byte of the characters.
+ // The caller should check that all dropped high bytes are zeros.
+ const uint16_t* value = GetValue();
+ for (int32_t i = 0; i < length; ++i) {
+ data[i] = static_cast<int8_t>(dchecked_integral_cast<uint8_t>(value[i]));
+ }
+ }
+}
+
+void String::FillBytesUTF16(Handle<ByteArray> array, int32_t index) {
+ int8_t* data = array->GetData() + index;
+ int32_t length = GetLength();
+ if (IsCompressed()) {
+ const uint8_t* value = GetValueCompressed();
+ uint32_t d_index = 0;
+ for (int i = 0; i < length; ++i) {
+ data[d_index++] = static_cast<int8_t>(value[i]);
+ data[d_index++] = 0;
+ }
+ } else {
+ const uint16_t* value = GetValue();
+ memcpy(data, value, length * sizeof(uint16_t));
+ }
+}
+
bool String::IsValueNull() {
return (IsCompressed()) ? (GetValueCompressed() == nullptr) : (GetValue() == nullptr);
}
diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h
index 2eb8e0a..90eb092 100644
--- a/runtime/mirror/string.h
+++ b/runtime/mirror/string.h
@@ -114,7 +114,7 @@
// Computes and returns the hash code.
int32_t ComputeHashCode() REQUIRES_SHARED(Locks::mutator_lock_);
- int32_t GetUtfLength() REQUIRES_SHARED(Locks::mutator_lock_);
+ int32_t GetModifiedUtf8Length() REQUIRES_SHARED(Locks::mutator_lock_);
uint16_t CharAt(int32_t index) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -142,6 +142,14 @@
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
template <bool kIsInstrumented = true>
+ ALWAYS_INLINE static ObjPtr<String> AllocFromUtf16ByteArray(Thread* self,
+ int32_t char_count,
+ Handle<ByteArray> array,
+ int32_t offset,
+ gc::AllocatorType allocator_type)
+ REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Roles::uninterruptible_);
+
+ template <bool kIsInstrumented = true>
ALWAYS_INLINE static ObjPtr<String> AllocFromCharArray(Thread* self,
int32_t count,
Handle<CharArray> array,
@@ -189,7 +197,14 @@
bool Equals(const char* modified_utf8) REQUIRES_SHARED(Locks::mutator_lock_);
- bool Equals(ObjPtr<String> that) REQUIRES_SHARED(Locks::mutator_lock_);
+ bool Equals(ObjPtr<mirror::String> that) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return Equals(that.Ptr());
+ }
+
+ // A version that takes a mirror::String pointer instead of ObjPtr as it's being
+ // called by the runtime app image code which can encode mirror::String at 64bit
+ // addresses (ObjPtr only works with 32bit pointers).
+ bool Equals(mirror::String* that) REQUIRES_SHARED(Locks::mutator_lock_);
// Create a modified UTF-8 encoded std::string from a java/lang/String object.
std::string ToModifiedUtf8() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -209,6 +224,12 @@
void GetChars(int32_t start, int32_t end, Handle<CharArray> array, int32_t index)
REQUIRES_SHARED(Locks::mutator_lock_);
+ void FillBytesLatin1(Handle<ByteArray> array, int32_t index)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ void FillBytesUTF16(Handle<ByteArray> array, int32_t index)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
template<VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags>
bool IsCompressed() REQUIRES_SHARED(Locks::mutator_lock_) {
return kUseStringCompression && IsCompressed(GetCount());
diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc
index b538fa9..a38ba1c 100644
--- a/runtime/mirror/throwable.cc
+++ b/runtime/mirror/throwable.cc
@@ -31,7 +31,7 @@
#include "object_array.h"
#include "stack_trace_element-inl.h"
#include "string.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
namespace mirror {
@@ -70,14 +70,14 @@
}
bool Throwable::IsCheckedException() {
- if (InstanceOf(WellKnownClasses::ToClass(WellKnownClasses::java_lang_Error))) {
+ if (IsError()) {
return false;
}
- return !InstanceOf(WellKnownClasses::ToClass(WellKnownClasses::java_lang_RuntimeException));
+ return !InstanceOf(WellKnownClasses::java_lang_RuntimeException.Get());
}
bool Throwable::IsError() {
- return InstanceOf(WellKnownClasses::ToClass(WellKnownClasses::java_lang_Error));
+ return InstanceOf(WellKnownClasses::java_lang_Error.Get());
}
int32_t Throwable::GetStackDepth() {
diff --git a/runtime/mirror/var_handle.cc b/runtime/mirror/var_handle.cc
index d36a2ab..2220f92 100644
--- a/runtime/mirror/var_handle.cc
+++ b/runtime/mirror/var_handle.cc
@@ -205,7 +205,7 @@
// Method to insert a read barrier for accessors to reference fields.
inline void ReadBarrierForVarHandleAccess(ObjPtr<Object> obj, MemberOffset field_offset)
REQUIRES_SHARED(Locks::mutator_lock_) {
- if (kUseReadBarrier) {
+ if (gUseReadBarrier) {
// We need to ensure that the reference stored in the field is a to-space one before attempting
// the CompareAndSet/CompareAndExchange/Exchange operation otherwise it will fail incorrectly
// if obj is in the process of being moved.
@@ -221,11 +221,6 @@
}
}
-inline MemberOffset GetMemberOffset(jfieldID field_id) REQUIRES_SHARED(Locks::mutator_lock_) {
- ArtField* const field = jni::DecodeArtField(field_id);
- return field->GetOffset();
-}
-
//
// Helper methods for storing results from atomic operations into
// JValue instances.
@@ -1934,7 +1929,7 @@
// Check access_mode is compatible with ByteBuffer's read-only property.
bool is_read_only = byte_buffer->GetFieldBoolean(
- GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_isReadOnly));
+ WellKnownClasses::java_nio_ByteBuffer_isReadOnly->GetOffset());
if (is_read_only && !IsReadOnlyAccessMode(access_mode)) {
ThrowReadOnlyBufferException();
return false;
@@ -1942,20 +1937,20 @@
// The native_address is only set for ByteBuffer instances backed by native memory.
const int64_t native_address =
- byte_buffer->GetField64(GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_address));
+ byte_buffer->GetField64(WellKnownClasses::java_nio_Buffer_address->GetOffset());
// Determine offset and limit for accesses.
int32_t byte_buffer_offset;
if (native_address == 0L) {
// Accessing a heap allocated byte buffer.
byte_buffer_offset = byte_buffer->GetField32(
- GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_offset));
+ WellKnownClasses::java_nio_ByteBuffer_offset->GetOffset());
} else {
// Accessing direct memory.
byte_buffer_offset = 0;
}
- const int32_t byte_buffer_limit = byte_buffer->GetField32(
- GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_limit));
+ const int32_t byte_buffer_limit =
+ byte_buffer->GetField32(WellKnownClasses::java_nio_Buffer_limit->GetOffset());
const int32_t byte_buffer_length = byte_buffer_offset + byte_buffer_limit;
const Primitive::Type primitive_type = GetVarType()->GetPrimitiveType();
@@ -1967,7 +1962,7 @@
int8_t* data;
if (native_address == 0) {
ObjPtr<ByteArray> heap_byte_array = byte_buffer->GetFieldObject<ByteArray>(
- GetMemberOffset(WellKnownClasses::java_nio_ByteBuffer_hb));
+ WellKnownClasses::java_nio_ByteBuffer_hb->GetOffset());
data = heap_byte_array->GetData();
} else {
data = reinterpret_cast<int8_t*>(static_cast<uint32_t>(native_address));
diff --git a/runtime/mirror/var_handle_test.cc b/runtime/mirror/var_handle_test.cc
index 9c1a2e7..859e86c 100644
--- a/runtime/mirror/var_handle_test.cc
+++ b/runtime/mirror/var_handle_test.cc
@@ -40,6 +40,10 @@
// Tests for mirror::VarHandle and it's descendents.
class VarHandleTest : public CommonRuntimeTest {
public:
+ VarHandleTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+
static ObjPtr<FieldVarHandle> CreateFieldVarHandle(Thread* const self,
ArtField* art_field,
int32_t access_modes_bit_mask)
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 0cad79b..4e64c95 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -1139,7 +1139,7 @@
lock_word.GCState()));
// Only this thread pays attention to the count. Thus there is no need for stronger
// than relaxed memory ordering.
- if (!kUseReadBarrier) {
+ if (!gUseReadBarrier) {
h_obj->SetLockWord(thin_locked, /* as_volatile= */ false);
AtraceMonitorLock(self, h_obj.Get(), /* is_wait= */ false);
return h_obj.Get(); // Success!
@@ -1239,7 +1239,7 @@
} else {
new_lw = LockWord::FromDefault(lock_word.GCState());
}
- if (!kUseReadBarrier) {
+ if (!gUseReadBarrier) {
DCHECK_EQ(new_lw.ReadBarrierState(), 0U);
// TODO: This really only needs memory_order_release, but we currently have
// no way to specify that. In fact there seem to be no legitimate uses of SetLockWord
@@ -1409,7 +1409,7 @@
{
ObjPtr<mirror::Object> lock_object = thread->GetMonitorEnterObject();
if (lock_object != nullptr) {
- if (kUseReadBarrier && Thread::Current()->GetIsGcMarking()) {
+ if (gUseReadBarrier && Thread::Current()->GetIsGcMarking()) {
// We may call Thread::Dump() in the middle of the CC thread flip and this thread's stack
// may have not been flipped yet and "pretty_object" may be a from-space (stale) ref, in
// which case the GetLockOwnerThreadId() call below will crash. So explicitly mark/forward
@@ -1613,13 +1613,13 @@
}
void MonitorList::DisallowNewMonitors() {
- CHECK(!kUseReadBarrier);
+ CHECK(!gUseReadBarrier);
MutexLock mu(Thread::Current(), monitor_list_lock_);
allow_new_monitors_ = false;
}
void MonitorList::AllowNewMonitors() {
- CHECK(!kUseReadBarrier);
+ CHECK(!gUseReadBarrier);
Thread* self = Thread::Current();
MutexLock mu(self, monitor_list_lock_);
allow_new_monitors_ = true;
@@ -1637,8 +1637,8 @@
MutexLock mu(self, monitor_list_lock_);
// CMS needs this to block for concurrent reference processing because an object allocated during
// the GC won't be marked and concurrent reference processing would incorrectly clear the JNI weak
- // ref. But CC (kUseReadBarrier == true) doesn't because of the to-space invariant.
- while (!kUseReadBarrier && UNLIKELY(!allow_new_monitors_)) {
+ // ref. But CC (gUseReadBarrier == true) doesn't because of the to-space invariant.
+ while (!gUseReadBarrier && UNLIKELY(!allow_new_monitors_)) {
// Check and run the empty checkpoint before blocking so the empty checkpoint will work in the
// presence of threads blocking for weak ref access.
self->CheckEmptyCheckpointFromWeakRefAccess(&monitor_list_lock_);
diff --git a/runtime/monitor_objects_stack_visitor.cc b/runtime/monitor_objects_stack_visitor.cc
index 2e75e37..524c0ec 100644
--- a/runtime/monitor_objects_stack_visitor.cc
+++ b/runtime/monitor_objects_stack_visitor.cc
@@ -90,7 +90,7 @@
void MonitorObjectsStackVisitor::VisitLockedObject(ObjPtr<mirror::Object> o, void* context) {
MonitorObjectsStackVisitor* self = reinterpret_cast<MonitorObjectsStackVisitor*>(context);
if (o != nullptr) {
- if (kUseReadBarrier && Thread::Current()->GetIsGcMarking()) {
+ if (gUseReadBarrier && Thread::Current()->GetIsGcMarking()) {
// We may call Thread::Dump() in the middle of the CC thread flip and this thread's stack
// may have not been flipped yet and "o" may be a from-space (stale) ref, in which case the
// IdentityHashCode call below will crash. So explicitly mark/forward it here.
diff --git a/runtime/monitor_pool.h b/runtime/monitor_pool.h
index e07aa97..133cde0 100644
--- a/runtime/monitor_pool.h
+++ b/runtime/monitor_pool.h
@@ -189,12 +189,12 @@
// Size of a monitor, rounded up to a multiple of alignment.
static constexpr size_t kAlignedMonitorSize = (sizeof(Monitor) + kMonitorAlignment - 1) &
-kMonitorAlignment;
- // As close to a page as we can get seems a good start.
- static constexpr size_t kChunkCapacity = kPageSize / kAlignedMonitorSize;
- // Chunk size that is referenced in the id. We can collapse this to the actually used storage
- // in a chunk, i.e., kChunkCapacity * kAlignedMonitorSize, but this will mean proper divisions.
- static constexpr size_t kChunkSize = kPageSize;
+ // Size of the chunks holding the actual monitors. The bottom bits of the monitor id are the
+ // index into such a chunk. We can collapse this to the actually used storage
+ // in a chunk, i.e., kChunkCapacity * kAlignedMonitorSize, but this would mean proper divisions.
+ static constexpr size_t kChunkSize = 4096;
static_assert(IsPowerOfTwo(kChunkSize), "kChunkSize must be power of 2");
+ static constexpr size_t kChunkCapacity = kChunkSize / kAlignedMonitorSize;
// The number of chunks of storage that can be referenced by the initial chunk list.
// The total number of usable monitor chunks is typically 255 times this number, so it
// should be large enough that we don't run out. We run out of address bits if it's > 512.
@@ -215,7 +215,7 @@
// Array of pointers to lists (again arrays) of pointers to chunks containing monitors.
// Zeroth entry points to a list (array) of kInitialChunkStorage pointers to chunks.
// Each subsequent list as twice as large as the preceding one.
- // Monitor Ids are interpreted as follows:
+ // Monitor Ids are effectively interpreted as follows:
// Top 3 bits (of 28): index into monitor_chunks_.
// Next 16 bits: index into the chunk list, i.e. monitor_chunks_[i].
// Last 9 bits: offset within chunk, expressed as multiple of kMonitorAlignment.
diff --git a/runtime/monitor_test.cc b/runtime/monitor_test.cc
index 66008f3..64986b6 100644
--- a/runtime/monitor_test.cc
+++ b/runtime/monitor_test.cc
@@ -35,12 +35,17 @@
class MonitorTest : public CommonRuntimeTest {
protected:
+ MonitorTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+
void SetUpRuntimeOptions(RuntimeOptions *options) override {
// Use a smaller heap
SetUpRuntimeOptionsForFillHeap(options);
options->push_back(std::make_pair("-Xint", nullptr));
}
+
public:
std::unique_ptr<Monitor> monitor_;
Handle<mirror::String> object_;
diff --git a/runtime/native/dalvik_system_BaseDexClassLoader.cc b/runtime/native/dalvik_system_BaseDexClassLoader.cc
index 607395d..a4f702c 100644
--- a/runtime/native/dalvik_system_BaseDexClassLoader.cc
+++ b/runtime/native/dalvik_system_BaseDexClassLoader.cc
@@ -19,43 +19,54 @@
#include <memory>
#include "class_loader_context.h"
+#include "class_root-inl.h"
+#include "mirror/object_array-alloc-inl.h"
#include "native_util.h"
#include "nativehelper/jni_macros.h"
-#include "well_known_classes.h"
+#include "thread-inl.h"
namespace art {
-static bool append_string(JNIEnv* env, jobjectArray array, uint32_t& i, const std::string& string) {
- ScopedLocalRef<jstring> jstring(env, env->NewStringUTF(string.c_str()));
- if (jstring.get() == nullptr) {
- DCHECK(env->ExceptionCheck());
+static bool append_string(Thread* self,
+ Handle<mirror::ObjectArray<mirror::String>> array,
+ uint32_t& i,
+ const std::string& string) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::String> ostring = mirror::String::AllocFromModifiedUtf8(self, string.c_str());
+ if (ostring == nullptr) {
+ DCHECK(self->IsExceptionPending());
return false;
}
- env->SetObjectArrayElement(array, i++, jstring.get());
+ // We're initializing a newly allocated array object, so we do not need to record that under
+ // a transaction. If the transaction is aborted, the whole object shall be unreachable.
+ array->SetWithoutChecks</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(i, ostring);
+ ++i;
return true;
}
static jobjectArray BaseDexClassLoader_computeClassLoaderContextsNative(JNIEnv* env,
jobject class_loader) {
CHECK(class_loader != nullptr);
- std::map<std::string, std::string> contextMap =
+ std::map<std::string, std::string> context_map =
ClassLoaderContext::EncodeClassPathContextsForClassLoader(class_loader);
- jobjectArray result = env->NewObjectArray(2 * contextMap.size(),
- WellKnownClasses::java_lang_String,
- nullptr);
- if (result == nullptr) {
- DCHECK(env->ExceptionCheck());
+ Thread* self = Thread::ForEnv(env);
+ ScopedObjectAccess soa(self);
+ StackHandleScope<1u> hs(self);
+ Handle<mirror::ObjectArray<mirror::String>> array = hs.NewHandle(
+ mirror::ObjectArray<mirror::String>::Alloc(
+ self, GetClassRoot<mirror::ObjectArray<mirror::String>>(), 2 * context_map.size()));
+ if (array == nullptr) {
+ DCHECK(self->IsExceptionPending());
return nullptr;
}
uint32_t i = 0;
- for (const auto& classpath_to_context : contextMap) {
+ for (const auto& classpath_to_context : context_map) {
const std::string& classpath = classpath_to_context.first;
const std::string& context = classpath_to_context.second;
- if (!append_string(env, result, i, classpath) || !append_string(env, result, i, context)) {
+ if (!append_string(self, array, i, classpath) || !append_string(self, array, i, context)) {
return nullptr;
}
}
- return result;
+ return soa.AddLocalReference<jobjectArray>(array.Get());
}
static JNINativeMethod gMethods[] = {
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index ecb9010..9f0c216 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -28,6 +28,7 @@
#include "base/logging.h"
#include "base/os.h"
#include "base/stl_util.h"
+#include "base/transform_iterator.h"
#include "base/utils.h"
#include "base/zip_archive.h"
#include "class_linker.h"
@@ -53,10 +54,19 @@
#include "oat_file_manager.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
-#include "well_known_classes.h"
+#include "string_array_utils.h"
+#include "thread-current-inl.h"
+
+#ifdef ART_TARGET_ANDROID
+#include <android/api-level.h>
+#include <sys/system_properties.h>
+#endif // ART_TARGET_ANDROID
namespace art {
+// Should be the same as dalvik.system.DexFile.ENFORCE_READ_ONLY_JAVA_DCL
+static constexpr uint64_t kEnforceReadOnlyJavaDcl = 218865702;
+
using android::base::StringPrintf;
static bool ConvertJavaArrayToDexFiles(
@@ -303,6 +313,57 @@
return CreateCookieFromOatFileManagerResult(env, dex_files, oat_file, error_msgs);
}
+#ifdef ART_TARGET_ANDROID
+static bool isReadOnlyJavaDclEnforced(JNIEnv* env) {
+ static bool is_at_least_u = [] {
+ const int api_level = android_get_device_api_level();
+ if (api_level > __ANDROID_API_T__) {
+ return true;
+ } else if (api_level == __ANDROID_API_T__) {
+ // Check if running U preview
+ char value[92] = {0};
+ if (__system_property_get("ro.build.version.preview_sdk", value) >= 0 && atoi(value) > 0) {
+ return true;
+ }
+ }
+ return false;
+ }();
+ if (is_at_least_u) {
+ // The reason why we are calling the AppCompat framework through JVM
+ // instead of directly using the CompatFramework C++ API is because feature
+ // overrides only apply to the Java API.
+ // CtsLibcoreTestCases is part of mainline modules, which requires the same test
+ // to run on older Android versions; the target SDK of CtsLibcoreTestCases is locked
+ // to the lowest supported API level (at the time of writing, it's API 31).
+ // We would need to be able to manually enable the compat change in CTS tests.
+ ScopedLocalRef<jclass> compat(env, env->FindClass("android/compat/Compatibility"));
+ jmethodID mId = env->GetStaticMethodID(compat.get(), "isChangeEnabled", "(J)Z");
+ return env->CallStaticBooleanMethod(compat.get(), mId, kEnforceReadOnlyJavaDcl) == JNI_TRUE;
+ } else {
+ return false;
+ }
+}
+#else // ART_TARGET_ANDROID
+constexpr static bool isReadOnlyJavaDclEnforced(JNIEnv*) {
+ (void)kEnforceReadOnlyJavaDcl;
+ return false;
+}
+#endif // ART_TARGET_ANDROID
+
+static bool isReadOnlyJavaDclChecked() {
+ if (!kIsTargetAndroid) {
+ return false;
+ }
+ const int uid = getuid();
+ // The following UIDs are exempted:
+ // * Root (0): root processes always have write access to files.
+ // * System (1000): /data/app/**.apk are owned by AID_SYSTEM;
+ // loading installed APKs in system_server is allowed.
+ // * Shell (2000): directly calling dalvikvm/app_process in ADB shell
+ // to run JARs with CLI is allowed.
+ return uid != 0 && uid != 1000 && uid != 2000;
+}
+
// TODO(calin): clean up the unused parameters (here and in libcore).
static jobject DexFile_openDexFileNative(JNIEnv* env,
jclass,
@@ -316,6 +377,17 @@
return nullptr;
}
+ if (isReadOnlyJavaDclChecked() && access(sourceName.c_str(), W_OK) == 0) {
+ LOG(ERROR) << "Attempt to load writable dex file: " << sourceName.c_str();
+ if (isReadOnlyJavaDclEnforced(env)) {
+ ScopedLocalRef<jclass> se(env, env->FindClass("java/lang/SecurityException"));
+ std::string message(
+ StringPrintf("Writable dex file '%s' is not allowed.", sourceName.c_str()));
+ env->ThrowNew(se.get(), message.c_str());
+ return nullptr;
+ }
+ }
+
std::vector<std::string> error_msgs;
const OatFile* oat_file = nullptr;
std::vector<std::unique_ptr<const DexFile>> dex_files =
@@ -482,23 +554,12 @@
}
// Now create output array and copy the set into it.
- jobjectArray result = env->NewObjectArray(descriptors.size(),
- WellKnownClasses::java_lang_String,
- nullptr);
- if (result != nullptr) {
- auto it = descriptors.begin();
- auto it_end = descriptors.end();
- jsize i = 0;
- for (; it != it_end; it++, ++i) {
- std::string descriptor(DescriptorToDot(*it));
- ScopedLocalRef<jstring> jdescriptor(env, env->NewStringUTF(descriptor.c_str()));
- if (jdescriptor.get() == nullptr) {
- return nullptr;
- }
- env->SetObjectArrayElement(result, i, jdescriptor.get());
- }
- }
- return result;
+ ScopedObjectAccess soa(Thread::ForEnv(env));
+ auto descriptor_to_dot = [](const char* descriptor) { return DescriptorToDot(descriptor); };
+ return soa.AddLocalReference<jobjectArray>(CreateStringArray(
+ soa.Self(),
+ descriptors.size(),
+ MakeTransformRange(descriptors, descriptor_to_dot)));
}
static jint GetDexOptNeeded(JNIEnv* env,
@@ -590,6 +651,8 @@
return nullptr;
}
+ // The API doesn't support passing a class loader context, so skip the class loader context check
+ // and assume that it's OK.
OatFileAssistant oat_file_assistant(filename.c_str(),
target_instruction_set,
/* context= */ nullptr,
@@ -627,23 +690,11 @@
OatFileAssistant::GetOptimizationStatus(
filename.c_str(), target_instruction_set, &compilation_filter, &compilation_reason);
- ScopedLocalRef<jstring> j_compilation_filter(env, env->NewStringUTF(compilation_filter.c_str()));
- if (j_compilation_filter.get() == nullptr) {
- return nullptr;
- }
- ScopedLocalRef<jstring> j_compilation_reason(env, env->NewStringUTF(compilation_reason.c_str()));
- if (j_compilation_reason.get() == nullptr) {
- return nullptr;
- }
-
- // Now create output array and copy the set into it.
- jobjectArray result = env->NewObjectArray(2,
- WellKnownClasses::java_lang_String,
- nullptr);
- env->SetObjectArrayElement(result, 0, j_compilation_filter.get());
- env->SetObjectArrayElement(result, 1, j_compilation_reason.get());
-
- return result;
+ ScopedObjectAccess soa(Thread::ForEnv(env));
+ return soa.AddLocalReference<jobjectArray>(CreateStringArray(soa.Self(), {
+ compilation_filter.c_str(),
+ compilation_reason.c_str()
+ }));
}
static jint DexFile_getDexOptNeeded(JNIEnv* env,
@@ -734,6 +785,41 @@
return CompilerFilter::DependsOnProfile(filter) ? JNI_TRUE : JNI_FALSE;
}
+static jboolean DexFile_isVerifiedCompilerFilter(JNIEnv* env,
+ jclass javeDexFileClass ATTRIBUTE_UNUSED,
+ jstring javaCompilerFilter) {
+ ScopedUtfChars compiler_filter(env, javaCompilerFilter);
+ if (env->ExceptionCheck()) {
+ return -1;
+ }
+
+ CompilerFilter::Filter filter;
+ if (!CompilerFilter::ParseCompilerFilter(compiler_filter.c_str(), &filter)) {
+ return JNI_FALSE;
+ }
+ return CompilerFilter::IsVerificationEnabled(filter) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean DexFile_isOptimizedCompilerFilter(JNIEnv* env,
+ jclass javeDexFileClass ATTRIBUTE_UNUSED,
+ jstring javaCompilerFilter) {
+ ScopedUtfChars compiler_filter(env, javaCompilerFilter);
+ if (env->ExceptionCheck()) {
+ return -1;
+ }
+
+ CompilerFilter::Filter filter;
+ if (!CompilerFilter::ParseCompilerFilter(compiler_filter.c_str(), &filter)) {
+ return JNI_FALSE;
+ }
+ return CompilerFilter::IsAotCompilationEnabled(filter) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean DexFile_isReadOnlyJavaDclEnforced(JNIEnv* env,
+ jclass javeDexFileClass ATTRIBUTE_UNUSED) {
+ return (isReadOnlyJavaDclChecked() && isReadOnlyJavaDclEnforced(env)) ? JNI_TRUE : JNI_FALSE;
+}
+
static jstring DexFile_getNonProfileGuidedCompilerFilter(JNIEnv* env,
jclass javeDexFileClass ATTRIBUTE_UNUSED,
jstring javaCompilerFilter) {
@@ -856,31 +942,16 @@
oat_filename = best_oat_file->GetLocation();
is_vdex_only = best_oat_file->IsBackedByVdexOnly();
}
- ScopedLocalRef<jstring> joatFilename(env, env->NewStringUTF(oat_filename.c_str()));
- if (joatFilename.get() == nullptr) {
- return nullptr;
- }
- if (is_vdex_only) {
- jobjectArray result = env->NewObjectArray(1,
- WellKnownClasses::java_lang_String,
- nullptr);
- env->SetObjectArrayElement(result, 0, joatFilename.get());
- return result;
- } else {
+ const char* filenames[] = { oat_filename.c_str(), nullptr };
+ ArrayRef<const char* const> used_filenames(filenames, 1u);
+ if (!is_vdex_only) {
vdex_filename = GetVdexFilename(oat_filename);
- ScopedLocalRef<jstring> jvdexFilename(env, env->NewStringUTF(vdex_filename.c_str()));
- if (jvdexFilename.get() == nullptr) {
- return nullptr;
- }
-
- jobjectArray result = env->NewObjectArray(2,
- WellKnownClasses::java_lang_String,
- nullptr);
- env->SetObjectArrayElement(result, 0, jvdexFilename.get());
- env->SetObjectArrayElement(result, 1, joatFilename.get());
- return result;
+ filenames[1] = vdex_filename.c_str();
+ used_filenames = ArrayRef<const char* const>(filenames, 2u);
}
+ ScopedObjectAccess soa(Thread::ForEnv(env));
+ return soa.AddLocalReference<jobjectArray>(CreateStringArray(soa.Self(), used_filenames));
}
static jlong DexFile_getStaticSizeOfDexFile(JNIEnv* env, jclass, jobject cookie) {
@@ -905,7 +976,7 @@
ScopedObjectAccess soa(env);
// Currently only allow this for debuggable apps.
- if (!runtime->IsJavaDebuggable()) {
+ if (!runtime->IsJavaDebuggableAtInit()) {
ThrowSecurityException("Can't exempt class, process is not debuggable.");
return;
}
@@ -924,55 +995,60 @@
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)Z"),
- NATIVE_METHOD(DexFile,
- defineClassNative,
- "(Ljava/lang/String;"
- "Ljava/lang/ClassLoader;"
- "Ljava/lang/Object;"
- "Ldalvik/system/DexFile;"
- ")Ljava/lang/Class;"),
- NATIVE_METHOD(DexFile, getClassNameList, "(Ljava/lang/Object;)[Ljava/lang/String;"),
- NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"),
- NATIVE_METHOD(DexFile, getDexOptNeeded,
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZ)I"),
- NATIVE_METHOD(DexFile, openDexFileNative,
- "(Ljava/lang/String;"
- "Ljava/lang/String;"
- "I"
- "Ljava/lang/ClassLoader;"
- "[Ldalvik/system/DexPathList$Element;"
- ")Ljava/lang/Object;"),
- NATIVE_METHOD(DexFile, openInMemoryDexFilesNative,
- "([Ljava/nio/ByteBuffer;"
- "[[B"
- "[I"
- "[I"
- "Ljava/lang/ClassLoader;"
- "[Ldalvik/system/DexPathList$Element;"
- ")Ljava/lang/Object;"),
- NATIVE_METHOD(DexFile, verifyInBackgroundNative,
- "(Ljava/lang/Object;"
- "Ljava/lang/ClassLoader;"
- ")V"),
- NATIVE_METHOD(DexFile, isValidCompilerFilter, "(Ljava/lang/String;)Z"),
- NATIVE_METHOD(DexFile, isProfileGuidedCompilerFilter, "(Ljava/lang/String;)Z"),
- NATIVE_METHOD(DexFile,
- getNonProfileGuidedCompilerFilter,
- "(Ljava/lang/String;)Ljava/lang/String;"),
- NATIVE_METHOD(DexFile,
- getSafeModeCompilerFilter,
- "(Ljava/lang/String;)Ljava/lang/String;"),
- NATIVE_METHOD(DexFile, isBackedByOatFile, "(Ljava/lang/Object;)Z"),
- NATIVE_METHOD(DexFile, getDexFileStatus,
- "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
- NATIVE_METHOD(DexFile, getDexFileOutputPaths,
- "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"),
- NATIVE_METHOD(DexFile, getStaticSizeOfDexFile, "(Ljava/lang/Object;)J"),
- NATIVE_METHOD(DexFile, getDexFileOptimizationStatus,
- "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"),
- NATIVE_METHOD(DexFile, setTrusted, "(Ljava/lang/Object;)V")
-};
+ NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)Z"),
+ NATIVE_METHOD(DexFile,
+ defineClassNative,
+ "(Ljava/lang/String;"
+ "Ljava/lang/ClassLoader;"
+ "Ljava/lang/Object;"
+ "Ldalvik/system/DexFile;"
+ ")Ljava/lang/Class;"),
+ NATIVE_METHOD(DexFile, getClassNameList, "(Ljava/lang/Object;)[Ljava/lang/String;"),
+ NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"),
+ NATIVE_METHOD(DexFile,
+ getDexOptNeeded,
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZ)I"),
+ NATIVE_METHOD(DexFile,
+ openDexFileNative,
+ "(Ljava/lang/String;"
+ "Ljava/lang/String;"
+ "I"
+ "Ljava/lang/ClassLoader;"
+ "[Ldalvik/system/DexPathList$Element;"
+ ")Ljava/lang/Object;"),
+ NATIVE_METHOD(DexFile,
+ openInMemoryDexFilesNative,
+ "([Ljava/nio/ByteBuffer;"
+ "[[B"
+ "[I"
+ "[I"
+ "Ljava/lang/ClassLoader;"
+ "[Ldalvik/system/DexPathList$Element;"
+ ")Ljava/lang/Object;"),
+ NATIVE_METHOD(DexFile,
+ verifyInBackgroundNative,
+ "(Ljava/lang/Object;"
+ "Ljava/lang/ClassLoader;"
+ ")V"),
+ NATIVE_METHOD(DexFile, isValidCompilerFilter, "(Ljava/lang/String;)Z"),
+ NATIVE_METHOD(DexFile, isProfileGuidedCompilerFilter, "(Ljava/lang/String;)Z"),
+ NATIVE_METHOD(DexFile, isVerifiedCompilerFilter, "(Ljava/lang/String;)Z"),
+ NATIVE_METHOD(DexFile, isOptimizedCompilerFilter, "(Ljava/lang/String;)Z"),
+ NATIVE_METHOD(DexFile, isReadOnlyJavaDclEnforced, "()Z"),
+ NATIVE_METHOD(
+ DexFile, getNonProfileGuidedCompilerFilter, "(Ljava/lang/String;)Ljava/lang/String;"),
+ NATIVE_METHOD(DexFile, getSafeModeCompilerFilter, "(Ljava/lang/String;)Ljava/lang/String;"),
+ NATIVE_METHOD(DexFile, isBackedByOatFile, "(Ljava/lang/Object;)Z"),
+ NATIVE_METHOD(
+ DexFile, getDexFileStatus, "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
+ NATIVE_METHOD(DexFile,
+ getDexFileOutputPaths,
+ "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"),
+ NATIVE_METHOD(DexFile, getStaticSizeOfDexFile, "(Ljava/lang/Object;)J"),
+ NATIVE_METHOD(DexFile,
+ getDexFileOptimizationStatus,
+ "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"),
+ NATIVE_METHOD(DexFile, setTrusted, "(Ljava/lang/Object;)V")};
void register_dalvik_system_DexFile(JNIEnv* env) {
REGISTER_NATIVE_METHODS("dalvik/system/DexFile");
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index 2e09c9f..3653a83 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -27,6 +27,7 @@
#include "base/histogram-inl.h"
#include "base/time_utils.h"
#include "class_linker.h"
+#include "class_root-inl.h"
#include "common_throws.h"
#include "debugger.h"
#include "gc/space/bump_pointer_space.h"
@@ -41,37 +42,26 @@
#include "mirror/array-alloc-inl.h"
#include "mirror/array-inl.h"
#include "mirror/class.h"
-#include "mirror/object_array-inl.h"
+#include "mirror/object_array-alloc-inl.h"
#include "native_util.h"
#include "nativehelper/scoped_local_ref.h"
#include "nativehelper/scoped_utf_chars.h"
#include "scoped_fast_native_object_access-inl.h"
+#include "string_array_utils.h"
+#include "thread-inl.h"
#include "trace.h"
-#include "well_known_classes.h"
namespace art {
static jobjectArray VMDebug_getVmFeatureList(JNIEnv* env, jclass) {
- static const char* features[] = {
- "method-trace-profiling",
- "method-trace-profiling-streaming",
- "method-sample-profiling",
- "hprof-heap-dump",
- "hprof-heap-dump-streaming",
- };
- jobjectArray result = env->NewObjectArray(arraysize(features),
- WellKnownClasses::java_lang_String,
- nullptr);
- if (result != nullptr) {
- for (size_t i = 0; i < arraysize(features); ++i) {
- ScopedLocalRef<jstring> jfeature(env, env->NewStringUTF(features[i]));
- if (jfeature.get() == nullptr) {
- return nullptr;
- }
- env->SetObjectArrayElement(result, i, jfeature.get());
- }
- }
- return result;
+ ScopedObjectAccess soa(Thread::ForEnv(env));
+ return soa.AddLocalReference<jobjectArray>(CreateStringArray(soa.Self(), {
+ "method-trace-profiling",
+ "method-trace-profiling-streaming",
+ "method-sample-profiling",
+ "hprof-heap-dump",
+ "hprof-heap-dump-streaming",
+ }));
}
static void VMDebug_startAllocCounting(JNIEnv*, jclass) {
@@ -178,6 +168,13 @@
return -1;
}
+static void VMDebug_suspendAllAndSendVmStart(JNIEnv*, jclass)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ // This function will be replaced by the debugger when it's connected. See
+ // external/oj-libjdwp/src/share/vmDebug.c for implementation when debugger is connected.
+ ThrowRuntimeException("ART's suspendAllAndSendVmStart is not implemented");
+}
+
static void VMDebug_printLoadedClasses(JNIEnv* env, jclass, jint flags) {
class DumpClassVisitor : public ClassVisitor {
public:
@@ -373,55 +370,77 @@
}
}
-static bool SetRuntimeStatValue(JNIEnv* env,
- jobjectArray result,
+static bool SetRuntimeStatValue(Thread* self,
+ Handle<mirror::ObjectArray<mirror::String>> array,
VMDebugRuntimeStatId id,
- const std::string& value) {
- ScopedLocalRef<jstring> jvalue(env, env->NewStringUTF(value.c_str()));
- if (jvalue.get() == nullptr) {
+ const std::string& value) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::String> ovalue = mirror::String::AllocFromModifiedUtf8(self, value.c_str());
+ if (ovalue == nullptr) {
+ DCHECK(self->IsExceptionPending());
return false;
}
- env->SetObjectArrayElement(result, static_cast<jint>(id), jvalue.get());
+ // We're initializing a newly allocated array object, so we do not need to record that under
+ // a transaction. If the transaction is aborted, the whole object shall be unreachable.
+ array->SetWithoutChecks</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(
+ static_cast<int32_t>(id), ovalue);
return true;
}
static jobjectArray VMDebug_getRuntimeStatsInternal(JNIEnv* env, jclass) {
- jobjectArray result = env->NewObjectArray(
- static_cast<jint>(VMDebugRuntimeStatId::kNumRuntimeStats),
- WellKnownClasses::java_lang_String,
- nullptr);
- if (result == nullptr) {
+ Thread* self = Thread::ForEnv(env);
+ ScopedObjectAccess soa(self);
+ StackHandleScope<1u> hs(self);
+ int32_t size = enum_cast<int32_t>(VMDebugRuntimeStatId::kNumRuntimeStats);
+ Handle<mirror::ObjectArray<mirror::String>> array = hs.NewHandle(
+ mirror::ObjectArray<mirror::String>::Alloc(
+ self, GetClassRoot<mirror::ObjectArray<mirror::String>>(), size));
+ if (array == nullptr) {
+ DCHECK(self->IsExceptionPending());
return nullptr;
}
gc::Heap* heap = Runtime::Current()->GetHeap();
- if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcGcCount,
+ if (!SetRuntimeStatValue(self,
+ array,
+ VMDebugRuntimeStatId::kArtGcGcCount,
std::to_string(heap->GetGcCount()))) {
return nullptr;
}
- if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcGcTime,
+ if (!SetRuntimeStatValue(self,
+ array,
+ VMDebugRuntimeStatId::kArtGcGcTime,
std::to_string(NsToMs(heap->GetGcTime())))) {
return nullptr;
}
- if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBytesAllocated,
+ if (!SetRuntimeStatValue(self,
+ array,
+ VMDebugRuntimeStatId::kArtGcBytesAllocated,
std::to_string(heap->GetBytesAllocatedEver()))) {
return nullptr;
}
- if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBytesFreed,
+ if (!SetRuntimeStatValue(self,
+ array,
+ VMDebugRuntimeStatId::kArtGcBytesFreed,
std::to_string(heap->GetBytesFreedEver()))) {
return nullptr;
}
- if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBlockingGcCount,
+ if (!SetRuntimeStatValue(self,
+ array,
+ VMDebugRuntimeStatId::kArtGcBlockingGcCount,
std::to_string(heap->GetBlockingGcCount()))) {
return nullptr;
}
- if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBlockingGcTime,
+ if (!SetRuntimeStatValue(self,
+ array,
+ VMDebugRuntimeStatId::kArtGcBlockingGcTime,
std::to_string(NsToMs(heap->GetBlockingGcTime())))) {
return nullptr;
}
{
std::ostringstream output;
heap->DumpGcCountRateHistogram(output);
- if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcGcCountRateHistogram,
+ if (!SetRuntimeStatValue(self,
+ array,
+ VMDebugRuntimeStatId::kArtGcGcCountRateHistogram,
output.str())) {
return nullptr;
}
@@ -429,12 +448,14 @@
{
std::ostringstream output;
heap->DumpBlockingGcCountRateHistogram(output);
- if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBlockingGcCountRateHistogram,
+ if (!SetRuntimeStatValue(self,
+ array,
+ VMDebugRuntimeStatId::kArtGcBlockingGcCountRateHistogram,
output.str())) {
return nullptr;
}
}
- return result;
+ return soa.AddLocalReference<jobjectArray>(array.Get());
}
static void VMDebug_nativeAttachAgent(JNIEnv* env, jclass, jstring agent, jobject classloader) {
@@ -466,7 +487,7 @@
Runtime* runtime = Runtime::Current();
ScopedObjectAccess soa(env);
- if (!runtime->IsJavaDebuggable()) {
+ if (!runtime->IsJavaDebuggableAtInit()) {
ThrowSecurityException("Can't exempt class, process is not debuggable.");
return;
}
@@ -495,32 +516,33 @@
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"),
- NATIVE_METHOD(VMDebug, countInstancesOfClasses, "([Ljava/lang/Class;Z)[J"),
- NATIVE_METHOD(VMDebug, dumpHprofData, "(Ljava/lang/String;I)V"),
- NATIVE_METHOD(VMDebug, dumpHprofDataDdms, "()V"),
- NATIVE_METHOD(VMDebug, dumpReferenceTables, "()V"),
- NATIVE_METHOD(VMDebug, getAllocCount, "(I)I"),
- FAST_NATIVE_METHOD(VMDebug, getLoadedClassCount, "()I"),
- NATIVE_METHOD(VMDebug, getVmFeatureList, "()[Ljava/lang/String;"),
- FAST_NATIVE_METHOD(VMDebug, isDebuggerConnected, "()Z"),
- FAST_NATIVE_METHOD(VMDebug, isDebuggingEnabled, "()Z"),
- NATIVE_METHOD(VMDebug, getMethodTracingMode, "()I"),
- FAST_NATIVE_METHOD(VMDebug, lastDebuggerActivity, "()J"),
- FAST_NATIVE_METHOD(VMDebug, printLoadedClasses, "(I)V"),
- NATIVE_METHOD(VMDebug, resetAllocCount, "(I)V"),
- NATIVE_METHOD(VMDebug, startAllocCounting, "()V"),
- NATIVE_METHOD(VMDebug, startMethodTracingDdmsImpl, "(IIZI)V"),
- NATIVE_METHOD(VMDebug, startMethodTracingFd, "(Ljava/lang/String;IIIZIZ)V"),
- NATIVE_METHOD(VMDebug, startMethodTracingFilename, "(Ljava/lang/String;IIZI)V"),
- NATIVE_METHOD(VMDebug, stopAllocCounting, "()V"),
- NATIVE_METHOD(VMDebug, stopMethodTracing, "()V"),
- FAST_NATIVE_METHOD(VMDebug, threadCpuTimeNanos, "()J"),
- NATIVE_METHOD(VMDebug, getRuntimeStatInternal, "(I)Ljava/lang/String;"),
- NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;"),
- NATIVE_METHOD(VMDebug, nativeAttachAgent, "(Ljava/lang/String;Ljava/lang/ClassLoader;)V"),
- NATIVE_METHOD(VMDebug, allowHiddenApiReflectionFrom, "(Ljava/lang/Class;)V"),
- NATIVE_METHOD(VMDebug, setAllocTrackerStackDepth, "(I)V"),
+ NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"),
+ NATIVE_METHOD(VMDebug, countInstancesOfClasses, "([Ljava/lang/Class;Z)[J"),
+ NATIVE_METHOD(VMDebug, dumpHprofData, "(Ljava/lang/String;I)V"),
+ NATIVE_METHOD(VMDebug, dumpHprofDataDdms, "()V"),
+ NATIVE_METHOD(VMDebug, dumpReferenceTables, "()V"),
+ NATIVE_METHOD(VMDebug, getAllocCount, "(I)I"),
+ FAST_NATIVE_METHOD(VMDebug, getLoadedClassCount, "()I"),
+ NATIVE_METHOD(VMDebug, getVmFeatureList, "()[Ljava/lang/String;"),
+ FAST_NATIVE_METHOD(VMDebug, isDebuggerConnected, "()Z"),
+ FAST_NATIVE_METHOD(VMDebug, isDebuggingEnabled, "()Z"),
+ NATIVE_METHOD(VMDebug, suspendAllAndSendVmStart, "()V"),
+ NATIVE_METHOD(VMDebug, getMethodTracingMode, "()I"),
+ FAST_NATIVE_METHOD(VMDebug, lastDebuggerActivity, "()J"),
+ FAST_NATIVE_METHOD(VMDebug, printLoadedClasses, "(I)V"),
+ NATIVE_METHOD(VMDebug, resetAllocCount, "(I)V"),
+ NATIVE_METHOD(VMDebug, startAllocCounting, "()V"),
+ NATIVE_METHOD(VMDebug, startMethodTracingDdmsImpl, "(IIZI)V"),
+ NATIVE_METHOD(VMDebug, startMethodTracingFd, "(Ljava/lang/String;IIIZIZ)V"),
+ NATIVE_METHOD(VMDebug, startMethodTracingFilename, "(Ljava/lang/String;IIZI)V"),
+ NATIVE_METHOD(VMDebug, stopAllocCounting, "()V"),
+ NATIVE_METHOD(VMDebug, stopMethodTracing, "()V"),
+ FAST_NATIVE_METHOD(VMDebug, threadCpuTimeNanos, "()J"),
+ NATIVE_METHOD(VMDebug, getRuntimeStatInternal, "(I)Ljava/lang/String;"),
+ NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;"),
+ NATIVE_METHOD(VMDebug, nativeAttachAgent, "(Ljava/lang/String;Ljava/lang/ClassLoader;)V"),
+ NATIVE_METHOD(VMDebug, allowHiddenApiReflectionFrom, "(Ljava/lang/Class;)V"),
+ NATIVE_METHOD(VMDebug, setAllocTrackerStackDepth, "(I)V"),
};
void register_dalvik_system_VMDebug(JNIEnv* env) {
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index db5d420..74202fc 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -29,6 +29,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include "android-base/properties.h"
#include "arch/instruction_set.h"
#include "art_method-inl.h"
#include "base/enums.h"
@@ -41,7 +42,7 @@
#include "dex/dex_file-inl.h"
#include "dex/dex_file_types.h"
#include "gc/accounting/card_table-inl.h"
-#include "gc/allocator/dlmalloc.h"
+#include "gc/allocator/art-dlmalloc.h"
#include "gc/heap.h"
#include "gc/space/dlmalloc_space.h"
#include "gc/space/image_space.h"
@@ -60,9 +61,9 @@
#include "runtime.h"
#include "scoped_fast_native_object_access-inl.h"
#include "scoped_thread_state_change-inl.h"
-#include "thread.h"
+#include "string_array_utils.h"
+#include "thread-inl.h"
#include "thread_list.h"
-#include "well_known_classes.h"
namespace art {
@@ -189,27 +190,9 @@
}
static jobjectArray VMRuntime_properties(JNIEnv* env, jobject) {
- DCHECK(WellKnownClasses::java_lang_String != nullptr);
-
const std::vector<std::string>& properties = Runtime::Current()->GetProperties();
- ScopedLocalRef<jobjectArray> ret(env,
- env->NewObjectArray(static_cast<jsize>(properties.size()),
- WellKnownClasses::java_lang_String,
- nullptr /* initial element */));
- if (ret == nullptr) {
- DCHECK(env->ExceptionCheck());
- return nullptr;
- }
- for (size_t i = 0; i != properties.size(); ++i) {
- ScopedLocalRef<jstring> str(env, env->NewStringUTF(properties[i].c_str()));
- if (str == nullptr) {
- DCHECK(env->ExceptionCheck());
- return nullptr;
- }
- env->SetObjectArrayElement(ret.get(), static_cast<jsize>(i), str.get());
- DCHECK(!env->ExceptionCheck());
- }
- return ret.release();
+ ScopedObjectAccess soa(Thread::ForEnv(env));
+ return soa.AddLocalReference<jobjectArray>(CreateStringArray(soa.Self(), properties));
}
// This is for backward compatibility with dalvik which returned the
@@ -253,6 +236,13 @@
return down_cast<JNIEnvExt*>(env)->GetVm()->IsCheckJniEnabled() ? JNI_TRUE : JNI_FALSE;
}
+static jint VMRuntime_getSdkVersionNative(JNIEnv* env ATTRIBUTE_UNUSED,
+ jclass klass ATTRIBUTE_UNUSED,
+ jint default_sdk_version) {
+ return android::base::GetIntProperty("ro.build.version.sdk",
+ default_sdk_version);
+}
+
static void VMRuntime_setTargetSdkVersionNative(JNIEnv*, jobject, jint target_sdk_version) {
// This is the target SDK version of the app we're about to run. It is intended that this a place
// where workarounds can be enabled.
@@ -337,31 +327,31 @@
}
static void VMRuntime_trimHeap(JNIEnv* env, jobject) {
- Runtime::Current()->GetHeap()->Trim(ThreadForEnv(env));
+ Runtime::Current()->GetHeap()->Trim(Thread::ForEnv(env));
}
static void VMRuntime_requestHeapTrim(JNIEnv* env, jobject) {
- Runtime::Current()->GetHeap()->RequestTrim(ThreadForEnv(env));
+ Runtime::Current()->GetHeap()->RequestTrim(Thread::ForEnv(env));
}
static void VMRuntime_requestConcurrentGC(JNIEnv* env, jobject) {
gc::Heap *heap = Runtime::Current()->GetHeap();
- heap->RequestConcurrentGC(ThreadForEnv(env),
+ heap->RequestConcurrentGC(Thread::ForEnv(env),
gc::kGcCauseBackground,
true,
heap->GetCurrentGcNum());
}
static void VMRuntime_startHeapTaskProcessor(JNIEnv* env, jobject) {
- Runtime::Current()->GetHeap()->GetTaskProcessor()->Start(ThreadForEnv(env));
+ Runtime::Current()->GetHeap()->GetTaskProcessor()->Start(Thread::ForEnv(env));
}
static void VMRuntime_stopHeapTaskProcessor(JNIEnv* env, jobject) {
- Runtime::Current()->GetHeap()->GetTaskProcessor()->Stop(ThreadForEnv(env));
+ Runtime::Current()->GetHeap()->GetTaskProcessor()->Stop(Thread::ForEnv(env));
}
static void VMRuntime_runHeapTasks(JNIEnv* env, jobject) {
- Runtime::Current()->GetHeap()->GetTaskProcessor()->RunAllTasks(ThreadForEnv(env));
+ Runtime::Current()->GetHeap()->GetTaskProcessor()->RunAllTasks(Thread::ForEnv(env));
}
static void VMRuntime_preloadDexCaches(JNIEnv* env ATTRIBUTE_UNUSED, jobject) {
@@ -509,6 +499,41 @@
return ClassLoaderContext::IsValidEncoding(encoded_class_loader_context.c_str());
}
+static jobject VMRuntime_getBaseApkOptimizationInfo(JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
+ AppInfo* app_info = Runtime::Current()->GetAppInfo();
+ DCHECK(app_info != nullptr);
+
+ std::string compiler_filter;
+ std::string compilation_reason;
+ app_info->GetPrimaryApkOptimizationStatus(&compiler_filter, &compilation_reason);
+
+ ScopedLocalRef<jclass> cls(env, env->FindClass("dalvik/system/DexFile$OptimizationInfo"));
+ if (cls == nullptr) {
+ DCHECK(env->ExceptionCheck());
+ return nullptr;
+ }
+
+ jmethodID ctor = env->GetMethodID(cls.get(), "<init>", "(Ljava/lang/String;Ljava/lang/String;)V");
+ if (ctor == nullptr) {
+ DCHECK(env->ExceptionCheck());
+ return nullptr;
+ }
+
+ ScopedLocalRef<jstring> j_compiler_filter(env, env->NewStringUTF(compiler_filter.c_str()));
+ if (j_compiler_filter == nullptr) {
+ DCHECK(env->ExceptionCheck());
+ return nullptr;
+ }
+
+ ScopedLocalRef<jstring> j_compilation_reason(env, env->NewStringUTF(compilation_reason.c_str()));
+ if (j_compilation_reason == nullptr) {
+ DCHECK(env->ExceptionCheck());
+ return nullptr;
+ }
+
+ return env->NewObject(cls.get(), ctor, j_compiler_filter.get(), j_compilation_reason.get());
+}
+
static JNINativeMethod gMethods[] = {
FAST_NATIVE_METHOD(VMRuntime, addressOf, "(Ljava/lang/Object;)J"),
NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"),
@@ -524,6 +549,7 @@
FAST_NATIVE_METHOD(VMRuntime, newNonMovableArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"),
FAST_NATIVE_METHOD(VMRuntime, newUnpaddedArray, "(Ljava/lang/Class;I)Ljava/lang/Object;"),
NATIVE_METHOD(VMRuntime, properties, "()[Ljava/lang/String;"),
+ NATIVE_METHOD(VMRuntime, getSdkVersionNative, "(I)I"),
NATIVE_METHOD(VMRuntime, setTargetSdkVersionNative, "(I)V"),
NATIVE_METHOD(VMRuntime, setDisabledCompatChangesNative, "([J)V"),
NATIVE_METHOD(VMRuntime, registerNativeAllocation, "(J)V"),
@@ -557,6 +583,8 @@
NATIVE_METHOD(VMRuntime, bootCompleted, "()V"),
NATIVE_METHOD(VMRuntime, resetJitCounters, "()V"),
NATIVE_METHOD(VMRuntime, isValidClassLoaderContext, "(Ljava/lang/String;)Z"),
+ NATIVE_METHOD(VMRuntime, getBaseApkOptimizationInfo,
+ "()Ldalvik/system/DexFile$OptimizationInfo;"),
};
void register_dalvik_system_VMRuntime(JNIEnv* env) {
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index eae7c20..f7d7d80 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -205,7 +205,7 @@
if ((runtime_flags & DEBUG_JAVA_DEBUGGABLE) != 0) {
runtime->AddCompilerOption("--debuggable");
runtime_flags |= DEBUG_GENERATE_MINI_DEBUG_INFO;
- runtime->SetJavaDebuggable(true);
+ runtime->SetRuntimeDebugState(Runtime::RuntimeDebugState::kJavaDebuggableAtInit);
{
// Deoptimize the boot image as it may be non-debuggable.
ScopedSuspendAll ssa(__FUNCTION__);
@@ -257,7 +257,7 @@
runtime->PreZygoteFork();
// Grab thread before fork potentially makes Thread::pthread_key_self_ unusable.
- return reinterpret_cast<jlong>(ThreadForEnv(env));
+ return reinterpret_cast<jlong>(Thread::ForEnv(env));
}
static void ZygoteHooks_nativePostZygoteFork(JNIEnv*, jclass) {
@@ -382,7 +382,8 @@
proc_name = StringPrintf("%u", static_cast<uint32_t>(pid));
}
- std::string trace_file = StringPrintf("/data/misc/trace/%s.trace.bin", proc_name.c_str());
+ const char* path = kIsTargetBuild ? "/data/misc/trace" : "/tmp";
+ std::string trace_file = StringPrintf("%s/%s.trace.bin", path, proc_name.c_str());
Trace::Start(trace_file.c_str(),
buffer_size,
0, // TODO: Expose flags.
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index da42e61..c5fdda7 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -19,8 +19,9 @@
#include <iostream>
#include "art_field-inl.h"
-#include "art_method-inl.h"
+#include "art_method-alloc-inl.h"
#include "base/enums.h"
+#include "base/sdk_version.h"
#include "class_linker-inl.h"
#include "class_root-inl.h"
#include "common_throws.h"
@@ -32,6 +33,7 @@
#include "hidden_api.h"
#include "jni/jni_internal.h"
#include "mirror/class-alloc-inl.h"
+#include "mirror/class_ext.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "mirror/field.h"
@@ -43,6 +45,7 @@
#include "mirror/proxy.h"
#include "mirror/string-alloc-inl.h"
#include "mirror/string-inl.h"
+#include "mirror/string.h"
#include "native_util.h"
#include "nativehelper/jni_macros.h"
#include "nativehelper/scoped_local_ref.h"
@@ -53,7 +56,7 @@
#include "reflective_handle_scope-inl.h"
#include "scoped_fast_native_object_access-inl.h"
#include "scoped_thread_state_change-inl.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -89,14 +92,17 @@
static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean initialize,
jobject javaLoader) {
ScopedFastNativeObjectAccess soa(env);
- ScopedUtfChars name(env, javaName);
- if (name.c_str() == nullptr) {
+ StackHandleScope<3> hs(soa.Self());
+ Handle<mirror::String> mirror_name = hs.NewHandle(soa.Decode<mirror::String>(javaName));
+ if (mirror_name == nullptr) {
+ soa.Self()->ThrowNewWrappedException("Ljava/lang/NullPointerException;", /*msg=*/ nullptr);
return nullptr;
}
// We need to validate and convert the name (from x.y.z to x/y/z). This
// is especially handy for array types, since we want to avoid
// auto-generating bogus array classes.
+ std::string name = mirror_name->ToModifiedUtf8();
if (!IsValidBinaryClassName(name.c_str())) {
soa.Self()->ThrowNewExceptionF("Ljava/lang/ClassNotFoundException;",
"Invalid name: %s", name.c_str());
@@ -104,29 +110,46 @@
}
std::string descriptor(DotToDescriptor(name.c_str()));
- StackHandleScope<2> hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(
hs.NewHandle(soa.Decode<mirror::ClassLoader>(javaLoader)));
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
Handle<mirror::Class> c(
hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader)));
- if (c == nullptr) {
- ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred());
- env->ExceptionClear();
- jthrowable cnfe = reinterpret_cast<jthrowable>(
- env->NewObject(WellKnownClasses::java_lang_ClassNotFoundException,
- WellKnownClasses::java_lang_ClassNotFoundException_init,
- javaName,
- cause.get()));
+ if (UNLIKELY(c == nullptr)) {
+ StackHandleScope<2> hs2(soa.Self());
+ Handle<mirror::Object> cause = hs2.NewHandle(soa.Self()->GetException());
+ soa.Self()->ClearException();
+ Handle<mirror::Object> cnfe =
+ WellKnownClasses::java_lang_ClassNotFoundException_init->NewObject<'L', 'L'>(
+ hs2, soa.Self(), mirror_name, cause);
if (cnfe != nullptr) {
// Make sure allocation didn't fail with an OOME.
- env->Throw(cnfe);
+ soa.Self()->SetException(ObjPtr<mirror::Throwable>::DownCast(cnfe.Get()));
}
return nullptr;
}
if (initialize) {
class_linker->EnsureInitialized(soa.Self(), c, true, true);
}
+
+ // java.lang.ClassValue was added in Android U, and proguarding tools
+ // used that as justification to remove computeValue method implementation.
+ // Usual pattern was to check that Class.forName("java.lang.ClassValue")
+ // call does not throw and use ClassValue-based implementation or fallback
+ // to other solution if it does throw.
+ // So far ClassValue is the only class with such a problem and hence this
+ // ad-hoc check.
+ // See b/259501764.
+ uint32_t targetSdkVersion = Runtime::Current()->GetTargetSdkVersion();
+ if (IsSdkVersionSetAndAtMost(targetSdkVersion, SdkVersion::kT)) {
+ ObjPtr<mirror::Class> java_lang_ClassValue =
+ WellKnownClasses::ToClass(WellKnownClasses::java_lang_ClassValue);
+ if (c.Get() == java_lang_ClassValue.Ptr()) {
+ soa.Self()->ThrowNewException("Ljava/lang/ClassNotFoundException;", "java.lang.ClassValue");
+ return nullptr;
+ }
+ }
+
return soa.AddLocalReference<jclass>(c.Get());
}
@@ -563,7 +586,7 @@
if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
// Return an empty array instead of a null pointer.
ObjPtr<mirror::Class> annotation_array_class =
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_annotation_Annotation__array);
+ WellKnownClasses::ToClass(WellKnownClasses::java_lang_annotation_Annotation__array);
ObjPtr<mirror::ObjectArray<mirror::Object>> empty_array =
mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(),
annotation_array_class,
@@ -750,6 +773,71 @@
return soa.AddLocalReference<jclass>(annotations::GetDeclaringClass(klass));
}
+static jclass Class_getNestHostFromAnnotation(JNIEnv* env, jobject javaThis) {
+ ScopedFastNativeObjectAccess soa(env);
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
+ if (klass->IsObsoleteObject()) {
+ ThrowRuntimeException("Obsolete Object!");
+ return nullptr;
+ }
+ if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+ return nullptr;
+ }
+ ObjPtr<mirror::Class> hostClass = annotations::GetNestHost(klass);
+ if (hostClass == nullptr) {
+ return nullptr;
+ }
+ return soa.AddLocalReference<jclass>(hostClass);
+}
+
+static jobjectArray Class_getNestMembersFromAnnotation(JNIEnv* env, jobject javaThis) {
+ ScopedFastNativeObjectAccess soa(env);
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
+ if (klass->IsObsoleteObject()) {
+ ThrowRuntimeException("Obsolete Object!");
+ return nullptr;
+ }
+ if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+ return nullptr;
+ }
+ ObjPtr<mirror::ObjectArray<mirror::Class>> classes = annotations::GetNestMembers(klass);
+ if (classes == nullptr) {
+ return nullptr;
+ }
+ return soa.AddLocalReference<jobjectArray>(classes);
+}
+
+static jobjectArray Class_getPermittedSubclassesFromAnnotation(JNIEnv* env, jobject javaThis) {
+ ScopedFastNativeObjectAccess soa(env);
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::Class> klass(hs.NewHandle(DecodeClass(soa, javaThis)));
+ if (klass->IsObsoleteObject()) {
+ ThrowRuntimeException("Obsolete Object!");
+ return nullptr;
+ }
+ if (klass->IsProxyClass() || klass->GetDexCache() == nullptr) {
+ return nullptr;
+ }
+ ObjPtr<mirror::ObjectArray<mirror::Class>> classes = annotations::GetPermittedSubclasses(klass);
+ if (classes == nullptr) {
+ return nullptr;
+ }
+ return soa.AddLocalReference<jobjectArray>(classes);
+}
+
+static jobject Class_ensureExtDataPresent(JNIEnv* env, jobject javaThis) {
+ ScopedFastNativeObjectAccess soa(env);
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::Class> klass = hs.NewHandle(DecodeClass(soa, javaThis));
+
+ ObjPtr<mirror::Object> extDataPtr =
+ mirror::Class::EnsureExtDataPresent(klass, Thread::Current());
+
+ return soa.AddLocalReference<jobject>(extDataPtr);
+}
+
static jobject Class_newInstance(JNIEnv* env, jobject javaThis) {
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<4> hs(soa.Self());
@@ -841,6 +929,7 @@
static JNINativeMethod gMethods[] = {
FAST_NATIVE_METHOD(Class, classForName,
"(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"),
+ FAST_NATIVE_METHOD(Class, ensureExtDataPresent, "()Ldalvik/system/ClassExt;"),
FAST_NATIVE_METHOD(Class, getDeclaredAnnotation,
"(Ljava/lang/Class;)Ljava/lang/annotation/Annotation;"),
FAST_NATIVE_METHOD(Class, getDeclaredAnnotations, "()[Ljava/lang/annotation/Annotation;"),
@@ -865,6 +954,9 @@
FAST_NATIVE_METHOD(Class, getInterfacesInternal, "()[Ljava/lang/Class;"),
FAST_NATIVE_METHOD(Class, getPrimitiveClass, "(Ljava/lang/String;)Ljava/lang/Class;"),
FAST_NATIVE_METHOD(Class, getNameNative, "()Ljava/lang/String;"),
+ FAST_NATIVE_METHOD(Class, getNestHostFromAnnotation, "()Ljava/lang/Class;"),
+ FAST_NATIVE_METHOD(Class, getNestMembersFromAnnotation, "()[Ljava/lang/Class;"),
+ FAST_NATIVE_METHOD(Class, getPermittedSubclassesFromAnnotation, "()[Ljava/lang/Class;"),
FAST_NATIVE_METHOD(Class, getPublicDeclaredFields, "()[Ljava/lang/reflect/Field;"),
FAST_NATIVE_METHOD(Class, getSignatureAnnotation, "()[Ljava/lang/String;"),
FAST_NATIVE_METHOD(Class, isAnonymousClass, "()Z"),
diff --git a/runtime/native/java_lang_StackStreamFactory.cc b/runtime/native/java_lang_StackStreamFactory.cc
new file mode 100644
index 0000000..f876c10
--- /dev/null
+++ b/runtime/native/java_lang_StackStreamFactory.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 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 "java_lang_StackStreamFactory.h"
+
+#include "nativehelper/jni_macros.h"
+
+#include "jni/jni_internal.h"
+#include "native_util.h"
+#include "scoped_fast_native_object_access-inl.h"
+#include "thread.h"
+
+namespace art {
+
+static jobject StackStreamFactory_nativeGetStackAnchor(JNIEnv* env, jclass) {
+ ScopedFastNativeObjectAccess soa(env);
+ return soa.Self()->CreateInternalStackTrace(soa);
+}
+
+static jint StackStreamFactory_nativeFetchStackFrameInfo(JNIEnv* env, jclass,
+ jlong mode, jobject anchor, jint startLevel, jint batchSize, jint startBufferIndex,
+ jobjectArray frameBuffer) {
+ if (anchor == nullptr) {
+ return startLevel;
+ }
+ ScopedFastNativeObjectAccess soa(env);
+ return Thread::InternalStackTraceToStackFrameInfoArray(soa, mode, anchor,
+ startLevel, batchSize, startBufferIndex, frameBuffer);
+}
+
+static JNINativeMethod gMethods[] = {
+ FAST_NATIVE_METHOD(StackStreamFactory, nativeGetStackAnchor, "()Ljava/lang/Object;"),
+ FAST_NATIVE_METHOD(StackStreamFactory, nativeFetchStackFrameInfo, "(JLjava/lang/Object;III[Ljava/lang/Object;)I"),
+};
+
+void register_java_lang_StackStreamFactory(JNIEnv* env) {
+ REGISTER_NATIVE_METHODS("java/lang/StackStreamFactory");
+}
+
+} // namespace art
diff --git a/runtime/native/java_lang_StackStreamFactory.h b/runtime/native/java_lang_StackStreamFactory.h
new file mode 100644
index 0000000..2216871
--- /dev/null
+++ b/runtime/native/java_lang_StackStreamFactory.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_STACKSTREAMFACTORY_H_
+#define ART_RUNTIME_NATIVE_JAVA_LANG_STACKSTREAMFACTORY_H_
+
+#include <jni.h>
+
+namespace art {
+
+void register_java_lang_StackStreamFactory(JNIEnv* env);
+
+} // namespace art
+
+#endif // ART_RUNTIME_NATIVE_JAVA_LANG_STACKSTREAMFACTORY_H_
diff --git a/runtime/native/java_lang_String.cc b/runtime/native/java_lang_String.cc
index 94ca5b5..f70a188 100644
--- a/runtime/native/java_lang_String.cc
+++ b/runtime/native/java_lang_String.cc
@@ -88,6 +88,22 @@
soa.Decode<mirror::String>(java_this)->GetChars(start, end, char_array, index);
}
+static void String_fillBytesLatin1(JNIEnv* env, jobject java_this,
+ jbyteArray buffer, jint byteIndex) {
+ ScopedFastNativeObjectAccess soa(env);
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ByteArray> byte_array(hs.NewHandle(soa.Decode<mirror::ByteArray>(buffer)));
+ soa.Decode<mirror::String>(java_this)->FillBytesLatin1(byte_array, byteIndex);
+}
+
+static void String_fillBytesUTF16(JNIEnv* env, jobject java_this,
+ jbyteArray buffer, jint byteIndex) {
+ ScopedFastNativeObjectAccess soa(env);
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ByteArray> byte_array(hs.NewHandle(soa.Decode<mirror::ByteArray>(buffer)));
+ soa.Decode<mirror::String>(java_this)->FillBytesUTF16(byte_array, byteIndex);
+}
+
static jstring String_intern(JNIEnv* env, jobject java_this) {
ScopedFastNativeObjectAccess soa(env);
ObjPtr<mirror::String> result = soa.Decode<mirror::String>(java_this)->Intern();
@@ -125,6 +141,8 @@
FAST_NATIVE_METHOD(String, doReplace, "(CC)Ljava/lang/String;"),
FAST_NATIVE_METHOD(String, fastSubstring, "(II)Ljava/lang/String;"),
FAST_NATIVE_METHOD(String, getCharsNoCheck, "(II[CI)V"),
+ FAST_NATIVE_METHOD(String, fillBytesLatin1, "([BI)V"),
+ FAST_NATIVE_METHOD(String, fillBytesUTF16, "([BI)V"),
FAST_NATIVE_METHOD(String, intern, "()Ljava/lang/String;"),
FAST_NATIVE_METHOD(String, toCharArray, "()[C"),
};
diff --git a/runtime/native/java_lang_StringFactory.cc b/runtime/native/java_lang_StringFactory.cc
index 9086ee9..2fbebc0 100644
--- a/runtime/native/java_lang_StringFactory.cc
+++ b/runtime/native/java_lang_StringFactory.cc
@@ -56,6 +56,36 @@
return soa.AddLocalReference<jstring>(result);
}
+static jstring StringFactory_newStringFromUtf16Bytes(
+ JNIEnv* env, jclass, jbyteArray java_data, jint offset, jint char_count) {
+ ScopedFastNativeObjectAccess soa(env);
+ if (UNLIKELY(java_data == nullptr)) {
+ ThrowNullPointerException("data == null");
+ return nullptr;
+ }
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ByteArray> byte_array(hs.NewHandle(soa.Decode<mirror::ByteArray>(java_data)));
+ int32_t data_size = byte_array->GetLength();
+ DCHECK_GE(data_size, 0);
+ if (offset < 0 ||
+ offset > data_size ||
+ static_cast<uint32_t>(char_count) > (static_cast<uint32_t>(data_size - offset) >> 1)) {
+ soa.Self()->ThrowNewExceptionF("Ljava/lang/StringIndexOutOfBoundsException;",
+ "length=%d; regionStart=%d; bytePairLength=%d",
+ data_size,
+ offset,
+ char_count);
+ return nullptr;
+ }
+ gc::AllocatorType allocator_type = Runtime::Current()->GetHeap()->GetCurrentAllocator();
+ ObjPtr<mirror::String> result = mirror::String::AllocFromUtf16ByteArray(soa.Self(),
+ char_count,
+ byte_array,
+ offset,
+ allocator_type);
+ return soa.AddLocalReference<jstring>(result);
+}
+
// The char array passed as `java_data` must not be a null reference.
static jstring StringFactory_newStringFromChars(JNIEnv* env, jclass, jint offset,
jint char_count, jcharArray java_data) {
@@ -269,6 +299,7 @@
FAST_NATIVE_METHOD(StringFactory, newStringFromChars, "(II[C)Ljava/lang/String;"),
FAST_NATIVE_METHOD(StringFactory, newStringFromString, "(Ljava/lang/String;)Ljava/lang/String;"),
FAST_NATIVE_METHOD(StringFactory, newStringFromUtf8Bytes, "([BII)Ljava/lang/String;"),
+ FAST_NATIVE_METHOD(StringFactory, newStringFromUtf16Bytes, "([BII)Ljava/lang/String;"),
};
void register_java_lang_StringFactory(JNIEnv* env) {
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index 11e02a2..b9c72b8 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -18,6 +18,8 @@
#include "base/zip_archive.h"
#include "class_linker.h"
+#include "base/transform_iterator.h"
+#include "base/stl_util.h"
#include "dex/descriptors_names.h"
#include "dex/dex_file_loader.h"
#include "dex/utf.h"
@@ -25,13 +27,16 @@
#include "jni/jni_internal.h"
#include "mirror/class_loader.h"
#include "mirror/object-inl.h"
+#include "mirror/object_array-alloc-inl.h"
#include "native_util.h"
#include "nativehelper/jni_macros.h"
#include "nativehelper/scoped_local_ref.h"
#include "nativehelper/scoped_utf_chars.h"
#include "obj_ptr.h"
#include "scoped_fast_native_object_access-inl.h"
-#include "well_known_classes.h"
+#include "string_array_utils.h"
+#include "thread-inl.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -49,14 +54,13 @@
}
static ObjPtr<mirror::Class> FindClassInPathClassLoader(ClassLinker* cl,
- ScopedObjectAccessAlreadyRunnable& soa,
Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader)
REQUIRES_SHARED(Locks::mutator_lock_) {
ObjPtr<mirror::Class> result;
- if (cl->FindClassInBaseDexClassLoader(soa, self, descriptor, hash, class_loader, &result)) {
+ if (cl->FindClassInBaseDexClassLoader(self, descriptor, hash, class_loader, &result)) {
DCHECK(!self->IsExceptionPending());
return result;
}
@@ -93,12 +97,9 @@
if (c != nullptr && c->IsErroneous()) {
cl->ThrowEarlierClassFailure(c);
Thread* self = soa.Self();
- ObjPtr<mirror::Class> iae_class =
- self->DecodeJObject(WellKnownClasses::java_lang_IllegalAccessError)->AsClass();
- ObjPtr<mirror::Class> ncdfe_class =
- self->DecodeJObject(WellKnownClasses::java_lang_NoClassDefFoundError)->AsClass();
- ObjPtr<mirror::Class> exception = self->GetException()->GetClass();
- if (exception == iae_class || exception == ncdfe_class) {
+ ObjPtr<mirror::Class> exception_class = self->GetException()->GetClass();
+ if (exception_class == WellKnownClasses::java_lang_IllegalAccessError ||
+ exception_class == WellKnownClasses::java_lang_NoClassDefFoundError) {
self->ThrowNewWrappedException("Ljava/lang/ClassNotFoundException;",
c->PrettyDescriptor().c_str());
}
@@ -112,7 +113,6 @@
// Try the common case.
StackHandleScope<1> hs(soa.Self());
c = VMClassLoader::FindClassInPathClassLoader(cl,
- soa,
soa.Self(),
descriptor.c_str(),
descriptor_hash,
@@ -131,28 +131,37 @@
* Returns an array of entries from the boot classpath that could contain resources.
*/
static jobjectArray VMClassLoader_getBootClassPathEntries(JNIEnv* env, jclass) {
- const std::vector<const DexFile*>& path =
- Runtime::Current()->GetClassLinker()->GetBootClassPath();
- jobjectArray array =
- env->NewObjectArray(path.size(), WellKnownClasses::java_lang_String, nullptr);
- if (array == nullptr) {
- DCHECK(env->ExceptionCheck());
- return nullptr;
- }
- for (size_t i = 0; i < path.size(); ++i) {
- const DexFile* dex_file = path[i];
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ const std::vector<const DexFile*>& path = class_linker->GetBootClassPath();
+ auto is_base_dex = [](const DexFile* dex_file) {
+ return !DexFileLoader::IsMultiDexLocation(dex_file->GetLocation().c_str());
+ };
+ size_t jar_count = std::count_if(path.begin(), path.end(), is_base_dex);
+ const DexFile* last_dex_file = nullptr;
+ auto dchecked_is_base_dex = [&](const DexFile* dex_file) {
// For multidex locations, e.g., x.jar!classes2.dex, we want to look into x.jar.
- const std::string location(DexFileLoader::GetBaseLocation(dex_file->GetLocation()));
-
- ScopedLocalRef<jstring> javaPath(env, env->NewStringUTF(location.c_str()));
- if (javaPath.get() == nullptr) {
- DCHECK(env->ExceptionCheck());
- return nullptr;
+ // But we do not need to look into the base dex file more than once so we filter
+ // out multidex locations using the fact that they follow the base location.
+ if (kIsDebugBuild) {
+ if (is_base_dex(dex_file)) {
+ CHECK_EQ(DexFileLoader::GetBaseLocation(dex_file->GetLocation().c_str()),
+ dex_file->GetLocation());
+ } else {
+ CHECK(last_dex_file != nullptr);
+ CHECK_EQ(DexFileLoader::GetBaseLocation(dex_file->GetLocation().c_str()),
+ DexFileLoader::GetBaseLocation(last_dex_file->GetLocation().c_str()));
+ }
+ last_dex_file = dex_file;
}
- env->SetObjectArrayElement(array, i, javaPath.get());
- }
- return array;
+ return is_base_dex(dex_file);
+ };
+ auto get_location = [](const DexFile* dex_file) { return dex_file->GetLocation(); };
+ ScopedObjectAccess soa(Thread::ForEnv(env));
+ return soa.AddLocalReference<jobjectArray>(CreateStringArray(
+ soa.Self(),
+ jar_count,
+ MakeTransformRange(Filter(path, dchecked_is_base_dex), get_location)));
}
static JNINativeMethod gMethods[] = {
diff --git a/runtime/native/java_lang_ref_Reference.cc b/runtime/native/java_lang_ref_Reference.cc
index f23010b..8b5635d 100644
--- a/runtime/native/java_lang_ref_Reference.cc
+++ b/runtime/native/java_lang_ref_Reference.cc
@@ -37,7 +37,7 @@
}
static jboolean Reference_refersTo0(JNIEnv* env, jobject javaThis, jobject o) {
- if (kUseReadBarrier && !kUseBakerReadBarrier) {
+ if (gUseReadBarrier && !kUseBakerReadBarrier) {
// Fall back to naive implementation that may block and needlessly preserve javaThis.
return env->IsSameObject(Reference_getReferent(env, javaThis), o);
}
@@ -48,7 +48,7 @@
if (referent == other) {
return JNI_TRUE;
}
- if (!kUseReadBarrier || referent.IsNull() || other.IsNull()) {
+ if (!gUseReadBarrier || referent.IsNull() || other.IsNull()) {
return JNI_FALSE;
}
// Explicitly handle the case in which referent is a from-space pointer. Don't use a
diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc
index 1d362c0..4b2cc43 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -33,7 +33,6 @@
#include "native_util.h"
#include "reflection.h"
#include "scoped_fast_native_object_access-inl.h"
-#include "well_known_classes.h"
namespace art {
diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc
index fef16b9..87c9f6c 100644
--- a/runtime/native/java_lang_reflect_Executable.cc
+++ b/runtime/native/java_lang_reflect_Executable.cc
@@ -19,7 +19,7 @@
#include "android-base/stringprintf.h"
#include "nativehelper/jni_macros.h"
-#include "art_method-inl.h"
+#include "art_method-alloc-inl.h"
#include "class_root-inl.h"
#include "dex/dex_file_annotations.h"
#include "handle.h"
@@ -45,7 +45,7 @@
if (method->GetDeclaringClass()->IsProxyClass()) {
// Return an empty array instead of a null pointer.
ObjPtr<mirror::Class> annotation_array_class =
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_annotation_Annotation__array);
+ WellKnownClasses::ToClass(WellKnownClasses::java_lang_annotation_Annotation__array);
ObjPtr<mirror::ObjectArray<mirror::Object>> empty_array =
mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), annotation_array_class, 0);
return soa.AddLocalReference<jobjectArray>(empty_array);
@@ -128,7 +128,7 @@
// Workaround for dexers (d8/dx) that do not insert annotations
// for implicit parameters (b/68033708).
ObjPtr<mirror::Class> annotation_array_class =
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_annotation_Annotation__array);
+ WellKnownClasses::ToClass(WellKnownClasses::java_lang_annotation_Annotation__array);
Handle<mirror::ObjectArray<mirror::Object>> empty_annotations = hs.NewHandle(
mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), annotation_array_class, 0));
if (empty_annotations.IsNull()) {
@@ -157,7 +157,7 @@
static jobjectArray Executable_getParameters0(JNIEnv* env, jobject javaMethod) {
ScopedFastNativeObjectAccess soa(env);
Thread* self = soa.Self();
- StackHandleScope<8> hs(self);
+ StackHandleScope<6> hs(self);
Handle<mirror::Method> executable = hs.NewHandle(soa.Decode<mirror::Method>(javaMethod));
ArtMethod* art_method = executable.Get()->GetArtMethod();
@@ -197,57 +197,37 @@
// Instantiate a Parameter[] to hold the result.
Handle<mirror::Class> parameter_array_class =
hs.NewHandle(
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_reflect_Parameter__array));
- Handle<mirror::ObjectArray<mirror::Object>> parameter_array =
- hs.NewHandle(
- mirror::ObjectArray<mirror::Object>::Alloc(self,
- parameter_array_class.Get(),
- names_count));
+ WellKnownClasses::ToClass(WellKnownClasses::java_lang_reflect_Parameter__array));
+ Handle<mirror::ObjectArray<mirror::Object>> parameter_array = hs.NewHandle(
+ mirror::ObjectArray<mirror::Object>::Alloc(self, parameter_array_class.Get(), names_count));
if (UNLIKELY(parameter_array == nullptr)) {
self->AssertPendingException();
return nullptr;
}
- Handle<mirror::Class> parameter_class =
- hs.NewHandle(soa.Decode<mirror::Class>(WellKnownClasses::java_lang_reflect_Parameter));
- ArtMethod* parameter_init =
- jni::DecodeArtMethod(WellKnownClasses::java_lang_reflect_Parameter_init);
+ ArtMethod* parameter_init = WellKnownClasses::java_lang_reflect_Parameter_init;
// Mutable handles used in the loop below to ensure cleanup without scaling the number of
// handles by the number of parameters.
MutableHandle<mirror::String> name = hs.NewHandle<mirror::String>(nullptr);
- MutableHandle<mirror::Object> parameter = hs.NewHandle<mirror::Object>(nullptr);
// Populate the Parameter[] to return.
for (int32_t parameter_index = 0; parameter_index < names_count; parameter_index++) {
name.Assign(names.Get()->Get(parameter_index));
int32_t modifiers = access_flags.Get()->Get(parameter_index);
- // Allocate / initialize the Parameter to add to parameter_array.
- parameter.Assign(parameter_class->AllocObject(self));
+ // Create the Parameter to add to parameter_array.
+ ObjPtr<mirror::Object> parameter = parameter_init->NewObject<'L', 'I', 'L', 'I'>(
+ self, name, modifiers, executable, parameter_index);
if (UNLIKELY(parameter == nullptr)) {
- self->AssertPendingOOMException();
+ DCHECK(self->IsExceptionPending());
return nullptr;
}
- uint32_t args[5] = { PointerToLowMemUInt32(parameter.Get()),
- PointerToLowMemUInt32(name.Get()),
- static_cast<uint32_t>(modifiers),
- PointerToLowMemUInt32(executable.Get()),
- static_cast<uint32_t>(parameter_index)
- };
- JValue result;
- static const char* method_signature = "VLILI"; // return + parameter types
- parameter_init->Invoke(self, args, sizeof(args), &result, method_signature);
- if (UNLIKELY(self->IsExceptionPending())) {
- return nullptr;
- }
-
- // Store the Parameter in the Parameter[].
- parameter_array.Get()->Set(parameter_index, parameter.Get());
- if (UNLIKELY(self->IsExceptionPending())) {
- return nullptr;
- }
+ // We're initializing a newly allocated array object, so we do not need to record that under
+ // a transaction. If the transaction is aborted, the whole object shall be unreachable.
+ parameter_array->SetWithoutChecks</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(
+ parameter_index, parameter);
}
return soa.AddLocalReference<jobjectArray>(parameter_array.Get());
}
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index cfc4a29..946a4e4 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -491,7 +491,7 @@
if (field->GetDeclaringClass()->IsProxyClass()) {
// Return an empty array instead of a null pointer.
ObjPtr<mirror::Class> annotation_array_class =
- soa.Decode<mirror::Class>(WellKnownClasses::java_lang_annotation_Annotation__array);
+ WellKnownClasses::ToClass(WellKnownClasses::java_lang_annotation_Annotation__array);
ObjPtr<mirror::ObjectArray<mirror::Object>> empty_array =
mirror::ObjectArray<mirror::Object>::Alloc(soa.Self(), annotation_array_class, 0);
return soa.AddLocalReference<jobjectArray>(empty_array);
diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc
index 2c0dd80..5f02ad0 100644
--- a/runtime/native/java_lang_reflect_Method.cc
+++ b/runtime/native/java_lang_reflect_Method.cc
@@ -31,7 +31,6 @@
#include "native_util.h"
#include "reflection.h"
#include "scoped_fast_native_object_access-inl.h"
-#include "well_known_classes.h"
namespace art {
@@ -80,6 +79,7 @@
}
}
+NO_STACK_PROTECTOR
static jobject Method_invoke(JNIEnv* env, jobject javaMethod, jobject javaReceiver,
jobjectArray javaArgs) {
ScopedFastNativeObjectAccess soa(env);
diff --git a/runtime/native/jdk_internal_misc_Unsafe.cc b/runtime/native/jdk_internal_misc_Unsafe.cc
index 307a2fa..6e2f558 100644
--- a/runtime/native/jdk_internal_misc_Unsafe.cc
+++ b/runtime/native/jdk_internal_misc_Unsafe.cc
@@ -34,7 +34,7 @@
#include "art_field-inl.h"
#include "native_util.h"
#include "scoped_fast_native_object_access-inl.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -92,14 +92,18 @@
return Unsafe_compareAndSetLong(env, obj, javaObj, offset, expectedValue, newValue);
}
-static jboolean Unsafe_compareAndSetObject(JNIEnv* env, jobject, jobject javaObj, jlong offset,
- jobject javaExpectedValue, jobject javaNewValue) {
+static jboolean Unsafe_compareAndSetReference(JNIEnv* env,
+ jobject,
+ jobject javaObj,
+ jlong offset,
+ jobject javaExpectedValue,
+ jobject javaNewValue) {
ScopedFastNativeObjectAccess soa(env);
ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
ObjPtr<mirror::Object> expectedValue = soa.Decode<mirror::Object>(javaExpectedValue);
ObjPtr<mirror::Object> newValue = soa.Decode<mirror::Object>(javaNewValue);
// JNI must use non transactional mode.
- if (kUseReadBarrier) {
+ if (gUseReadBarrier) {
// Need to make sure the reference stored in the field is a to-space one before attempting the
// CAS or the CAS could fail incorrectly.
// Note that the read barrier load does NOT need to be volatile.
@@ -122,10 +126,10 @@
static jboolean Unsafe_compareAndSwapObject(JNIEnv* env, jobject obj, jobject javaObj, jlong offset,
jobject javaExpectedValue, jobject javaNewValue) {
- // compareAndSetObject has the same semantics as compareAndSwapObject, except for
+ // compareAndSetReference has the same semantics as compareAndSwapObject, except for
// being strict (volatile). Since this was implemented in a strict mode it can
// just call the volatile version unless it gets relaxed.
- return Unsafe_compareAndSetObject(env, obj, javaObj, offset, javaExpectedValue, javaNewValue);
+ return Unsafe_compareAndSetReference(env, obj, javaObj, offset, javaExpectedValue, javaNewValue);
}
static jint Unsafe_getInt(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
@@ -201,22 +205,22 @@
obj->SetField64<false>(MemberOffset(offset), newValue);
}
-static jobject Unsafe_getObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
+static jobject Unsafe_getReferenceVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
ScopedFastNativeObjectAccess soa(env);
ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
ObjPtr<mirror::Object> value = obj->GetFieldObjectVolatile<mirror::Object>(MemberOffset(offset));
return soa.AddLocalReference<jobject>(value);
}
-static jobject Unsafe_getObject(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
+static jobject Unsafe_getReference(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
ScopedFastNativeObjectAccess soa(env);
ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
ObjPtr<mirror::Object> value = obj->GetFieldObject<mirror::Object>(MemberOffset(offset));
return soa.AddLocalReference<jobject>(value);
}
-static void Unsafe_putObject(JNIEnv* env, jobject, jobject javaObj, jlong offset,
- jobject javaNewValue) {
+static void Unsafe_putReference(
+ JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) {
ScopedFastNativeObjectAccess soa(env);
ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
ObjPtr<mirror::Object> newValue = soa.Decode<mirror::Object>(javaNewValue);
@@ -224,8 +228,8 @@
obj->SetFieldObject<false>(MemberOffset(offset), newValue);
}
-static void Unsafe_putObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset,
- jobject javaNewValue) {
+static void Unsafe_putReferenceVolatile(
+ JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) {
ScopedFastNativeObjectAccess soa(env);
ObjPtr<mirror::Object> obj = soa.Decode<mirror::Object>(javaObj);
ObjPtr<mirror::Object> newValue = soa.Decode<mirror::Object>(javaNewValue);
@@ -474,12 +478,14 @@
static void Unsafe_unpark(JNIEnv* env, jobject, jobject jthread) {
art::ScopedFastNativeObjectAccess soa(env);
- if (jthread == nullptr || !env->IsInstanceOf(jthread, WellKnownClasses::java_lang_Thread)) {
+ ObjPtr<mirror::Object> mirror_thread = soa.Decode<mirror::Object>(jthread);
+ if (mirror_thread == nullptr ||
+ !mirror_thread->InstanceOf(WellKnownClasses::java_lang_Thread.Get())) {
ThrowIllegalArgumentException("Argument to unpark() was not a Thread");
return;
}
art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_);
- art::Thread* thread = art::Thread::FromManagedThread(soa, jthread);
+ art::Thread* thread = art::Thread::FromManagedThread(soa, mirror_thread);
if (thread != nullptr) {
thread->Unpark();
} else {
@@ -487,80 +493,82 @@
// or the thread has already terminated. Setting the field to true will be
// respected when the thread does start, and is harmless if the thread has
// already terminated.
- ArtField* unparked =
- jni::DecodeArtField(WellKnownClasses::java_lang_Thread_unparkedBeforeStart);
+ ArtField* unparked = WellKnownClasses::java_lang_Thread_unparkedBeforeStart;
// JNI must use non transactional mode.
- unparked->SetBoolean<false>(soa.Decode<mirror::Object>(jthread), JNI_TRUE);
+ unparked->SetBoolean<false>(mirror_thread, JNI_TRUE);
}
}
static JNINativeMethod gMethods[] = {
- FAST_NATIVE_METHOD(Unsafe, compareAndSwapInt, "(Ljava/lang/Object;JII)Z"),
- FAST_NATIVE_METHOD(Unsafe, compareAndSwapLong, "(Ljava/lang/Object;JJJ)Z"),
- FAST_NATIVE_METHOD(Unsafe, compareAndSwapObject, "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z"),
- FAST_NATIVE_METHOD(Unsafe, compareAndSetInt, "(Ljava/lang/Object;JII)Z"),
- FAST_NATIVE_METHOD(Unsafe, compareAndSetLong, "(Ljava/lang/Object;JJJ)Z"),
- FAST_NATIVE_METHOD(Unsafe, compareAndSetObject, "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z"),
- FAST_NATIVE_METHOD(Unsafe, getIntVolatile, "(Ljava/lang/Object;J)I"),
- FAST_NATIVE_METHOD(Unsafe, putIntVolatile, "(Ljava/lang/Object;JI)V"),
- FAST_NATIVE_METHOD(Unsafe, getLongVolatile, "(Ljava/lang/Object;J)J"),
- FAST_NATIVE_METHOD(Unsafe, putLongVolatile, "(Ljava/lang/Object;JJ)V"),
- FAST_NATIVE_METHOD(Unsafe, getObjectVolatile, "(Ljava/lang/Object;J)Ljava/lang/Object;"),
- FAST_NATIVE_METHOD(Unsafe, putObjectVolatile, "(Ljava/lang/Object;JLjava/lang/Object;)V"),
- FAST_NATIVE_METHOD(Unsafe, getInt, "(Ljava/lang/Object;J)I"),
- FAST_NATIVE_METHOD(Unsafe, putInt, "(Ljava/lang/Object;JI)V"),
- FAST_NATIVE_METHOD(Unsafe, putOrderedInt, "(Ljava/lang/Object;JI)V"),
- FAST_NATIVE_METHOD(Unsafe, getLong, "(Ljava/lang/Object;J)J"),
- FAST_NATIVE_METHOD(Unsafe, putLong, "(Ljava/lang/Object;JJ)V"),
- FAST_NATIVE_METHOD(Unsafe, putOrderedLong, "(Ljava/lang/Object;JJ)V"),
- FAST_NATIVE_METHOD(Unsafe, getObject, "(Ljava/lang/Object;J)Ljava/lang/Object;"),
- FAST_NATIVE_METHOD(Unsafe, putObject, "(Ljava/lang/Object;JLjava/lang/Object;)V"),
- FAST_NATIVE_METHOD(Unsafe, putOrderedObject, "(Ljava/lang/Object;JLjava/lang/Object;)V"),
- FAST_NATIVE_METHOD(Unsafe, getArrayBaseOffsetForComponentType, "(Ljava/lang/Class;)I"),
- FAST_NATIVE_METHOD(Unsafe, getArrayIndexScaleForComponentType, "(Ljava/lang/Class;)I"),
- FAST_NATIVE_METHOD(Unsafe, addressSize, "()I"),
- FAST_NATIVE_METHOD(Unsafe, pageSize, "()I"),
- FAST_NATIVE_METHOD(Unsafe, allocateMemory, "(J)J"),
- FAST_NATIVE_METHOD(Unsafe, freeMemory, "(J)V"),
- FAST_NATIVE_METHOD(Unsafe, setMemory, "(JJB)V"),
- FAST_NATIVE_METHOD(Unsafe, copyMemory0, "(Ljava/lang/Object;JLjava/lang/Object;JJ)V"),
- FAST_NATIVE_METHOD(Unsafe, getBoolean, "(Ljava/lang/Object;J)Z"),
+ FAST_NATIVE_METHOD(Unsafe, compareAndSwapInt, "(Ljava/lang/Object;JII)Z"),
+ FAST_NATIVE_METHOD(Unsafe, compareAndSwapLong, "(Ljava/lang/Object;JJJ)Z"),
+ FAST_NATIVE_METHOD(
+ Unsafe, compareAndSwapObject, "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z"),
+ FAST_NATIVE_METHOD(Unsafe, compareAndSetInt, "(Ljava/lang/Object;JII)Z"),
+ FAST_NATIVE_METHOD(Unsafe, compareAndSetLong, "(Ljava/lang/Object;JJJ)Z"),
+ FAST_NATIVE_METHOD(Unsafe,
+ compareAndSetReference,
+ "(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z"),
+ FAST_NATIVE_METHOD(Unsafe, getIntVolatile, "(Ljava/lang/Object;J)I"),
+ FAST_NATIVE_METHOD(Unsafe, putIntVolatile, "(Ljava/lang/Object;JI)V"),
+ FAST_NATIVE_METHOD(Unsafe, getLongVolatile, "(Ljava/lang/Object;J)J"),
+ FAST_NATIVE_METHOD(Unsafe, putLongVolatile, "(Ljava/lang/Object;JJ)V"),
+ FAST_NATIVE_METHOD(Unsafe, getReferenceVolatile, "(Ljava/lang/Object;J)Ljava/lang/Object;"),
+ FAST_NATIVE_METHOD(Unsafe, putReferenceVolatile, "(Ljava/lang/Object;JLjava/lang/Object;)V"),
+ FAST_NATIVE_METHOD(Unsafe, getInt, "(Ljava/lang/Object;J)I"),
+ FAST_NATIVE_METHOD(Unsafe, putInt, "(Ljava/lang/Object;JI)V"),
+ FAST_NATIVE_METHOD(Unsafe, putOrderedInt, "(Ljava/lang/Object;JI)V"),
+ FAST_NATIVE_METHOD(Unsafe, getLong, "(Ljava/lang/Object;J)J"),
+ FAST_NATIVE_METHOD(Unsafe, putLong, "(Ljava/lang/Object;JJ)V"),
+ FAST_NATIVE_METHOD(Unsafe, putOrderedLong, "(Ljava/lang/Object;JJ)V"),
+ FAST_NATIVE_METHOD(Unsafe, getReference, "(Ljava/lang/Object;J)Ljava/lang/Object;"),
+ FAST_NATIVE_METHOD(Unsafe, putReference, "(Ljava/lang/Object;JLjava/lang/Object;)V"),
+ FAST_NATIVE_METHOD(Unsafe, putOrderedObject, "(Ljava/lang/Object;JLjava/lang/Object;)V"),
+ FAST_NATIVE_METHOD(Unsafe, getArrayBaseOffsetForComponentType, "(Ljava/lang/Class;)I"),
+ FAST_NATIVE_METHOD(Unsafe, getArrayIndexScaleForComponentType, "(Ljava/lang/Class;)I"),
+ FAST_NATIVE_METHOD(Unsafe, addressSize, "()I"),
+ FAST_NATIVE_METHOD(Unsafe, pageSize, "()I"),
+ FAST_NATIVE_METHOD(Unsafe, allocateMemory, "(J)J"),
+ FAST_NATIVE_METHOD(Unsafe, freeMemory, "(J)V"),
+ FAST_NATIVE_METHOD(Unsafe, setMemory, "(JJB)V"),
+ FAST_NATIVE_METHOD(Unsafe, copyMemory0, "(Ljava/lang/Object;JLjava/lang/Object;JJ)V"),
+ FAST_NATIVE_METHOD(Unsafe, getBoolean, "(Ljava/lang/Object;J)Z"),
- FAST_NATIVE_METHOD(Unsafe, getByte, "(Ljava/lang/Object;J)B"),
- FAST_NATIVE_METHOD(Unsafe, getChar, "(Ljava/lang/Object;J)C"),
- FAST_NATIVE_METHOD(Unsafe, getShort, "(Ljava/lang/Object;J)S"),
- FAST_NATIVE_METHOD(Unsafe, getFloat, "(Ljava/lang/Object;J)F"),
- FAST_NATIVE_METHOD(Unsafe, getDouble, "(Ljava/lang/Object;J)D"),
- FAST_NATIVE_METHOD(Unsafe, putBoolean, "(Ljava/lang/Object;JZ)V"),
- FAST_NATIVE_METHOD(Unsafe, putByte, "(Ljava/lang/Object;JB)V"),
- FAST_NATIVE_METHOD(Unsafe, putChar, "(Ljava/lang/Object;JC)V"),
- FAST_NATIVE_METHOD(Unsafe, putShort, "(Ljava/lang/Object;JS)V"),
- FAST_NATIVE_METHOD(Unsafe, putFloat, "(Ljava/lang/Object;JF)V"),
- FAST_NATIVE_METHOD(Unsafe, putDouble, "(Ljava/lang/Object;JD)V"),
- FAST_NATIVE_METHOD(Unsafe, unpark, "(Ljava/lang/Object;)V"),
- NATIVE_METHOD(Unsafe, park, "(ZJ)V"),
+ FAST_NATIVE_METHOD(Unsafe, getByte, "(Ljava/lang/Object;J)B"),
+ FAST_NATIVE_METHOD(Unsafe, getChar, "(Ljava/lang/Object;J)C"),
+ FAST_NATIVE_METHOD(Unsafe, getShort, "(Ljava/lang/Object;J)S"),
+ FAST_NATIVE_METHOD(Unsafe, getFloat, "(Ljava/lang/Object;J)F"),
+ FAST_NATIVE_METHOD(Unsafe, getDouble, "(Ljava/lang/Object;J)D"),
+ FAST_NATIVE_METHOD(Unsafe, putBoolean, "(Ljava/lang/Object;JZ)V"),
+ FAST_NATIVE_METHOD(Unsafe, putByte, "(Ljava/lang/Object;JB)V"),
+ FAST_NATIVE_METHOD(Unsafe, putChar, "(Ljava/lang/Object;JC)V"),
+ FAST_NATIVE_METHOD(Unsafe, putShort, "(Ljava/lang/Object;JS)V"),
+ FAST_NATIVE_METHOD(Unsafe, putFloat, "(Ljava/lang/Object;JF)V"),
+ FAST_NATIVE_METHOD(Unsafe, putDouble, "(Ljava/lang/Object;JD)V"),
+ FAST_NATIVE_METHOD(Unsafe, unpark, "(Ljava/lang/Object;)V"),
+ NATIVE_METHOD(Unsafe, park, "(ZJ)V"),
- // Each of the getFoo variants are overloaded with a call that operates
- // directively on a native pointer.
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getByte, "(J)B", getByteJ),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getChar, "(J)C", getCharJ),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getShort, "(J)S", getShortJ),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getInt, "(J)I", getIntJ),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getLong, "(J)J", getLongJ),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getFloat, "(J)F", getFloatJ),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getDouble, "(J)D", getDoubleJ),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putByte, "(JB)V", putByteJB),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putChar, "(JC)V", putCharJC),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putShort, "(JS)V", putShortJS),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putInt, "(JI)V", putIntJI),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putLong, "(JJ)V", putLongJJ),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putFloat, "(JF)V", putFloatJF),
- OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putDouble, "(JD)V", putDoubleJD),
+ // Each of the getFoo variants are overloaded with a call that operates
+ // directively on a native pointer.
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getByte, "(J)B", getByteJ),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getChar, "(J)C", getCharJ),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getShort, "(J)S", getShortJ),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getInt, "(J)I", getIntJ),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getLong, "(J)J", getLongJ),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getFloat, "(J)F", getFloatJ),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, getDouble, "(J)D", getDoubleJ),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putByte, "(JB)V", putByteJB),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putChar, "(JC)V", putCharJC),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putShort, "(JS)V", putShortJS),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putInt, "(JI)V", putIntJI),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putLong, "(JJ)V", putLongJJ),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putFloat, "(JF)V", putFloatJF),
+ OVERLOADED_FAST_NATIVE_METHOD(Unsafe, putDouble, "(JD)V", putDoubleJD),
- // CAS
- FAST_NATIVE_METHOD(Unsafe, loadFence, "()V"),
- FAST_NATIVE_METHOD(Unsafe, storeFence, "()V"),
- FAST_NATIVE_METHOD(Unsafe, fullFence, "()V"),
+ // CAS
+ FAST_NATIVE_METHOD(Unsafe, loadFence, "()V"),
+ FAST_NATIVE_METHOD(Unsafe, storeFence, "()V"),
+ FAST_NATIVE_METHOD(Unsafe, fullFence, "()V"),
};
void register_jdk_internal_misc_Unsafe(JNIEnv* env) {
diff --git a/runtime/native/libcore_util_CharsetUtils.cc b/runtime/native/libcore_util_CharsetUtils.cc
index 92f355e..c53fd6e 100644
--- a/runtime/native/libcore_util_CharsetUtils.cc
+++ b/runtime/native/libcore_util_CharsetUtils.cc
@@ -50,24 +50,6 @@
}
}
-static void CharsetUtils_isoLatin1BytesToChars(JNIEnv* env, jclass, jbyteArray javaBytes,
- jint offset, jint length, jcharArray javaChars) {
- ScopedByteArrayRO bytes(env, javaBytes);
- if (bytes.get() == nullptr) {
- return;
- }
- ScopedCharArrayRW chars(env, javaChars);
- if (chars.get() == nullptr) {
- return;
- }
-
- const jbyte* src = &bytes[offset];
- jchar* dst = &chars[0];
- for (int i = length - 1; i >= 0; --i) {
- *dst++ = static_cast<jchar>(*src++ & 0xff);
- }
-}
-
/**
* Translates the given characters to US-ASCII or ISO-8859-1 bytes, using the fact that
* Unicode code points between U+0000 and U+007f inclusive are identical to US-ASCII, while
@@ -157,7 +139,6 @@
static JNINativeMethod gMethods[] = {
FAST_NATIVE_METHOD(CharsetUtils, asciiBytesToChars, "([BII[C)V"),
- FAST_NATIVE_METHOD(CharsetUtils, isoLatin1BytesToChars, "([BII[C)V"),
FAST_NATIVE_METHOD(CharsetUtils, toAsciiBytes, "(Ljava/lang/String;II)[B"),
FAST_NATIVE_METHOD(CharsetUtils, toIsoLatin1Bytes, "(Ljava/lang/String;II)[B"),
FAST_NATIVE_METHOD(CharsetUtils, toUtf8Bytes, "(Ljava/lang/String;II)[B"),
diff --git a/runtime/native/string_array_utils.h b/runtime/native/string_array_utils.h
new file mode 100644
index 0000000..41d5093
--- /dev/null
+++ b/runtime/native/string_array_utils.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_NATIVE_STRING_ARRAY_UTILS_H_
+#define ART_RUNTIME_NATIVE_STRING_ARRAY_UTILS_H_
+
+#include "base/locks.h"
+#include "class_root-inl.h"
+#include "handle_scope-inl.h"
+#include "mirror/object_array-alloc-inl.h"
+#include "mirror/string.h"
+
+namespace art {
+
+namespace detail {
+
+inline const char* GetStringCStr(const char* str) { return str; }
+inline const char* GetStringCStr(const std::string& str) { return str.c_str(); }
+
+} // namespace detail
+
+template <typename Container>
+ObjPtr<mirror::ObjectArray<mirror::String>> CreateStringArray(
+ Thread* self, size_t size, const Container& entries) REQUIRES_SHARED(Locks::mutator_lock_) {
+ StackHandleScope<1u> hs(self);
+ Handle<mirror::ObjectArray<mirror::String>> array = hs.NewHandle(
+ mirror::ObjectArray<mirror::String>::Alloc(
+ self, GetClassRoot<mirror::ObjectArray<mirror::String>>(), size));
+ if (array == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ // Note: If the container's iterator returns a `std::string` by value, the `auto&&`
+ // binds as a const reference and extends the lifetime of the temporary object.
+ size_t pos = 0u;
+ for (auto&& entry : entries) {
+ ObjPtr<mirror::String> oentry =
+ mirror::String::AllocFromModifiedUtf8(self, detail::GetStringCStr(entry));
+ if (oentry == nullptr) {
+ DCHECK(self->IsExceptionPending());
+ return nullptr;
+ }
+ // We're initializing a newly allocated array object, so we do not need to record that under
+ // a transaction. If the transaction is aborted, the whole object shall be unreachable.
+ DCHECK_LT(pos, size);
+ array->SetWithoutChecks</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(
+ pos, oentry);
+ ++pos;
+ }
+ DCHECK_EQ(pos, size);
+ return array.Get();
+}
+
+template <typename Container>
+ObjPtr<mirror::ObjectArray<mirror::String>> CreateStringArray(
+ Thread* self, const Container& entries) REQUIRES_SHARED(Locks::mutator_lock_) {
+ return CreateStringArray(self, entries.size(), entries);
+}
+
+inline ObjPtr<mirror::ObjectArray<mirror::String>> CreateStringArray(
+ Thread* self, std::initializer_list<const char*> entries)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return CreateStringArray<std::initializer_list<const char*>>(self, entries);
+}
+
+} // namespace art
+
+#endif // ART_RUNTIME_NATIVE_STRING_ARRAY_UTILS_H_
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index e9c5af0..8a203ce 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -34,7 +34,7 @@
#include "art_field-inl.h"
#include "native_util.h"
#include "scoped_fast_native_object_access-inl.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -69,7 +69,7 @@
ObjPtr<mirror::Object> expectedValue = soa.Decode<mirror::Object>(javaExpectedValue);
ObjPtr<mirror::Object> newValue = soa.Decode<mirror::Object>(javaNewValue);
// JNI must use non transactional mode.
- if (kUseReadBarrier) {
+ if (gUseReadBarrier) {
// Need to make sure the reference stored in the field is a to-space one before attempting the
// CAS or the CAS could fail incorrectly.
// Note that the read barrier load does NOT need to be volatile.
@@ -521,12 +521,14 @@
static void Unsafe_unpark(JNIEnv* env, jobject, jobject jthread) {
art::ScopedFastNativeObjectAccess soa(env);
- if (jthread == nullptr || !env->IsInstanceOf(jthread, WellKnownClasses::java_lang_Thread)) {
+ ObjPtr<mirror::Object> mirror_thread = soa.Decode<mirror::Object>(jthread);
+ if (mirror_thread == nullptr ||
+ !mirror_thread->InstanceOf(WellKnownClasses::java_lang_Thread.Get())) {
ThrowIllegalArgumentException("Argument to unpark() was not a Thread");
return;
}
art::MutexLock mu(soa.Self(), *art::Locks::thread_list_lock_);
- art::Thread* thread = art::Thread::FromManagedThread(soa, jthread);
+ art::Thread* thread = art::Thread::FromManagedThread(soa, mirror_thread);
if (thread != nullptr) {
thread->Unpark();
} else {
@@ -534,10 +536,9 @@
// or the thread has already terminated. Setting the field to true will be
// respected when the thread does start, and is harmless if the thread has
// already terminated.
- ArtField* unparked =
- jni::DecodeArtField(WellKnownClasses::java_lang_Thread_unparkedBeforeStart);
+ ArtField* unparked = WellKnownClasses::java_lang_Thread_unparkedBeforeStart;
// JNI must use non transactional mode.
- unparked->SetBoolean<false>(soa.Decode<mirror::Object>(jthread), JNI_TRUE);
+ unparked->SetBoolean<false>(mirror_thread, JNI_TRUE);
}
}
diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc
index b7c3665..be41a68 100644
--- a/runtime/native_stack_dump.cc
+++ b/runtime/native_stack_dump.cc
@@ -18,14 +18,14 @@
#include <memory>
#include <ostream>
+#include <string_view>
#include <stdio.h>
#include "art_method.h"
// For DumpNativeStack.
-#include <backtrace/Backtrace.h>
-#include <backtrace/BacktraceMap.h>
+#include <unwindstack/AndroidUnwinder.h>
#if defined(__linux__)
@@ -69,13 +69,16 @@
static constexpr bool kUseAddr2line = !kIsTargetBuild;
std::string FindAddr2line() {
-#ifdef ART_CLANG_PATH
+#if !defined(ART_TARGET) && !defined(ART_CLANG_PATH)
+ #error "ART_CLANG_PATH must be defined on host build"
+#endif
+#if defined(ART_CLANG_PATH)
const char* env_value = getenv("ANDROID_BUILD_TOP");
if (env_value != nullptr) {
return std::string(env_value) + "/" + ART_CLANG_PATH + "/bin/llvm-addr2line";
}
#endif
- return std::string("/usr/bin/addr2line");
+ return std::string("llvm-addr2line");
}
ALWAYS_INLINE
@@ -298,50 +301,51 @@
}
}
-static bool PcIsWithinQuickCode(ArtMethod* method, uintptr_t pc) NO_THREAD_SAFETY_ANALYSIS {
- const void* entry_point = method->GetEntryPointFromQuickCompiledCode();
- if (entry_point == nullptr) {
- return pc == 0;
+// Remove method parameters by finding matching top-level parenthesis and removing them.
+// Since functions can be defined inside functions, this can remove multiple substrings.
+std::string StripParameters(std::string name) {
+ size_t end = name.size();
+ int nesting = 0;
+ for (ssize_t i = name.size() - 1; i > 0; i--) {
+ if (name[i] == ')' && nesting++ == 0) {
+ end = i + 1;
+ }
+ if (name[i] == '(' && --nesting == 0) {
+ name = name.erase(i, end - i);
+ }
}
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- if (class_linker->IsQuickGenericJniStub(entry_point) ||
- class_linker->IsQuickResolutionStub(entry_point) ||
- class_linker->IsQuickToInterpreterBridge(entry_point)) {
- return false;
- }
- // The backtrace library might have heuristically subracted instruction
- // size from the pc, to pretend the pc is at the calling instruction.
- if (reinterpret_cast<uintptr_t>(GetQuickInstrumentationExitPc()) - pc <= 4) {
- return false;
- }
- uintptr_t code = reinterpret_cast<uintptr_t>(EntryPointToCodePointer(entry_point));
- uintptr_t code_size = reinterpret_cast<const OatQuickMethodHeader*>(code)[-1].GetCodeSize();
- return code <= pc && pc <= (code + code_size);
+ return name;
}
void DumpNativeStack(std::ostream& os,
pid_t tid,
- BacktraceMap* existing_map,
+ const char* prefix,
+ ArtMethod* current_method,
+ void* ucontext_ptr,
+ bool skip_frames) {
+ unwindstack::AndroidLocalUnwinder unwinder;
+ DumpNativeStack(os, unwinder, tid, prefix, current_method, ucontext_ptr, skip_frames);
+}
+
+void DumpNativeStack(std::ostream& os,
+ unwindstack::AndroidLocalUnwinder& unwinder,
+ pid_t tid,
const char* prefix,
ArtMethod* current_method,
void* ucontext_ptr,
bool skip_frames) {
// Historical note: This was disabled when running under Valgrind (b/18119146).
- BacktraceMap* map = existing_map;
- std::unique_ptr<BacktraceMap> tmp_map;
- if (map == nullptr) {
- tmp_map.reset(BacktraceMap::Create(getpid()));
- map = tmp_map.get();
+ unwindstack::AndroidUnwinderData data(!skip_frames /*show_all_frames*/);
+ bool unwind_ret;
+ if (ucontext_ptr != nullptr) {
+ unwind_ret = unwinder.Unwind(ucontext_ptr, data);
+ } else {
+ unwind_ret = unwinder.Unwind(tid, data);
}
- std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid, map));
- backtrace->SetSkipFrames(skip_frames);
- if (!backtrace->Unwind(0, reinterpret_cast<ucontext*>(ucontext_ptr))) {
- os << prefix << "(backtrace::Unwind failed for thread " << tid
- << ": " << backtrace->GetErrorString(backtrace->GetError()) << ")" << std::endl;
- return;
- } else if (backtrace->NumFrames() == 0) {
- os << prefix << "(no native stack frames for thread " << tid << ")" << std::endl;
+ if (!unwind_ret) {
+ os << prefix << "(Unwind failed for thread " << tid << ": "
+ << data.GetErrorString() << ")" << std::endl;
return;
}
@@ -356,9 +360,9 @@
}
std::unique_ptr<Addr2linePipe> addr2line_state;
-
- for (Backtrace::const_iterator it = backtrace->begin();
- it != backtrace->end(); ++it) {
+ data.DemangleFunctionNames();
+ bool holds_mutator_lock = Locks::mutator_lock_->IsSharedHeld(Thread::Current());
+ for (const unwindstack::FrameData& frame : data.frames) {
// We produce output like this:
// ] #00 pc 000075bb8 /system/lib/libc.so (unwind_backtrace_thread+536)
// In order for parsing tools to continue to function, the stack dump
@@ -367,53 +371,57 @@
// The parsers require a single space before and after pc, and two spaces
// after the <RELATIVE_ADDR>. There can be any prefix data before the
// #XX. <RELATIVE_ADDR> has to be a hex number but with no 0x prefix.
- os << prefix << StringPrintf("#%02zu pc ", it->num);
+ os << prefix << StringPrintf("#%02zu pc ", frame.num);
bool try_addr2line = false;
- if (!BacktraceMap::IsValid(it->map)) {
- os << StringPrintf(Is64BitInstructionSet(kRuntimeISA) ? "%016" PRIx64 " ???"
- : "%08" PRIx64 " ???",
- it->pc);
+ if (frame.map_info == nullptr) {
+ os << StringPrintf("%08" PRIx64 " ???", frame.pc);
} else {
- os << StringPrintf(Is64BitInstructionSet(kRuntimeISA) ? "%016" PRIx64 " "
- : "%08" PRIx64 " ",
- it->rel_pc);
- if (it->map.name.empty()) {
- os << StringPrintf("<anonymous:%" PRIx64 ">", it->map.start);
+ os << StringPrintf("%08" PRIx64 " ", frame.rel_pc);
+ const std::shared_ptr<unwindstack::MapInfo>& map_info = frame.map_info;
+ if (map_info->name().empty()) {
+ os << StringPrintf("<anonymous:%" PRIx64 ">", map_info->start());
} else {
- os << it->map.name;
+ os << map_info->name().c_str();
}
- if (it->map.offset != 0) {
- os << StringPrintf(" (offset %" PRIx64 ")", it->map.offset);
+ if (map_info->elf_start_offset() != 0) {
+ os << StringPrintf(" (offset %" PRIx64 ")", map_info->elf_start_offset());
}
os << " (";
- if (!it->func_name.empty()) {
- os << it->func_name;
- if (it->func_offset != 0) {
- os << "+" << it->func_offset;
+ if (!frame.function_name.empty()) {
+ // Remove parameters from the printed function name to improve signal/noise in the logs.
+ // Also, ANRs are often trimmed, so printing less means we get more useful data out.
+ // We can still symbolize the function based on the PC and build-id (including inlining).
+ os << StripParameters(frame.function_name.c_str());
+ if (frame.function_offset != 0) {
+ os << "+" << frame.function_offset;
}
// Functions found using the gdb jit interface will be in an empty
// map that cannot be found using addr2line.
- if (!it->map.name.empty()) {
+ if (!map_info->name().empty()) {
try_addr2line = true;
}
- } else if (current_method != nullptr &&
- Locks::mutator_lock_->IsSharedHeld(Thread::Current()) &&
- PcIsWithinQuickCode(current_method, it->pc)) {
- const void* start_of_code = current_method->GetEntryPointFromQuickCompiledCode();
- os << current_method->JniLongName() << "+"
- << (it->pc - reinterpret_cast<uint64_t>(start_of_code));
+ } else if (current_method != nullptr && holds_mutator_lock) {
+ const OatQuickMethodHeader* header = current_method->GetOatQuickMethodHeader(frame.pc);
+ if (header != nullptr) {
+ const void* start_of_code = header->GetCode();
+ os << current_method->JniLongName() << "+"
+ << (frame.pc - reinterpret_cast<uint64_t>(start_of_code));
+ } else {
+ os << "???";
+ }
} else {
os << "???";
}
os << ")";
- std::string build_id = map->GetBuildId(it->pc);
+ std::string build_id = map_info->GetPrintableBuildID();
if (!build_id.empty()) {
os << " (BuildId: " << build_id << ")";
}
}
os << std::endl;
if (try_addr2line && use_addr2line) {
- Addr2line(it->map.name, it->rel_pc, os, prefix, &addr2line_state);
+ // Guaranteed that map_info is not nullptr and name is non-empty.
+ Addr2line(frame.map_info->name(), frame.rel_pc, os, prefix, &addr2line_state);
}
}
@@ -426,7 +434,15 @@
void DumpNativeStack(std::ostream& os ATTRIBUTE_UNUSED,
pid_t tid ATTRIBUTE_UNUSED,
- BacktraceMap* existing_map ATTRIBUTE_UNUSED,
+ const char* prefix ATTRIBUTE_UNUSED,
+ ArtMethod* current_method ATTRIBUTE_UNUSED,
+ void* ucontext_ptr ATTRIBUTE_UNUSED,
+ bool skip_frames ATTRIBUTE_UNUSED) {
+}
+
+void DumpNativeStack(std::ostream& os ATTRIBUTE_UNUSED,
+ unwindstack::AndroidLocalUnwinder& existing_map ATTRIBUTE_UNUSED,
+ pid_t tid ATTRIBUTE_UNUSED,
const char* prefix ATTRIBUTE_UNUSED,
ArtMethod* current_method ATTRIBUTE_UNUSED,
void* ucontext_ptr ATTRIBUTE_UNUSED,
diff --git a/runtime/native_stack_dump.h b/runtime/native_stack_dump.h
index 4d4b36b..86a8ce2 100644
--- a/runtime/native_stack_dump.h
+++ b/runtime/native_stack_dump.h
@@ -23,16 +23,30 @@
#include "base/macros.h"
-class BacktraceMap;
+namespace unwindstack {
+class AndroidLocalUnwinder;
+} // namespace unwindstack
namespace art {
class ArtMethod;
+// Remove method parameters by finding matching top-level parenthesis and removing them.
+// Since functions can be defined inside functions, this can remove multiple substrings.
+std::string StripParameters(std::string name);
+
// Dumps the native stack for thread 'tid' to 'os'.
void DumpNativeStack(std::ostream& os,
pid_t tid,
- BacktraceMap* map = nullptr,
+ const char* prefix = "",
+ ArtMethod* current_method = nullptr,
+ void* ucontext = nullptr,
+ bool skip_frames = true)
+ NO_THREAD_SAFETY_ANALYSIS;
+
+void DumpNativeStack(std::ostream& os,
+ unwindstack::AndroidLocalUnwinder& unwinder,
+ pid_t tid,
const char* prefix = "",
ArtMethod* current_method = nullptr,
void* ucontext = nullptr,
diff --git a/runtime/native_stack_dump_test.cc b/runtime/native_stack_dump_test.cc
new file mode 100644
index 0000000..4446495
--- /dev/null
+++ b/runtime/native_stack_dump_test.cc
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 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 "native_stack_dump.h"
+
+#include <gtest/gtest.h>
+
+namespace art {
+
+TEST(StripParametersTest, ValidInput) {
+ EXPECT_EQ(StripParameters("foo(int)"), "foo");
+ EXPECT_EQ(StripParameters("foo(int, std::string)"), "foo");
+ EXPECT_EQ(StripParameters("foo(int) const"), "foo const");
+ EXPECT_EQ(StripParameters("foo(int)::bar(int)"), "foo::bar");
+ EXPECT_EQ(StripParameters("foo<int>(int)"), "foo<int>");
+}
+
+TEST(StripParametersTest, InvalidInput) {
+ EXPECT_EQ(StripParameters("foo(int?"), "foo(int?");
+ EXPECT_EQ(StripParameters("foo?int)"), "foo?int)");
+ EXPECT_EQ(StripParameters("(foo(int)"), "(foo");
+ EXPECT_EQ(StripParameters(")foo(int)"), ")foo");
+ EXPECT_EQ(StripParameters("foo(((int)))"), "foo");
+}
+
+} // namespace art
diff --git a/runtime/noop_compiler_callbacks.h b/runtime/noop_compiler_callbacks.h
index f415831..aed0014 100644
--- a/runtime/noop_compiler_callbacks.h
+++ b/runtime/noop_compiler_callbacks.h
@@ -27,6 +27,7 @@
~NoopCompilerCallbacks() {}
void AddUncompilableMethod(MethodReference ref ATTRIBUTE_UNUSED) override {}
+ void AddUncompilableClass(ClassReference ref ATTRIBUTE_UNUSED) override {}
void ClassRejected(ClassReference ref ATTRIBUTE_UNUSED) override {}
verifier::VerifierDeps* GetVerifierDeps() const override { return nullptr; }
diff --git a/runtime/nterp_helpers.cc b/runtime/nterp_helpers.cc
index 12afa3a..fb2046c 100644
--- a/runtime/nterp_helpers.cc
+++ b/runtime/nterp_helpers.cc
@@ -120,9 +120,7 @@
static_cast<size_t>(InstructionSetPointerSize(isa));
}
-static uint16_t GetNumberOfOutRegs(ArtMethod* method, InstructionSet isa)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- CodeItemDataAccessor accessor(method->DexInstructionData());
+static uint16_t GetNumberOfOutRegs(const CodeItemDataAccessor& accessor, InstructionSet isa) {
uint16_t out_regs = accessor.OutsSize();
switch (isa) {
case InstructionSet::kX86: {
@@ -136,14 +134,23 @@
return out_regs;
}
-size_t NterpGetFrameSize(ArtMethod* method, InstructionSet isa) {
+static uint16_t GetNumberOfOutRegs(ArtMethod* method, InstructionSet isa)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ CodeItemDataAccessor accessor(method->DexInstructionData());
+ return GetNumberOfOutRegs(accessor, isa);
+}
+
+// Note: There may be two pieces of alignment but there is no need to align
+// out args to `kPointerSize` separately before aligning to kStackAlignment.
+// This allows using the size without padding for the maximum frame size check
+// in `CanMethodUseNterp()`.
+static size_t NterpGetFrameSizeWithoutPadding(ArtMethod* method, InstructionSet isa)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
CodeItemDataAccessor accessor(method->DexInstructionData());
const uint16_t num_regs = accessor.RegistersSize();
- const uint16_t out_regs = GetNumberOfOutRegs(method, isa);
+ const uint16_t out_regs = GetNumberOfOutRegs(accessor, isa);
size_t pointer_size = static_cast<size_t>(InstructionSetPointerSize(isa));
- // Note: There may be two pieces of alignment but there is no need to align
- // out args to `kPointerSize` separately before aligning to kStackAlignment.
DCHECK(IsAlignedParam(kStackAlignment, pointer_size));
DCHECK(IsAlignedParam(NterpGetFrameEntrySize(isa), pointer_size));
DCHECK(IsAlignedParam(kVRegSize * 2, pointer_size));
@@ -154,7 +161,13 @@
pointer_size + // saved dex pc
(out_regs * kVRegSize) + // out arguments
pointer_size; // method
- return RoundUp(frame_size, kStackAlignment);
+ return frame_size;
+}
+
+// The frame size nterp will use for the given method.
+static inline size_t NterpGetFrameSize(ArtMethod* method, InstructionSet isa)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ return RoundUp(NterpGetFrameSizeWithoutPadding(method, isa), kStackAlignment);
}
QuickMethodFrameInfo NterpFrameInfo(ArtMethod** frame) {
@@ -162,7 +175,7 @@
RuntimeCalleeSaveFrame::GetCoreSpills(CalleeSaveType::kSaveAllCalleeSaves);
uint32_t fp_spills =
RuntimeCalleeSaveFrame::GetFpSpills(CalleeSaveType::kSaveAllCalleeSaves);
- return QuickMethodFrameInfo(NterpGetFrameSize(*frame), core_spills, fp_spills);
+ return QuickMethodFrameInfo(NterpGetFrameSize(*frame, kRuntimeISA), core_spills, fp_spills);
}
uintptr_t NterpGetRegistersArray(ArtMethod** frame) {
@@ -206,13 +219,20 @@
}
bool CanMethodUseNterp(ArtMethod* method, InstructionSet isa) {
- return !method->IsNative() &&
- method->IsInvokable() &&
- !method->MustCountLocks() &&
+ uint32_t access_flags = method->GetAccessFlags();
+ if (ArtMethod::IsNative(access_flags) ||
+ !ArtMethod::IsInvokable(access_flags) ||
+ ArtMethod::MustCountLocks(access_flags) ||
// Proxy methods do not go through the JIT like other methods, so we don't
// run them with nterp.
- !method->IsProxyMethod() &&
- NterpGetFrameSize(method, isa) <= interpreter::kNterpMaxFrame;
+ method->IsProxyMethod()) {
+ return false;
+ }
+ // There is no need to add the alignment padding size for comparison with aligned limit.
+ size_t frame_size_without_padding = NterpGetFrameSizeWithoutPadding(method, isa);
+ DCHECK_EQ(NterpGetFrameSize(method, isa), RoundUp(frame_size_without_padding, kStackAlignment));
+ static_assert(IsAligned<kStackAlignment>(interpreter::kNterpMaxFrame));
+ return frame_size_without_padding <= interpreter::kNterpMaxFrame;
}
} // namespace art
diff --git a/runtime/nterp_helpers.h b/runtime/nterp_helpers.h
index 0f9e758..236059b 100644
--- a/runtime/nterp_helpers.h
+++ b/runtime/nterp_helpers.h
@@ -24,12 +24,6 @@
class ArtMethod;
/**
- * The frame size nterp will use for the given method.
- */
-size_t NterpGetFrameSize(ArtMethod* method, InstructionSet isa = kRuntimeISA)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
-/**
* Returns the QuickMethodFrameInfo of the given frame corresponding to the
* given method.
*/
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 64ed14a..42bb7d4 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -29,9 +29,6 @@
using android::base::StringPrintf;
-constexpr const char OatHeader::kTrueValue[];
-constexpr const char OatHeader::kFalseValue[];
-
static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) {
size_t estimate = 0U;
if (variable_data != nullptr) {
diff --git a/runtime/oat.h b/runtime/oat.h
index 462d41c..20d32cb 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@
class PACKED(4) OatHeader {
public:
static constexpr std::array<uint8_t, 4> kOatMagic { { 'o', 'a', 't', '\n' } };
- // Last oat version changed reason: Revert^4 "bss support for inlining BCP into non-BCP".
- static constexpr std::array<uint8_t, 4> kOatVersion { { '2', '2', '5', '\0' } };
+ // Last oat version changed reason: Add a new QuickEntryPoint for a String constructor.
+ static constexpr std::array<uint8_t, 4> kOatVersion { { '2', '2', '9', '\0' } };
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
static constexpr const char* kDebuggableKey = "debuggable";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 63778c7..20ac77d 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -445,7 +445,7 @@
UNLIKELY(oat_file->Size() - index_bss_mapping_offset <
IndexBssMapping::ComputeSize(index_bss_mapping->size())))) {
*error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zu for '%s' with unaligned or "
- " truncated %s bss mapping, offset %u of %zu, length %zu",
+ "truncated %s bss mapping, offset %u of %zu, length %zu",
oat_file->GetLocation().c_str(),
dex_file_index,
dex_file_location.c_str(),
@@ -465,15 +465,25 @@
const VdexFile* vdex_file,
const uint8_t** type_lookup_table_data,
std::string* error_msg) {
- if (type_lookup_table_start == nullptr ||
- reinterpret_cast<const uint32_t*>(type_lookup_table_start)[0] == 0) {
+ if (type_lookup_table_start == nullptr) {
*type_lookup_table_data = nullptr;
return true;
}
- *type_lookup_table_data = type_lookup_table_start + sizeof(uint32_t);
- size_t expected_table_size = TypeLookupTable::RawDataLength(header.class_defs_size_);
+ if (UNLIKELY(!vdex_file->Contains(type_lookup_table_start, sizeof(uint32_t)))) {
+ *error_msg =
+ StringPrintf("In vdex file '%s' found invalid type lookup table start %p of size %zu "
+ "not in [%p, %p]",
+ vdex_file->GetName().c_str(),
+ type_lookup_table_start,
+ sizeof(uint32_t),
+ vdex_file->Begin(),
+ vdex_file->End());
+ return false;
+ }
+
size_t found_size = reinterpret_cast<const uint32_t*>(type_lookup_table_start)[0];
+ size_t expected_table_size = TypeLookupTable::RawDataLength(header.class_defs_size_);
if (UNLIKELY(found_size != expected_table_size)) {
*error_msg =
StringPrintf("In vdex file '%s' unexpected type lookup table size: found %zu, expected %zu",
@@ -482,20 +492,20 @@
expected_table_size);
return false;
}
- if (UNLIKELY(!vdex_file->Contains(*type_lookup_table_data))) {
+
+ if (found_size == 0) {
+ *type_lookup_table_data = nullptr;
+ return true;
+ }
+
+ *type_lookup_table_data = type_lookup_table_start + sizeof(uint32_t);
+ if (UNLIKELY(!vdex_file->Contains(*type_lookup_table_data, found_size))) {
*error_msg =
- StringPrintf("In vdex file '%s' found invalid type lookup table pointer %p not in [%p, %p]",
+ StringPrintf("In vdex file '%s' found invalid type lookup table data %p of size %zu "
+ "not in [%p, %p]",
vdex_file->GetName().c_str(),
type_lookup_table_data,
- vdex_file->Begin(),
- vdex_file->End());
- return false;
- }
- if (UNLIKELY(!vdex_file->Contains(*type_lookup_table_data + expected_table_size - 1))) {
- *error_msg =
- StringPrintf("In vdex file '%s' found overflowing type lookup table %p not in [%p, %p]",
- vdex_file->GetName().c_str(),
- type_lookup_table_data + expected_table_size,
+ found_size,
vdex_file->Begin(),
vdex_file->End());
return false;
@@ -514,6 +524,16 @@
uint32_t i = 0;
const uint8_t* type_lookup_table_start = nullptr;
for (const DexFile* dex_file : dex_files) {
+ // Defensively verify external dex file checksum. `OatFileAssistant`
+ // expects this check to happen during oat file setup when the oat file
+ // does not contain dex code.
+ if (dex_file->GetLocationChecksum() != vdex_->GetLocationChecksum(i)) {
+ *error_msg = StringPrintf("Dex checksum does not match for %s, dex has %d, vdex has %d",
+ dex_file->GetLocation().c_str(),
+ dex_file->GetLocationChecksum(),
+ vdex_->GetLocationChecksum(i));
+ return false;
+ }
std::string dex_location = dex_file->GetLocation();
std::string canonical_location = DexFileLoader::GetDexCanonicalLocation(dex_location.c_str());
@@ -828,7 +848,9 @@
external_dex_files_.push_back(std::move(dex_file));
}
}
- // Defensively verify external dex file checksum.
+ // Defensively verify external dex file checksum. `OatFileAssistant`
+ // expects this check to happen during oat file setup when the oat file
+ // does not contain dex code.
if (dex_file_checksum != external_dex_files_[i]->GetLocationChecksum()) {
*error_msg = StringPrintf("In oat file '%s', dex file checksum 0x%08x does not match"
" checksum 0x%08x of external dex file '%s'",
@@ -1719,21 +1741,25 @@
for (const uint8_t* dex_file_start = vdex_file->GetNextDexFileData(nullptr, i);
dex_file_start != nullptr;
dex_file_start = vdex_file->GetNextDexFileData(dex_file_start, ++i)) {
- const DexFile::Header* header = reinterpret_cast<const DexFile::Header*>(dex_file_start);
- if (UNLIKELY(!vdex_file->Contains(dex_file_start))) {
+ if (UNLIKELY(!vdex_file->Contains(dex_file_start, sizeof(DexFile::Header)))) {
*error_msg =
- StringPrintf("In vdex file '%s' found invalid dex file pointer %p not in [%p, %p]",
+ StringPrintf("In vdex file '%s' found invalid dex header %p of size %zu "
+ "not in [%p, %p]",
dex_location.c_str(),
dex_file_start,
+ sizeof(DexFile::Header),
vdex_file->Begin(),
vdex_file->End());
return nullptr;
}
- if (UNLIKELY(!vdex_file->Contains(dex_file_start + header->file_size_ - 1))) {
+ const DexFile::Header* header = reinterpret_cast<const DexFile::Header*>(dex_file_start);
+ if (UNLIKELY(!vdex_file->Contains(dex_file_start, header->file_size_))) {
*error_msg =
- StringPrintf("In vdex file '%s' found overflowing dex file %p not in [%p, %p]",
+ StringPrintf("In vdex file '%s' found invalid dex file pointer %p of size %d "
+ "not in [%p, %p]",
dex_location.c_str(),
- dex_file_start + header->file_size_,
+ dex_file_start,
+ header->file_size_,
vdex_file->Begin(),
vdex_file->End());
return nullptr;
@@ -1815,7 +1841,7 @@
store.Put(OatHeader::kCompilerFilter, CompilerFilter::NameOfFilter(CompilerFilter::kVerify));
store.Put(OatHeader::kCompilationReasonKey, "vdex");
store.Put(OatHeader::kConcurrentCopying,
- kUseReadBarrier ? OatHeader::kTrueValue : OatHeader::kFalseValue);
+ gUseReadBarrier ? OatHeader::kTrueValue : OatHeader::kFalseValue);
oat_header_.reset(OatHeader::Create(kRuntimeISA,
isa_features.get(),
number_of_dex_files,
@@ -1907,17 +1933,6 @@
reservation,
error_msg);
if (with_dlopen != nullptr) {
- Runtime* runtime = Runtime::Current();
- // The runtime might not be available at this point if we're running
- // dex2oat or oatdump.
- if (runtime != nullptr) {
- size_t madvise_size_limit = runtime->GetMadviseWillNeedSizeOdex();
- Runtime::MadviseFileForRange(madvise_size_limit,
- with_dlopen->Size(),
- with_dlopen->Begin(),
- with_dlopen->End(),
- oat_location);
- }
return with_dlopen;
}
if (kPrintDlOpenErrorMessage) {
@@ -2252,7 +2267,7 @@
return OatFile::OatClass(oat_file_,
ClassStatus::kNotReady,
/* type= */ OatClassType::kNoneCompiled,
- /* bitmap_size= */ 0u,
+ /* num_methods= */ 0u,
/* bitmap_pointer= */ nullptr,
/* methods_pointer= */ nullptr);
}
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index c1b1acb..fdb4217 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -270,7 +270,7 @@
return OatClass(/* oat_file= */ nullptr,
ClassStatus::kErrorUnresolved,
OatClassType::kNoneCompiled,
- /* bitmap_size= */ 0,
+ /* num_methods= */ 0,
/* bitmap_pointer= */ nullptr,
/* methods_pointer= */ nullptr);
}
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 914d2dd..dd400f2 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -16,15 +16,19 @@
#include "oat_file_assistant.h"
-#include <sstream>
-
#include <sys/stat.h>
-#include "zlib.h"
+
+#include <memory>
+#include <optional>
+#include <sstream>
+#include <vector>
#include "android-base/file.h"
+#include "android-base/logging.h"
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
-
+#include "arch/instruction_set.h"
+#include "base/array_ref.h"
#include "base/compiler_filter.h"
#include "base/file_utils.h"
#include "base/logging.h" // For VLOG.
@@ -44,13 +48,16 @@
#include "gc/space/image_space.h"
#include "image.h"
#include "oat.h"
+#include "oat_file_assistant_context.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "vdex_file.h"
+#include "zlib.h"
namespace art {
-using android::base::StringPrintf;
+using ::android::base::ConsumePrefix;
+using ::android::base::StringPrintf;
static constexpr const char* kAnonymousDexPrefix = "Anonymous-DexFile@";
static constexpr const char* kVdexExtension = ".vdex";
@@ -82,22 +89,24 @@
const InstructionSet isa,
ClassLoaderContext* context,
bool load_executable,
- bool only_load_trusted_executable)
+ bool only_load_trusted_executable,
+ OatFileAssistantContext* ofa_context)
: OatFileAssistant(dex_location,
isa,
context,
load_executable,
only_load_trusted_executable,
- /*vdex_fd=*/ -1,
- /*oat_fd=*/ -1,
- /*zip_fd=*/ -1) {}
-
+ ofa_context,
+ /*vdex_fd=*/-1,
+ /*oat_fd=*/-1,
+ /*zip_fd=*/-1) {}
OatFileAssistant::OatFileAssistant(const char* dex_location,
const InstructionSet isa,
ClassLoaderContext* context,
bool load_executable,
bool only_load_trusted_executable,
+ OatFileAssistantContext* ofa_context,
int vdex_fd,
int oat_fd,
int zip_fd)
@@ -105,12 +114,12 @@
isa_(isa),
load_executable_(load_executable),
only_load_trusted_executable_(only_load_trusted_executable),
- odex_(this, /*is_oat_location=*/ false),
- oat_(this, /*is_oat_location=*/ true),
- vdex_for_odex_(this, /*is_oat_location=*/ false),
- vdex_for_oat_(this, /*is_oat_location=*/ true),
- dm_for_odex_(this, /*is_oat_location=*/ false),
- dm_for_oat_(this, /*is_oat_location=*/ true),
+ odex_(this, /*is_oat_location=*/false),
+ oat_(this, /*is_oat_location=*/true),
+ vdex_for_odex_(this, /*is_oat_location=*/false),
+ vdex_for_oat_(this, /*is_oat_location=*/true),
+ dm_for_odex_(this, /*is_oat_location=*/false),
+ dm_for_oat_(this, /*is_oat_location=*/true),
zip_fd_(zip_fd) {
CHECK(dex_location != nullptr) << "OatFileAssistant: null dex location";
CHECK_IMPLIES(load_executable, context != nullptr) << "Loading executable without a context";
@@ -127,12 +136,33 @@
dex_location_.assign(dex_location);
+ Runtime* runtime = Runtime::Current();
+
+ if (load_executable_ && runtime == nullptr) {
+ LOG(WARNING) << "OatFileAssistant: Load executable specified, "
+ << "but no active runtime is found. Will not attempt to load executable.";
+ load_executable_ = false;
+ }
+
if (load_executable_ && isa != kRuntimeISA) {
LOG(WARNING) << "OatFileAssistant: Load executable specified, "
<< "but isa is not kRuntimeISA. Will not attempt to load executable.";
load_executable_ = false;
}
+ if (ofa_context == nullptr) {
+ CHECK(runtime != nullptr) << "runtime_options is not provided, and no active runtime is found.";
+ ofa_context_ = std::make_unique<OatFileAssistantContext>(runtime);
+ } else {
+ ofa_context_ = ofa_context;
+ }
+
+ if (runtime == nullptr) {
+ // We need `MemMap` for mapping files. We don't have to initialize it when there is a runtime
+ // because the runtime initializes it.
+ MemMap::Init();
+ }
+
// Get the odex filename.
std::string error_msg;
std::string odex_file_name;
@@ -159,7 +189,11 @@
if (!UseFdToReadFiles()) {
// Get the oat filename.
std::string oat_file_name;
- if (DexLocationToOatFilename(dex_location_, isa_, &oat_file_name, &error_msg)) {
+ if (DexLocationToOatFilename(dex_location_,
+ isa_,
+ GetRuntimeOptions().deny_art_apex_data_files,
+ &oat_file_name,
+ &error_msg)) {
oat_.Reset(oat_file_name, /*use_fd=*/ false);
std::string vdex_file_name = GetVdexFilename(oat_file_name);
vdex_for_oat_.Reset(vdex_file_name, UseFdToReadFiles(), zip_fd, vdex_fd, oat_fd);
@@ -190,6 +224,51 @@
}
}
+std::unique_ptr<OatFileAssistant> OatFileAssistant::Create(
+ const std::string& filename,
+ const std::string& isa_str,
+ const std::optional<std::string>& context_str,
+ bool load_executable,
+ bool only_load_trusted_executable,
+ OatFileAssistantContext* ofa_context,
+ /*out*/ std::unique_ptr<ClassLoaderContext>* context,
+ /*out*/ std::string* error_msg) {
+ InstructionSet isa = GetInstructionSetFromString(isa_str.c_str());
+ if (isa == InstructionSet::kNone) {
+ *error_msg = StringPrintf("Instruction set '%s' is invalid", isa_str.c_str());
+ return nullptr;
+ }
+
+ std::unique_ptr<ClassLoaderContext> tmp_context = nullptr;
+ if (context_str.has_value()) {
+ tmp_context = ClassLoaderContext::Create(context_str->c_str());
+ if (tmp_context == nullptr) {
+ *error_msg = StringPrintf("Class loader context '%s' is invalid", context_str->c_str());
+ return nullptr;
+ }
+
+ if (!tmp_context->OpenDexFiles(android::base::Dirname(filename.c_str()),
+ /*context_fds=*/{},
+ /*only_read_checksums=*/true)) {
+ *error_msg =
+ StringPrintf("Failed to load class loader context files for '%s' with context '%s'",
+ filename.c_str(),
+ context_str->c_str());
+ return nullptr;
+ }
+ }
+
+ auto assistant = std::make_unique<OatFileAssistant>(filename.c_str(),
+ isa,
+ tmp_context.get(),
+ load_executable,
+ only_load_trusted_executable,
+ ofa_context);
+
+ *context = std::move(tmp_context);
+ return assistant;
+}
+
bool OatFileAssistant::UseFdToReadFiles() {
return zip_fd_ >= 0;
}
@@ -199,11 +278,9 @@
// specified by the user. This is okay, because the boot class path should
// be the same for all ISAs.
// TODO: Can we verify the boot class path is the same for all ISAs?
- Runtime* runtime = Runtime::Current();
- ClassLinker* class_linker = runtime->GetClassLinker();
- const auto& boot_class_path = class_linker->GetBootClassPath();
- for (size_t i = 0; i < boot_class_path.size(); i++) {
- if (boot_class_path[i]->GetLocation() == dex_location_) {
+ for (const std::string& boot_class_path_location :
+ GetRuntimeOptions().boot_class_path_locations) {
+ if (boot_class_path_location == dex_location_) {
VLOG(oat) << "Dex location " << dex_location_ << " is in boot class path";
return true;
}
@@ -211,19 +288,61 @@
return false;
}
-int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target,
+OatFileAssistant::DexOptTrigger OatFileAssistant::GetDexOptTrigger(
+ CompilerFilter::Filter target_compiler_filter, bool profile_changed, bool downgrade) {
+ if (downgrade) {
+ // The caller's intention is to downgrade the compiler filter. We should only re-compile if the
+ // target compiler filter is worse than the current one.
+ return DexOptTrigger{.targetFilterIsWorse = true};
+ }
+
+ // This is the usual case. The caller's intention is to see if a better oat file can be generated.
+ DexOptTrigger dexopt_trigger{.targetFilterIsBetter = true, .primaryBootImageBecomesUsable = true};
+ if (profile_changed && CompilerFilter::DependsOnProfile(target_compiler_filter)) {
+ // Since the profile has been changed, we should re-compile even if the compilation does not
+ // make the compiler filter better.
+ dexopt_trigger.targetFilterIsSame = true;
+ }
+ return dexopt_trigger;
+}
+
+int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
bool profile_changed,
bool downgrade) {
OatFileInfo& info = GetBestInfo();
- DexOptNeeded dexopt_needed = info.GetDexOptNeeded(target,
- profile_changed,
- downgrade);
+ DexOptNeeded dexopt_needed = info.GetDexOptNeeded(
+ target_compiler_filter, GetDexOptTrigger(target_compiler_filter, profile_changed, downgrade));
+ if (dexopt_needed != kNoDexOptNeeded && (&info == &dm_for_oat_ || &info == &dm_for_odex_)) {
+ // The usable vdex file is in the DM file. This information cannot be encoded in the integer.
+ // Return kDex2OatFromScratch so that neither the vdex in the "oat" location nor the vdex in the
+ // "odex" location will be picked by installd.
+ return kDex2OatFromScratch;
+ }
if (info.IsOatLocation() || dexopt_needed == kDex2OatFromScratch) {
return dexopt_needed;
}
return -dexopt_needed;
}
+bool OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
+ DexOptTrigger dexopt_trigger,
+ /*out*/ DexOptStatus* dexopt_status) {
+ OatFileInfo& info = GetBestInfo();
+ DexOptNeeded dexopt_needed = info.GetDexOptNeeded(target_compiler_filter, dexopt_trigger);
+ if (info.IsUseable()) {
+ if (&info == &dm_for_oat_ || &info == &dm_for_odex_) {
+ dexopt_status->location_ = kLocationDm;
+ } else if (info.IsOatLocation()) {
+ dexopt_status->location_ = kLocationOat;
+ } else {
+ dexopt_status->location_ = kLocationOdex;
+ }
+ } else {
+ dexopt_status->location_ = kLocationNoneOrError;
+ }
+ return dexopt_needed != kNoDexOptNeeded;
+}
+
bool OatFileAssistant::IsUpToDate() {
return GetBestInfo().Status() == kOatUpToDate;
}
@@ -328,13 +447,13 @@
return true;
}
-bool OatFileAssistant::HasDexFiles() {
+std::optional<bool> OatFileAssistant::HasDexFiles(std::string* error_msg) {
ScopedTrace trace("HasDexFiles");
- // Ensure GetRequiredDexChecksums has been run so that
- // has_original_dex_files_ is initialized. We don't care about the result of
- // GetRequiredDexChecksums.
- GetRequiredDexChecksums();
- return has_original_dex_files_;
+ const std::vector<std::uint32_t>* checksums = GetRequiredDexChecksums(error_msg);
+ if (checksums == nullptr) {
+ return std::nullopt;
+ }
+ return !checksums->empty();
}
OatFileAssistant::OatStatus OatFileAssistant::OdexFileStatus() {
@@ -345,43 +464,18 @@
return oat_.Status();
}
-bool OatFileAssistant::DexChecksumUpToDate(const VdexFile& file, std::string* error_msg) {
- ScopedTrace trace("DexChecksumUpToDate(vdex)");
- const std::vector<uint32_t>* required_dex_checksums = GetRequiredDexChecksums();
- if (required_dex_checksums == nullptr) {
- LOG(WARNING) << "Required dex checksums not found. Assuming dex checksums are up to date.";
+bool OatFileAssistant::DexChecksumUpToDate(const OatFile& file, std::string* error_msg) {
+ if (!file.ContainsDexCode()) {
+ // We've already checked during oat file creation that the dex files loaded
+ // from external files have the same checksums as the ones in the vdex file.
return true;
}
-
- uint32_t number_of_dex_files = file.GetNumberOfDexFiles();
- if (required_dex_checksums->size() != number_of_dex_files) {
- *error_msg = StringPrintf("expected %zu dex files but found %u",
- required_dex_checksums->size(),
- number_of_dex_files);
+ ScopedTrace trace("DexChecksumUpToDate");
+ const std::vector<uint32_t>* required_dex_checksums = GetRequiredDexChecksums(error_msg);
+ if (required_dex_checksums == nullptr) {
return false;
}
-
- for (uint32_t i = 0; i < number_of_dex_files; i++) {
- uint32_t expected_checksum = (*required_dex_checksums)[i];
- uint32_t actual_checksum = file.GetLocationChecksum(i);
- if (expected_checksum != actual_checksum) {
- std::string dex = DexFileLoader::GetMultiDexLocation(i, dex_location_.c_str());
- *error_msg = StringPrintf("Dex checksum does not match for dex: %s."
- "Expected: %u, actual: %u",
- dex.c_str(),
- expected_checksum,
- actual_checksum);
- return false;
- }
- }
-
- return true;
-}
-
-bool OatFileAssistant::DexChecksumUpToDate(const OatFile& file, std::string* error_msg) {
- ScopedTrace trace("DexChecksumUpToDate(oat)");
- const std::vector<uint32_t>* required_dex_checksums = GetRequiredDexChecksums();
- if (required_dex_checksums == nullptr) {
+ if (required_dex_checksums->empty()) {
LOG(WARNING) << "Required dex checksums not found. Assuming dex checksums are up to date.";
return true;
}
@@ -419,16 +513,13 @@
// compiled code and are otherwise okay, we should return something like
// kOatRelocationOutOfDate. If they don't contain compiled code, the read
// barrier state doesn't matter.
- const bool is_cc = file.GetOatHeader().IsConcurrentCopying();
- constexpr bool kRuntimeIsCC = kUseReadBarrier;
- if (is_cc != kRuntimeIsCC) {
+ if (file.GetOatHeader().IsConcurrentCopying() != gUseReadBarrier) {
return kOatCannotOpen;
}
// Verify the dex checksum.
std::string error_msg;
- VdexFile* vdex = file.GetVdexFile();
- if (!DexChecksumUpToDate(*vdex, &error_msg)) {
+ if (!DexChecksumUpToDate(file, &error_msg)) {
LOG(ERROR) << error_msg;
return kOatDexOutOfDate;
}
@@ -443,7 +534,8 @@
VLOG(oat) << "Oat image checksum does not match image checksum.";
return kOatBootImageOutOfDate;
}
- if (!gc::space::ImageSpace::ValidateApexVersions(file, &error_msg)) {
+ if (!gc::space::ImageSpace::ValidateApexVersions(
+ file, GetOatFileAssistantContext()->GetApexVersions(), &error_msg)) {
VLOG(oat) << error_msg;
return kOatBootImageOutOfDate;
}
@@ -451,16 +543,19 @@
VLOG(oat) << "Image checksum test skipped for compiler filter " << current_compiler_filter;
}
- // zip_file_only_contains_uncompressed_dex_ is only set during fetching the dex checksums.
- DCHECK(required_dex_checksums_attempted_);
if (only_load_trusted_executable_ &&
- !LocationIsTrusted(file.GetLocation(), !Runtime::Current()->DenyArtApexDataFiles()) &&
- file.ContainsDexCode() &&
- zip_file_only_contains_uncompressed_dex_) {
- LOG(ERROR) << "Not loading "
- << dex_location_
- << ": oat file has dex code, but APK has uncompressed dex code";
- return kOatDexOutOfDate;
+ !LocationIsTrusted(file.GetLocation(),
+ !GetRuntimeOptions().deny_art_apex_data_files) &&
+ file.ContainsDexCode()) {
+ // zip_file_only_contains_uncompressed_dex_ is only set during fetching the dex checksums.
+ GetRequiredDexChecksums(&error_msg);
+ // The constraint is only enforced if the zip has uncompressed dex code.
+ if (zip_file_only_contains_uncompressed_dex_) {
+ LOG(ERROR) << "Not loading "
+ << dex_location_
+ << ": oat file has dex code, but APK has uncompressed dex code";
+ return kOatDexOutOfDate;
+ }
}
if (!ClassLoaderContextIsOkay(file)) {
@@ -474,6 +569,11 @@
InstructionSet isa,
/* out */ std::string* dex_location,
/* out */ std::string* vdex_filename) {
+ // Normally, OatFileAssistant should not assume that there is an active runtime. However, we
+ // reference the runtime here. This is okay because we are in a static function that is unrelated
+ // to other parts of OatFileAssistant.
+ DCHECK(Runtime::Current() != nullptr);
+
uint32_t checksum = adler32(0L, Z_NULL, 0);
for (const DexFile::Header* header : headers) {
checksum = adler32_combine(checksum,
@@ -571,13 +671,23 @@
InstructionSet isa,
std::string* oat_filename,
std::string* error_msg) {
+ DCHECK(Runtime::Current() != nullptr);
+ return DexLocationToOatFilename(
+ location, isa, Runtime::Current()->DenyArtApexDataFiles(), oat_filename, error_msg);
+}
+
+bool OatFileAssistant::DexLocationToOatFilename(const std::string& location,
+ InstructionSet isa,
+ bool deny_art_apex_data_files,
+ std::string* oat_filename,
+ std::string* error_msg) {
CHECK(oat_filename != nullptr);
CHECK(error_msg != nullptr);
// Check if `location` could have an oat file in the ART APEX data directory. If so, and the
// file exists, use it.
const std::string apex_data_file = GetApexDataOdexFilename(location, isa);
- if (!apex_data_file.empty() && !Runtime::Current()->DenyArtApexDataFiles()) {
+ if (!apex_data_file.empty() && !deny_art_apex_data_files) {
if (OS::FileExists(apex_data_file.c_str(), /*check_file_type=*/true)) {
*oat_filename = apex_data_file;
return true;
@@ -614,30 +724,133 @@
return GetDalvikCacheFilename(location.c_str(), dalvik_cache.c_str(), oat_filename, error_msg);
}
-const std::vector<uint32_t>* OatFileAssistant::GetRequiredDexChecksums() {
+const std::vector<uint32_t>* OatFileAssistant::GetRequiredDexChecksums(std::string* error_msg) {
if (!required_dex_checksums_attempted_) {
required_dex_checksums_attempted_ = true;
- required_dex_checksums_found_ = false;
- cached_required_dex_checksums_.clear();
- std::string error_msg;
+ std::vector<uint32_t> checksums;
const ArtDexFileLoader dex_file_loader;
std::vector<std::string> dex_locations_ignored;
if (dex_file_loader.GetMultiDexChecksums(dex_location_.c_str(),
- &cached_required_dex_checksums_,
+ &checksums,
&dex_locations_ignored,
- &error_msg,
+ &cached_required_dex_checksums_error_,
zip_fd_,
&zip_file_only_contains_uncompressed_dex_)) {
- required_dex_checksums_found_ = true;
- has_original_dex_files_ = true;
- } else {
- // The only valid case here is for APKs without dex files.
- required_dex_checksums_found_ = false;
- has_original_dex_files_ = false;
- VLOG(oat) << "Could not get required checksum: " << error_msg;
+ if (checksums.empty()) {
+ // The only valid case here is for APKs without dex files.
+ VLOG(oat) << "No dex file found in " << dex_location_;
+ }
+
+ cached_required_dex_checksums_ = std::move(checksums);
}
}
- return required_dex_checksums_found_ ? &cached_required_dex_checksums_ : nullptr;
+
+ if (cached_required_dex_checksums_.has_value()) {
+ return &cached_required_dex_checksums_.value();
+ } else {
+ *error_msg = cached_required_dex_checksums_error_;
+ DCHECK(!error_msg->empty());
+ return nullptr;
+ }
+}
+
+bool OatFileAssistant::ValidateBootClassPathChecksums(OatFileAssistantContext* ofa_context,
+ InstructionSet isa,
+ std::string_view oat_checksums,
+ std::string_view oat_boot_class_path,
+ /*out*/ std::string* error_msg) {
+ const std::vector<std::string>& bcp_locations =
+ ofa_context->GetRuntimeOptions().boot_class_path_locations;
+
+ if (oat_checksums.empty() || oat_boot_class_path.empty()) {
+ *error_msg = oat_checksums.empty() ? "Empty checksums" : "Empty boot class path";
+ return false;
+ }
+
+ size_t oat_bcp_size = gc::space::ImageSpace::CheckAndCountBCPComponents(
+ oat_boot_class_path, ArrayRef<const std::string>(bcp_locations), error_msg);
+ if (oat_bcp_size == static_cast<size_t>(-1)) {
+ DCHECK(!error_msg->empty());
+ return false;
+ }
+ DCHECK_LE(oat_bcp_size, bcp_locations.size());
+
+ size_t bcp_index = 0;
+ size_t boot_image_index = 0;
+ bool found_d = false;
+
+ while (bcp_index < oat_bcp_size) {
+ static_assert(gc::space::ImageSpace::kImageChecksumPrefix == 'i', "Format prefix check");
+ static_assert(gc::space::ImageSpace::kDexFileChecksumPrefix == 'd', "Format prefix check");
+ if (StartsWith(oat_checksums, "i") && !found_d) {
+ const std::vector<OatFileAssistantContext::BootImageInfo>& boot_image_info_list =
+ ofa_context->GetBootImageInfoList(isa);
+ if (boot_image_index >= boot_image_info_list.size()) {
+ *error_msg = StringPrintf("Missing boot image for %s, remaining checksums: %s",
+ bcp_locations[bcp_index].c_str(),
+ std::string(oat_checksums).c_str());
+ return false;
+ }
+
+ const OatFileAssistantContext::BootImageInfo& boot_image_info =
+ boot_image_info_list[boot_image_index];
+ if (!ConsumePrefix(&oat_checksums, boot_image_info.checksum)) {
+ *error_msg = StringPrintf("Image checksum mismatch, expected %s to start with %s",
+ std::string(oat_checksums).c_str(),
+ boot_image_info.checksum.c_str());
+ return false;
+ }
+
+ bcp_index += boot_image_info.component_count;
+ boot_image_index++;
+ } else if (StartsWith(oat_checksums, "d")) {
+ found_d = true;
+ const std::vector<std::string>* bcp_checksums =
+ ofa_context->GetBcpChecksums(bcp_index, error_msg);
+ if (bcp_checksums == nullptr) {
+ return false;
+ }
+ oat_checksums.remove_prefix(1u);
+ for (const std::string& checksum : *bcp_checksums) {
+ if (!ConsumePrefix(&oat_checksums, checksum)) {
+ *error_msg = StringPrintf(
+ "Dex checksum mismatch for bootclasspath file %s, expected %s to start with %s",
+ bcp_locations[bcp_index].c_str(),
+ std::string(oat_checksums).c_str(),
+ checksum.c_str());
+ return false;
+ }
+ }
+
+ bcp_index++;
+ } else {
+ *error_msg = StringPrintf("Unexpected checksums, expected %s to start with %s",
+ std::string(oat_checksums).c_str(),
+ found_d ? "'d'" : "'i' or 'd'");
+ return false;
+ }
+
+ if (bcp_index < oat_bcp_size) {
+ if (!ConsumePrefix(&oat_checksums, ":")) {
+ if (oat_checksums.empty()) {
+ *error_msg =
+ StringPrintf("Checksum too short, missing %zu components", oat_bcp_size - bcp_index);
+ } else {
+ *error_msg = StringPrintf("Missing ':' separator at start of %s",
+ std::string(oat_checksums).c_str());
+ }
+ return false;
+ }
+ }
+ }
+
+ if (!oat_checksums.empty()) {
+ *error_msg =
+ StringPrintf("Checksum too long, unexpected tail: %s", std::string(oat_checksums).c_str());
+ return false;
+ }
+
+ return true;
}
bool OatFileAssistant::ValidateBootClassPathChecksums(const OatFile& oat_file) {
@@ -649,45 +862,26 @@
if (oat_boot_class_path_checksums == nullptr || oat_boot_class_path == 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;
- }
- Runtime* runtime = Runtime::Current();
std::string error_msg;
- bool result = false;
- // Fast path when the runtime boot classpath cheksums and boot classpath
- // locations directly match.
- if (oat_boot_class_path_checksums_view == runtime->GetBootClassPathChecksums() &&
- isa_ == kRuntimeISA &&
- oat_boot_class_path_view == android::base::Join(runtime->GetBootClassPathLocations(), ":")) {
- result = true;
- } else {
- result = gc::space::ImageSpace::VerifyBootClassPathChecksums(
- oat_boot_class_path_checksums_view,
- oat_boot_class_path_view,
- ArrayRef<const std::string>(runtime->GetImageLocations()),
- ArrayRef<const std::string>(runtime->GetBootClassPathLocations()),
- ArrayRef<const std::string>(runtime->GetBootClassPath()),
- ArrayRef<const int>(runtime->GetBootClassPathFds()),
- isa_,
- &error_msg);
- }
+ bool result = ValidateBootClassPathChecksums(GetOatFileAssistantContext(),
+ isa_,
+ oat_boot_class_path_checksums,
+ oat_boot_class_path,
+ &error_msg);
if (!result) {
VLOG(oat) << "Failed to verify checksums of oat file " << oat_file.GetLocation()
<< " error: " << error_msg;
return false;
}
- // 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;
}
+bool OatFileAssistant::IsPrimaryBootImageUsable() {
+ return !GetOatFileAssistantContext()->GetBootImageInfoList(isa_).empty();
+}
+
OatFileAssistant::OatFileInfo& OatFileAssistant::GetBestInfo() {
ScopedTrace trace("GetBestInfo");
// TODO(calin): Document the side effects of class loading when
@@ -808,24 +1002,34 @@
}
OatFileAssistant::DexOptNeeded OatFileAssistant::OatFileInfo::GetDexOptNeeded(
- CompilerFilter::Filter target,
- bool profile_changed,
- bool downgrade) {
-
+ CompilerFilter::Filter target_compiler_filter, const DexOptTrigger dexopt_trigger) {
if (IsUseable()) {
- return CompilerFilterIsOkay(target, profile_changed, downgrade)
- ? kNoDexOptNeeded
- : kDex2OatForFilter;
+ return ShouldRecompileForFilter(target_compiler_filter, dexopt_trigger) ? kDex2OatForFilter :
+ kNoDexOptNeeded;
+ }
+
+ // In this case, the oat file is not usable. If the caller doesn't seek for a better compiler
+ // filter (e.g., the caller wants to downgrade), then we should not recompile.
+ if (!dexopt_trigger.targetFilterIsBetter) {
+ return kNoDexOptNeeded;
}
if (Status() == kOatBootImageOutOfDate) {
return kDex2OatForBootImage;
}
- if (oat_file_assistant_->HasDexFiles()) {
- return kDex2OatFromScratch;
+ std::string error_msg;
+ std::optional<bool> has_dex_files = oat_file_assistant_->HasDexFiles(&error_msg);
+ if (has_dex_files.has_value()) {
+ if (*has_dex_files) {
+ return kDex2OatFromScratch;
+ } else {
+ // No dex file, so there is nothing we need to do.
+ return kNoDexOptNeeded;
+ }
} else {
- // No dex file, there is nothing we need to do.
+ // Unable to open the dex file, so there is nothing we can do.
+ LOG(WARNING) << error_msg;
return kNoDexOptNeeded;
}
}
@@ -840,7 +1044,8 @@
return nullptr;
}
- if (LocationIsOnArtApexData(filename_) && Runtime::Current()->DenyArtApexDataFiles()) {
+ if (LocationIsOnArtApexData(filename_) &&
+ oat_file_assistant_->GetRuntimeOptions().deny_art_apex_data_files) {
LOG(WARNING) << "OatFileAssistant rejected file " << filename_
<< ": ART apexdata is untrusted.";
return nullptr;
@@ -934,25 +1139,24 @@
return file_.get();
}
-bool OatFileAssistant::OatFileInfo::CompilerFilterIsOkay(
- CompilerFilter::Filter target, bool profile_changed, bool downgrade) {
+bool OatFileAssistant::OatFileInfo::ShouldRecompileForFilter(CompilerFilter::Filter target,
+ const DexOptTrigger dexopt_trigger) {
const OatFile* file = GetFile();
- if (file == nullptr) {
- return false;
- }
+ DCHECK(file != nullptr);
CompilerFilter::Filter current = file->GetCompilerFilter();
- if (profile_changed && CompilerFilter::DependsOnProfile(current)) {
- VLOG(oat) << "Compiler filter not okay because Profile changed";
- return false;
+ if (dexopt_trigger.targetFilterIsBetter && CompilerFilter::IsBetter(target, current)) {
+ return true;
+ }
+ if (dexopt_trigger.targetFilterIsSame && current == target) {
+ return true;
+ }
+ if (dexopt_trigger.targetFilterIsWorse && CompilerFilter::IsBetter(current, target)) {
+ return true;
}
- if (downgrade) {
- return !CompilerFilter::IsBetter(current, target);
- }
-
- if (CompilerFilter::DependsOnImageChecksum(current) &&
- CompilerFilter::IsAsGoodAs(current, target)) {
+ if (dexopt_trigger.primaryBootImageBecomesUsable &&
+ CompilerFilter::DependsOnImageChecksum(current)) {
// If the oat file has been compiled without an image, and the runtime is
// now running with an image loaded from disk, return that we need to
// re-compile. The recompilation will generate a better oat file, and with an app
@@ -961,16 +1165,21 @@
file->GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey);
if (oat_boot_class_path_checksums != nullptr &&
!StartsWith(oat_boot_class_path_checksums, "i") &&
- !Runtime::Current()->HasImageWithProfile()) {
+ oat_file_assistant_->IsPrimaryBootImageUsable()) {
DCHECK(!file->GetOatHeader().RequiresImage());
- return false;
+ return true;
}
}
- return CompilerFilter::IsAsGoodAs(current, target);
+ return false;
}
bool OatFileAssistant::ClassLoaderContextIsOkay(const OatFile& oat_file) const {
+ if (context_ == nullptr) {
+ // The caller requests to skip the check.
+ return true;
+ }
+
if (oat_file.IsBackedByVdexOnly()) {
// Only a vdex file, we don't depend on the class loader context.
return true;
@@ -982,12 +1191,6 @@
return true;
}
- if (context_ == nullptr) {
- // When no class loader context is provided (which happens for deprecated
- // DexFile APIs), just assume it is OK.
- return true;
- }
-
ClassLoaderContext::VerificationResult matches = context_->VerifyClassLoaderContextMatch(
oat_file.GetClassLoaderContext(),
/*verify_names=*/ true,
@@ -1044,17 +1247,19 @@
// TODO(calin): we could provide a more refined status here
// (e.g. run from uncompressed apk, run with vdex but not oat etc). It will allow us to
// track more experiments but adds extra complexity.
-void OatFileAssistant::GetOptimizationStatus(
- const std::string& filename,
- InstructionSet isa,
- std::string* out_compilation_filter,
- std::string* out_compilation_reason) {
+void OatFileAssistant::GetOptimizationStatus(const std::string& filename,
+ InstructionSet isa,
+ std::string* out_compilation_filter,
+ std::string* out_compilation_reason,
+ OatFileAssistantContext* ofa_context) {
// It may not be possible to load an oat file executable (e.g., selinux restrictions). Load
// non-executable and check the status manually.
OatFileAssistant oat_file_assistant(filename.c_str(),
isa,
- /* context= */ nullptr,
- /*load_executable=*/ false);
+ /*context=*/nullptr,
+ /*load_executable=*/false,
+ /*only_load_trusted_executable=*/false,
+ ofa_context);
std::string out_odex_location; // unused
std::string out_odex_status; // unused
oat_file_assistant.GetOptimizationStatus(
@@ -1090,28 +1295,25 @@
OatStatus status = oat_file_info.Status();
const char* reason = oat_file->GetCompilationReason();
*out_compilation_reason = reason == nullptr ? "unknown" : reason;
+
+ // If the oat file is invalid, the vdex file will be picked, so the status is `kOatUpToDate`. If
+ // the vdex file is also invalid, then either `oat_file` is nullptr, or `status` is
+ // `kOatDexOutOfDate`.
+ DCHECK(status == kOatUpToDate || status == kOatDexOutOfDate);
+
switch (status) {
case kOatUpToDate:
*out_compilation_filter = CompilerFilter::NameOfFilter(oat_file->GetCompilerFilter());
*out_odex_status = "up-to-date";
return;
- case kOatCannotOpen: // This should never happen, but be robust.
- *out_compilation_filter = "error";
- *out_compilation_reason = "error";
- // This mostly happens when we cannot open the vdex file,
- // or the file is corrupt.
- *out_odex_status = "io-error-or-corruption";
- return;
-
+ case kOatCannotOpen:
case kOatBootImageOutOfDate:
- *out_compilation_filter = "run-from-apk-fallback";
- *out_odex_status = "boot-image-more-recent";
- return;
-
case kOatContextOutOfDate:
- *out_compilation_filter = "run-from-apk-fallback";
- *out_odex_status = "context-mismatch";
+ // These should never happen, but be robust.
+ *out_compilation_filter = "unexpected";
+ *out_compilation_reason = "unexpected";
+ *out_odex_status = "unexpected";
return;
case kOatDexOutOfDate:
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index c243cc3..7001650 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -19,16 +19,19 @@
#include <cstdint>
#include <memory>
+#include <optional>
#include <sstream>
#include <string>
+#include <variant>
-#include "base/compiler_filter.h"
#include "arch/instruction_set.h"
+#include "base/compiler_filter.h"
#include "base/os.h"
#include "base/scoped_flock.h"
#include "base/unix_file/fd_file.h"
#include "class_loader_context.h"
#include "oat_file.h"
+#include "oat_file_assistant_context.h"
namespace art {
@@ -89,6 +92,45 @@
kOatUpToDate,
};
+ // A bit field to represent the conditions where dexopt should be performed.
+ struct DexOptTrigger {
+ // Dexopt should be performed if the target compiler filter is better than the current compiler
+ // filter. See `CompilerFilter::IsBetter`.
+ bool targetFilterIsBetter : 1;
+ // Dexopt should be performed if the target compiler filter is the same as the current compiler
+ // filter.
+ bool targetFilterIsSame : 1;
+ // Dexopt should be performed if the target compiler filter is worse than the current compiler
+ // filter. See `CompilerFilter::IsBetter`.
+ bool targetFilterIsWorse : 1;
+ // Dexopt should be performed if the current oat file was compiled without a primary image,
+ // and the runtime is now running with a primary image loaded from disk.
+ bool primaryBootImageBecomesUsable : 1;
+ };
+
+ // Represents the location of the current oat file and/or vdex file.
+ enum Location {
+ // Does not exist, or an error occurs.
+ kLocationNoneOrError = 0,
+ // In the global "dalvik-cache" folder.
+ kLocationOat = 1,
+ // In the "oat" folder next to the dex file.
+ kLocationOdex = 2,
+ // In the DM file. This means the only usable file is the vdex file.
+ kLocationDm = 3,
+ };
+
+ // Represents the status of the current oat file and/or vdex file.
+ class DexOptStatus {
+ public:
+ Location GetLocation() { return location_; }
+ bool IsVdexUsable() { return location_ != kLocationNoneOrError; }
+
+ private:
+ Location location_ = kLocationNoneOrError;
+ friend class OatFileAssistant;
+ };
+
// Constructs an OatFileAssistant object to assist the oat file
// corresponding to the given dex location with the target instruction set.
//
@@ -104,17 +146,23 @@
// device. For example, on an arm device, use arm or arm64. An oat file can
// be loaded executable only if the ISA matches the current runtime.
//
+ // context should be the class loader context to check against, or null to skip the check.
+ //
// load_executable should be true if the caller intends to try and load
// executable code for this dex location.
//
// only_load_trusted_executable should be true if the caller intends to have
// only oat files from trusted locations loaded executable. See IsTrustedLocation() for
// details on trusted locations.
+ //
+ // runtime_options should be provided with all the required fields filled if the caller intends to
+ // use OatFileAssistant without a runtime.
OatFileAssistant(const char* dex_location,
const InstructionSet isa,
ClassLoaderContext* context,
bool load_executable,
- bool only_load_trusted_executable = false);
+ bool only_load_trusted_executable = false,
+ OatFileAssistantContext* ofa_context = nullptr);
// Similar to this(const char*, const InstructionSet, bool), however, if a valid zip_fd is
// provided, vdex, oat, and zip files will be read from vdex_fd, oat_fd and zip_fd respectively.
@@ -124,10 +172,25 @@
ClassLoaderContext* context,
bool load_executable,
bool only_load_trusted_executable,
+ OatFileAssistantContext* ofa_context,
int vdex_fd,
int oat_fd,
int zip_fd);
+ // A convenient factory function that accepts ISA, class loader context, and compiler filter in
+ // strings. Returns the created instance and ClassLoaderContext on success, or returns nullptr and
+ // outputs an error message if it fails to parse the input strings.
+ // The returned ClassLoaderContext must live at least as long as the OatFileAssistant.
+ static std::unique_ptr<OatFileAssistant> Create(
+ const std::string& filename,
+ const std::string& isa_str,
+ const std::optional<std::string>& context_str,
+ bool load_executable,
+ bool only_load_trusted_executable,
+ OatFileAssistantContext* ofa_context,
+ /*out*/ std::unique_ptr<ClassLoaderContext>* context,
+ /*out*/ std::string* error_msg);
+
// Returns true if the dex location refers to an element of the boot class
// path.
bool IsInBootClassPath();
@@ -148,10 +211,18 @@
// Returns a positive status code if the status refers to the oat file in
// the oat location. Returns a negative status code if the status refers to
// the oat file in the odex location.
+ //
+ // Deprecated. Use the other overload.
int GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
bool profile_changed = false,
bool downgrade = false);
+ // Returns true if dexopt needs to be performed with respect to the given target compilation
+ // filter and dexopt trigger. Also returns the status of the current oat file and/or vdex file.
+ bool GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
+ const DexOptTrigger dexopt_trigger,
+ /*out*/ DexOptStatus* dexopt_status);
+
// Returns true if there is up-to-date code for this dex location,
// irrespective of the compiler filter of the up-to-date code.
bool IsUpToDate();
@@ -176,7 +247,7 @@
// - out_compilation_reason: the optimization reason. The reason might
// be "unknown" if the compiler artifacts were not annotated during optimizations.
// - out_odex_status: a human readable refined status of the validity of the odex file.
- // E.g. up-to-date, boot-image-more-recent, apk-more-recent.
+ // Possible values are: "up-to-date", "apk-more-recent", and "io-error-no-oat".
//
// This method will try to mimic the runtime effect of loading the dex file.
// For example, if there is no usable oat file, the compiler filter will be set
@@ -189,7 +260,8 @@
static void GetOptimizationStatus(const std::string& filename,
InstructionSet isa,
std::string* out_compilation_filter,
- std::string* out_compilation_reason);
+ std::string* out_compilation_reason,
+ OatFileAssistantContext* ofa_context = nullptr);
// Open and returns an image space associated with the oat file.
static std::unique_ptr<gc::space::ImageSpace> OpenImageSpace(const OatFile* oat_file);
@@ -212,8 +284,8 @@
const std::string& dex_location,
std::vector<std::unique_ptr<const DexFile>>* out_dex_files);
- // Returns whether this is an apk/zip wit a classes.dex entry.
- bool HasDexFiles();
+ // Returns whether this is an apk/zip wit a classes.dex entry, or nullopt if an error occurred.
+ std::optional<bool> HasDexFiles(std::string* error_msg);
// If the dex file has been installed with a compiled oat file alongside
// it, the compiled oat file will have the extension .odex, and is referred
@@ -253,15 +325,28 @@
// Returns false on error, in which case error_msg describes the error and
// oat_filename is not changed.
// Neither oat_filename nor error_msg may be null.
+ //
+ // Calling this function requires an active runtime.
static bool DexLocationToOatFilename(const std::string& location,
InstructionSet isa,
std::string* oat_filename,
std::string* error_msg);
+ // Same as above, but also takes `deny_art_apex_data_files` from input.
+ //
+ // Calling this function does not require an active runtime.
+ static bool DexLocationToOatFilename(const std::string& location,
+ InstructionSet isa,
+ bool deny_art_apex_data_files,
+ std::string* oat_filename,
+ std::string* error_msg);
+
// Computes the dex location and vdex filename. If the data directory of the process
// is known, creates an absolute path in that directory and tries to infer path
// of a corresponding vdex file. Otherwise only creates a basename dex_location
// from the combined checksums. Returns true if all out-arguments have been set.
+ //
+ // Calling this function requires an active runtime.
static bool AnonymousDexVdexLocation(const std::vector<const DexFile::Header*>& dex_headers,
InstructionSet isa,
/* out */ std::string* dex_location,
@@ -273,6 +358,16 @@
bool ClassLoaderContextIsOkay(const OatFile& oat_file) const;
+ // Validates the boot class path checksum of an OatFile.
+ bool ValidateBootClassPathChecksums(const OatFile& oat_file);
+
+ // Validates the given bootclasspath and bootclasspath checksums found in an oat header.
+ static bool ValidateBootClassPathChecksums(OatFileAssistantContext* ofa_context,
+ InstructionSet isa,
+ std::string_view oat_checksums,
+ std::string_view oat_boot_class_path,
+ /*out*/ std::string* error_msg);
+
private:
class OatFileInfo {
public:
@@ -298,15 +393,10 @@
// Returns the status of this oat file.
OatStatus Status();
- // Return the DexOptNeeded value for this oat file with respect to the
- // given target_compilation_filter.
- // profile_changed should be true to indicate the profile has recently
- // changed for this dex location.
- // downgrade should be true if the purpose of dexopt is to downgrade the
- // compiler filter.
+ // Return the DexOptNeeded value for this oat file with respect to the given target compilation
+ // filter and dexopt trigger.
DexOptNeeded GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
- bool profile_changed,
- bool downgrade);
+ const DexOptTrigger dexopt_trigger);
// Returns the loaded file.
// Loads the file if needed. Returns null if the file failed to load.
@@ -339,13 +429,10 @@
std::unique_ptr<OatFile> ReleaseFileForUse();
private:
- // Returns true if the compiler filter used to generate the file is at
- // least as good as the given target filter. profile_changed should be
- // true to indicate the profile has recently changed for this dex
- // location.
- // downgrade should be true if the purpose of dexopt is to downgrade the
- // compiler filter.
- bool CompilerFilterIsOkay(CompilerFilter::Filter target, bool profile_changed, bool downgrade);
+ // Returns true if the oat file is usable but at least one dexopt trigger is matched. This
+ // function should only be called if the oat file is usable.
+ bool ShouldRecompileForFilter(CompilerFilter::Filter target,
+ const DexOptTrigger dexopt_trigger);
// Release the loaded oat file.
// Returns null if the oat file hasn't been loaded.
@@ -387,11 +474,6 @@
// files are being read.
bool UseFdToReadFiles();
- // Returns true if the dex checksums in the given vdex file are up to date
- // with respect to the dex location. If the dex checksums are not up to
- // date, error_msg is updated with a message describing the problem.
- bool DexChecksumUpToDate(const VdexFile& file, std::string* error_msg);
-
// Returns true if the dex checksums in the given oat file are up to date
// with respect to the dex location. If the dex checksums are not up to
// date, error_msg is updated with a message describing the problem.
@@ -402,18 +484,42 @@
OatStatus GivenOatFileStatus(const OatFile& file);
// Gets the dex checksums required for an up-to-date oat file.
- // Returns cached_required_dex_checksums if the required checksums were
- // located. Returns null if the required checksums were not found. The
- // caller shouldn't clean up or free the returned pointer. This sets the
- // has_original_dex_files_ field to true if the checksums were found for the
- // dex_location_ dex file.
- const std::vector<uint32_t>* GetRequiredDexChecksums();
+ // Returns cached_required_dex_checksums if the required checksums were located. Returns an empty
+ // list if `dex_location_` refers to a zip and there is no dex file in it. Returns nullptr if an
+ // error occurred. The caller shouldn't clean up or free the returned pointer.
+ const std::vector<uint32_t>* GetRequiredDexChecksums(std::string* error_msg);
- // Validates the boot class path checksum of an OatFile.
- bool ValidateBootClassPathChecksums(const OatFile& oat_file);
+ // Returns whether there is at least one boot image usable.
+ bool IsPrimaryBootImageUsable();
+
+ // Returns the trigger for the deprecated overload of `GetDexOptNeeded`.
+ //
+ // Deprecated. Do not use in new code.
+ DexOptTrigger GetDexOptTrigger(CompilerFilter::Filter target_compiler_filter,
+ bool profile_changed,
+ bool downgrade);
+
+ // Returns the pointer to the owned or unowned instance of OatFileAssistantContext.
+ OatFileAssistantContext* GetOatFileAssistantContext() {
+ if (std::holds_alternative<OatFileAssistantContext*>(ofa_context_)) {
+ return std::get<OatFileAssistantContext*>(ofa_context_);
+ } else {
+ return std::get<std::unique_ptr<OatFileAssistantContext>>(ofa_context_).get();
+ }
+ }
+
+ // The runtime options taken from the active runtime or the input.
+ //
+ // All member functions should get runtime options from this variable rather than referencing the
+ // active runtime. This is to allow OatFileAssistant to function without an active runtime.
+ const OatFileAssistantContext::RuntimeOptions& GetRuntimeOptions() {
+ return GetOatFileAssistantContext()->GetRuntimeOptions();
+ }
std::string dex_location_;
+ // The class loader context to check against, or null representing that the check should be
+ // skipped.
ClassLoaderContext* context_;
// Whether or not the parent directory of the dex file is writable.
@@ -434,10 +540,9 @@
// Cached value of the required dex checksums.
// This should be accessed only by the GetRequiredDexChecksums() method.
- std::vector<uint32_t> cached_required_dex_checksums_;
+ std::optional<std::vector<uint32_t>> cached_required_dex_checksums_;
+ std::string cached_required_dex_checksums_error_;
bool required_dex_checksums_attempted_ = false;
- bool required_dex_checksums_found_;
- bool has_original_dex_files_;
// The AOT-compiled file of an app when the APK of the app is in /data.
OatFileInfo odex_;
@@ -459,8 +564,8 @@
// File descriptor corresponding to apk, dex file, or zip.
int zip_fd_;
- std::string cached_boot_class_path_;
- std::string cached_boot_class_path_checksums_;
+ // Owned or unowned instance of OatFileAssistantContext.
+ std::variant<std::unique_ptr<OatFileAssistantContext>, OatFileAssistantContext*> ofa_context_;
friend class OatFileAssistantTest;
diff --git a/runtime/oat_file_assistant_context.cc b/runtime/oat_file_assistant_context.cc
new file mode 100644
index 0000000..4bd83b7
--- /dev/null
+++ b/runtime/oat_file_assistant_context.cc
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2022 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 "oat_file_assistant_context.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+#include "arch/instruction_set.h"
+#include "base/array_ref.h"
+#include "base/logging.h"
+#include "base/mem_map.h"
+#include "class_linker.h"
+#include "dex/art_dex_file_loader.h"
+#include "gc/heap.h"
+#include "gc/space/image_space.h"
+
+namespace art {
+
+using ::android::base::StringPrintf;
+using ::art::gc::space::ImageSpace;
+
+OatFileAssistantContext::OatFileAssistantContext(
+ std::unique_ptr<OatFileAssistantContext::RuntimeOptions> runtime_options)
+ : runtime_options_(std::move(runtime_options)) {
+ DCHECK_EQ(runtime_options_->boot_class_path.size(),
+ runtime_options_->boot_class_path_locations.size());
+ DCHECK_IMPLIES(
+ runtime_options_->boot_class_path_fds != nullptr,
+ runtime_options_->boot_class_path.size() == runtime_options_->boot_class_path_fds->size());
+ // Opening dex files and boot images require MemMap.
+ MemMap::Init();
+}
+
+OatFileAssistantContext::OatFileAssistantContext(Runtime* runtime)
+ : OatFileAssistantContext(std::make_unique<OatFileAssistantContext::RuntimeOptions>(
+ OatFileAssistantContext::RuntimeOptions{
+ .image_locations = runtime->GetImageLocations(),
+ .boot_class_path = runtime->GetBootClassPath(),
+ .boot_class_path_locations = runtime->GetBootClassPathLocations(),
+ .boot_class_path_fds = !runtime->GetBootClassPathFds().empty() ?
+ &runtime->GetBootClassPathFds() :
+ nullptr,
+ .deny_art_apex_data_files = runtime->DenyArtApexDataFiles(),
+ })) {
+ // Fetch boot image info from the runtime.
+ std::vector<BootImageInfo>& boot_image_info_list = boot_image_info_list_by_isa_[kRuntimeISA];
+ for (const ImageSpace* image_space : runtime->GetHeap()->GetBootImageSpaces()) {
+ // We only need the checksum of the first component for each boot image. They are in image
+ // spaces that have a non-zero component count.
+ if (image_space->GetComponentCount() > 0) {
+ BootImageInfo& boot_image_info = boot_image_info_list.emplace_back();
+ boot_image_info.component_count = image_space->GetComponentCount();
+ ImageSpace::AppendImageChecksum(image_space->GetComponentCount(),
+ image_space->GetImageHeader().GetImageChecksum(),
+ &boot_image_info.checksum);
+ }
+ }
+
+ // Fetch BCP checksums from the runtime.
+ size_t bcp_index = 0;
+ std::vector<std::string>* current_bcp_checksums = nullptr;
+ for (const DexFile* dex_file : runtime->GetClassLinker()->GetBootClassPath()) {
+ if (!DexFileLoader::IsMultiDexLocation(dex_file->GetLocation().c_str())) {
+ DCHECK_LT(bcp_index, runtime_options_->boot_class_path.size());
+ current_bcp_checksums = &bcp_checksums_by_index_[bcp_index++];
+ }
+ DCHECK_NE(current_bcp_checksums, nullptr);
+ current_bcp_checksums->push_back(StringPrintf("/%08x", dex_file->GetLocationChecksum()));
+ }
+ DCHECK_EQ(bcp_index, runtime_options_->boot_class_path.size());
+
+ // Fetch APEX versions from the runtime.
+ apex_versions_ = runtime->GetApexVersions();
+}
+
+const OatFileAssistantContext::RuntimeOptions& OatFileAssistantContext::GetRuntimeOptions() const {
+ return *runtime_options_;
+}
+
+bool OatFileAssistantContext::FetchAll(std::string* error_msg) {
+ std::vector<InstructionSet> isas = GetSupportedInstructionSets(error_msg);
+ if (isas.empty()) {
+ return false;
+ }
+ for (InstructionSet isa : isas) {
+ GetBootImageInfoList(isa);
+ }
+ for (size_t i = 0; i < runtime_options_->boot_class_path.size(); i++) {
+ if (GetBcpChecksums(i, error_msg) == nullptr) {
+ return false;
+ }
+ }
+ GetApexVersions();
+ return true;
+}
+
+const std::vector<OatFileAssistantContext::BootImageInfo>&
+OatFileAssistantContext::GetBootImageInfoList(InstructionSet isa) {
+ if (auto it = boot_image_info_list_by_isa_.find(isa); it != boot_image_info_list_by_isa_.end()) {
+ return it->second;
+ }
+
+ ImageSpace::BootImageLayout layout(
+ ArrayRef<const std::string>(runtime_options_->image_locations),
+ ArrayRef<const std::string>(runtime_options_->boot_class_path),
+ ArrayRef<const std::string>(runtime_options_->boot_class_path_locations),
+ runtime_options_->boot_class_path_fds != nullptr ?
+ ArrayRef<const int>(*runtime_options_->boot_class_path_fds) :
+ ArrayRef<const int>(),
+ /*boot_class_path_image_fds=*/ArrayRef<const int>(),
+ /*boot_class_path_vdex_fds=*/ArrayRef<const int>(),
+ /*boot_class_path_oat_fds=*/ArrayRef<const int>(),
+ &GetApexVersions());
+
+ std::string error_msg;
+ if (!layout.LoadFromSystem(isa, /*allow_in_memory_compilation=*/false, &error_msg)) {
+ // At this point, `layout` contains nothing.
+ VLOG(oat) << "Some error occurred when loading boot images for oat file validation: "
+ << error_msg;
+ // Create an empty entry so that we don't have to retry when the function is called again.
+ return boot_image_info_list_by_isa_[isa];
+ }
+
+ std::vector<BootImageInfo>& boot_image_info_list = boot_image_info_list_by_isa_[isa];
+ for (const ImageSpace::BootImageLayout::ImageChunk& chunk : layout.GetChunks()) {
+ BootImageInfo& boot_image_info = boot_image_info_list.emplace_back();
+ boot_image_info.component_count = chunk.component_count;
+ ImageSpace::AppendImageChecksum(
+ chunk.component_count, chunk.checksum, &boot_image_info.checksum);
+ }
+ return boot_image_info_list;
+}
+
+const std::vector<std::string>* OatFileAssistantContext::GetBcpChecksums(size_t bcp_index,
+ std::string* error_msg) {
+ DCHECK_LT(bcp_index, runtime_options_->boot_class_path.size());
+
+ if (auto it = bcp_checksums_by_index_.find(bcp_index); it != bcp_checksums_by_index_.end()) {
+ return &it->second;
+ }
+
+ std::vector<uint32_t> checksums;
+ std::vector<std::string> dex_locations;
+ ArtDexFileLoader dex_file_loader;
+ if (!dex_file_loader.GetMultiDexChecksums(
+ runtime_options_->boot_class_path[bcp_index].c_str(),
+ &checksums,
+ &dex_locations,
+ error_msg,
+ runtime_options_->boot_class_path_fds != nullptr ?
+ (*runtime_options_->boot_class_path_fds)[bcp_index] :
+ -1)) {
+ return nullptr;
+ }
+
+ DCHECK(!checksums.empty());
+ std::vector<std::string>& bcp_checksums = bcp_checksums_by_index_[bcp_index];
+ for (uint32_t checksum : checksums) {
+ bcp_checksums.push_back(StringPrintf("/%08x", checksum));
+ }
+ return &bcp_checksums;
+}
+
+const std::string& OatFileAssistantContext::GetApexVersions() {
+ if (apex_versions_.has_value()) {
+ return apex_versions_.value();
+ }
+
+ apex_versions_ = Runtime::GetApexVersions(
+ ArrayRef<const std::string>(runtime_options_->boot_class_path_locations));
+ return apex_versions_.value();
+}
+
+} // namespace art
diff --git a/runtime/oat_file_assistant_context.h b/runtime/oat_file_assistant_context.h
new file mode 100644
index 0000000..cc98c59
--- /dev/null
+++ b/runtime/oat_file_assistant_context.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_OAT_FILE_ASSISTANT_CONTEXT_H_
+#define ART_RUNTIME_OAT_FILE_ASSISTANT_CONTEXT_H_
+
+#include <optional>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "arch/instruction_set.h"
+#include "runtime.h"
+
+namespace art {
+
+// A helper class for OatFileAssistant that fetches and caches information including boot image
+// checksums, bootclasspath checksums, and APEX versions. The same instance can be reused across
+// OatFileAssistant calls on different dex files for different instruction sets.
+// This class is not thread-safe until `FetchAll` is called.
+class OatFileAssistantContext {
+ public:
+ // Options that a runtime would take.
+ // Note that the struct only keeps references, so the caller must keep the objects alive during
+ // the lifetime of OatFileAssistant.
+ struct RuntimeOptions {
+ // Required. See `-Ximage`.
+ const std::vector<std::string>& image_locations;
+ // Required. See `-Xbootclasspath`.
+ const std::vector<std::string>& boot_class_path;
+ // Required. See `-Xbootclasspath-locations`.
+ const std::vector<std::string>& boot_class_path_locations;
+ // Optional. See `-Xbootclasspathfds`.
+ const std::vector<int>* const boot_class_path_fds = nullptr;
+ // Optional. See `-Xdeny-art-apex-data-files`.
+ const bool deny_art_apex_data_files = false;
+ };
+
+ // Information about a boot image.
+ struct BootImageInfo {
+ // Number of BCP jars covered by the boot image.
+ size_t component_count;
+ // Checksum of the boot image. The format is "i;<component_count>/<checksum_in_8_digit_hex>"
+ std::string checksum;
+ };
+
+ // Constructs OatFileAssistantContext from runtime options. Does not fetch information on
+ // construction. Information will be fetched from disk when needed.
+ explicit OatFileAssistantContext(std::unique_ptr<RuntimeOptions> runtime_options);
+ // Constructs OatFileAssistantContext from a runtime instance. Fetches as much information as
+ // possible from the runtime. The rest information will be fetched from disk when needed.
+ explicit OatFileAssistantContext(Runtime* runtime);
+ // Returns runtime options.
+ const RuntimeOptions& GetRuntimeOptions() const;
+ // Fetches all information that hasn't been fetched from disk and caches it. All operations will
+ // be read-only after a successful call to this function.
+ bool FetchAll(std::string* error_msg);
+ // Returns information about the boot image of the given instruction set.
+ const std::vector<BootImageInfo>& GetBootImageInfoList(InstructionSet isa);
+ // Returns the checksums of the dex files in the BCP jar at the given index, or nullptr on error.
+ // The format of each checksum is "/<checksum_in_8_digit_hex>".
+ const std::vector<std::string>* GetBcpChecksums(size_t bcp_index, std::string* error_msg);
+ // Returns a string that represents the apex versions of boot classpath jars. See
+ // `Runtime::apex_versions_` for the encoding format.
+ const std::string& GetApexVersions();
+
+ private:
+ std::unique_ptr<RuntimeOptions> runtime_options_;
+ std::unordered_map<InstructionSet, std::vector<BootImageInfo>> boot_image_info_list_by_isa_;
+ std::unordered_map<size_t, std::vector<std::string>> bcp_checksums_by_index_;
+ std::optional<std::string> apex_versions_;
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_OAT_FILE_ASSISTANT_CONTEXT_H_
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 07998dd..ef60ea0 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -16,97 +16,157 @@
#include "oat_file_assistant.h"
+#include <fcntl.h>
+#include <gtest/gtest.h>
#include <sys/param.h>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <optional>
#include <string>
+#include <type_traits>
#include <vector>
-#include <fcntl.h>
-#include <gtest/gtest.h>
-
+#include "android-base/scopeguard.h"
#include "android-base/strings.h"
-
+#include "arch/instruction_set.h"
#include "art_field-inl.h"
#include "base/os.h"
#include "base/utils.h"
-#include "class_linker-inl.h"
+#include "class_linker.h"
#include "class_loader_context.h"
#include "common_runtime_test.h"
#include "dexopt_test.h"
-#include "hidden_api.h"
#include "oat.h"
#include "oat_file.h"
+#include "oat_file_assistant_context.h"
#include "oat_file_manager.h"
-#include "scoped_thread_state_change-inl.h"
-#include "thread-current-inl.h"
+#include "scoped_thread_state_change.h"
+#include "thread.h"
namespace art {
-class OatFileAssistantTest : public DexoptTest {
+class OatFileAssistantBaseTest : public DexoptTest {};
+
+class OatFileAssistantTest : public OatFileAssistantBaseTest,
+ public testing::WithParamInterface<bool> {
public:
- void VerifyOptimizationStatus(OatFileAssistant* assistant,
- const std::string& file,
- const std::string& expected_filter,
+ void SetUp() override {
+ DexoptTest::SetUp();
+ with_runtime_ = GetParam();
+ ofa_context_ = CreateOatFileAssistantContext();
+ }
+
+ // Verifies all variants of `GetOptimizationStatus`.
+ //
+ // `expected_filter` can be either a value of `CompilerFilter::Filter` or a string.
+ // If `check_context` is true, only verifies the variants that checks class loader context.
+ template <typename T>
+ void VerifyOptimizationStatus(const std::string& file,
+ ClassLoaderContext* context,
+ const T& expected_filter,
const std::string& expected_reason,
- const std::string& expected_odex_status) {
- // Verify the static methods (called from PM for dexOptNeeded).
- std::string compilation_filter1;
- std::string compilation_reason1;
+ const std::string& expected_odex_status,
+ bool check_context = false) {
+ std::string expected_filter_name;
+ if constexpr (std::is_same_v<T, CompilerFilter::Filter>) {
+ expected_filter_name = CompilerFilter::NameOfFilter(expected_filter);
+ } else {
+ expected_filter_name = expected_filter;
+ }
- OatFileAssistant::GetOptimizationStatus(
- file, kRuntimeISA, &compilation_filter1, &compilation_reason1);
+ // Verify the static method (called from PM for dumpsys).
+ // This variant does not check class loader context.
+ if (!check_context) {
+ std::string compilation_filter;
+ std::string compilation_reason;
- ASSERT_EQ(expected_filter, compilation_filter1);
- ASSERT_EQ(expected_reason, compilation_reason1);
+ OatFileAssistant::GetOptimizationStatus(file,
+ kRuntimeISA,
+ &compilation_filter,
+ &compilation_reason,
+ MaybeGetOatFileAssistantContext());
- // Verify the instance methods (called at runtime for systrace).
- std::string odex_location2; // ignored
- std::string compilation_filter2;
- std::string compilation_reason2;
- std::string odex_status2;
+ ASSERT_EQ(expected_filter_name, compilation_filter);
+ ASSERT_EQ(expected_reason, compilation_reason);
+ }
+
+ // Verify the instance methods (called at runtime and from artd).
+ OatFileAssistant assistant = CreateOatFileAssistant(file.c_str(), context);
+ VerifyOptimizationStatusWithInstance(
+ &assistant, expected_filter_name, expected_reason, expected_odex_status);
+ }
+
+ void VerifyOptimizationStatusWithInstance(OatFileAssistant* assistant,
+ const std::string& expected_filter,
+ const std::string& expected_reason,
+ const std::string& expected_odex_status) {
+ std::string odex_location; // ignored
+ std::string compilation_filter;
+ std::string compilation_reason;
+ std::string odex_status;
assistant->GetOptimizationStatus(
- &odex_location2,
- &compilation_filter2,
- &compilation_reason2,
- &odex_status2);
+ &odex_location, &compilation_filter, &compilation_reason, &odex_status);
- ASSERT_EQ(expected_filter, compilation_filter2);
- ASSERT_EQ(expected_reason, compilation_reason2);
- ASSERT_EQ(expected_odex_status, odex_status2);
+ ASSERT_EQ(expected_filter, compilation_filter);
+ ASSERT_EQ(expected_reason, compilation_reason);
+ ASSERT_EQ(expected_odex_status, odex_status);
}
- void VerifyOptimizationStatus(OatFileAssistant* assistant,
- const std::string& file,
- CompilerFilter::Filter expected_filter,
- const std::string& expected_reason,
- const std::string& expected_odex_status) {
- VerifyOptimizationStatus(
- assistant,
- file,
- CompilerFilter::NameOfFilter(expected_filter),
- expected_reason,
- expected_odex_status);
- }
-
- void InsertNewBootClasspathEntry() {
- std::string extra_dex_filename = GetMultiDexSrc1();
- Runtime* runtime = Runtime::Current();
- runtime->boot_class_path_.push_back(extra_dex_filename);
- if (!runtime->boot_class_path_locations_.empty()) {
- runtime->boot_class_path_locations_.push_back(extra_dex_filename);
+ bool InsertNewBootClasspathEntry(const std::string& src, std::string* error_msg) {
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ ArtDexFileLoader dex_file_loader;
+ if (!dex_file_loader.Open(src.c_str(),
+ src,
+ /*verify=*/true,
+ /*verify_checksum=*/false,
+ error_msg,
+ &dex_files)) {
+ return false;
}
+
+ runtime_->AppendToBootClassPath(src, src, dex_files);
+ std::move(dex_files.begin(), dex_files.end(), std::back_inserter(opened_dex_files_));
+
+ return true;
}
- int GetDexOptNeeded(
- OatFileAssistant* assistant,
- CompilerFilter::Filter compiler_filter,
- bool profile_changed = false,
- bool downgrade = false) {
- return assistant->GetDexOptNeeded(
- compiler_filter,
- profile_changed,
- downgrade);
+ // Verifies the current version of `GetDexOptNeeded` (called from artd).
+ void VerifyGetDexOptNeeded(OatFileAssistant* assistant,
+ CompilerFilter::Filter compiler_filter,
+ OatFileAssistant::DexOptTrigger dexopt_trigger,
+ bool expected_dexopt_needed,
+ bool expected_is_vdex_usable,
+ OatFileAssistant::Location expected_location) {
+ OatFileAssistant::DexOptStatus status;
+ EXPECT_EQ(
+ assistant->GetDexOptNeeded(compiler_filter, dexopt_trigger, &status),
+ expected_dexopt_needed);
+ EXPECT_EQ(status.IsVdexUsable(), expected_is_vdex_usable);
+ EXPECT_EQ(status.GetLocation(), expected_location);
+ }
+
+ // Verifies all versions of `GetDexOptNeeded` with the default dexopt trigger.
+ void VerifyGetDexOptNeededDefault(OatFileAssistant* assistant,
+ CompilerFilter::Filter compiler_filter,
+ bool expected_dexopt_needed,
+ bool expected_is_vdex_usable,
+ OatFileAssistant::Location expected_location,
+ int expected_legacy_result) {
+ // Verify the current version (called from artd).
+ VerifyGetDexOptNeeded(assistant,
+ compiler_filter,
+ default_trigger_,
+ expected_dexopt_needed,
+ expected_is_vdex_usable,
+ expected_location);
+
+ // Verify the legacy version (called from PM).
+ EXPECT_EQ(
+ assistant->GetDexOptNeeded(compiler_filter, /*profile_changed=*/false, /*downgrade=*/false),
+ expected_legacy_result);
}
static std::unique_ptr<ClassLoaderContext> InitializeDefaultContext() {
@@ -115,7 +175,65 @@
return context;
}
+ // Temporarily disables the pointer to the current runtime if `with_runtime_` is false.
+ // Essentially simulates an environment where there is no active runtime.
+ android::base::ScopeGuard<std::function<void()>> ScopedMaybeWithoutRuntime() {
+ if (!with_runtime_) {
+ Runtime::TestOnlySetCurrent(nullptr);
+ }
+ return android::base::make_scope_guard(
+ [this]() { Runtime::TestOnlySetCurrent(runtime_.get()); });
+ }
+
+ std::unique_ptr<OatFileAssistantContext> CreateOatFileAssistantContext() {
+ return std::make_unique<OatFileAssistantContext>(
+ std::make_unique<OatFileAssistantContext::RuntimeOptions>(
+ OatFileAssistantContext::RuntimeOptions{
+ .image_locations = runtime_->GetImageLocations(),
+ .boot_class_path = runtime_->GetBootClassPath(),
+ .boot_class_path_locations = runtime_->GetBootClassPathLocations(),
+ .boot_class_path_fds = !runtime_->GetBootClassPathFds().empty() ?
+ &runtime_->GetBootClassPathFds() :
+ nullptr,
+ .deny_art_apex_data_files = runtime_->DenyArtApexDataFiles(),
+ }));
+ }
+
+ OatFileAssistantContext* MaybeGetOatFileAssistantContext() {
+ return with_runtime_ ? nullptr : ofa_context_.get();
+ }
+
+ // A helper function to create OatFileAssistant with some default arguments.
+ OatFileAssistant CreateOatFileAssistant(const char* dex_location,
+ ClassLoaderContext* context = nullptr,
+ bool load_executable = false,
+ int vdex_fd = -1,
+ int oat_fd = -1,
+ int zip_fd = -1) {
+ return OatFileAssistant(dex_location,
+ kRuntimeISA,
+ context != nullptr ? context : default_context_.get(),
+ load_executable,
+ /*only_load_trusted_executable=*/false,
+ MaybeGetOatFileAssistantContext(),
+ vdex_fd,
+ oat_fd,
+ zip_fd);
+ }
+
+ void ExpectHasDexFiles(OatFileAssistant* oat_file_assistant, bool expected_value) {
+ std::string error_msg;
+ std::optional<bool> has_dex_files = oat_file_assistant->HasDexFiles(&error_msg);
+ ASSERT_TRUE(has_dex_files.has_value()) << error_msg;
+ EXPECT_EQ(*has_dex_files, expected_value);
+ }
+
std::unique_ptr<ClassLoaderContext> default_context_ = InitializeDefaultContext();
+ bool with_runtime_;
+ const OatFileAssistant::DexOptTrigger default_trigger_{.targetFilterIsBetter = true,
+ .primaryBootImageBecomesUsable = true};
+ std::unique_ptr<OatFileAssistantContext> ofa_context_;
+ std::vector<std::unique_ptr<const DexFile>> opened_dex_files_;
};
class ScopedNonWritable {
@@ -154,7 +272,7 @@
// Case: We have a MultiDEX file and up-to-date ODEX file for it with relative
// encoded dex locations.
// Expect: The oat file status is kNoDexOptNeeded.
-TEST_F(OatFileAssistantTest, RelativeEncodedDexLocation) {
+TEST_P(OatFileAssistantTest, RelativeEncodedDexLocation) {
std::string dex_location = GetScratchDir() + "/RelativeEncodedDexLocation.jar";
std::string odex_location = GetOdexDir() + "/RelativeEncodedDexLocation.odex";
@@ -172,21 +290,24 @@
std::string error_msg;
ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
// Verify we can load both dex files.
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- true);
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(),
+ /*context=*/nullptr,
+ /*load_executable=*/true);
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
- EXPECT_TRUE(oat_file->IsExecutable());
+ if (with_runtime_) {
+ EXPECT_TRUE(oat_file->IsExecutable());
+ }
std::vector<std::unique_ptr<const DexFile>> dex_files;
dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
EXPECT_EQ(2u, dex_files.size());
}
-TEST_F(OatFileAssistantTest, MakeUpToDateWithContext) {
+TEST_P(OatFileAssistantTest, MakeUpToDateWithContext) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string odex_location = GetOdexDir() + "/TestDex.odex";
std::string context_location = GetScratchDir() + "/ContextDex.jar";
@@ -198,8 +319,6 @@
ASSERT_TRUE(context != nullptr);
ASSERT_TRUE(context->OpenDexFiles());
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, context.get(), false);
-
std::string error_msg;
std::vector<std::string> args;
args.push_back("--dex-file=" + dex_location);
@@ -207,6 +326,10 @@
args.push_back("--class-loader-context=" + context_str);
ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(), context.get());
+
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_NE(nullptr, oat_file.get());
ASSERT_NE(nullptr, oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey));
@@ -214,7 +337,7 @@
oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey));
}
-TEST_F(OatFileAssistantTest, GetDexOptNeededWithUpToDateContextRelative) {
+TEST_P(OatFileAssistantTest, GetDexOptNeededWithUpToDateContextRelative) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string odex_location = GetOdexDir() + "/TestDex.odex";
std::string context_location = GetScratchDir() + "/ContextDex.jar";
@@ -228,11 +351,6 @@
std::vector<int> context_fds;
ASSERT_TRUE(relative_context->OpenDexFiles(GetScratchDir(), context_fds));
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- relative_context.get(),
- false);
-
std::string error_msg;
std::vector<std::string> args;
args.push_back("--dex-file=" + dex_location);
@@ -240,56 +358,80 @@
args.push_back("--class-loader-context=PCL[" + context_location + "]");
ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kDefaultCompilerFilter));
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant =
+ CreateOatFileAssistant(dex_location.c_str(), relative_context.get());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kDefaultCompilerFilter,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kNoDexOptNeeded);
}
// Case: We have a DEX file, but no OAT file for it.
// Expect: The status is kDex2OatNeeded.
-TEST_F(OatFileAssistantTest, DexNoOat) {
+TEST_P(OatFileAssistantTest, DexNoOat) {
std::string dex_location = GetScratchDir() + "/DexNoOat.jar";
Copy(GetDexSrc1(), dex_location);
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeedProfile));
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeedProfile,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
VerifyOptimizationStatus(
- &oat_file_assistant,
- dex_location,
- "run-from-apk",
- "unknown",
- "io-error-no-oat");
+ dex_location, default_context_.get(), "run-from-apk", "unknown", "io-error-no-oat");
}
// Case: We have no DEX file and no OAT file.
// Expect: Status is kNoDexOptNeeded. Loading should fail, but not crash.
-TEST_F(OatFileAssistantTest, NoDexNoOat) {
+TEST_P(OatFileAssistantTest, NoDexNoOat) {
std::string dex_location = GetScratchDir() + "/NoDexNoOat.jar";
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- true);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
- EXPECT_FALSE(oat_file_assistant.HasDexFiles());
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ std::string error_msg_ignored;
+ EXPECT_FALSE(oat_file_assistant.HasDexFiles(&error_msg_ignored).has_value());
// Trying to get the best oat file should fail, but not crash.
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -298,85 +440,106 @@
// Case: We have a DEX file and an ODEX file, but no OAT file.
// Expect: The status is kNoDexOptNeeded.
-TEST_F(OatFileAssistantTest, OdexUpToDate) {
+TEST_P(OatFileAssistantTest, OdexUpToDate) {
std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar";
std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex";
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, "install");
- // Force the use of oat location by making the dex parent not writable.
- OatFileAssistant oat_file_assistant(
- dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- /*load_executable=*/ false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kEverything));
+ // Force the use of oat location by making the dex parent not writable.
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kEverything,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kDex2OatForFilter);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
VerifyOptimizationStatus(
- &oat_file_assistant,
- dex_location,
- CompilerFilter::kSpeed,
- "install",
- "up-to-date");
+ dex_location, default_context_.get(), CompilerFilter::kSpeed, "install", "up-to-date");
}
// Case: We have an ODEX file compiled against partial boot image.
// Expect: The status is kNoDexOptNeeded.
-TEST_F(OatFileAssistantTest, OdexUpToDatePartialBootImage) {
+TEST_P(OatFileAssistantTest, OdexUpToDatePartialBootImage) {
std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar";
std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex";
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, "install");
// Insert an extra dex file to the boot class path.
- InsertNewBootClasspathEntry();
+ std::string error_msg;
+ ASSERT_TRUE(InsertNewBootClasspathEntry(GetMultiDexSrc1(), &error_msg)) << error_msg;
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
// Force the use of oat location by making the dex parent not writable.
- OatFileAssistant oat_file_assistant(
- dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- /*load_executable=*/ false);
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kEverything));
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kEverything,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kDex2OatForFilter);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
VerifyOptimizationStatus(
- &oat_file_assistant,
- dex_location,
- CompilerFilter::kSpeed,
- "install",
- "up-to-date");
+ dex_location, default_context_.get(), CompilerFilter::kSpeed, "install", "up-to-date");
}
// Case: We have a DEX file and a PIC ODEX file, but no OAT file. We load the dex
// file via a symlink.
// Expect: The status is kNoDexOptNeeded.
-TEST_F(OatFileAssistantTest, OdexUpToDateSymLink) {
+TEST_P(OatFileAssistantTest, OdexUpToDateSymLink) {
std::string scratch_dir = GetScratchDir();
std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar";
std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex";
@@ -389,29 +552,44 @@
ASSERT_EQ(0, symlink(scratch_dir.c_str(), link.c_str()));
dex_location = link + "/OdexUpToDate.jar";
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kEverything));
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kEverything,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kDex2OatForFilter);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
}
// Case: We have a DEX file and up-to-date OAT file for it.
// Expect: The status is kNoDexOptNeeded.
-TEST_F(OatFileAssistantTest, OatUpToDate) {
+TEST_P(OatFileAssistantTest, OatUpToDate) {
if (IsExecutedAsRoot()) {
// We cannot simulate non writable locations when executed as root: b/38000545.
LOG(ERROR) << "Test skipped because it's running as root";
@@ -426,36 +604,47 @@
ScopedNonWritable scoped_non_writable(dex_location);
ASSERT_TRUE(scoped_non_writable.IsSuccessful());
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kEverything));
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kEverything,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatForFilter);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
VerifyOptimizationStatus(
- &oat_file_assistant,
- dex_location,
- CompilerFilter::kSpeed,
- "unknown",
- "up-to-date");
+ dex_location, default_context_.get(), CompilerFilter::kSpeed, "unknown", "up-to-date");
}
// Case: Passing valid file descriptors of updated odex/vdex files along with the dex file.
// Expect: The status is kNoDexOptNeeded.
-TEST_F(OatFileAssistantTest, GetDexOptNeededWithFd) {
+TEST_P(OatFileAssistantTest, GetDexOptNeededWithFd) {
std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
std::string odex_location = GetScratchDir() + "/OatUpToDate.odex";
std::string vdex_location = GetScratchDir() + "/OatUpToDate.vdex";
@@ -470,32 +659,48 @@
android::base::unique_fd vdex_fd(open(vdex_location.c_str(), O_RDONLY | O_CLOEXEC));
android::base::unique_fd zip_fd(open(dex_location.c_str(), O_RDONLY | O_CLOEXEC));
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false,
- false,
- vdex_fd.get(),
- odex_fd.get(),
- zip_fd.get());
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kEverything));
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(),
+ /*context=*/nullptr,
+ /*load_executable=*/false,
+ vdex_fd.get(),
+ odex_fd.get(),
+ zip_fd.get());
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kEverything,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kDex2OatForFilter);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
}
// Case: Passing invalid odex fd and valid vdex and zip fds.
// Expect: The status should be kDex2OatForBootImage.
-TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexFd) {
+TEST_P(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexFd) {
std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
std::string odex_location = GetScratchDir() + "/OatUpToDate.odex";
std::string vdex_location = GetScratchDir() + "/OatUpToDate.vdex";
@@ -509,30 +714,42 @@
android::base::unique_fd vdex_fd(open(vdex_location.c_str(), O_RDONLY | O_CLOEXEC));
android::base::unique_fd zip_fd(open(dex_location.c_str(), O_RDONLY | O_CLOEXEC));
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false,
- false,
- vdex_fd.get(),
- /* oat_fd= */ -1,
- zip_fd.get());
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
- EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
- EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kEverything));
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(),
+ /*context=*/nullptr,
+ /*load_executable=*/false,
+ vdex_fd.get(),
+ /*oat_fd=*/-1,
+ zip_fd.get());
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kDex2OatForFilter);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kEverything,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kDex2OatForFilter);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
}
// Case: Passing invalid vdex fd and valid odex and zip fds.
// Expect: The status should be kDex2OatFromScratch.
-TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidVdexFd) {
+TEST_P(OatFileAssistantTest, GetDexOptNeededWithInvalidVdexFd) {
std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
std::string odex_location = GetScratchDir() + "/OatUpToDate.odex";
@@ -545,48 +762,57 @@
android::base::unique_fd odex_fd(open(odex_location.c_str(), O_RDONLY | O_CLOEXEC));
android::base::unique_fd zip_fd(open(dex_location.c_str(), O_RDONLY | O_CLOEXEC));
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false,
- false,
- /* vdex_fd= */ -1,
- odex_fd.get(),
- zip_fd.get());
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(),
+ /*context=*/nullptr,
+ /*load_executable=*/false,
+ /*vdex_fd=*/-1,
+ odex_fd.get(),
+ zip_fd.get());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
}
// Case: Passing invalid vdex and odex fd with valid zip fd.
// Expect: The status is kDex2oatFromScratch.
-TEST_F(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexVdexFd) {
+TEST_P(OatFileAssistantTest, GetDexOptNeededWithInvalidOdexVdexFd) {
std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
Copy(GetDexSrc1(), dex_location);
android::base::unique_fd zip_fd(open(dex_location.c_str(), O_RDONLY | O_CLOEXEC));
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false,
- false,
- /* vdex_fd= */ -1,
- /* oat_fd= */ -1,
- zip_fd);
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(),
+ /*context=*/nullptr,
+ /*load_executable=*/false,
+ /*vdex_fd=*/-1,
+ /*oat_fd=*/-1,
+ zip_fd);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
}
// Case: We have a DEX file and up-to-date VDEX file for it, but no
// ODEX file.
-TEST_F(OatFileAssistantTest, VdexUpToDateNoOdex) {
+TEST_P(OatFileAssistantTest, VdexUpToDateNoOdex) {
std::string dex_location = GetScratchDir() + "/VdexUpToDateNoOdex.jar";
std::string odex_location = GetOdexDir() + "/VdexUpToDateNoOdex.oat";
@@ -597,30 +823,32 @@
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
ASSERT_EQ(0, unlink(odex_location.c_str()));
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
- EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kDex2OatForFilter);
// Make sure we don't crash in this case when we dump the status. We don't
// care what the actual dumped value is.
oat_file_assistant.GetStatusDump();
- VerifyOptimizationStatus(
- &oat_file_assistant,
- dex_location,
- "verify",
- "vdex",
- "up-to-date");
+ VerifyOptimizationStatus(dex_location, default_context_.get(), "verify", "vdex", "up-to-date");
}
// Case: We have a DEX file and empty VDEX and ODEX files.
-TEST_F(OatFileAssistantTest, EmptyVdexOdex) {
+TEST_P(OatFileAssistantTest, EmptyVdexOdex) {
std::string dex_location = GetScratchDir() + "/EmptyVdexOdex.jar";
std::string odex_location = GetOdexDir() + "/EmptyVdexOdex.oat";
std::string vdex_location = GetOdexDir() + "/EmptyVdexOdex.vdex";
@@ -629,17 +857,20 @@
ScratchFile vdex_file(vdex_location.c_str());
ScratchFile odex_file(odex_location.c_str());
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
}
// Case: We have a DEX file and up-to-date (OAT) VDEX file for it, but no OAT
// file.
-TEST_F(OatFileAssistantTest, VdexUpToDateNoOat) {
+TEST_P(OatFileAssistantTest, VdexUpToDateNoOat) {
if (IsExecutedAsRoot()) {
// We cannot simulate non writable locations when executed as root: b/38000545.
LOG(ERROR) << "Test skipped because it's running as root";
@@ -650,7 +881,8 @@
std::string oat_location;
std::string error_msg;
ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename(
- dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg;
+ dex_location, kRuntimeISA, /* deny_art_apex_data_files= */false, &oat_location, &error_msg))
+ << error_msg;
Copy(GetDexSrc1(), dex_location);
GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
@@ -658,19 +890,23 @@
ScopedNonWritable scoped_non_writable(dex_location);
ASSERT_TRUE(scoped_non_writable.IsSuccessful());
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
- EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatForFilter);
}
// Case: We have a DEX file and speed-profile OAT file for it.
// Expect: The status is kNoDexOptNeeded if the profile hasn't changed, but
// kDex2Oat if the profile has changed.
-TEST_F(OatFileAssistantTest, ProfileOatUpToDate) {
+TEST_P(OatFileAssistantTest, ProfileOatUpToDate) {
if (IsExecutedAsRoot()) {
// We cannot simulate non writable locations when executed as root: b/38000545.
LOG(ERROR) << "Test skipped because it's running as root";
@@ -684,29 +920,62 @@
ScopedNonWritable scoped_non_writable(dex_location);
ASSERT_TRUE(scoped_non_writable.IsSuccessful());
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeedProfile,
+ default_trigger_,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat);
+ EXPECT_EQ(
+ OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile, /*profile_changed=*/false));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ default_trigger_,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat);
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeedProfile, false));
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerify, /*profile_changed=*/false));
+
+ OatFileAssistant::DexOptTrigger profile_changed_trigger = default_trigger_;
+ profile_changed_trigger.targetFilterIsSame = true;
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeedProfile,
+ profile_changed_trigger,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat);
+ EXPECT_EQ(
+ OatFileAssistant::kDex2OatForFilter,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile, /*profile_changed=*/true));
+
+ // We should not recompile even if `profile_changed` is true because the compiler filter should
+ // not be downgraded.
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ profile_changed_trigger,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat);
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify, false));
- EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeedProfile, true));
- EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify, true));
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerify, /*profile_changed=*/true));
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
}
// Case: We have a MultiDEX file and up-to-date OAT file for it.
// Expect: The status is kNoDexOptNeeded and we load all dex files.
-TEST_F(OatFileAssistantTest, MultiDexOatUpToDate) {
+TEST_P(OatFileAssistantTest, MultiDexOatUpToDate) {
if (IsExecutedAsRoot()) {
// We cannot simulate non writable locations when executed as root: b/38000545.
LOG(ERROR) << "Test skipped because it's running as root";
@@ -720,18 +989,25 @@
ScopedNonWritable scoped_non_writable(dex_location);
ASSERT_TRUE(scoped_non_writable.IsSuccessful());
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- true);
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(),
+ /*context=*/nullptr,
+ /*load_executable=*/true);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ ExpectHasDexFiles(&oat_file_assistant, true);
// Verify we can load both dex files.
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
- EXPECT_TRUE(oat_file->IsExecutable());
+ if (with_runtime_) {
+ EXPECT_TRUE(oat_file->IsExecutable());
+ }
std::vector<std::unique_ptr<const DexFile>> dex_files;
dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
EXPECT_EQ(2u, dex_files.size());
@@ -739,7 +1015,7 @@
// Case: We have a MultiDEX file where the non-main multdex entry is out of date.
// Expect: The status is kDex2OatNeeded.
-TEST_F(OatFileAssistantTest, MultiDexNonMainOutOfDate) {
+TEST_P(OatFileAssistantTest, MultiDexNonMainOutOfDate) {
if (IsExecutedAsRoot()) {
// We cannot simulate non writable locations when executed as root: b/38000545.
LOG(ERROR) << "Test skipped because it's running as root";
@@ -759,18 +1035,21 @@
ScopedNonWritable scoped_non_writable(dex_location);
ASSERT_TRUE(scoped_non_writable.IsSuccessful());
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- true);
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
+ ExpectHasDexFiles(&oat_file_assistant, true);
}
// Case: We have a DEX file and an OAT file out of date with respect to the
// dex checksum.
-TEST_F(OatFileAssistantTest, OatDexOutOfDate) {
+TEST_P(OatFileAssistantTest, OatDexOutOfDate) {
if (IsExecutedAsRoot()) {
// We cannot simulate non writable locations when executed as root: b/38000545.
LOG(ERROR) << "Test skipped because it's running as root";
@@ -788,31 +1067,34 @@
ScopedNonWritable scoped_non_writable(dex_location);
ASSERT_TRUE(scoped_non_writable.IsSuccessful());
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
VerifyOptimizationStatus(
- &oat_file_assistant,
- dex_location,
- "run-from-apk-fallback",
- "unknown",
- "apk-more-recent");
+ dex_location, default_context_.get(), "run-from-apk-fallback", "unknown", "apk-more-recent");
}
// Case: We have a DEX file and an (ODEX) VDEX file out of date with respect
// to the dex checksum, but no ODEX file.
-TEST_F(OatFileAssistantTest, VdexDexOutOfDate) {
+TEST_P(OatFileAssistantTest, VdexDexOutOfDate) {
std::string dex_location = GetScratchDir() + "/VdexDexOutOfDate.jar";
std::string odex_location = GetOdexDir() + "/VdexDexOutOfDate.oat";
@@ -821,18 +1103,21 @@
ASSERT_EQ(0, unlink(odex_location.c_str()));
Copy(GetDexSrc2(), dex_location);
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
}
// Case: We have a MultiDEX (ODEX) VDEX file where the non-main multidex entry
// is out of date and there is no corresponding ODEX file.
-TEST_F(OatFileAssistantTest, VdexMultiDexNonMainOutOfDate) {
+TEST_P(OatFileAssistantTest, VdexMultiDexNonMainOutOfDate) {
std::string dex_location = GetScratchDir() + "/VdexMultiDexNonMainOutOfDate.jar";
std::string odex_location = GetOdexDir() + "/VdexMultiDexNonMainOutOfDate.odex";
@@ -841,18 +1126,21 @@
ASSERT_EQ(0, unlink(odex_location.c_str()));
Copy(GetMultiDexSrc2(), dex_location);
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
}
// Case: We have a DEX file and an OAT file out of date with respect to the
// boot image.
-TEST_F(OatFileAssistantTest, OatImageOutOfDate) {
+TEST_P(OatFileAssistantTest, OatImageOutOfDate) {
if (IsExecutedAsRoot()) {
// We cannot simulate non writable locations when executed as root: b/38000545.
LOG(ERROR) << "Test skipped because it's running as root";
@@ -869,35 +1157,70 @@
ScopedNonWritable scoped_non_writable(dex_location);
ASSERT_TRUE(scoped_non_writable.IsSuccessful());
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
- EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatForFilter);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatBootImageOutOfDate, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
+
+ VerifyOptimizationStatus(dex_location, default_context_.get(), "verify", "vdex", "up-to-date");
+}
+
+TEST_P(OatFileAssistantTest, OatContextOutOfDate) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string odex_location = GetOdexDir() + "/TestDex.odex";
+
+ std::string context_location = GetScratchDir() + "/ContextDex.jar";
+ Copy(GetDexSrc1(), dex_location);
+ Copy(GetDexSrc2(), context_location);
+
+ std::string error_msg;
+ std::vector<std::string> args;
+ args.push_back("--dex-file=" + dex_location);
+ args.push_back("--oat-file=" + odex_location);
+ args.push_back("--class-loader-context=PCL[" + context_location + "]");
+ ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
+
+ // Update the context by overriding the jar file.
+ Copy(GetMultiDexSrc2(), context_location);
+
+ std::unique_ptr<ClassLoaderContext> context =
+ ClassLoaderContext::Create("PCL[" + context_location + "]");
+ ASSERT_TRUE(context != nullptr);
+ ASSERT_TRUE(context->OpenDexFiles());
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
VerifyOptimizationStatus(
- &oat_file_assistant,
- dex_location,
- "verify",
- "vdex",
- "up-to-date");
+ dex_location.c_str(), context.get(), "verify", "vdex", "up-to-date", /*check_context=*/true);
}
// Case: We have a DEX file and a verify-at-runtime OAT file out of date with
// respect to the boot image.
// It shouldn't matter that the OAT file is out of date, because it is
// verify-at-runtime.
-TEST_F(OatFileAssistantTest, OatVerifyAtRuntimeImageOutOfDate) {
+TEST_P(OatFileAssistantTest, OatVerifyAtRuntimeImageOutOfDate) {
if (IsExecutedAsRoot()) {
// We cannot simulate non writable locations when executed as root: b/38000545.
LOG(ERROR) << "Test skipped because it's running as root";
@@ -914,23 +1237,30 @@
ScopedNonWritable scoped_non_writable(dex_location);
ASSERT_TRUE(scoped_non_writable.IsSuccessful());
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOat,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatForFilter);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
}
// Case: We have a DEX file and an ODEX file, but no OAT file.
-TEST_F(OatFileAssistantTest, DexOdexNoOat) {
+TEST_P(OatFileAssistantTest, DexOdexNoOat) {
std::string dex_location = GetScratchDir() + "/DexOdexNoOat.jar";
std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex";
@@ -938,21 +1268,28 @@
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
- // Verify the status.
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ // Verify the status.
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
// We should still be able to get the non-executable odex file to run from.
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -961,41 +1298,56 @@
// Case: We have a resource-only DEX file, no ODEX file and no
// OAT file. Expect: The status is kNoDexOptNeeded.
-TEST_F(OatFileAssistantTest, ResourceOnlyDex) {
+TEST_P(OatFileAssistantTest, ResourceOnlyDex) {
std::string dex_location = GetScratchDir() + "/ResourceOnlyDex.jar";
Copy(GetResourceOnlySrc1(), dex_location);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
// Verify the status.
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- true);
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kVerify));
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
- EXPECT_FALSE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, false);
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
- EXPECT_FALSE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, false);
}
// Case: We have a DEX file, an ODEX file and an OAT file.
// Expect: It shouldn't crash. We should load the odex file executable.
-TEST_F(OatFileAssistantTest, OdexOatOverlap) {
+TEST_P(OatFileAssistantTest, OdexOatOverlap) {
std::string dex_location = GetScratchDir() + "/OdexOatOverlap.jar";
std::string odex_location = GetOdexDir() + "/OdexOatOverlap.odex";
@@ -1004,24 +1356,31 @@
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
- // Verify things don't go bad.
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- true);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ // Verify things don't go bad.
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(),
+ /*context=*/nullptr,
+ /*load_executable=*/true);
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
- EXPECT_TRUE(oat_file->IsExecutable());
+ if (with_runtime_) {
+ EXPECT_TRUE(oat_file->IsExecutable());
+ }
std::vector<std::unique_ptr<const DexFile>> dex_files;
dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
EXPECT_EQ(1u, dex_files.size());
@@ -1029,7 +1388,7 @@
// Case: We have a DEX file and a VerifyAtRuntime ODEX file, but no OAT file.
// Expect: The status is kNoDexOptNeeded, because VerifyAtRuntime contains no code.
-TEST_F(OatFileAssistantTest, DexVerifyAtRuntimeOdexNoOat) {
+TEST_P(OatFileAssistantTest, DexVerifyAtRuntimeOdexNoOat) {
std::string dex_location = GetScratchDir() + "/DexVerifyAtRuntimeOdexNoOat.jar";
std::string odex_location = GetOdexDir() + "/DexVerifyAtRuntimeOdexNoOat.odex";
@@ -1037,26 +1396,33 @@
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kExtract);
- // Verify the status.
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
- EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ // Verify the status.
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kDex2OatForFilter);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
- EXPECT_TRUE(oat_file_assistant.HasDexFiles());
+ ExpectHasDexFiles(&oat_file_assistant, true);
}
// Case: We have a DEX file and up-to-date OAT file for it.
// Expect: We should load an executable dex file.
-TEST_F(OatFileAssistantTest, LoadOatUpToDate) {
+TEST_P(OatFileAssistantTest, LoadOatUpToDate) {
if (IsExecutedAsRoot()) {
// We cannot simulate non writable locations when executed as root: b/38000545.
LOG(ERROR) << "Test skipped because it's running as root";
@@ -1071,15 +1437,18 @@
ScopedNonWritable scoped_non_writable(dex_location);
ASSERT_TRUE(scoped_non_writable.IsSuccessful());
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
// Load the oat using an oat file assistant.
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- true);
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(),
+ /*context=*/nullptr,
+ /*load_executable=*/true);
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
- EXPECT_TRUE(oat_file->IsExecutable());
+ if (with_runtime_) {
+ EXPECT_TRUE(oat_file->IsExecutable());
+ }
std::vector<std::unique_ptr<const DexFile>> dex_files;
dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
EXPECT_EQ(1u, dex_files.size());
@@ -1087,7 +1456,7 @@
// Case: We have a DEX file and up-to-date quicken OAT file for it.
// Expect: We should still load the oat file as executable.
-TEST_F(OatFileAssistantTest, LoadExecInterpretOnlyOatUpToDate) {
+TEST_P(OatFileAssistantTest, LoadExecInterpretOnlyOatUpToDate) {
if (IsExecutedAsRoot()) {
// We cannot simulate non writable locations when executed as root: b/38000545.
LOG(ERROR) << "Test skipped because it's running as root";
@@ -1102,15 +1471,18 @@
ScopedNonWritable scoped_non_writable(dex_location);
ASSERT_TRUE(scoped_non_writable.IsSuccessful());
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
// Load the oat using an oat file assistant.
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- true);
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(),
+ /*context=*/nullptr,
+ /*load_executable=*/true);
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
- EXPECT_TRUE(oat_file->IsExecutable());
+ if (with_runtime_) {
+ EXPECT_TRUE(oat_file->IsExecutable());
+ }
std::vector<std::unique_ptr<const DexFile>> dex_files;
dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
EXPECT_EQ(1u, dex_files.size());
@@ -1118,7 +1490,7 @@
// Case: We have a DEX file and up-to-date OAT file for it.
// Expect: Loading non-executable should load the oat non-executable.
-TEST_F(OatFileAssistantTest, LoadNoExecOatUpToDate) {
+TEST_P(OatFileAssistantTest, LoadNoExecOatUpToDate) {
if (IsExecutedAsRoot()) {
// We cannot simulate non writable locations when executed as root: b/38000545.
LOG(ERROR) << "Test skipped because it's running as root";
@@ -1134,15 +1506,18 @@
GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
// Load the oat using an oat file assistant.
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(),
+ /*context=*/nullptr,
+ /*load_executable=*/true);
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
- EXPECT_FALSE(oat_file->IsExecutable());
+ if (with_runtime_) {
+ EXPECT_TRUE(oat_file->IsExecutable());
+ }
std::vector<std::unique_ptr<const DexFile>> dex_files;
dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
EXPECT_EQ(1u, dex_files.size());
@@ -1186,54 +1561,64 @@
// Case: Non-absolute path to Dex location.
// Expect: Not sure, but it shouldn't crash.
-TEST_F(OatFileAssistantTest, NonAbsoluteDexLocation) {
+TEST_P(OatFileAssistantTest, NonAbsoluteDexLocation) {
std::string abs_dex_location = GetScratchDir() + "/NonAbsoluteDexLocation.jar";
Copy(GetDexSrc1(), abs_dex_location);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
std::string dex_location = MakePathRelative(abs_dex_location);
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- true);
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
}
// Case: Very short, non-existent Dex location.
// Expect: kNoDexOptNeeded.
-TEST_F(OatFileAssistantTest, ShortDexLocation) {
+TEST_P(OatFileAssistantTest, ShortDexLocation) {
std::string dex_location = "/xx";
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- true);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
- EXPECT_FALSE(oat_file_assistant.HasDexFiles());
+ std::string error_msg_ignored;
+ EXPECT_FALSE(oat_file_assistant.HasDexFiles(&error_msg_ignored).has_value());
}
// Case: Non-standard extension for dex file.
// Expect: The status is kDex2OatNeeded.
-TEST_F(OatFileAssistantTest, LongDexExtension) {
+TEST_P(OatFileAssistantTest, LongDexExtension) {
std::string dex_location = GetScratchDir() + "/LongDexExtension.jarx";
Copy(GetDexSrc1(), dex_location);
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
- EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kSpeed));
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kDex2OatFromScratch);
EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
@@ -1243,7 +1628,7 @@
// A task to generate a dex location. Used by the RaceToGenerate test.
class RaceGenerateTask : public Task {
public:
- RaceGenerateTask(OatFileAssistantTest& test,
+ RaceGenerateTask(OatFileAssistantBaseTest& test,
const std::string& dex_location,
const std::string& oat_location,
Mutex* lock)
@@ -1251,8 +1636,7 @@
dex_location_(dex_location),
oat_location_(oat_location),
lock_(lock),
- loaded_oat_file_(nullptr)
- {}
+ loaded_oat_file_(nullptr) {}
void Run(Thread* self ATTRIBUTE_UNUSED) override {
// Load the dex files, and save a pointer to the loaded oat file, so that
@@ -1288,7 +1672,7 @@
}
private:
- OatFileAssistantTest& test_;
+ OatFileAssistantBaseTest& test_;
std::string dex_location_;
std::string oat_location_;
Mutex* lock_;
@@ -1297,7 +1681,7 @@
// Test the case where dex2oat invocations race with multiple processes trying to
// load the oat file.
-TEST_F(OatFileAssistantTest, RaceToGenerate) {
+TEST_F(OatFileAssistantBaseTest, RaceToGenerate) {
std::string dex_location = GetScratchDir() + "/RaceToGenerate.jar";
std::string oat_location = GetOdexDir() + "/RaceToGenerate.oat";
@@ -1309,7 +1693,7 @@
// take a while to generate.
Copy(GetLibCoreDexFileNames()[0], dex_location);
- const size_t kNumThreads = 32;
+ const size_t kNumThreads = 16;
Thread* self = Thread::Current();
ThreadPool thread_pool("Oat file assistant test thread pool", kNumThreads);
std::vector<std::unique_ptr<RaceGenerateTask>> tasks;
@@ -1336,7 +1720,7 @@
// Case: We have a DEX file and an ODEX file, and no OAT file,
// Expect: We should load the odex file executable.
-TEST_F(OatFileAssistantTest, LoadDexOdexNoOat) {
+TEST_P(OatFileAssistantTest, LoadDexOdexNoOat) {
std::string dex_location = GetScratchDir() + "/LoadDexOdexNoOat.jar";
std::string odex_location = GetOdexDir() + "/LoadDexOdexNoOat.odex";
@@ -1344,15 +1728,18 @@
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
// Load the oat using an executable oat file assistant.
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- true);
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(),
+ /*context=*/nullptr,
+ /*load_executable=*/true);
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
- EXPECT_TRUE(oat_file->IsExecutable());
+ if (with_runtime_) {
+ EXPECT_TRUE(oat_file->IsExecutable());
+ }
std::vector<std::unique_ptr<const DexFile>> dex_files;
dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
EXPECT_EQ(1u, dex_files.size());
@@ -1360,7 +1747,7 @@
// Case: We have a MultiDEX file and an ODEX file, and no OAT file.
// Expect: We should load the odex file executable.
-TEST_F(OatFileAssistantTest, LoadMultiDexOdexNoOat) {
+TEST_P(OatFileAssistantTest, LoadMultiDexOdexNoOat) {
std::string dex_location = GetScratchDir() + "/LoadMultiDexOdexNoOat.jar";
std::string odex_location = GetOdexDir() + "/LoadMultiDexOdexNoOat.odex";
@@ -1368,15 +1755,18 @@
Copy(GetMultiDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
// Load the oat using an executable oat file assistant.
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- default_context_.get(),
- true);
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str(),
+ /*context=*/nullptr,
+ /*load_executable=*/true);
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
ASSERT_TRUE(oat_file.get() != nullptr);
- EXPECT_TRUE(oat_file->IsExecutable());
+ if (with_runtime_) {
+ EXPECT_TRUE(oat_file->IsExecutable());
+ }
std::vector<std::unique_ptr<const DexFile>> dex_files;
dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
EXPECT_EQ(2u, dex_files.size());
@@ -1402,7 +1792,7 @@
// Verify the dexopt status values from dalvik.system.DexFile
// match the OatFileAssistant::DexOptStatus values.
-TEST_F(OatFileAssistantTest, DexOptStatusValues) {
+TEST_F(OatFileAssistantBaseTest, DexOptStatusValues) {
std::pair<OatFileAssistant::DexOptNeeded, const char*> mapping[] = {
{OatFileAssistant::kNoDexOptNeeded, "NO_DEXOPT_NEEDED"},
{OatFileAssistant::kDex2OatFromScratch, "DEX2OAT_FROM_SCRATCH"},
@@ -1426,7 +1816,7 @@
}
}
-TEST_F(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) {
+TEST_P(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string odex_location = GetOdexDir() + "/TestDex.odex";
@@ -1455,43 +1845,366 @@
ASSERT_TRUE(updated_context != nullptr);
std::vector<int> context_fds;
ASSERT_TRUE(updated_context->OpenDexFiles("", context_fds, /*only_read_checksums*/ true));
- OatFileAssistant oat_file_assistant(
- dex_location.c_str(), kRuntimeISA, updated_context.get(), false);
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant =
+ CreateOatFileAssistant(dex_location.c_str(), updated_context.get());
// DexOptNeeded should advise compilation for filter when the context changes.
- EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kDefaultCompilerFilter));
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kDefaultCompilerFilter,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/-OatFileAssistant::kDex2OatForFilter);
}
{
std::unique_ptr<ClassLoaderContext> updated_context = ClassLoaderContext::Create(context_str);
ASSERT_TRUE(updated_context != nullptr);
std::vector<int> context_fds;
- ASSERT_TRUE(updated_context->OpenDexFiles("", context_fds, /*only_read_checksums*/ true));
- OatFileAssistant oat_file_assistant(
- dex_location.c_str(), kRuntimeISA, updated_context.get(), false);
- // Now check that DexOptNeeded does not advise compilation if we only extracted the file.
+ ASSERT_TRUE(updated_context->OpenDexFiles("", context_fds, /*only_read_checksums*/ true));
args.push_back("--compiler-filter=extract");
ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant =
+ CreateOatFileAssistant(dex_location.c_str(), updated_context.get());
+ // Now check that DexOptNeeded does not advise compilation if we only extracted the file.
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
}
{
std::unique_ptr<ClassLoaderContext> updated_context = ClassLoaderContext::Create(context_str);
ASSERT_TRUE(updated_context != nullptr);
std::vector<int> context_fds;
- ASSERT_TRUE(updated_context->OpenDexFiles("", context_fds, /*only_read_checksums*/ true));
- OatFileAssistant oat_file_assistant(
- dex_location.c_str(), kRuntimeISA, updated_context.get(), false);
- // Now check that DexOptNeeded does not advise compilation if we only verify the file.
+ ASSERT_TRUE(updated_context->OpenDexFiles("", context_fds, /*only_read_checksums*/ true));
args.push_back("--compiler-filter=verify");
ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- GetDexOptNeeded(&oat_file_assistant, CompilerFilter::kExtract));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant =
+ CreateOatFileAssistant(dex_location.c_str(), updated_context.get());
+ // Now check that DexOptNeeded does not advise compilation if we only verify the file.
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kExtract,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
}
}
+// Case: We have a DEX file and speed-profile ODEX file for it. The caller's intention is to
+// downgrade the compiler filter.
+// Expect: Dexopt should be performed only if the target compiler filter is worse than the current
+// one.
+TEST_P(OatFileAssistantTest, Downgrade) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string odex_location = GetOdexDir() + "/TestDex.odex";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeedProfile);
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+ OatFileAssistant::DexOptTrigger downgrade_trigger{.targetFilterIsWorse = true};
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ downgrade_trigger,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex);
+ EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(
+ CompilerFilter::kSpeed, /*profile_changed=*/false, /*downgrade=*/true));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeedProfile,
+ downgrade_trigger,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex);
+ EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(
+ CompilerFilter::kSpeedProfile, /*profile_changed=*/false, /*downgrade=*/true));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ downgrade_trigger,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex);
+ EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
+ oat_file_assistant.GetDexOptNeeded(
+ CompilerFilter::kVerify, /*profile_changed=*/false, /*downgrade=*/true));
+}
+
+// Case: We have a DEX file but we don't have an ODEX file for it. The caller's intention is to
+// downgrade the compiler filter.
+// Expect: Dexopt should never be performed regardless of the target compiler filter.
+TEST_P(OatFileAssistantTest, DowngradeNoOdex) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ Copy(GetDexSrc1(), dex_location);
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+ OatFileAssistant::DexOptTrigger downgrade_trigger{.targetFilterIsWorse = true};
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ downgrade_trigger,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(
+ CompilerFilter::kSpeed, /*profile_changed=*/false, /*downgrade=*/true));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeedProfile,
+ downgrade_trigger,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(
+ CompilerFilter::kSpeedProfile, /*profile_changed=*/false, /*downgrade=*/true));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ downgrade_trigger,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(
+ CompilerFilter::kVerify, /*profile_changed=*/false, /*downgrade=*/true));
+}
+
+// Case: We have a DEX file and speed-profile ODEX file for it. The legacy version is called with
+// both `profile_changed` and `downgrade` being true. This won't happen in the real case. Just to be
+// complete.
+// Expect: The behavior should be as `profile_changed` is false and `downgrade` is true.
+TEST_P(OatFileAssistantTest, ProfileChangedDowngrade) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string odex_location = GetOdexDir() + "/TestDex.odex";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeedProfile);
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(
+ CompilerFilter::kSpeed, /*profile_changed=*/true, /*downgrade=*/true));
+
+ EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(
+ CompilerFilter::kSpeedProfile, /*profile_changed=*/true, /*downgrade=*/true));
+
+ EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
+ oat_file_assistant.GetDexOptNeeded(
+ CompilerFilter::kVerify, /*profile_changed=*/true, /*downgrade=*/true));
+}
+
+// Case: We have a DEX file and speed-profile ODEX file for it. The caller's intention is to force
+// the compilation.
+// Expect: Dexopt should be performed regardless of the target compiler filter. The VDEX file is
+// usable.
+//
+// The legacy version does not support this case. Historically, Package Manager does not take the
+// result from OatFileAssistant for forced compilation. It uses an arbitrary non-zero value instead.
+// Therefore, we don't test the legacy version here.
+TEST_P(OatFileAssistantTest, Force) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string odex_location = GetOdexDir() + "/TestDex.odex";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeedProfile);
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+ OatFileAssistant::DexOptTrigger force_trigger{.targetFilterIsBetter = true,
+ .targetFilterIsSame = true,
+ .targetFilterIsWorse = true,
+ .primaryBootImageBecomesUsable = true};
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ force_trigger,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex);
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeedProfile,
+ force_trigger,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex);
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ force_trigger,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationOdex);
+}
+
+// Case: We have a DEX file but we don't have an ODEX file for it. The caller's intention is to
+// force the compilation.
+// Expect: Dexopt should be performed regardless of the target compiler filter. No VDEX file is
+// usable.
+//
+// The legacy version does not support this case. Historically, Package Manager does not take the
+// result from OatFileAssistant for forced compilation. It uses an arbitrary non-zero value instead.
+// Therefore, we don't test the legacy version here.
+TEST_P(OatFileAssistantTest, ForceNoOdex) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ Copy(GetDexSrc1(), dex_location);
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+ OatFileAssistant::DexOptTrigger force_trigger{.targetFilterIsBetter = true,
+ .targetFilterIsSame = true,
+ .targetFilterIsWorse = true,
+ .primaryBootImageBecomesUsable = true};
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ force_trigger,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError);
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeedProfile,
+ force_trigger,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError);
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ force_trigger,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError);
+}
+
+// Case: We have a DEX file and a DM file for it.
+// Expect: Dexopt should be performed if the compiler filter is better than "verify". The location
+// should be kLocationDm.
+//
+// The legacy version should return kDex2OatFromScratch if the target compiler filter is better than
+// "verify".
+TEST_P(OatFileAssistantTest, DmUpToDate) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string dm_location = GetScratchDir() + "/TestDex.dm";
+ std::string odex_location = GetOdexDir() + "/TestDex.odex";
+ std::string vdex_location = GetOdexDir() + "/TestDex.vdex";
+ Copy(GetDexSrc1(), dex_location);
+
+ // Generate temporary ODEX and VDEX files in order to create the DM file from.
+ GenerateOdexForTest(
+ dex_location, odex_location, CompilerFilter::kVerify, "install", {"--copy-dex-files=false"});
+
+ CreateDexMetadata(vdex_location, dm_location);
+
+ // Cleanup the temporary files.
+ ASSERT_EQ(0, unlink(odex_location.c_str()));
+ ASSERT_EQ(0, unlink(vdex_location.c_str()));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ default_trigger_,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationDm);
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kSpeedProfile,
+ default_trigger_,
+ /*expected_dexopt_needed=*/true,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationDm);
+ EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeedProfile));
+
+ VerifyGetDexOptNeeded(&oat_file_assistant,
+ CompilerFilter::kVerify,
+ default_trigger_,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/true,
+ /*expected_location=*/OatFileAssistant::kLocationDm);
+ EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(CompilerFilter::kVerify));
+}
+
+// Case: We have an ODEX file, but the DEX file is gone.
+// Expect: No dexopt is needed, as there's nothing we can do.
+TEST_P(OatFileAssistantTest, OdexNoDex) {
+ std::string dex_location = GetScratchDir() + "/OdexNoDex.jar";
+ std::string odex_location = GetOdexDir() + "/OdexNoDex.oat";
+
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+ ASSERT_EQ(0, unlink(dex_location.c_str()));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+}
+
+// Case: We have a VDEX file, but the DEX file is gone.
+// Expect: No dexopt is needed, as there's nothing we can do.
+TEST_P(OatFileAssistantTest, VdexNoDex) {
+ std::string dex_location = GetScratchDir() + "/VdexNoDex.jar";
+ std::string odex_location = GetOdexDir() + "/VdexNoDex.oat";
+
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
+ ASSERT_EQ(0, unlink(odex_location.c_str()));
+ ASSERT_EQ(0, unlink(dex_location.c_str()));
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
+
+ VerifyGetDexOptNeededDefault(&oat_file_assistant,
+ CompilerFilter::kSpeed,
+ /*expected_dexopt_needed=*/false,
+ /*expected_is_vdex_usable=*/false,
+ /*expected_location=*/OatFileAssistant::kLocationNoneOrError,
+ /*expected_legacy_result=*/OatFileAssistant::kNoDexOptNeeded);
+}
+
// Test that GetLocation of a dex file is the same whether the dex
// filed is backed by an oat file or not.
-TEST_F(OatFileAssistantTest, GetDexLocation) {
+TEST_F(OatFileAssistantBaseTest, GetDexLocation) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string oat_location = GetOdexDir() + "/TestDex.odex";
std::string art_location = GetOdexDir() + "/TestDex.art";
@@ -1539,7 +2252,7 @@
// Test that a dex file on the platform location gets the right hiddenapi domain,
// regardless of whether it has a backing oat file.
-TEST_F(OatFileAssistantTest, SystemFrameworkDir) {
+TEST_F(OatFileAssistantBaseTest, SystemFrameworkDir) {
std::string filebase = "OatFileAssistantTestSystemFrameworkDir";
std::string dex_location = GetAndroidRoot() + "/framework/" + filebase + ".jar";
Copy(GetDexSrc1(), dex_location);
@@ -1619,7 +2332,7 @@
}
// Make sure OAT files that require app images are not loaded as executable.
-TEST_F(OatFileAssistantTest, LoadOatNoArt) {
+TEST_F(OatFileAssistantBaseTest, LoadOatNoArt) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string odex_location = GetOdexDir() + "/TestDex.odex";
std::string art_location = GetOdexDir() + "/TestDex.art";
@@ -1653,7 +2366,7 @@
EXPECT_FALSE(oat_file->IsExecutable());
}
-TEST_F(OatFileAssistantTest, GetDexOptNeededWithApexVersions) {
+TEST_P(OatFileAssistantTest, GetDexOptNeededWithApexVersions) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string odex_location = GetOdexDir() + "/TestDex.odex";
Copy(GetDexSrc1(), dex_location);
@@ -1667,8 +2380,9 @@
args.push_back("--apex-versions=" + Runtime::Current()->GetApexVersions());
ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
- OatFileAssistant oat_file_assistant(
- dex_location.c_str(), kRuntimeISA, default_context_.get(), false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
}
@@ -1681,8 +2395,9 @@
args.push_back("--apex-versions=" + Runtime::Current()->GetApexVersions().substr(0, 1));
ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
- OatFileAssistant oat_file_assistant(
- dex_location.c_str(), kRuntimeISA, default_context_.get(), false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
}
@@ -1695,12 +2410,161 @@
args.push_back("--apex-versions=/1/2/3/4");
ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
- OatFileAssistant oat_file_assistant(
- dex_location.c_str(), kRuntimeISA, default_context_.get(), false);
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ OatFileAssistant oat_file_assistant = CreateOatFileAssistant(dex_location.c_str());
EXPECT_EQ(OatFileAssistant::kOatBootImageOutOfDate, oat_file_assistant.OdexFileStatus());
}
}
+TEST_P(OatFileAssistantTest, Create) {
+ std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar";
+ std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, "install");
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ std::unique_ptr<ClassLoaderContext> context;
+ std::string error_msg;
+ std::unique_ptr<OatFileAssistant> oat_file_assistant =
+ OatFileAssistant::Create(dex_location,
+ GetInstructionSetString(kRuntimeISA),
+ default_context_->EncodeContextForDex2oat(/*base_dir=*/""),
+ /*load_executable=*/false,
+ /*only_load_trusted_executable=*/true,
+ MaybeGetOatFileAssistantContext(),
+ &context,
+ &error_msg);
+ ASSERT_NE(oat_file_assistant, nullptr);
+
+ // Verify that the created instance is usable.
+ VerifyOptimizationStatusWithInstance(oat_file_assistant.get(), "speed", "install", "up-to-date");
+}
+
+TEST_P(OatFileAssistantTest, CreateWithNullContext) {
+ std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar";
+ std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, "install");
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ std::unique_ptr<ClassLoaderContext> context;
+ std::string error_msg;
+ std::unique_ptr<OatFileAssistant> oat_file_assistant =
+ OatFileAssistant::Create(dex_location,
+ GetInstructionSetString(kRuntimeISA),
+ /*context_str=*/std::nullopt,
+ /*load_executable=*/false,
+ /*only_load_trusted_executable=*/true,
+ MaybeGetOatFileAssistantContext(),
+ &context,
+ &error_msg);
+ ASSERT_NE(oat_file_assistant, nullptr);
+ ASSERT_EQ(context, nullptr);
+
+ // Verify that the created instance is usable.
+ VerifyOptimizationStatusWithInstance(oat_file_assistant.get(), "speed", "install", "up-to-date");
+}
+
+TEST_P(OatFileAssistantTest, ErrorOnInvalidIsaString) {
+ std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar";
+ std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, "install");
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ std::unique_ptr<ClassLoaderContext> context;
+ std::string error_msg;
+ EXPECT_EQ(OatFileAssistant::Create(dex_location,
+ /*isa_str=*/"foo",
+ default_context_->EncodeContextForDex2oat(/*base_dir=*/""),
+ /*load_executable=*/false,
+ /*only_load_trusted_executable=*/true,
+ MaybeGetOatFileAssistantContext(),
+ &context,
+ &error_msg),
+ nullptr);
+ EXPECT_EQ(error_msg, "Instruction set 'foo' is invalid");
+}
+
+TEST_P(OatFileAssistantTest, ErrorOnInvalidContextString) {
+ std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar";
+ std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, "install");
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ std::unique_ptr<ClassLoaderContext> context;
+ std::string error_msg;
+ EXPECT_EQ(OatFileAssistant::Create(dex_location,
+ GetInstructionSetString(kRuntimeISA),
+ /*context_str=*/"foo",
+ /*load_executable=*/false,
+ /*only_load_trusted_executable=*/true,
+ MaybeGetOatFileAssistantContext(),
+ &context,
+ &error_msg),
+ nullptr);
+ EXPECT_EQ(error_msg, "Class loader context 'foo' is invalid");
+}
+
+TEST_P(OatFileAssistantTest, ErrorOnInvalidContextFile) {
+ std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar";
+ std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex";
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, "install");
+
+ // Create a broken context file.
+ std::string context_location = GetScratchDir() + "/BrokenContext.jar";
+ std::ofstream output(context_location);
+ output.close();
+
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+ std::unique_ptr<ClassLoaderContext> context;
+ std::string error_msg;
+ EXPECT_EQ(OatFileAssistant::Create(dex_location,
+ GetInstructionSetString(kRuntimeISA),
+ /*context_str=*/"PCL[" + context_location + "]",
+ /*load_executable=*/false,
+ /*only_load_trusted_executable=*/true,
+ MaybeGetOatFileAssistantContext(),
+ &context,
+ &error_msg),
+ nullptr);
+ EXPECT_EQ(error_msg,
+ "Failed to load class loader context files for '" + dex_location +
+ "' with context 'PCL[" + context_location + "]'");
+}
+
+// Verifies that `OatFileAssistant::ValidateBootClassPathChecksums` accepts the checksum string
+// produced by `gc::space::ImageSpace::GetBootClassPathChecksums`.
+TEST_P(OatFileAssistantTest, ValidateBootClassPathChecksums) {
+ std::string error_msg;
+ auto create_and_verify = [&]() {
+ std::string checksums = gc::space::ImageSpace::GetBootClassPathChecksums(
+ ArrayRef<gc::space::ImageSpace* const>(runtime_->GetHeap()->GetBootImageSpaces()),
+ ArrayRef<const DexFile* const>(runtime_->GetClassLinker()->GetBootClassPath()));
+ std::string bcp_locations = android::base::Join(runtime_->GetBootClassPathLocations(), ':');
+
+ ofa_context_ = CreateOatFileAssistantContext();
+ auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+ return OatFileAssistant::ValidateBootClassPathChecksums(
+ ofa_context_.get(), kRuntimeISA, checksums, bcp_locations, &error_msg);
+ };
+
+ ASSERT_TRUE(create_and_verify()) << error_msg;
+
+ for (const std::string& src : {GetDexSrc1(), GetDexSrc2()}) {
+ ASSERT_TRUE(InsertNewBootClasspathEntry(src, &error_msg)) << error_msg;
+ ASSERT_TRUE(create_and_verify()) << error_msg;
+ }
+}
+
// TODO: More Tests:
// * Test class linker falls back to unquickened dex for DexNoOat
// * Test class linker falls back to unquickened dex for MultiDexNoOat
@@ -1713,4 +2577,7 @@
// - Dex is stripped, don't have odex.
// - Oat file corrupted after status check, before reload unexecutable
// because it's unrelocated and no dex2oat
+
+INSTANTIATE_TEST_SUITE_P(WithOrWithoutRuntime, OatFileAssistantTest, testing::Values(true, false));
+
} // namespace art
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index c3a268d..6351220 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -50,6 +50,7 @@
#include "oat_file.h"
#include "oat_file_assistant.h"
#include "obj_ptr-inl.h"
+#include "runtime_image.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
#include "thread_list.h"
@@ -167,7 +168,7 @@
bool OatFileManager::ShouldLoadAppImage(const OatFile* source_oat_file) const {
Runtime* const runtime = Runtime::Current();
- return kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable());
+ return kEnableAppImage && (!runtime->IsJavaDebuggableAtInit() || source_oat_file->IsDebuggable());
}
std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
@@ -196,11 +197,11 @@
LOG(WARNING) << "Opening an oat file without a class loader. "
<< "Are you using the deprecated DexFile APIs?";
} else if (context != nullptr) {
- OatFileAssistant oat_file_assistant(dex_location,
- kRuntimeISA,
- context.get(),
- runtime->GetOatFilesExecutable(),
- only_use_system_oat_files_);
+ auto oat_file_assistant = std::make_unique<OatFileAssistant>(dex_location,
+ kRuntimeISA,
+ context.get(),
+ runtime->GetOatFilesExecutable(),
+ only_use_system_oat_files_);
// Get the current optimization status for trace debugging.
// Implementation detail note: GetOptimizationStatus will select the same
@@ -210,17 +211,8 @@
std::string compilation_filter;
std::string compilation_reason;
std::string odex_status;
- oat_file_assistant.GetOptimizationStatus(
- &odex_location,
- &compilation_filter,
- &compilation_reason,
- &odex_status);
-
- Runtime::Current()->GetAppInfo()->RegisterOdexStatus(
- dex_location,
- compilation_filter,
- compilation_reason,
- odex_status);
+ oat_file_assistant->GetOptimizationStatus(
+ &odex_location, &compilation_filter, &compilation_reason, &odex_status);
ScopedTrace odex_loading(StringPrintf(
"location=%s status=%s filter=%s reason=%s",
@@ -229,8 +221,18 @@
compilation_filter.c_str(),
compilation_reason.c_str()));
+ const bool has_registered_app_info = Runtime::Current()->GetAppInfo()->HasRegisteredAppInfo();
+ const AppInfo::CodeType code_type =
+ Runtime::Current()->GetAppInfo()->GetRegisteredCodeType(dex_location);
+ // We only want to madvise primary/split dex artifacts as a startup optimization. However,
+ // as the code_type for those artifacts may not be set until the initial app info registration,
+ // we conservatively madvise everything until the app info registration is complete.
+ const bool should_madvise_vdex_and_odex = !has_registered_app_info ||
+ code_type == AppInfo::CodeType::kPrimaryApk ||
+ code_type == AppInfo::CodeType::kSplitApk;
+
// Proceed with oat file loading.
- std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release());
+ std::unique_ptr<const OatFile> oat_file(oat_file_assistant->GetBestOatFile().release());
VLOG(oat) << "OatFileAssistant(" << dex_location << ").GetBestOatFile()="
<< (oat_file != nullptr ? oat_file->GetLocation() : "")
<< " (executable=" << (oat_file != nullptr ? oat_file->IsExecutable() : false) << ")";
@@ -241,64 +243,95 @@
<< " best_oat_file-location=" << oat_file->GetLocation();
if (oat_file != nullptr) {
+ bool compilation_enabled =
+ CompilerFilter::IsAotCompilationEnabled(oat_file->GetCompilerFilter());
// Load the dex files from the oat file.
bool added_image_space = false;
- if (oat_file->IsExecutable()) {
- ScopedTrace app_image_timing("AppImage:Loading");
+ if (should_madvise_vdex_and_odex) {
+ VLOG(oat) << "Madvising oat file: " << oat_file->GetLocation();
+ size_t madvise_size_limit = runtime->GetMadviseWillNeedSizeOdex();
+ Runtime::MadviseFileForRange(madvise_size_limit,
+ oat_file->Size(),
+ oat_file->Begin(),
+ oat_file->End(),
+ oat_file->GetLocation());
+ }
- // We need to throw away the image space if we are debuggable but the oat-file source of the
- // image is not otherwise we might get classes with inlined methods or other such things.
- std::unique_ptr<gc::space::ImageSpace> image_space;
- if (ShouldLoadAppImage(oat_file.get())) {
- image_space = oat_file_assistant.OpenImageSpace(oat_file.get());
+ ScopedTrace app_image_timing("AppImage:Loading");
+
+ // We need to throw away the image space if we are debuggable but the oat-file source of the
+ // image is not otherwise we might get classes with inlined methods or other such things.
+ std::unique_ptr<gc::space::ImageSpace> image_space;
+ if (ShouldLoadAppImage(oat_file.get())) {
+ if (oat_file->IsExecutable()) {
+ // App images generated by the compiler can only be used if the oat file
+ // is executable.
+ image_space = oat_file_assistant->OpenImageSpace(oat_file.get());
}
- if (image_space != nullptr) {
+ if (image_space == nullptr && !compilation_enabled) {
+ std::string art_file = RuntimeImage::GetRuntimeImagePath(dex_location);
+ std::string error_msg;
ScopedObjectAccess soa(self);
- StackHandleScope<1> hs(self);
- Handle<mirror::ClassLoader> h_loader(
- hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
- // Can not load app image without class loader.
- if (h_loader != nullptr) {
- std::string temp_error_msg;
- // Add image space has a race condition since other threads could be reading from the
- // spaces array.
+ image_space = gc::space::ImageSpace::CreateFromAppImage(
+ art_file.c_str(), oat_file.get(), &error_msg);
+ if (image_space == nullptr) {
+ VLOG(image) << "Could not load runtime generated app image: " << error_msg;
+ }
+ }
+ }
+ if (image_space != nullptr) {
+ ScopedObjectAccess soa(self);
+ StackHandleScope<1> hs(self);
+ Handle<mirror::ClassLoader> h_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader)));
+ // Can not load app image without class loader.
+ if (h_loader != nullptr) {
+ std::string temp_error_msg;
+ // Add image space has a race condition since other threads could be reading from the
+ // spaces array.
+ {
+ ScopedThreadSuspension sts(self, ThreadState::kSuspended);
+ gc::ScopedGCCriticalSection gcs(self,
+ gc::kGcCauseAddRemoveAppImageSpace,
+ gc::kCollectorTypeAddRemoveAppImageSpace);
+ ScopedSuspendAll ssa("Add image space");
+ runtime->GetHeap()->AddSpace(image_space.get());
+ }
+ {
+ ScopedTrace image_space_timing("Adding image space");
+ added_image_space = runtime->GetClassLinker()->AddImageSpace(image_space.get(),
+ h_loader,
+ /*out*/&dex_files,
+ /*out*/&temp_error_msg);
+ }
+ if (added_image_space) {
+ // Successfully added image space to heap, release the map so that it does not get
+ // freed.
+ image_space.release(); // NOLINT b/117926937
+
+ // Register for tracking.
+ for (const auto& dex_file : dex_files) {
+ dex::tracking::RegisterDexFile(dex_file.get());
+ }
+
+ if (!compilation_enabled) {
+ // Update the filter we are going to report to 'speed-profile'.
+ // Ideally, we would also update the compiler filter of the odex
+ // file, but at this point it's just too late.
+ compilation_filter = CompilerFilter::NameOfFilter(CompilerFilter::kSpeedProfile);
+ }
+ } else {
+ LOG(INFO) << "Failed to add image file: " << temp_error_msg;
+ dex_files.clear();
{
ScopedThreadSuspension sts(self, ThreadState::kSuspended);
gc::ScopedGCCriticalSection gcs(self,
gc::kGcCauseAddRemoveAppImageSpace,
gc::kCollectorTypeAddRemoveAppImageSpace);
- ScopedSuspendAll ssa("Add image space");
- runtime->GetHeap()->AddSpace(image_space.get());
+ ScopedSuspendAll ssa("Remove image space");
+ runtime->GetHeap()->RemoveSpace(image_space.get());
}
- {
- ScopedTrace image_space_timing("Adding image space");
- added_image_space = runtime->GetClassLinker()->AddImageSpace(image_space.get(),
- h_loader,
- /*out*/&dex_files,
- /*out*/&temp_error_msg);
- }
- if (added_image_space) {
- // Successfully added image space to heap, release the map so that it does not get
- // freed.
- image_space.release(); // NOLINT b/117926937
-
- // Register for tracking.
- for (const auto& dex_file : dex_files) {
- dex::tracking::RegisterDexFile(dex_file.get());
- }
- } else {
- LOG(INFO) << "Failed to add image file " << temp_error_msg;
- dex_files.clear();
- {
- ScopedThreadSuspension sts(self, ThreadState::kSuspended);
- gc::ScopedGCCriticalSection gcs(self,
- gc::kGcCauseAddRemoveAppImageSpace,
- gc::kCollectorTypeAddRemoveAppImageSpace);
- ScopedSuspendAll ssa("Remove image space");
- runtime->GetHeap()->RemoveSpace(image_space.get());
- }
- // Non-fatal, don't update error_msg.
- }
+ // Non-fatal, don't update error_msg.
}
}
}
@@ -310,12 +343,13 @@
<< oat_file->GetLocation()
<< " non-executable as it requires an image which we failed to load";
// file as non-executable.
- OatFileAssistant nonexecutable_oat_file_assistant(dex_location,
- kRuntimeISA,
- context.get(),
- /*load_executable=*/false,
- only_use_system_oat_files_);
- oat_file.reset(nonexecutable_oat_file_assistant.GetBestOatFile().release());
+ auto nonexecutable_oat_file_assistant =
+ std::make_unique<OatFileAssistant>(dex_location,
+ kRuntimeISA,
+ context.get(),
+ /*load_executable=*/false,
+ only_use_system_oat_files_);
+ oat_file.reset(nonexecutable_oat_file_assistant->GetBestOatFile().release());
// The file could be deleted concurrently (for example background
// dexopt, or secondary oat file being deleted by the app).
@@ -325,7 +359,7 @@
}
if (oat_file != nullptr) {
- dex_files = oat_file_assistant.LoadDexFiles(*oat_file.get(), dex_location);
+ dex_files = oat_file_assistant->LoadDexFiles(*oat_file.get(), dex_location);
// Register for tracking.
for (const auto& dex_file : dex_files) {
@@ -345,7 +379,8 @@
if (oat_file != nullptr) {
VdexFile* vdex_file = oat_file->GetVdexFile();
- if (vdex_file != nullptr) {
+ if (should_madvise_vdex_and_odex && vdex_file != nullptr) {
+ VLOG(oat) << "Madvising vdex file: " << vdex_file->GetName();
// Opened vdex file from an oat file, madvise it to its loaded state.
// TODO(b/196052575): Unify dex and vdex madvise knobs and behavior.
const size_t madvise_size_limit = Runtime::Current()->GetMadviseWillNeedSizeVdex();
@@ -365,7 +400,7 @@
// If so, report an error with the current stack trace.
// Most likely the developer didn't intend to do this because it will waste
// performance and memory.
- if (oat_file_assistant.GetBestStatus() == OatFileAssistant::kOatContextOutOfDate) {
+ if (oat_file_assistant->GetBestStatus() == OatFileAssistant::kOatContextOutOfDate) {
std::set<const DexFile*> already_exists_in_classpath =
context->CheckForDuplicateDexFiles(MakeNonOwningPointerVector(dex_files));
if (!already_exists_in_classpath.empty()) {
@@ -398,6 +433,12 @@
}
}
}
+
+ Runtime::Current()->GetAppInfo()->RegisterOdexStatus(
+ dex_location,
+ compilation_filter,
+ compilation_reason,
+ odex_status);
}
// If we arrive here with an empty dex files list, it means we fail to load
@@ -670,7 +711,7 @@
h_loader)));
if (h_class == nullptr) {
- CHECK(self->IsExceptionPending());
+ DCHECK(self->IsExceptionPending());
self->ClearException();
continue;
}
@@ -681,15 +722,14 @@
continue;
}
- CHECK(h_class->IsResolved()) << h_class->PrettyDescriptor();
+ DCHECK(h_class->IsResolved()) << h_class->PrettyDescriptor();
class_linker->VerifyClass(self, &verifier_deps, h_class);
- if (h_class->IsErroneous()) {
- // ClassLinker::VerifyClass throws, which isn't useful here.
- CHECK(soa.Self()->IsExceptionPending());
- soa.Self()->ClearException();
+ if (self->IsExceptionPending()) {
+ // ClassLinker::VerifyClass can throw, but the exception isn't useful here.
+ self->ClearException();
}
- CHECK(h_class->IsVerified() || h_class->IsErroneous())
+ DCHECK(h_class->IsVerified() || h_class->IsErroneous())
<< h_class->PrettyDescriptor() << ": state=" << h_class->GetStatus();
if (h_class->IsVerified()) {
@@ -817,6 +857,15 @@
verification_thread_pool_.reset(nullptr);
}
+void OatFileManager::WaitForBackgroundVerificationTasksToFinish() {
+ if (verification_thread_pool_ == nullptr) {
+ return;
+ }
+
+ Thread* const self = Thread::Current();
+ verification_thread_pool_->Wait(self, /* do_work= */ true, /* may_hold_locks= */ false);
+}
+
void OatFileManager::WaitForBackgroundVerificationTasks() {
if (verification_thread_pool_ != nullptr) {
Thread* const self = Thread::Current();
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
index b73ac58..e09390b 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -134,6 +134,9 @@
// If allocated, delete a thread pool of background verification threads.
void DeleteThreadPool();
+ // Wait for any ongoing background verification tasks to finish.
+ void WaitForBackgroundVerificationTasksToFinish();
+
// Wait for all background verification tasks to finish. This is only used by tests.
void WaitForBackgroundVerificationTasks();
diff --git a/runtime/oat_quick_method_header.cc b/runtime/oat_quick_method_header.cc
index 8fbf02a..f0764da 100644
--- a/runtime/oat_quick_method_header.cc
+++ b/runtime/oat_quick_method_header.cc
@@ -57,33 +57,61 @@
uintptr_t OatQuickMethodHeader::ToNativeQuickPc(ArtMethod* method,
const uint32_t dex_pc,
- bool is_for_catch_handler,
bool abort_on_failure) const {
const void* entry_point = GetEntryPoint();
DCHECK(!method->IsNative());
+ // For catch handlers use the ArrayRef<const uint32_t> version of ToNativeQuickPc.
+ DCHECK(!IsNterpMethodHeader());
+ DCHECK(IsOptimized());
+ // Search for the dex-to-pc mapping in stack maps.
+ CodeInfo code_info = CodeInfo::DecodeInlineInfoOnly(this);
+
+ StackMap stack_map = code_info.GetStackMapForDexPc(dex_pc);
+ if (stack_map.IsValid()) {
+ return reinterpret_cast<uintptr_t>(entry_point) + stack_map.GetNativePcOffset(kRuntimeISA);
+ }
+ if (abort_on_failure) {
+ ScopedObjectAccess soa(Thread::Current());
+ LOG(FATAL) << "Failed to find native offset for dex pc 0x" << std::hex << dex_pc << " in "
+ << method->PrettyMethod();
+ }
+ return UINTPTR_MAX;
+}
+
+uintptr_t OatQuickMethodHeader::ToNativeQuickPcForCatchHandlers(
+ ArtMethod* method,
+ ArrayRef<const uint32_t> dex_pc_list,
+ /* out */ uint32_t* stack_map_row,
+ bool abort_on_failure) const {
+ const void* entry_point = GetEntryPoint();
+ DCHECK(!method->IsNative());
if (IsNterpMethodHeader()) {
- // This should only be called on an nterp frame for getting a catch handler.
- CHECK(is_for_catch_handler);
return NterpGetCatchHandler();
}
DCHECK(IsOptimized());
// Search for the dex-to-pc mapping in stack maps.
CodeInfo code_info = CodeInfo::DecodeInlineInfoOnly(this);
- // All stack maps are stored in the same CodeItem section, safepoint stack
- // maps first, then catch stack maps. We use `is_for_catch_handler` to select
- // the order of iteration.
- StackMap stack_map =
- LIKELY(is_for_catch_handler) ? code_info.GetCatchStackMapForDexPc(dex_pc)
- : code_info.GetStackMapForDexPc(dex_pc);
+ StackMap stack_map = code_info.GetCatchStackMapForDexPc(dex_pc_list);
+ *stack_map_row = stack_map.Row();
if (stack_map.IsValid()) {
return reinterpret_cast<uintptr_t>(entry_point) +
stack_map.GetNativePcOffset(kRuntimeISA);
}
if (abort_on_failure) {
+ std::stringstream ss;
+ bool first = true;
+ ss << "Failed to find native offset for dex pcs (from outermost to innermost) " << std::hex;
+ for (auto dex_pc : dex_pc_list) {
+ if (!first) {
+ ss << ", ";
+ }
+ first = false;
+ ss << "0x" << dex_pc;
+ }
ScopedObjectAccess soa(Thread::Current());
- LOG(FATAL) << "Failed to find native offset for dex pc 0x" << std::hex << dex_pc
- << " in " << method->PrettyMethod();
+ ss << " in " << method->PrettyMethod();
+ LOG(FATAL) << ss.str();
}
return UINTPTR_MAX;
}
diff --git a/runtime/oat_quick_method_header.h b/runtime/oat_quick_method_header.h
index e347588..769d67f 100644
--- a/runtime/oat_quick_method_header.h
+++ b/runtime/oat_quick_method_header.h
@@ -28,6 +28,11 @@
class ArtMethod;
+// Size in bytes of the should_deoptimize flag on stack.
+// We just need 4 bytes for our purpose regardless of the architecture. Frame size
+// calculation will automatically do alignment for the final frame size.
+static constexpr size_t kShouldDeoptimizeFlagSize = 4;
+
// OatQuickMethodHeader precedes the raw code chunk generated by the compiler.
class PACKED(4) OatQuickMethodHeader {
public:
@@ -47,8 +52,8 @@
static OatQuickMethodHeader* FromCodePointer(const void* code_ptr) {
uintptr_t code = reinterpret_cast<uintptr_t>(code_ptr);
uintptr_t header = code - OFFSETOF_MEMBER(OatQuickMethodHeader, code_);
- DCHECK(IsAlignedParam(code, GetInstructionSetAlignment(kRuntimeISA)) ||
- IsAlignedParam(header, GetInstructionSetAlignment(kRuntimeISA)))
+ DCHECK(IsAlignedParam(code, GetInstructionSetCodeAlignment(kRuntimeISA)) ||
+ IsAlignedParam(header, GetInstructionSetCodeAlignment(kRuntimeISA)))
<< std::hex << code << " " << std::hex << header;
return reinterpret_cast<OatQuickMethodHeader*>(header);
}
@@ -58,7 +63,7 @@
}
static size_t InstructionAlignedSize() {
- return RoundUp(sizeof(OatQuickMethodHeader), GetInstructionSetAlignment(kRuntimeISA));
+ return RoundUp(sizeof(OatQuickMethodHeader), GetInstructionSetCodeAlignment(kRuntimeISA));
}
OatQuickMethodHeader(const OatQuickMethodHeader&) = default;
@@ -145,11 +150,28 @@
return CodeInfo::DecodeFrameInfo(GetOptimizedCodeInfoPtr());
}
+ size_t GetShouldDeoptimizeFlagOffset() const {
+ DCHECK(IsOptimized());
+ QuickMethodFrameInfo frame_info = GetFrameInfo();
+ size_t frame_size = frame_info.FrameSizeInBytes();
+ size_t core_spill_size =
+ POPCOUNT(frame_info.CoreSpillMask()) * GetBytesPerGprSpillLocation(kRuntimeISA);
+ size_t fpu_spill_size =
+ POPCOUNT(frame_info.FpSpillMask()) * GetBytesPerFprSpillLocation(kRuntimeISA);
+ return frame_size - core_spill_size - fpu_spill_size - kShouldDeoptimizeFlagSize;
+ }
+
+ // For non-catch handlers. Only used in test code.
uintptr_t ToNativeQuickPc(ArtMethod* method,
const uint32_t dex_pc,
- bool is_for_catch_handler,
bool abort_on_failure = true) const;
+ // For catch handlers.
+ uintptr_t ToNativeQuickPcForCatchHandlers(ArtMethod* method,
+ ArrayRef<const uint32_t> dex_pc_list,
+ /* out */ uint32_t* stack_map_row,
+ bool abort_on_failure = true) const;
+
uint32_t ToDexPc(ArtMethod** frame,
const uintptr_t pc,
bool abort_on_failure = true) const
diff --git a/runtime/offsets.h b/runtime/offsets.h
index cc18bf4..7974111 100644
--- a/runtime/offsets.h
+++ b/runtime/offsets.h
@@ -37,12 +37,28 @@
constexpr size_t SizeValue() const {
return val_;
}
+ Offset& operator+=(const size_t rhs) {
+ val_ += rhs;
+ return *this;
+ }
constexpr bool operator==(Offset o) const {
return SizeValue() == o.SizeValue();
}
constexpr bool operator!=(Offset o) const {
return !(*this == o);
}
+ constexpr bool operator<(Offset o) const {
+ return SizeValue() < o.SizeValue();
+ }
+ constexpr bool operator<=(Offset o) const {
+ return !(*this > o);
+ }
+ constexpr bool operator>(Offset o) const {
+ return o < *this;
+ }
+ constexpr bool operator>=(Offset o) const {
+ return !(*this < o);
+ }
protected:
size_t val_;
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index 4d24482..8e207b2 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -82,6 +82,7 @@
DCHECK_EQ(hiddenapi_policy_valuemap.size(),
static_cast<size_t>(hiddenapi::EnforcementPolicy::kMax) + 1);
+ // clang-format off
parser_builder->
SetCategory("standard")
.Define({"-classpath _", "-cp _"})
@@ -183,6 +184,10 @@
.IntoKey(M::Image)
.Define("-Xforcejitzygote")
.IntoKey(M::ForceJitZygote)
+ .Define("-Xallowinmemorycompilation")
+ .WithHelp("Allows compiling the boot classpath in memory when the given boot image is"
+ "unusable. This option is set by default for Zygote.")
+ .IntoKey(M::AllowInMemoryCompilation)
.Define("-Xprimaryzygote")
.IntoKey(M::PrimaryZygote)
.Define("-Xbootclasspath-locations:_")
@@ -464,18 +469,44 @@
.WithType<bool>()
.WithValueMap({{"false", false}, {"true", true}})
.IntoKey(M::PerfettoJavaHeapStackProf);
+ // clang-format on
- FlagBase::AddFlagsToCmdlineParser(parser_builder.get());
+ FlagBase::AddFlagsToCmdlineParser(parser_builder.get());
- parser_builder->Ignore({
- "-ea", "-da", "-enableassertions", "-disableassertions", "--runtime-arg", "-esa",
- "-dsa", "-enablesystemassertions", "-disablesystemassertions", "-Xrs", "-Xint:_",
- "-Xdexopt:_", "-Xnoquithandler", "-Xjnigreflimit:_", "-Xgenregmap", "-Xnogenregmap",
- "-Xverifyopt:_", "-Xcheckdexsum", "-Xincludeselectedop", "-Xjitop:_",
- "-Xincludeselectedmethod",
- "-Xjitblocking", "-Xjitmethod:_", "-Xjitclass:_", "-Xjitoffset:_",
- "-Xjitosrthreshold:_", "-Xjitconfig:_", "-Xjitcheckcg", "-Xjitverbose", "-Xjitprofile",
- "-Xjitdisableopt", "-Xjitsuspendpoll", "-XX:mainThreadStackSize=_"})
+ parser_builder
+ ->Ignore({"-ea",
+ "-da",
+ "-enableassertions",
+ "-disableassertions",
+ "--runtime-arg",
+ "-esa",
+ "-dsa",
+ "-enablesystemassertions",
+ "-disablesystemassertions",
+ "-Xrs",
+ "-Xint:_",
+ "-Xdexopt:_",
+ "-Xnoquithandler",
+ "-Xjnigreflimit:_",
+ "-Xgenregmap",
+ "-Xnogenregmap",
+ "-Xverifyopt:_",
+ "-Xcheckdexsum",
+ "-Xincludeselectedop",
+ "-Xjitop:_",
+ "-Xincludeselectedmethod",
+ "-Xjitblocking",
+ "-Xjitmethod:_",
+ "-Xjitclass:_",
+ "-Xjitoffset:_",
+ "-Xjitosrthreshold:_",
+ "-Xjitconfig:_",
+ "-Xjitcheckcg",
+ "-Xjitverbose",
+ "-Xjitprofile",
+ "-Xjitdisableopt",
+ "-Xjitsuspendpoll",
+ "-XX:mainThreadStackSize=_"})
.IgnoreUnrecognized(ignore_unrecognized)
.OrderCategories({"standard", "extended", "Dalvik", "ART"});
@@ -732,9 +763,11 @@
Exit(0);
}
// If `boot.art` exists in the ART APEX, it will be used. Otherwise, Everything will be JITed.
- args.Set(M::Image,
- ParseStringList<':'>{{"boot.art!/apex/com.android.art/etc/boot-image.prof",
- "/nonx/boot-framework.art!/system/etc/boot-image.prof"}});
+ args.Set(M::Image, ParseStringList<':'>::Split(GetJitZygoteBootImageLocation()));
+ }
+
+ if (args.Exists(M::Zygote)) {
+ args.Set(M::AllowInMemoryCompilation, Unit());
}
if (!args.Exists(M::CompilerCallbacksPtr) && !args.Exists(M::Image)) {
diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc
index d055186..ac8ec56 100644
--- a/runtime/proxy_test.cc
+++ b/runtime/proxy_test.cc
@@ -30,15 +30,8 @@
class ProxyTest : public CommonRuntimeTest {
protected:
- void SetUp() override {
- CommonRuntimeTest::SetUp();
- // The creation of a Proxy class uses WellKnownClasses. These are not normally initialized by
- // CommonRuntimeTest so we need to do that now.
- WellKnownClasses::Clear();
- WellKnownClasses::Init(art::Thread::Current()->GetJniEnv());
- // Since we aren't actually calling any of the native functions we can just immediately call
- // LateInit after calling Init.
- WellKnownClasses::LateInit(art::Thread::Current()->GetJniEnv());
+ ProxyTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
}
};
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index 2a6929a..8aac702 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -15,10 +15,14 @@
*/
#include "quick_exception_handler.h"
+
#include <ios>
+#include <queue>
+#include <sstream>
#include "arch/context.h"
#include "art_method-inl.h"
+#include "base/array_ref.h"
#include "base/enums.h"
#include "base/globals.h"
#include "base/logging.h" // For VLOG_IS_ON.
@@ -55,7 +59,6 @@
handler_quick_frame_pc_(0),
handler_method_header_(nullptr),
handler_quick_arg0_(0),
- handler_dex_pc_(0),
clear_exception_(false),
handler_frame_depth_(kInvalidFrameDepth),
full_fragment_done_(false) {}
@@ -67,12 +70,15 @@
Context* context,
Handle<mirror::Throwable>* exception,
QuickExceptionHandler* exception_handler,
- uint32_t skip_frames)
+ uint32_t skip_frames,
+ bool skip_top_unwind_callback)
REQUIRES_SHARED(Locks::mutator_lock_)
: StackVisitor(self, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
exception_(exception),
exception_handler_(exception_handler),
- skip_frames_(skip_frames) {
+ skip_frames_(skip_frames),
+ skip_unwind_callback_(skip_top_unwind_callback) {
+ DCHECK_IMPLIES(skip_unwind_callback_, skip_frames_ == 0);
}
bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -95,7 +101,24 @@
DCHECK(method->IsCalleeSaveMethod());
return true;
}
- return HandleTryItems(method);
+ bool continue_stack_walk = HandleTryItems(method);
+ // Collect methods for which MethodUnwind callback needs to be invoked. MethodUnwind callback
+ // can potentially throw, so we want to call these after we find the catch block.
+ // We stop the stack walk when we find the catch block. If we are ending the stack walk we don't
+ // have to unwind this method so don't record it.
+ if (continue_stack_walk && !skip_unwind_callback_) {
+ // Skip unwind callback is only used when method exit callback has thrown an exception. In
+ // that case, we should have runtime method (artMethodExitHook) on top of stack and the
+ // second should be the method for which method exit was called.
+ DCHECK_IMPLIES(skip_unwind_callback_, GetFrameDepth() == 2);
+ unwound_methods_.push(method);
+ }
+ skip_unwind_callback_ = false;
+ return continue_stack_walk;
+ }
+
+ std::queue<ArtMethod*>& GetUnwoundMethods() {
+ return unwound_methods_;
}
private:
@@ -112,10 +135,12 @@
uint32_t found_dex_pc = method->FindCatchBlock(to_find, dex_pc, &clear_exception);
exception_handler_->SetClearException(clear_exception);
if (found_dex_pc != dex::kDexNoIndex) {
- exception_handler_->SetHandlerDexPc(found_dex_pc);
+ exception_handler_->SetHandlerDexPcList(ComputeDexPcList(found_dex_pc));
+ uint32_t stack_map_row = -1;
exception_handler_->SetHandlerQuickFramePc(
- GetCurrentOatQuickMethodHeader()->ToNativeQuickPc(
- method, found_dex_pc, /* is_for_catch_handler= */ true));
+ GetCurrentOatQuickMethodHeader()->ToNativeQuickPcForCatchHandlers(
+ method, exception_handler_->GetHandlerDexPcList(), &stack_map_row));
+ exception_handler_->SetCatchStackMapRow(stack_map_row);
exception_handler_->SetHandlerQuickFrame(GetCurrentQuickFrame());
exception_handler_->SetHandlerMethodHeader(GetCurrentOatQuickMethodHeader());
return false; // End stack walk.
@@ -139,20 +164,29 @@
QuickExceptionHandler* const exception_handler_;
// The number of frames to skip searching for catches in.
uint32_t skip_frames_;
+ // The list of methods we would skip to reach the catch block. We record these to call
+ // MethodUnwind callbacks.
+ std::queue<ArtMethod*> unwound_methods_;
+ // Specifies if the unwind callback should be ignored for method at the top of the stack.
+ bool skip_unwind_callback_;
DISALLOW_COPY_AND_ASSIGN(CatchBlockStackVisitor);
};
// Finds the appropriate exception catch after calling all method exit instrumentation functions.
-// Note that this might change the exception being thrown.
-void QuickExceptionHandler::FindCatch(ObjPtr<mirror::Throwable> exception) {
+// Note that this might change the exception being thrown. If is_method_exit_exception is true
+// skip the method unwind call for the method on top of the stack as the exception was thrown by
+// method exit callback.
+void QuickExceptionHandler::FindCatch(ObjPtr<mirror::Throwable> exception,
+ bool is_method_exit_exception) {
DCHECK(!is_deoptimization_);
- instrumentation::InstrumentationStackPopper popper(self_);
+ instrumentation::Instrumentation* instr = Runtime::Current()->GetInstrumentation();
// The number of total frames we have so far popped.
uint32_t already_popped = 0;
bool popped_to_top = true;
StackHandleScope<1> hs(self_);
MutableHandle<mirror::Throwable> exception_ref(hs.NewHandle(exception));
+ bool skip_top_unwind_callback = is_method_exit_exception;
// Sending the instrumentation events (done by the InstrumentationStackPopper) can cause new
// exceptions to be thrown which will override the current exception. Therefore we need to perform
// the search for a catch in a loop until we have successfully popped all the way to a catch or
@@ -166,11 +200,15 @@
}
// Walk the stack to find catch handler.
- CatchBlockStackVisitor visitor(self_, context_,
+ CatchBlockStackVisitor visitor(self_,
+ context_,
&exception_ref,
this,
- /*skip_frames=*/already_popped);
+ /*skip_frames=*/already_popped,
+ skip_top_unwind_callback);
visitor.WalkStack(true);
+ skip_top_unwind_callback = false;
+
uint32_t new_pop_count = handler_frame_depth_;
DCHECK_GE(new_pop_count, already_popped);
already_popped = new_pop_count;
@@ -181,10 +219,25 @@
}
if (GetHandlerMethod() != nullptr) {
const DexFile* dex_file = GetHandlerMethod()->GetDexFile();
- int line_number =
- annotations::GetLineNumFromPC(dex_file, GetHandlerMethod(), handler_dex_pc_);
+ DCHECK(handler_dex_pc_list_.has_value());
+ DCHECK_GE(handler_dex_pc_list_->size(), 1u);
+ int line_number = annotations::GetLineNumFromPC(
+ dex_file, GetHandlerMethod(), handler_dex_pc_list_->front());
+
+ // We may have an inlined method. If so, we can add some extra logging.
+ std::stringstream ss;
+ ArtMethod* maybe_inlined_method = visitor.GetMethod();
+ if (maybe_inlined_method != GetHandlerMethod()) {
+ const DexFile* inlined_dex_file = maybe_inlined_method->GetDexFile();
+ DCHECK_GE(handler_dex_pc_list_->size(), 2u);
+ int inlined_line_number = annotations::GetLineNumFromPC(
+ inlined_dex_file, maybe_inlined_method, handler_dex_pc_list_->back());
+ ss << " which ends up calling inlined method " << maybe_inlined_method->PrettyMethod()
+ << " (line: " << inlined_line_number << ")";
+ }
+
LOG(INFO) << "Handler: " << GetHandlerMethod()->PrettyMethod() << " (line: "
- << line_number << ")";
+ << line_number << ")" << ss.str();
}
}
// Exception was cleared as part of delivery.
@@ -195,9 +248,13 @@
handler_method_header_->IsOptimized()) {
SetCatchEnvironmentForOptimizedHandler(&visitor);
}
- popped_to_top =
- popper.PopFramesTo(reinterpret_cast<uintptr_t>(handler_quick_frame_), exception_ref);
+ popped_to_top = instr->ProcessMethodUnwindCallbacks(self_,
+ visitor.GetUnwoundMethods(),
+ exception_ref);
} while (!popped_to_top);
+
+ // Pop off frames on instrumentation stack to keep it in sync with what is on the stack.
+ instr->PopInstrumentationStackUntil(self_, reinterpret_cast<uintptr_t>(handler_quick_frame_));
if (!clear_exception_) {
// Put exception back in root set with clear throw location.
self_->SetException(exception_ref.Get());
@@ -245,15 +302,18 @@
self_->DumpStack(LOG_STREAM(INFO) << "Setting catch phis: ");
}
- CodeItemDataAccessor accessor(GetHandlerMethod()->DexInstructionData());
- const size_t number_of_vregs = accessor.RegistersSize();
CodeInfo code_info(handler_method_header_);
// Find stack map of the catch block.
- StackMap catch_stack_map = code_info.GetCatchStackMapForDexPc(GetHandlerDexPc());
+ ArrayRef<const uint32_t> dex_pc_list = GetHandlerDexPcList();
+ DCHECK_GE(dex_pc_list.size(), 1u);
+ StackMap catch_stack_map = code_info.GetStackMapAt(GetCatchStackMapRow());
DCHECK(catch_stack_map.IsValid());
- DexRegisterMap catch_vreg_map = code_info.GetDexRegisterMapOf(catch_stack_map);
- DCHECK_EQ(catch_vreg_map.size(), number_of_vregs);
+ DCHECK_EQ(catch_stack_map.Row(), code_info.GetCatchStackMapForDexPc(dex_pc_list).Row());
+ const uint32_t catch_depth = dex_pc_list.size() - 1;
+ const size_t number_of_registers = stack_visitor->GetNumberOfRegisters(&code_info, catch_depth);
+ DexRegisterMap catch_vreg_map =
+ code_info.GetDexRegisterMapOf(catch_stack_map, /* first= */ 0, number_of_registers);
if (!catch_vreg_map.HasAnyLiveDexRegisters()) {
return;
@@ -263,26 +323,47 @@
StackMap throw_stack_map =
code_info.GetStackMapForNativePcOffset(stack_visitor->GetNativePcOffset());
DCHECK(throw_stack_map.IsValid());
- DexRegisterMap throw_vreg_map = code_info.GetDexRegisterMapOf(throw_stack_map);
- DCHECK_EQ(throw_vreg_map.size(), number_of_vregs);
+ const uint32_t throw_depth = stack_visitor->InlineDepth();
+ DCHECK_EQ(throw_depth, catch_depth);
+ DexRegisterMap throw_vreg_map =
+ code_info.GetDexRegisterMapOf(throw_stack_map, /* first= */ 0, number_of_registers);
+ DCHECK_EQ(throw_vreg_map.size(), catch_vreg_map.size());
- // Copy values between them.
- for (uint16_t vreg = 0; vreg < number_of_vregs; ++vreg) {
- DexRegisterLocation::Kind catch_location = catch_vreg_map[vreg].GetKind();
- if (catch_location == DexRegisterLocation::Kind::kNone) {
+ // First vreg that it is part of the catch's environment.
+ const size_t catch_vreg_start = catch_depth == 0
+ ? 0
+ : stack_visitor->GetNumberOfRegisters(&code_info, catch_depth - 1);
+
+ // We don't need to copy anything in the parent's environment.
+ for (size_t vreg = 0; vreg < catch_vreg_start; ++vreg) {
+ DexRegisterLocation::Kind catch_location_kind = catch_vreg_map[vreg].GetKind();
+ DCHECK(catch_location_kind == DexRegisterLocation::Kind::kNone ||
+ catch_location_kind == DexRegisterLocation::Kind::kConstant ||
+ catch_location_kind == DexRegisterLocation::Kind::kInStack)
+ << "Unexpected catch_location_kind: " << catch_location_kind;
+ }
+
+ // Copy values between the throw and the catch.
+ for (size_t vreg = catch_vreg_start; vreg < catch_vreg_map.size(); ++vreg) {
+ DexRegisterLocation::Kind catch_location_kind = catch_vreg_map[vreg].GetKind();
+ if (catch_location_kind == DexRegisterLocation::Kind::kNone) {
continue;
}
- DCHECK(catch_location == DexRegisterLocation::Kind::kInStack);
- // Get vreg value from its current location.
+ // Consistency checks.
+ DCHECK_EQ(catch_location_kind, DexRegisterLocation::Kind::kInStack);
uint32_t vreg_value;
VRegKind vreg_kind = ToVRegKind(throw_vreg_map[vreg].GetKind());
- bool get_vreg_success =
- stack_visitor->GetVReg(stack_visitor->GetMethod(),
- vreg,
- vreg_kind,
- &vreg_value,
- throw_vreg_map[vreg]);
+ DCHECK_NE(vreg_kind, kReferenceVReg)
+ << "The fast path in GetVReg doesn't expect a kReferenceVReg.";
+
+ // Get vreg value from its current location.
+ bool get_vreg_success = stack_visitor->GetVReg(stack_visitor->GetMethod(),
+ vreg,
+ vreg_kind,
+ &vreg_value,
+ throw_vreg_map[vreg],
+ /* need_full_register_list= */ true);
CHECK(get_vreg_success) << "VReg " << vreg << " was optimized out ("
<< "method=" << ArtMethod::PrettyMethod(stack_visitor->GetMethod())
<< ", dex_pc=" << stack_visitor->GetDexPc() << ", "
@@ -303,8 +384,8 @@
DeoptimizeStackVisitor(Thread* self,
Context* context,
QuickExceptionHandler* exception_handler,
- bool single_frame)
- REQUIRES_SHARED(Locks::mutator_lock_)
+ bool single_frame,
+ bool skip_method_exit_callbacks) REQUIRES_SHARED(Locks::mutator_lock_)
: StackVisitor(self, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
exception_handler_(exception_handler),
prev_shadow_frame_(nullptr),
@@ -313,8 +394,8 @@
single_frame_done_(false),
single_frame_deopt_method_(nullptr),
single_frame_deopt_quick_method_header_(nullptr),
- callee_method_(nullptr) {
- }
+ callee_method_(nullptr),
+ skip_method_exit_callbacks_(skip_method_exit_callbacks) {}
ArtMethod* GetSingleFrameDeoptMethod() const {
return single_frame_deopt_method_;
@@ -361,13 +442,15 @@
return true;
} else if (method->IsNative()) {
// If we return from JNI with a pending exception and want to deoptimize, we need to skip
- // the native method.
- // The top method is a runtime method, the native method comes next.
- CHECK_EQ(GetFrameDepth(), 1U);
+ // the native method. The top method is a runtime method, the native method comes next.
+ // We also deoptimize due to method instrumentation reasons from method entry / exit
+ // callbacks. In these cases native method is at the top of stack.
+ CHECK((GetFrameDepth() == 1U) || (GetFrameDepth() == 0U));
callee_method_ = method;
return true;
} else if (!single_frame_deopt_ &&
- !Runtime::Current()->IsAsyncDeoptimizeable(GetCurrentQuickFramePc())) {
+ !Runtime::Current()->IsAsyncDeoptimizeable(GetOuterMethod(),
+ GetCurrentQuickFramePc())) {
// We hit some code that's not deoptimizeable. However, Single-frame deoptimization triggered
// from compiled code is always allowed since HDeoptimize always saves the full environment.
LOG(WARNING) << "Got request to deoptimize un-deoptimizable method "
@@ -382,7 +465,7 @@
CodeItemDataAccessor accessor(method->DexInstructionData());
const size_t num_regs = accessor.RegistersSize();
if (new_frame == nullptr) {
- new_frame = ShadowFrame::CreateDeoptimizedFrame(num_regs, nullptr, method, GetDexPc());
+ new_frame = ShadowFrame::CreateDeoptimizedFrame(num_regs, method, GetDexPc());
updated_vregs = nullptr;
} else {
updated_vregs = GetThread()->GetUpdatedVRegFlags(frame_id);
@@ -393,6 +476,25 @@
} else {
HandleOptimizingDeoptimization(method, new_frame, updated_vregs);
}
+ // Update if method exit event needs to be reported. We should report exit event only if we
+ // have reported an entry event. So tell interpreter if/ an entry event was reported.
+ bool supports_exit_events =
+ Runtime::Current()->GetInstrumentation()->MethodSupportsExitEvents(
+ method, GetCurrentOatQuickMethodHeader());
+ new_frame->SetSkipMethodExitEvents(!supports_exit_events);
+ // If we are deoptimizing after method exit callback we shouldn't call the method exit
+ // callbacks again for the top frame. We may have to deopt after the callback if the callback
+ // either throws or performs other actions that require a deopt.
+ // We only need to skip for the top frame and the rest of the frames should still run the
+ // callbacks. So only do this check for the top frame.
+ if (GetFrameDepth() == 0U && skip_method_exit_callbacks_) {
+ new_frame->SetSkipMethodExitEvents(true);
+ // This exception was raised by method exit callbacks and we shouldn't report it to
+ // listeners for these exceptions.
+ if (GetThread()->IsExceptionPending()) {
+ new_frame->SetSkipNextExceptionEvent(true);
+ }
+ }
if (updated_vregs != nullptr) {
// Calling Thread::RemoveDebuggerShadowFrameMapping will also delete the updated_vregs
// array so this must come after we processed the frame.
@@ -549,6 +651,10 @@
ArtMethod* single_frame_deopt_method_;
const OatQuickMethodHeader* single_frame_deopt_quick_method_header_;
ArtMethod* callee_method_;
+ // This specifies if method exit callbacks should be skipped for the top frame. We may request
+ // a deopt after running method exit callbacks if the callback throws or requests events that
+ // need a deopt.
+ bool skip_method_exit_callbacks_;
DISALLOW_COPY_AND_ASSIGN(DeoptimizeStackVisitor);
};
@@ -568,13 +674,13 @@
}
}
-void QuickExceptionHandler::DeoptimizeStack() {
+void QuickExceptionHandler::DeoptimizeStack(bool skip_method_exit_callbacks) {
DCHECK(is_deoptimization_);
if (kDebugExceptionDelivery) {
self_->DumpStack(LOG_STREAM(INFO) << "Deoptimizing: ");
}
- DeoptimizeStackVisitor visitor(self_, context_, this, false);
+ DeoptimizeStackVisitor visitor(self_, context_, this, false, skip_method_exit_callbacks);
visitor.WalkStack(true);
PrepareForLongJumpToInvokeStubOrInterpreterBridge();
}
@@ -582,7 +688,10 @@
void QuickExceptionHandler::DeoptimizeSingleFrame(DeoptimizationKind kind) {
DCHECK(is_deoptimization_);
- DeoptimizeStackVisitor visitor(self_, context_, this, true);
+ // This deopt is requested while still executing the method. We haven't run method exit callbacks
+ // yet, so don't skip them.
+ DeoptimizeStackVisitor visitor(
+ self_, context_, this, true, /* skip_method_exit_callbacks= */ false);
visitor.WalkStack(true);
// Compiled code made an explicit deoptimization.
@@ -642,7 +751,7 @@
uintptr_t return_pc = 0;
if (method_tracing_active_) {
instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
- return_pc = instrumentation->PopFramesForDeoptimization(
+ return_pc = instrumentation->PopInstrumentationStackUntil(
self_, reinterpret_cast<uintptr_t>(handler_quick_frame_));
}
return return_pc;
@@ -661,9 +770,14 @@
if (!is_deoptimization_ &&
handler_method_header_ != nullptr &&
handler_method_header_->IsNterpMethodHeader()) {
+ // Interpreter procceses one method at a time i.e. not inlining
+ DCHECK(handler_dex_pc_list_.has_value());
+ DCHECK_EQ(handler_dex_pc_list_->size(), 1u) << "We shouldn't have any inlined frames.";
context_->SetNterpDexPC(reinterpret_cast<uintptr_t>(
- GetHandlerMethod()->DexInstructions().Insns() + handler_dex_pc_));
+ GetHandlerMethod()->DexInstructions().Insns() + handler_dex_pc_list_->front()));
}
+ // Clear the dex_pc list so as not to leak memory.
+ handler_dex_pc_list_.reset();
context_->DoLongJump();
UNREACHABLE();
}
diff --git a/runtime/quick_exception_handler.h b/runtime/quick_exception_handler.h
index 4ff981d..81c907e 100644
--- a/runtime/quick_exception_handler.h
+++ b/runtime/quick_exception_handler.h
@@ -18,10 +18,14 @@
#define ART_RUNTIME_QUICK_EXCEPTION_HANDLER_H_
#include <android-base/logging.h>
+#include <cstdint>
+#include <optional>
+#include "base/array_ref.h"
#include "base/macros.h"
#include "base/mutex.h"
#include "deoptimization_kind.h"
+#include "stack_map.h"
#include "stack_reference.h"
namespace art {
@@ -49,12 +53,16 @@
// Find the catch handler for the given exception and call all required Instrumentation methods.
// Note this might result in the exception being caught being different from 'exception'.
- void FindCatch(ObjPtr<mirror::Throwable> exception) REQUIRES_SHARED(Locks::mutator_lock_);
+ void FindCatch(ObjPtr<mirror::Throwable> exception, bool is_method_exit_exception)
+ REQUIRES_SHARED(Locks::mutator_lock_);
// Deoptimize the stack to the upcall/some code that's not deoptimizeable. For
// every compiled frame, we create a "copy" shadow frame that will be executed
// with the interpreter.
- void DeoptimizeStack() REQUIRES_SHARED(Locks::mutator_lock_);
+ // skip_method_exit_callbacks specifies if we should skip method exit callbacks for the top frame.
+ // It is set if a deopt is needed after calling method exit callback for ex: if the callback
+ // throws or performs other actions that require a deopt.
+ void DeoptimizeStack(bool skip_method_exit_callbacks) REQUIRES_SHARED(Locks::mutator_lock_);
// Deoptimize a single frame. It's directly triggered from compiled code. It
// has the following properties:
@@ -102,12 +110,21 @@
return *handler_quick_frame_;
}
- uint32_t GetHandlerDexPc() const {
- return handler_dex_pc_;
+ ArrayRef<const uint32_t> GetHandlerDexPcList() const {
+ DCHECK(handler_dex_pc_list_.has_value());
+ return ArrayRef<const uint32_t>(handler_dex_pc_list_.value());
}
- void SetHandlerDexPc(uint32_t dex_pc) {
- handler_dex_pc_ = dex_pc;
+ void SetHandlerDexPcList(std::vector<uint32_t>&& handler_dex_pc_list) {
+ handler_dex_pc_list_ = handler_dex_pc_list;
+ }
+
+ uint32_t GetCatchStackMapRow() const {
+ return catch_stack_map_row_;
+ }
+
+ void SetCatchStackMapRow(uint32_t stack_map_row) {
+ catch_stack_map_row_ = stack_map_row;
}
bool GetClearException() const {
@@ -150,8 +167,12 @@
const OatQuickMethodHeader* handler_method_header_;
// The value for argument 0.
uintptr_t handler_quick_arg0_;
- // The handler's dex PC, zero implies an uncaught exception.
- uint32_t handler_dex_pc_;
+ // The handler's dex PC list including the inline dex_pcs. The dex_pcs are ordered from outermost
+ // to innermost. An empty list implies an uncaught exception.
+ // Marked as optional so that we can make sure we destroy it before doing a long jump.
+ std::optional<std::vector<uint32_t>> handler_dex_pc_list_;
+ // StackMap row corresponding to the found catch.
+ uint32_t catch_stack_map_row_;
// Should the exception be cleared as the catch block has no move-exception?
bool clear_exception_;
// Frame depth of the catch handler or the upcall.
diff --git a/runtime/read_barrier-inl.h b/runtime/read_barrier-inl.h
index b0434d8..36c6f44 100644
--- a/runtime/read_barrier-inl.h
+++ b/runtime/read_barrier-inl.h
@@ -21,6 +21,7 @@
#include "gc/accounting/read_barrier_table.h"
#include "gc/collector/concurrent_copying-inl.h"
+#include "gc/collector/mark_compact.h"
#include "gc/heap.h"
#include "mirror/object-readbarrier-inl.h"
#include "mirror/object_reference.h"
@@ -34,12 +35,11 @@
inline MirrorType* ReadBarrier::Barrier(
mirror::Object* obj, MemberOffset offset, mirror::HeapReference<MirrorType>* ref_addr) {
constexpr bool with_read_barrier = kReadBarrierOption == kWithReadBarrier;
- if (kUseReadBarrier && with_read_barrier) {
+ if (gUseReadBarrier && with_read_barrier) {
if (kCheckDebugDisallowReadBarrierCount) {
Thread* const self = Thread::Current();
- if (self != nullptr) {
- CHECK_EQ(self->GetDebugDisallowReadBarrierCount(), 0u);
- }
+ CHECK(self != nullptr);
+ CHECK_EQ(self->GetDebugDisallowReadBarrierCount(), 0u);
}
if (kUseBakerReadBarrier) {
// fake_address_dependency (must be zero) is used to create artificial data dependency from
@@ -91,6 +91,12 @@
LOG(FATAL) << "Unexpected read barrier type";
UNREACHABLE();
}
+ } else if (kReadBarrierOption == kWithFromSpaceBarrier) {
+ CHECK(gUseUserfaultfd);
+ MirrorType* old = ref_addr->template AsMirrorPtr<kIsVolatile>();
+ mirror::Object* ref =
+ Runtime::Current()->GetHeap()->MarkCompactCollector()->GetFromSpaceAddrFromBarrier(old);
+ return reinterpret_cast<MirrorType*>(ref);
} else {
// No read barrier.
return ref_addr->template AsMirrorPtr<kIsVolatile>();
@@ -102,12 +108,11 @@
GcRootSource* gc_root_source) {
MirrorType* ref = *root;
const bool with_read_barrier = kReadBarrierOption == kWithReadBarrier;
- if (kUseReadBarrier && with_read_barrier) {
+ if (gUseReadBarrier && with_read_barrier) {
if (kCheckDebugDisallowReadBarrierCount) {
Thread* const self = Thread::Current();
- if (self != nullptr) {
- CHECK_EQ(self->GetDebugDisallowReadBarrierCount(), 0u);
- }
+ CHECK(self != nullptr);
+ CHECK_EQ(self->GetDebugDisallowReadBarrierCount(), 0u);
}
if (kUseBakerReadBarrier) {
// TODO: separate the read barrier code from the collector code more.
@@ -147,12 +152,11 @@
GcRootSource* gc_root_source) {
MirrorType* ref = root->AsMirrorPtr();
const bool with_read_barrier = kReadBarrierOption == kWithReadBarrier;
- if (kUseReadBarrier && with_read_barrier) {
+ if (gUseReadBarrier && with_read_barrier) {
if (kCheckDebugDisallowReadBarrierCount) {
Thread* const self = Thread::Current();
- if (self != nullptr) {
- CHECK_EQ(self->GetDebugDisallowReadBarrierCount(), 0u);
- }
+ CHECK(self != nullptr);
+ CHECK_EQ(self->GetDebugDisallowReadBarrierCount(), 0u);
}
if (kUseBakerReadBarrier) {
// TODO: separate the read barrier code from the collector code more.
@@ -192,7 +196,7 @@
inline MirrorType* ReadBarrier::IsMarked(MirrorType* ref) {
// Only read-barrier configurations can have mutators run while
// the GC is marking.
- if (!kUseReadBarrier) {
+ if (!gUseReadBarrier) {
return ref;
}
// IsMarked does not handle null, so handle it here.
diff --git a/runtime/read_barrier.h b/runtime/read_barrier.h
index 3b89377..be5a9a0 100644
--- a/runtime/read_barrier.h
+++ b/runtime/read_barrier.h
@@ -94,7 +94,7 @@
// Without the holder object, and only with the read barrier configuration (no-op otherwise).
static void MaybeAssertToSpaceInvariant(mirror::Object* ref)
REQUIRES_SHARED(Locks::mutator_lock_) {
- if (kUseReadBarrier) {
+ if (gUseReadBarrier) {
AssertToSpaceInvariant(ref);
}
}
diff --git a/runtime/read_barrier_config.h b/runtime/read_barrier_config.h
index d505bed..876e3d7 100644
--- a/runtime/read_barrier_config.h
+++ b/runtime/read_barrier_config.h
@@ -41,6 +41,12 @@
#define USE_READ_BARRIER
#endif
+// Reserve marking register (and its refreshing logic) for all GCs as nterp
+// requires it. In the future if and when nterp is made independent of
+// read-barrier, we can switch back to the current behavior by making this
+// definition conditional on USE_BAKER_READ_BARRIER and setting
+// kReserveMarkingRegister to kUseBakerReadBarrier.
+#define RESERVE_MARKING_REGISTER
// C++-specific configuration part..
@@ -56,23 +62,34 @@
static constexpr bool kUseBakerReadBarrier = false;
#endif
+// Read comment for RESERVE_MARKING_REGISTER above
+static constexpr bool kReserveMarkingRegister = true;
+
#ifdef USE_TABLE_LOOKUP_READ_BARRIER
static constexpr bool kUseTableLookupReadBarrier = true;
#else
static constexpr bool kUseTableLookupReadBarrier = false;
#endif
-static constexpr bool kUseReadBarrier = kUseBakerReadBarrier || kUseTableLookupReadBarrier;
-
-// Debugging flag that forces the generation of read barriers, but
-// does not trigger the use of the concurrent copying GC.
-//
-// TODO: Remove this flag when the read barriers compiler
-// instrumentation is completed.
-static constexpr bool kForceReadBarrier = false;
-// TODO: Likewise, remove this flag when kForceReadBarrier is removed
-// and replace it with kUseReadBarrier.
-static constexpr bool kEmitCompilerReadBarrier = kForceReadBarrier || kUseReadBarrier;
+// Only if read-barrier isn't forced (see build/art.go) but is selected, that we need
+// to see if we support userfaultfd GC. All the other cases can be constexpr here.
+#ifdef ART_FORCE_USE_READ_BARRIER
+constexpr bool gUseReadBarrier = kUseBakerReadBarrier || kUseTableLookupReadBarrier;
+constexpr bool gUseUserfaultfd = !gUseReadBarrier;
+static_assert(!gUseUserfaultfd);
+#else
+#ifndef ART_USE_READ_BARRIER
+constexpr bool gUseReadBarrier = false;
+#ifdef ART_DEFAULT_GC_TYPE_IS_CMC
+constexpr bool gUseUserfaultfd = true;
+#else
+constexpr bool gUseUserfaultfd = false;
+#endif
+#else
+extern const bool gUseReadBarrier;
+extern const bool gUseUserfaultfd;
+#endif
+#endif
// Disabled for performance reasons.
static constexpr bool kCheckDebugDisallowReadBarrierCount = kIsDebugBuild;
diff --git a/runtime/read_barrier_option.h b/runtime/read_barrier_option.h
index d918d46..36fc2d2 100644
--- a/runtime/read_barrier_option.h
+++ b/runtime/read_barrier_option.h
@@ -84,6 +84,7 @@
enum ReadBarrierOption {
kWithReadBarrier, // Perform a read barrier.
kWithoutReadBarrier, // Don't perform a read barrier.
+ kWithFromSpaceBarrier, // Get the from-space address for the given to-space address. Used by CMC
};
} // namespace art
diff --git a/runtime/reference_table.h b/runtime/reference_table.h
index 2ffd866..b204533 100644
--- a/runtime/reference_table.h
+++ b/runtime/reference_table.h
@@ -28,6 +28,9 @@
#include "obj_ptr.h"
namespace art {
+namespace jni {
+class LocalReferenceTable;
+} // namespace jni
namespace mirror {
class Object;
} // namespace mirror
@@ -61,6 +64,7 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::alloc_tracker_lock_);
friend class IndirectReferenceTable; // For Dump.
+ friend class jni::LocalReferenceTable; // For Dump.
std::string name_;
Table entries_;
diff --git a/runtime/reference_table_test.cc b/runtime/reference_table_test.cc
index 7a1e668..420543e 100644
--- a/runtime/reference_table_test.cc
+++ b/runtime/reference_table_test.cc
@@ -22,6 +22,7 @@
#include "art_method-inl.h"
#include "class_linker.h"
+#include "class_root-inl.h"
#include "common_runtime_test.h"
#include "dex/primitive.h"
#include "handle_scope-inl.h"
@@ -34,13 +35,17 @@
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
-#include "well_known_classes.h"
namespace art {
using android::base::StringPrintf;
-class ReferenceTableTest : public CommonRuntimeTest {};
+class ReferenceTableTest : public CommonRuntimeTest {
+ protected:
+ ReferenceTableTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+};
static ObjPtr<mirror::Object> CreateWeakReference(ObjPtr<mirror::Object> referent)
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -194,18 +199,12 @@
// avoids having to create the low-level args array ourselves.
Handle<mirror::Object> h_with_trace;
{
- jmethodID substr = soa.Env()->GetMethodID(WellKnownClasses::java_lang_String,
- "substring",
- "(II)Ljava/lang/String;");
+ ArtMethod* substr = GetClassRoot<mirror::String>()->FindClassMethod(
+ "substring", "(II)Ljava/lang/String;", kRuntimePointerSize);
ASSERT_TRUE(substr != nullptr);
- jobject jobj = soa.Env()->AddLocalReference<jobject>(h_without_trace.Get());
- ASSERT_TRUE(jobj != nullptr);
- jobject result = soa.Env()->CallObjectMethod(jobj,
- substr,
- static_cast<jint>(0),
- static_cast<jint>(4));
- ASSERT_TRUE(result != nullptr);
- h_with_trace = hs.NewHandle(soa.Self()->DecodeJObject(result));
+ h_with_trace = hs.NewHandle(
+ substr->InvokeFinal<'L', 'I', 'I'>(soa.Self(), h_without_trace.Get(), 0, 4));
+ ASSERT_TRUE(h_with_trace != nullptr);
}
Handle<mirror::Object> h_ref;
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index a7290a2..bfe9c8f 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -17,7 +17,7 @@
#include "reflection-inl.h"
#include "art_field-inl.h"
-#include "art_method-inl.h"
+#include "art_method-alloc-inl.h"
#include "base/enums.h"
#include "class_linker.h"
#include "common_throws.h"
@@ -502,17 +502,17 @@
<< soa.Self()->GetException()->GetClass()->PrettyDescriptor();
} else {
// If we get another exception when we are trying to wrap, then just use that instead.
- ScopedLocalRef<jthrowable> th(soa.Env(), soa.Env()->ExceptionOccurred());
+ StackHandleScope<2u> hs(soa.Self());
+ Handle<mirror::Throwable> cause = hs.NewHandle(soa.Self()->GetException());
soa.Self()->ClearException();
- jobject exception_instance =
- soa.Env()->NewObject(WellKnownClasses::java_lang_reflect_InvocationTargetException,
- WellKnownClasses::java_lang_reflect_InvocationTargetException_init,
- th.get());
+ Handle<mirror::Object> exception_instance =
+ WellKnownClasses::java_lang_reflect_InvocationTargetException_init->NewObject<'L'>(
+ hs, soa.Self(), cause);
if (exception_instance == nullptr) {
soa.Self()->AssertPendingException();
return false;
}
- soa.Env()->Throw(reinterpret_cast<jthrowable>(exception_instance));
+ soa.Self()->SetException(exception_instance->AsThrowable());
}
return false;
}
@@ -523,6 +523,7 @@
} // anonymous namespace
template <>
+NO_STACK_PROTECTOR
JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
jobject obj,
ArtMethod* method,
@@ -534,7 +535,7 @@
ThrowStackOverflowError(soa.Self());
return JValue();
}
- bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
+ bool is_string_init = method->IsStringConstructor();
if (is_string_init) {
// Replace calls to String.<init> with equivalent StringFactory call.
method = WellKnownClasses::StringInitToStringFactory(method);
@@ -555,6 +556,7 @@
}
template <>
+NO_STACK_PROTECTOR
JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa,
jobject obj,
jmethodID mid,
@@ -575,7 +577,7 @@
ThrowStackOverflowError(soa.Self());
return JValue();
}
- bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
+ bool is_string_init = method->IsStringConstructor();
if (is_string_init) {
// Replace calls to String.<init> with equivalent StringFactory call.
method = WellKnownClasses::StringInitToStringFactory(method);
@@ -618,7 +620,7 @@
}
ObjPtr<mirror::Object> receiver = soa.Decode<mirror::Object>(obj);
ArtMethod* method = FindVirtualMethod(receiver, interface_method);
- bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
+ bool is_string_init = method->IsStringConstructor();
if (is_string_init) {
// Replace calls to String.<init> with equivalent StringFactory call.
method = WellKnownClasses::StringInitToStringFactory(method);
@@ -662,7 +664,7 @@
ObjPtr<mirror::Object> receiver = soa.Decode<mirror::Object>(obj);
ArtMethod* method = FindVirtualMethod(receiver, interface_method);
- bool is_string_init = method->GetDeclaringClass()->IsStringClass() && method->IsConstructor();
+ bool is_string_init = method->IsStringConstructor();
if (is_string_init) {
// Replace calls to String.<init> with equivalent StringFactory call.
method = WellKnownClasses::StringInitToStringFactory(method);
@@ -838,7 +840,7 @@
return nullptr;
}
- jmethodID m = nullptr;
+ ArtMethod* m = nullptr;
const char* shorty;
switch (src_class) {
case Primitive::kPrimBoolean:
@@ -889,11 +891,8 @@
arg_array.Append(value.GetI());
}
- jni::DecodeArtMethod(m)->Invoke(soa.Self(),
- arg_array.GetArray(),
- arg_array.GetNumBytes(),
- &result,
- shorty);
+ DCHECK(m->GetDeclaringClass()->IsInitialized()); // By `ClassLinker::RunRootClinits()`.
+ m->Invoke(soa.Self(), arg_array.GetArray(), arg_array.GetNumBytes(), &result, shorty);
return result.GetL();
}
@@ -1067,8 +1066,8 @@
IndirectRefKind kind = IndirectReferenceTable::GetIndirectRefKind(ref);
if (kind == kLocal) {
self->GetJniEnv()->UpdateLocal(obj, result);
- } else if (kind == kJniTransitionOrInvalid) {
- LOG(FATAL) << "Unsupported UpdateReference for kind kJniTransitionOrInvalid";
+ } else if (kind == kJniTransition) {
+ LOG(FATAL) << "Unsupported UpdateReference for kind kJniTransition";
} else if (kind == kGlobal) {
self->GetJniEnv()->GetVm()->UpdateGlobal(self, ref, result);
} else {
diff --git a/runtime/reflection.h b/runtime/reflection.h
index b0e27da..13dc8e1 100644
--- a/runtime/reflection.h
+++ b/runtime/reflection.h
@@ -99,6 +99,7 @@
// num_frames is number of frames we look up for access check.
template<PointerSize pointer_size>
+NO_STACK_PROTECTOR
jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa,
jobject method,
jobject receiver,
diff --git a/runtime/reflection_test.cc b/runtime/reflection_test.cc
index 8a76855..4a24d2a 100644
--- a/runtime/reflection_test.cc
+++ b/runtime/reflection_test.cc
@@ -21,7 +21,7 @@
#include "art_method-inl.h"
#include "base/enums.h"
-#include "common_compiler_test.h"
+#include "common_runtime_test.h"
#include "dex/descriptors_names.h"
#include "jni/java_vm_ext.h"
#include "jni/jni_internal.h"
@@ -31,11 +31,10 @@
namespace art {
-// TODO: Convert to CommonRuntimeTest. Currently CompileDirectMethod is used in one test.
-class ReflectionTest : public CommonCompilerTest {
+class ReflectionTest : public CommonRuntimeTest {
protected:
void SetUp() override {
- CommonCompilerTest::SetUp();
+ CommonRuntimeTest::SetUp();
vm_ = Runtime::Current()->GetJavaVM();
@@ -76,7 +75,7 @@
void TearDown() override {
CleanUpJniEnv();
- CommonCompilerTest::TearDown();
+ CommonRuntimeTest::TearDown();
}
jclass GetPrimitiveClass(char descriptor) {
@@ -511,33 +510,6 @@
jclass sioobe_;
};
-TEST_F(ReflectionTest, StaticMainMethod) {
- ScopedObjectAccess soa(Thread::Current());
- jobject jclass_loader = LoadDex("Main");
- StackHandleScope<1> hs(soa.Self());
- Handle<mirror::ClassLoader> class_loader(
- hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
- CompileDirectMethod(class_loader, "Main", "main", "([Ljava/lang/String;)V");
-
- ObjPtr<mirror::Class> klass = class_linker_->FindClass(soa.Self(), "LMain;", class_loader);
- ASSERT_TRUE(klass != nullptr);
-
- ArtMethod* method = klass->FindClassMethod("main",
- "([Ljava/lang/String;)V",
- kRuntimePointerSize);
- ASSERT_TRUE(method != nullptr);
- ASSERT_TRUE(method->IsStatic());
-
- // Start runtime.
- bool started = runtime_->Start();
- CHECK(started);
- soa.Self()->TransitionFromSuspendedToRunnable();
-
- jvalue args[1];
- args[0].l = nullptr;
- InvokeWithJValues(soa, nullptr, jni::EncodeArtMethod(method), args);
-}
-
TEST_F(ReflectionTest, StaticNopMethod) {
InvokeNopMethod(true);
}
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index e20f883..8c0150e 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -16,15 +16,15 @@
#include "runtime.h"
-// sys/mount.h has to come before linux/fs.h due to redefinition of MS_RDONLY, MS_BIND, etc
-#include <sys/mount.h>
+#include <utility>
+
#ifdef __linux__
-#include <linux/fs.h>
#include <sys/prctl.h>
#endif
#include <fcntl.h>
#include <signal.h>
+#include <sys/mount.h>
#include <sys/syscall.h>
#if defined(__APPLE__)
@@ -102,6 +102,7 @@
#include "jni_id_type.h"
#include "linear_alloc.h"
#include "memory_representation.h"
+#include "metrics/statsd.h"
#include "mirror/array.h"
#include "mirror/class-alloc-inl.h"
#include "mirror/class-inl.h"
@@ -125,6 +126,7 @@
#include "native/dalvik_system_ZygoteHooks.h"
#include "native/java_lang_Class.h"
#include "native/java_lang_Object.h"
+#include "native/java_lang_StackStreamFactory.h"
#include "native/java_lang_String.h"
#include "native/java_lang_StringFactory.h"
#include "native/java_lang_System.h"
@@ -152,6 +154,7 @@
#include "native_bridge_art_interface.h"
#include "native_stack_dump.h"
#include "nativehelper/scoped_local_ref.h"
+#include "nterp_helpers.h"
#include "oat.h"
#include "oat_file_manager.h"
#include "oat_quick_method_header.h"
@@ -162,6 +165,7 @@
#include "reflection.h"
#include "runtime_callbacks.h"
#include "runtime_common.h"
+#include "runtime_image.h"
#include "runtime_intrinsics.h"
#include "runtime_options.h"
#include "scoped_thread_state_change-inl.h"
@@ -175,9 +179,10 @@
#include "transaction.h"
#include "vdex_file.h"
#include "verifier/class_verifier.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
#ifdef ART_TARGET_ANDROID
+#include <android/api-level.h>
#include <android/set_abort_message.h>
#include "com_android_apex.h"
namespace apex = com::android::apex;
@@ -200,10 +205,6 @@
static constexpr double kNormalMinLoadFactor = 0.4;
static constexpr double kNormalMaxLoadFactor = 0.7;
-// Extra added to the default heap growth multiplier. Used to adjust the GC ergonomics for the read
-// barrier config.
-static constexpr double kExtraDefaultHeapGrowthMultiplier = kUseReadBarrier ? 1.0 : 0.0;
-
Runtime* Runtime::instance_ = nullptr;
struct TraceConfig {
@@ -289,7 +290,7 @@
is_native_debuggable_(false),
async_exceptions_thrown_(false),
non_standard_exits_enabled_(false),
- is_java_debuggable_(false),
+ runtime_debug_state_(RuntimeDebugState::kNonJavaDebuggable),
monitor_timeout_enable_(false),
monitor_timeout_ns_(0),
zygote_max_failed_boots_(0),
@@ -312,7 +313,8 @@
verifier_logging_threshold_ms_(100),
verifier_missing_kthrow_fatal_(false),
perfetto_hprof_enabled_(false),
- perfetto_javaheapprof_enabled_(false) {
+ perfetto_javaheapprof_enabled_(false),
+ out_of_memory_error_hook_(nullptr) {
static_assert(Runtime::kCalleeSaveSize ==
static_cast<uint32_t>(CalleeSaveType::kLastCalleeSaveType), "Unexpected size");
CheckConstants();
@@ -339,10 +341,16 @@
// In this case we will just try again without allocating a peer so that shutdown can continue.
// Very few things are actually capable of distinguishing between the peer & peerless states so
// this should be fine.
+ // Running callbacks is prone to deadlocks in libjdwp tests that need an event handler lock to
+ // process any event. We also need to enter a GCCriticalSection when processing certain events
+ // (for ex: removing the last breakpoint). These two restrictions together make the tear down
+ // of the jdwp tests deadlock prone if we fail to finish Thread::Attach callback.
+ // (TODO:b/251163712) Remove this once we update deopt manager to not use GCCriticalSection.
bool thread_attached = AttachCurrentThread("Shutdown thread",
/* as_daemon= */ false,
GetSystemThreadGroup(),
- /* create_peer= */ IsStarted());
+ /* create_peer= */ IsStarted(),
+ /* should_run_callbacks= */ false);
if (UNLIKELY(!thread_attached)) {
LOG(WARNING) << "Failed to attach shutdown thread. Trying again without a peer.";
CHECK(AttachCurrentThread("Shutdown thread (no java peer)",
@@ -406,6 +414,12 @@
if (oat_file_manager_ != nullptr) {
oat_file_manager_->WaitForWorkersToBeCreated();
}
+ // Disable GC before deleting the thread-pool and shutting down runtime as it
+ // restricts attaching new threads.
+ heap_->DisableGCForShutdown();
+ heap_->WaitForWorkersToBeCreated();
+ // Make sure to let the GC complete if it is running.
+ heap_->WaitForGcToComplete(gc::kGcCauseBackground, self);
{
ScopedTrace trace2("Wait for shutdown cond");
@@ -421,8 +435,8 @@
if (IsFinishedStarting()) {
ScopedTrace trace2("Waiting for Daemons");
self->ClearException();
- self->GetJniEnv()->CallStaticVoidMethod(WellKnownClasses::java_lang_Daemons,
- WellKnownClasses::java_lang_Daemons_stop);
+ ScopedObjectAccess soa(self);
+ WellKnownClasses::java_lang_Daemons_stop->InvokeStatic<'V'>(self);
}
// Shutdown any trace running.
@@ -435,13 +449,8 @@
callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kDeath);
}
- if (attach_shutdown_thread) {
- DetachCurrentThread();
- self = nullptr;
- }
-
- // Make sure to let the GC complete if it is running.
- heap_->WaitForGcToComplete(gc::kGcCauseBackground, self);
+ // Delete thread pools before detaching the current thread in case tasks
+ // getting deleted need to have access to Thread::Current.
heap_->DeleteThreadPool();
if (oat_file_manager_ != nullptr) {
oat_file_manager_->DeleteThreadPool();
@@ -449,6 +458,11 @@
DeleteThreadPool();
CHECK(thread_pool_ == nullptr);
+ if (attach_shutdown_thread) {
+ DetachCurrentThread(/* should_run_callbacks= */ false);
+ self = nullptr;
+ }
+
// Make sure our internal threads are dead before we start tearing down things they're using.
GetRuntimeCallbacks()->StopDebugger();
// Deletion ordering is tricky. Null out everything we've deleted.
@@ -501,8 +515,8 @@
monitor_pool_ = nullptr;
delete class_linker_;
class_linker_ = nullptr;
- delete small_irt_allocator_;
- small_irt_allocator_ = nullptr;
+ delete small_lrt_allocator_;
+ small_lrt_allocator_ = nullptr;
delete heap_;
heap_ = nullptr;
delete intern_table_;
@@ -516,7 +530,8 @@
// Destroy allocators before shutting down the MemMap because they may use it.
java_vm_.reset();
linear_alloc_.reset();
- low_4gb_arena_pool_.reset();
+ startup_linear_alloc_.reset();
+ linear_alloc_arena_pool_.reset();
arena_pool_.reset();
jit_arena_pool_.reset();
protected_fault_page_.Reset();
@@ -543,7 +558,7 @@
os << "Runtime aborting...\n";
if (Runtime::Current() == nullptr) {
os << "(Runtime does not yet exist!)\n";
- DumpNativeStack(os, GetTid(), nullptr, " native: ", nullptr);
+ DumpNativeStack(os, GetTid(), " native: ", nullptr);
return;
}
Thread* self = Thread::Current();
@@ -555,7 +570,7 @@
if (self == nullptr) {
os << "(Aborting thread was not attached to runtime!)\n";
- DumpNativeStack(os, GetTid(), nullptr, " native: ", nullptr);
+ DumpNativeStack(os, GetTid(), " native: ", nullptr);
} else {
os << "Aborting thread:\n";
if (Locks::mutator_lock_->IsExclusiveHeld(self) || Locks::mutator_lock_->IsSharedHeld(self)) {
@@ -697,38 +712,52 @@
// notreached
}
-class FindNativeMethodsVisitor : public ClassVisitor {
+/**
+ * Update entrypoints (native and Java) of methods before the first fork. This
+ * helps sharing pages where ArtMethods are allocated between the zygote and
+ * forked apps.
+ */
+class UpdateMethodsPreFirstForkVisitor : public ClassVisitor {
public:
- FindNativeMethodsVisitor(Thread* self, ClassLinker* class_linker)
+ UpdateMethodsPreFirstForkVisitor(Thread* self, ClassLinker* class_linker)
: vm_(down_cast<JNIEnvExt*>(self->GetJniEnv())->GetVm()),
self_(self),
- class_linker_(class_linker) {}
+ class_linker_(class_linker),
+ can_use_nterp_(interpreter::CanRuntimeUseNterp()) {}
bool operator()(ObjPtr<mirror::Class> klass) override REQUIRES_SHARED(Locks::mutator_lock_) {
bool is_initialized = klass->IsVisiblyInitialized();
for (ArtMethod& method : klass->GetDeclaredMethods(kRuntimePointerSize)) {
- if (method.IsNative() && (is_initialized || !NeedsClinitCheckBeforeCall(&method))) {
- const void* existing = method.GetEntryPointFromJni();
- if (method.IsCriticalNative()
- ? class_linker_->IsJniDlsymLookupCriticalStub(existing)
- : class_linker_->IsJniDlsymLookupStub(existing)) {
- const void* native_code =
- vm_->FindCodeForNativeMethod(&method, /*error_msg=*/ nullptr, /*can_suspend=*/ false);
- if (native_code != nullptr) {
- class_linker_->RegisterNative(self_, &method, native_code);
+ if (is_initialized || !method.NeedsClinitCheckBeforeCall()) {
+ if (method.IsNative()) {
+ const void* existing = method.GetEntryPointFromJni();
+ if (method.IsCriticalNative()
+ ? class_linker_->IsJniDlsymLookupCriticalStub(existing)
+ : class_linker_->IsJniDlsymLookupStub(existing)) {
+ const void* native_code =
+ vm_->FindCodeForNativeMethod(&method, /*error_msg=*/ nullptr, /*can_suspend=*/ false);
+ if (native_code != nullptr) {
+ class_linker_->RegisterNative(self_, &method, native_code);
+ }
}
}
+ } else if (can_use_nterp_) {
+ const void* existing = method.GetEntryPointFromQuickCompiledCode();
+ if (class_linker_->IsQuickResolutionStub(existing) && CanMethodUseNterp(&method)) {
+ method.SetEntryPointFromQuickCompiledCode(interpreter::GetNterpWithClinitEntryPoint());
+ }
}
}
return true;
}
private:
- JavaVMExt* vm_;
- Thread* self_;
- ClassLinker* class_linker_;
+ JavaVMExt* const vm_;
+ Thread* const self_;
+ ClassLinker* const class_linker_;
+ const bool can_use_nterp_;
- DISALLOW_COPY_AND_ASSIGN(FindNativeMethodsVisitor);
+ DISALLOW_COPY_AND_ASSIGN(UpdateMethodsPreFirstForkVisitor);
};
void Runtime::PreZygoteFork() {
@@ -736,14 +765,16 @@
GetJit()->PreZygoteFork();
}
if (!heap_->HasZygoteSpace()) {
+ Thread* self = Thread::Current();
// This is the first fork. Update ArtMethods in the boot classpath now to
// avoid having forked apps dirty the memory.
- ScopedObjectAccess soa(Thread::Current());
+
// Ensure we call FixupStaticTrampolines on all methods that are
// initialized.
- class_linker_->MakeInitializedClassesVisiblyInitialized(soa.Self(), /*wait=*/ true);
- // Update native method JNI entrypoints.
- FindNativeMethodsVisitor visitor(soa.Self(), class_linker_);
+ class_linker_->MakeInitializedClassesVisiblyInitialized(self, /*wait=*/ true);
+
+ ScopedObjectAccess soa(self);
+ UpdateMethodsPreFirstForkVisitor visitor(self, class_linker_);
class_linker_->VisitClasses(&visitor);
}
heap_->PreZygoteFork();
@@ -787,7 +818,6 @@
// from mutators. See b/32167580.
GetJit()->GetCodeCache()->SweepRootTables(visitor);
}
- Thread::SweepInterpreterCaches(visitor);
// All other generic system-weak holders.
for (gc::AbstractSystemWeakHolder* holder : system_weak_holders_) {
@@ -816,6 +846,18 @@
return runtime != nullptr && runtime->IsStarted() && !runtime->IsShuttingDownLocked();
}
+void Runtime::AddGeneratedCodeRange(const void* start, size_t size) {
+ if (HandlesSignalsInCompiledCode()) {
+ fault_manager.AddGeneratedCodeRange(start, size);
+ }
+}
+
+void Runtime::RemoveGeneratedCodeRange(const void* start, size_t size) {
+ if (HandlesSignalsInCompiledCode()) {
+ fault_manager.RemoveGeneratedCodeRange(start, size);
+ }
+}
+
bool Runtime::Create(RuntimeArgumentMap&& runtime_options) {
// TODO: acquire a static mutex on Runtime to avoid racing.
if (Runtime::instance_ != nullptr) {
@@ -845,43 +887,34 @@
}
ScopedObjectAccess soa(Thread::Current());
- ClassLinker* cl = Runtime::Current()->GetClassLinker();
+ ClassLinker* cl = runtime->GetClassLinker();
auto pointer_size = cl->GetImagePointerSize();
- StackHandleScope<2> hs(soa.Self());
- Handle<mirror::Class> class_loader_class(
- hs.NewHandle(soa.Decode<mirror::Class>(WellKnownClasses::java_lang_ClassLoader)));
- CHECK(cl->EnsureInitialized(soa.Self(), class_loader_class, true, true));
+ ObjPtr<mirror::Class> class_loader_class = GetClassRoot<mirror::ClassLoader>(cl);
+ DCHECK(class_loader_class->IsInitialized()); // Class roots have been initialized.
ArtMethod* getSystemClassLoader = class_loader_class->FindClassMethod(
"getSystemClassLoader", "()Ljava/lang/ClassLoader;", pointer_size);
CHECK(getSystemClassLoader != nullptr);
CHECK(getSystemClassLoader->IsStatic());
- JValue result = InvokeWithJValues(soa,
- nullptr,
- getSystemClassLoader,
- nullptr);
- JNIEnv* env = soa.Self()->GetJniEnv();
- ScopedLocalRef<jobject> system_class_loader(env, soa.AddLocalReference<jobject>(result.GetL()));
- CHECK(system_class_loader.get() != nullptr);
+ ObjPtr<mirror::Object> system_class_loader = getSystemClassLoader->InvokeStatic<'L'>(soa.Self());
+ CHECK(system_class_loader != nullptr);
- soa.Self()->SetClassLoaderOverride(system_class_loader.get());
+ ScopedAssertNoThreadSuspension sants(__FUNCTION__);
+ jobject g_system_class_loader =
+ runtime->GetJavaVM()->AddGlobalRef(soa.Self(), system_class_loader);
+ soa.Self()->SetClassLoaderOverride(g_system_class_loader);
- Handle<mirror::Class> thread_class(
- hs.NewHandle(soa.Decode<mirror::Class>(WellKnownClasses::java_lang_Thread)));
- CHECK(cl->EnsureInitialized(soa.Self(), thread_class, true, true));
-
+ ObjPtr<mirror::Class> thread_class = WellKnownClasses::java_lang_Thread.Get();
ArtField* contextClassLoader =
thread_class->FindDeclaredInstanceField("contextClassLoader", "Ljava/lang/ClassLoader;");
CHECK(contextClassLoader != nullptr);
// We can't run in a transaction yet.
- contextClassLoader->SetObject<false>(
- soa.Self()->GetPeer(),
- soa.Decode<mirror::ClassLoader>(system_class_loader.get()).Ptr());
+ contextClassLoader->SetObject<false>(soa.Self()->GetPeer(), system_class_loader);
- return env->NewGlobalRef(system_class_loader.get());
+ return g_system_class_loader;
}
std::string Runtime::GetCompilerExecutable() const {
@@ -933,26 +966,12 @@
// Restore main thread state to kNative as expected by native code.
Thread* self = Thread::Current();
- self->TransitionFromRunnableToSuspended(ThreadState::kNative);
-
started_ = true;
- if (!IsImageDex2OatEnabled() || !GetHeap()->HasBootImageSpace()) {
- ScopedObjectAccess soa(self);
- StackHandleScope<3> hs(soa.Self());
+ class_linker_->RunEarlyRootClinits(self);
+ InitializeIntrinsics();
- ObjPtr<mirror::ObjectArray<mirror::Class>> class_roots = GetClassLinker()->GetClassRoots();
- auto class_class(hs.NewHandle<mirror::Class>(GetClassRoot<mirror::Class>(class_roots)));
- auto string_class(hs.NewHandle<mirror::Class>(GetClassRoot<mirror::String>(class_roots)));
- auto field_class(hs.NewHandle<mirror::Class>(GetClassRoot<mirror::Field>(class_roots)));
-
- class_linker_->EnsureInitialized(soa.Self(), class_class, true, true);
- class_linker_->EnsureInitialized(soa.Self(), string_class, true, true);
- self->AssertNoPendingException();
- // Field class is needed for register_java_net_InetAddress in libcore, b/28153851.
- class_linker_->EnsureInitialized(soa.Self(), field_class, true, true);
- self->AssertNoPendingException();
- }
+ self->TransitionFromRunnableToSuspended(ThreadState::kNative);
// InitNativeMethods needs to be after started_ so that the classes
// it touches will have methods linked to the oat file if necessary.
@@ -961,11 +980,6 @@
InitNativeMethods();
}
- // IntializeIntrinsics needs to be called after the WellKnownClasses::Init in InitNativeMethods
- // because in checking the invocation types of intrinsic methods ArtMethod::GetInvokeType()
- // needs the SignaturePolymorphic annotation class which is initialized in WellKnownClasses::Init.
- InitializeIntrinsics();
-
// InitializeCorePlatformApiPrivateFields() needs to be called after well known class
// initializtion in InitNativeMethods().
art::hiddenapi::InitializeCorePlatformApiPrivateFields();
@@ -988,8 +1002,23 @@
if (!jit::Jit::LoadCompilerLibrary(&error_msg)) {
LOG(WARNING) << "Failed to load JIT compiler with error " << error_msg;
}
- CreateJitCodeCache(/*rwx_memory_allowed=*/true);
CreateJit();
+#ifdef ADDRESS_SANITIZER
+ // (b/238730394): In older implementations of sanitizer + glibc there is a race between
+ // pthread_create and dlopen that could cause a deadlock. pthread_create interceptor in ASAN
+ // uses dl_pthread_iterator with a callback that could request a dl_load_lock via call to
+ // __tls_get_addr [1]. dl_pthread_iterate would already hold dl_load_lock so this could cause a
+ // deadlock. __tls_get_addr needs a dl_load_lock only when there is a dlopen happening in
+ // parallel. As a workaround we wait for the pthread_create (i.e JIT thread pool creation) to
+ // finish before going to the next phase. Creating a system class loader could need a dlopen so
+ // we wait here till threads are initialized.
+ // [1] https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/sanitizer_common/sanitizer_linux_libcdep.cpp#L408
+ // See this for more context: https://reviews.llvm.org/D98926
+ // TODO(b/238730394): Revisit this workaround once we migrate to musl libc.
+ if (jit_ != nullptr) {
+ jit_->GetThreadPool()->WaitForWorkersToBeCreated();
+ }
+#endif
}
// Send the start phase event. We have to wait till here as this is when the main thread peer
@@ -1015,24 +1044,14 @@
GetInstructionSetString(kRuntimeISA));
}
- StartDaemonThreads();
-
- // Make sure the environment is still clean (no lingering local refs from starting daemon
- // threads).
{
ScopedObjectAccess soa(self);
+ StartDaemonThreads();
self->GetJniEnv()->AssertLocalsEmpty();
- }
- // Send the initialized phase event. Send it after starting the Daemon threads so that agents
- // cannot delay the daemon threads from starting forever.
- {
- ScopedObjectAccess soa(self);
+ // Send the initialized phase event. Send it after starting the Daemon threads so that agents
+ // cannot delay the daemon threads from starting forever.
callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kInit);
- }
-
- {
- ScopedObjectAccess soa(self);
self->GetJniEnv()->AssertLocalsEmpty();
}
@@ -1128,8 +1147,8 @@
std::vector<std::string> jars = android::base::Split(system_server_classpath, ":");
app_info_.RegisterAppInfo("android",
jars,
- /*cur_profile_path=*/ "",
- /*ref_profile_path=*/ "",
+ /*profile_output_filename=*/ "",
+ /*ref_profile_filename=*/ "",
AppInfo::CodeType::kPrimaryApk);
}
@@ -1144,7 +1163,6 @@
}
// Create the thread pools.
- heap_->CreateThreadPool();
// Avoid creating the runtime thread pool for system server since it will not be used and would
// waste memory.
if (!is_system_server) {
@@ -1203,12 +1221,13 @@
}
if (Runtime::Current()->IsSystemServer()) {
std::string err;
- ScopedTrace tr("odrefresh stats logging");
+ ScopedTrace tr("odrefresh and device stats logging");
ScopedThreadSuspension sts(Thread::Current(), ThreadState::kNative);
// Report stats if available. This should be moved into ART Services when they are ready.
if (!odrefresh::UploadStatsIfAvailable(&err)) {
LOG(WARNING) << "Failed to upload odrefresh metrics: " << err;
}
+ metrics::ReportDeviceMetrics();
}
if (LIKELY(automatically_set_jni_ids_indirection_) && CanSetJniIdType()) {
@@ -1244,15 +1263,11 @@
Thread* self = Thread::Current();
- // Must be in the kNative state for calling native methods.
- CHECK_EQ(self->GetState(), ThreadState::kNative);
+ DCHECK_EQ(self->GetState(), ThreadState::kRunnable);
- JNIEnv* env = self->GetJniEnv();
- env->CallStaticVoidMethod(WellKnownClasses::java_lang_Daemons,
- WellKnownClasses::java_lang_Daemons_start);
- if (env->ExceptionCheck()) {
- env->ExceptionDescribe();
- LOG(FATAL) << "Error starting java.lang.Daemons";
+ WellKnownClasses::java_lang_Daemons_start->InvokeStatic<'V'>(self);
+ if (UNLIKELY(self->IsExceptionPending())) {
+ LOG(FATAL) << "Error starting java.lang.Daemons: " << self->GetException()->Dump();
}
VLOG(startup) << "Runtime::StartDaemonThreads exiting";
@@ -1328,9 +1343,9 @@
detailMessageField->SetObject</* kTransactionActive= */ false>(exception->Read(), message);
}
-void Runtime::InitializeApexVersions() {
+std::string Runtime::GetApexVersions(ArrayRef<const std::string> boot_class_path_locations) {
std::vector<std::string_view> bcp_apexes;
- for (std::string_view jar : Runtime::Current()->GetBootClassPathLocations()) {
+ for (std::string_view jar : boot_class_path_locations) {
std::string_view apex = ApexNameFromLocation(jar);
if (!apex.empty()) {
bcp_apexes.push_back(apex);
@@ -1338,20 +1353,20 @@
}
static const char* kApexFileName = "/apex/apex-info-list.xml";
// Start with empty markers.
- apex_versions_ = std::string(bcp_apexes.size(), '/');
+ std::string empty_apex_versions(bcp_apexes.size(), '/');
// When running on host or chroot, we just use empty markers.
if (!kIsTargetBuild || !OS::FileExists(kApexFileName)) {
- return;
+ return empty_apex_versions;
}
#ifdef ART_TARGET_ANDROID
if (access(kApexFileName, R_OK) != 0) {
PLOG(WARNING) << "Failed to read " << kApexFileName;
- return;
+ return empty_apex_versions;
}
auto info_list = apex::readApexInfoList(kApexFileName);
if (!info_list.has_value()) {
LOG(WARNING) << "Failed to parse " << kApexFileName;
- return;
+ return empty_apex_versions;
}
std::string result;
@@ -1375,10 +1390,17 @@
android::base::StringAppendF(&result, "/%" PRIu64, version);
}
}
- apex_versions_ = result;
+ return result;
+#else
+ return empty_apex_versions; // Not an Android build.
#endif
}
+void Runtime::InitializeApexVersions() {
+ apex_versions_ =
+ GetApexVersions(ArrayRef<const std::string>(Runtime::Current()->GetBootClassPathLocations()));
+}
+
void Runtime::ReloadAllFlags(const std::string& caller) {
FlagBase::ReloadAllFlags(caller);
}
@@ -1490,6 +1512,7 @@
is_explicit_gc_disabled_ = runtime_options.Exists(Opt::DisableExplicitGC);
image_dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::ImageDex2Oat);
dump_native_stack_on_sig_quit_ = runtime_options.GetOrDefault(Opt::DumpNativeStackOnSigQuit);
+ allow_in_memory_compilation_ = runtime_options.Exists(Opt::AllowInMemoryCompilation);
if (is_zygote_ || runtime_options.Exists(Opt::OnlyUseTrustedOatFiles)) {
oat_file_manager_->SetOnlyUseTrustedOatFiles();
@@ -1505,7 +1528,7 @@
compiler_options_ = runtime_options.ReleaseOrDefault(Opt::CompilerOptions);
for (const std::string& option : Runtime::Current()->GetCompilerOptions()) {
if (option == "--debuggable") {
- SetJavaDebuggable(true);
+ SetRuntimeDebugState(RuntimeDebugState::kJavaDebuggableAtInit);
break;
}
}
@@ -1552,6 +1575,8 @@
<< (core_platform_api_policy_ == hiddenapi::EnforcementPolicy::kEnabled ? "true" : "false");
}
+ // Dex2Oat's Runtime does not need the signal chain or the fault handler
+ // and it passes the `NoSigChain` option to `Runtime` to indicate this.
no_sig_chain_ = runtime_options.Exists(Opt::NoSigChain);
force_native_bridge_ = runtime_options.Exists(Opt::ForceNativeBridge);
@@ -1587,9 +1612,11 @@
// If low memory mode, use 1.0 as the multiplier by default.
foreground_heap_growth_multiplier = 1.0f;
} else {
+ // Extra added to the default heap growth multiplier for concurrent GC
+ // compaction algorithms. This is done for historical reasons.
+ // TODO: remove when we revisit heap configurations.
foreground_heap_growth_multiplier =
- runtime_options.GetOrDefault(Opt::ForegroundHeapGrowthMultiplier) +
- kExtraDefaultHeapGrowthMultiplier;
+ runtime_options.GetOrDefault(Opt::ForegroundHeapGrowthMultiplier) + 1.0f;
}
XGcOption xgc_option = runtime_options.GetOrDefault(Opt::GcOption);
@@ -1599,6 +1626,11 @@
// Cache the apex versions.
InitializeApexVersions();
+ BackgroundGcOption background_gc =
+ gUseReadBarrier ? BackgroundGcOption(gc::kCollectorTypeCCBackground)
+ : (gUseUserfaultfd ? BackgroundGcOption(xgc_option.collector_type_)
+ : runtime_options.GetOrDefault(Opt::BackgroundGc));
+
heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize),
runtime_options.GetOrDefault(Opt::HeapGrowthLimit),
runtime_options.GetOrDefault(Opt::HeapMinFree),
@@ -1617,9 +1649,8 @@
image_locations_,
instruction_set_,
// Override the collector type to CC if the read barrier config.
- kUseReadBarrier ? gc::kCollectorTypeCC : xgc_option.collector_type_,
- kUseReadBarrier ? BackgroundGcOption(gc::kCollectorTypeCCBackground)
- : runtime_options.GetOrDefault(Opt::BackgroundGc),
+ gUseReadBarrier ? gc::kCollectorTypeCC : xgc_option.collector_type_,
+ background_gc,
runtime_options.GetOrDefault(Opt::LargeObjectSpace),
runtime_options.GetOrDefault(Opt::LargeObjectThreshold),
runtime_options.GetOrDefault(Opt::ParallelGCThreads),
@@ -1700,13 +1731,19 @@
jit_arena_pool_.reset(new MemMapArenaPool(/* low_4gb= */ false, "CompilerMetadata"));
}
- if (IsAotCompiler() && Is64BitInstructionSet(kRuntimeISA)) {
- // 4gb, no malloc. Explanation in header.
- low_4gb_arena_pool_.reset(new MemMapArenaPool(/* low_4gb= */ true));
+ // For 64 bit compilers, it needs to be in low 4GB in the case where we are cross compiling for a
+ // 32 bit target. In this case, we have 32 bit pointers in the dex cache arrays which can't hold
+ // when we have 64 bit ArtMethod pointers.
+ const bool low_4gb = IsAotCompiler() && Is64BitInstructionSet(kRuntimeISA);
+ if (gUseUserfaultfd) {
+ linear_alloc_arena_pool_.reset(new GcVisitedArenaPool(low_4gb, IsZygote()));
+ } else if (low_4gb) {
+ linear_alloc_arena_pool_.reset(new MemMapArenaPool(low_4gb));
}
linear_alloc_.reset(CreateLinearAlloc());
+ startup_linear_alloc_.reset(CreateLinearAlloc());
- small_irt_allocator_ = new SmallIrtAllocator();
+ small_lrt_allocator_ = new jni::SmallLrtAllocator();
BlockSignals();
InitPlatformSignalHandlers();
@@ -1732,10 +1769,9 @@
}
if (!no_sig_chain_) {
- // Dex2Oat's Runtime does not need the signal chain or the fault handler.
- if (implicit_null_checks_ || implicit_so_checks_ || implicit_suspend_checks_) {
- fault_manager.Init();
+ fault_manager.Init();
+ if (HandlesSignalsInCompiledCode()) {
// These need to be in a specific order. The null point check handler must be
// after the suspend check and stack overflow check handlers.
//
@@ -1756,6 +1792,12 @@
if (kEnableJavaStackTraceHandler) {
new JavaStackTraceHandler(&fault_manager);
}
+
+ if (interpreter::CanRuntimeUseNterp()) {
+ // Nterp code can use signal handling just like the compiled managed code.
+ OatQuickMethodHeader* nterp_header = OatQuickMethodHeader::NterpMethodHeader;
+ fault_manager.AddGeneratedCodeRange(nterp_header->GetCode(), nterp_header->GetCodeSize());
+ }
}
}
@@ -1777,7 +1819,7 @@
// ClassLinker needs an attached thread, but we can't fully attach a thread without creating
// objects. We can't supply a thread group yet; it will be fixed later. Since we are the main
// thread, we do not get a java peer.
- Thread* self = Thread::Attach("main", false, nullptr, false);
+ Thread* self = Thread::Attach("main", false, nullptr, false, /* should_run_callbacks= */ true);
CHECK_EQ(self->GetThreadId(), ThreadList::kMainThreadId);
CHECK(self != nullptr);
@@ -2137,10 +2179,6 @@
// Set up the native methods provided by the runtime itself.
RegisterRuntimeNativeMethods(env);
- // Initialize classes used in JNI. The initialization requires runtime native
- // methods to be loaded first.
- WellKnownClasses::Init(env);
-
// Then set up libjavacore / libopenjdk / libicu_jni ,which are just
// 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
@@ -2149,20 +2187,26 @@
// By setting calling class to java.lang.Object, the caller location for these
// JNI libs is core-oj.jar in the ART APEX, and hence they are loaded from the
// com_android_art linker namespace.
+ jclass java_lang_Object;
+ {
+ ScopedObjectAccess soa(self);
+ java_lang_Object = reinterpret_cast<jclass>(
+ GetJavaVM()->AddGlobalRef(self, GetClassRoot<mirror::Object>(GetClassLinker())));
+ }
// 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)) {
+ env, "libicu_jni.so", nullptr, java_lang_Object, &error_msg)) {
LOG(FATAL) << "LoadNativeLibrary failed for \"libicu_jni.so\": " << error_msg;
}
}
{
std::string error_msg;
if (!java_vm_->LoadNativeLibrary(
- env, "libjavacore.so", nullptr, WellKnownClasses::java_lang_Object, &error_msg)) {
+ env, "libjavacore.so", nullptr, java_lang_Object, &error_msg)) {
LOG(FATAL) << "LoadNativeLibrary failed for \"libjavacore.so\": " << error_msg;
}
}
@@ -2172,10 +2216,11 @@
: "libopenjdk.so";
std::string error_msg;
if (!java_vm_->LoadNativeLibrary(
- env, kOpenJdkLibrary, nullptr, WellKnownClasses::java_lang_Object, &error_msg)) {
+ env, kOpenJdkLibrary, nullptr, java_lang_Object, &error_msg)) {
LOG(FATAL) << "LoadNativeLibrary failed for \"" << kOpenJdkLibrary << "\": " << error_msg;
}
}
+ env->DeleteGlobalRef(java_lang_Object);
// Initialize well known classes that may invoke runtime native methods.
WellKnownClasses::LateInit(env);
@@ -2188,17 +2233,28 @@
}
void Runtime::InitThreadGroups(Thread* self) {
- JNIEnvExt* env = self->GetJniEnv();
- ScopedJniEnvLocalRefState env_state(env);
+ ScopedObjectAccess soa(self);
+ ArtField* main_thread_group_field = WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup;
+ ArtField* system_thread_group_field = WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup;
+ // Note: This is running before `ClassLinker::RunRootClinits()`, so we cannot rely on
+ // `ThreadGroup` and `Thread` being initialized.
+ // TODO: Clean up initialization order after all well-known methods are converted to `ArtMethod*`
+ // (and therefore the `WellKnownClasses::Init()` shall not initialize any classes).
+ StackHandleScope<2u> hs(self);
+ Handle<mirror::Class> thread_group_class =
+ hs.NewHandle(main_thread_group_field->GetDeclaringClass());
+ bool initialized = GetClassLinker()->EnsureInitialized(
+ self, thread_group_class, /*can_init_fields=*/ true, /*can_init_parents=*/ true);
+ CHECK(initialized);
+ Handle<mirror::Class> thread_class = hs.NewHandle(WellKnownClasses::java_lang_Thread.Get());
+ initialized = GetClassLinker()->EnsureInitialized(
+ self, thread_class, /*can_init_fields=*/ true, /*can_init_parents=*/ true);
+ CHECK(initialized);
main_thread_group_ =
- env->NewGlobalRef(env->GetStaticObjectField(
- WellKnownClasses::java_lang_ThreadGroup,
- WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup));
+ soa.Vm()->AddGlobalRef(self, main_thread_group_field->GetObject(thread_group_class.Get()));
CHECK_IMPLIES(main_thread_group_ == nullptr, IsAotCompiler());
system_thread_group_ =
- env->NewGlobalRef(env->GetStaticObjectField(
- WellKnownClasses::java_lang_ThreadGroup,
- WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup));
+ soa.Vm()->AddGlobalRef(self, system_thread_group_field->GetObject(thread_group_class.Get()));
CHECK_IMPLIES(system_thread_group_ == nullptr, IsAotCompiler());
}
@@ -2237,6 +2293,7 @@
register_java_lang_reflect_Parameter(env);
register_java_lang_reflect_Proxy(env);
register_java_lang_ref_Reference(env);
+ register_java_lang_StackStreamFactory(env);
register_java_lang_String(env);
register_java_lang_StringFactory(env);
register_java_lang_System(env);
@@ -2270,6 +2327,9 @@
}
void Runtime::DumpForSigQuit(std::ostream& os) {
+ // Print backtraces first since they are important do diagnose ANRs,
+ // and ANRs can often be trimmed to limit upload size.
+ thread_list_->DumpForSigQuit(os);
GetClassLinker()->DumpForSigQuit(os);
GetInternTable()->DumpForSigQuit(os);
GetJavaVM()->DumpForSigQuit(os);
@@ -2285,7 +2345,6 @@
GetMetrics()->DumpForSigQuit(os);
os << "\n";
- thread_list_->DumpForSigQuit(os);
BaseMutex::DumpAll(os);
// Inform anyone else who is interested in SigQuit.
@@ -2296,11 +2355,11 @@
}
void Runtime::DumpLockHolders(std::ostream& os) {
- uint64_t mutator_lock_owner = Locks::mutator_lock_->GetExclusiveOwnerTid();
+ pid_t mutator_lock_owner = Locks::mutator_lock_->GetExclusiveOwnerTid();
pid_t thread_list_lock_owner = GetThreadList()->GetLockOwner();
pid_t classes_lock_owner = GetClassLinker()->GetClassesLockOwner();
pid_t dex_lock_owner = GetClassLinker()->GetDexLockOwner();
- if ((thread_list_lock_owner | classes_lock_owner | dex_lock_owner) != 0) {
+ if ((mutator_lock_owner | thread_list_lock_owner | classes_lock_owner | dex_lock_owner) != 0) {
os << "Mutator lock exclusive owner tid: " << mutator_lock_owner << "\n"
<< "ThreadList lock owner tid: " << thread_list_lock_owner << "\n"
<< "ClassLinker classes lock owner tid: " << classes_lock_owner << "\n"
@@ -2375,9 +2434,13 @@
}
bool Runtime::AttachCurrentThread(const char* thread_name, bool as_daemon, jobject thread_group,
- bool create_peer) {
+ bool create_peer, bool should_run_callbacks) {
ScopedTrace trace(__FUNCTION__);
- Thread* self = Thread::Attach(thread_name, as_daemon, thread_group, create_peer);
+ Thread* self = Thread::Attach(thread_name,
+ as_daemon,
+ thread_group,
+ create_peer,
+ should_run_callbacks);
// Run ThreadGroup.add to notify the group that this thread is now started.
if (self != nullptr && create_peer && !IsAotCompiler()) {
ScopedObjectAccess soa(self);
@@ -2386,7 +2449,7 @@
return self != nullptr;
}
-void Runtime::DetachCurrentThread() {
+void Runtime::DetachCurrentThread(bool should_run_callbacks) {
ScopedTrace trace(__FUNCTION__);
Thread* self = Thread::Current();
if (self == nullptr) {
@@ -2395,7 +2458,7 @@
if (self->HasManagedStack()) {
LOG(FATAL) << *Thread::Current() << " attempting to detach while still running code";
}
- thread_list_->Unregister(self);
+ thread_list_->Unregister(self, should_run_callbacks);
}
mirror::Throwable* Runtime::GetPreAllocatedOutOfMemoryErrorWhenThrowingException() {
@@ -2457,6 +2520,9 @@
class_linker_->VisitRoots(visitor, flags);
jni_id_manager_->VisitRoots(visitor);
heap_->VisitAllocationRecords(visitor);
+ if (jit_ != nullptr) {
+ jit_->GetCodeCache()->VisitRoots(visitor);
+ }
if ((flags & kVisitRootFlagNewRoots) == 0) {
// Guaranteed to have no new roots in the constant roots.
VisitConstantRoots(visitor);
@@ -2585,7 +2651,7 @@
}
void Runtime::DisallowNewSystemWeaks() {
- CHECK(!kUseReadBarrier);
+ CHECK(!gUseReadBarrier);
monitor_list_->DisallowNewMonitors();
intern_table_->ChangeWeakRootState(gc::kWeakRootStateNoReadsOrWrites);
java_vm_->DisallowNewWeakGlobals();
@@ -2601,7 +2667,7 @@
}
void Runtime::AllowNewSystemWeaks() {
- CHECK(!kUseReadBarrier);
+ CHECK(!gUseReadBarrier);
monitor_list_->AllowNewMonitors();
intern_table_->ChangeWeakRootState(gc::kWeakRootStateNormal); // TODO: Do this in the sweeping.
java_vm_->AllowNewWeakGlobals();
@@ -2698,7 +2764,8 @@
return;
}
if (!OS::FileExists(profile_output_filename.c_str(), /*check_file_type=*/ false)) {
- LOG(WARNING) << "JIT profile information will not be recorded: profile file does not exist.";
+ LOG(WARNING) << "JIT profile information will not be recorded: profile file does not exist: "
+ << profile_output_filename;
return;
}
if (code_paths.empty()) {
@@ -2722,7 +2789,13 @@
// Make initialized classes visibly initialized now. If that happened during the transaction
// and then the transaction was aborted, we would roll back the status update but not the
// ClassLinker's bookkeeping structures, so these classes would never be visibly initialized.
- GetClassLinker()->MakeInitializedClassesVisiblyInitialized(Thread::Current(), /*wait=*/ true);
+ {
+ Thread* self = Thread::Current();
+ StackHandleScope<1> hs(self);
+ HandleWrapper<mirror::Class> h(hs.NewHandleWrapper(&root));
+ ScopedThreadSuspension sts(self, ThreadState::kNative);
+ GetClassLinker()->MakeInitializedClassesVisiblyInitialized(Thread::Current(), /*wait=*/ true);
+ }
// Pass the runtime `ArenaPool` to the transaction.
arena_pool = GetArenaPool();
} else {
@@ -2950,7 +3023,9 @@
}
}
-void Runtime::CreateJitCodeCache(bool rwx_memory_allowed) {
+void Runtime::CreateJit() {
+ DCHECK(jit_code_cache_ == nullptr);
+ DCHECK(jit_ == nullptr);
if (kIsDebugBuild && GetInstrumentation()->IsForcedInterpretOnly()) {
DCHECK(!jit_options_->UseJitCompilation());
}
@@ -2959,28 +3034,19 @@
return;
}
+ if (IsSafeMode()) {
+ LOG(INFO) << "Not creating JIT because of SafeMode.";
+ return;
+ }
+
std::string error_msg;
bool profiling_only = !jit_options_->UseJitCompilation();
jit_code_cache_.reset(jit::JitCodeCache::Create(profiling_only,
- rwx_memory_allowed,
+ /*rwx_memory_allowed=*/ true,
IsZygote(),
&error_msg));
if (jit_code_cache_.get() == nullptr) {
LOG(WARNING) << "Failed to create JIT Code Cache: " << error_msg;
- }
-}
-
-void Runtime::CreateJit() {
- DCHECK(jit_ == nullptr);
- if (jit_code_cache_.get() == nullptr) {
- if (!IsSafeMode()) {
- LOG(WARNING) << "Missing code cache, cannot create JIT.";
- }
- return;
- }
- if (IsSafeMode()) {
- LOG(INFO) << "Not creating JIT because of SafeMode.";
- jit_code_cache_.reset();
return;
}
@@ -3043,12 +3109,13 @@
return verify_ == verifier::VerifyMode::kSoftFail;
}
-bool Runtime::IsAsyncDeoptimizeable(uintptr_t code) const {
+bool Runtime::IsAsyncDeoptimizeable(ArtMethod* method, uintptr_t code) const {
if (OatQuickMethodHeader::NterpMethodHeader != nullptr) {
if (OatQuickMethodHeader::NterpMethodHeader->Contains(code)) {
return true;
}
}
+
// We only support async deopt (ie the compiled code is not explicitly asking for
// deopt, but something else like the debugger) in debuggable JIT code.
// We could look at the oat file where `code` is being defined,
@@ -3056,17 +3123,58 @@
// only rely on the JIT for debuggable apps.
// The JIT-zygote is not debuggable so we need to be sure to exclude code from the non-private
// region as well.
- return IsJavaDebuggable() && GetJit() != nullptr &&
- GetJit()->GetCodeCache()->PrivateRegionContainsPc(reinterpret_cast<const void*>(code));
+ if (GetJit() != nullptr &&
+ GetJit()->GetCodeCache()->PrivateRegionContainsPc(reinterpret_cast<const void*>(code))) {
+ // If the code is JITed code then check if it was compiled as debuggable.
+ const OatQuickMethodHeader* header = method->GetOatQuickMethodHeader(code);
+ return CodeInfo::IsDebuggable(header->GetOptimizedCodeInfoPtr());
+ }
+
+ return false;
}
+
LinearAlloc* Runtime::CreateLinearAlloc() {
- // For 64 bit compilers, it needs to be in low 4GB in the case where we are cross compiling for a
- // 32 bit target. In this case, we have 32 bit pointers in the dex cache arrays which can't hold
- // when we have 64 bit ArtMethod pointers.
- return (IsAotCompiler() && Is64BitInstructionSet(kRuntimeISA))
- ? new LinearAlloc(low_4gb_arena_pool_.get())
- : new LinearAlloc(arena_pool_.get());
+ ArenaPool* pool = linear_alloc_arena_pool_.get();
+ return pool != nullptr
+ ? new LinearAlloc(pool, gUseUserfaultfd)
+ : new LinearAlloc(arena_pool_.get(), /*track_allocs=*/ false);
+}
+
+class Runtime::SetupLinearAllocForZygoteFork : public AllocatorVisitor {
+ public:
+ explicit SetupLinearAllocForZygoteFork(Thread* self) : self_(self) {}
+
+ bool Visit(LinearAlloc* alloc) override {
+ alloc->SetupForPostZygoteFork(self_);
+ return true;
+ }
+
+ private:
+ Thread* self_;
+};
+
+void Runtime::SetupLinearAllocForPostZygoteFork(Thread* self) {
+ if (gUseUserfaultfd) {
+ // Setup all the linear-allocs out there for post-zygote fork. This will
+ // basically force the arena allocator to ask for a new arena for the next
+ // allocation. All arenas allocated from now on will be in the userfaultfd
+ // visited space.
+ if (GetLinearAlloc() != nullptr) {
+ GetLinearAlloc()->SetupForPostZygoteFork(self);
+ }
+ if (GetStartupLinearAlloc() != nullptr) {
+ GetStartupLinearAlloc()->SetupForPostZygoteFork(self);
+ }
+ {
+ Locks::mutator_lock_->AssertNotHeld(self);
+ ReaderMutexLock mu2(self, *Locks::mutator_lock_);
+ ReaderMutexLock mu3(self, *Locks::classlinker_classes_lock_);
+ SetupLinearAllocForZygoteFork visitor(self);
+ GetClassLinker()->VisitAllocators(&visitor);
+ }
+ static_cast<GcVisitedArenaPool*>(GetLinearAllocArenaPool())->SetupPostZygoteMode();
+ }
}
double Runtime::GetHashTableMinLoadFactor() const {
@@ -3144,15 +3252,22 @@
auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
for (auto& m : klass->GetMethods(pointer_size)) {
const void* code = m.GetEntryPointFromQuickCompiledCode();
+ if (!m.IsInvokable()) {
+ continue;
+ }
+ // For java debuggable runtimes we also deoptimize native methods. For other cases (boot
+ // image profiling) we don't need to deoptimize native methods. If this changes also
+ // update Instrumentation::CanUseAotCode.
+ bool deoptimize_native_methods = Runtime::Current()->IsJavaDebuggable();
if (Runtime::Current()->GetHeap()->IsInBootImageOatFile(code) &&
- !m.IsNative() &&
+ (!m.IsNative() || deoptimize_native_methods) &&
!m.IsProxyMethod()) {
instrumentation_->InitializeMethodsCode(&m, /*aot_code=*/ nullptr);
}
if (Runtime::Current()->GetJit() != nullptr &&
Runtime::Current()->GetJit()->GetCodeCache()->IsInZygoteExecSpace(code) &&
- !m.IsNative()) {
+ (!m.IsNative() || deoptimize_native_methods)) {
DCHECK(!m.IsProxyMethod());
instrumentation_->InitializeMethodsCode(&m, /*aot_code=*/ nullptr);
}
@@ -3171,23 +3286,24 @@
instrumentation::Instrumentation* const instrumentation_;
};
-void Runtime::SetJavaDebuggable(bool value) {
- is_java_debuggable_ = value;
- // Do not call DeoptimizeBootImage just yet, the runtime may still be starting up.
+void Runtime::SetRuntimeDebugState(RuntimeDebugState state) {
+ if (state != RuntimeDebugState::kJavaDebuggableAtInit) {
+ // We never change the state if we started as a debuggable runtime.
+ DCHECK(runtime_debug_state_ != RuntimeDebugState::kJavaDebuggableAtInit);
+ }
+ runtime_debug_state_ = state;
}
void Runtime::DeoptimizeBootImage() {
// If we've already started and we are setting this runtime to debuggable,
// we patch entry points of methods in boot image to interpreter bridge, as
// boot image code may be AOT compiled as not debuggable.
- if (!GetInstrumentation()->IsForcedInterpretOnly()) {
- UpdateEntryPointsClassVisitor visitor(GetInstrumentation());
- GetClassLinker()->VisitClasses(&visitor);
- jit::Jit* jit = GetJit();
- if (jit != nullptr) {
- // Code previously compiled may not be compiled debuggable.
- jit->GetCodeCache()->TransitionToDebuggable();
- }
+ UpdateEntryPointsClassVisitor visitor(GetInstrumentation());
+ GetClassLinker()->VisitClasses(&visitor);
+ jit::Jit* jit = GetJit();
+ if (jit != nullptr) {
+ // Code previously compiled may not be compiled debuggable.
+ jit->GetCodeCache()->TransitionToDebuggable();
}
}
@@ -3235,6 +3351,31 @@
startup_completed_.store(false, std::memory_order_seq_cst);
}
+class CollectStartupDexCacheVisitor : public DexCacheVisitor {
+ public:
+ explicit CollectStartupDexCacheVisitor(VariableSizedHandleScope& handles) : handles_(handles) {}
+
+ void Visit(ObjPtr<mirror::DexCache> dex_cache)
+ REQUIRES_SHARED(Locks::dex_lock_, Locks::mutator_lock_) override {
+ handles_.NewHandle(dex_cache);
+ }
+
+ private:
+ VariableSizedHandleScope& handles_;
+};
+
+class UnlinkVisitor {
+ public:
+ UnlinkVisitor() {}
+
+ void VisitRootIfNonNull(StackReference<mirror::Object>* ref)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (!ref->IsNull()) {
+ ref->AsMirrorPtr()->AsDexCache()->UnlinkStartupCaches();
+ }
+ }
+};
+
class Runtime::NotifyStartupCompletedTask : public gc::HeapTask {
public:
NotifyStartupCompletedTask() : gc::HeapTask(/*target_run_time=*/ NanoTime()) {}
@@ -3243,10 +3384,38 @@
VLOG(startup) << "NotifyStartupCompletedTask running";
Runtime* const runtime = Runtime::Current();
{
- ScopedTrace trace("Releasing app image spaces metadata");
+ std::string compiler_filter;
+ std::string compilation_reason;
+ runtime->GetAppInfo()->GetPrimaryApkOptimizationStatus(&compiler_filter, &compilation_reason);
+ CompilerFilter::Filter filter;
+ if (CompilerFilter::ParseCompilerFilter(compiler_filter.c_str(), &filter) &&
+ !CompilerFilter::IsAotCompilationEnabled(filter)) {
+ std::string error_msg;
+ if (!RuntimeImage::WriteImageToDisk(&error_msg)) {
+ LOG(DEBUG) << "Could not write temporary image to disk " << error_msg;
+ }
+ }
+ }
+ // Fetch the startup linear alloc before the checkpoint to play nice with
+ // 1002-notify-startup test which resets the startup state.
+ std::unique_ptr<LinearAlloc> startup_linear_alloc(runtime->ReleaseStartupLinearAlloc());
+ {
+ ScopedTrace trace("Releasing dex caches and app image spaces metadata");
ScopedObjectAccess soa(Thread::Current());
- // Request empty checkpoints to make sure no threads are accessing the image space metadata
- // section when we madvise it. Use GC exclusion to prevent deadlocks that may happen if
+
+ // Collect dex caches that were allocated with the startup linear alloc.
+ VariableSizedHandleScope handles(soa.Self());
+ {
+ CollectStartupDexCacheVisitor visitor(handles);
+ ReaderMutexLock mu(self, *Locks::dex_lock_);
+ runtime->GetClassLinker()->VisitDexCaches(&visitor);
+ }
+
+ // Request empty checkpoints to make sure no threads are:
+ // - accessing the image space metadata section when we madvise it
+ // - accessing dex caches when we free them
+ //
+ // Use GC exclusion to prevent deadlocks that may happen if
// multiple threads are attempting to run empty checkpoints at the same time.
{
// Avoid using ScopedGCCriticalSection since that does not allow thread suspension. This is
@@ -3255,8 +3424,15 @@
gc::ScopedInterruptibleGCCriticalSection sigcs(self,
gc::kGcCauseRunEmptyCheckpoint,
gc::kCollectorTypeCriticalSection);
+ // Do the unlinking of dex cache arrays in the GC critical section to
+ // avoid GC not seeing these arrays. We do it before the checkpoint so
+ // we know after the checkpoint, no thread is holding onto the array.
+ UnlinkVisitor visitor;
+ handles.VisitRoots(visitor);
+
runtime->GetThreadList()->RunEmptyCheckpoint();
}
+
for (gc::space::ContinuousSpace* space : runtime->GetHeap()->GetContinuousSpaces()) {
if (space->IsImageSpace()) {
gc::space::ImageSpace* image_space = space->AsImageSpace();
@@ -3272,6 +3448,13 @@
ScopedTrace trace2("Delete thread pool");
runtime->DeleteThreadPool();
}
+
+ {
+ // We know that after the checkpoint, there is no thread that can hold
+ // the startup linear alloc, so it's safe to delete it now.
+ ScopedTrace trace2("Delete startup linear alloc");
+ startup_linear_alloc.reset();
+ }
}
};
@@ -3324,8 +3507,12 @@
WellKnownClasses::HandleJniIdTypeChange(Thread::Current()->GetJniEnv());
}
+bool Runtime::IsSystemServerProfiled() const {
+ return IsSystemServer() && jit_options_->GetSaveProfilingInfo();
+}
+
bool Runtime::GetOatFilesExecutable() const {
- return !IsAotCompiler() && !(IsSystemServer() && jit_options_->GetSaveProfilingInfo());
+ return !IsAotCompiler() && !IsSystemServerProfiled();
}
void Runtime::ProcessWeakClass(GcRoot<mirror::Class>* root_ptr,
@@ -3358,6 +3545,22 @@
const uint8_t* map_begin,
const uint8_t* map_end,
const std::string& file_name) {
+#ifdef ART_TARGET_ANDROID
+ // Short-circuit the madvise optimization for background processes. This
+ // avoids IO and memory contention with foreground processes, particularly
+ // those involving app startup.
+ // Note: We can only safely short-circuit the madvise on T+, as it requires
+ // the framework to always immediately notify ART of process states.
+ static const int kApiLevel = android_get_device_api_level();
+ const bool accurate_process_state_at_startup = kApiLevel >= __ANDROID_API_T__;
+ if (accurate_process_state_at_startup) {
+ const Runtime* runtime = Runtime::Current();
+ if (runtime != nullptr && !runtime->InJankPerceptibleProcessState()) {
+ return;
+ }
+ }
+#endif // ART_TARGET_ANDROID
+
// Ideal blockTransferSize for madvising files (128KiB)
static constexpr size_t kIdealIoTransferSizeBytes = 128*1024;
@@ -3399,6 +3602,8 @@
}
}
+// Return whether a boot image has a profile. This means we'll need to pre-JIT
+// methods in that profile for performance.
bool Runtime::HasImageWithProfile() const {
for (gc::space::ImageSpace* space : GetHeap()->GetBootImageSpaces()) {
if (!space->GetProfileFiles().empty()) {
@@ -3408,4 +3613,70 @@
return false;
}
+void Runtime::AppendToBootClassPath(const std::string& filename, const std::string& location) {
+ DCHECK(!DexFileLoader::IsMultiDexLocation(filename.c_str()));
+ boot_class_path_.push_back(filename);
+ if (!boot_class_path_locations_.empty()) {
+ DCHECK(!DexFileLoader::IsMultiDexLocation(location.c_str()));
+ boot_class_path_locations_.push_back(location);
+ }
+}
+
+void Runtime::AppendToBootClassPath(
+ const std::string& filename,
+ const std::string& location,
+ const std::vector<std::unique_ptr<const art::DexFile>>& dex_files) {
+ AppendToBootClassPath(filename, location);
+ ScopedObjectAccess soa(Thread::Current());
+ for (const std::unique_ptr<const art::DexFile>& dex_file : dex_files) {
+ // The first element must not be at a multi-dex location, while other elements must be.
+ DCHECK_NE(DexFileLoader::IsMultiDexLocation(dex_file->GetLocation().c_str()),
+ dex_file.get() == dex_files.begin()->get());
+ GetClassLinker()->AppendToBootClassPath(Thread::Current(), dex_file.get());
+ }
+}
+
+void Runtime::AppendToBootClassPath(const std::string& filename,
+ const std::string& location,
+ const std::vector<const art::DexFile*>& dex_files) {
+ AppendToBootClassPath(filename, location);
+ ScopedObjectAccess soa(Thread::Current());
+ for (const art::DexFile* dex_file : dex_files) {
+ // The first element must not be at a multi-dex location, while other elements must be.
+ DCHECK_NE(DexFileLoader::IsMultiDexLocation(dex_file->GetLocation().c_str()),
+ dex_file == *dex_files.begin());
+ GetClassLinker()->AppendToBootClassPath(Thread::Current(), dex_file);
+ }
+}
+
+void Runtime::AppendToBootClassPath(
+ const std::string& filename,
+ const std::string& location,
+ const std::vector<std::pair<const art::DexFile*, ObjPtr<mirror::DexCache>>>&
+ dex_files_and_cache) {
+ AppendToBootClassPath(filename, location);
+ ScopedObjectAccess soa(Thread::Current());
+ for (const auto& [dex_file, dex_cache] : dex_files_and_cache) {
+ // The first element must not be at a multi-dex location, while other elements must be.
+ DCHECK_NE(DexFileLoader::IsMultiDexLocation(dex_file->GetLocation().c_str()),
+ dex_file == dex_files_and_cache.begin()->first);
+ GetClassLinker()->AppendToBootClassPath(dex_file, dex_cache);
+ }
+}
+
+void Runtime::AddExtraBootDexFiles(const std::string& filename,
+ const std::string& location,
+ std::vector<std::unique_ptr<const art::DexFile>>&& dex_files) {
+ AppendToBootClassPath(filename, location);
+ ScopedObjectAccess soa(Thread::Current());
+ if (kIsDebugBuild) {
+ for (const std::unique_ptr<const art::DexFile>& dex_file : dex_files) {
+ // The first element must not be at a multi-dex location, while other elements must be.
+ DCHECK_NE(DexFileLoader::IsMultiDexLocation(dex_file->GetLocation().c_str()),
+ dex_file.get() == dex_files.begin()->get());
+ }
+ }
+ GetClassLinker()->AddExtraBootDexFiles(Thread::Current(), std::move(dex_files));
+}
+
} // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index e7b71e2..a0a36b9 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -68,6 +68,10 @@
class JitOptions;
} // namespace jit
+namespace jni {
+class SmallLrtAllocator;
+} // namespace jni
+
namespace mirror {
class Array;
class ClassLoader;
@@ -107,7 +111,6 @@
struct RuntimeArgumentMap;
class RuntimeCallbacks;
class SignalCatcher;
-class SmallIrtAllocator;
class StackOverflowHandler;
class SuspensionHandler;
class ThreadList;
@@ -133,6 +136,19 @@
static bool Create(const RuntimeOptions& raw_options, bool ignore_unrecognized)
SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_);
+ enum class RuntimeDebugState {
+ // This doesn't support any debug features / method tracing. This is the expected state usually.
+ kNonJavaDebuggable,
+ // This supports method tracing and a restricted set of debug features (for ex: redefinition
+ // isn't supported). We transition to this state when method tracing has started or when the
+ // debugger was attached and transition back to NonDebuggable once the tracing has stopped /
+ // the debugger agent has detached..
+ kJavaDebuggable,
+ // The runtime was started as a debuggable runtime. This allows us to support the extended set
+ // of debug features (for ex: redefinition). We never transition out of this state.
+ kJavaDebuggableAtInit
+ };
+
bool EnsurePluginLoaded(const char* plugin_name, std::string* error_msg);
bool EnsurePerfettoPlugin(std::string* error_msg);
@@ -257,6 +273,13 @@
return instance_;
}
+ // Set the current runtime to be the given instance.
+ // Note that this function is not responsible for cleaning up the old instance or taking the
+ // ownership of the new instance.
+ //
+ // For test use only.
+ static void TestOnlySetCurrent(Runtime* instance) { instance_ = instance; }
+
// Aborts semi-cleanly. Used in the implementation of LOG(FATAL), which most
// callers should prefer.
NO_RETURN static void Abort(const char* msg) REQUIRES(!Locks::abort_lock_);
@@ -271,13 +294,16 @@
jobject GetSystemClassLoader() const;
// Attaches the calling native thread to the runtime.
- bool AttachCurrentThread(const char* thread_name, bool as_daemon, jobject thread_group,
- bool create_peer);
+ bool AttachCurrentThread(const char* thread_name,
+ bool as_daemon,
+ jobject thread_group,
+ bool create_peer,
+ bool should_run_callbacks = true);
void CallExitHook(jint status);
// Detaches the current native thread from the runtime.
- void DetachCurrentThread() REQUIRES(!Locks::mutator_lock_);
+ void DetachCurrentThread(bool should_run_callbacks = true) REQUIRES(!Locks::mutator_lock_);
void DumpDeoptimizations(std::ostream& os);
void DumpForSigQuit(std::ostream& os);
@@ -295,6 +321,28 @@
return boot_class_path_locations_.empty() ? boot_class_path_ : boot_class_path_locations_;
}
+ // Dynamically adds an element to boot class path.
+ void AppendToBootClassPath(const std::string& filename,
+ const std::string& location,
+ const std::vector<std::unique_ptr<const art::DexFile>>& dex_files);
+
+ // Same as above, but takes raw pointers.
+ void AppendToBootClassPath(const std::string& filename,
+ const std::string& location,
+ const std::vector<const art::DexFile*>& dex_files);
+
+ // Same as above, but also takes a dex cache for each dex file.
+ void AppendToBootClassPath(
+ const std::string& filename,
+ const std::string& location,
+ const std::vector<std::pair<const art::DexFile*, ObjPtr<mirror::DexCache>>>&
+ dex_files_and_cache);
+
+ // Dynamically adds an element to boot class path and takes ownership of the dex files.
+ void AddExtraBootDexFiles(const std::string& filename,
+ const std::string& location,
+ std::vector<std::unique_ptr<const art::DexFile>>&& dex_files);
+
const std::vector<int>& GetBootClassPathFds() const {
return boot_class_path_fds_;
}
@@ -325,8 +373,8 @@
return class_linker_;
}
- SmallIrtAllocator* GetSmallIrtAllocator() const {
- return small_irt_allocator_;
+ jni::SmallLrtAllocator* GetSmallLrtAllocator() const {
+ return small_lrt_allocator_;
}
jni::JniIdManager* GetJniIdManager() const {
@@ -430,8 +478,7 @@
// Sweep system weaks, the system weak is deleted if the visitor return null. Otherwise, the
// system weak is updated to be the visitor's returned value.
- void SweepSystemWeaks(IsMarkedVisitor* visitor)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ void SweepSystemWeaks(IsMarkedVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
// Walk all reflective objects and visit their targets as well as any method/fields held by the
// runtime threads that are marked as being reflective.
@@ -498,6 +545,10 @@
return OFFSETOF_MEMBER(Runtime, callee_save_methods_[static_cast<size_t>(type)]);
}
+ static constexpr MemberOffset GetInstrumentationOffset() {
+ return MemberOffset(OFFSETOF_MEMBER(Runtime, instrumentation_));
+ }
+
InstructionSet GetInstructionSet() const {
return instruction_set_;
}
@@ -567,7 +618,8 @@
// Transaction support.
bool IsActiveTransaction() const;
- void EnterTransactionMode(bool strict, mirror::Class* root);
+ // EnterTransactionMode may suspend.
+ void EnterTransactionMode(bool strict, mirror::Class* root) REQUIRES_SHARED(Locks::mutator_lock_);
void ExitTransactionMode();
void RollbackAllTransactions() REQUIRES_SHARED(Locks::mutator_lock_);
// Transaction rollback and exit transaction are always done together, it's convenience to
@@ -752,6 +804,9 @@
// Create the JIT and instrumentation and code cache.
void CreateJit();
+ ArenaPool* GetLinearAllocArenaPool() {
+ return linear_alloc_arena_pool_.get();
+ }
ArenaPool* GetArenaPool() {
return arena_pool_.get();
}
@@ -768,12 +823,21 @@
return linear_alloc_.get();
}
+ LinearAlloc* GetStartupLinearAlloc() {
+ return startup_linear_alloc_.get();
+ }
+
jit::JitOptions* GetJITOptions() {
return jit_options_.get();
}
bool IsJavaDebuggable() const {
- return is_java_debuggable_;
+ return runtime_debug_state_ == RuntimeDebugState::kJavaDebuggable ||
+ runtime_debug_state_ == RuntimeDebugState::kJavaDebuggableAtInit;
+ }
+
+ bool IsJavaDebuggableAtInit() const {
+ return runtime_debug_state_ == RuntimeDebugState::kJavaDebuggableAtInit;
}
void SetProfileableFromShell(bool value) {
@@ -792,7 +856,7 @@
return is_profileable_;
}
- void SetJavaDebuggable(bool value);
+ void SetRuntimeDebugState(RuntimeDebugState state);
// Deoptimize the boot image, called for Java debuggable apps.
void DeoptimizeBootImage() REQUIRES(Locks::mutator_lock_);
@@ -850,6 +914,11 @@
// Create a normal LinearAlloc or low 4gb version if we are 64 bit AOT compiler.
LinearAlloc* CreateLinearAlloc();
+ // Setup linear-alloc allocators to stop using the current arena so that the
+ // next allocations, which would be after zygote fork, happens in userfaultfd
+ // visited space.
+ void SetupLinearAllocForPostZygoteFork(Thread* self)
+ REQUIRES(!Locks::mutator_lock_, !Locks::classlinker_classes_lock_);
OatFileManager& GetOatFileManager() const {
DCHECK(oat_file_manager_ != nullptr);
@@ -890,7 +959,8 @@
// Returns if the code can be deoptimized asynchronously. Code may be compiled with some
// optimization that makes it impossible to deoptimize.
- bool IsAsyncDeoptimizeable(uintptr_t code) const REQUIRES_SHARED(Locks::mutator_lock_);
+ bool IsAsyncDeoptimizeable(ArtMethod* method, uintptr_t code) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
// Returns a saved copy of the environment (getenv/setenv values).
// Used by Fork to protect against overwriting LD_LIBRARY_PATH, etc.
@@ -1004,6 +1074,10 @@
ThreadPool* const thread_pool_;
};
+ LinearAlloc* ReleaseStartupLinearAlloc() {
+ return startup_linear_alloc_.release();
+ }
+
bool LoadAppImageStartupCache() const {
return load_app_image_startup_cache_;
}
@@ -1050,7 +1124,11 @@
uint64_t GetMonitorTimeoutNs() const {
return monitor_timeout_ns_;
}
- // Return true if we should load oat files as executable or not.
+
+ // Return whether this is system server and it is being profiled.
+ bool IsSystemServerProfiled() const;
+
+ // Return whether we should load oat files as executable or not.
bool GetOatFilesExecutable() const;
metrics::ArtMetrics* GetMetrics() { return &metrics_; }
@@ -1073,6 +1151,14 @@
// image rather that an image loaded from disk.
bool HasImageWithProfile() const;
+ bool GetNoSigChain() const {
+ return no_sig_chain_;
+ }
+
+ void AddGeneratedCodeRange(const void* start, size_t size);
+ void RemoveGeneratedCodeRange(const void* start, size_t size)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// Trigger a flag reload from system properties or device congfigs.
//
// Should only be called from runtime init and zygote post fork as
@@ -1084,11 +1170,33 @@
// See Flags::ReloadAllFlags as well.
static void ReloadAllFlags(const std::string& caller);
+ // Parses /apex/apex-info-list.xml to build a string containing apex versions of boot classpath
+ // jars, which is encoded into .oat files.
+ static std::string GetApexVersions(ArrayRef<const std::string> boot_class_path_locations);
+
+ bool AllowInMemoryCompilation() const { return allow_in_memory_compilation_; }
+
+ // Used by plugin code to attach a hook for OOME.
+ void SetOutOfMemoryErrorHook(void (*hook)()) {
+ out_of_memory_error_hook_ = hook;
+ }
+
+ void OutOfMemoryErrorHook() {
+ if (out_of_memory_error_hook_ != nullptr) {
+ out_of_memory_error_hook_();
+ }
+ }
+
private:
static void InitPlatformSignalHandlers();
Runtime();
+ bool HandlesSignalsInCompiledCode() const {
+ return !no_sig_chain_ &&
+ (implicit_null_checks_ || implicit_so_checks_ || implicit_suspend_checks_);
+ }
+
void BlockSignals();
bool Init(RuntimeArgumentMap&& runtime_options)
@@ -1097,7 +1205,7 @@
void RegisterRuntimeNativeMethods(JNIEnv* env);
void InitMetrics();
- void StartDaemonThreads();
+ void StartDaemonThreads() REQUIRES_SHARED(Locks::mutator_lock_);
void StartSignalCatcher();
void MaybeSaveJitProfilingInfo();
@@ -1124,10 +1232,11 @@
ThreadPool* AcquireThreadPool() REQUIRES(!Locks::runtime_thread_pool_lock_);
void ReleaseThreadPool() REQUIRES(!Locks::runtime_thread_pool_lock_);
- // Parses /apex/apex-info-list.xml to initialize a string containing versions
- // of boot classpath jars and encoded into .oat files.
+ // Caches the apex versions produced by `GetApexVersions`.
void InitializeApexVersions();
+ void AppendToBootClassPath(const std::string& filename, const std::string& location);
+
// A pointer to the active runtime or null.
static Runtime* instance_;
@@ -1194,14 +1303,21 @@
std::unique_ptr<ArenaPool> jit_arena_pool_;
std::unique_ptr<ArenaPool> arena_pool_;
- // Special low 4gb pool for compiler linear alloc. We need ArtFields to be in low 4gb if we are
- // compiling using a 32 bit image on a 64 bit compiler in case we resolve things in the image
- // since the field arrays are int arrays in this case.
- std::unique_ptr<ArenaPool> low_4gb_arena_pool_;
+ // This pool is used for linear alloc if we are using userfaultfd GC, or if
+ // low 4gb pool is required for compiler linear alloc. Otherwise, use
+ // arena_pool_.
+ // We need ArtFields to be in low 4gb if we are compiling using a 32 bit image
+ // on a 64 bit compiler in case we resolve things in the image since the field
+ // arrays are int arrays in this case.
+ std::unique_ptr<ArenaPool> linear_alloc_arena_pool_;
// Shared linear alloc for now.
std::unique_ptr<LinearAlloc> linear_alloc_;
+ // Linear alloc used for allocations during startup. Will be deleted after
+ // startup.
+ std::unique_ptr<LinearAlloc> startup_linear_alloc_;
+
// The number of spins that are done before thread suspension is used to forcibly inflate.
size_t max_spins_before_thin_lock_inflation_;
MonitorList* monitor_list_;
@@ -1215,7 +1331,7 @@
SignalCatcher* signal_catcher_;
- SmallIrtAllocator* small_irt_allocator_;
+ jni::SmallLrtAllocator* small_lrt_allocator_;
std::unique_ptr<jni::JniIdManager> jni_id_manager_;
@@ -1332,7 +1448,7 @@
bool non_standard_exits_enabled_;
// Whether Java code needs to be debuggable.
- bool is_java_debuggable_;
+ RuntimeDebugState runtime_debug_state_;
bool monitor_timeout_enable_;
uint64_t monitor_timeout_ns_;
@@ -1437,6 +1553,9 @@
// True if files in /data/misc/apexdata/com.android.art are considered untrustworthy.
bool deny_art_apex_data_files_;
+ // Whether to allow compiling the boot classpath in memory when the given boot image is unusable.
+ bool allow_in_memory_compilation_ = false;
+
// Saved environment.
class EnvSnapshot {
public:
@@ -1473,6 +1592,9 @@
bool perfetto_hprof_enabled_;
bool perfetto_javaheapprof_enabled_;
+ // Called on out of memory error
+ void (*out_of_memory_error_hook_)();
+
metrics::ArtMetrics metrics_;
std::unique_ptr<metrics::MetricsReporter> metrics_reporter_;
@@ -1493,6 +1615,7 @@
friend class ScopedThreadPoolUsage;
friend class OatFileAssistantTest;
class NotifyStartupCompletedTask;
+ class SetupLinearAllocForZygoteFork;
DISALLOW_COPY_AND_ASSIGN(Runtime);
};
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
index 753ac28..28c81a2 100644
--- a/runtime/runtime_callbacks.cc
+++ b/runtime/runtime_callbacks.cc
@@ -105,9 +105,9 @@
Remove(cb, &method_inspection_callbacks_);
}
-bool RuntimeCallbacks::IsMethodBeingInspected(ArtMethod* m) {
+bool RuntimeCallbacks::HaveLocalsChanged() {
for (MethodInspectionCallback* cb : COPY(method_inspection_callbacks_)) {
- if (cb->IsMethodBeingInspected(m)) {
+ if (cb->HaveLocalsChanged()) {
return true;
}
}
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index b1a7e55..98584a8 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -143,9 +143,8 @@
public:
virtual ~MethodInspectionCallback() {}
- // Returns true if the method is being inspected currently and the runtime should not modify it in
- // potentially dangerous ways (i.e. replace with compiled version, JIT it, etc).
- virtual bool IsMethodBeingInspected(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+ // Returns true if any locals have changed. If any locals have changed we shouldn't OSR.
+ virtual bool HaveLocalsChanged() REQUIRES_SHARED(Locks::mutator_lock_) = 0;
};
// Callback to let something request to be notified when reflective objects are being visited and
@@ -225,9 +224,9 @@
void AddParkCallback(ParkCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
void RemoveParkCallback(ParkCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
- // Returns true if some MethodInspectionCallback indicates the method is being inspected/depended
- // on by some code.
- bool IsMethodBeingInspected(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
+ // Returns true if any locals have changed. This is used to prevent OSRing frames that have
+ // some locals changed.
+ bool HaveLocalsChanged() REQUIRES_SHARED(Locks::mutator_lock_);
void AddMethodInspectionCallback(MethodInspectionCallback* cb)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc
index 7f64721..6f0b8a1 100644
--- a/runtime/runtime_callbacks_test.cc
+++ b/runtime/runtime_callbacks_test.cc
@@ -27,7 +27,7 @@
#include "jni.h"
-#include "art_method-inl.h"
+#include "art_method-alloc-inl.h"
#include "base/mem_map.h"
#include "base/mutex.h"
#include "class_linker.h"
@@ -35,7 +35,7 @@
#include "dex/class_reference.h"
#include "handle.h"
#include "handle_scope-inl.h"
-#include "mirror/class-inl.h"
+#include "mirror/class-alloc-inl.h"
#include "mirror/class_loader.h"
#include "monitor-inl.h"
#include "nativehelper/scoped_local_ref.h"
@@ -44,7 +44,7 @@
#include "scoped_thread_state_change-inl.h"
#include "thread-inl.h"
#include "thread_list.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
namespace art {
@@ -164,40 +164,34 @@
cb_.state = CallbackState::kBase; // Ignore main thread attach.
- {
- ScopedObjectAccess soa(self);
- MakeExecutable(soa.Decode<mirror::Class>(WellKnownClasses::java_lang_Thread));
- }
+ ScopedObjectAccess soa(self);
+ MakeExecutable(WellKnownClasses::java_lang_Thread.Get());
- JNIEnv* env = self->GetJniEnv();
+ StackHandleScope<3u> hs(self);
+ Handle<mirror::String> thread_name = hs.NewHandle(
+ mirror::String::AllocFromModifiedUtf8(self, "ThreadLifecycleCallback test thread"));
+ ASSERT_TRUE(thread_name != nullptr);
- ScopedLocalRef<jobject> thread_name(env,
- env->NewStringUTF("ThreadLifecycleCallback test thread"));
- ASSERT_TRUE(thread_name.get() != nullptr);
+ Handle<mirror::Object> thread_group =
+ hs.NewHandle(soa.Decode<mirror::Object>(runtime_->GetMainThreadGroup()));
+ Handle<mirror::Object> thread =
+ WellKnownClasses::java_lang_Thread_init->NewObject<'L', 'L', 'I', 'Z'>(
+ hs, self, thread_group, thread_name, kMinThreadPriority, /*daemon=*/ false);
+ ASSERT_FALSE(self->IsExceptionPending());
+ ASSERT_TRUE(thread != nullptr);
- ScopedLocalRef<jobject> thread(env, env->AllocObject(WellKnownClasses::java_lang_Thread));
- ASSERT_TRUE(thread.get() != nullptr);
+ ArtMethod* start_method =
+ thread->GetClass()->FindClassMethod("start", "()V", kRuntimePointerSize);
+ ASSERT_TRUE(start_method != nullptr);
- env->CallNonvirtualVoidMethod(thread.get(),
- WellKnownClasses::java_lang_Thread,
- WellKnownClasses::java_lang_Thread_init,
- runtime_->GetMainThreadGroup(),
- thread_name.get(),
- kMinThreadPriority,
- JNI_FALSE);
- ASSERT_FALSE(env->ExceptionCheck());
+ start_method->InvokeVirtual<'V'>(self, thread.Get());
+ ASSERT_FALSE(self->IsExceptionPending());
- jmethodID start_id = env->GetMethodID(WellKnownClasses::java_lang_Thread, "start", "()V");
- ASSERT_TRUE(start_id != nullptr);
+ ArtMethod* join_method = thread->GetClass()->FindClassMethod("join", "()V", kRuntimePointerSize);
+ ASSERT_TRUE(join_method != nullptr);
- env->CallVoidMethod(thread.get(), start_id);
- ASSERT_FALSE(env->ExceptionCheck());
-
- jmethodID join_id = env->GetMethodID(WellKnownClasses::java_lang_Thread, "join", "()V");
- ASSERT_TRUE(join_id != nullptr);
-
- env->CallVoidMethod(thread.get(), join_id);
- ASSERT_FALSE(env->ExceptionCheck());
+ join_method->InvokeFinal<'V'>(self, thread.Get());
+ ASSERT_FALSE(self->IsExceptionPending());
EXPECT_EQ(cb_.state, CallbackState::kDied);
}
@@ -303,6 +297,7 @@
TEST_F(ClassLoadCallbackRuntimeCallbacksTest, ClassLoadCallback) {
ScopedObjectAccess soa(Thread::Current());
jobject jclass_loader = LoadDex("XandY");
+ cb_.data.clear(); // Clear class loading records from `LoadDex()`, if any.
VariableSizedHandleScope hs(soa.Self());
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(
soa.Decode<mirror::ClassLoader>(jclass_loader)));
@@ -514,12 +509,11 @@
ASSERT_TRUE(started);
{
ScopedObjectAccess soa(self);
- cb_.SetInterestingObject(
- soa.Decode<mirror::Class>(WellKnownClasses::java_util_Collections));
+ cb_.SetInterestingObject(WellKnownClasses::java_util_Collections.Get());
Monitor::Wait(
self,
// Just a random class
- soa.Decode<mirror::Class>(WellKnownClasses::java_util_Collections),
+ WellKnownClasses::java_util_Collections.Get(),
/*ms=*/0,
/*ns=*/0,
/*interruptShouldThrow=*/false,
diff --git a/runtime/runtime_common.h b/runtime/runtime_common.h
index 925594e..ec08907 100644
--- a/runtime/runtime_common.h
+++ b/runtime/runtime_common.h
@@ -42,7 +42,7 @@
void Dump(std::ostream& os) const {
// This is a backtrace from a crash, do not skip any frames in case the
// crash is in the unwinder itself.
- DumpNativeStack(os, GetTid(), nullptr, "\t", nullptr, raw_context_, false);
+ DumpNativeStack(os, GetTid(), "\t", nullptr, raw_context_, false);
}
private:
// Stores the context of the signal that was unexpected and will terminate the runtime. The
diff --git a/runtime/runtime_image.cc b/runtime/runtime_image.cc
new file mode 100644
index 0000000..7137991
--- /dev/null
+++ b/runtime/runtime_image.cc
@@ -0,0 +1,662 @@
+/*
+ * Copyright (C) 2022 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 "runtime_image.h"
+
+#include <lz4.h>
+#include <sstream>
+#include <unistd.h>
+
+#include "android-base/stringprintf.h"
+
+#include "base/bit_utils.h"
+#include "base/file_utils.h"
+#include "base/length_prefixed_array.h"
+#include "base/unix_file/fd_file.h"
+#include "base/utils.h"
+#include "class_loader_utils.h"
+#include "class_root-inl.h"
+#include "gc/space/image_space.h"
+#include "image.h"
+#include "mirror/object-inl.h"
+#include "mirror/object-refvisitor-inl.h"
+#include "mirror/object_array-alloc-inl.h"
+#include "mirror/object_array-inl.h"
+#include "mirror/object_array.h"
+#include "mirror/string-inl.h"
+#include "scoped_thread_state_change-inl.h"
+#include "vdex_file.h"
+
+namespace art {
+
+/**
+ * Helper class to generate an app image at runtime.
+ */
+class RuntimeImageHelper {
+ public:
+ explicit RuntimeImageHelper(gc::Heap* heap) :
+ boot_image_begin_(heap->GetBootImagesStartAddress()),
+ boot_image_size_(heap->GetBootImagesSize()),
+ image_begin_(boot_image_begin_ + boot_image_size_),
+ // Note: image relocation considers the image header in the bitmap.
+ object_section_size_(sizeof(ImageHeader)),
+ intern_table_(InternStringHash(this), InternStringEquals(this)) {}
+
+
+ bool Generate(std::string* error_msg) {
+ if (!WriteObjects(error_msg)) {
+ return false;
+ }
+
+ // Generate the sections information stored in the header.
+ dchecked_vector<ImageSection> sections(ImageHeader::kSectionCount);
+ CreateImageSections(sections);
+
+ // Generate the bitmap section, stored page aligned after the sections data
+ // and of size `object_section_size_` page aligned.
+ size_t sections_end = sections[ImageHeader::kSectionMetadata].End();
+ image_bitmap_ = gc::accounting::ContinuousSpaceBitmap::Create(
+ "image bitmap",
+ reinterpret_cast<uint8_t*>(image_begin_),
+ RoundUp(object_section_size_, kPageSize));
+ for (uint32_t offset : object_offsets_) {
+ DCHECK(IsAligned<kObjectAlignment>(image_begin_ + sizeof(ImageHeader) + offset));
+ image_bitmap_.Set(
+ reinterpret_cast<mirror::Object*>(image_begin_ + sizeof(ImageHeader) + offset));
+ }
+ const size_t bitmap_bytes = image_bitmap_.Size();
+ auto* bitmap_section = §ions[ImageHeader::kSectionImageBitmap];
+ *bitmap_section = ImageSection(RoundUp(sections_end, kPageSize),
+ RoundUp(bitmap_bytes, kPageSize));
+
+ // Compute boot image checksum and boot image components, to be stored in
+ // the header.
+ gc::Heap* const heap = Runtime::Current()->GetHeap();
+ uint32_t boot_image_components = 0u;
+ uint32_t boot_image_checksums = 0u;
+ const std::vector<gc::space::ImageSpace*>& image_spaces = heap->GetBootImageSpaces();
+ for (size_t i = 0u, size = image_spaces.size(); i != size; ) {
+ const ImageHeader& header = image_spaces[i]->GetImageHeader();
+ boot_image_components += header.GetComponentCount();
+ boot_image_checksums ^= header.GetImageChecksum();
+ DCHECK_LE(header.GetImageSpaceCount(), size - i);
+ i += header.GetImageSpaceCount();
+ }
+
+ header_ = ImageHeader(
+ /* image_reservation_size= */ RoundUp(sections_end, kPageSize),
+ /* component_count= */ 1,
+ image_begin_,
+ sections_end,
+ sections.data(),
+ /* image_roots= */ image_begin_ + sizeof(ImageHeader),
+ /* oat_checksum= */ 0,
+ /* oat_file_begin= */ 0,
+ /* oat_data_begin= */ 0,
+ /* oat_data_end= */ 0,
+ /* oat_file_end= */ 0,
+ heap->GetBootImagesStartAddress(),
+ heap->GetBootImagesSize(),
+ boot_image_components,
+ boot_image_checksums,
+ static_cast<uint32_t>(kRuntimePointerSize));
+
+ // Data size includes everything except the bitmap.
+ header_.data_size_ = sections_end;
+
+ // Write image methods - needs to happen after creation of the header.
+ WriteImageMethods();
+
+ return true;
+ }
+
+ const std::vector<uint8_t>& GetData() const {
+ return image_data_;
+ }
+
+ const ImageHeader& GetHeader() const {
+ return header_;
+ }
+
+ const gc::accounting::ContinuousSpaceBitmap& GetImageBitmap() const {
+ return image_bitmap_;
+ }
+
+ const std::string& GetDexLocation() const {
+ return dex_location_;
+ }
+
+ void GenerateInternData(std::vector<uint8_t>& data) const {
+ intern_table_.WriteToMemory(data.data());
+ }
+
+ private:
+ bool IsInBootImage(const void* obj) const {
+ return reinterpret_cast<uintptr_t>(obj) - boot_image_begin_ < boot_image_size_;
+ }
+
+ // Returns a pointer that can be stored in `image_data_`:
+ // - The pointer itself for boot image objects,
+ // - The offset in the image for all other objects.
+ mirror::Object* GetOrComputeImageAddress(ObjPtr<mirror::Object> object)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (object == nullptr || IsInBootImage(object.Ptr())) {
+ DCHECK(object == nullptr || Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(object));
+ return object.Ptr();
+ } else if (object->IsClassLoader()) {
+ // DexCache and Class point to class loaders. For runtime-generated app
+ // images, we don't encode the class loader. It will be set when the
+ // runtime is loading the image.
+ return nullptr;
+ } else {
+ uint32_t offset = CopyObject(object);
+ return reinterpret_cast<mirror::Object*>(image_begin_ + sizeof(ImageHeader) + offset);
+ }
+ }
+
+ void CreateImageSections(dchecked_vector<ImageSection>& sections) const {
+ sections[ImageHeader::kSectionObjects] =
+ ImageSection(0u, object_section_size_);
+ sections[ImageHeader::kSectionArtFields] =
+ ImageSection(sections[ImageHeader::kSectionObjects].End(), 0u);
+ sections[ImageHeader::kSectionArtMethods] =
+ ImageSection(sections[ImageHeader::kSectionArtFields].End(), 0u);
+ sections[ImageHeader::kSectionImTables] =
+ ImageSection(sections[ImageHeader::kSectionArtMethods].End(), 0u);
+ sections[ImageHeader::kSectionIMTConflictTables] =
+ ImageSection(sections[ImageHeader::kSectionImTables].End(), 0u);
+ sections[ImageHeader::kSectionRuntimeMethods] =
+ ImageSection(sections[ImageHeader::kSectionIMTConflictTables].End(), 0u);
+
+ // Round up to the alignment the string table expects. See HashSet::WriteToMemory.
+ size_t cur_pos = RoundUp(sections[ImageHeader::kSectionRuntimeMethods].End(), sizeof(uint64_t));
+
+ size_t intern_table_bytes = intern_table_.WriteToMemory(nullptr);
+ sections[ImageHeader::kSectionInternedStrings] = ImageSection(cur_pos, intern_table_bytes);
+
+ // Obtain the new position and round it up to the appropriate alignment.
+ cur_pos = RoundUp(sections[ImageHeader::kSectionInternedStrings].End(), sizeof(uint64_t));
+ sections[ImageHeader::kSectionClassTable] = ImageSection(cur_pos, 0u);
+
+ // Round up to the alignment of the offsets we are going to store.
+ cur_pos = RoundUp(sections[ImageHeader::kSectionClassTable].End(), sizeof(uint32_t));
+ sections[ImageHeader::kSectionStringReferenceOffsets] = ImageSection(cur_pos, 0u);
+
+ // Round up to the alignment of the offsets we are going to store.
+ cur_pos =
+ RoundUp(sections[ImageHeader::kSectionStringReferenceOffsets].End(), sizeof(uint32_t));
+
+ sections[ImageHeader::kSectionMetadata] = ImageSection(cur_pos, 0u);
+ }
+
+ // Returns the copied mirror Object. This is really its content, it should not
+ // be returned as an `ObjPtr` (as it's not a GC object), nor stored anywhere.
+ template<typename T> T* FromImageOffsetToRuntimeContent(uint32_t offset) {
+ uint32_t vector_data_offset = offset - sizeof(ImageHeader) - image_begin_;
+ return reinterpret_cast<T*>(image_data_.data() + vector_data_offset);
+ }
+
+ class InternStringHash {
+ public:
+ explicit InternStringHash(RuntimeImageHelper* helper) : helper_(helper) {}
+
+ // NO_THREAD_SAFETY_ANALYSIS as these helpers get passed to `HashSet`.
+ size_t operator()(mirror::String* str) const NO_THREAD_SAFETY_ANALYSIS {
+ int32_t hash = str->GetStoredHashCode();
+ DCHECK_EQ(hash, str->ComputeHashCode());
+ // An additional cast to prevent undesired sign extension.
+ return static_cast<uint32_t>(hash);
+ }
+
+ size_t operator()(uint32_t entry) const NO_THREAD_SAFETY_ANALYSIS {
+ return (*this)(helper_->FromImageOffsetToRuntimeContent<mirror::String>(entry));
+ }
+
+ private:
+ RuntimeImageHelper* helper_;
+ };
+
+ class InternStringEquals {
+ public:
+ explicit InternStringEquals(RuntimeImageHelper* helper) : helper_(helper) {}
+
+ // NO_THREAD_SAFETY_ANALYSIS as these helpers get passed to `HashSet`.
+ bool operator()(uint32_t entry, mirror::String* other) const NO_THREAD_SAFETY_ANALYSIS {
+ if (kIsDebugBuild) {
+ Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
+ }
+ return other->Equals(helper_->FromImageOffsetToRuntimeContent<mirror::String>(entry));
+ }
+
+ bool operator()(uint32_t entry, uint32_t other) const NO_THREAD_SAFETY_ANALYSIS {
+ return (*this)(entry, helper_->FromImageOffsetToRuntimeContent<mirror::String>(other));
+ }
+
+ private:
+ RuntimeImageHelper* helper_;
+ };
+
+ using InternTableSet =
+ HashSet<uint32_t, DefaultEmptyFn<uint32_t>, InternStringHash, InternStringEquals>;
+
+ void VisitDexCache(ObjPtr<mirror::DexCache> dex_cache) REQUIRES_SHARED(Locks::mutator_lock_) {
+ const DexFile& dex_file = *dex_cache->GetDexFile();
+ // Currently only copy string objects into the image. Populate the intern
+ // table with these strings.
+ for (uint32_t i = 0; i < dex_file.NumStringIds(); ++i) {
+ ObjPtr<mirror::String> str = dex_cache->GetResolvedString(dex::StringIndex(i));
+ if (str != nullptr && !IsInBootImage(str.Ptr())) {
+ uint32_t hash = static_cast<uint32_t>(str->GetStoredHashCode());
+ DCHECK_EQ(hash, static_cast<uint32_t>(str->ComputeHashCode()))
+ << "Dex cache strings should be interned";
+ if (intern_table_.FindWithHash(str.Ptr(), hash) == intern_table_.end()) {
+ uint32_t offset = CopyObject(str);
+ intern_table_.InsertWithHash(image_begin_ + offset + sizeof(ImageHeader), hash);
+ }
+ }
+ }
+ }
+
+ void VisitDexCaches(Handle<mirror::ObjectArray<mirror::Object>> dex_cache_array)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ for (int32_t i = 0; i < dex_cache_array->GetLength(); ++i) {
+ VisitDexCache(ObjPtr<mirror::DexCache>::DownCast((dex_cache_array->Get(i))));
+ }
+ }
+
+ bool WriteObjects(std::string* error_msg) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ScopedObjectAccess soa(Thread::Current());
+ VariableSizedHandleScope handles(soa.Self());
+
+ Handle<mirror::Class> object_array_class = handles.NewHandle(
+ GetClassRoot<mirror::ObjectArray<mirror::Object>>(class_linker));
+
+ Handle<mirror::ObjectArray<mirror::Object>> image_roots = handles.NewHandle(
+ mirror::ObjectArray<mirror::Object>::Alloc(
+ soa.Self(), object_array_class.Get(), ImageHeader::kImageRootsMax));
+
+ if (image_roots == nullptr) {
+ DCHECK(soa.Self()->IsExceptionPending());
+ soa.Self()->ClearException();
+ *error_msg = "Out of memory when trying to generate a runtime app image";
+ return false;
+ }
+
+ // Find the dex files that will be used for generating the app image.
+ dchecked_vector<Handle<mirror::DexCache>> dex_caches;
+ FindDexCaches(soa.Self(), dex_caches, handles);
+
+ if (dex_caches.size() == 0) {
+ *error_msg = "Did not find dex caches to generate an app image";
+ return false;
+ }
+ const OatDexFile* oat_dex_file = dex_caches[0]->GetDexFile()->GetOatDexFile();
+ VdexFile* vdex_file = oat_dex_file->GetOatFile()->GetVdexFile();
+ // The first entry in `dex_caches` contains the location of the primary APK.
+ dex_location_ = oat_dex_file->GetDexFileLocation();
+
+ size_t number_of_dex_files = vdex_file->GetNumberOfDexFiles();
+ if (number_of_dex_files != dex_caches.size()) {
+ // This means some dex files haven't been executed. For simplicity, just
+ // register them and recollect dex caches.
+ Handle<mirror::ClassLoader> loader = handles.NewHandle(dex_caches[0]->GetClassLoader());
+ VisitClassLoaderDexFiles(soa.Self(), loader, [&](const art::DexFile* dex_file)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ class_linker->RegisterDexFile(*dex_file, dex_caches[0]->GetClassLoader());
+ return true; // Continue with other dex files.
+ });
+ dex_caches.clear();
+ FindDexCaches(soa.Self(), dex_caches, handles);
+ if (number_of_dex_files != dex_caches.size()) {
+ *error_msg = "Number of dex caches does not match number of dex files in the primary APK";
+ return false;
+ }
+ }
+
+ // Create and populate the checksums aray.
+ Handle<mirror::IntArray> checksums_array = handles.NewHandle(
+ mirror::IntArray::Alloc(soa.Self(), number_of_dex_files));
+
+ if (checksums_array == nullptr) {
+ DCHECK(soa.Self()->IsExceptionPending());
+ soa.Self()->ClearException();
+ *error_msg = "Out of memory when trying to generate a runtime app image";
+ return false;
+ }
+
+ const VdexFile::VdexChecksum* checksums = vdex_file->GetDexChecksumsArray();
+ static_assert(sizeof(VdexFile::VdexChecksum) == sizeof(int32_t));
+ for (uint32_t i = 0; i < number_of_dex_files; ++i) {
+ checksums_array->Set(i, checksums[i]);
+ }
+
+ // Create and populate the dex caches aray.
+ Handle<mirror::ObjectArray<mirror::Object>> dex_cache_array = handles.NewHandle(
+ mirror::ObjectArray<mirror::Object>::Alloc(
+ soa.Self(), object_array_class.Get(), dex_caches.size()));
+
+ if (dex_cache_array == nullptr) {
+ DCHECK(soa.Self()->IsExceptionPending());
+ soa.Self()->ClearException();
+ *error_msg = "Out of memory when trying to generate a runtime app image";
+ return false;
+ }
+
+ for (uint32_t i = 0; i < dex_caches.size(); ++i) {
+ dex_cache_array->Set(i, dex_caches[i].Get());
+ }
+
+ image_roots->Set(ImageHeader::kDexCaches, dex_cache_array.Get());
+ image_roots->Set(ImageHeader::kClassRoots, class_linker->GetClassRoots());
+ image_roots->Set(ImageHeader::kAppImageDexChecksums, checksums_array.Get());
+
+ {
+ // Now that we have created all objects needed for the `image_roots`, copy
+ // it into the buffer. Note that this will recursively copy all objects
+ // contained in `image_roots`. That's acceptable as we don't have cycles,
+ // nor a deep graph.
+ ScopedAssertNoThreadSuspension sants("Writing runtime app image");
+ CopyObject(image_roots.Get());
+ }
+
+ // Copy objects stored in the dex caches.
+ VisitDexCaches(dex_cache_array);
+ return true;
+ }
+
+ class FixupVisitor {
+ public:
+ FixupVisitor(RuntimeImageHelper* image, size_t copy_offset)
+ : image_(image), copy_offset_(copy_offset) {}
+
+ // We do not visit native roots. These are handled with other logic.
+ void VisitRootIfNonNull(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED)
+ const {
+ LOG(FATAL) << "UNREACHABLE";
+ }
+ void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {
+ LOG(FATAL) << "UNREACHABLE";
+ }
+
+ void operator()(ObjPtr<mirror::Object> obj,
+ MemberOffset offset,
+ bool is_static ATTRIBUTE_UNUSED) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Object> ref = obj->GetFieldObject<mirror::Object>(offset);
+ mirror::Object* address = image_->GetOrComputeImageAddress(ref.Ptr());
+ mirror::Object* copy =
+ reinterpret_cast<mirror::Object*>(image_->image_data_.data() + copy_offset_);
+ copy->GetFieldObjectReferenceAddr<kVerifyNone>(offset)->Assign(address);
+ }
+
+ // java.lang.ref.Reference visitor.
+ void operator()(ObjPtr<mirror::Class> klass ATTRIBUTE_UNUSED,
+ ObjPtr<mirror::Reference> ref) const
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ operator()(ref, mirror::Reference::ReferentOffset(), /* is_static */ false);
+ }
+
+ private:
+ RuntimeImageHelper* image_;
+ size_t copy_offset_;
+ };
+
+ // Copy `obj` in `image_data_` and relocate references. Returns the offset
+ // within our buffer.
+ uint32_t CopyObject(ObjPtr<mirror::Object> obj) REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Copy the object in `image_data_`.
+ size_t object_size = obj->SizeOf();
+ size_t offset = image_data_.size();
+ DCHECK(IsAligned<kObjectAlignment>(offset));
+ object_offsets_.push_back(offset);
+ image_data_.resize(RoundUp(image_data_.size() + object_size, kObjectAlignment));
+ memcpy(image_data_.data() + offset, obj.Ptr(), object_size);
+ object_section_size_ += RoundUp(object_size, kObjectAlignment);
+
+ // Fixup reference pointers.
+ FixupVisitor visitor(this, offset);
+ obj->VisitReferences</*kVisitNativeRoots=*/ false>(visitor, visitor);
+
+ mirror::Object* copy = reinterpret_cast<mirror::Object*>(image_data_.data() + offset);
+
+ // Clear any lockword data.
+ copy->SetLockWord(LockWord::Default(), /* as_volatile= */ false);
+
+ // For dex caches, clear pointers to data that will be set at runtime.
+ if (obj->IsDexCache()) {
+ reinterpret_cast<mirror::DexCache*>(copy)->ResetNativeArrays();
+ reinterpret_cast<mirror::DexCache*>(copy)->SetDexFile(nullptr);
+ }
+ return offset;
+ }
+
+ class CollectDexCacheVisitor : public DexCacheVisitor {
+ public:
+ explicit CollectDexCacheVisitor(VariableSizedHandleScope& handles) : handles_(handles) {}
+
+ void Visit(ObjPtr<mirror::DexCache> dex_cache)
+ REQUIRES_SHARED(Locks::dex_lock_, Locks::mutator_lock_) override {
+ dex_caches_.push_back(handles_.NewHandle(dex_cache));
+ }
+ const std::vector<Handle<mirror::DexCache>>& GetDexCaches() const {
+ return dex_caches_;
+ }
+ private:
+ VariableSizedHandleScope& handles_;
+ std::vector<Handle<mirror::DexCache>> dex_caches_;
+ };
+
+ // Find dex caches corresponding to the primary APK.
+ void FindDexCaches(Thread* self,
+ dchecked_vector<Handle<mirror::DexCache>>& dex_caches,
+ VariableSizedHandleScope& handles)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(dex_caches.empty());
+ // Collect all dex caches.
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ CollectDexCacheVisitor visitor(handles);
+ {
+ ReaderMutexLock mu(self, *Locks::dex_lock_);
+ class_linker->VisitDexCaches(&visitor);
+ }
+
+ // Find the primary APK.
+ AppInfo* app_info = Runtime::Current()->GetAppInfo();
+ for (Handle<mirror::DexCache> cache : visitor.GetDexCaches()) {
+ if (app_info->GetRegisteredCodeType(cache->GetDexFile()->GetLocation()) ==
+ AppInfo::CodeType::kPrimaryApk) {
+ dex_caches.push_back(handles.NewHandle(cache.Get()));
+ break;
+ }
+ }
+
+ if (dex_caches.empty()) {
+ return;
+ }
+
+ const OatDexFile* oat_dex_file = dex_caches[0]->GetDexFile()->GetOatDexFile();
+ if (oat_dex_file == nullptr) {
+ // We need a .oat file for loading an app image;
+ dex_caches.clear();
+ return;
+ }
+ const OatFile* oat_file = oat_dex_file->GetOatFile();
+ for (Handle<mirror::DexCache> cache : visitor.GetDexCaches()) {
+ if (cache.Get() != dex_caches[0].Get()) {
+ const OatDexFile* other_oat_dex_file = cache->GetDexFile()->GetOatDexFile();
+ if (other_oat_dex_file != nullptr && other_oat_dex_file->GetOatFile() == oat_file) {
+ dex_caches.push_back(handles.NewHandle(cache.Get()));
+ }
+ }
+ }
+ }
+
+ static uint64_t PointerToUint64(void* ptr) {
+ return reinterpret_cast64<uint64_t>(ptr);
+ }
+
+ void WriteImageMethods() {
+ ScopedObjectAccess soa(Thread::Current());
+ // We can just use plain runtime pointers.
+ Runtime* runtime = Runtime::Current();
+ header_.image_methods_[ImageHeader::kResolutionMethod] =
+ PointerToUint64(runtime->GetResolutionMethod());
+ header_.image_methods_[ImageHeader::kImtConflictMethod] =
+ PointerToUint64(runtime->GetImtConflictMethod());
+ header_.image_methods_[ImageHeader::kImtUnimplementedMethod] =
+ PointerToUint64(runtime->GetImtUnimplementedMethod());
+ header_.image_methods_[ImageHeader::kSaveAllCalleeSavesMethod] =
+ PointerToUint64(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveAllCalleeSaves));
+ header_.image_methods_[ImageHeader::kSaveRefsOnlyMethod] =
+ PointerToUint64(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsOnly));
+ header_.image_methods_[ImageHeader::kSaveRefsAndArgsMethod] =
+ PointerToUint64(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs));
+ header_.image_methods_[ImageHeader::kSaveEverythingMethod] =
+ PointerToUint64(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverything));
+ header_.image_methods_[ImageHeader::kSaveEverythingMethodForClinit] =
+ PointerToUint64(runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForClinit));
+ header_.image_methods_[ImageHeader::kSaveEverythingMethodForSuspendCheck] =
+ PointerToUint64(
+ runtime->GetCalleeSaveMethod(CalleeSaveType::kSaveEverythingForSuspendCheck));
+ }
+
+ // Header for the image, created at the end once we know the size of all
+ // sections.
+ ImageHeader header_;
+
+ // Contents of the image sections.
+ std::vector<uint8_t> image_data_;
+
+ // Bitmap of live objects in `image_data_`. Populated from `object_offsets_`
+ // once we know `object_section_size`.
+ gc::accounting::ContinuousSpaceBitmap image_bitmap_;
+
+ // A list of offsets in `image_data_` where objects begin.
+ std::vector<uint32_t> object_offsets_;
+
+ // Cached values of boot image information.
+ const uint32_t boot_image_begin_;
+ const uint32_t boot_image_size_;
+
+ // Where the image begins: just after the boot image.
+ const uint32_t image_begin_;
+
+ // Size of the `kSectionObjects` section.
+ size_t object_section_size_;
+
+ // The location of the primary APK / dex file.
+ std::string dex_location_;
+
+ // The intern table for strings that we will write to disk.
+ InternTableSet intern_table_;
+};
+
+std::string RuntimeImage::GetRuntimeImagePath(const std::string& dex_location) {
+ const std::string& data_dir = Runtime::Current()->GetProcessDataDirectory();
+
+ std::string new_location = ReplaceFileExtension(
+ dex_location, (kRuntimePointerSize == PointerSize::k32 ? "art32" : "art64"));
+
+ if (data_dir.empty()) {
+ // The data ditectory is empty for tests.
+ return new_location;
+ } else {
+ std::replace(new_location.begin(), new_location.end(), '/', '@');
+ return data_dir + "/" + new_location;
+ }
+}
+
+bool RuntimeImage::WriteImageToDisk(std::string* error_msg) {
+ gc::Heap* heap = Runtime::Current()->GetHeap();
+ if (!heap->HasBootImageSpace()) {
+ *error_msg = "Cannot generate an app image without a boot image";
+ return false;
+ }
+ RuntimeImageHelper image(heap);
+ if (!image.Generate(error_msg)) {
+ return false;
+ }
+
+ const std::string path = GetRuntimeImagePath(image.GetDexLocation());
+ // We first generate the app image in a temporary file, which we will then
+ // move to `path`.
+ const std::string temp_path = path + std::to_string(getpid());
+ std::unique_ptr<File> out(OS::CreateEmptyFileWriteOnly(temp_path.c_str()));
+ if (out == nullptr) {
+ *error_msg = "Could not open " + temp_path + " for writing";
+ return false;
+ }
+
+ // Write section infos. The header is written at the end in case we get killed.
+ if (!out->Write(reinterpret_cast<const char*>(image.GetData().data()),
+ image.GetData().size(),
+ sizeof(ImageHeader))) {
+ *error_msg = "Could not write image data to " + temp_path;
+ out->Unlink();
+ return false;
+ }
+
+ {
+ // Write intern string set.
+ auto intern_section = image.GetHeader().GetImageSection(ImageHeader::kSectionInternedStrings);
+ std::vector<uint8_t> intern_data(intern_section.Size());
+ image.GenerateInternData(intern_data);
+ if (!out->Write(reinterpret_cast<const char*>(intern_data.data()),
+ intern_section.Size(),
+ intern_section.Offset())) {
+ *error_msg = "Could not write intern section " + temp_path;
+ out->Unlink();
+ return false;
+ }
+ }
+
+ // Write bitmap.
+ auto bitmap_section = image.GetHeader().GetImageSection(ImageHeader::kSectionImageBitmap);
+ if (!out->Write(reinterpret_cast<const char*>(image.GetImageBitmap().Begin()),
+ bitmap_section.Size(),
+ bitmap_section.Offset())) {
+ *error_msg = "Could not write image bitmap " + temp_path;
+ out->Unlink();
+ return false;
+ }
+
+ // Now write header.
+ if (!out->Write(reinterpret_cast<const char*>(&image.GetHeader()), sizeof(ImageHeader), 0u)) {
+ *error_msg = "Could not write image header to " + temp_path;
+ out->Unlink();
+ return false;
+ }
+
+ if (out->FlushClose() != 0) {
+ *error_msg = "Could not flush and close " + temp_path;
+ // Unlink directly: we cannot use `out` as we may have closed it.
+ unlink(temp_path.c_str());
+ return false;
+ }
+
+ if (rename(temp_path.c_str(), path.c_str()) != 0) {
+ *error_msg =
+ "Failed to move runtime app image to " + path + ": " + std::string(strerror(errno));
+ // Unlink directly: we cannot use `out` as we have closed it.
+ unlink(temp_path.c_str());
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace art
diff --git a/runtime/runtime_image.h b/runtime/runtime_image.h
new file mode 100644
index 0000000..d494e1c
--- /dev/null
+++ b/runtime/runtime_image.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_RUNTIME_IMAGE_H_
+#define ART_RUNTIME_RUNTIME_IMAGE_H_
+
+#include <string>
+
+namespace art {
+
+class RuntimeImage {
+ public:
+ // Writes an app image for the currently running process.
+ static bool WriteImageToDisk(std::string* error_msg);
+
+ // Gets the path where a runtime-generated app image is stored.
+ static std::string GetRuntimeImagePath(const std::string& dex_location);
+};
+
+} // namespace art
+
+#endif // ART_RUNTIME_RUNTIME_IMAGE_H_
diff --git a/runtime/runtime_intrinsics.cc b/runtime/runtime_intrinsics.cc
index 1672c49..fb60cfe 100644
--- a/runtime/runtime_intrinsics.cc
+++ b/runtime/runtime_intrinsics.cc
@@ -90,10 +90,10 @@
}
}
-bool AreAllIntrinsicsInitialized() {
- ScopedObjectAccess soa(Thread::Current());
+bool AreAllIntrinsicsInitialized() REQUIRES_SHARED(Locks::mutator_lock_) {
+ Thread* self = Thread::Current();
#define IS_INTRINSIC_INITIALIZED(Name, InvokeType, _, __, ___, ClassName, MethodName, Signature) \
- IsIntrinsicInitialized(soa.Self(), \
+ IsIntrinsicInitialized(self, \
Intrinsics::k##Name, \
InvokeType, \
ClassName, \
@@ -107,11 +107,11 @@
} // namespace
void InitializeIntrinsics() {
- ScopedObjectAccess soa(Thread::Current());
+ Thread* self = Thread::Current();
// Initialization here uses the short-circuit operator || to stop
// initializing if there's an already initialized intrinsic.
#define INITIALIZE_INTRINSIC(Name, InvokeType, _, __, ___, ClassName, MethodName, Signature) \
- InitializeIntrinsic(soa.Self(), \
+ InitializeIntrinsic(self, \
Intrinsics::k##Name, \
InvokeType, \
ClassName, \
diff --git a/runtime/runtime_intrinsics.h b/runtime/runtime_intrinsics.h
index 98dc9bc..bf2cb2b 100644
--- a/runtime/runtime_intrinsics.h
+++ b/runtime/runtime_intrinsics.h
@@ -17,9 +17,11 @@
#ifndef ART_RUNTIME_RUNTIME_INTRINSICS_H_
#define ART_RUNTIME_RUNTIME_INTRINSICS_H_
+#include "base/locks.h"
+
namespace art {
-void InitializeIntrinsics();
+void InitializeIntrinsics() REQUIRES_SHARED(Locks::mutator_lock_);
} // namespace art
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 76d1657..769ed04 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -47,6 +47,7 @@
RUNTIME_OPTIONS_KEY (std::string, ClassPath)
RUNTIME_OPTIONS_KEY (ParseStringList<':'>,Image)
RUNTIME_OPTIONS_KEY (Unit, ForceJitZygote)
+RUNTIME_OPTIONS_KEY (Unit, AllowInMemoryCompilation)
RUNTIME_OPTIONS_KEY (Unit, CheckJni)
RUNTIME_OPTIONS_KEY (Unit, JniOptsForceCopy)
RUNTIME_OPTIONS_KEY (std::string, JdwpOptions, "suspend=n,server=y")
@@ -80,7 +81,7 @@
RUNTIME_OPTIONS_KEY (Unit, IgnoreMaxFootprint)
RUNTIME_OPTIONS_KEY (bool, AlwaysLogExplicitGcs, true)
RUNTIME_OPTIONS_KEY (Unit, LowMemoryMode)
-RUNTIME_OPTIONS_KEY (bool, UseTLAB, (kUseTlab || kUseReadBarrier))
+RUNTIME_OPTIONS_KEY (bool, UseTLAB, kUseTlab)
RUNTIME_OPTIONS_KEY (bool, EnableHSpaceCompactForOOM, true)
RUNTIME_OPTIONS_KEY (bool, UseJitCompilation, true)
RUNTIME_OPTIONS_KEY (bool, UseProfiledJitCompilation, false)
diff --git a/runtime/scoped_thread_state_change-inl.h b/runtime/scoped_thread_state_change-inl.h
index d601952..674d791 100644
--- a/runtime/scoped_thread_state_change-inl.h
+++ b/runtime/scoped_thread_state_change-inl.h
@@ -94,7 +94,7 @@
}
inline ScopedObjectAccessAlreadyRunnable::ScopedObjectAccessAlreadyRunnable(JNIEnv* env)
- : self_(ThreadForEnv(env)), env_(down_cast<JNIEnvExt*>(env)), vm_(env_->GetVm()) {}
+ : self_(Thread::ForEnv(env)), env_(down_cast<JNIEnvExt*>(env)), vm_(env_->GetVm()) {}
inline ScopedObjectAccessAlreadyRunnable::ScopedObjectAccessAlreadyRunnable(Thread* self)
: self_(self),
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 50a96d0..7b70d41 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -129,7 +129,7 @@
GetCurrentQuickFrame(), cur_quick_frame_pc_, abort_on_failure);
} else if (cur_oat_quick_method_header_->IsOptimized()) {
StackMap* stack_map = GetCurrentStackMap();
- DCHECK(stack_map->IsValid());
+ CHECK(stack_map->IsValid()) << "StackMap not found for " << std::hex << cur_quick_frame_pc_;
return stack_map->GetDexPc();
} else {
DCHECK(cur_oat_quick_method_header_->IsNterpMethodHeader());
@@ -140,6 +140,29 @@
}
}
+std::vector<uint32_t> StackVisitor::ComputeDexPcList(uint32_t handler_dex_pc) const {
+ std::vector<uint32_t> result;
+ if (cur_shadow_frame_ == nullptr && cur_quick_frame_ != nullptr && IsInInlinedFrame()) {
+ const BitTableRange<InlineInfo>& infos = current_inline_frames_;
+ DCHECK_NE(infos.size(), 0u);
+
+ // Outermost dex_pc.
+ result.push_back(GetCurrentStackMap()->GetDexPc());
+
+ // The mid dex_pcs. Note that we skip the last one since we want to change that for
+ // `handler_dex_pc`.
+ for (size_t index = 0; index < infos.size() - 1; ++index) {
+ result.push_back(infos[index].GetDexPc());
+ }
+ }
+
+ // The innermost dex_pc has to be the handler dex_pc. In the case of no inline frames, it will be
+ // just the one dex_pc. In the case of inlining we will be replacing the innermost InlineInfo's
+ // dex_pc with this one.
+ result.push_back(handler_dex_pc);
+ return result;
+}
+
extern "C" mirror::Object* artQuickGetProxyThisObject(ArtMethod** sp)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -213,7 +236,8 @@
uint16_t vreg,
VRegKind kind,
uint32_t* val,
- std::optional<DexRegisterLocation> location) const {
+ std::optional<DexRegisterLocation> location,
+ bool need_full_register_list) const {
if (cur_quick_frame_ != nullptr) {
DCHECK(context_ != nullptr); // You can't reliably read registers without a context.
DCHECK(m == GetMethod());
@@ -235,10 +259,10 @@
// which does not decode the stack maps.
result = GetVRegFromOptimizedCode(location.value(), val);
// Compare to the slower overload.
- DCHECK_EQ(result, GetVRegFromOptimizedCode(m, vreg, kind, &val2));
+ DCHECK_EQ(result, GetVRegFromOptimizedCode(m, vreg, kind, &val2, need_full_register_list));
DCHECK_EQ(*val, val2);
} else {
- result = GetVRegFromOptimizedCode(m, vreg, kind, val);
+ result = GetVRegFromOptimizedCode(m, vreg, kind, val, need_full_register_list);
}
}
if (kind == kReferenceVReg) {
@@ -261,16 +285,20 @@
}
}
+size_t StackVisitor::GetNumberOfRegisters(CodeInfo* code_info, int depth) const {
+ return depth == 0
+ ? code_info->GetNumberOfDexRegisters()
+ : current_inline_frames_[depth - 1].GetNumberOfDexRegisters();
+}
+
bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m,
uint16_t vreg,
VRegKind kind,
- uint32_t* val) const {
+ uint32_t* val,
+ bool need_full_register_list) const {
DCHECK_EQ(m, GetMethod());
// Can't be null or how would we compile its instructions?
DCHECK(m->GetCodeItem() != nullptr) << m->PrettyMethod();
- CodeItemDataAccessor accessor(m->DexInstructionData());
- uint16_t number_of_dex_registers = accessor.RegistersSize();
- DCHECK_LT(vreg, number_of_dex_registers);
const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader();
CodeInfo code_info(method_header);
@@ -278,13 +306,18 @@
StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
DCHECK(stack_map.IsValid());
- DexRegisterMap dex_register_map = IsInInlinedFrame()
- ? code_info.GetInlineDexRegisterMapOf(stack_map, current_inline_frames_.back())
- : code_info.GetDexRegisterMapOf(stack_map);
+ DexRegisterMap dex_register_map = (IsInInlinedFrame() && !need_full_register_list)
+ ? code_info.GetInlineDexRegisterMapOf(stack_map, current_inline_frames_.back())
+ : code_info.GetDexRegisterMapOf(stack_map,
+ /* first= */ 0,
+ GetNumberOfRegisters(&code_info, InlineDepth()));
+
if (dex_register_map.empty()) {
return false;
}
- DCHECK_EQ(dex_register_map.size(), number_of_dex_registers);
+
+ const size_t number_of_dex_registers = dex_register_map.size();
+ DCHECK_LT(vreg, number_of_dex_registers);
DexRegisterLocation::Kind location_kind = dex_register_map[vreg].GetKind();
switch (location_kind) {
case DexRegisterLocation::Kind::kInStack: {
@@ -745,15 +778,6 @@
// (resolution, instrumentation) trampoline; or
// - fake a Generic JNI frame in art_jni_dlsym_lookup_critical_stub.
DCHECK(method->IsNative());
- if (kIsDebugBuild && !method->IsCriticalNative()) {
- ClassLinker* class_linker = runtime->GetClassLinker();
- const void* entry_point = runtime->GetInstrumentation()->GetCodeForInvoke(method);
- CHECK(class_linker->IsQuickGenericJniStub(entry_point) ||
- // The current entrypoint (after filtering out trampolines) may have changed
- // from GenericJNI to JIT-compiled stub since we have entered this frame.
- (runtime->GetJit() != nullptr &&
- runtime->GetJit()->GetCodeCache()->ContainsPc(entry_point))) << method->PrettyMethod();
- }
// Generic JNI frame is just like the SaveRefsAndArgs frame.
// Note that HandleScope, if any, is below the frame.
return RuntimeCalleeSaveFrame::GetMethodFrameInfo(CalleeSaveType::kSaveRefsAndArgs);
@@ -780,7 +804,6 @@
DCHECK(thread_ == Thread::Current() || thread_->IsSuspended());
}
CHECK_EQ(cur_depth_, 0U);
- size_t inlined_frames_count = 0;
for (const ManagedStack* current_fragment = thread_->GetManagedStack();
current_fragment != nullptr; current_fragment = current_fragment->GetLink()) {
@@ -788,6 +811,12 @@
cur_quick_frame_ = current_fragment->GetTopQuickFrame();
cur_quick_frame_pc_ = 0;
DCHECK(cur_oat_quick_method_header_ == nullptr);
+
+ if (kDebugStackWalk) {
+ LOG(INFO) << "Tid=" << thread_-> GetThreadId()
+ << ", ManagedStack fragement: " << current_fragment;
+ }
+
if (cur_quick_frame_ != nullptr) { // Handle quick stack frames.
// Can't be both a shadow and a quick fragment.
DCHECK(current_fragment->GetTopShadowFrame() == nullptr);
@@ -800,10 +829,20 @@
// between GenericJNI frame and JIT-compiled JNI stub; the entrypoint may have
// changed since the frame was entered. The top quick frame tag indicates
// GenericJNI here, otherwise it's either AOT-compiled or JNI-compiled JNI stub.
- if (UNLIKELY(current_fragment->GetTopQuickFrameTag())) {
+ if (UNLIKELY(current_fragment->GetTopQuickFrameGenericJniTag())) {
// The generic JNI does not have any method header.
cur_oat_quick_method_header_ = nullptr;
+ } else if (UNLIKELY(current_fragment->GetTopQuickFrameJitJniTag())) {
+ // Should be JITed code.
+ Runtime* runtime = Runtime::Current();
+ const void* code = runtime->GetJit()->GetCodeCache()->GetJniStubCode(method);
+ CHECK(code != nullptr) << method->PrettyMethod();
+ cur_oat_quick_method_header_ = OatQuickMethodHeader::FromCodePointer(code);
} else {
+ // We are sure we are not running GenericJni here. Though the entry point could still be
+ // GenericJnistub. The entry point is usually JITed, AOT or instrumentation stub when
+ // instrumentation is enabled. It could be lso a resolution stub if the class isn't
+ // visibly initialized yet.
const void* existing_entry_point = method->GetEntryPointFromQuickCompiledCode();
CHECK(existing_entry_point != nullptr);
Runtime* runtime = Runtime::Current();
@@ -819,7 +858,11 @@
if (code != nullptr) {
cur_oat_quick_method_header_ = OatQuickMethodHeader::FromEntryPoint(code);
} else {
- // This must be a JITted JNI stub frame.
+ // This must be a JITted JNI stub frame. For non-debuggable runtimes we only generate
+ // JIT stubs if there are no AOT stubs for native methods. Since we checked for AOT
+ // code earlier, we must be running JITed code. For debuggable runtimes we might have
+ // JIT code even when AOT code is present but we tag SP in JITed JNI stubs
+ // in debuggable runtimes. This case is handled earlier.
CHECK(runtime->GetJit() != nullptr);
code = runtime->GetJit()->GetCodeCache()->GetJniStubCode(method);
CHECK(code != nullptr) << method->PrettyMethod();
@@ -834,8 +877,12 @@
cur_oat_quick_method_header_ = method->GetOatQuickMethodHeader(cur_quick_frame_pc_);
}
header_retrieved = false; // Force header retrieval in next iteration.
- ValidateFrame();
+ if (kDebugStackWalk) {
+ LOG(INFO) << "Early print: Tid=" << thread_-> GetThreadId() << ", method: "
+ << ArtMethod::PrettyMethod(method) << "@" << method;
+ }
+ ValidateFrame();
if ((walk_kind_ == StackWalkKind::kIncludeInlinedFrames)
&& (cur_oat_quick_method_header_ != nullptr)
&& cur_oat_quick_method_header_->IsOptimized()
@@ -854,7 +901,6 @@
return;
}
cur_depth_++;
- inlined_frames_count++;
}
}
}
@@ -908,7 +954,8 @@
cur_quick_frame_ = reinterpret_cast<ArtMethod**>(next_frame);
if (kDebugStackWalk) {
- LOG(INFO) << ArtMethod::PrettyMethod(method) << "@" << method << " size=" << frame_size
+ LOG(INFO) << "Tid=" << thread_-> GetThreadId() << ", method: "
+ << ArtMethod::PrettyMethod(method) << "@" << method << " size=" << frame_size
<< std::boolalpha
<< " optimized=" << (cur_oat_quick_method_header_ != nullptr &&
cur_oat_quick_method_header_->IsOptimized())
@@ -928,6 +975,12 @@
cur_oat_quick_method_header_ = nullptr;
} else if (cur_shadow_frame_ != nullptr) {
do {
+ if (kDebugStackWalk) {
+ ArtMethod* method = cur_shadow_frame_->GetMethod();
+ LOG(INFO) << "Tid=" << thread_-> GetThreadId() << ", method: "
+ << ArtMethod::PrettyMethod(method) << "@" << method
+ << ", ShadowFrame";
+ }
ValidateFrame();
bool should_continue = VisitFrame();
if (UNLIKELY(!should_continue)) {
diff --git a/runtime/stack.h b/runtime/stack.h
index 1b00b54..a4bcf17 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -58,11 +58,6 @@
};
std::ostream& operator<<(std::ostream& os, VRegKind rhs);
-// Size in bytes of the should_deoptimize flag on stack.
-// We just need 4 bytes for our purpose regardless of the architecture. Frame size
-// calculation will automatically do alignment for the final frame size.
-static constexpr size_t kShouldDeoptimizeFlagSize = 4;
-
/*
* Our current stack layout.
* The Dalvik registers come first, followed by the
@@ -197,6 +192,12 @@
uint32_t GetDexPc(bool abort_on_failure = true) const REQUIRES_SHARED(Locks::mutator_lock_);
+ // Returns a vector of the inlined dex pcs, in order from outermost to innermost but it replaces
+ // the innermost one with `handler_dex_pc`. In essence, (outermost dex pc, mid dex pc #1, ..., mid
+ // dex pc #n-1, `handler_dex_pc`).
+ std::vector<uint32_t> ComputeDexPcList(uint32_t handler_dex_pc) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
ObjPtr<mirror::Object> GetThisObject() const REQUIRES_SHARED(Locks::mutator_lock_);
size_t GetNativePcOffset() const REQUIRES_SHARED(Locks::mutator_lock_);
@@ -230,9 +231,8 @@
uint16_t vreg,
VRegKind kind,
uint32_t* val,
- std::optional<DexRegisterLocation> location =
- std::optional<DexRegisterLocation>()) const
- REQUIRES_SHARED(Locks::mutator_lock_);
+ std::optional<DexRegisterLocation> location = std::optional<DexRegisterLocation>(),
+ bool need_full_register_list = false) const REQUIRES_SHARED(Locks::mutator_lock_);
bool GetVRegPair(ArtMethod* m, uint16_t vreg, VRegKind kind_lo, VRegKind kind_hi,
uint64_t* val) const
@@ -268,10 +268,16 @@
return !current_inline_frames_.empty();
}
+ size_t InlineDepth() const { return current_inline_frames_.size(); }
+
InlineInfo GetCurrentInlinedFrame() const {
return current_inline_frames_.back();
}
+ const BitTableRange<InlineInfo>& GetCurrentInlinedFrames() const {
+ return current_inline_frames_;
+ }
+
uintptr_t GetCurrentQuickFramePc() const {
return cur_quick_frame_pc_;
}
@@ -302,10 +308,26 @@
*should_deoptimize_addr = *should_deoptimize_addr | static_cast<uint8_t>(value);
};
+ void UnsetShouldDeoptimizeFlag(DeoptimizeFlagValue value) REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint8_t* should_deoptimize_addr = GetShouldDeoptimizeFlagAddr();
+ *should_deoptimize_addr = *should_deoptimize_addr & ~static_cast<uint8_t>(value);
+ };
+
uint8_t GetShouldDeoptimizeFlag() const REQUIRES_SHARED(Locks::mutator_lock_) {
return *GetShouldDeoptimizeFlagAddr();
}
+ bool ShouldForceDeoptForRedefinition() const REQUIRES_SHARED(Locks::mutator_lock_) {
+ uint8_t should_deopt_flag = GetShouldDeoptimizeFlag();
+ return (should_deopt_flag &
+ static_cast<uint8_t>(DeoptimizeFlagValue::kForceDeoptForRedefinition)) != 0;
+ }
+
+ // Return the number of dex register in the map from the outermost frame to the number of inlined
+ // frames indicated by `depth`. If `depth` is 0, grab just the registers from the outermost level.
+ // If it is greater than 0, grab as many inline frames as `depth` indicates.
+ size_t GetNumberOfRegisters(CodeInfo* code_info, int depth) const;
+
private:
// Private constructor known in the case that num_frames_ has already been computed.
StackVisitor(Thread* thread,
@@ -334,7 +356,8 @@
bool GetVRegFromOptimizedCode(ArtMethod* m,
uint16_t vreg,
VRegKind kind,
- uint32_t* val) const
+ uint32_t* val,
+ bool need_full_register_list = false) const
REQUIRES_SHARED(Locks::mutator_lock_);
bool GetVRegPairFromDebuggerShadowFrame(uint16_t vreg,
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index 7a13dbd..514d30e 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -20,9 +20,12 @@
#include <limits>
#include "arch/instruction_set.h"
+#include "base/array_ref.h"
#include "base/bit_memory_region.h"
#include "base/bit_table.h"
#include "base/bit_utils.h"
+#include "base/globals.h"
+#include "base/logging.h"
#include "base/memory_region.h"
#include "dex/dex_file_types.h"
#include "dex_register_location.h"
@@ -360,15 +363,12 @@
return GetMethodInfoOf(inline_info).GetMethodIndex();
}
+ // Returns the dex registers for `stack_map`, ignoring any inlined dex registers.
ALWAYS_INLINE DexRegisterMap GetDexRegisterMapOf(StackMap stack_map) const {
- if (stack_map.HasDexRegisterMap()) {
- DexRegisterMap map(number_of_dex_registers_, DexRegisterLocation::Invalid());
- DecodeDexRegisterMap(stack_map.Row(), /* first_dex_register= */ 0, &map);
- return map;
- }
- return DexRegisterMap(0, DexRegisterLocation::None());
+ return GetDexRegisterMapOf(stack_map, /* first= */ 0, number_of_dex_registers_);
}
+ // Returns the dex register map of `inline_info`, and just those registers.
ALWAYS_INLINE DexRegisterMap GetInlineDexRegisterMapOf(StackMap stack_map,
InlineInfo inline_info) const {
if (stack_map.HasDexRegisterMap()) {
@@ -381,6 +381,17 @@
? number_of_dex_registers_
: inline_infos_.GetRow(inline_info.Row() - 1).GetNumberOfDexRegisters();
uint32_t last = inline_info.GetNumberOfDexRegisters();
+ return GetDexRegisterMapOf(stack_map, first, last);
+ }
+ return DexRegisterMap(0, DexRegisterLocation::None());
+ }
+
+ // Returns the dex register map of `stack_map` in the range the range [first, last).
+ ALWAYS_INLINE DexRegisterMap GetDexRegisterMapOf(StackMap stack_map,
+ uint32_t first,
+ uint32_t last) const {
+ if (stack_map.HasDexRegisterMap()) {
+ DCHECK_LE(first, last);
DexRegisterMap map(last - first, DexRegisterLocation::Invalid());
DecodeDexRegisterMap(stack_map.Row(), first, &map);
return map;
@@ -409,12 +420,39 @@
return stack_maps_.GetInvalidRow();
}
- // Searches the stack map list backwards because catch stack maps are stored at the end.
- StackMap GetCatchStackMapForDexPc(uint32_t dex_pc) const {
+ StackMap GetCatchStackMapForDexPc(ArrayRef<const uint32_t> dex_pcs) const {
+ // Searches the stack map list backwards because catch stack maps are stored at the end.
for (size_t i = GetNumberOfStackMaps(); i > 0; --i) {
StackMap stack_map = GetStackMapAt(i - 1);
- if (stack_map.GetDexPc() == dex_pc && stack_map.GetKind() == StackMap::Kind::Catch) {
- return stack_map;
+ if (UNLIKELY(stack_map.GetKind() != StackMap::Kind::Catch)) {
+ // Early break since we should have catch stack maps only at the end.
+ if (kIsDebugBuild) {
+ for (size_t j = i - 1; j > 0; --j) {
+ DCHECK(GetStackMapAt(j - 1).GetKind() != StackMap::Kind::Catch);
+ }
+ }
+ break;
+ }
+
+ // Both the handler dex_pc and all of the inline dex_pcs have to match i.e. we want dex_pcs to
+ // be [stack_map_dex_pc, inline_dex_pc_1, ..., inline_dex_pc_n].
+ if (stack_map.GetDexPc() != dex_pcs.front()) {
+ continue;
+ }
+
+ const BitTableRange<InlineInfo>& inline_infos = GetInlineInfosOf(stack_map);
+ if (inline_infos.size() == dex_pcs.size() - 1) {
+ bool matching_dex_pcs = true;
+ for (size_t inline_info_index = 0; inline_info_index < inline_infos.size();
+ ++inline_info_index) {
+ if (inline_infos[inline_info_index].GetDexPc() != dex_pcs[inline_info_index + 1]) {
+ matching_dex_pcs = false;
+ break;
+ }
+ }
+ if (matching_dex_pcs) {
+ return stack_map;
+ }
}
}
return stack_maps_.GetInvalidRow();
@@ -449,6 +487,14 @@
return (*code_info_data & kIsBaseline) != 0;
}
+ ALWAYS_INLINE static bool IsDebuggable(const uint8_t* code_info_data) {
+ return (*code_info_data & kIsDebuggable) != 0;
+ }
+
+ uint32_t GetNumberOfDexRegisters() {
+ return number_of_dex_registers_;
+ }
+
private:
// Scan backward to determine dex register locations at given stack map.
void DecodeDexRegisterMap(uint32_t stack_map_index,
@@ -495,11 +541,18 @@
enum Flags {
kHasInlineInfo = 1 << 0,
kIsBaseline = 1 << 1,
+ kIsDebuggable = 1 << 2,
};
// The CodeInfo starts with sequence of variable-length bit-encoded integers.
+ // (Please see kVarintMax for more details about encoding).
static constexpr size_t kNumHeaders = 7;
- uint32_t flags_ = 0; // Note that the space is limited to three bits.
+ // Note that the space for flags is limited to three bits. We use a custom encoding where we
+ // encode the value inline if it is less than kVarintMax. We want to access flags without
+ // decoding the entire CodeInfo header so the value of flags cannot be more than kVarintMax.
+ // See IsDebuggable / IsBaseline / HasInlineInfo on how we access flags_ without decoding the
+ // header.
+ uint32_t flags_ = 0;
uint32_t code_size_ = 0; // The size of native PC range in bytes.
uint32_t packed_frame_size_ = 0; // Frame size in kStackAlignment units.
uint32_t core_spill_mask_ = 0;
diff --git a/runtime/string_builder_append.cc b/runtime/string_builder_append.cc
index 85b70eb..0083b91 100644
--- a/runtime/string_builder_append.cc
+++ b/runtime/string_builder_append.cc
@@ -20,9 +20,11 @@
#include "base/logging.h"
#include "common_throws.h"
#include "gc/heap.h"
+#include "mirror/array-inl.h"
#include "mirror/string-alloc-inl.h"
#include "obj_ptr-inl.h"
#include "runtime.h"
+#include "well_known_classes.h"
namespace art {
@@ -60,6 +62,11 @@
return new_string->GetLength() - (data - new_string->GetValue());
}
+ template <typename CharType>
+ CharType* AppendFpArg(ObjPtr<mirror::String> new_string,
+ CharType* data,
+ size_t fp_arg_index) const REQUIRES_SHARED(Locks::mutator_lock_);
+
template <typename CharType, size_t size>
static CharType* AppendLiteral(ObjPtr<mirror::String> new_string,
CharType* data,
@@ -75,6 +82,8 @@
CharType* data,
int64_t value) REQUIRES_SHARED(Locks::mutator_lock_);
+ int32_t ConvertFpArgs() REQUIRES_SHARED(Locks::mutator_lock_);
+
template <typename CharType>
void StoreData(ObjPtr<mirror::String> new_string, CharType* data) const
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -93,6 +102,15 @@
// References are moved to the handle scope during CalculateLengthWithFlag().
StackHandleScope<kMaxArgs> hs_;
+ // We convert float/double values using jdk.internal.math.FloatingDecimal which uses
+ // a thread-local converter under the hood. As we may have more than one
+ // float/double argument, we need to copy the data out of the converter.
+ // Maximum number of characters is 26. See BinaryToASCIIBuffer.buffer in FloatingDecimal.java .
+ // (This is more than enough for the `ExceptionalBinaryToASCIIBuffer` cases.)
+ static constexpr size_t kBinaryToASCIIBufferSize = 26;
+ uint8_t converted_fp_args_[kMaxArgs][kBinaryToASCIIBufferSize];
+ int32_t converted_fp_arg_lengths_[kMaxArgs];
+
// The length and flag to store when the AppendBuilder is used as a pre-fence visitor.
int32_t length_with_flag_ = 0u;
};
@@ -142,6 +160,18 @@
return log10_value_estimate + adjustment;
}
+template <typename CharType>
+inline CharType* StringBuilderAppend::Builder::AppendFpArg(ObjPtr<mirror::String> new_string,
+ CharType* data,
+ size_t fp_arg_index) const {
+ DCHECK_LE(fp_arg_index, std::size(converted_fp_args_));
+ const uint8_t* src = converted_fp_args_[fp_arg_index];
+ size_t length = converted_fp_arg_lengths_[fp_arg_index];
+ DCHECK_LE(length, kBinaryToASCIIBufferSize);
+ DCHECK_LE(length, RemainingSpace(new_string, data));
+ return std::copy_n(src, length, data);
+}
+
template <typename CharType, size_t size>
inline CharType* StringBuilderAppend::Builder::AppendLiteral(ObjPtr<mirror::String> new_string,
CharType* data,
@@ -204,10 +234,111 @@
return data + length;
}
+int32_t StringBuilderAppend::Builder::ConvertFpArgs() {
+ int32_t fp_args_length = 0u;
+ const uint32_t* current_arg = args_;
+ size_t fp_arg_index = 0u;
+ for (uint32_t f = format_; f != 0u; f >>= kBitsPerArg) {
+ DCHECK_LE(f & kArgMask, static_cast<uint32_t>(Argument::kLast));
+ bool fp_arg = false;
+ ObjPtr<mirror::Object> converter;
+ switch (static_cast<Argument>(f & kArgMask)) {
+ case Argument::kString:
+ case Argument::kBoolean:
+ case Argument::kChar:
+ case Argument::kInt:
+ break;
+ case Argument::kLong: {
+ current_arg = AlignUp(current_arg, sizeof(int64_t));
+ ++current_arg; // Skip the low word, let the common code skip the high word.
+ break;
+ }
+ case Argument::kFloat: {
+ fp_arg = true;
+ float arg = bit_cast<float>(*current_arg);
+ converter = WellKnownClasses::jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_F
+ ->InvokeStatic<'L', 'F'>(hs_.Self(), arg);
+ break;
+ }
+ case Argument::kDouble: {
+ fp_arg = true;
+ current_arg = AlignUp(current_arg, sizeof(int64_t));
+ double arg = bit_cast<double>(
+ static_cast<uint64_t>(current_arg[0]) + (static_cast<uint64_t>(current_arg[1]) << 32));
+ converter = WellKnownClasses::jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_D
+ ->InvokeStatic<'L', 'D'>(hs_.Self(), arg);
+ ++current_arg; // Skip the low word, let the common code skip the high word.
+ break;
+ }
+ case Argument::kStringBuilder:
+ case Argument::kCharArray:
+ case Argument::kObject:
+ LOG(FATAL) << "Unimplemented arg format: 0x" << std::hex
+ << (f & kArgMask) << " full format: 0x" << std::hex << format_;
+ UNREACHABLE();
+ default:
+ LOG(FATAL) << "Unexpected arg format: 0x" << std::hex
+ << (f & kArgMask) << " full format: 0x" << std::hex << format_;
+ UNREACHABLE();
+ }
+ if (fp_arg) {
+ // If we see an exception (presumably OOME or SOE), keep it as is, even
+ // though it may be confusing to see the stack trace for FP argument
+ // conversion continue at the StringBuilder.toString() invoke location.
+ DCHECK_EQ(converter == nullptr, hs_.Self()->IsExceptionPending());
+ if (UNLIKELY(converter == nullptr)) {
+ return -1;
+ }
+ ArtField* btab_buffer_field =
+ WellKnownClasses::jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_buffer;
+ int32_t length;
+ if (converter->GetClass() == btab_buffer_field->GetDeclaringClass()) {
+ // Call `converter.getChars(converter.buffer)`.
+ StackHandleScope<1u> hs2(hs_.Self());
+ Handle<mirror::CharArray> buffer =
+ hs2.NewHandle(btab_buffer_field->GetObj<mirror::CharArray>(converter));
+ DCHECK(buffer != nullptr);
+ length = WellKnownClasses::jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_getChars
+ ->InvokeInstance<'I', 'L'>(hs_.Self(), converter, buffer.Get());
+ if (UNLIKELY(hs_.Self()->IsExceptionPending())) {
+ return -1;
+ }
+ // The converted string is now at the front of the buffer.
+ DCHECK_GT(length, 0);
+ DCHECK_LE(length, buffer->GetLength());
+ DCHECK_LE(static_cast<size_t>(length), std::size(converted_fp_args_[0]));
+ DCHECK(mirror::String::AllASCII(buffer->GetData(), length));
+ std::copy_n(buffer->GetData(), length, converted_fp_args_[fp_arg_index]);
+ } else {
+ ArtField* ebtab_image_field = WellKnownClasses::
+ jdk_internal_math_FloatingDecimal_ExceptionalBinaryToASCIIBuffer_image;
+ DCHECK(converter->GetClass() == ebtab_image_field->GetDeclaringClass());
+ ObjPtr<mirror::String> converted = ebtab_image_field->GetObj<mirror::String>(converter);
+ DCHECK(converted != nullptr);
+ length = converted->GetLength();
+ if (mirror::kUseStringCompression) {
+ DCHECK(converted->IsCompressed());
+ memcpy(converted_fp_args_[fp_arg_index], converted->GetValueCompressed(), length);
+ } else {
+ DCHECK(mirror::String::AllASCII(converted->GetValue(), length));
+ std::copy_n(converted->GetValue(), length, converted_fp_args_[fp_arg_index]);
+ }
+ }
+ converted_fp_arg_lengths_[fp_arg_index] = length;
+ fp_args_length += length;
+ ++fp_arg_index;
+ }
+ ++current_arg;
+ DCHECK_LE(fp_arg_index, kMaxArgs);
+ }
+ return fp_args_length;
+}
+
inline int32_t StringBuilderAppend::Builder::CalculateLengthWithFlag() {
static_assert(static_cast<size_t>(Argument::kEnd) == 0u, "kEnd must be 0.");
bool compressible = mirror::kUseStringCompression;
uint64_t length = 0u;
+ bool has_fp_args = false;
const uint32_t* current_arg = args_;
for (uint32_t f = format_; f != 0u; f >>= kBitsPerArg) {
DCHECK_LE(f & kArgMask, static_cast<uint32_t>(Argument::kLast));
@@ -243,12 +374,19 @@
++current_arg; // Skip the low word, let the common code skip the high word.
break;
}
+ case Argument::kDouble:
+ current_arg = AlignUp(current_arg, sizeof(int64_t));
+ ++current_arg; // Skip the low word, let the common code skip the high word.
+ FALLTHROUGH_INTENDED;
+ case Argument::kFloat:
+ // Conversion shall be performed in a separate pass because it calls back to
+ // managed code and we need to convert reference arguments to `Handle<>`s first.
+ has_fp_args = true;
+ break;
case Argument::kStringBuilder:
case Argument::kCharArray:
case Argument::kObject:
- case Argument::kFloat:
- case Argument::kDouble:
LOG(FATAL) << "Unimplemented arg format: 0x" << std::hex
<< (f & kArgMask) << " full format: 0x" << std::hex << format_;
UNREACHABLE();
@@ -261,6 +399,16 @@
DCHECK_LE(hs_.NumberOfReferences(), kMaxArgs);
}
+ if (UNLIKELY(has_fp_args)) {
+ // Call Java helpers to convert FP args.
+ int32_t fp_args_length = ConvertFpArgs();
+ if (fp_args_length == -1) {
+ return -1;
+ }
+ DCHECK_GT(fp_args_length, 0);
+ length += fp_args_length;
+ }
+
if (length > std::numeric_limits<int32_t>::max()) {
// We cannot allocate memory for the entire result.
hs_.Self()->ThrowNewException("Ljava/lang/OutOfMemoryError;",
@@ -276,6 +424,7 @@
inline void StringBuilderAppend::Builder::StoreData(ObjPtr<mirror::String> new_string,
CharType* data) const {
size_t handle_index = 0u;
+ size_t fp_arg_index = 0u;
const uint32_t* current_arg = args_;
for (uint32_t f = format_; f != 0u; f >>= kBitsPerArg) {
DCHECK_LE(f & kArgMask, static_cast<uint32_t>(Argument::kLast));
@@ -315,11 +464,18 @@
++current_arg; // Skip the low word, let the common code skip the high word.
break;
}
+ case Argument::kDouble:
+ current_arg = AlignUp(current_arg, sizeof(int64_t));
+ ++current_arg; // Skip the low word, let the common code skip the high word.
+ FALLTHROUGH_INTENDED;
+ case Argument::kFloat: {
+ data = AppendFpArg(new_string, data, fp_arg_index);
+ ++fp_arg_index;
+ break;
+ }
case Argument::kStringBuilder:
case Argument::kCharArray:
- case Argument::kFloat:
- case Argument::kDouble:
LOG(FATAL) << "Unimplemented arg format: 0x" << std::hex
<< (f & kArgMask) << " full format: 0x" << std::hex << format_;
UNREACHABLE();
@@ -330,6 +486,7 @@
}
++current_arg;
DCHECK_LE(handle_index, hs_.NumberOfReferences());
+ DCHECK_LE(fp_arg_index, std::size(converted_fp_args_));
}
DCHECK_EQ(RemainingSpace(new_string, data), 0u) << std::hex << format_;
}
diff --git a/runtime/subtype_check.h b/runtime/subtype_check.h
index ca1feb5..90b9b57 100644
--- a/runtime/subtype_check.h
+++ b/runtime/subtype_check.h
@@ -382,12 +382,12 @@
if (UNLIKELY(!klass->HasSuperClass())) {
// Object root always goes directly from Uninitialized -> Assigned.
- const SubtypeCheckInfo root_sci = GetSubtypeCheckInfo(klass);
+ SubtypeCheckInfo root_sci = GetSubtypeCheckInfo(klass);
if (root_sci.GetState() != SubtypeCheckInfo::kUninitialized) {
return root_sci; // No change needed.
}
- const SubtypeCheckInfo new_root_sci = root_sci.CreateRoot();
+ SubtypeCheckInfo new_root_sci = root_sci.CreateRoot();
SetSubtypeCheckInfo(klass, new_root_sci);
// The object root is always in the Uninitialized|Assigned state.
@@ -571,9 +571,7 @@
DCHECK_EQ(depth, klass->Depth());
SubtypeCheckBitsAndStatus current_bits_and_status = ReadField(klass);
- const SubtypeCheckInfo current =
- SubtypeCheckInfo::Create(current_bits_and_status.subtype_check_info_, depth);
- return current;
+ return SubtypeCheckInfo::Create(current_bits_and_status.subtype_check_info_, depth);
}
static void SetSubtypeCheckInfo(ClassPtr klass, const SubtypeCheckInfo& new_sci)
diff --git a/runtime/subtype_check_info.h b/runtime/subtype_check_info.h
index d734557..eef68d7 100644
--- a/runtime/subtype_check_info.h
+++ b/runtime/subtype_check_info.h
@@ -153,7 +153,7 @@
// Create from the depth and the bitstring+of state.
// This is done for convenience to avoid passing in "depth" everywhere,
// since our current state is almost always a function of depth.
- static SubtypeCheckInfo Create(SubtypeCheckBits compressed_value, size_t depth) {
+ static SubtypeCheckInfo Create(const SubtypeCheckBits& compressed_value, size_t depth) {
SubtypeCheckInfo io;
io.depth_ = depth;
io.bitstring_and_of_ = compressed_value;
@@ -168,9 +168,8 @@
//
// Normally, return kSubtypeOf or kNotSubtypeOf.
Result IsSubtypeOf(const SubtypeCheckInfo& target) {
- if (target.GetState() != SubtypeCheckInfo::kAssigned) {
- return Result::kUnknownSubtypeOf;
- } else if (GetState() == SubtypeCheckInfo::kUninitialized) {
+ if (target.GetState() != SubtypeCheckInfo::kAssigned ||
+ GetState() == SubtypeCheckInfo::kUninitialized) {
return Result::kUnknownSubtypeOf;
}
diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h
index 324cd37..a99977f 100644
--- a/runtime/thread-inl.h
+++ b/runtime/thread-inl.h
@@ -34,7 +34,7 @@
namespace art {
// Quickly access the current thread from a JNIEnv.
-static inline Thread* ThreadForEnv(JNIEnv* env) {
+inline Thread* Thread::ForEnv(JNIEnv* env) {
JNIEnvExt* full_env(down_cast<JNIEnvExt*>(env));
return full_env->GetSelf();
}
@@ -373,7 +373,7 @@
}
inline bool Thread::GetWeakRefAccessEnabled() const {
- CHECK(kUseReadBarrier);
+ DCHECK(gUseReadBarrier);
DCHECK(this == Thread::Current());
WeakRefAccessState s = tls32_.weak_ref_access_enabled.load(std::memory_order_relaxed);
if (LIKELY(s == WeakRefAccessState::kVisiblyEnabled)) {
@@ -428,7 +428,7 @@
int delta,
AtomicInteger* suspend_barrier,
SuspendReason reason) {
- if (delta > 0 && ((kUseReadBarrier && this != self) || suspend_barrier != nullptr)) {
+ if (delta > 0 && ((gUseReadBarrier && this != self) || suspend_barrier != nullptr)) {
// When delta > 0 (requesting a suspend), ModifySuspendCountInternal() may fail either if
// active_suspend_barriers is full or we are in the middle of a thread flip. Retry in a loop.
while (true) {
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 78ba26d..dc5cd3c 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -41,6 +41,8 @@
#include "android-base/stringprintf.h"
#include "android-base/strings.h"
+#include "unwindstack/AndroidUnwinder.h"
+
#include "arch/context-inl.h"
#include "arch/context.h"
#include "art_field-inl.h"
@@ -84,6 +86,7 @@
#include "mirror/class_loader.h"
#include "mirror/object_array-alloc-inl.h"
#include "mirror/object_array-inl.h"
+#include "mirror/stack_frame_info.h"
#include "mirror/stack_trace_element.h"
#include "monitor.h"
#include "monitor_objects_stack_visitor.h"
@@ -110,9 +113,10 @@
#include "stack_map.h"
#include "thread-inl.h"
#include "thread_list.h"
+#include "trace.h"
#include "verifier/method_verifier.h"
#include "verify_object.h"
-#include "well_known_classes.h"
+#include "well_known_classes-inl.h"
#if ART_USE_FUTEXES
#include "linux/futex.h"
@@ -130,7 +134,7 @@
using android::base::StringAppendV;
using android::base::StringPrintf;
-extern "C" NO_RETURN void artDeoptimize(Thread* self);
+extern "C" NO_RETURN void artDeoptimize(Thread* self, bool skip_method_exit_callbacks);
bool Thread::is_started_ = false;
pthread_key_t Thread::pthread_key_self_;
@@ -166,7 +170,7 @@
void UpdateReadBarrierEntrypoints(QuickEntryPoints* qpoints, bool is_active);
void Thread::SetIsGcMarkingAndUpdateEntrypoints(bool is_marking) {
- CHECK(kUseReadBarrier);
+ CHECK(gUseReadBarrier);
tls32_.is_gc_marking = is_marking;
UpdateReadBarrierEntrypoints(&tlsPtr_.quick_entrypoints, /* is_active= */ is_marking);
}
@@ -272,6 +276,7 @@
ObjPtr<mirror::Throwable> exception,
bool from_code,
DeoptimizationMethodType method_type) {
+ DCHECK(exception != Thread::GetDeoptimizationException());
DeoptimizationContextRecord* record = new DeoptimizationContextRecord(
return_value,
is_reference,
@@ -433,15 +438,18 @@
tlsPtr_.stacked_shadow_frame_record = record;
}
-ShadowFrame* Thread::PopStackedShadowFrame(StackedShadowFrameType type, bool must_be_present) {
+ShadowFrame* Thread::MaybePopDeoptimizedStackedShadowFrame() {
StackedShadowFrameRecord* record = tlsPtr_.stacked_shadow_frame_record;
- if (must_be_present) {
- DCHECK(record != nullptr);
- } else {
- if (record == nullptr || record->GetType() != type) {
- return nullptr;
- }
+ if (record == nullptr ||
+ record->GetType() != StackedShadowFrameType::kDeoptimizationShadowFrame) {
+ return nullptr;
}
+ return PopStackedShadowFrame();
+}
+
+ShadowFrame* Thread::PopStackedShadowFrame() {
+ StackedShadowFrameRecord* record = tlsPtr_.stacked_shadow_frame_record;
+ DCHECK_NE(record, nullptr);
tlsPtr_.stacked_shadow_frame_record = record->GetLink();
ShadowFrame* shadow_frame = record->GetShadowFrame();
delete record;
@@ -531,7 +539,7 @@
return shadow_frame;
}
VLOG(deopt) << "Create pre-deopted ShadowFrame for " << ArtMethod::PrettyMethod(method);
- shadow_frame = ShadowFrame::CreateDeoptimizedFrame(num_vregs, nullptr, method, dex_pc);
+ shadow_frame = ShadowFrame::CreateDeoptimizedFrame(num_vregs, method, dex_pc);
FrameIdToShadowFrame* record = FrameIdToShadowFrame::Create(frame_id,
shadow_frame,
tlsPtr_.frame_id_to_shadow_frame,
@@ -601,6 +609,10 @@
env->DeleteGlobalRef(old_jpeer);
}
+void* Thread::CreateCallbackWithUffdGc(void* arg) {
+ return Thread::CreateCallback(arg);
+}
+
void* Thread::CreateCallback(void* arg) {
Thread* self = reinterpret_cast<Thread*>(arg);
Runtime* runtime = Runtime::Current();
@@ -634,7 +646,7 @@
self->DeleteJPeer(self->GetJniEnv());
self->SetThreadName(self->GetThreadName()->ToModifiedUtf8().c_str());
- ArtField* priorityField = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_priority);
+ ArtField* priorityField = WellKnownClasses::java_lang_Thread_priority;
self->SetNativePriority(priorityField->GetInt(self->tlsPtr_.opeer));
runtime->GetRuntimeCallbacks()->ThreadStart(self);
@@ -642,8 +654,7 @@
// Unpark ourselves if the java peer was unparked before it started (see
// b/28845097#comment49 for more information)
- ArtField* unparkedField = jni::DecodeArtField(
- WellKnownClasses::java_lang_Thread_unparkedBeforeStart);
+ ArtField* unparkedField = WellKnownClasses::java_lang_Thread_unparkedBeforeStart;
bool should_unpark = false;
{
// Hold the lock here, so that if another thread calls unpark before the thread starts
@@ -657,19 +668,17 @@
}
// Invoke the 'run' method of our java.lang.Thread.
ObjPtr<mirror::Object> receiver = self->tlsPtr_.opeer;
- jmethodID mid = WellKnownClasses::java_lang_Thread_run;
- ScopedLocalRef<jobject> ref(soa.Env(), soa.AddLocalReference<jobject>(receiver));
- InvokeVirtualOrInterfaceWithJValues(soa, ref.get(), mid, nullptr);
+ WellKnownClasses::java_lang_Thread_run->InvokeVirtual<'V'>(self, receiver);
}
// Detach and delete self.
- Runtime::Current()->GetThreadList()->Unregister(self);
+ Runtime::Current()->GetThreadList()->Unregister(self, /* should_run_callbacks= */ true);
return nullptr;
}
Thread* Thread::FromManagedThread(const ScopedObjectAccessAlreadyRunnable& soa,
ObjPtr<mirror::Object> thread_peer) {
- ArtField* f = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_nativePeer);
+ ArtField* f = WellKnownClasses::java_lang_Thread_nativePeer;
Thread* result = reinterpret_cast64<Thread*>(f->GetLong(thread_peer));
// Check that if we have a result it is either suspended or we hold the thread_list_lock_
// to stop it from going away.
@@ -786,7 +795,7 @@
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wframe-larger-than="
NO_INLINE
- static void Touch(uintptr_t target) {
+ __attribute__((no_sanitize("memtag"))) static void Touch(uintptr_t target) {
volatile size_t zero = 0;
// Use a large local volatile array to ensure a large frame size. Do not use anything close
// to a full page for ASAN. It would be nice to ensure the frame size is at most a page, but
@@ -827,6 +836,22 @@
madvise(pregion, unwanted_size, MADV_DONTNEED);
}
+template <bool kSupportTransaction>
+static void SetNativePeer(ObjPtr<mirror::Object> java_peer, Thread* thread)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ArtField* field = WellKnownClasses::java_lang_Thread_nativePeer;
+ if (kSupportTransaction && Runtime::Current()->IsActiveTransaction()) {
+ field->SetLong</*kTransactionActive=*/ true>(java_peer, reinterpret_cast<jlong>(thread));
+ } else {
+ field->SetLong</*kTransactionActive=*/ false>(java_peer, reinterpret_cast<jlong>(thread));
+ }
+}
+
+static void SetNativePeer(JNIEnv* env, jobject java_peer, Thread* thread) {
+ ScopedObjectAccess soa(env);
+ SetNativePeer</*kSupportTransaction=*/ false>(soa.Decode<mirror::Object>(java_peer), thread);
+}
+
void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) {
CHECK(java_peer != nullptr);
Thread* self = static_cast<JNIEnvExt*>(env)->GetSelf();
@@ -834,7 +859,7 @@
if (VLOG_IS_ON(threads)) {
ScopedObjectAccess soa(env);
- ArtField* f = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_name);
+ ArtField* f = WellKnownClasses::java_lang_Thread_name;
ObjPtr<mirror::String> java_name =
f->GetObject(soa.Decode<mirror::Object>(java_peer))->AsString();
std::string thread_name;
@@ -873,8 +898,7 @@
// Thread.start is synchronized, so we know that nativePeer is 0, and know that we're not racing
// to assign it.
- env->SetLongField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer,
- reinterpret_cast<jlong>(child_thread));
+ SetNativePeer(env, java_peer, child_thread);
// Try to allocate a JNIEnvExt for the thread. We do this here as we might be out of memory and
// do not have a good way to report this on the child's side.
@@ -893,7 +917,8 @@
CHECK_PTHREAD_CALL(pthread_attr_setstacksize, (&attr, stack_size), stack_size);
pthread_create_result = pthread_create(&new_pthread,
&attr,
- Thread::CreateCallback,
+ gUseUserfaultfd ? Thread::CreateCallbackWithUffdGc
+ : Thread::CreateCallback,
child_thread);
CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attr), "new thread");
@@ -918,7 +943,7 @@
delete child_thread;
child_thread = nullptr;
// TODO: remove from thread group?
- env->SetLongField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer, 0);
+ SetNativePeer(env, java_peer, nullptr);
{
std::string msg(child_jni_env_ext.get() == nullptr ?
StringPrintf("Could not allocate JNI Env: %s", error_msg.c_str()) :
@@ -982,7 +1007,10 @@
}
template <typename PeerAction>
-Thread* Thread::Attach(const char* thread_name, bool as_daemon, PeerAction peer_action) {
+Thread* Thread::Attach(const char* thread_name,
+ bool as_daemon,
+ PeerAction peer_action,
+ bool should_run_callbacks) {
Runtime* runtime = Runtime::Current();
ScopedTrace trace("Thread::Attach");
if (runtime == nullptr) {
@@ -1017,7 +1045,7 @@
// Run the action that is acting on the peer.
if (!peer_action(self)) {
- runtime->GetThreadList()->Unregister(self);
+ runtime->GetThreadList()->Unregister(self, should_run_callbacks);
// Unregister deletes self, no need to do this here.
return nullptr;
}
@@ -1032,7 +1060,7 @@
self->Dump(LOG_STREAM(INFO));
}
- {
+ if (should_run_callbacks) {
ScopedObjectAccess soa(self);
runtime->GetRuntimeCallbacks()->ThreadStart(self);
}
@@ -1043,7 +1071,8 @@
Thread* Thread::Attach(const char* thread_name,
bool as_daemon,
jobject thread_group,
- bool create_peer) {
+ bool create_peer,
+ bool should_run_callbacks) {
auto create_peer_action = [&](Thread* self) {
// If we're the main thread, ClassLinker won't be created until after we're attached,
// so that thread needs a two-stage attach. Regular threads don't need this hack.
@@ -1076,67 +1105,58 @@
}
return true;
};
- return Attach(thread_name, as_daemon, create_peer_action);
+ return Attach(thread_name, as_daemon, create_peer_action, should_run_callbacks);
}
Thread* Thread::Attach(const char* thread_name, bool as_daemon, jobject thread_peer) {
auto set_peer_action = [&](Thread* self) {
// Install the given peer.
- {
- DCHECK(self == Thread::Current());
- ScopedObjectAccess soa(self);
- self->tlsPtr_.opeer = soa.Decode<mirror::Object>(thread_peer).Ptr();
- }
- self->GetJniEnv()->SetLongField(thread_peer,
- WellKnownClasses::java_lang_Thread_nativePeer,
- reinterpret_cast64<jlong>(self));
+ DCHECK(self == Thread::Current());
+ ScopedObjectAccess soa(self);
+ ObjPtr<mirror::Object> peer = soa.Decode<mirror::Object>(thread_peer);
+ self->tlsPtr_.opeer = peer.Ptr();
+ SetNativePeer</*kSupportTransaction=*/ false>(peer, self);
return true;
};
- return Attach(thread_name, as_daemon, set_peer_action);
+ return Attach(thread_name, as_daemon, set_peer_action, /* should_run_callbacks= */ true);
}
void Thread::CreatePeer(const char* name, bool as_daemon, jobject thread_group) {
Runtime* runtime = Runtime::Current();
CHECK(runtime->IsStarted());
- JNIEnv* env = tlsPtr_.jni_env;
+ Thread* self = this;
+ DCHECK_EQ(self, Thread::Current());
- if (thread_group == nullptr) {
- thread_group = runtime->GetMainThreadGroup();
- }
- ScopedLocalRef<jobject> thread_name(env, env->NewStringUTF(name));
+ ScopedObjectAccess soa(self);
+ StackHandleScope<4u> hs(self);
+ DCHECK(WellKnownClasses::java_lang_ThreadGroup->IsInitialized());
+ Handle<mirror::Object> thr_group = hs.NewHandle(soa.Decode<mirror::Object>(
+ thread_group != nullptr ? thread_group : runtime->GetMainThreadGroup()));
+ Handle<mirror::String> thread_name = hs.NewHandle(
+ name != nullptr ? mirror::String::AllocFromModifiedUtf8(self, name) : nullptr);
// Add missing null check in case of OOM b/18297817
- if (name != nullptr && thread_name.get() == nullptr) {
- CHECK(IsExceptionPending());
+ if (name != nullptr && UNLIKELY(thread_name == nullptr)) {
+ CHECK(self->IsExceptionPending());
return;
}
jint thread_priority = GetNativePriority();
- jboolean thread_is_daemon = as_daemon;
- ScopedLocalRef<jobject> peer(env, env->AllocObject(WellKnownClasses::java_lang_Thread));
- if (peer.get() == nullptr) {
+ DCHECK(WellKnownClasses::java_lang_Thread->IsInitialized());
+ Handle<mirror::Object> peer =
+ hs.NewHandle(WellKnownClasses::java_lang_Thread->AllocObject(self));
+ if (UNLIKELY(peer == nullptr)) {
CHECK(IsExceptionPending());
return;
}
- {
- ScopedObjectAccess soa(this);
- tlsPtr_.opeer = soa.Decode<mirror::Object>(peer.get()).Ptr();
- }
- env->CallNonvirtualVoidMethod(peer.get(),
- WellKnownClasses::java_lang_Thread,
- WellKnownClasses::java_lang_Thread_init,
- thread_group, thread_name.get(), thread_priority, thread_is_daemon);
- if (IsExceptionPending()) {
+ tlsPtr_.opeer = peer.Get();
+ WellKnownClasses::java_lang_Thread_init->InvokeInstance<'V', 'L', 'L', 'I', 'Z'>(
+ self, peer.Get(), thr_group.Get(), thread_name.Get(), thread_priority, as_daemon);
+ if (self->IsExceptionPending()) {
return;
}
- Thread* self = this;
- DCHECK_EQ(self, Thread::Current());
- env->SetLongField(peer.get(),
- WellKnownClasses::java_lang_Thread_nativePeer,
- reinterpret_cast64<jlong>(self));
+ SetNativePeer</*kSupportTransaction=*/ false>(peer.Get(), self);
- ScopedObjectAccess soa(self);
- StackHandleScope<1> hs(self);
MutableHandle<mirror::String> peer_thread_name(hs.NewHandle(GetThreadName()));
if (peer_thread_name == nullptr) {
// The Thread constructor should have set the Thread.name to a
@@ -1144,18 +1164,16 @@
// available (in the compiler, in tests), we manually assign the
// fields the constructor should have set.
if (runtime->IsActiveTransaction()) {
- InitPeer<true>(soa,
- tlsPtr_.opeer,
- thread_is_daemon,
- thread_group,
- thread_name.get(),
+ InitPeer<true>(tlsPtr_.opeer,
+ as_daemon,
+ thr_group.Get(),
+ thread_name.Get(),
thread_priority);
} else {
- InitPeer<false>(soa,
- tlsPtr_.opeer,
- thread_is_daemon,
- thread_group,
- thread_name.get(),
+ InitPeer<false>(tlsPtr_.opeer,
+ as_daemon,
+ thr_group.Get(),
+ thread_name.Get(),
thread_priority);
}
peer_thread_name.Assign(GetThreadName());
@@ -1166,27 +1184,32 @@
}
}
-jobject Thread::CreateCompileTimePeer(JNIEnv* env,
- const char* name,
- bool as_daemon,
- jobject thread_group) {
+ObjPtr<mirror::Object> Thread::CreateCompileTimePeer(const char* name,
+ bool as_daemon,
+ jobject thread_group) {
Runtime* runtime = Runtime::Current();
CHECK(!runtime->IsStarted());
+ Thread* self = this;
+ DCHECK_EQ(self, Thread::Current());
- if (thread_group == nullptr) {
- thread_group = runtime->GetMainThreadGroup();
- }
- ScopedLocalRef<jobject> thread_name(env, env->NewStringUTF(name));
+ ScopedObjectAccessUnchecked soa(self);
+ StackHandleScope<3u> hs(self);
+ DCHECK(WellKnownClasses::java_lang_ThreadGroup->IsInitialized());
+ Handle<mirror::Object> thr_group = hs.NewHandle(soa.Decode<mirror::Object>(
+ thread_group != nullptr ? thread_group : runtime->GetMainThreadGroup()));
+ Handle<mirror::String> thread_name = hs.NewHandle(
+ name != nullptr ? mirror::String::AllocFromModifiedUtf8(self, name) : nullptr);
// Add missing null check in case of OOM b/18297817
- if (name != nullptr && thread_name.get() == nullptr) {
- CHECK(Thread::Current()->IsExceptionPending());
+ if (name != nullptr && UNLIKELY(thread_name == nullptr)) {
+ CHECK(self->IsExceptionPending());
return nullptr;
}
jint thread_priority = kNormThreadPriority; // Always normalize to NORM priority.
- jboolean thread_is_daemon = as_daemon;
- ScopedLocalRef<jobject> peer(env, env->AllocObject(WellKnownClasses::java_lang_Thread));
- if (peer.get() == nullptr) {
+ DCHECK(WellKnownClasses::java_lang_Thread->IsInitialized());
+ Handle<mirror::Object> peer = hs.NewHandle(
+ WellKnownClasses::java_lang_Thread->AllocObject(self));
+ if (peer == nullptr) {
CHECK(Thread::Current()->IsExceptionPending());
return nullptr;
}
@@ -1197,41 +1220,34 @@
// non-null value. However, because we can run without code
// available (in the compiler, in tests), we manually assign the
// fields the constructor should have set.
- ScopedObjectAccessUnchecked soa(Thread::Current());
if (runtime->IsActiveTransaction()) {
- InitPeer<true>(soa,
- soa.Decode<mirror::Object>(peer.get()),
- thread_is_daemon,
- thread_group,
- thread_name.get(),
+ InitPeer<true>(peer.Get(),
+ as_daemon,
+ thr_group.Get(),
+ thread_name.Get(),
thread_priority);
} else {
- InitPeer<false>(soa,
- soa.Decode<mirror::Object>(peer.get()),
- thread_is_daemon,
- thread_group,
- thread_name.get(),
+ InitPeer<false>(peer.Get(),
+ as_daemon,
+ thr_group.Get(),
+ thread_name.Get(),
thread_priority);
}
- return peer.release();
+ return peer.Get();
}
template<bool kTransactionActive>
-void Thread::InitPeer(ScopedObjectAccessAlreadyRunnable& soa,
- ObjPtr<mirror::Object> peer,
- jboolean thread_is_daemon,
- jobject thread_group,
- jobject thread_name,
+void Thread::InitPeer(ObjPtr<mirror::Object> peer,
+ bool as_daemon,
+ ObjPtr<mirror::Object> thread_group,
+ ObjPtr<mirror::String> thread_name,
jint thread_priority) {
- jni::DecodeArtField(WellKnownClasses::java_lang_Thread_daemon)->
- SetBoolean<kTransactionActive>(peer, thread_is_daemon);
- jni::DecodeArtField(WellKnownClasses::java_lang_Thread_group)->
- SetObject<kTransactionActive>(peer, soa.Decode<mirror::Object>(thread_group));
- jni::DecodeArtField(WellKnownClasses::java_lang_Thread_name)->
- SetObject<kTransactionActive>(peer, soa.Decode<mirror::Object>(thread_name));
- jni::DecodeArtField(WellKnownClasses::java_lang_Thread_priority)->
- SetInt<kTransactionActive>(peer, thread_priority);
+ WellKnownClasses::java_lang_Thread_daemon->SetBoolean<kTransactionActive>(peer,
+ static_cast<uint8_t>(as_daemon ? 1u : 0u));
+ WellKnownClasses::java_lang_Thread_group->SetObject<kTransactionActive>(peer, thread_group);
+ WellKnownClasses::java_lang_Thread_name->SetObject<kTransactionActive>(peer, thread_name);
+ WellKnownClasses::java_lang_Thread_priority->SetInt<kTransactionActive>(peer, thread_priority);
}
void Thread::SetCachedThreadName(const char* name) {
@@ -1390,18 +1406,26 @@
tls32_.num_name_readers.fetch_sub(1 /* at least memory_order_release */);
}
-void Thread::Dump(std::ostream& os, bool dump_native_stack, BacktraceMap* backtrace_map,
- bool force_dump_stack) const {
+Thread::DumpOrder Thread::Dump(std::ostream& os,
+ bool dump_native_stack,
+ bool force_dump_stack) const {
DumpState(os);
- DumpStack(os, dump_native_stack, backtrace_map, force_dump_stack);
+ return DumpStack(os, dump_native_stack, force_dump_stack);
+}
+
+Thread::DumpOrder Thread::Dump(std::ostream& os,
+ unwindstack::AndroidLocalUnwinder& unwinder,
+ bool dump_native_stack,
+ bool force_dump_stack) const {
+ DumpState(os);
+ return DumpStack(os, unwinder, dump_native_stack, force_dump_stack);
}
ObjPtr<mirror::String> Thread::GetThreadName() const {
- ArtField* f = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_name);
if (tlsPtr_.opeer == nullptr) {
return nullptr;
}
- ObjPtr<mirror::Object> name = f->GetObject(tlsPtr_.opeer);
+ ObjPtr<mirror::Object> name = WellKnownClasses::java_lang_Thread_name->GetObject(tlsPtr_.opeer);
return name == nullptr ? nullptr : name->AsString();
}
@@ -1473,7 +1497,7 @@
return false;
}
- if (kUseReadBarrier && delta > 0 && this != self && tlsPtr_.flip_function != nullptr) {
+ if (gUseReadBarrier && delta > 0 && this != self && tlsPtr_.flip_function != nullptr) {
// Force retry of a suspend request if it's in the middle of a thread flip to avoid a
// deadlock. b/31683379.
return false;
@@ -1939,20 +1963,15 @@
// cause ScopedObjectAccessUnchecked to deadlock.
if (gAborting == 0 && self != nullptr && thread != nullptr && thread->tlsPtr_.opeer != nullptr) {
ScopedObjectAccessUnchecked soa(self);
- priority = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_priority)
- ->GetInt(thread->tlsPtr_.opeer);
- is_daemon = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_daemon)
- ->GetBoolean(thread->tlsPtr_.opeer);
+ priority = WellKnownClasses::java_lang_Thread_priority->GetInt(thread->tlsPtr_.opeer);
+ is_daemon = WellKnownClasses::java_lang_Thread_daemon->GetBoolean(thread->tlsPtr_.opeer);
ObjPtr<mirror::Object> thread_group =
- jni::DecodeArtField(WellKnownClasses::java_lang_Thread_group)
- ->GetObject(thread->tlsPtr_.opeer);
+ WellKnownClasses::java_lang_Thread_group->GetObject(thread->tlsPtr_.opeer);
if (thread_group != nullptr) {
- ArtField* group_name_field =
- jni::DecodeArtField(WellKnownClasses::java_lang_ThreadGroup_name);
ObjPtr<mirror::String> group_name_string =
- group_name_field->GetObject(thread_group)->AsString();
+ WellKnownClasses::java_lang_ThreadGroup_name->GetObject(thread_group)->AsString();
group_name = (group_name_string != nullptr) ? group_name_string->ToModifiedUtf8() : "<null>";
}
} else if (thread != nullptr) {
@@ -1980,6 +1999,9 @@
if (thread->IsStillStarting()) {
os << " (still starting up)";
}
+ if (thread->tls32_.disable_thread_flip_count != 0) {
+ os << " DisableFlipCount = " << thread->tls32_.disable_thread_flip_count;
+ }
os << "\n";
} else {
os << '"' << ::art::GetThreadName(tid) << '"'
@@ -2192,11 +2214,13 @@
UNREACHABLE();
}
PrintObject(obj, msg, owner_tid);
+ num_blocked++;
}
void VisitLockedObject(ObjPtr<mirror::Object> obj)
override
REQUIRES_SHARED(Locks::mutator_lock_) {
PrintObject(obj, " - locked ", ThreadList::kInvalidThreadId);
+ num_locked++;
}
void PrintObject(ObjPtr<mirror::Object> obj,
@@ -2230,6 +2254,8 @@
ArtMethod* last_method;
int last_line_number;
size_t repetition_count;
+ size_t num_blocked = 0;
+ size_t num_locked = 0;
};
static bool ShouldShowNativeStack(const Thread* thread)
@@ -2261,7 +2287,9 @@
return current_method != nullptr && current_method->IsNative();
}
-void Thread::DumpJavaStack(std::ostream& os, bool check_suspended, bool dump_locks) const {
+Thread::DumpOrder Thread::DumpJavaStack(std::ostream& os,
+ bool check_suspended,
+ bool dump_locks) const {
// Dumping the Java stack involves the verifier for locks. The verifier operates under the
// assumption that there is no exception pending on entry. Thus, stash any pending exception.
// Thread::Current() instead of this in case a thread is dumping the stack of another suspended
@@ -2272,12 +2300,28 @@
StackDumpVisitor dumper(os, const_cast<Thread*>(this), context.get(),
!tls32_.throwing_OutOfMemoryError, check_suspended, dump_locks);
dumper.WalkStack();
+ if (IsJitSensitiveThread()) {
+ return DumpOrder::kMain;
+ } else if (dumper.num_blocked > 0) {
+ return DumpOrder::kBlocked;
+ } else if (dumper.num_locked > 0) {
+ return DumpOrder::kLocked;
+ } else {
+ return DumpOrder::kDefault;
+ }
}
-void Thread::DumpStack(std::ostream& os,
- bool dump_native_stack,
- BacktraceMap* backtrace_map,
- bool force_dump_stack) const {
+Thread::DumpOrder Thread::DumpStack(std::ostream& os,
+ bool dump_native_stack,
+ bool force_dump_stack) const {
+ unwindstack::AndroidLocalUnwinder unwinder;
+ return DumpStack(os, unwinder, dump_native_stack, force_dump_stack);
+}
+
+Thread::DumpOrder Thread::DumpStack(std::ostream& os,
+ unwindstack::AndroidLocalUnwinder& unwinder,
+ bool dump_native_stack,
+ bool force_dump_stack) const {
// TODO: we call this code when dying but may not have suspended the thread ourself. The
// IsSuspended check is therefore racy with the use for dumping (normally we inhibit
// the race with the thread_suspend_count_lock_).
@@ -2288,6 +2332,7 @@
// thread's stack in debug builds where we'll hit the not suspended check in the stack walk.
safe_to_dump = (safe_to_dump || dump_for_abort);
}
+ DumpOrder dump_order = DumpOrder::kDefault;
if (safe_to_dump || force_dump_stack) {
// If we're currently in native code, dump that stack before dumping the managed stack.
if (dump_native_stack && (dump_for_abort || force_dump_stack || ShouldShowNativeStack(this))) {
@@ -2295,14 +2340,15 @@
GetCurrentMethod(nullptr,
/*check_suspended=*/ !force_dump_stack,
/*abort_on_error=*/ !(dump_for_abort || force_dump_stack));
- DumpNativeStack(os, GetTid(), backtrace_map, " native: ", method);
+ DumpNativeStack(os, unwinder, GetTid(), " native: ", method);
}
- DumpJavaStack(os,
- /*check_suspended=*/ !force_dump_stack,
- /*dump_locks=*/ !force_dump_stack);
+ dump_order = DumpJavaStack(os,
+ /*check_suspended=*/ !force_dump_stack,
+ /*dump_locks=*/ !force_dump_stack);
} else {
os << "Not able to dump stack of thread that isn't suspended";
}
+ return dump_order;
}
void Thread::ThreadExitCallback(void* arg) {
@@ -2380,25 +2426,17 @@
}
void Thread::NotifyThreadGroup(ScopedObjectAccessAlreadyRunnable& soa, jobject thread_group) {
- ScopedLocalRef<jobject> thread_jobject(
- soa.Env(), soa.Env()->AddLocalReference<jobject>(Thread::Current()->GetPeer()));
- ScopedLocalRef<jobject> thread_group_jobject_scoped(
- soa.Env(), nullptr);
- jobject thread_group_jobject = thread_group;
+ ObjPtr<mirror::Object> thread_object = soa.Self()->GetPeer();
+ ObjPtr<mirror::Object> thread_group_object = soa.Decode<mirror::Object>(thread_group);
if (thread_group == nullptr || kIsDebugBuild) {
// There is always a group set. Retrieve it.
- thread_group_jobject_scoped.reset(
- soa.Env()->GetObjectField(thread_jobject.get(),
- WellKnownClasses::java_lang_Thread_group));
- thread_group_jobject = thread_group_jobject_scoped.get();
+ thread_group_object = WellKnownClasses::java_lang_Thread_group->GetObject(thread_object);
if (kIsDebugBuild && thread_group != nullptr) {
- CHECK(soa.Env()->IsSameObject(thread_group, thread_group_jobject));
+ CHECK(thread_group_object == soa.Decode<mirror::Object>(thread_group));
}
}
- soa.Env()->CallNonvirtualVoidMethod(thread_group_jobject,
- WellKnownClasses::java_lang_ThreadGroup,
- WellKnownClasses::java_lang_ThreadGroup_add,
- thread_jobject.get());
+ WellKnownClasses::java_lang_ThreadGroup_add->InvokeVirtual<'V', 'L'>(
+ soa.Self(), thread_group_object, thread_object);
}
Thread::Thread(bool daemon)
@@ -2458,8 +2496,7 @@
void Thread::AssertPendingOOMException() const {
AssertPendingException();
auto* e = GetException();
- CHECK_EQ(e->GetClass(), DecodeJObject(WellKnownClasses::java_lang_OutOfMemoryError)->AsClass())
- << e->Dump();
+ CHECK_EQ(e->GetClass(), WellKnownClasses::java_lang_OutOfMemoryError.Get()) << e->Dump();
}
void Thread::AssertNoPendingException() const {
@@ -2497,7 +2534,7 @@
Thread* const self_;
};
-void Thread::Destroy() {
+void Thread::Destroy(bool should_run_callbacks) {
Thread* self = this;
DCHECK_EQ(self, Thread::Current());
@@ -2522,27 +2559,25 @@
if (tlsPtr_.opeer != nullptr) {
ScopedObjectAccess soa(self);
+ if (UNLIKELY(self->GetMethodTraceBuffer() != nullptr)) {
+ Trace::FlushThreadBuffer(self);
+ self->ResetMethodTraceBuffer();
+ }
// We may need to call user-supplied managed code, do this before final clean-up.
- HandleUncaughtExceptions(soa);
- RemoveFromThreadGroup(soa);
+ HandleUncaughtExceptions();
+ RemoveFromThreadGroup();
Runtime* runtime = Runtime::Current();
- if (runtime != nullptr) {
+ if (runtime != nullptr && should_run_callbacks) {
runtime->GetRuntimeCallbacks()->ThreadDeath(self);
}
// this.nativePeer = 0;
- if (Runtime::Current()->IsActiveTransaction()) {
- jni::DecodeArtField(WellKnownClasses::java_lang_Thread_nativePeer)
- ->SetLong<true>(tlsPtr_.opeer, 0);
- } else {
- jni::DecodeArtField(WellKnownClasses::java_lang_Thread_nativePeer)
- ->SetLong<false>(tlsPtr_.opeer, 0);
- }
+ SetNativePeer</*kSupportTransaction=*/ true>(tlsPtr_.opeer, nullptr);
// Thread.join() is implemented as an Object.wait() on the Thread.lock object. Signal anyone
// who is waiting.
ObjPtr<mirror::Object> lock =
- jni::DecodeArtField(WellKnownClasses::java_lang_Thread_lock)->GetObject(tlsPtr_.opeer);
+ WellKnownClasses::java_lang_Thread_lock->GetObject(tlsPtr_.opeer);
// (This conditional is only needed for tests, where Thread.lock won't have been set.)
if (lock != nullptr) {
StackHandleScope<1> hs(self);
@@ -2559,7 +2594,7 @@
}
// Mark-stack revocation must be performed at the very end. No
// checkpoint/flip-function or read-barrier should be called after this.
- if (kUseReadBarrier) {
+ if (gUseReadBarrier) {
Runtime::Current()->GetHeap()->ConcurrentCopyingCollector()->RevokeThreadLocalMarkStack(this);
}
}
@@ -2604,43 +2639,44 @@
SetCachedThreadName(nullptr); // Deallocate name.
delete tlsPtr_.deps_or_stack_trace_sample.stack_trace_sample;
+ if (tlsPtr_.method_trace_buffer != nullptr) {
+ delete[] tlsPtr_.method_trace_buffer;
+ }
+
Runtime::Current()->GetHeap()->AssertThreadLocalBuffersAreRevoked(this);
TearDownAlternateSignalStack();
}
-void Thread::HandleUncaughtExceptions(ScopedObjectAccessAlreadyRunnable& soa) {
- if (!IsExceptionPending()) {
+void Thread::HandleUncaughtExceptions() {
+ Thread* self = this;
+ DCHECK_EQ(self, Thread::Current());
+ if (!self->IsExceptionPending()) {
return;
}
- ScopedLocalRef<jobject> peer(tlsPtr_.jni_env, soa.AddLocalReference<jobject>(tlsPtr_.opeer));
- ScopedThreadStateChange tsc(this, ThreadState::kNative);
// Get and clear the exception.
- ScopedLocalRef<jthrowable> exception(tlsPtr_.jni_env, tlsPtr_.jni_env->ExceptionOccurred());
- tlsPtr_.jni_env->ExceptionClear();
+ ObjPtr<mirror::Object> exception = self->GetException();
+ self->ClearException();
// Call the Thread instance's dispatchUncaughtException(Throwable)
- tlsPtr_.jni_env->CallVoidMethod(peer.get(),
- WellKnownClasses::java_lang_Thread_dispatchUncaughtException,
- exception.get());
+ WellKnownClasses::java_lang_Thread_dispatchUncaughtException->InvokeFinal<'V', 'L'>(
+ self, tlsPtr_.opeer, exception);
// If the dispatchUncaughtException threw, clear that exception too.
- tlsPtr_.jni_env->ExceptionClear();
+ self->ClearException();
}
-void Thread::RemoveFromThreadGroup(ScopedObjectAccessAlreadyRunnable& soa) {
- // this.group.removeThread(this);
+void Thread::RemoveFromThreadGroup() {
+ Thread* self = this;
+ DCHECK_EQ(self, Thread::Current());
+ // this.group.threadTerminated(this);
// group can be null if we're in the compiler or a test.
- ObjPtr<mirror::Object> ogroup = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_group)
- ->GetObject(tlsPtr_.opeer);
- if (ogroup != nullptr) {
- ScopedLocalRef<jobject> group(soa.Env(), soa.AddLocalReference<jobject>(ogroup));
- ScopedLocalRef<jobject> peer(soa.Env(), soa.AddLocalReference<jobject>(tlsPtr_.opeer));
- ScopedThreadStateChange tsc(soa.Self(), ThreadState::kNative);
- tlsPtr_.jni_env->CallVoidMethod(group.get(),
- WellKnownClasses::java_lang_ThreadGroup_removeThread,
- peer.get());
+ ObjPtr<mirror::Object> group =
+ WellKnownClasses::java_lang_Thread_group->GetObject(tlsPtr_.opeer);
+ if (group != nullptr) {
+ WellKnownClasses::java_lang_ThreadGroup_threadTerminated->InvokeVirtual<'V', 'L'>(
+ self, group, tlsPtr_.opeer);
}
}
@@ -2741,10 +2777,10 @@
bool expect_null = false;
// The "kinds" below are sorted by the frequency we expect to encounter them.
if (kind == kLocal) {
- IndirectReferenceTable& locals = tlsPtr_.jni_env->locals_;
+ jni::LocalReferenceTable& locals = tlsPtr_.jni_env->locals_;
// Local references do not need a read barrier.
- result = locals.Get<kWithoutReadBarrier>(ref);
- } else if (kind == kJniTransitionOrInvalid) {
+ result = locals.Get(ref);
+ } else if (kind == kJniTransition) {
// The `jclass` for a static method points to the CompressedReference<> in the
// `ArtMethod::declaring_class_`. Other `jobject` arguments point to spilled stack
// references but a StackReference<> is just a subclass of CompressedReference<>.
@@ -3138,6 +3174,148 @@
return result;
}
+[[nodiscard]] static ObjPtr<mirror::StackFrameInfo> InitStackFrameInfo(
+ const ScopedObjectAccessAlreadyRunnable& soa,
+ ClassLinker* class_linker,
+ Handle<mirror::StackFrameInfo> stackFrameInfo,
+ ArtMethod* method,
+ uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_) {
+ StackHandleScope<4> hs(soa.Self());
+ int32_t line_number;
+ auto source_name_object(hs.NewHandle<mirror::String>(nullptr));
+ if (method->IsProxyMethod()) {
+ line_number = -1;
+ // source_name_object intentionally left null for proxy methods
+ } else {
+ line_number = method->GetLineNumFromDexPC(dex_pc);
+ if (line_number == -1) {
+ // Make the line_number field of StackFrameInfo hold the dex pc.
+ // source_name_object is intentionally left null if we failed to map the dex pc to
+ // a line number (most probably because there is no debug info). See b/30183883.
+ line_number = static_cast<int32_t>(dex_pc);
+ } else {
+ const char* source_file = method->GetDeclaringClassSourceFile();
+ if (source_file != nullptr) {
+ source_name_object.Assign(mirror::String::AllocFromModifiedUtf8(soa.Self(), source_file));
+ if (source_name_object == nullptr) {
+ soa.Self()->AssertPendingOOMException();
+ return nullptr;
+ }
+ }
+ }
+ }
+
+ Handle<mirror::Class> declaring_class_object(
+ hs.NewHandle<mirror::Class>(method->GetDeclaringClass()));
+
+ ArtMethod* interface_method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize);
+ const char* method_name = interface_method->GetName();
+ CHECK(method_name != nullptr);
+ Handle<mirror::String> method_name_object(
+ hs.NewHandle(mirror::String::AllocFromModifiedUtf8(soa.Self(), method_name)));
+ if (method_name_object == nullptr) {
+ soa.Self()->AssertPendingOOMException();
+ return nullptr;
+ }
+
+ dex::ProtoIndex proto_idx =
+ method->GetDexFile()->GetIndexForProtoId(interface_method->GetPrototype());
+ Handle<mirror::MethodType> method_type_object(hs.NewHandle<mirror::MethodType>(
+ class_linker->ResolveMethodType(soa.Self(), proto_idx, interface_method)));
+ if (method_type_object == nullptr) {
+ soa.Self()->AssertPendingOOMException();
+ return nullptr;
+ }
+
+ stackFrameInfo->AssignFields(declaring_class_object,
+ method_type_object,
+ method_name_object,
+ source_name_object,
+ line_number,
+ static_cast<int32_t>(dex_pc));
+ return stackFrameInfo.Get();
+}
+
+constexpr jlong FILL_CLASS_REFS_ONLY = 0x2; // StackStreamFactory.FILL_CLASS_REFS_ONLY
+
+jint Thread::InternalStackTraceToStackFrameInfoArray(
+ const ScopedObjectAccessAlreadyRunnable& soa,
+ jlong mode, // See java.lang.StackStreamFactory for the mode flags
+ jobject internal,
+ jint startLevel,
+ jint batchSize,
+ jint startBufferIndex,
+ jobjectArray output_array) {
+ // Decode the internal stack trace into the depth, method trace and PC trace.
+ // Subtract one for the methods and PC trace.
+ int32_t depth = soa.Decode<mirror::Array>(internal)->GetLength() - 1;
+ DCHECK_GE(depth, 0);
+
+ StackHandleScope<6> hs(soa.Self());
+ Handle<mirror::ObjectArray<mirror::Object>> framesOrClasses =
+ hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Object>>(output_array));
+
+ jint endBufferIndex = startBufferIndex;
+
+ if (startLevel < 0 || startLevel >= depth) {
+ return endBufferIndex;
+ }
+
+ int32_t bufferSize = framesOrClasses->GetLength();
+ if (startBufferIndex < 0 || startBufferIndex >= bufferSize) {
+ return endBufferIndex;
+ }
+
+ // The FILL_CLASS_REFS_ONLY flag is defined in AbstractStackWalker.fetchStackFrames() javadoc.
+ bool isClassArray = (mode & FILL_CLASS_REFS_ONLY) != 0;
+
+ Handle<mirror::ObjectArray<mirror::Object>> decoded_traces =
+ hs.NewHandle(soa.Decode<mirror::Object>(internal)->AsObjectArray<mirror::Object>());
+ // Methods and dex PC trace is element 0.
+ DCHECK(decoded_traces->Get(0)->IsIntArray() || decoded_traces->Get(0)->IsLongArray());
+ Handle<mirror::PointerArray> method_trace =
+ hs.NewHandle(ObjPtr<mirror::PointerArray>::DownCast(decoded_traces->Get(0)));
+
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ Handle<mirror::Class> sfi_class =
+ hs.NewHandle(class_linker->FindSystemClass(soa.Self(), "Ljava/lang/StackFrameInfo;"));
+ DCHECK(sfi_class != nullptr);
+
+ MutableHandle<mirror::StackFrameInfo> frame = hs.NewHandle<mirror::StackFrameInfo>(nullptr);
+ MutableHandle<mirror::Class> clazz = hs.NewHandle<mirror::Class>(nullptr);
+ for (uint32_t i = static_cast<uint32_t>(startLevel); i < static_cast<uint32_t>(depth); ++i) {
+ if (endBufferIndex >= startBufferIndex + batchSize || endBufferIndex >= bufferSize) {
+ break;
+ }
+
+ ArtMethod* method = method_trace->GetElementPtrSize<ArtMethod*>(i, kRuntimePointerSize);
+ if (isClassArray) {
+ clazz.Assign(method->GetDeclaringClass());
+ framesOrClasses->Set(endBufferIndex, clazz.Get());
+ } else {
+ // Prepare parameters for fields in StackFrameInfo
+ uint32_t dex_pc = method_trace->GetElementPtrSize<uint32_t>(
+ i + static_cast<uint32_t>(method_trace->GetLength()) / 2, kRuntimePointerSize);
+
+ ObjPtr<mirror::Object> frameObject = framesOrClasses->Get(endBufferIndex);
+ // If libcore didn't allocate the object, we just stop here, but it's unlikely.
+ if (frameObject == nullptr || !frameObject->InstanceOf(sfi_class.Get())) {
+ break;
+ }
+ frame.Assign(ObjPtr<mirror::StackFrameInfo>::DownCast(frameObject));
+ frame.Assign(InitStackFrameInfo(soa, class_linker, frame, method, dex_pc));
+ // Break if InitStackFrameInfo fails to allocate objects or assign the fields.
+ if (frame == nullptr) {
+ break;
+ }
+ }
+
+ ++endBufferIndex;
+ }
+
+ return endBufferIndex;
+}
+
jobjectArray Thread::CreateAnnotatedStackTrace(const ScopedObjectAccessAlreadyRunnable& soa) const {
// This code allocates. Do not allow it to operate with a pending exception.
if (IsExceptionPending()) {
@@ -3574,6 +3752,7 @@
QUICK_ENTRY_POINT_INFO(pAputObject)
QUICK_ENTRY_POINT_INFO(pJniMethodStart)
QUICK_ENTRY_POINT_INFO(pJniMethodEnd)
+ QUICK_ENTRY_POINT_INFO(pJniMethodEntryHook)
QUICK_ENTRY_POINT_INFO(pJniDecodeReferenceResult)
QUICK_ENTRY_POINT_INFO(pJniLockObject)
QUICK_ENTRY_POINT_INFO(pJniUnlockObject)
@@ -3639,6 +3818,7 @@
QUICK_ENTRY_POINT_INFO(pA64Store)
QUICK_ENTRY_POINT_INFO(pNewEmptyString)
QUICK_ENTRY_POINT_INFO(pNewStringFromBytes_B)
+ QUICK_ENTRY_POINT_INFO(pNewStringFromBytes_BB)
QUICK_ENTRY_POINT_INFO(pNewStringFromBytes_BI)
QUICK_ENTRY_POINT_INFO(pNewStringFromBytes_BII)
QUICK_ENTRY_POINT_INFO(pNewStringFromBytes_BIII)
@@ -3653,6 +3833,7 @@
QUICK_ENTRY_POINT_INFO(pNewStringFromString)
QUICK_ENTRY_POINT_INFO(pNewStringFromStringBuffer)
QUICK_ENTRY_POINT_INFO(pNewStringFromStringBuilder)
+ QUICK_ENTRY_POINT_INFO(pNewStringFromUtf16Bytes_BII)
QUICK_ENTRY_POINT_INFO(pJniReadBarrier)
QUICK_ENTRY_POINT_INFO(pReadBarrierMarkReg00)
QUICK_ENTRY_POINT_INFO(pReadBarrierMarkReg01)
@@ -3691,12 +3872,15 @@
os << offset;
}
-void Thread::QuickDeliverException() {
+void Thread::QuickDeliverException(bool skip_method_exit_callbacks) {
// Get exception from thread.
ObjPtr<mirror::Throwable> exception = GetException();
CHECK(exception != nullptr);
if (exception == GetDeoptimizationException()) {
- artDeoptimize(this);
+ // This wasn't a real exception, so just clear it here. If there was an actual exception it
+ // will be recorded in the DeoptimizationContext and it will be restored later.
+ ClearException();
+ artDeoptimize(this, skip_method_exit_callbacks);
UNREACHABLE();
}
@@ -3718,62 +3902,30 @@
// deoptimization. It may happen if a debugger is attached and requests new events (single-step,
// breakpoint, ...) when the exception is reported.
//
- // Note we need to check for both force_frame_pop and force_retry_instruction. The first is
- // expected to happen fairly regularly but the second can only happen if we are using
- // instrumentation trampolines (for example with DDMS tracing). That forces us to do deopt later
- // and see every frame being popped. We don't need to handle it any differently.
- ShadowFrame* cf;
- bool force_deopt = false;
- if (Runtime::Current()->AreNonStandardExitsEnabled() || kIsDebugBuild) {
+ if (Dbg::IsForcedInterpreterNeededForException(this) || IsForceInterpreter()) {
NthCallerVisitor visitor(this, 0, false);
visitor.WalkStack();
- cf = visitor.GetCurrentShadowFrame();
- if (cf == nullptr) {
- cf = FindDebuggerShadowFrame(visitor.GetFrameId());
- }
- bool force_frame_pop = cf != nullptr && cf->GetForcePopFrame();
- bool force_retry_instr = cf != nullptr && cf->GetForceRetryInstruction();
- if (kIsDebugBuild && force_frame_pop) {
- DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
- NthCallerVisitor penultimate_visitor(this, 1, false);
- penultimate_visitor.WalkStack();
- ShadowFrame* penultimate_frame = penultimate_visitor.GetCurrentShadowFrame();
- if (penultimate_frame == nullptr) {
- penultimate_frame = FindDebuggerShadowFrame(penultimate_visitor.GetFrameId());
+ if (visitor.GetCurrentQuickFrame() != nullptr) {
+ if (Runtime::Current()->IsAsyncDeoptimizeable(visitor.GetOuterMethod(), visitor.caller_pc)) {
+ // method_type shouldn't matter due to exception handling.
+ const DeoptimizationMethodType method_type = DeoptimizationMethodType::kDefault;
+ // Save the exception into the deoptimization context so it can be restored
+ // before entering the interpreter.
+ PushDeoptimizationContext(
+ JValue(),
+ /* is_reference= */ false,
+ exception,
+ /* from_code= */ false,
+ method_type);
+ artDeoptimize(this, skip_method_exit_callbacks);
+ UNREACHABLE();
+ } else {
+ LOG(WARNING) << "Got a deoptimization request on un-deoptimizable method "
+ << visitor.caller->PrettyMethod();
}
- }
- if (force_retry_instr) {
- DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
- }
- force_deopt = force_frame_pop || force_retry_instr;
- }
- if (Dbg::IsForcedInterpreterNeededForException(this) || force_deopt || IsForceInterpreter()) {
- NthCallerVisitor visitor(this, 0, false);
- visitor.WalkStack();
- if (Runtime::Current()->IsAsyncDeoptimizeable(visitor.caller_pc)) {
- // method_type shouldn't matter due to exception handling.
- const DeoptimizationMethodType method_type = DeoptimizationMethodType::kDefault;
- // Save the exception into the deoptimization context so it can be restored
- // before entering the interpreter.
- if (force_deopt) {
- VLOG(deopt) << "Deopting " << cf->GetMethod()->PrettyMethod() << " for frame-pop";
- DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
- // Get rid of the exception since we are doing a framepop instead.
- LOG(WARNING) << "Suppressing pending exception for retry-instruction/frame-pop: "
- << exception->Dump();
- ClearException();
- }
- PushDeoptimizationContext(
- JValue(),
- /* is_reference= */ false,
- (force_deopt ? nullptr : exception),
- /* from_code= */ false,
- method_type);
- artDeoptimize(this);
- UNREACHABLE();
- } else if (visitor.caller != nullptr) {
- LOG(WARNING) << "Got a deoptimization request on un-deoptimizable method "
- << visitor.caller->PrettyMethod();
+ } else {
+ // This is either top of call stack, or shadow frame.
+ DCHECK(visitor.caller == nullptr || visitor.IsShadowFrame());
}
}
@@ -3781,7 +3933,7 @@
// resolution.
ClearException();
QuickExceptionHandler exception_handler(this, false);
- exception_handler.FindCatch(exception);
+ exception_handler.FindCatch(exception, skip_method_exit_callbacks);
if (exception_handler.GetClearException()) {
// Exception was cleared as part of delivery.
DCHECK(!IsExceptionPending());
@@ -3851,7 +4003,11 @@
// We are visiting the references in compiled frames, so we do not need
// to know the inlined frames.
: StackVisitor(thread, context, StackVisitor::StackWalkKind::kSkipInlinedFrames),
- visitor_(visitor) {}
+ visitor_(visitor) {
+ gc::Heap* const heap = Runtime::Current()->GetHeap();
+ visit_declaring_class_ = heap->CurrentCollectorType() != gc::CollectorType::kCollectorTypeCMC
+ || !heap->MarkCompactCollector()->IsCompacting(Thread::Current());
+ }
bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) {
if (false) {
@@ -3896,6 +4052,9 @@
void VisitDeclaringClass(ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_)
NO_THREAD_SAFETY_ANALYSIS {
+ if (!visit_declaring_class_) {
+ return;
+ }
ObjPtr<mirror::Class> klass = method->GetDeclaringClassUnchecked<kWithoutReadBarrier>();
// klass can be null for runtime methods.
if (klass != nullptr) {
@@ -3978,7 +4137,7 @@
// (PC shall be known thanks to the runtime frame for throwing SIOOBE).
// Note that JIT does not emit that intrinic implementation.
const void* pc = reinterpret_cast<const void*>(GetCurrentQuickFramePc());
- if (pc != 0u && Runtime::Current()->GetHeap()->IsInBootImageOatFile(pc)) {
+ if (pc != nullptr && Runtime::Current()->GetHeap()->IsInBootImageOatFile(pc)) {
return;
}
}
@@ -4189,6 +4348,7 @@
// Visitor for when we visit a root.
RootVisitor& visitor_;
+ bool visit_declaring_class_;
};
class RootCallbackVisitor {
@@ -4307,6 +4467,9 @@
case Opcode::CONST_STRING:
case Opcode::CONST_STRING_JUMBO: {
mirror::Object* object = reinterpret_cast<mirror::Object*>(*value);
+ if (object == nullptr) {
+ return;
+ }
mirror::Object* new_object = visitor->IsMarked(object);
// We know the string is marked because it's a strongly-interned string that
// is always alive (see b/117621117 for trying to make those strings weak).
@@ -4327,16 +4490,12 @@
// New opcode is using the cache. We need to explicitly handle it in this method.
DCHECK(false) << "Unhandled opcode " << inst->Opcode();
}
-};
+}
-void Thread::SweepInterpreterCaches(IsMarkedVisitor* visitor) {
- MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
- Runtime::Current()->GetThreadList()->ForEach([visitor](Thread* thread) {
- Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
- for (InterpreterCache::Entry& entry : thread->GetInterpreterCache()->GetArray()) {
- SweepCacheEntry(visitor, reinterpret_cast<const Instruction*>(entry.first), &entry.second);
- }
- });
+void Thread::SweepInterpreterCache(IsMarkedVisitor* visitor) {
+ for (InterpreterCache::Entry& entry : GetInterpreterCache()->GetArray()) {
+ SweepCacheEntry(visitor, reinterpret_cast<const Instruction*>(entry.first), &entry.second);
+ }
}
// FIXME: clang-r433403 reports the below function exceeds frame size limit.
@@ -4425,6 +4584,15 @@
return has_tlab;
}
+void Thread::AdjustTlab(size_t slide_bytes) {
+ if (HasTlab()) {
+ tlsPtr_.thread_local_start -= slide_bytes;
+ tlsPtr_.thread_local_pos -= slide_bytes;
+ tlsPtr_.thread_local_end -= slide_bytes;
+ tlsPtr_.thread_local_limit -= slide_bytes;
+ }
+}
+
std::ostream& operator<<(std::ostream& os, const Thread& thread) {
thread.ShortDump(os);
return os;
@@ -4435,9 +4603,11 @@
VLOG(threads) << "Protecting stack at " << pregion;
if (mprotect(pregion, kStackOverflowProtectedSize, PROT_NONE) == -1) {
if (fatal_on_error) {
- LOG(FATAL) << "Unable to create protected region in stack for implicit overflow check. "
+ // b/249586057, LOG(FATAL) times out
+ LOG(ERROR) << "Unable to create protected region in stack for implicit overflow check. "
"Reason: "
<< strerror(errno) << " size: " << kStackOverflowProtectedSize;
+ exit(1);
}
return false;
}
@@ -4471,25 +4641,29 @@
void Thread::DeoptimizeWithDeoptimizationException(JValue* result) {
DCHECK_EQ(GetException(), Thread::GetDeoptimizationException());
ClearException();
- ShadowFrame* shadow_frame =
- PopStackedShadowFrame(StackedShadowFrameType::kDeoptimizationShadowFrame);
ObjPtr<mirror::Throwable> pending_exception;
bool from_code = false;
DeoptimizationMethodType method_type;
PopDeoptimizationContext(result, &pending_exception, &from_code, &method_type);
SetTopOfStack(nullptr);
- SetTopOfShadowStack(shadow_frame);
// Restore the exception that was pending before deoptimization then interpret the
// deoptimized frames.
if (pending_exception != nullptr) {
SetException(pending_exception);
}
- interpreter::EnterInterpreterFromDeoptimize(this,
- shadow_frame,
- result,
- from_code,
- method_type);
+
+ ShadowFrame* shadow_frame = MaybePopDeoptimizedStackedShadowFrame();
+ // We may not have a shadow frame if we deoptimized at the return of the
+ // quick_to_interpreter_bridge which got directly called by art_quick_invoke_stub.
+ if (shadow_frame != nullptr) {
+ SetTopOfShadowStack(shadow_frame);
+ interpreter::EnterInterpreterFromDeoptimize(this,
+ shadow_frame,
+ result,
+ from_code,
+ method_type);
+ }
}
void Thread::SetAsyncException(ObjPtr<mirror::Throwable> new_exception) {
@@ -4534,7 +4708,7 @@
mirror::Object* Thread::GetPeerFromOtherThread() const {
DCHECK(tlsPtr_.jpeer == nullptr);
mirror::Object* peer = tlsPtr_.opeer;
- if (kUseReadBarrier && Current()->GetIsGcMarking()) {
+ if (gUseReadBarrier && Current()->GetIsGcMarking()) {
// We may call Thread::Dump() in the middle of the CC thread flip and this thread's stack
// may have not been flipped yet and peer may be a from-space (stale) ref. So explicitly
// mark/forward it here.
@@ -4587,8 +4761,7 @@
if (GetPeer() == nullptr) {
return false;
}
- return jni::DecodeArtField(
- WellKnownClasses::java_lang_Thread_systemDaemon)->GetBoolean(GetPeer());
+ return WellKnownClasses::java_lang_Thread_systemDaemon->GetBoolean(GetPeer());
}
std::string Thread::StateAndFlagsAsHexString() const {
@@ -4606,7 +4779,9 @@
CHECK(self_->IsExceptionPending()) << *self_;
ObjPtr<mirror::Throwable> old_suppressed(excp_.Get());
excp_.Assign(self_->GetException());
- LOG(WARNING) << message << "Suppressing old exception: " << old_suppressed->Dump();
+ if (old_suppressed != nullptr) {
+ LOG(WARNING) << message << "Suppressing old exception: " << old_suppressed->Dump();
+ }
self_->ClearException();
}
diff --git a/runtime/thread.h b/runtime/thread.h
index dd8b061..6c74dd9 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -38,6 +38,7 @@
#include "handle.h"
#include "handle_scope.h"
#include "interpreter/interpreter_cache.h"
+#include "interpreter/shadow_frame.h"
#include "javaheapprof/javaheapsampler.h"
#include "jvalue.h"
#include "managed_stack.h"
@@ -48,7 +49,9 @@
#include "runtime_stats.h"
#include "thread_state.h"
-class BacktraceMap;
+namespace unwindstack {
+class AndroidLocalUnwinder;
+} // namespace unwindstack
namespace art {
@@ -188,7 +191,7 @@
// This should match RosAlloc::kNumThreadLocalSizeBrackets.
static constexpr size_t kNumRosAllocThreadLocalSizeBracketsInThread = 16;
-static constexpr size_t kSharedMethodHotnessThreshold = 0xffff;
+static constexpr size_t kSharedMethodHotnessThreshold = 0x1fff;
// Thread's stack layout for implicit stack overflow checks:
//
@@ -229,8 +232,11 @@
// Attaches the calling native thread to the runtime, returning the new native peer.
// Used to implement JNI AttachCurrentThread and AttachCurrentThreadAsDaemon calls.
- static Thread* Attach(const char* thread_name, bool as_daemon, jobject thread_group,
- bool create_peer);
+ static Thread* Attach(const char* thread_name,
+ bool as_daemon,
+ jobject thread_group,
+ bool create_peer,
+ bool should_run_callbacks);
// Attaches the calling native thread to the runtime, returning the new native peer.
static Thread* Attach(const char* thread_name, bool as_daemon, jobject thread_peer);
@@ -242,6 +248,9 @@
// TODO: mark as PURE so the compiler may coalesce and remove?
static Thread* Current();
+ // Get the thread from the JNI environment.
+ static Thread* ForEnv(JNIEnv* env);
+
// On a runnable thread, check for pending thread suspension request and handle if pending.
void AllowThreadSuspension() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -267,16 +276,28 @@
// Dumps a one-line summary of thread state (used for operator<<).
void ShortDump(std::ostream& os) const;
+ // Order of threads for ANRs (ANRs can be trimmed, so we print important ones first).
+ enum class DumpOrder : uint8_t {
+ kMain, // Always print the main thread first (there might not be one).
+ kBlocked, // Then print all threads that are blocked due to waiting on lock.
+ kLocked, // Then print all threads that are holding some lock already.
+ kDefault, // Print all other threads which might not be interesting for ANR.
+ };
+
// Dumps the detailed thread state and the thread stack (used for SIGQUIT).
- void Dump(std::ostream& os,
- bool dump_native_stack = true,
- BacktraceMap* backtrace_map = nullptr,
- bool force_dump_stack = false) const
+ DumpOrder Dump(std::ostream& os,
+ bool dump_native_stack = true,
+ bool force_dump_stack = false) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ DumpOrder Dump(std::ostream& os,
+ unwindstack::AndroidLocalUnwinder& unwinder,
+ bool dump_native_stack = true,
+ bool force_dump_stack = false) const
REQUIRES_SHARED(Locks::mutator_lock_);
- void DumpJavaStack(std::ostream& os,
- bool check_suspended = true,
- bool dump_locks = true) const
+ DumpOrder DumpJavaStack(std::ostream& os,
+ bool check_suspended = true,
+ bool dump_locks = true) const
REQUIRES_SHARED(Locks::mutator_lock_);
// Dumps the SIGQUIT per-thread header. 'thread' can be null for a non-attached thread, in which
@@ -373,11 +394,11 @@
void WaitForFlipFunction(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_);
gc::accounting::AtomicStack<mirror::Object>* GetThreadLocalMarkStack() {
- CHECK(kUseReadBarrier);
+ CHECK(gUseReadBarrier);
return tlsPtr_.thread_local_mark_stack;
}
void SetThreadLocalMarkStack(gc::accounting::AtomicStack<mirror::Object>* stack) {
- CHECK(kUseReadBarrier);
+ CHECK(gUseReadBarrier);
tlsPtr_.thread_local_mark_stack = stack;
}
@@ -546,8 +567,12 @@
// that needs to be dealt with, false otherwise.
bool ObserveAsyncException() REQUIRES_SHARED(Locks::mutator_lock_);
- // Find catch block and perform long jump to appropriate exception handle
- NO_RETURN void QuickDeliverException() REQUIRES_SHARED(Locks::mutator_lock_);
+ // Find catch block and perform long jump to appropriate exception handle. When
+ // is_method_exit_exception is true, the exception was thrown by the method exit callback and we
+ // should not send method unwind for the method on top of the stack since method exit callback was
+ // already called.
+ NO_RETURN void QuickDeliverException(bool is_method_exit_exception = false)
+ REQUIRES_SHARED(Locks::mutator_lock_);
Context* GetLongJumpContext();
void ReleaseLongJumpContext(Context* context) {
@@ -573,8 +598,8 @@
tlsPtr_.managed_stack.SetTopQuickFrame(top_method);
}
- void SetTopOfStackTagged(ArtMethod** top_method) {
- tlsPtr_.managed_stack.SetTopQuickFrameTagged(top_method);
+ void SetTopOfStackGenericJniTagged(ArtMethod** top_method) {
+ tlsPtr_.managed_stack.SetTopQuickFrameGenericJniTagged(top_method);
}
void SetTopOfShadowStack(ShadowFrame* top) {
@@ -708,6 +733,16 @@
jobjectArray output_array = nullptr, int* stack_depth = nullptr)
REQUIRES_SHARED(Locks::mutator_lock_);
+ static jint InternalStackTraceToStackFrameInfoArray(
+ const ScopedObjectAccessAlreadyRunnable& soa,
+ jlong mode, // See java.lang.StackStreamFactory for the mode flags
+ jobject internal,
+ jint startLevel,
+ jint batchSize,
+ jint startIndex,
+ jobjectArray output_array) // java.lang.StackFrameInfo[]
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
jobjectArray CreateAnnotatedStackTrace(const ScopedObjectAccessAlreadyRunnable& soa) const
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -715,6 +750,9 @@
return tlsPtr_.frame_id_to_shadow_frame != nullptr;
}
+ // This is done by GC using a checkpoint (or in a stop-the-world pause).
+ void SweepInterpreterCache(IsMarkedVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
void VisitRoots(RootVisitor* visitor, VisitRootFlags flags)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -739,6 +777,13 @@
}
template<PointerSize pointer_size>
+ static constexpr ThreadOffset<pointer_size> TidOffset() {
+ return ThreadOffset<pointer_size>(
+ OFFSETOF_MEMBER(Thread, tls32_) +
+ OFFSETOF_MEMBER(tls_32bit_sized_values, tid));
+ }
+
+ template<PointerSize pointer_size>
static constexpr ThreadOffset<pointer_size> InterruptedOffset() {
return ThreadOffset<pointer_size>(
OFFSETOF_MEMBER(Thread, tls32_) +
@@ -766,6 +811,13 @@
OFFSETOF_MEMBER(tls_32bit_sized_values, is_gc_marking));
}
+ template <PointerSize pointer_size>
+ static constexpr ThreadOffset<pointer_size> DeoptCheckRequiredOffset() {
+ return ThreadOffset<pointer_size>(
+ OFFSETOF_MEMBER(Thread, tls32_) +
+ OFFSETOF_MEMBER(tls_32bit_sized_values, is_deopt_check_required));
+ }
+
static constexpr size_t IsGcMarkingSize() {
return sizeof(tls32_.is_gc_marking);
}
@@ -1011,33 +1063,34 @@
}
bool GetIsGcMarking() const {
- CHECK(kUseReadBarrier);
+ CHECK(gUseReadBarrier);
return tls32_.is_gc_marking;
}
void SetIsGcMarkingAndUpdateEntrypoints(bool is_marking);
+ bool IsDeoptCheckRequired() const { return tls32_.is_deopt_check_required; }
+
+ void SetDeoptCheckRequired(bool flag) { tls32_.is_deopt_check_required = flag; }
+
bool GetWeakRefAccessEnabled() const; // Only safe for current thread.
void SetWeakRefAccessEnabled(bool enabled) {
- CHECK(kUseReadBarrier);
+ DCHECK(gUseReadBarrier);
WeakRefAccessState new_state = enabled ?
WeakRefAccessState::kEnabled : WeakRefAccessState::kDisabled;
tls32_.weak_ref_access_enabled.store(new_state, std::memory_order_release);
}
uint32_t GetDisableThreadFlipCount() const {
- CHECK(kUseReadBarrier);
return tls32_.disable_thread_flip_count;
}
void IncrementDisableThreadFlipCount() {
- CHECK(kUseReadBarrier);
++tls32_.disable_thread_flip_count;
}
void DecrementDisableThreadFlipCount() {
- CHECK(kUseReadBarrier);
DCHECK_GT(tls32_.disable_thread_flip_count, 0U);
--tls32_.disable_thread_flip_count;
}
@@ -1091,7 +1144,8 @@
void AssertHasDeoptimizationContext()
REQUIRES_SHARED(Locks::mutator_lock_);
void PushStackedShadowFrame(ShadowFrame* sf, StackedShadowFrameType type);
- ShadowFrame* PopStackedShadowFrame(StackedShadowFrameType type, bool must_be_present = true);
+ ShadowFrame* PopStackedShadowFrame();
+ ShadowFrame* MaybePopDeoptimizedStackedShadowFrame();
// For debugger, find the shadow frame that corresponds to a frame id.
// Or return null if there is none.
@@ -1152,6 +1206,22 @@
tlsPtr_.deps_or_stack_trace_sample.verifier_deps = verifier_deps;
}
+ uintptr_t* GetMethodTraceBuffer() { return tlsPtr_.method_trace_buffer; }
+
+ size_t* GetMethodTraceIndexPtr() { return &tlsPtr_.method_trace_buffer_index; }
+
+ uintptr_t* SetMethodTraceBuffer(uintptr_t* buffer) {
+ return tlsPtr_.method_trace_buffer = buffer;
+ }
+
+ void ResetMethodTraceBuffer() {
+ if (tlsPtr_.method_trace_buffer != nullptr) {
+ delete[] tlsPtr_.method_trace_buffer;
+ }
+ tlsPtr_.method_trace_buffer = nullptr;
+ tlsPtr_.method_trace_buffer_index = 0;
+ }
+
uint64_t GetTraceClockBase() const {
return tls64_.trace_clock_base;
}
@@ -1206,6 +1276,10 @@
DCHECK_LE(tlsPtr_.thread_local_end, tlsPtr_.thread_local_limit);
}
+ // Called from Concurrent mark-compact GC to slide the TLAB pointers backwards
+ // to adjust to post-compact addresses.
+ void AdjustTlab(size_t slide_bytes);
+
// Doesn't check that there is room.
mirror::Object* AllocTlab(size_t bytes);
void SetTlab(uint8_t* start, uint8_t* end, uint8_t* limit);
@@ -1298,11 +1372,12 @@
bool IncrementMakeVisiblyInitializedCounter() {
tls32_.make_visibly_initialized_counter += 1u;
- return tls32_.make_visibly_initialized_counter == kMakeVisiblyInitializedCounterTriggerCount;
- }
-
- void ClearMakeVisiblyInitializedCounter() {
- tls32_.make_visibly_initialized_counter = 0u;
+ DCHECK_LE(tls32_.make_visibly_initialized_counter, kMakeVisiblyInitializedCounterTriggerCount);
+ if (tls32_.make_visibly_initialized_counter == kMakeVisiblyInitializedCounterTriggerCount) {
+ tls32_.make_visibly_initialized_counter = 0u;
+ return true;
+ }
+ return false;
}
void PushVerifier(verifier::MethodVerifier* verifier);
@@ -1347,10 +1422,9 @@
// Set to the read barrier marking entrypoints to be non-null.
void SetReadBarrierEntrypoints();
- static jobject CreateCompileTimePeer(JNIEnv* env,
- const char* name,
- bool as_daemon,
- jobject thread_group)
+ ObjPtr<mirror::Object> CreateCompileTimePeer(const char* name,
+ bool as_daemon,
+ jobject thread_group)
REQUIRES_SHARED(Locks::mutator_lock_);
ALWAYS_INLINE InterpreterCache* GetInterpreterCache() {
@@ -1413,7 +1487,7 @@
private:
explicit Thread(bool daemon);
~Thread() REQUIRES(!Locks::mutator_lock_, !Locks::thread_suspend_count_lock_);
- void Destroy();
+ void Destroy(bool should_run_callbacks);
// Deletes and clears the tlsPtr_.jpeer field. Done in a way so that both it and opeer cannot be
// observed to be set at the same time by instrumentation.
@@ -1424,16 +1498,16 @@
template <typename PeerAction>
static Thread* Attach(const char* thread_name,
bool as_daemon,
- PeerAction p);
+ PeerAction p,
+ bool should_run_callbacks);
void CreatePeer(const char* name, bool as_daemon, jobject thread_group);
template<bool kTransactionActive>
- static void InitPeer(ScopedObjectAccessAlreadyRunnable& soa,
- ObjPtr<mirror::Object> peer,
- jboolean thread_is_daemon,
- jobject thread_group,
- jobject thread_name,
+ static void InitPeer(ObjPtr<mirror::Object> peer,
+ bool as_daemon,
+ ObjPtr<mirror::Object> thread_group,
+ ObjPtr<mirror::String> thread_name,
jint thread_priority)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -1479,10 +1553,14 @@
void VerifyStackImpl() REQUIRES_SHARED(Locks::mutator_lock_);
void DumpState(std::ostream& os) const REQUIRES_SHARED(Locks::mutator_lock_);
- void DumpStack(std::ostream& os,
- bool dump_native_stack = true,
- BacktraceMap* backtrace_map = nullptr,
- bool force_dump_stack = false) const
+ DumpOrder DumpStack(std::ostream& os,
+ bool dump_native_stack = true,
+ bool force_dump_stack = false) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+ DumpOrder DumpStack(std::ostream& os,
+ unwindstack::AndroidLocalUnwinder& unwinder,
+ bool dump_native_stack = true,
+ bool force_dump_stack = false) const
REQUIRES_SHARED(Locks::mutator_lock_);
// Out-of-line conveniences for debugging in gdb.
@@ -1490,12 +1568,13 @@
// Like Thread::Dump(std::cerr).
void DumpFromGdb() const REQUIRES_SHARED(Locks::mutator_lock_);
+ // A wrapper around CreateCallback used when userfaultfd GC is used to
+ // identify the GC by stacktrace.
+ static NO_INLINE void* CreateCallbackWithUffdGc(void* arg);
static void* CreateCallback(void* arg);
- void HandleUncaughtExceptions(ScopedObjectAccessAlreadyRunnable& soa)
- REQUIRES_SHARED(Locks::mutator_lock_);
- void RemoveFromThreadGroup(ScopedObjectAccessAlreadyRunnable& soa)
- REQUIRES_SHARED(Locks::mutator_lock_);
+ void HandleUncaughtExceptions() REQUIRES_SHARED(Locks::mutator_lock_);
+ void RemoveFromThreadGroup() REQUIRES_SHARED(Locks::mutator_lock_);
// Initialize a thread.
//
@@ -1563,9 +1642,6 @@
template <bool kPrecise>
void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
- static void SweepInterpreterCaches(IsMarkedVisitor* visitor)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
static bool IsAotCompiler();
void ReleaseLongJumpContextInternal();
@@ -1712,6 +1788,7 @@
thread_exit_check_count(0),
is_transitioning_to_runnable(false),
is_gc_marking(false),
+ is_deopt_check_required(false),
weak_ref_access_enabled(WeakRefAccessState::kVisiblyEnabled),
disable_thread_flip_count(0),
user_code_suspend_count(0),
@@ -1766,6 +1843,12 @@
// GC roots.
bool32_t is_gc_marking;
+ // True if we need to check for deoptimization when returning from the runtime functions. This
+ // is required only when a class is redefined to prevent executing code that has field offsets
+ // embedded. For non-debuggable apps redefinition is not allowed and this flag should always be
+ // set to false.
+ bool32_t is_deopt_check_required;
+
// Thread "interrupted" status; stays raised until queried or thrown.
Atomic<bool32_t> interrupted;
@@ -1873,7 +1956,9 @@
method_verifier(nullptr),
thread_local_mark_stack(nullptr),
async_exception(nullptr),
- top_reflective_handle_scope(nullptr) {
+ top_reflective_handle_scope(nullptr),
+ method_trace_buffer(nullptr),
+ method_trace_buffer_index(0) {
std::fill(held_mutexes, held_mutexes + kLockLevelCount, nullptr);
}
@@ -2036,6 +2121,12 @@
// Top of the linked-list for reflective-handle scopes or null if none.
BaseReflectiveHandleScope* top_reflective_handle_scope;
+
+ // Pointer to a thread-local buffer for method tracing.
+ uintptr_t* method_trace_buffer;
+
+ // The index of the next free entry in method_trace_buffer.
+ size_t method_trace_buffer_index;
} tlsPtr_;
// Small thread-local cache to be used from the interpreter.
@@ -2068,8 +2159,10 @@
SafeMap<std::string, std::unique_ptr<TLSData>, std::less<>> custom_tls_
GUARDED_BY(Locks::custom_tls_lock_);
-#ifndef __BIONIC__
- __attribute__((tls_model("initial-exec")))
+#if !defined(__BIONIC__)
+#if !defined(ANDROID_HOST_MUSL)
+ __attribute__((tls_model("initial-exec")))
+#endif
static thread_local Thread* self_tls_;
#endif
@@ -2152,17 +2245,18 @@
class ScopedStackedShadowFramePusher {
public:
- ScopedStackedShadowFramePusher(Thread* self, ShadowFrame* sf, StackedShadowFrameType type)
- : self_(self), type_(type) {
- self_->PushStackedShadowFrame(sf, type);
+ ScopedStackedShadowFramePusher(Thread* self, ShadowFrame* sf) : self_(self), sf_(sf) {
+ DCHECK_EQ(sf->GetLink(), nullptr);
+ self_->PushStackedShadowFrame(sf, StackedShadowFrameType::kShadowFrameUnderConstruction);
}
~ScopedStackedShadowFramePusher() {
- self_->PopStackedShadowFrame(type_);
+ ShadowFrame* sf = self_->PopStackedShadowFrame();
+ DCHECK_EQ(sf, sf_);
}
private:
Thread* const self_;
- const StackedShadowFrameType type_;
+ ShadowFrame* const sf_;
DISALLOW_COPY_AND_ASSIGN(ScopedStackedShadowFramePusher);
};
@@ -2186,13 +2280,13 @@
explicit ScopedTransitioningToRunnable(Thread* self)
: self_(self) {
DCHECK_EQ(self, Thread::Current());
- if (kUseReadBarrier) {
+ if (gUseReadBarrier) {
self_->SetIsTransitioningToRunnable(true);
}
}
~ScopedTransitioningToRunnable() {
- if (kUseReadBarrier) {
+ if (gUseReadBarrier) {
self_->SetIsTransitioningToRunnable(false);
}
}
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 6482e72..8fa7bde 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -20,14 +20,17 @@
#include <sys/types.h>
#include <unistd.h>
+#include <map>
#include <sstream>
+#include <tuple>
#include <vector>
#include "android-base/stringprintf.h"
-#include "backtrace/BacktraceMap.h"
#include "nativehelper/scoped_local_ref.h"
#include "nativehelper/scoped_utf_chars.h"
+#include "unwindstack/AndroidUnwinder.h"
+#include "art_field-inl.h"
#include "base/aborting.h"
#include "base/histogram-inl.h"
#include "base/mutex-inl.h"
@@ -42,8 +45,10 @@
#include "gc_root.h"
#include "jni/jni_internal.h"
#include "lock_word.h"
+#include "mirror/string.h"
#include "monitor.h"
#include "native_stack_dump.h"
+#include "obj_ptr-inl.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"
#include "trace.h"
@@ -101,12 +106,11 @@
Runtime::Current()->DetachCurrentThread();
}
WaitForOtherNonDaemonThreadsToExit();
- // Disable GC and wait for GC to complete in case there are still daemon threads doing
- // allocations.
+ // The only caller of this function, ~Runtime, has already disabled GC and
+ // ensured that the last GC is finished.
gc::Heap* const heap = Runtime::Current()->GetHeap();
- heap->DisableGCForShutdown();
- // In case a GC is in progress, wait for it to finish.
- heap->WaitForGcToComplete(gc::kGcCauseBackground, Thread::Current());
+ CHECK(heap->IsGCDisabledForShutdown());
+
// TODO: there's an unaddressed race here where a thread may attach during shutdown, see
// Thread::Init.
SuspendAllDaemonThreadsForShutdown();
@@ -124,10 +128,10 @@
void ThreadList::DumpNativeStacks(std::ostream& os) {
MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
- std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(getpid()));
+ unwindstack::AndroidLocalUnwinder unwinder;
for (const auto& thread : list_) {
os << "DUMPING THREAD " << thread->GetTid() << "\n";
- DumpNativeStack(os, thread->GetTid(), map.get(), "\t");
+ DumpNativeStack(os, unwinder, thread->GetTid(), "\t");
os << "\n";
}
}
@@ -153,7 +157,7 @@
// refactor DumpState to avoid skipping analysis.
Thread::DumpState(os, nullptr, tid);
if (dump_native_stack) {
- DumpNativeStack(os, tid, nullptr, " native: ");
+ DumpNativeStack(os, tid, " native: ");
}
os << std::endl;
}
@@ -190,16 +194,14 @@
// A closure used by Thread::Dump.
class DumpCheckpoint final : public Closure {
public:
- DumpCheckpoint(std::ostream* os, bool dump_native_stack)
- : os_(os),
+ DumpCheckpoint(bool dump_native_stack)
+ : lock_("Dump checkpoint lock", kGenericBottomLock),
+ os_(),
// Avoid verifying count in case a thread doesn't end up passing through the barrier.
// This avoids a SIGABRT that would otherwise happen in the destructor.
barrier_(0, /*verify_count_on_shutdown=*/false),
- backtrace_map_(dump_native_stack ? BacktraceMap::Create(getpid()) : nullptr),
+ unwinder_(std::vector<std::string>{}, std::vector<std::string> {"oat", "odex"}),
dump_native_stack_(dump_native_stack) {
- if (backtrace_map_ != nullptr) {
- backtrace_map_->SetSuffixesToIgnore(std::vector<std::string> { "oat", "odex" });
- }
}
void Run(Thread* thread) override {
@@ -208,18 +210,28 @@
Thread* self = Thread::Current();
CHECK(self != nullptr);
std::ostringstream local_os;
+ Thread::DumpOrder dump_order;
{
ScopedObjectAccess soa(self);
- thread->Dump(local_os, dump_native_stack_, backtrace_map_.get());
+ dump_order = thread->Dump(local_os, unwinder_, dump_native_stack_);
}
{
- // Use the logging lock to ensure serialization when writing to the common ostream.
- MutexLock mu(self, *Locks::logging_lock_);
- *os_ << local_os.str() << std::endl;
+ MutexLock mu(self, lock_);
+ // Sort, so that the most interesting threads for ANR are printed first (ANRs can be trimmed).
+ std::pair<Thread::DumpOrder, uint32_t> sort_key(dump_order, thread->GetThreadId());
+ os_.emplace(sort_key, std::move(local_os));
}
barrier_.Pass(self);
}
+ // Called at the end to print all the dumps in sequential prioritized order.
+ void Dump(Thread* self, std::ostream& os) {
+ MutexLock mu(self, lock_);
+ for (const auto& it : os_) {
+ os << it.second.str() << std::endl;
+ }
+ }
+
void WaitForThreadsToRunThroughCheckpoint(size_t threads_running_checkpoint) {
Thread* self = Thread::Current();
ScopedThreadStateChange tsc(self, ThreadState::kWaitingForCheckPointsToRun);
@@ -232,12 +244,14 @@
}
private:
- // The common stream that will accumulate all the dumps.
- std::ostream* const os_;
+ // Storage for the per-thread dumps (guarded by lock since they are generated in parallel).
+ // Map is used to obtain sorted order. The key is unique, but use multimap just in case.
+ Mutex lock_;
+ std::multimap<std::pair<Thread::DumpOrder, uint32_t>, std::ostringstream> os_ GUARDED_BY(lock_);
// The barrier to be passed through and for the requestor to wait upon.
Barrier barrier_;
// A backtrace map, so that all threads use a shared info and don't reacquire/parse separately.
- std::unique_ptr<BacktraceMap> backtrace_map_;
+ unwindstack::AndroidLocalUnwinder unwinder_;
// Whether we should dump the native stack.
const bool dump_native_stack_;
};
@@ -249,7 +263,7 @@
os << "DALVIK THREADS (" << list_.size() << "):\n";
}
if (self != nullptr) {
- DumpCheckpoint checkpoint(&os, dump_native_stack);
+ DumpCheckpoint checkpoint(dump_native_stack);
size_t threads_running_checkpoint;
{
// Use SOA to prevent deadlocks if multiple threads are calling Dump() at the same time.
@@ -259,6 +273,7 @@
if (threads_running_checkpoint != 0) {
checkpoint.WaitForThreadsToRunThroughCheckpoint(threads_running_checkpoint);
}
+ checkpoint.Dump(self, os);
} else {
DumpUnattachedThreads(os, dump_native_stack);
}
@@ -486,7 +501,6 @@
// Assume it's stuck and safe to dump its stack.
thread->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT),
/*dump_native_stack=*/ true,
- /*backtrace_map=*/ nullptr,
/*force_dump_stack=*/ true);
}
}
@@ -851,20 +865,16 @@
return true;
}
-static void ThreadSuspendByPeerWarning(Thread* self,
+static void ThreadSuspendByPeerWarning(ScopedObjectAccess& soa,
LogSeverity severity,
const char* message,
- jobject peer) {
- JNIEnvExt* env = self->GetJniEnv();
- ScopedLocalRef<jstring>
- scoped_name_string(env, static_cast<jstring>(env->GetObjectField(
- peer, WellKnownClasses::java_lang_Thread_name)));
- ScopedUtfChars scoped_name_chars(env, scoped_name_string.get());
- if (scoped_name_chars.c_str() == nullptr) {
- LOG(severity) << message << ": " << peer;
- env->ExceptionClear();
+ jobject peer) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Object> name =
+ WellKnownClasses::java_lang_Thread_name->GetObject(soa.Decode<mirror::Object>(peer));
+ if (name == nullptr) {
+ LOG(severity) << message << ": " << peer;
} else {
- LOG(severity) << message << ": " << peer << ":" << scoped_name_chars.c_str();
+ LOG(severity) << message << ": " << peer << ":" << name->AsString()->ToModifiedUtf8();
}
}
@@ -902,7 +912,7 @@
reason);
DCHECK(updated);
}
- ThreadSuspendByPeerWarning(self,
+ ThreadSuspendByPeerWarning(soa,
::android::base::WARNING,
"No such thread for suspend",
peer);
@@ -955,7 +965,7 @@
const uint64_t total_delay = NanoTime() - start_time;
if (total_delay >= thread_suspend_timeout_ns_) {
if (suspended_thread == nullptr) {
- ThreadSuspendByPeerWarning(self,
+ ThreadSuspendByPeerWarning(soa,
::android::base::FATAL,
"Failed to issue suspend request",
peer);
@@ -967,7 +977,7 @@
// Explicitly release thread_suspend_count_lock_; we haven't held it for long, so
// seeing threads blocked on it is not informative.
Locks::thread_suspend_count_lock_->Unlock(self);
- ThreadSuspendByPeerWarning(self,
+ ThreadSuspendByPeerWarning(soa,
::android::base::FATAL,
"Thread suspension timed out",
peer);
@@ -1275,7 +1285,7 @@
}
CHECK(!Contains(self));
list_.push_back(self);
- if (kUseReadBarrier) {
+ if (gUseReadBarrier) {
gc::collector::ConcurrentCopying* const cc =
Runtime::Current()->GetHeap()->ConcurrentCopyingCollector();
// Initialize according to the state of the CC collector.
@@ -1287,10 +1297,14 @@
}
}
-void ThreadList::Unregister(Thread* self) {
+void ThreadList::Unregister(Thread* self, bool should_run_callbacks) {
DCHECK_EQ(self, Thread::Current());
CHECK_NE(self->GetState(), ThreadState::kRunnable);
Locks::mutator_lock_->AssertNotHeld(self);
+ if (self->tls32_.disable_thread_flip_count != 0) {
+ LOG(FATAL) << "Incomplete PrimitiveArrayCritical section at exit: " << *self << "count = "
+ << self->tls32_.disable_thread_flip_count;
+ }
VLOG(threads) << "ThreadList::Unregister() " << *self;
@@ -1304,7 +1318,7 @@
// causes the threads to join. It is important to do this after incrementing unregistering_count_
// since we want the runtime to wait for the daemon threads to exit before deleting the thread
// list.
- self->Destroy();
+ self->Destroy(should_run_callbacks);
// If tracing, remember thread id and name before thread exits.
Trace::StoreExitingThreadInfo(self);
@@ -1320,7 +1334,7 @@
std::string thread_name;
self->GetThreadName(thread_name);
std::ostringstream os;
- DumpNativeStack(os, GetTid(), nullptr, " native: ", nullptr);
+ DumpNativeStack(os, GetTid(), " native: ", nullptr);
LOG(ERROR) << "Request to unregister unattached thread " << thread_name << "\n" << os.str();
break;
} else {
@@ -1414,6 +1428,13 @@
}
}
+void ThreadList::SweepInterpreterCaches(IsMarkedVisitor* visitor) const {
+ MutexLock mu(Thread::Current(), *Locks::thread_list_lock_);
+ for (const auto& thread : list_) {
+ thread->SweepInterpreterCache(visitor);
+ }
+}
+
uint32_t ThreadList::AllocThreadId(Thread* self) {
MutexLock mu(self, *Locks::allocated_thread_ids_lock_);
for (size_t i = 0; i < allocated_ids_.size(); ++i) {
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index 29b0c52..c1ffe9e 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -153,7 +153,7 @@
REQUIRES(!Locks::mutator_lock_,
!Locks::thread_list_lock_,
!Locks::thread_suspend_count_lock_);
- void Unregister(Thread* self)
+ void Unregister(Thread* self, bool should_run_callbacks)
REQUIRES(!Locks::mutator_lock_,
!Locks::thread_list_lock_,
!Locks::thread_suspend_count_lock_);
@@ -167,6 +167,9 @@
void VisitReflectiveTargets(ReflectiveValueVisitor* visitor) const REQUIRES(Locks::mutator_lock_);
+ void SweepInterpreterCaches(IsMarkedVisitor* visitor) const
+ REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_);
+
// Return a copy of the thread list.
std::list<Thread*> GetList() REQUIRES(Locks::thread_list_lock_) {
return list_;
diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc
index 57d7f61..92ac845 100644
--- a/runtime/thread_pool.cc
+++ b/runtime/thread_pool.cc
@@ -119,6 +119,12 @@
void* ThreadPoolWorker::Callback(void* arg) {
ThreadPoolWorker* worker = reinterpret_cast<ThreadPoolWorker*>(arg);
Runtime* runtime = Runtime::Current();
+ // Don't run callbacks for ThreadPoolWorkers. These are created for JITThreadPool and
+ // HeapThreadPool and are purely internal threads of the runtime and we don't need to run
+ // callbacks for the thread attach / detach listeners.
+ // (b/251163712) Calling callbacks for heap thread pool workers causes deadlocks in some libjdwp
+ // tests. Deadlocks happen when a GC thread is attached while libjdwp holds the event handler
+ // lock for an event that triggers an entrypoint update from deopt manager.
CHECK(runtime->AttachCurrentThread(
worker->name_.c_str(),
true,
@@ -129,13 +135,14 @@
// rely on being able to (for example) wait for all threads to finish some task. If debuggers
// are suspending these threads that might not be possible.
worker->thread_pool_->create_peers_ ? runtime->GetSystemThreadGroup() : nullptr,
- worker->thread_pool_->create_peers_));
+ worker->thread_pool_->create_peers_,
+ /* should_run_callbacks= */ false));
worker->thread_ = Thread::Current();
// Mark thread pool workers as runtime-threads.
worker->thread_->SetIsRuntimeThread(true);
// Do work until its time to shut down.
worker->Run();
- runtime->DetachCurrentThread();
+ runtime->DetachCurrentThread(/* should_run_callbacks= */ false);
return nullptr;
}
@@ -246,6 +253,11 @@
started_ = false;
}
+bool ThreadPool::HasStarted(Thread* self) {
+ MutexLock mu(self, task_queue_lock_);
+ return started_;
+}
+
Task* ThreadPool::GetTask(Thread* self) {
MutexLock mu(self, task_queue_lock_);
while (!IsShuttingDown()) {
diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h
index b9e5a97..5c75733 100644
--- a/runtime/thread_pool.h
+++ b/runtime/thread_pool.h
@@ -123,6 +123,9 @@
// Do not allow workers to grab any new tasks.
void StopWorkers(Thread* self) REQUIRES(!task_queue_lock_);
+ // Returns if the thread pool has started.
+ bool HasStarted(Thread* self) REQUIRES(!task_queue_lock_);
+
// Add a new task, the first available started worker will process it. Does not delete the task
// after running it, it is the caller's responsibility.
void AddTask(Thread* self, Task* task) REQUIRES(!task_queue_lock_);
diff --git a/runtime/trace.cc b/runtime/trace.cc
index ec61726..387816f 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -393,17 +393,12 @@
// Enable count of allocs if specified in the flags.
bool enable_stats = false;
- if (runtime->GetJit() != nullptr) {
- // TODO b/110263880 It would be better if we didn't need to do this.
- // Since we need to hold the method entrypoint across a suspend to ensure instrumentation
- // hooks are called correctly we have to disable jit-gc to ensure that the entrypoint doesn't
- // go away. Furthermore we need to leave this off permanently since one could get the same
- // effect by causing this to be toggled on and off.
- runtime->GetJit()->GetCodeCache()->SetGarbageCollectCode(false);
- }
-
// Create Trace object.
{
+ // Suspend JIT here since we are switching runtime to debuggable. Debuggable runtimes cannot use
+ // JITed code from before so we need to invalidated all JITed code here. Enter suspend JIT scope
+ // to prevent any races with ongoing JIT compilations.
+ jit::ScopedJitSuspend suspend_jit;
// Required since EnableMethodTracing calls ConfigureStubs which visits class linker classes.
gc::ScopedGCCriticalSection gcs(self,
gc::kGcCauseInstrumentation,
@@ -421,6 +416,17 @@
"Sampling profiler thread");
the_trace_->interval_us_ = interval_us;
} else {
+ if (!runtime->IsJavaDebuggable()) {
+ art::jit::Jit* jit = runtime->GetJit();
+ if (jit != nullptr) {
+ jit->GetCodeCache()->InvalidateAllCompiledCode();
+ jit->GetCodeCache()->TransitionToDebuggable();
+ jit->GetJitCompiler()->SetDebuggableCompilerOption(true);
+ }
+ runtime->SetRuntimeDebugState(art::Runtime::RuntimeDebugState::kJavaDebuggable);
+ runtime->GetInstrumentation()->UpdateEntrypointsForDebuggable();
+ runtime->DeoptimizeBootImage();
+ }
runtime->GetInstrumentation()->AddListener(
the_trace_,
instrumentation::Instrumentation::kMethodEntered |
@@ -431,8 +437,8 @@
// we know that inlining and other problematic optimizations are disabled. We might just
// want to use the trampolines anyway since it is faster. It makes the story with disabling
// jit-gc more complex though.
- runtime->GetInstrumentation()->EnableMethodTracing(
- kTracerInstrumentationKey, /*needs_interpreter=*/!runtime->IsJavaDebuggable());
+ runtime->GetInstrumentation()->EnableMethodTracing(kTracerInstrumentationKey,
+ /*needs_interpreter=*/false);
}
}
}
@@ -474,6 +480,7 @@
gc::ScopedGCCriticalSection gcs(self,
gc::kGcCauseInstrumentation,
gc::kCollectorTypeInstrumentation);
+ jit::ScopedJitSuspend suspend_jit;
ScopedSuspendAll ssa(__FUNCTION__);
if (the_trace->trace_mode_ == TraceMode::kSampling) {
@@ -486,6 +493,14 @@
instrumentation::Instrumentation::kMethodExited |
instrumentation::Instrumentation::kMethodUnwind);
runtime->GetInstrumentation()->DisableMethodTracing(kTracerInstrumentationKey);
+ if (!runtime->IsJavaDebuggableAtInit() && !runtime->IsShuttingDown(self)) {
+ art::jit::Jit* jit = runtime->GetJit();
+ if (jit != nullptr) {
+ jit->GetCodeCache()->InvalidateAllCompiledCode();
+ jit->GetJitCompiler()->SetDebuggableCompilerOption(false);
+ }
+ runtime->SetRuntimeDebugState(art::Runtime::RuntimeDebugState::kNonJavaDebuggable);
+ }
}
}
// At this point, code may read buf_ as it's writers are shutdown
@@ -515,6 +530,11 @@
}
}
+void Trace::FlushThreadBuffer(Thread* self) {
+ MutexLock mu(self, *Locks::trace_lock_);
+ the_trace_->FlushStreamingBuffer(self);
+}
+
void Trace::Abort() {
// Do not write anything anymore.
StopTracing(false, false);
@@ -548,6 +568,10 @@
}
static constexpr size_t kMinBufSize = 18U; // Trace header is up to 18B.
+// Size of per-thread buffer size. The value is chosen arbitrarily. This value
+// should be greater than kMinBufSize.
+static constexpr size_t kPerThreadBufSize = 512 * 1024;
+static_assert(kPerThreadBufSize > kMinBufSize);
Trace::Trace(File* trace_file,
size_t buffer_size,
@@ -556,11 +580,16 @@
TraceMode trace_mode)
: trace_file_(trace_file),
buf_(new uint8_t[std::max(kMinBufSize, buffer_size)]()),
- flags_(flags), trace_output_mode_(output_mode), trace_mode_(trace_mode),
+ flags_(flags),
+ trace_output_mode_(output_mode),
+ trace_mode_(trace_mode),
clock_source_(default_clock_source_),
buffer_size_(std::max(kMinBufSize, buffer_size)),
- start_time_(MicroTime()), clock_overhead_ns_(GetClockOverheadNanoSeconds()),
- overflow_(false), interval_us_(0), streaming_lock_(nullptr),
+ start_time_(MicroTime()),
+ clock_overhead_ns_(GetClockOverheadNanoSeconds()),
+ overflow_(false),
+ interval_us_(0),
+ streaming_lock_(nullptr),
unique_methods_lock_(new Mutex("unique methods lock", kTracingUniqueMethodsLock)) {
CHECK_IMPLIES(trace_file == nullptr, output_mode == TraceOutputMode::kDDMS);
@@ -584,7 +613,12 @@
if (output_mode == TraceOutputMode::kStreaming) {
streaming_lock_ = new Mutex("tracing lock", LockLevel::kTracingStreamingLock);
- seen_threads_.reset(new ThreadIDBitSet());
+ // Flush the header information to the file. We use a per thread buffer, so
+ // it is easier to just write the header information directly to file.
+ if (!trace_file_->WriteFully(buf_.get(), kTraceHeaderLength)) {
+ PLOG(WARNING) << "Failed streaming a tracing event.";
+ }
+ cur_offset_.store(0, std::memory_order_relaxed);
}
}
@@ -664,20 +698,30 @@
std::string header(os.str());
if (trace_output_mode_ == TraceOutputMode::kStreaming) {
- // Protect access to buf_ and satisfy sanitizer for calls to WriteBuf / FlushBuf.
- MutexLock mu(Thread::Current(), *streaming_lock_);
+ // Flush thread specific buffer from all threads.
+ {
+ MutexLock tl_lock(Thread::Current(), *Locks::thread_list_lock_);
+ for (Thread* thread : Runtime::Current()->GetThreadList()->GetList()) {
+ if (thread->GetMethodTraceBuffer() != nullptr) {
+ FlushStreamingBuffer(thread);
+ thread->ResetMethodTraceBuffer();
+ }
+ }
+ }
+ // It is expected that this method is called when all other threads are suspended, so there
+ // cannot be any writes to trace_file_ after finish tracing.
// Write a special token to mark the end of trace records and the start of
// trace summary.
uint8_t buf[7];
Append2LE(buf, 0);
buf[2] = kOpTraceSummary;
Append4LE(buf + 3, static_cast<uint32_t>(header.length()));
- WriteToBuf(buf, sizeof(buf));
// Write the trace summary. The summary is identical to the file header when
// the output mode is not streaming (except for methods).
- WriteToBuf(reinterpret_cast<const uint8_t*>(header.c_str()), header.length());
- // Flush the buffer, which may include some trace records before the summary.
- FlushBuf();
+ if (!trace_file_->WriteFully(buf, sizeof(buf)) ||
+ !trace_file_->WriteFully(header.c_str(), header.length())) {
+ PLOG(WARNING) << "Failed streaming a tracing event.";
+ }
} else {
if (trace_file_.get() == nullptr) {
std::vector<uint8_t> data;
@@ -757,7 +801,6 @@
}
void Trace::MethodUnwind(Thread* thread,
- Handle<mirror::Object> this_object ATTRIBUTE_UNUSED,
ArtMethod* method,
uint32_t dex_pc ATTRIBUTE_UNUSED) {
uint32_t thread_clock_diff = 0;
@@ -819,18 +862,6 @@
return false;
}
-bool Trace::RegisterThread(Thread* thread) {
- pid_t tid = thread->GetTid();
- CHECK_LT(0U, static_cast<uint32_t>(tid));
- CHECK_LT(static_cast<uint32_t>(tid), kMaxThreadIdNumber);
-
- if (!(*seen_threads_)[tid]) {
- seen_threads_->set(tid);
- return true;
- }
- return false;
-}
-
std::string Trace::GetMethodLine(ArtMethod* method) {
method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize);
return StringPrintf("%#x\t%s\t%s\t%s\t%s\n", (EncodeTraceMethod(method) << TraceActionBits),
@@ -838,54 +869,155 @@
method->GetSignature().ToString().c_str(), method->GetDeclaringClassSourceFile());
}
-void Trace::WriteToBuf(const uint8_t* src, size_t src_size) {
- // Updates to cur_offset_ are done under the streaming_lock_ here as in streaming mode.
- int32_t old_offset = cur_offset_.load(std::memory_order_relaxed);
- int32_t new_offset = old_offset + static_cast<int32_t>(src_size);
- if (dchecked_integral_cast<size_t>(new_offset) > buffer_size_) {
- // Flush buffer.
- if (!trace_file_->WriteFully(buf_.get(), old_offset)) {
- PLOG(WARNING) << "Failed streaming a tracing event.";
- }
+void Trace::RecordStreamingMethodEvent(Thread* thread,
+ ArtMethod* method,
+ TraceAction action,
+ uint32_t thread_clock_diff,
+ uint32_t wall_clock_diff) {
+ uintptr_t* method_trace_buffer = thread->GetMethodTraceBuffer();
+ size_t* current_offset = thread->GetMethodTraceIndexPtr();
+ // Initialize the buffer lazily. It's just simpler to keep the creation at one place.
+ if (method_trace_buffer == nullptr) {
+ method_trace_buffer = new uintptr_t[std::max(kMinBufSize, kPerThreadBufSize)]();
+ thread->SetMethodTraceBuffer(method_trace_buffer);
+ *current_offset = 0;
- // Check whether the data is too large for the buffer, then write immediately.
- if (src_size >= buffer_size_) {
- if (!trace_file_->WriteFully(src, src_size)) {
+ // This is the first event from this thread, so first record information about the thread.
+ std::string thread_name;
+ thread->GetThreadName(thread_name);
+ static constexpr size_t kThreadNameHeaderSize = 7;
+ uint8_t header[kThreadNameHeaderSize];
+ Append2LE(header, 0);
+ header[2] = kOpNewThread;
+ // We use only 16 bits to encode thread id. On Android, we don't expect to use more than
+ // 16-bits for a Tid. For 32-bit platforms it is always ensured we use less than 16 bits.
+ // See __check_max_thread_id in bionic for more details. Even on 64-bit the max threads
+ // is currently less than 65536.
+ // TODO(mythria): On host, we know thread ids can be greater than 16 bits. Consider adding
+ // a map similar to method ids.
+ DCHECK(!kIsTargetBuild || thread->GetTid() < (1 << 16));
+ Append2LE(header + 3, static_cast<uint16_t>(thread->GetTid()));
+ Append2LE(header + 5, static_cast<uint16_t>(thread_name.length()));
+
+ {
+ MutexLock mu(Thread::Current(), *streaming_lock_);
+ if (!trace_file_->WriteFully(header, kThreadNameHeaderSize) ||
+ !trace_file_->WriteFully(reinterpret_cast<const uint8_t*>(thread_name.c_str()),
+ thread_name.length())) {
PLOG(WARNING) << "Failed streaming a tracing event.";
}
- cur_offset_.store(0, std::memory_order_relaxed); // Buffer is empty now.
- return;
+ }
+ }
+
+ size_t required_entries = (clock_source_ == TraceClockSource::kDual) ? 4 : 3;
+ if (*current_offset + required_entries >= kPerThreadBufSize) {
+ // We don't have space for further entries. Flush the contents of the buffer and reuse the
+ // buffer to store contents. Reset the index to the start of the buffer.
+ FlushStreamingBuffer(thread);
+ *current_offset = 0;
+ }
+
+ // Record entry in per-thread trace buffer.
+ method_trace_buffer[*current_offset] = reinterpret_cast<uintptr_t>(method);
+ *current_offset += 1;
+ // TODO(mythria): We only need two bits to record the action. Consider merging
+ // it with the method entry to save space.
+ method_trace_buffer[*current_offset] = action;
+ *current_offset += 1;
+ if (UseThreadCpuClock()) {
+ method_trace_buffer[*current_offset] = thread_clock_diff;
+ *current_offset += 1;
+ }
+ if (UseWallClock()) {
+ method_trace_buffer[*current_offset] = wall_clock_diff;
+ *current_offset += 1;
+ }
+}
+
+void Trace::WriteToBuf(uint8_t* header,
+ size_t header_size,
+ const std::string& data,
+ size_t* current_index,
+ uint8_t* buffer,
+ size_t buffer_size) {
+ EnsureSpace(buffer, current_index, buffer_size, header_size);
+ memcpy(buffer + *current_index, header, header_size);
+ *current_index += header_size;
+
+ EnsureSpace(buffer, current_index, buffer_size, data.length());
+ if (data.length() < buffer_size) {
+ memcpy(buffer + *current_index, reinterpret_cast<const uint8_t*>(data.c_str()), data.length());
+ *current_index += data.length();
+ } else {
+ // The data is larger than buffer, so write directly to the file. EnsureSpace should have
+ // flushed any data in the buffer.
+ DCHECK_EQ(*current_index, 0U);
+ if (!trace_file_->WriteFully(reinterpret_cast<const uint8_t*>(data.c_str()), data.length())) {
+ PLOG(WARNING) << "Failed streaming a tracing event.";
+ }
+ }
+}
+
+void Trace::FlushStreamingBuffer(Thread* thread) {
+ // Take a streaming_lock_ to serialize writes across threads. We also need to allocate a unique
+ // method id for each method. We do that by maintaining a map from id to method for each newly
+ // seen method (see RegisterMethod). streaming_lock_ also is required to serialize these.
+ MutexLock mu(Thread::Current(), *streaming_lock_);
+ uintptr_t* method_trace_buffer = thread->GetMethodTraceBuffer();
+ // Create a temporary buffer to encode the trace events from the specified thread.
+ size_t buffer_size = kPerThreadBufSize;
+ size_t current_index = 0;
+ std::unique_ptr<uint8_t[]> buffer(new uint8_t[std::max(kMinBufSize, buffer_size)]);
+
+ size_t num_entries = *(thread->GetMethodTraceIndexPtr());
+ for (size_t entry_index = 0; entry_index < num_entries;) {
+ ArtMethod* method = reinterpret_cast<ArtMethod*>(method_trace_buffer[entry_index++]);
+ TraceAction action = DecodeTraceAction(method_trace_buffer[entry_index++]);
+ uint32_t thread_time = 0;
+ uint32_t wall_time = 0;
+ if (UseThreadCpuClock()) {
+ thread_time = method_trace_buffer[entry_index++];
+ }
+ if (UseWallClock()) {
+ wall_time = method_trace_buffer[entry_index++];
}
- old_offset = 0;
- new_offset = static_cast<int32_t>(src_size);
+ // If we haven't seen this method before record information about the method.
+ if (RegisterMethod(method)) {
+ // Write a special block with the name.
+ std::string method_line(GetMethodLine(method));
+ static constexpr size_t kMethodNameHeaderSize = 5;
+ uint8_t method_header[kMethodNameHeaderSize];
+ DCHECK_LT(kMethodNameHeaderSize, kPerThreadBufSize);
+ Append2LE(method_header, 0);
+ method_header[2] = kOpNewMethod;
+ Append2LE(method_header + 3, static_cast<uint16_t>(method_line.length()));
+ WriteToBuf(method_header,
+ kMethodNameHeaderSize,
+ method_line,
+ ¤t_index,
+ buffer.get(),
+ buffer_size);
+ }
+
+ const size_t record_size = GetRecordSize(clock_source_);
+ DCHECK_LT(record_size, kPerThreadBufSize);
+ EnsureSpace(buffer.get(), ¤t_index, buffer_size, record_size);
+ EncodeEventEntry(buffer.get() + current_index, thread, method, action, thread_time, wall_time);
+ current_index += record_size;
}
- cur_offset_.store(new_offset, std::memory_order_relaxed);
- // Fill in data.
- memcpy(buf_.get() + old_offset, src, src_size);
+
+ // Flush the contents of buffer to file.
+ if (!trace_file_->WriteFully(buffer.get(), current_index)) {
+ PLOG(WARNING) << "Failed streaming a tracing event.";
+ }
}
-void Trace::FlushBuf() {
- // Updates to cur_offset_ are done under the streaming_lock_ here as in streaming mode.
- int32_t offset = cur_offset_.load(std::memory_order_relaxed);
- if (!trace_file_->WriteFully(buf_.get(), offset)) {
- PLOG(WARNING) << "Failed flush the remaining data in streaming.";
- }
- cur_offset_.store(0, std::memory_order_relaxed);
-}
-
-void Trace::LogMethodTraceEvent(Thread* thread, ArtMethod* method,
- instrumentation::Instrumentation::InstrumentationEvent event,
- uint32_t thread_clock_diff, uint32_t wall_clock_diff) {
- // This method is called in both tracing modes (method and
- // sampling). In sampling mode, this method is only called by the
- // sampling thread. In method tracing mode, it can be called
- // concurrently.
-
- // Ensure we always use the non-obsolete version of the method so that entry/exit events have the
- // same pointer value.
- method = method->GetNonObsoleteMethod();
-
+void Trace::RecordMethodEvent(Thread* thread,
+ ArtMethod* method,
+ TraceAction action,
+ uint32_t thread_clock_diff,
+ uint32_t wall_clock_diff) {
// Advance cur_offset_ atomically.
int32_t new_offset;
int32_t old_offset = 0;
@@ -893,20 +1025,40 @@
// In the non-streaming case, we do a busy loop here trying to get
// an offset to write our record and advance cur_offset_ for the
// next use.
- if (trace_output_mode_ != TraceOutputMode::kStreaming) {
- // Although multiple threads can call this method concurrently,
- // the compare_exchange_weak here is still atomic (by definition).
- // A succeeding update is visible to other cores when they pass
- // through this point.
- old_offset = cur_offset_.load(std::memory_order_relaxed); // Speculative read
- do {
- new_offset = old_offset + GetRecordSize(clock_source_);
- if (static_cast<size_t>(new_offset) > buffer_size_) {
- overflow_ = true;
- return;
- }
- } while (!cur_offset_.compare_exchange_weak(old_offset, new_offset, std::memory_order_relaxed));
- }
+ // Although multiple threads can call this method concurrently,
+ // the compare_exchange_weak here is still atomic (by definition).
+ // A succeeding update is visible to other cores when they pass
+ // through this point.
+ old_offset = cur_offset_.load(std::memory_order_relaxed); // Speculative read
+ do {
+ new_offset = old_offset + GetRecordSize(clock_source_);
+ if (static_cast<size_t>(new_offset) > buffer_size_) {
+ overflow_ = true;
+ return;
+ }
+ } while (!cur_offset_.compare_exchange_weak(old_offset, new_offset, std::memory_order_relaxed));
+
+ // Write data into the tracing buffer (if not streaming) or into a
+ // small buffer on the stack (if streaming) which we'll put into the
+ // tracing buffer below.
+ //
+ // These writes to the tracing buffer are synchronised with the
+ // future reads that (only) occur under FinishTracing(). The callers
+ // of FinishTracing() acquire locks and (implicitly) synchronise
+ // the buffer memory.
+ uint8_t* ptr;
+ ptr = buf_.get() + old_offset;
+ EncodeEventEntry(ptr, thread, method, action, thread_clock_diff, wall_clock_diff);
+}
+
+void Trace::LogMethodTraceEvent(Thread* thread,
+ ArtMethod* method,
+ instrumentation::Instrumentation::InstrumentationEvent event,
+ uint32_t thread_clock_diff,
+ uint32_t wall_clock_diff) {
+ // This method is called in both tracing modes (method and sampling). In sampling mode, this
+ // method is only called by the sampling thread. In method tracing mode, it can be called
+ // concurrently.
TraceAction action = kTraceMethodEnter;
switch (event) {
@@ -923,25 +1075,25 @@
UNIMPLEMENTED(FATAL) << "Unexpected event: " << event;
}
- uint32_t method_value = EncodeTraceMethodAndAction(method, action);
+ // Ensure we always use the non-obsolete version of the method so that entry/exit events have the
+ // same pointer value.
+ method = method->GetNonObsoleteMethod();
- // Write data into the tracing buffer (if not streaming) or into a
- // small buffer on the stack (if streaming) which we'll put into the
- // tracing buffer below.
- //
- // These writes to the tracing buffer are synchronised with the
- // future reads that (only) occur under FinishTracing(). The callers
- // of FinishTracing() acquire locks and (implicitly) synchronise
- // the buffer memory.
- uint8_t* ptr;
- static constexpr size_t kPacketSize = 14U; // The maximum size of data in a packet.
- uint8_t stack_buf[kPacketSize]; // Space to store a packet when in streaming mode.
if (trace_output_mode_ == TraceOutputMode::kStreaming) {
- ptr = stack_buf;
+ RecordStreamingMethodEvent(thread, method, action, thread_clock_diff, wall_clock_diff);
} else {
- ptr = buf_.get() + old_offset;
+ RecordMethodEvent(thread, method, action, thread_clock_diff, wall_clock_diff);
}
+}
+void Trace::EncodeEventEntry(uint8_t* ptr,
+ Thread* thread,
+ ArtMethod* method,
+ TraceAction action,
+ uint32_t thread_clock_diff,
+ uint32_t wall_clock_diff) {
+ static constexpr size_t kPacketSize = 14U; // The maximum size of data in a packet.
+ uint32_t method_value = EncodeTraceMethodAndAction(method, action);
Append2LE(ptr, thread->GetTid());
Append4LE(ptr + 2, method_value);
ptr += 6;
@@ -954,33 +1106,20 @@
Append4LE(ptr, wall_clock_diff);
}
static_assert(kPacketSize == 2 + 4 + 4 + 4, "Packet size incorrect.");
+}
- if (trace_output_mode_ == TraceOutputMode::kStreaming) {
- MutexLock mu(Thread::Current(), *streaming_lock_); // To serialize writing.
- if (RegisterMethod(method)) {
- // Write a special block with the name.
- std::string method_line(GetMethodLine(method));
- uint8_t buf2[5];
- Append2LE(buf2, 0);
- buf2[2] = kOpNewMethod;
- Append2LE(buf2 + 3, static_cast<uint16_t>(method_line.length()));
- WriteToBuf(buf2, sizeof(buf2));
- WriteToBuf(reinterpret_cast<const uint8_t*>(method_line.c_str()), method_line.length());
- }
- if (RegisterThread(thread)) {
- // It might be better to postpone this. Threads might not have received names...
- std::string thread_name;
- thread->GetThreadName(thread_name);
- uint8_t buf2[7];
- Append2LE(buf2, 0);
- buf2[2] = kOpNewThread;
- Append2LE(buf2 + 3, static_cast<uint16_t>(thread->GetTid()));
- Append2LE(buf2 + 5, static_cast<uint16_t>(thread_name.length()));
- WriteToBuf(buf2, sizeof(buf2));
- WriteToBuf(reinterpret_cast<const uint8_t*>(thread_name.c_str()), thread_name.length());
- }
- WriteToBuf(stack_buf, sizeof(stack_buf));
+void Trace::EnsureSpace(uint8_t* buffer,
+ size_t* current_index,
+ size_t buffer_size,
+ size_t required_size) {
+ if (*current_index + required_size < buffer_size) {
+ return;
}
+
+ if (!trace_file_->WriteFully(buffer, *current_index)) {
+ PLOG(WARNING) << "Failed streaming a tracing event.";
+ }
+ *current_index = 0;
}
void Trace::GetVisitedMethods(size_t buf_size,
diff --git a/runtime/trace.h b/runtime/trace.h
index c6f36e4..bce8efc 100644
--- a/runtime/trace.h
+++ b/runtime/trace.h
@@ -49,9 +49,6 @@
using DexIndexBitSet = std::bitset<65536>;
-constexpr size_t kMaxThreadIdNumber = kIsTargetBuild ? 65536U : 1048576U;
-using ThreadIDBitSet = std::bitset<kMaxThreadIdNumber>;
-
enum TracingMode {
kTracingInactive,
kMethodTracingActive, // Trace activity synchronous with method progress.
@@ -166,6 +163,10 @@
REQUIRES(!Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::trace_lock_);
static TracingMode GetMethodTracingMode() REQUIRES(!Locks::trace_lock_);
+ // Flush the per-thread buffer. This is called when the thread is about to detach.
+ static void FlushThreadBuffer(Thread* thread) REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::trace_lock_) NO_THREAD_SAFETY_ANALYSIS;
+
bool UseWallClock();
bool UseThreadCpuClock();
void MeasureClockOverhead();
@@ -184,7 +185,6 @@
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!unique_methods_lock_, !streaming_lock_)
override;
void MethodUnwind(Thread* thread,
- Handle<mirror::Object> this_object,
ArtMethod* method,
uint32_t dex_pc)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!unique_methods_lock_, !streaming_lock_)
@@ -275,13 +275,55 @@
bool RegisterThread(Thread* thread)
REQUIRES(streaming_lock_);
- // Copy a temporary buffer to the main buffer. Used for streaming. Exposed here for lock
- // annotation.
- void WriteToBuf(const uint8_t* src, size_t src_size)
- REQUIRES(streaming_lock_);
- // Flush the main buffer to file. Used for streaming. Exposed here for lock annotation.
- void FlushBuf()
- REQUIRES(streaming_lock_);
+ void RecordMethodEvent(Thread* thread,
+ ArtMethod* method,
+ TraceAction action,
+ uint32_t thread_clock_diff,
+ uint32_t wall_clock_diff) REQUIRES(!unique_methods_lock_);
+
+ // Encodes event in non-streaming mode. This assumes that there is enough space reserved to
+ // encode the entry.
+ void EncodeEventEntry(uint8_t* ptr,
+ Thread* thread,
+ ArtMethod* method,
+ TraceAction action,
+ uint32_t thread_clock_diff,
+ uint32_t wall_clock_diff) REQUIRES(!unique_methods_lock_);
+
+ // These methods are used to encode events in streaming mode.
+
+ // This records the method event in the per-thread buffer if there is sufficient space for the
+ // entire record. If the buffer is full then it just flushes the buffer and then records the
+ // entry.
+ void RecordStreamingMethodEvent(Thread* thread,
+ ArtMethod* method,
+ TraceAction action,
+ uint32_t thread_clock_diff,
+ uint32_t wall_clock_diff) REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!unique_methods_lock_) REQUIRES(!streaming_lock_);
+ // This encodes all the events in the per-thread trace buffer and writes it to the trace file.
+ // This acquires streaming lock to prevent any other threads writing concurrently. It is required
+ // to serialize these since each method is encoded with a unique id which is assigned when the
+ // method is seen for the first time in the recoreded events. So we need to serialize these
+ // flushes across threads.
+ void FlushStreamingBuffer(Thread* thread) REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!unique_methods_lock_) REQUIRES(!streaming_lock_);
+ // Ensures there is sufficient space in the buffer to record the requested_size. If there is not
+ // enough sufficient space the current contents of the buffer are written to the file and
+ // current_index is reset to 0. This doesn't check if buffer_size is big enough to hold the
+ // requested size.
+ void EnsureSpace(uint8_t* buffer,
+ size_t* current_index,
+ size_t buffer_size,
+ size_t required_size);
+ // Writes header followed by data to the buffer at the current_index. This also updates the
+ // current_index to point to the next entry.
+ void WriteToBuf(uint8_t* header,
+ size_t header_size,
+ const std::string& data,
+ size_t* current_index,
+ uint8_t* buffer,
+ size_t buffer_size);
uint32_t EncodeTraceMethod(ArtMethod* method) REQUIRES(!unique_methods_lock_);
uint32_t EncodeTraceMethodAndAction(ArtMethod* method, TraceAction action)
@@ -369,7 +411,6 @@
// Streaming mode data.
Mutex* streaming_lock_;
std::map<const DexFile*, DexIndexBitSet*> seen_methods_ GUARDED_BY(streaming_lock_);
- std::unique_ptr<ThreadIDBitSet> seen_threads_ GUARDED_BY(streaming_lock_);
// Bijective map from ArtMethod* to index.
// Map from ArtMethod* to index in unique_methods_;
diff --git a/runtime/transaction.cc b/runtime/transaction.cc
index 006aa56..08452bd 100644
--- a/runtime/transaction.cc
+++ b/runtime/transaction.cc
@@ -410,7 +410,6 @@
for (auto& it : array_logs_) {
mirror::Array* old_root = it.first;
- CHECK(!old_root->IsObjectArray());
mirror::Array* new_root = old_root;
visitor->VisitRoot(reinterpret_cast<mirror::Object**>(&new_root), RootInfo(kRootUnknown));
if (new_root != old_root) {
diff --git a/runtime/transaction.h b/runtime/transaction.h
index d147038..6fa8e58 100644
--- a/runtime/transaction.h
+++ b/runtime/transaction.h
@@ -245,15 +245,17 @@
REQUIRES(Locks::intern_table_lock_);
void VisitRoots(RootVisitor* visitor) REQUIRES_SHARED(Locks::mutator_lock_);
- InternStringLog() = default;
+ // Only the move constructor is supported.
+ InternStringLog() = delete;
+ InternStringLog(const InternStringLog& log) = delete;
+ InternStringLog& operator=(const InternStringLog& log) = delete;
InternStringLog(InternStringLog&& log) = default;
+ InternStringLog& operator=(InternStringLog&& log) = delete;
private:
mutable GcRoot<mirror::String> str_;
const StringKind string_kind_;
const StringOp string_op_;
-
- DISALLOW_COPY_AND_ASSIGN(InternStringLog);
};
class ResolveStringLog : public ValueObject {
diff --git a/runtime/var_handles.cc b/runtime/var_handles.cc
index 73b4dda..0c7e3bd 100644
--- a/runtime/var_handles.cc
+++ b/runtime/var_handles.cc
@@ -41,7 +41,7 @@
const size_t num_vregs = accessor_type->NumberOfVRegs();
const int num_params = accessor_type->GetPTypes()->GetLength();
ShadowFrameAllocaUniquePtr accessor_frame =
- CREATE_SHADOW_FRAME(num_vregs, nullptr, shadow_frame.GetMethod(), shadow_frame.GetDexPC());
+ CREATE_SHADOW_FRAME(num_vregs, shadow_frame.GetMethod(), shadow_frame.GetDexPC());
ShadowFrameGetter getter(shadow_frame, operands);
static const uint32_t kFirstDestinationReg = 0;
ShadowFrameSetter setter(accessor_frame.get(), kFirstDestinationReg);
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index fb3f809..f07233c 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -49,10 +49,6 @@
using android::base::StringPrintf;
-constexpr uint8_t VdexFile::VdexFileHeader::kVdexInvalidMagic[4];
-constexpr uint8_t VdexFile::VdexFileHeader::kVdexMagic[4];
-constexpr uint8_t VdexFile::VdexFileHeader::kVdexVersion[4];
-
bool VdexFile::VdexFileHeader::IsMagicValid() const {
return (memcmp(magic_, kVdexMagic, sizeof(kVdexMagic)) == 0);
}
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 3ccbfa5..a35d720 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -246,8 +246,8 @@
const uint8_t* Begin() const { return mmap_.Begin(); }
const uint8_t* End() const { return mmap_.End(); }
size_t Size() const { return mmap_.Size(); }
- bool Contains(const uint8_t* pointer) const {
- return pointer >= Begin() && pointer < End();
+ bool Contains(const uint8_t* pointer, size_t size) const {
+ return Begin() <= pointer && size <= Size() && pointer <= End() - size;
}
const VdexFileHeader& GetVdexFileHeader() const {
diff --git a/runtime/vdex_file_test.cc b/runtime/vdex_file_test.cc
index 565487e..1ac10eb 100644
--- a/runtime/vdex_file_test.cc
+++ b/runtime/vdex_file_test.cc
@@ -20,12 +20,11 @@
#include <gtest/gtest.h>
-#include "common_runtime_test.h"
+#include "base/common_art_test.h"
namespace art {
-class VdexFileTest : public CommonRuntimeTest {
-};
+class VdexFileTest : public CommonArtTest {};
TEST_F(VdexFileTest, OpenEmptyVdex) {
// Verify we fail to open an empty vdex file.
diff --git a/runtime/verifier/class_verifier.cc b/runtime/verifier/class_verifier.cc
index 8c541f8..8946bb2 100644
--- a/runtime/verifier/class_verifier.cc
+++ b/runtime/verifier/class_verifier.cc
@@ -176,6 +176,9 @@
GetMetrics()->ClassVerificationCount()->AddOne();
+ GetMetrics()->ClassVerificationTotalTimeDelta()->Add(elapsed_time_microseconds);
+ GetMetrics()->ClassVerificationCountDelta()->AddOne();
+
if (failure_data.kind == verifier::FailureKind::kHardFailure && callbacks != nullptr) {
ClassReference ref(dex_file, dex_file->GetIndexForClassDef(class_def));
callbacks->ClassRejected(ref);
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index ec0ac73..1d1d3ee 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -1054,6 +1054,10 @@
// We can't assume the instruction is well formed, handle the case where calculating the size
// goes past the end of the code item.
SafeDexInstructionIterator it(code_item_accessor_.begin(), code_item_accessor_.end());
+ if (it == code_item_accessor_.end()) {
+ Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "code item has no opcode";
+ return false;
+ }
for ( ; !it.IsErrorState() && it < code_item_accessor_.end(); ++it) {
// In case the instruction goes past the end of the code item, make sure to not process it.
SafeDexInstructionIterator next = it;
@@ -4899,7 +4903,7 @@
template <bool kVerifierDebug>
bool MethodVerifier<kVerifierDebug>::PotentiallyMarkRuntimeThrow() {
- if (IsAotMode() || IsSdkVersionSetAndAtLeast(api_level_, SdkVersion::kT)) {
+ if (IsAotMode() || IsSdkVersionSetAndAtLeast(api_level_, SdkVersion::kS_V2)) {
return false;
}
// Compatibility mode: we treat the following code unreachable and the verifier
diff --git a/runtime/verifier/method_verifier_test.cc b/runtime/verifier/method_verifier_test.cc
index ec07047..0f0fd17 100644
--- a/runtime/verifier/method_verifier_test.cc
+++ b/runtime/verifier/method_verifier_test.cc
@@ -37,6 +37,10 @@
class MethodVerifierTest : public CommonRuntimeTest {
protected:
+ MethodVerifierTest() {
+ use_boot_image_ = true; // Make the Runtime creation cheaper.
+ }
+
void VerifyClass(const std::string& descriptor)
REQUIRES_SHARED(Locks::mutator_lock_) {
ASSERT_FALSE(descriptor.empty());
diff --git a/runtime/verifier/reg_type_test.cc b/runtime/verifier/reg_type_test.cc
index a157464..a36cf71 100644
--- a/runtime/verifier/reg_type_test.cc
+++ b/runtime/verifier/reg_type_test.cc
@@ -32,7 +32,7 @@
namespace verifier {
class RegTypeTest : public CommonRuntimeTest {
- public:
+ protected:
RegTypeTest() {
use_boot_image_ = true; // Make the Runtime creation cheaper.
}
@@ -360,7 +360,7 @@
EXPECT_TRUE(double_reg_type.HasClass());
}
-class RegTypeReferenceTest : public CommonRuntimeTest {};
+class RegTypeReferenceTest : public RegTypeTest {};
TEST_F(RegTypeReferenceTest, JavalangObjectImprecise) {
// Tests matching precisions. A reference type that was created precise doesn't
diff --git a/runtime/verifier/register_line-inl.h b/runtime/verifier/register_line-inl.h
index 6b53687..7b5a496 100644
--- a/runtime/verifier/register_line-inl.h
+++ b/runtime/verifier/register_line-inl.h
@@ -20,7 +20,6 @@
#include "register_line.h"
#include "base/logging.h" // For VLOG.
-#include "debug_print.h"
#include "method_verifier.h"
#include "reg_type_cache-inl.h"
@@ -139,14 +138,6 @@
}
verifier->Fail(fail_type) << "register v" << vsrc << " has type "
<< src_type << " but expected " << check_type;
- if (check_type.IsNonZeroReferenceTypes() &&
- !check_type.IsUnresolvedTypes() &&
- check_type.HasClass() &&
- src_type.IsNonZeroReferenceTypes() &&
- !src_type.IsUnresolvedTypes() &&
- src_type.HasClass()) {
- DumpB77342775DebugData(check_type.GetClass(), src_type.GetClass());
- }
return false;
}
if (check_type.IsLowHalf()) {
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index db5fa0f..781cf7b 100644
--- a/runtime/verifier/verifier_deps.cc
+++ b/runtime/verifier/verifier_deps.cc
@@ -760,6 +760,18 @@
return true;
}
+void VerifierDeps::ClearData(const std::vector<const DexFile*>& dex_files) {
+ for (const DexFile* dex_file : dex_files) {
+ auto it = dex_deps_.find(dex_file);
+ if (it == dex_deps_.end()) {
+ continue;
+ }
+ std::unique_ptr<DexFileDeps> deps(new DexFileDeps(dex_file->NumClassDefs()));
+ it->second.swap(deps);
+ }
+}
+
+
bool VerifierDeps::VerifyDexFile(Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const DexFileDeps& deps,
diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h
index 8e3b8a7..46b3554 100644
--- a/runtime/verifier/verifier_deps.h
+++ b/runtime/verifier/verifier_deps.h
@@ -137,6 +137,9 @@
return GetDexFileDeps(dex_file) != nullptr;
}
+ // Resets the data related to the given dex files.
+ void ClearData(const std::vector<const DexFile*>& dex_files);
+
// Parses raw VerifierDeps data to extract bitvectors of which class def indices
// were verified or not. The given `dex_files` must match the order and count of
// dex files used to create the VerifierDeps.
diff --git a/runtime/well_known_classes-inl.h b/runtime/well_known_classes-inl.h
new file mode 100644
index 0000000..23fe145
--- /dev/null
+++ b/runtime/well_known_classes-inl.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_WELL_KNOWN_CLASSES_INL_H_
+#define ART_RUNTIME_WELL_KNOWN_CLASSES_INL_H_
+
+#include "well_known_classes.h"
+
+#include "art_field-inl.h"
+#include "art_method-inl.h"
+
+namespace art {
+namespace detail {
+
+template <typename MemberType, MemberType** kMember>
+template <ReadBarrierOption kReadBarrierOption>
+ObjPtr<mirror::Class> ClassFromMember<MemberType, kMember>::Get() {
+ return (*kMember)->template GetDeclaringClass<kReadBarrierOption>();
+}
+
+template <typename MemberType, MemberType** kMember>
+mirror::Class* ClassFromMember<MemberType, kMember>::operator->() const {
+ return Get().Ptr();
+}
+
+template <typename MemberType, MemberType** kMember>
+inline bool operator==(const ClassFromMember<MemberType, kMember> lhs, ObjPtr<mirror::Class> rhs) {
+ return lhs.Get() == rhs;
+}
+
+template <typename MemberType, MemberType** kMember>
+inline bool operator==(ObjPtr<mirror::Class> lhs, const ClassFromMember<MemberType, kMember> rhs) {
+ return rhs == lhs;
+}
+
+template <typename MemberType, MemberType** kMember>
+bool operator!=(const ClassFromMember<MemberType, kMember> lhs, ObjPtr<mirror::Class> rhs) {
+ return !(lhs == rhs);
+}
+
+template <typename MemberType, MemberType** kMember>
+bool operator!=(ObjPtr<mirror::Class> lhs, const ClassFromMember<MemberType, kMember> rhs) {
+ return !(rhs == lhs);
+}
+
+} // namespace detail
+} // namespace art
+
+#endif // ART_RUNTIME_WELL_KNOWN_CLASSES_INL_H_
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index cf9e321..6a4a87f 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -23,11 +23,15 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include "art_method-inl.h"
+#include "base/casts.h"
#include "base/enums.h"
#include "class_linker.h"
+#include "class_root-inl.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
-#include "entrypoints/runtime_asm_entrypoints.h"
+#include "handle_scope-inl.h"
#include "hidden_api.h"
+#include "jni/java_vm_ext.h"
#include "jni/jni_internal.h"
#include "jni_id_type.h"
#include "mirror/class.h"
@@ -44,139 +48,126 @@
jclass WellKnownClasses::dalvik_annotation_optimization_CriticalNative;
jclass WellKnownClasses::dalvik_annotation_optimization_FastNative;
jclass WellKnownClasses::dalvik_annotation_optimization_NeverCompile;
-jclass WellKnownClasses::dalvik_system_BaseDexClassLoader;
-jclass WellKnownClasses::dalvik_system_DelegateLastClassLoader;
-jclass WellKnownClasses::dalvik_system_DexClassLoader;
-jclass WellKnownClasses::dalvik_system_DexFile;
-jclass WellKnownClasses::dalvik_system_DexPathList;
-jclass WellKnownClasses::dalvik_system_DexPathList__Element;
-jclass WellKnownClasses::dalvik_system_EmulatedStackFrame;
-jclass WellKnownClasses::dalvik_system_InMemoryDexClassLoader;
-jclass WellKnownClasses::dalvik_system_PathClassLoader;
-jclass WellKnownClasses::dalvik_system_VMRuntime;
+jclass WellKnownClasses::dalvik_annotation_optimization_NeverInline;
jclass WellKnownClasses::java_lang_annotation_Annotation__array;
-jclass WellKnownClasses::java_lang_BootClassLoader;
-jclass WellKnownClasses::java_lang_ClassLoader;
-jclass WellKnownClasses::java_lang_ClassNotFoundException;
-jclass WellKnownClasses::java_lang_Daemons;
-jclass WellKnownClasses::java_lang_Error;
-jclass WellKnownClasses::java_lang_IllegalAccessError;
-jclass WellKnownClasses::java_lang_NoClassDefFoundError;
-jclass WellKnownClasses::java_lang_Object;
-jclass WellKnownClasses::java_lang_OutOfMemoryError;
-jclass WellKnownClasses::java_lang_reflect_InvocationTargetException;
-jclass WellKnownClasses::java_lang_reflect_Parameter;
+jclass WellKnownClasses::java_lang_ClassValue;
jclass WellKnownClasses::java_lang_reflect_Parameter__array;
-jclass WellKnownClasses::java_lang_reflect_Proxy;
-jclass WellKnownClasses::java_lang_RuntimeException;
-jclass WellKnownClasses::java_lang_StackOverflowError;
-jclass WellKnownClasses::java_lang_String;
jclass WellKnownClasses::java_lang_StringFactory;
jclass WellKnownClasses::java_lang_System;
-jclass WellKnownClasses::java_lang_Thread;
-jclass WellKnownClasses::java_lang_ThreadGroup;
-jclass WellKnownClasses::java_lang_Throwable;
jclass WellKnownClasses::java_lang_Void;
-jclass WellKnownClasses::java_nio_Buffer;
-jclass WellKnownClasses::java_nio_ByteBuffer;
-jclass WellKnownClasses::java_nio_DirectByteBuffer;
-jclass WellKnownClasses::java_util_Collections;
-jclass WellKnownClasses::java_util_function_Consumer;
-jclass WellKnownClasses::libcore_reflect_AnnotationFactory;
-jclass WellKnownClasses::libcore_reflect_AnnotationMember;
-jclass WellKnownClasses::libcore_util_EmptyArray;
-jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk;
-jclass WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer;
+jclass WellKnownClasses::libcore_reflect_AnnotationMember__array;
-jmethodID WellKnownClasses::dalvik_system_BaseDexClassLoader_getLdLibraryPath;
-jmethodID WellKnownClasses::dalvik_system_VMRuntime_runFinalization;
-jmethodID WellKnownClasses::dalvik_system_VMRuntime_hiddenApiUsed;
-jmethodID WellKnownClasses::java_lang_Boolean_valueOf;
-jmethodID WellKnownClasses::java_lang_Byte_valueOf;
-jmethodID WellKnownClasses::java_lang_Character_valueOf;
-jmethodID WellKnownClasses::java_lang_ClassLoader_loadClass;
-jmethodID WellKnownClasses::java_lang_ClassNotFoundException_init;
-jmethodID WellKnownClasses::java_lang_Daemons_start;
-jmethodID WellKnownClasses::java_lang_Daemons_stop;
-jmethodID WellKnownClasses::java_lang_Daemons_waitForDaemonStart;
-jmethodID WellKnownClasses::java_lang_Double_doubleToRawLongBits;
-jmethodID WellKnownClasses::java_lang_Double_valueOf;
-jmethodID WellKnownClasses::java_lang_Float_floatToRawIntBits;
-jmethodID WellKnownClasses::java_lang_Float_valueOf;
-jmethodID WellKnownClasses::java_lang_Integer_valueOf;
-jmethodID WellKnownClasses::java_lang_invoke_MethodHandle_asType;
-jmethodID WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact;
-jmethodID WellKnownClasses::java_lang_invoke_MethodHandles_lookup;
-jmethodID WellKnownClasses::java_lang_invoke_MethodHandles_Lookup_findConstructor;
-jmethodID WellKnownClasses::java_lang_Long_valueOf;
-jmethodID WellKnownClasses::java_lang_ref_FinalizerReference_add;
-jmethodID WellKnownClasses::java_lang_ref_ReferenceQueue_add;
-jmethodID WellKnownClasses::java_lang_reflect_InvocationTargetException_init;
-jmethodID WellKnownClasses::java_lang_reflect_Parameter_init;
-jmethodID WellKnownClasses::java_lang_reflect_Proxy_init;
-jmethodID WellKnownClasses::java_lang_reflect_Proxy_invoke;
-jmethodID WellKnownClasses::java_lang_Runtime_nativeLoad;
-jmethodID WellKnownClasses::java_lang_Short_valueOf;
-jmethodID WellKnownClasses::java_lang_String_charAt;
-jmethodID WellKnownClasses::java_lang_Thread_dispatchUncaughtException;
-jmethodID WellKnownClasses::java_lang_Thread_init;
-jmethodID WellKnownClasses::java_lang_Thread_run;
-jmethodID WellKnownClasses::java_lang_ThreadGroup_add;
-jmethodID WellKnownClasses::java_lang_ThreadGroup_removeThread;
-jmethodID WellKnownClasses::java_nio_Buffer_isDirect;
-jmethodID WellKnownClasses::java_nio_DirectByteBuffer_init;
-jmethodID WellKnownClasses::java_util_function_Consumer_accept;
-jmethodID WellKnownClasses::libcore_reflect_AnnotationFactory_createAnnotation;
-jmethodID WellKnownClasses::libcore_reflect_AnnotationMember_init;
-jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
-jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
+ArtMethod* WellKnownClasses::dalvik_system_BaseDexClassLoader_getLdLibraryPath;
+ArtMethod* WellKnownClasses::dalvik_system_DelegateLastClassLoader_init;
+ArtMethod* WellKnownClasses::dalvik_system_DexClassLoader_init;
+ArtMethod* WellKnownClasses::dalvik_system_InMemoryDexClassLoader_init;
+ArtMethod* WellKnownClasses::dalvik_system_PathClassLoader_init;
+ArtMethod* WellKnownClasses::dalvik_system_VMRuntime_hiddenApiUsed;
+ArtMethod* WellKnownClasses::java_lang_Boolean_valueOf;
+ArtMethod* WellKnownClasses::java_lang_BootClassLoader_init;
+ArtMethod* WellKnownClasses::java_lang_Byte_valueOf;
+ArtMethod* WellKnownClasses::java_lang_Character_valueOf;
+ArtMethod* WellKnownClasses::java_lang_ClassLoader_loadClass;
+ArtMethod* WellKnownClasses::java_lang_ClassNotFoundException_init;
+ArtMethod* WellKnownClasses::java_lang_Daemons_start;
+ArtMethod* WellKnownClasses::java_lang_Daemons_stop;
+ArtMethod* WellKnownClasses::java_lang_Daemons_waitForDaemonStart;
+ArtMethod* WellKnownClasses::java_lang_Double_doubleToRawLongBits;
+ArtMethod* WellKnownClasses::java_lang_Double_valueOf;
+ArtMethod* WellKnownClasses::java_lang_Error_init;
+ArtMethod* WellKnownClasses::java_lang_Float_floatToRawIntBits;
+ArtMethod* WellKnownClasses::java_lang_Float_valueOf;
+ArtMethod* WellKnownClasses::java_lang_IllegalAccessError_init;
+ArtMethod* WellKnownClasses::java_lang_Integer_valueOf;
+ArtMethod* WellKnownClasses::java_lang_Long_valueOf;
+ArtMethod* WellKnownClasses::java_lang_NoClassDefFoundError_init;
+ArtMethod* WellKnownClasses::java_lang_OutOfMemoryError_init;
+ArtMethod* WellKnownClasses::java_lang_Runtime_nativeLoad;
+ArtMethod* WellKnownClasses::java_lang_RuntimeException_init;
+ArtMethod* WellKnownClasses::java_lang_Short_valueOf;
+ArtMethod* WellKnownClasses::java_lang_StackOverflowError_init;
+ArtMethod* WellKnownClasses::java_lang_String_charAt;
+ArtMethod* WellKnownClasses::java_lang_Thread_dispatchUncaughtException;
+ArtMethod* WellKnownClasses::java_lang_Thread_init;
+ArtMethod* WellKnownClasses::java_lang_Thread_run;
+ArtMethod* WellKnownClasses::java_lang_ThreadGroup_add;
+ArtMethod* WellKnownClasses::java_lang_ThreadGroup_threadTerminated;
+ArtMethod* WellKnownClasses::java_lang_invoke_MethodHandle_asType;
+ArtMethod* WellKnownClasses::java_lang_invoke_MethodHandle_invokeExact;
+ArtMethod* WellKnownClasses::java_lang_invoke_MethodHandles_lookup;
+ArtMethod* WellKnownClasses::java_lang_invoke_MethodHandles_Lookup_findConstructor;
+ArtMethod* WellKnownClasses::java_lang_ref_FinalizerReference_add;
+ArtMethod* WellKnownClasses::java_lang_ref_ReferenceQueue_add;
+ArtMethod* WellKnownClasses::java_lang_reflect_InvocationTargetException_init;
+ArtMethod* WellKnownClasses::java_lang_reflect_Parameter_init;
+ArtMethod* WellKnownClasses::java_lang_reflect_Proxy_init;
+ArtMethod* WellKnownClasses::java_lang_reflect_Proxy_invoke;
+ArtMethod* WellKnownClasses::java_nio_Buffer_isDirect;
+ArtMethod* WellKnownClasses::java_nio_DirectByteBuffer_init;
+ArtMethod* WellKnownClasses::java_util_function_Consumer_accept;
+ArtMethod* WellKnownClasses::jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_D;
+ArtMethod* WellKnownClasses::jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_F;
+ArtMethod* WellKnownClasses::jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_getChars;
+ArtMethod* WellKnownClasses::libcore_reflect_AnnotationFactory_createAnnotation;
+ArtMethod* WellKnownClasses::libcore_reflect_AnnotationMember_init;
+ArtMethod* WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
+ArtMethod* WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
-jfieldID WellKnownClasses::dalvik_system_DexFile_cookie;
-jfieldID WellKnownClasses::dalvik_system_DexFile_fileName;
-jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList;
-jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
-jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter;
-jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements;
-jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
-jfieldID WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
-jfieldID WellKnownClasses::java_io_FileDescriptor_descriptor;
-jfieldID WellKnownClasses::java_lang_ClassLoader_parent;
-jfieldID WellKnownClasses::java_lang_Thread_parkBlocker;
-jfieldID WellKnownClasses::java_lang_Thread_daemon;
-jfieldID WellKnownClasses::java_lang_Thread_group;
-jfieldID WellKnownClasses::java_lang_Thread_lock;
-jfieldID WellKnownClasses::java_lang_Thread_name;
-jfieldID WellKnownClasses::java_lang_Thread_priority;
-jfieldID WellKnownClasses::java_lang_Thread_nativePeer;
-jfieldID WellKnownClasses::java_lang_Thread_systemDaemon;
-jfieldID WellKnownClasses::java_lang_Thread_unparkedBeforeStart;
-jfieldID WellKnownClasses::java_lang_ThreadGroup_groups;
-jfieldID WellKnownClasses::java_lang_ThreadGroup_ngroups;
-jfieldID WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup;
-jfieldID WellKnownClasses::java_lang_ThreadGroup_name;
-jfieldID WellKnownClasses::java_lang_ThreadGroup_parent;
-jfieldID WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup;
-jfieldID WellKnownClasses::java_lang_Throwable_cause;
-jfieldID WellKnownClasses::java_lang_Throwable_detailMessage;
-jfieldID WellKnownClasses::java_lang_Throwable_stackTrace;
-jfieldID WellKnownClasses::java_lang_Throwable_stackState;
-jfieldID WellKnownClasses::java_lang_Throwable_suppressedExceptions;
-jfieldID WellKnownClasses::java_nio_Buffer_address;
-jfieldID WellKnownClasses::java_nio_Buffer_capacity;
-jfieldID WellKnownClasses::java_nio_Buffer_elementSizeShift;
-jfieldID WellKnownClasses::java_nio_Buffer_limit;
-jfieldID WellKnownClasses::java_nio_Buffer_position;
-jfieldID WellKnownClasses::java_nio_ByteBuffer_address;
-jfieldID WellKnownClasses::java_nio_ByteBuffer_hb;
-jfieldID WellKnownClasses::java_nio_ByteBuffer_isReadOnly;
-jfieldID WellKnownClasses::java_nio_ByteBuffer_limit;
-jfieldID WellKnownClasses::java_nio_ByteBuffer_offset;
-jfieldID WellKnownClasses::java_util_Collections_EMPTY_LIST;
-jfieldID WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT;
-jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data;
-jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_length;
-jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_offset;
-jfieldID WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_type;
+ArtField* WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList;
+ArtField* WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
+ArtField* WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter;
+ArtField* WellKnownClasses::dalvik_system_DexFile_cookie;
+ArtField* WellKnownClasses::dalvik_system_DexFile_fileName;
+ArtField* WellKnownClasses::dalvik_system_DexPathList_dexElements;
+ArtField* WellKnownClasses::dalvik_system_DexPathList__Element_dexFile;
+ArtField* WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
+ArtField* WellKnownClasses::java_io_FileDescriptor_descriptor;
+ArtField* WellKnownClasses::java_lang_ClassLoader_parent;
+ArtField* WellKnownClasses::java_lang_Thread_parkBlocker;
+ArtField* WellKnownClasses::java_lang_Thread_daemon;
+ArtField* WellKnownClasses::java_lang_Thread_group;
+ArtField* WellKnownClasses::java_lang_Thread_lock;
+ArtField* WellKnownClasses::java_lang_Thread_name;
+ArtField* WellKnownClasses::java_lang_Thread_priority;
+ArtField* WellKnownClasses::java_lang_Thread_nativePeer;
+ArtField* WellKnownClasses::java_lang_Thread_systemDaemon;
+ArtField* WellKnownClasses::java_lang_Thread_unparkedBeforeStart;
+ArtField* WellKnownClasses::java_lang_ThreadGroup_groups;
+ArtField* WellKnownClasses::java_lang_ThreadGroup_ngroups;
+ArtField* WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup;
+ArtField* WellKnownClasses::java_lang_ThreadGroup_name;
+ArtField* WellKnownClasses::java_lang_ThreadGroup_parent;
+ArtField* WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup;
+ArtField* WellKnownClasses::java_lang_Throwable_cause;
+ArtField* WellKnownClasses::java_lang_Throwable_detailMessage;
+ArtField* WellKnownClasses::java_lang_Throwable_stackTrace;
+ArtField* WellKnownClasses::java_lang_Throwable_stackState;
+ArtField* WellKnownClasses::java_lang_Throwable_suppressedExceptions;
+ArtField* WellKnownClasses::java_nio_Buffer_address;
+ArtField* WellKnownClasses::java_nio_Buffer_capacity;
+ArtField* WellKnownClasses::java_nio_Buffer_elementSizeShift;
+ArtField* WellKnownClasses::java_nio_Buffer_limit;
+ArtField* WellKnownClasses::java_nio_Buffer_position;
+ArtField* WellKnownClasses::java_nio_ByteBuffer_hb;
+ArtField* WellKnownClasses::java_nio_ByteBuffer_isReadOnly;
+ArtField* WellKnownClasses::java_nio_ByteBuffer_offset;
+ArtField* WellKnownClasses::java_util_Collections_EMPTY_LIST;
+ArtField* WellKnownClasses::jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_buffer;
+ArtField* WellKnownClasses::jdk_internal_math_FloatingDecimal_ExceptionalBinaryToASCIIBuffer_image;
+ArtField* WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT;
+ArtField* WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data;
+ArtField* WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_length;
+ArtField* WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_offset;
+ArtField* WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_type;
+
+static ObjPtr<mirror::Class> FindSystemClass(ClassLinker* class_linker,
+ Thread* self,
+ const char* descriptor)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Class> klass = class_linker->FindSystemClass(self, descriptor);
+ CHECK(klass != nullptr) << "Couldn't find system class: " << descriptor;
+ return klass;
+}
static jclass CacheClass(JNIEnv* env, const char* jni_class_name) {
ScopedLocalRef<jclass> c(env, env->FindClass(jni_class_name));
@@ -186,73 +177,57 @@
return reinterpret_cast<jclass>(env->NewGlobalRef(c.get()));
}
-static jfieldID CacheField(JNIEnv* env, jclass c, bool is_static,
- const char* name, const char* signature) {
- jfieldID fid;
- {
- ScopedObjectAccess soa(env);
- if (Runtime::Current()->GetJniIdType() != JniIdType::kSwapablePointer) {
- fid = jni::EncodeArtField</*kEnableIndexIds*/ true>(
- FindFieldJNI(soa, c, name, signature, is_static));
- } else {
- fid = jni::EncodeArtField</*kEnableIndexIds*/ false>(
- FindFieldJNI(soa, c, name, signature, is_static));
- }
- }
- if (fid == nullptr) {
- ScopedObjectAccess soa(env);
- if (soa.Self()->IsExceptionPending()) {
- LOG(FATAL_WITHOUT_ABORT) << soa.Self()->GetException()->Dump();
- }
+static ArtField* CacheField(ObjPtr<mirror::Class> klass,
+ bool is_static,
+ const char* name,
+ const char* signature) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ArtField* field = is_static
+ ? klass->FindDeclaredStaticField(name, signature)
+ : klass->FindDeclaredInstanceField(name, signature);
+ if (UNLIKELY(field == nullptr)) {
std::ostringstream os;
- WellKnownClasses::ToClass(c)->DumpClass(os, mirror::Class::kDumpClassFullDetail);
- LOG(FATAL) << "Couldn't find field \"" << name << "\" with signature \"" << signature << "\": "
- << os.str();
+ klass->DumpClass(os, mirror::Class::kDumpClassFullDetail);
+ LOG(FATAL) << "Couldn't find " << (is_static ? "static" : "instance") << " field \""
+ << name << "\" with signature \"" << signature << "\": " << os.str();
+ UNREACHABLE();
}
- return fid;
+ return field;
}
-static jmethodID CacheMethod(JNIEnv* env, jclass c, bool is_static,
- const char* name, const char* signature) {
- jmethodID mid;
- {
- ScopedObjectAccess soa(env);
- if (Runtime::Current()->GetJniIdType() != JniIdType::kSwapablePointer) {
- mid = jni::EncodeArtMethod</*kEnableIndexIds*/ true>(
- FindMethodJNI(soa, c, name, signature, is_static));
- } else {
- mid = jni::EncodeArtMethod</*kEnableIndexIds*/ false>(
- FindMethodJNI(soa, c, name, signature, is_static));
- }
- }
- if (mid == nullptr) {
- ScopedObjectAccess soa(env);
- if (soa.Self()->IsExceptionPending()) {
- LOG(FATAL_WITHOUT_ABORT) << soa.Self()->GetException()->Dump();
- }
+static ArtMethod* CacheMethod(ObjPtr<mirror::Class> klass,
+ bool is_static,
+ const char* name,
+ const char* signature,
+ PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_) {
+ ArtMethod* method = klass->IsInterface()
+ ? klass->FindInterfaceMethod(name, signature, pointer_size)
+ : klass->FindClassMethod(name, signature, pointer_size);
+ if (UNLIKELY(method == nullptr) || UNLIKELY(is_static != method->IsStatic())) {
std::ostringstream os;
- WellKnownClasses::ToClass(c)->DumpClass(os, mirror::Class::kDumpClassFullDetail);
- LOG(FATAL) << "Couldn't find method \"" << name << "\" with signature \"" << signature << "\": "
- << os.str();
+ klass->DumpClass(os, mirror::Class::kDumpClassFullDetail);
+ LOG(FATAL) << "Couldn't find " << (is_static ? "static" : "instance") << " method \""
+ << name << "\" with signature \"" << signature << "\": " << os.str();
+ UNREACHABLE();
}
- return mid;
+ DCHECK(method->GetDeclaringClass() == klass);
+ return method;
}
-static jmethodID CacheMethod(JNIEnv* env, const char* klass, bool is_static,
- const char* name, const char* signature) {
- ScopedLocalRef<jclass> java_class(env, env->FindClass(klass));
- return CacheMethod(env, java_class.get(), is_static, name, signature);
-}
-
-static jmethodID CachePrimitiveBoxingMethod(JNIEnv* env, char prim_name, const char* boxed_name) {
- ScopedLocalRef<jclass> boxed_class(env, env->FindClass(boxed_name));
- return CacheMethod(env, boxed_class.get(), true, "valueOf",
- android::base::StringPrintf("(%c)L%s;", prim_name, boxed_name).c_str());
+static ArtMethod* CachePrimitiveBoxingMethod(ClassLinker* class_linker,
+ Thread* self,
+ char prim_name,
+ const char* boxed_name)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ ObjPtr<mirror::Class> boxed_class = FindSystemClass(class_linker, self, boxed_name);
+ PointerSize pointer_size = class_linker->GetImagePointerSize();
+ std::string signature = android::base::StringPrintf("(%c)%s", prim_name, boxed_name);
+ return CacheMethod(boxed_class, /*is_static=*/ true, "valueOf", signature.c_str(), pointer_size);
}
#define STRING_INIT_LIST(V) \
V(java_lang_String_init, "()V", newEmptyString, "newEmptyString", "()Ljava/lang/String;", NewEmptyString) \
V(java_lang_String_init_B, "([B)V", newStringFromBytes_B, "newStringFromBytes", "([B)Ljava/lang/String;", NewStringFromBytes_B) \
+ V(java_lang_String_init_BB, "([BB)V", newStringFromBytes_BB, "newStringFromBytes", "([BB)Ljava/lang/String;", NewStringFromBytes_BB) \
V(java_lang_String_init_BI, "([BI)V", newStringFromBytes_BI, "newStringFromBytes", "([BI)Ljava/lang/String;", NewStringFromBytes_BI) \
V(java_lang_String_init_BII, "([BII)V", newStringFromBytes_BII, "newStringFromBytes", "([BII)Ljava/lang/String;", NewStringFromBytes_BII) \
V(java_lang_String_init_BIII, "([BIII)V", newStringFromBytes_BIII, "newStringFromBytes", "([BIII)Ljava/lang/String;", NewStringFromBytes_BIII) \
@@ -345,50 +320,16 @@
dalvik_annotation_optimization_FastNative = CacheClass(env, "dalvik/annotation/optimization/FastNative");
dalvik_annotation_optimization_NeverCompile =
CacheClass(env, "dalvik/annotation/optimization/NeverCompile");
- dalvik_system_BaseDexClassLoader = CacheClass(env, "dalvik/system/BaseDexClassLoader");
- dalvik_system_DelegateLastClassLoader = CacheClass(env, "dalvik/system/DelegateLastClassLoader");
- dalvik_system_DexClassLoader = CacheClass(env, "dalvik/system/DexClassLoader");
- dalvik_system_DexFile = CacheClass(env, "dalvik/system/DexFile");
- dalvik_system_DexPathList = CacheClass(env, "dalvik/system/DexPathList");
- dalvik_system_DexPathList__Element = CacheClass(env, "dalvik/system/DexPathList$Element");
- dalvik_system_EmulatedStackFrame = CacheClass(env, "dalvik/system/EmulatedStackFrame");
- dalvik_system_InMemoryDexClassLoader = CacheClass(env, "dalvik/system/InMemoryDexClassLoader");
- dalvik_system_PathClassLoader = CacheClass(env, "dalvik/system/PathClassLoader");
- dalvik_system_VMRuntime = CacheClass(env, "dalvik/system/VMRuntime");
+ dalvik_annotation_optimization_NeverInline =
+ CacheClass(env, "dalvik/annotation/optimization/NeverInline");
java_lang_annotation_Annotation__array = CacheClass(env, "[Ljava/lang/annotation/Annotation;");
- java_lang_BootClassLoader = CacheClass(env, "java/lang/BootClassLoader");
- java_lang_ClassLoader = CacheClass(env, "java/lang/ClassLoader");
- java_lang_ClassNotFoundException = CacheClass(env, "java/lang/ClassNotFoundException");
- java_lang_Daemons = CacheClass(env, "java/lang/Daemons");
- java_lang_Object = CacheClass(env, "java/lang/Object");
- java_lang_OutOfMemoryError = CacheClass(env, "java/lang/OutOfMemoryError");
- java_lang_Error = CacheClass(env, "java/lang/Error");
- java_lang_IllegalAccessError = CacheClass(env, "java/lang/IllegalAccessError");
- java_lang_NoClassDefFoundError = CacheClass(env, "java/lang/NoClassDefFoundError");
- java_lang_reflect_InvocationTargetException = CacheClass(env, "java/lang/reflect/InvocationTargetException");
- java_lang_reflect_Parameter = CacheClass(env, "java/lang/reflect/Parameter");
+ java_lang_ClassValue = CacheClass(env, "java/lang/ClassValue");
java_lang_reflect_Parameter__array = CacheClass(env, "[Ljava/lang/reflect/Parameter;");
- java_lang_reflect_Proxy = CacheClass(env, "java/lang/reflect/Proxy");
- java_lang_RuntimeException = CacheClass(env, "java/lang/RuntimeException");
- java_lang_StackOverflowError = CacheClass(env, "java/lang/StackOverflowError");
- java_lang_String = CacheClass(env, "java/lang/String");
java_lang_StringFactory = CacheClass(env, "java/lang/StringFactory");
java_lang_System = CacheClass(env, "java/lang/System");
- java_lang_Thread = CacheClass(env, "java/lang/Thread");
- java_lang_ThreadGroup = CacheClass(env, "java/lang/ThreadGroup");
- java_lang_Throwable = CacheClass(env, "java/lang/Throwable");
java_lang_Void = CacheClass(env, "java/lang/Void");
- java_nio_Buffer = CacheClass(env, "java/nio/Buffer");
- java_nio_ByteBuffer = CacheClass(env, "java/nio/ByteBuffer");
- java_nio_DirectByteBuffer = CacheClass(env, "java/nio/DirectByteBuffer");
- java_util_Collections = CacheClass(env, "java/util/Collections");
- java_util_function_Consumer = CacheClass(env, "java/util/function/Consumer");
- libcore_reflect_AnnotationFactory = CacheClass(env, "libcore/reflect/AnnotationFactory");
- libcore_reflect_AnnotationMember = CacheClass(env, "libcore/reflect/AnnotationMember");
- libcore_util_EmptyArray = CacheClass(env, "libcore/util/EmptyArray");
- org_apache_harmony_dalvik_ddmc_Chunk = CacheClass(env, "org/apache/harmony/dalvik/ddmc/Chunk");
- org_apache_harmony_dalvik_ddmc_DdmServer = CacheClass(env, "org/apache/harmony/dalvik/ddmc/DdmServer");
+ libcore_reflect_AnnotationMember__array = CacheClass(env, "[Llibcore/reflect/AnnotationMember;");
InitFieldsAndMethodsOnly(env);
}
@@ -397,149 +338,453 @@
hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption(
hiddenapi::EnforcementPolicy::kDisabled);
- dalvik_system_BaseDexClassLoader_getLdLibraryPath = CacheMethod(env, dalvik_system_BaseDexClassLoader, false, "getLdLibraryPath", "()Ljava/lang/String;");
- dalvik_system_VMRuntime_runFinalization = CacheMethod(env, dalvik_system_VMRuntime, true, "runFinalization", "(J)V");
- dalvik_system_VMRuntime_hiddenApiUsed = CacheMethod(env, dalvik_system_VMRuntime, true, "hiddenApiUsed", "(ILjava/lang/String;Ljava/lang/String;IZ)V");
+ Thread* self = Thread::ForEnv(env);
+ ScopedObjectAccess soa(self);
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- java_lang_ClassNotFoundException_init = CacheMethod(env, java_lang_ClassNotFoundException, false, "<init>", "(Ljava/lang/String;Ljava/lang/Throwable;)V");
- java_lang_ClassLoader_loadClass = CacheMethod(env, java_lang_ClassLoader, false, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
+ java_lang_Boolean_valueOf =
+ CachePrimitiveBoxingMethod(class_linker, self, 'Z', "Ljava/lang/Boolean;");
+ java_lang_Byte_valueOf =
+ CachePrimitiveBoxingMethod(class_linker, self, 'B', "Ljava/lang/Byte;");
+ java_lang_Character_valueOf =
+ CachePrimitiveBoxingMethod(class_linker, self, 'C', "Ljava/lang/Character;");
+ java_lang_Double_valueOf =
+ CachePrimitiveBoxingMethod(class_linker, self, 'D', "Ljava/lang/Double;");
+ java_lang_Float_valueOf =
+ CachePrimitiveBoxingMethod(class_linker, self, 'F', "Ljava/lang/Float;");
+ java_lang_Integer_valueOf =
+ CachePrimitiveBoxingMethod(class_linker, self, 'I', "Ljava/lang/Integer;");
+ java_lang_Long_valueOf =
+ CachePrimitiveBoxingMethod(class_linker, self, 'J', "Ljava/lang/Long;");
+ java_lang_Short_valueOf =
+ CachePrimitiveBoxingMethod(class_linker, self, 'S', "Ljava/lang/Short;");
- java_lang_Daemons_start = CacheMethod(env, java_lang_Daemons, true, "start", "()V");
- java_lang_Daemons_stop = CacheMethod(env, java_lang_Daemons, true, "stop", "()V");
- java_lang_Daemons_waitForDaemonStart = CacheMethod(env, java_lang_Daemons, true, "waitForDaemonStart", "()V");
- java_lang_invoke_MethodHandle_asType = CacheMethod(env, "java/lang/invoke/MethodHandle", false, "asType", "(Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;");
- java_lang_invoke_MethodHandle_invokeExact = CacheMethod(env, "java/lang/invoke/MethodHandle", false, "invokeExact", "([Ljava/lang/Object;)Ljava/lang/Object;");
- java_lang_invoke_MethodHandles_lookup = CacheMethod(env, "java/lang/invoke/MethodHandles", true, "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;");
- java_lang_invoke_MethodHandles_Lookup_findConstructor = CacheMethod(env, "java/lang/invoke/MethodHandles$Lookup", false, "findConstructor", "(Ljava/lang/Class;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;");
+ StackHandleScope<42u> hs(self);
+ Handle<mirror::Class> d_s_bdcl =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ldalvik/system/BaseDexClassLoader;"));
+ Handle<mirror::Class> d_s_dlcl =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ldalvik/system/DelegateLastClassLoader;"));
+ Handle<mirror::Class> d_s_dcl =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ldalvik/system/DexClassLoader;"));
+ Handle<mirror::Class> d_s_df =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ldalvik/system/DexFile;"));
+ Handle<mirror::Class> d_s_dpl =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ldalvik/system/DexPathList;"));
+ Handle<mirror::Class> d_s_dpl_e =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ldalvik/system/DexPathList$Element;"));
+ Handle<mirror::Class> d_s_imdcl =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ldalvik/system/InMemoryDexClassLoader;"));
+ Handle<mirror::Class> d_s_pcl =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ldalvik/system/PathClassLoader;"));
+ Handle<mirror::Class> d_s_vmr =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ldalvik/system/VMRuntime;"));
+ Handle<mirror::Class> j_i_fd =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/io/FileDescriptor;"));
+ Handle<mirror::Class> j_l_bcl =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/BootClassLoader;"));
+ Handle<mirror::Class> j_l_cl =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/ClassLoader;"));
+ Handle<mirror::Class> j_l_cnfe =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/ClassNotFoundException;"));
+ Handle<mirror::Class> j_l_Daemons =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/Daemons;"));
+ Handle<mirror::Class> j_l_Error =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/Error;"));
+ Handle<mirror::Class> j_l_IllegalAccessError =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/IllegalAccessError;"));
+ Handle<mirror::Class> j_l_NoClassDefFoundError =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/NoClassDefFoundError;"));
+ Handle<mirror::Class> j_l_OutOfMemoryError =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/OutOfMemoryError;"));
+ Handle<mirror::Class> j_l_RuntimeException =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/RuntimeException;"));
+ Handle<mirror::Class> j_l_StackOverflowError =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/StackOverflowError;"));
+ Handle<mirror::Class> j_l_Thread =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/Thread;"));
+ Handle<mirror::Class> j_l_tg =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/ThreadGroup;"));
+ Handle<mirror::Class> j_l_i_MethodHandle =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/invoke/MethodHandle;"));
+ Handle<mirror::Class> j_l_i_MethodHandles =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/invoke/MethodHandles;"));
+ Handle<mirror::Class> j_l_i_MethodHandles_Lookup =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/invoke/MethodHandles$Lookup;"));
+ Handle<mirror::Class> j_l_r_fr =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/ref/FinalizerReference;"));
+ Handle<mirror::Class> j_l_r_rq =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/ref/ReferenceQueue;"));
+ Handle<mirror::Class> j_l_rl_ite = hs.NewHandle(
+ FindSystemClass(class_linker, self, "Ljava/lang/reflect/InvocationTargetException;"));
+ Handle<mirror::Class> j_l_rl_Parameter =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/reflect/Parameter;"));
+ Handle<mirror::Class> j_n_b =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/nio/Buffer;"));
+ Handle<mirror::Class> j_n_bb =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/nio/ByteBuffer;"));
+ Handle<mirror::Class> j_n_dbb =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/nio/DirectByteBuffer;"));
+ Handle<mirror::Class> j_u_c =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/util/Collections;"));
+ Handle<mirror::Class> j_u_f_c =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/util/function/Consumer;"));
+ Handle<mirror::Class> j_i_m_fd =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljdk/internal/math/FloatingDecimal;"));
+ Handle<mirror::Class> j_i_m_fd_btab = hs.NewHandle(FindSystemClass(
+ class_linker, self, "Ljdk/internal/math/FloatingDecimal$BinaryToASCIIBuffer;"));
+ Handle<mirror::Class> j_i_m_fd_ebtab = hs.NewHandle(FindSystemClass(
+ class_linker, self, "Ljdk/internal/math/FloatingDecimal$ExceptionalBinaryToASCIIBuffer;"));
+ Handle<mirror::Class> l_r_af =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Llibcore/reflect/AnnotationFactory;"));
+ Handle<mirror::Class> l_r_am =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Llibcore/reflect/AnnotationMember;"));
+ Handle<mirror::Class> l_u_ea =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Llibcore/util/EmptyArray;"));
+ Handle<mirror::Class> o_a_h_d_c =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Lorg/apache/harmony/dalvik/ddmc/Chunk;"));
+ Handle<mirror::Class> o_a_h_d_d_ds =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Lorg/apache/harmony/dalvik/ddmc/DdmServer;"));
- java_lang_ref_FinalizerReference_add = CacheMethod(env, "java/lang/ref/FinalizerReference", true, "add", "(Ljava/lang/Object;)V");
- java_lang_ref_ReferenceQueue_add = CacheMethod(env, "java/lang/ref/ReferenceQueue", true, "add", "(Ljava/lang/ref/Reference;)V");
+ ScopedAssertNoThreadSuspension sants(__FUNCTION__);
+ PointerSize pointer_size = class_linker->GetImagePointerSize();
- java_lang_reflect_InvocationTargetException_init = CacheMethod(env, java_lang_reflect_InvocationTargetException, false, "<init>", "(Ljava/lang/Throwable;)V");
- java_lang_reflect_Parameter_init = CacheMethod(env, java_lang_reflect_Parameter, false, "<init>", "(Ljava/lang/String;ILjava/lang/reflect/Executable;I)V");
- java_lang_String_charAt = CacheMethod(env, java_lang_String, false, "charAt", "(I)C");
- java_lang_Thread_dispatchUncaughtException = CacheMethod(env, java_lang_Thread, false, "dispatchUncaughtException", "(Ljava/lang/Throwable;)V");
- java_lang_Thread_init = CacheMethod(env, java_lang_Thread, false, "<init>", "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
- java_lang_Thread_run = CacheMethod(env, java_lang_Thread, false, "run", "()V");
- java_lang_ThreadGroup_add = CacheMethod(env, java_lang_ThreadGroup, false, "add", "(Ljava/lang/Thread;)V");
- java_lang_ThreadGroup_removeThread = CacheMethod(env, java_lang_ThreadGroup, false, "threadTerminated", "(Ljava/lang/Thread;)V");
- java_nio_Buffer_isDirect = CacheMethod(env, java_nio_Buffer, false, "isDirect", "()Z");
- java_nio_DirectByteBuffer_init = CacheMethod(env, java_nio_DirectByteBuffer, false, "<init>", "(JI)V");
- java_util_function_Consumer_accept = CacheMethod(env, java_util_function_Consumer, false, "accept", "(Ljava/lang/Object;)V");
- libcore_reflect_AnnotationFactory_createAnnotation = CacheMethod(env, libcore_reflect_AnnotationFactory, true, "createAnnotation", "(Ljava/lang/Class;[Llibcore/reflect/AnnotationMember;)Ljava/lang/annotation/Annotation;");
- libcore_reflect_AnnotationMember_init = CacheMethod(env, libcore_reflect_AnnotationMember, false, "<init>", "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/reflect/Method;)V");
- org_apache_harmony_dalvik_ddmc_DdmServer_broadcast = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "broadcast", "(I)V");
- org_apache_harmony_dalvik_ddmc_DdmServer_dispatch = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "dispatch", "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
+ dalvik_system_BaseDexClassLoader_getLdLibraryPath = CacheMethod(
+ d_s_bdcl.Get(),
+ /*is_static=*/ false,
+ "getLdLibraryPath",
+ "()Ljava/lang/String;",
+ pointer_size);
+ dalvik_system_DelegateLastClassLoader_init = CacheMethod(
+ d_s_dlcl.Get(),
+ /*is_static=*/ false,
+ "<init>",
+ "(Ljava/lang/String;Ljava/lang/ClassLoader;)V",
+ pointer_size);
+ dalvik_system_DexClassLoader_init = CacheMethod(
+ d_s_dcl.Get(),
+ /*is_static=*/ false,
+ "<init>",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V",
+ pointer_size);
+ dalvik_system_InMemoryDexClassLoader_init = CacheMethod(
+ d_s_imdcl.Get(),
+ /*is_static=*/ false,
+ "<init>",
+ "(Ljava/nio/ByteBuffer;Ljava/lang/ClassLoader;)V",
+ pointer_size);
+ dalvik_system_PathClassLoader_init = CacheMethod(
+ d_s_pcl.Get(),
+ /*is_static=*/ false,
+ "<init>",
+ "(Ljava/lang/String;Ljava/lang/ClassLoader;)V",
+ pointer_size);
- dalvik_system_BaseDexClassLoader_pathList = CacheField(env, dalvik_system_BaseDexClassLoader, false, "pathList", "Ldalvik/system/DexPathList;");
- dalvik_system_BaseDexClassLoader_sharedLibraryLoaders = CacheField(env, dalvik_system_BaseDexClassLoader, false, "sharedLibraryLoaders", "[Ljava/lang/ClassLoader;");
- dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter = CacheField(env, dalvik_system_BaseDexClassLoader, false, "sharedLibraryLoadersAfter", "[Ljava/lang/ClassLoader;");
- dalvik_system_DexFile_cookie = CacheField(env, dalvik_system_DexFile, false, "mCookie", "Ljava/lang/Object;");
- dalvik_system_DexFile_fileName = CacheField(env, dalvik_system_DexFile, false, "mFileName", "Ljava/lang/String;");
- dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;");
- dalvik_system_DexPathList__Element_dexFile = CacheField(env, dalvik_system_DexPathList__Element, false, "dexFile", "Ldalvik/system/DexFile;");
- dalvik_system_VMRuntime_nonSdkApiUsageConsumer = CacheField(env, dalvik_system_VMRuntime, true, "nonSdkApiUsageConsumer", "Ljava/util/function/Consumer;");
+ dalvik_system_VMRuntime_hiddenApiUsed = CacheMethod(
+ d_s_vmr.Get(),
+ /*is_static=*/ true,
+ "hiddenApiUsed",
+ "(ILjava/lang/String;Ljava/lang/String;IZ)V",
+ pointer_size);
- ScopedLocalRef<jclass> java_io_FileDescriptor(env, env->FindClass("java/io/FileDescriptor"));
- java_io_FileDescriptor_descriptor = CacheField(env, java_io_FileDescriptor.get(), false, "descriptor", "I");
+ java_lang_BootClassLoader_init =
+ CacheMethod(j_l_bcl.Get(), /*is_static=*/ false, "<init>", "()V", pointer_size);
+ java_lang_ClassLoader_loadClass = CacheMethod(
+ j_l_cl.Get(),
+ /*is_static=*/ false,
+ "loadClass",
+ "(Ljava/lang/String;)Ljava/lang/Class;",
+ pointer_size);
- java_lang_ClassLoader_parent =
- CacheField(env, java_lang_ClassLoader, false, "parent", "Ljava/lang/ClassLoader;");
- java_lang_Thread_parkBlocker =
- CacheField(env, java_lang_Thread, false, "parkBlocker", "Ljava/lang/Object;");
- java_lang_Thread_daemon = CacheField(env, java_lang_Thread, false, "daemon", "Z");
- java_lang_Thread_group =
- CacheField(env, java_lang_Thread, false, "group", "Ljava/lang/ThreadGroup;");
- java_lang_Thread_lock = CacheField(env, java_lang_Thread, false, "lock", "Ljava/lang/Object;");
- java_lang_Thread_name = CacheField(env, java_lang_Thread, false, "name", "Ljava/lang/String;");
- java_lang_Thread_priority = CacheField(env, java_lang_Thread, false, "priority", "I");
- java_lang_Thread_nativePeer = CacheField(env, java_lang_Thread, false, "nativePeer", "J");
- java_lang_Thread_systemDaemon = CacheField(env, java_lang_Thread, false, "systemDaemon", "Z");
- java_lang_Thread_unparkedBeforeStart =
- CacheField(env, java_lang_Thread, false, "unparkedBeforeStart", "Z");
- java_lang_ThreadGroup_groups =
- CacheField(env, java_lang_ThreadGroup, false, "groups", "[Ljava/lang/ThreadGroup;");
- java_lang_ThreadGroup_ngroups = CacheField(env, java_lang_ThreadGroup, false, "ngroups", "I");
- java_lang_ThreadGroup_mainThreadGroup =
- CacheField(env, java_lang_ThreadGroup, true, "mainThreadGroup", "Ljava/lang/ThreadGroup;");
- java_lang_ThreadGroup_name =
- CacheField(env, java_lang_ThreadGroup, false, "name", "Ljava/lang/String;");
- java_lang_ThreadGroup_parent =
- CacheField(env, java_lang_ThreadGroup, false, "parent", "Ljava/lang/ThreadGroup;");
- java_lang_ThreadGroup_systemThreadGroup =
- CacheField(env, java_lang_ThreadGroup, true, "systemThreadGroup", "Ljava/lang/ThreadGroup;");
- java_lang_Throwable_cause =
- CacheField(env, java_lang_Throwable, false, "cause", "Ljava/lang/Throwable;");
- java_lang_Throwable_detailMessage =
- CacheField(env, java_lang_Throwable, false, "detailMessage", "Ljava/lang/String;");
- java_lang_Throwable_stackTrace =
- CacheField(env, java_lang_Throwable, false, "stackTrace", "[Ljava/lang/StackTraceElement;");
- java_lang_Throwable_stackState =
- CacheField(env, java_lang_Throwable, false, "backtrace", "Ljava/lang/Object;");
- java_lang_Throwable_suppressedExceptions =
- CacheField(env, java_lang_Throwable, false, "suppressedExceptions", "Ljava/util/List;");
+ java_lang_ClassNotFoundException_init = CacheMethod(
+ j_l_cnfe.Get(),
+ /*is_static=*/ false,
+ "<init>",
+ "(Ljava/lang/String;Ljava/lang/Throwable;)V",
+ pointer_size);
- java_nio_Buffer_address = CacheField(env, java_nio_Buffer, false, "address", "J");
- java_nio_Buffer_capacity = CacheField(env, java_nio_Buffer, false, "capacity", "I");
- java_nio_Buffer_elementSizeShift =
- CacheField(env, java_nio_Buffer, false, "_elementSizeShift", "I");
- java_nio_Buffer_limit = CacheField(env, java_nio_Buffer, false, "limit", "I");
- java_nio_Buffer_position = CacheField(env, java_nio_Buffer, false, "position", "I");
-
- java_nio_ByteBuffer_address = CacheField(env, java_nio_ByteBuffer, false, "address", "J");
- java_nio_ByteBuffer_hb = CacheField(env, java_nio_ByteBuffer, false, "hb", "[B");
- java_nio_ByteBuffer_isReadOnly = CacheField(env, java_nio_ByteBuffer, false, "isReadOnly", "Z");
- java_nio_ByteBuffer_limit = CacheField(env, java_nio_ByteBuffer, false, "limit", "I");
- java_nio_ByteBuffer_offset = CacheField(env, java_nio_ByteBuffer, false, "offset", "I");
- java_util_Collections_EMPTY_LIST =
- CacheField(env, java_util_Collections, true, "EMPTY_LIST", "Ljava/util/List;");
- libcore_util_EmptyArray_STACK_TRACE_ELEMENT = CacheField(
- env, libcore_util_EmptyArray, true, "STACK_TRACE_ELEMENT", "[Ljava/lang/StackTraceElement;");
- org_apache_harmony_dalvik_ddmc_Chunk_data =
- CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "data", "[B");
- org_apache_harmony_dalvik_ddmc_Chunk_length =
- CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "length", "I");
- org_apache_harmony_dalvik_ddmc_Chunk_offset =
- CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "offset", "I");
- org_apache_harmony_dalvik_ddmc_Chunk_type =
- CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "type", "I");
-
- java_lang_Boolean_valueOf = CachePrimitiveBoxingMethod(env, 'Z', "java/lang/Boolean");
- java_lang_Byte_valueOf = CachePrimitiveBoxingMethod(env, 'B', "java/lang/Byte");
- java_lang_Character_valueOf = CachePrimitiveBoxingMethod(env, 'C', "java/lang/Character");
- java_lang_Double_valueOf = CachePrimitiveBoxingMethod(env, 'D', "java/lang/Double");
- java_lang_Float_valueOf = CachePrimitiveBoxingMethod(env, 'F', "java/lang/Float");
- java_lang_Integer_valueOf = CachePrimitiveBoxingMethod(env, 'I', "java/lang/Integer");
- java_lang_Long_valueOf = CachePrimitiveBoxingMethod(env, 'J', "java/lang/Long");
- java_lang_Short_valueOf = CachePrimitiveBoxingMethod(env, 'S', "java/lang/Short");
-
+ ObjPtr<mirror::Class> j_l_Double = java_lang_Double_valueOf->GetDeclaringClass();
java_lang_Double_doubleToRawLongBits =
- CacheMethod(env, "java/lang/Double", /*is_static=*/ true, "doubleToRawLongBits", "(D)J");
+ CacheMethod(j_l_Double, /*is_static=*/ true, "doubleToRawLongBits", "(D)J", pointer_size);
+ ObjPtr<mirror::Class> j_l_Float = java_lang_Float_valueOf->GetDeclaringClass();
java_lang_Float_floatToRawIntBits =
- CacheMethod(env, "java/lang/Float", /*is_static=*/ true, "floatToRawIntBits", "(F)I");
+ CacheMethod(j_l_Float, /*is_static=*/ true, "floatToRawIntBits", "(F)I", pointer_size);
+
+ java_lang_Daemons_start = CacheMethod(
+ j_l_Daemons.Get(), /*is_static=*/ true, "start", "()V", pointer_size);
+ java_lang_Daemons_stop = CacheMethod(
+ j_l_Daemons.Get(), /*is_static=*/ true, "stop", "()V", pointer_size);
+ java_lang_Daemons_waitForDaemonStart = CacheMethod(
+ j_l_Daemons.Get(), /*is_static=*/ true, "waitForDaemonStart", "()V", pointer_size);
+
+ java_lang_Error_init = CacheMethod(
+ j_l_Error.Get(), /*is_static=*/ false, "<init>", "()V", pointer_size);
+ java_lang_IllegalAccessError_init = CacheMethod(
+ j_l_IllegalAccessError.Get(), /*is_static=*/ false, "<init>", "()V", pointer_size);
+ java_lang_NoClassDefFoundError_init = CacheMethod(
+ j_l_NoClassDefFoundError.Get(), /*is_static=*/ false, "<init>", "()V", pointer_size);
+ java_lang_OutOfMemoryError_init = CacheMethod(
+ j_l_OutOfMemoryError.Get(), /*is_static=*/ false, "<init>", "()V", pointer_size);
+ java_lang_RuntimeException_init = CacheMethod(
+ j_l_RuntimeException.Get(), /*is_static=*/ false, "<init>", "()V", pointer_size);
+ java_lang_StackOverflowError_init = CacheMethod(
+ j_l_StackOverflowError.Get(), /*is_static=*/ false, "<init>", "()V", pointer_size);
+
+ ObjPtr<mirror::Class> j_l_String = GetClassRoot<mirror::String>(class_linker);
+ java_lang_String_charAt = CacheMethod(
+ j_l_String, /*is_static=*/ false, "charAt", "(I)C", pointer_size);
+
+ java_lang_Thread_dispatchUncaughtException = CacheMethod(
+ j_l_Thread.Get(),
+ /*is_static=*/ false,
+ "dispatchUncaughtException",
+ "(Ljava/lang/Throwable;)V",
+ pointer_size);
+ java_lang_Thread_init = CacheMethod(
+ j_l_Thread.Get(),
+ /*is_static=*/ false,
+ "<init>",
+ "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V",
+ pointer_size);
+ java_lang_Thread_run = CacheMethod(
+ j_l_Thread.Get(), /*is_static=*/ false, "run", "()V", pointer_size);
+ java_lang_ThreadGroup_add = CacheMethod(
+ j_l_tg.Get(), /*is_static=*/ false, "add", "(Ljava/lang/Thread;)V", pointer_size);
+ java_lang_ThreadGroup_threadTerminated = CacheMethod(
+ j_l_tg.Get(),
+ /*is_static=*/ false,
+ "threadTerminated",
+ "(Ljava/lang/Thread;)V",
+ pointer_size);
+
+ java_lang_invoke_MethodHandle_asType = CacheMethod(
+ j_l_i_MethodHandle.Get(),
+ /*is_static=*/ false,
+ "asType",
+ "(Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;",
+ pointer_size);
+ java_lang_invoke_MethodHandle_invokeExact = CacheMethod(
+ j_l_i_MethodHandle.Get(),
+ /*is_static=*/ false,
+ "invokeExact",
+ "([Ljava/lang/Object;)Ljava/lang/Object;",
+ pointer_size);
+ java_lang_invoke_MethodHandles_lookup = CacheMethod(
+ j_l_i_MethodHandles.Get(),
+ /*is_static=*/ true,
+ "lookup",
+ "()Ljava/lang/invoke/MethodHandles$Lookup;",
+ pointer_size);
+ java_lang_invoke_MethodHandles_Lookup_findConstructor = CacheMethod(
+ j_l_i_MethodHandles_Lookup.Get(),
+ /*is_static=*/ false,
+ "findConstructor",
+ "(Ljava/lang/Class;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;",
+ pointer_size);
+
+ java_lang_ref_FinalizerReference_add = CacheMethod(
+ j_l_r_fr.Get(), /*is_static=*/ true, "add", "(Ljava/lang/Object;)V", pointer_size);
+ java_lang_ref_ReferenceQueue_add = CacheMethod(
+ j_l_r_rq.Get(), /*is_static=*/ true, "add", "(Ljava/lang/ref/Reference;)V", pointer_size);
+
+ java_lang_reflect_InvocationTargetException_init = CacheMethod(
+ j_l_rl_ite.Get(), /*is_static=*/ false, "<init>", "(Ljava/lang/Throwable;)V", pointer_size);
+ java_lang_reflect_Parameter_init = CacheMethod(
+ j_l_rl_Parameter.Get(),
+ /*is_static=*/ false,
+ "<init>",
+ "(Ljava/lang/String;ILjava/lang/reflect/Executable;I)V",
+ pointer_size);
+
+ ObjPtr<mirror::Class> j_l_rl_Proxy = GetClassRoot<mirror::Proxy>(class_linker);
+ java_lang_reflect_Proxy_init = CacheMethod(
+ j_l_rl_Proxy,
+ /*is_static=*/ false,
+ "<init>",
+ "(Ljava/lang/reflect/InvocationHandler;)V",
+ pointer_size);
+ java_lang_reflect_Proxy_invoke = CacheMethod(
+ j_l_rl_Proxy,
+ /*is_static=*/ true,
+ "invoke",
+ "(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;",
+ pointer_size);
+
+ java_nio_Buffer_isDirect =
+ CacheMethod(j_n_b.Get(), /*is_static=*/ false, "isDirect", "()Z", pointer_size);
+ java_nio_DirectByteBuffer_init =
+ CacheMethod(j_n_dbb.Get(), /*is_static=*/ false, "<init>", "(JI)V", pointer_size);
+
+ java_util_function_Consumer_accept = CacheMethod(
+ j_u_f_c.Get(), /*is_static=*/ false, "accept", "(Ljava/lang/Object;)V", pointer_size);
+
+ jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_D = CacheMethod(
+ j_i_m_fd.Get(),
+ /*is_static=*/ true,
+ "getBinaryToASCIIConverter",
+ "(D)Ljdk/internal/math/FloatingDecimal$BinaryToASCIIConverter;",
+ pointer_size);
+ jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_F = CacheMethod(
+ j_i_m_fd.Get(),
+ /*is_static=*/ true,
+ "getBinaryToASCIIConverter",
+ "(F)Ljdk/internal/math/FloatingDecimal$BinaryToASCIIConverter;",
+ pointer_size);
+ jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_getChars =
+ CacheMethod(j_i_m_fd_btab.Get(), /*is_static=*/ false, "getChars", "([C)I", pointer_size);
+
+ libcore_reflect_AnnotationFactory_createAnnotation = CacheMethod(
+ l_r_af.Get(),
+ /*is_static=*/ true,
+ "createAnnotation",
+ "(Ljava/lang/Class;[Llibcore/reflect/AnnotationMember;)Ljava/lang/annotation/Annotation;",
+ pointer_size);
+ libcore_reflect_AnnotationMember_init = CacheMethod(
+ l_r_am.Get(),
+ /*is_static=*/ false,
+ "<init>",
+ "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/reflect/Method;)V",
+ pointer_size);
+
+ org_apache_harmony_dalvik_ddmc_DdmServer_broadcast =
+ CacheMethod(o_a_h_d_d_ds.Get(), /*is_static=*/ true, "broadcast", "(I)V", pointer_size);
+ org_apache_harmony_dalvik_ddmc_DdmServer_dispatch = CacheMethod(
+ o_a_h_d_d_ds.Get(),
+ /*is_static=*/ true,
+ "dispatch",
+ "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;",
+ pointer_size);
+
+ dalvik_system_BaseDexClassLoader_pathList = CacheField(
+ d_s_bdcl.Get(), /*is_static=*/ false, "pathList", "Ldalvik/system/DexPathList;");
+ dalvik_system_BaseDexClassLoader_sharedLibraryLoaders = CacheField(
+ d_s_bdcl.Get(), /*is_static=*/ false, "sharedLibraryLoaders", "[Ljava/lang/ClassLoader;");
+ dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter = CacheField(
+ d_s_bdcl.Get(),
+ /*is_static=*/ false,
+ "sharedLibraryLoadersAfter",
+ "[Ljava/lang/ClassLoader;");
+ dalvik_system_DexFile_cookie = CacheField(
+ d_s_df.Get(), /*is_static=*/ false, "mCookie", "Ljava/lang/Object;");
+ dalvik_system_DexFile_fileName = CacheField(
+ d_s_df.Get(), /*is_static=*/ false, "mFileName", "Ljava/lang/String;");
+ dalvik_system_DexPathList_dexElements = CacheField(
+ d_s_dpl.Get(), /*is_static=*/ false, "dexElements", "[Ldalvik/system/DexPathList$Element;");
+ dalvik_system_DexPathList__Element_dexFile = CacheField(
+ d_s_dpl_e.Get(), /*is_static=*/ false, "dexFile", "Ldalvik/system/DexFile;");
+
+ dalvik_system_VMRuntime_nonSdkApiUsageConsumer = CacheField(
+ d_s_vmr.Get(),
+ /*is_static=*/ true,
+ "nonSdkApiUsageConsumer",
+ "Ljava/util/function/Consumer;");
+
+ java_io_FileDescriptor_descriptor = CacheField(
+ j_i_fd.Get(), /*is_static=*/ false, "descriptor", "I");
+
+ java_lang_ClassLoader_parent = CacheField(
+ j_l_cl.Get(), /*is_static=*/ false, "parent", "Ljava/lang/ClassLoader;");
+
+ java_lang_Thread_parkBlocker =
+ CacheField(j_l_Thread.Get(), /*is_static=*/ false, "parkBlocker", "Ljava/lang/Object;");
+ java_lang_Thread_daemon = CacheField(j_l_Thread.Get(), /*is_static=*/ false, "daemon", "Z");
+ java_lang_Thread_group =
+ CacheField(j_l_Thread.Get(), /*is_static=*/ false, "group", "Ljava/lang/ThreadGroup;");
+ java_lang_Thread_lock =
+ CacheField(j_l_Thread.Get(), /*is_static=*/ false, "lock", "Ljava/lang/Object;");
+ java_lang_Thread_name =
+ CacheField(j_l_Thread.Get(), /*is_static=*/ false, "name", "Ljava/lang/String;");
+ java_lang_Thread_priority = CacheField(j_l_Thread.Get(), /*is_static=*/ false, "priority", "I");
+ java_lang_Thread_nativePeer =
+ CacheField(j_l_Thread.Get(), /*is_static=*/ false, "nativePeer", "J");
+ java_lang_Thread_systemDaemon =
+ CacheField(j_l_Thread.Get(), /*is_static=*/ false, "systemDaemon", "Z");
+ java_lang_Thread_unparkedBeforeStart =
+ CacheField(j_l_Thread.Get(), /*is_static=*/ false, "unparkedBeforeStart", "Z");
+
+ java_lang_ThreadGroup_groups =
+ CacheField(j_l_tg.Get(), /*is_static=*/ false, "groups", "[Ljava/lang/ThreadGroup;");
+ java_lang_ThreadGroup_ngroups = CacheField(j_l_tg.Get(), /*is_static=*/ false, "ngroups", "I");
+ java_lang_ThreadGroup_mainThreadGroup =
+ CacheField(j_l_tg.Get(), /*is_static=*/ true, "mainThreadGroup", "Ljava/lang/ThreadGroup;");
+ java_lang_ThreadGroup_name =
+ CacheField(j_l_tg.Get(), /*is_static=*/ false, "name", "Ljava/lang/String;");
+ java_lang_ThreadGroup_parent =
+ CacheField(j_l_tg.Get(), /*is_static=*/ false, "parent", "Ljava/lang/ThreadGroup;");
+ java_lang_ThreadGroup_systemThreadGroup =
+ CacheField(j_l_tg.Get(), /*is_static=*/ true, "systemThreadGroup", "Ljava/lang/ThreadGroup;");
+
+ ObjPtr<mirror::Class> j_l_Throwable = GetClassRoot<mirror::Throwable>(class_linker);
+ java_lang_Throwable_cause = CacheField(
+ j_l_Throwable, /*is_static=*/ false, "cause", "Ljava/lang/Throwable;");
+ java_lang_Throwable_detailMessage = CacheField(
+ j_l_Throwable, /*is_static=*/ false, "detailMessage", "Ljava/lang/String;");
+ java_lang_Throwable_stackTrace = CacheField(
+ j_l_Throwable, /*is_static=*/ false, "stackTrace", "[Ljava/lang/StackTraceElement;");
+ java_lang_Throwable_stackState = CacheField(
+ j_l_Throwable, /*is_static=*/ false, "backtrace", "Ljava/lang/Object;");
+ java_lang_Throwable_suppressedExceptions = CacheField(
+ j_l_Throwable, /*is_static=*/ false, "suppressedExceptions", "Ljava/util/List;");
+
+ java_nio_Buffer_address = CacheField(j_n_b.Get(), /*is_static=*/ false, "address", "J");
+ java_nio_Buffer_capacity = CacheField(j_n_b.Get(), /*is_static=*/ false, "capacity", "I");
+ java_nio_Buffer_elementSizeShift =
+ CacheField(j_n_b.Get(), /*is_static=*/ false, "_elementSizeShift", "I");
+ java_nio_Buffer_limit = CacheField(j_n_b.Get(), /*is_static=*/ false, "limit", "I");
+ java_nio_Buffer_position = CacheField(j_n_b.Get(), /*is_static=*/ false, "position", "I");
+
+ java_nio_ByteBuffer_hb = CacheField(j_n_bb.Get(), /*is_static=*/ false, "hb", "[B");
+ java_nio_ByteBuffer_isReadOnly =
+ CacheField(j_n_bb.Get(), /*is_static=*/ false, "isReadOnly", "Z");
+ java_nio_ByteBuffer_offset = CacheField(j_n_bb.Get(), /*is_static=*/ false, "offset", "I");
+
+ java_util_Collections_EMPTY_LIST =
+ CacheField(j_u_c.Get(), /*is_static=*/ true, "EMPTY_LIST", "Ljava/util/List;");
+
+ jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_buffer =
+ CacheField(j_i_m_fd_btab.Get(), /*is_static=*/ false, "buffer", "[C");
+ jdk_internal_math_FloatingDecimal_ExceptionalBinaryToASCIIBuffer_image = CacheField(
+ j_i_m_fd_ebtab.Get(), /*is_static=*/ false, "image", "Ljava/lang/String;");
+
+ libcore_util_EmptyArray_STACK_TRACE_ELEMENT = CacheField(
+ l_u_ea.Get(), /*is_static=*/ true, "STACK_TRACE_ELEMENT", "[Ljava/lang/StackTraceElement;");
+
+ org_apache_harmony_dalvik_ddmc_Chunk_data =
+ CacheField(o_a_h_d_c.Get(), /*is_static=*/ false, "data", "[B");
+ org_apache_harmony_dalvik_ddmc_Chunk_length =
+ CacheField(o_a_h_d_c.Get(), /*is_static=*/ false, "length", "I");
+ org_apache_harmony_dalvik_ddmc_Chunk_offset =
+ CacheField(o_a_h_d_c.Get(), /*is_static=*/ false, "offset", "I");
+ org_apache_harmony_dalvik_ddmc_Chunk_type =
+ CacheField(o_a_h_d_c.Get(), /*is_static=*/ false, "type", "I");
}
void WellKnownClasses::LateInit(JNIEnv* env) {
- // CacheField and CacheMethod will initialize their classes. Classes below
- // have clinit sections that call JNI methods. Late init is required
- // to make sure these JNI methods are available.
- ScopedLocalRef<jclass> java_lang_Runtime(env, env->FindClass("java/lang/Runtime"));
- java_lang_Runtime_nativeLoad =
- CacheMethod(env, java_lang_Runtime.get(), true, "nativeLoad",
- "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/Class;)"
- "Ljava/lang/String;");
- java_lang_reflect_Proxy_init =
- CacheMethod(env, java_lang_reflect_Proxy, false, "<init>",
- "(Ljava/lang/reflect/InvocationHandler;)V");
- // This invariant is important since otherwise we will have the entire proxy invoke system
- // confused.
- DCHECK_NE(
- jni::DecodeArtMethod(java_lang_reflect_Proxy_init)->GetEntryPointFromQuickCompiledCode(),
- GetQuickInstrumentationEntryPoint());
- java_lang_reflect_Proxy_invoke =
- CacheMethod(env, java_lang_reflect_Proxy, true, "invoke",
- "(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;"
- "[Ljava/lang/Object;)Ljava/lang/Object;");
+ // Initialize the `Runtime` class that was previously initialized
+ // by `CacheMethod()` calling `FindMethodJNI()`.
+ // TODO: Move this initialization to `ClassLinker`.
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Thread* self = Thread::ForEnv(env);
+ ScopedObjectAccess soa(self);
+ StackHandleScope<1u> hs(self);
+ Handle<mirror::Class> j_l_Runtime =
+ hs.NewHandle(FindSystemClass(class_linker, self, "Ljava/lang/Runtime;"));
+ bool success = class_linker->EnsureInitialized(
+ self, j_l_Runtime, /*can_init_fields=*/ true, /*can_init_parents=*/ true);
+ CHECK(success) << "Failed to initialize " << j_l_Runtime->PrettyDescriptor();
+
+ // The function `GetClassLoader()` in `jni_internal.cc` is checking if the caller
+ // is `java_lang_Runtime_nativeLoad` and, if so, returns the class loader override.
+ // However, this function is used several times between `WellKnownClasses::Init()`
+ // and setting up the override by the `Runtime` and requires that we take the other
+ // path, rather than returning the uninitialized override. Therefore we cannot
+ // initialize this well-known method early and require the `LateInit()`.
+ // TODO: Clean up the initialization steps.
+ java_lang_Runtime_nativeLoad = CacheMethod(
+ j_l_Runtime.Get(),
+ /*is_static=*/ true,
+ "nativeLoad",
+ "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/Class;)Ljava/lang/String;",
+ class_linker->GetImagePointerSize());
}
void WellKnownClasses::HandleJniIdTypeChange(JNIEnv* env) {
@@ -551,85 +796,66 @@
dalvik_annotation_optimization_CriticalNative = nullptr;
dalvik_annotation_optimization_FastNative = nullptr;
dalvik_annotation_optimization_NeverCompile = nullptr;
- dalvik_system_BaseDexClassLoader = nullptr;
- dalvik_system_DelegateLastClassLoader = nullptr;
- dalvik_system_DexClassLoader = nullptr;
- dalvik_system_DexFile = nullptr;
- dalvik_system_DexPathList = nullptr;
- dalvik_system_DexPathList__Element = nullptr;
- dalvik_system_EmulatedStackFrame = nullptr;
- dalvik_system_PathClassLoader = nullptr;
- dalvik_system_VMRuntime = nullptr;
+ dalvik_annotation_optimization_NeverInline = nullptr;
java_lang_annotation_Annotation__array = nullptr;
- java_lang_BootClassLoader = nullptr;
- java_lang_ClassLoader = nullptr;
- java_lang_ClassNotFoundException = nullptr;
- java_lang_Daemons = nullptr;
- java_lang_Error = nullptr;
- java_lang_IllegalAccessError = nullptr;
- java_lang_NoClassDefFoundError = nullptr;
- java_lang_Object = nullptr;
- java_lang_OutOfMemoryError = nullptr;
- java_lang_reflect_InvocationTargetException = nullptr;
- java_lang_reflect_Parameter = nullptr;
+ java_lang_ClassValue = nullptr;
java_lang_reflect_Parameter__array = nullptr;
- java_lang_reflect_Proxy = nullptr;
- java_lang_RuntimeException = nullptr;
- java_lang_StackOverflowError = nullptr;
- java_lang_String = nullptr;
java_lang_StringFactory = nullptr;
java_lang_System = nullptr;
- java_lang_Thread = nullptr;
- java_lang_ThreadGroup = nullptr;
- java_lang_Throwable = nullptr;
java_lang_Void = nullptr;
- java_util_Collections = nullptr;
- java_nio_Buffer = nullptr;
- java_nio_ByteBuffer = nullptr;
- java_nio_DirectByteBuffer = nullptr;
- libcore_reflect_AnnotationFactory = nullptr;
- libcore_reflect_AnnotationMember = nullptr;
- libcore_util_EmptyArray = nullptr;
- org_apache_harmony_dalvik_ddmc_Chunk = nullptr;
- org_apache_harmony_dalvik_ddmc_DdmServer = nullptr;
+ libcore_reflect_AnnotationMember__array = nullptr;
dalvik_system_BaseDexClassLoader_getLdLibraryPath = nullptr;
- dalvik_system_VMRuntime_runFinalization = nullptr;
+ WellKnownClasses::dalvik_system_DelegateLastClassLoader_init = nullptr;
+ WellKnownClasses::dalvik_system_DexClassLoader_init = nullptr;
+ WellKnownClasses::dalvik_system_InMemoryDexClassLoader_init = nullptr;
+ WellKnownClasses::dalvik_system_PathClassLoader_init = nullptr;
dalvik_system_VMRuntime_hiddenApiUsed = nullptr;
java_io_FileDescriptor_descriptor = nullptr;
java_lang_Boolean_valueOf = nullptr;
java_lang_Byte_valueOf = nullptr;
java_lang_Character_valueOf = nullptr;
+ java_lang_BootClassLoader_init = nullptr;
java_lang_ClassLoader_loadClass = nullptr;
java_lang_ClassNotFoundException_init = nullptr;
java_lang_Daemons_start = nullptr;
java_lang_Daemons_stop = nullptr;
+ java_lang_Daemons_waitForDaemonStart = nullptr;
java_lang_Double_doubleToRawLongBits = nullptr;
java_lang_Double_valueOf = nullptr;
+ java_lang_Error_init = nullptr;
java_lang_Float_floatToRawIntBits = nullptr;
java_lang_Float_valueOf = nullptr;
+ java_lang_IllegalAccessError_init = nullptr;
java_lang_Integer_valueOf = nullptr;
+ java_lang_Long_valueOf = nullptr;
+ java_lang_NoClassDefFoundError_init = nullptr;
+ java_lang_OutOfMemoryError_init = nullptr;
+ java_lang_Runtime_nativeLoad = nullptr;
+ java_lang_RuntimeException_init = nullptr;
+ java_lang_Short_valueOf = nullptr;
+ java_lang_StackOverflowError_init = nullptr;
+ java_lang_String_charAt = nullptr;
+ java_lang_Thread_dispatchUncaughtException = nullptr;
+ java_lang_Thread_init = nullptr;
+ java_lang_Thread_run = nullptr;
+ java_lang_ThreadGroup_add = nullptr;
+ java_lang_ThreadGroup_threadTerminated = nullptr;
java_lang_invoke_MethodHandle_asType = nullptr;
java_lang_invoke_MethodHandle_invokeExact = nullptr;
java_lang_invoke_MethodHandles_lookup = nullptr;
java_lang_invoke_MethodHandles_Lookup_findConstructor = nullptr;
- java_lang_Long_valueOf = nullptr;
java_lang_ref_FinalizerReference_add = nullptr;
java_lang_ref_ReferenceQueue_add = nullptr;
java_lang_reflect_InvocationTargetException_init = nullptr;
java_lang_reflect_Parameter_init = nullptr;
java_lang_reflect_Proxy_init = nullptr;
java_lang_reflect_Proxy_invoke = nullptr;
- java_lang_Runtime_nativeLoad = nullptr;
- java_lang_Short_valueOf = nullptr;
- java_lang_String_charAt = nullptr;
- java_lang_Thread_dispatchUncaughtException = nullptr;
- java_lang_Thread_init = nullptr;
- java_lang_Thread_run = nullptr;
- java_lang_ThreadGroup_add = nullptr;
- java_lang_ThreadGroup_removeThread = nullptr;
java_nio_Buffer_isDirect = nullptr;
java_nio_DirectByteBuffer_init = nullptr;
+ jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_D = nullptr;
+ jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_F = nullptr;
+ jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_getChars = nullptr;
libcore_reflect_AnnotationFactory_createAnnotation = nullptr;
libcore_reflect_AnnotationMember_init = nullptr;
org_apache_harmony_dalvik_ddmc_DdmServer_broadcast = nullptr;
@@ -664,12 +890,12 @@
java_nio_Buffer_elementSizeShift = nullptr;
java_nio_Buffer_limit = nullptr;
java_nio_Buffer_position = nullptr;
- java_nio_ByteBuffer_address = nullptr;
java_nio_ByteBuffer_hb = nullptr;
java_nio_ByteBuffer_isReadOnly = nullptr;
- java_nio_ByteBuffer_limit = nullptr;
java_nio_ByteBuffer_offset = nullptr;
java_util_Collections_EMPTY_LIST = nullptr;
+ jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_buffer = nullptr;
+ jdk_internal_math_FloatingDecimal_ExceptionalBinaryToASCIIBuffer_image = nullptr;
libcore_util_EmptyArray_STACK_TRACE_ELEMENT = nullptr;
org_apache_harmony_dalvik_ddmc_Chunk_data = nullptr;
org_apache_harmony_dalvik_ddmc_Chunk_length = nullptr;
@@ -678,7 +904,8 @@
}
ObjPtr<mirror::Class> WellKnownClasses::ToClass(jclass global_jclass) {
- auto ret = ObjPtr<mirror::Class>::DownCast(Thread::Current()->DecodeJObject(global_jclass));
+ JavaVMExt* vm = Runtime::Current()->GetJavaVM();
+ auto ret = ObjPtr<mirror::Class>::DownCast(vm->DecodeGlobal(global_jclass));
DCHECK(!ret.IsNull());
return ret;
}
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index c334f5d..753d386 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -20,15 +20,45 @@
#include "base/locks.h"
#include "jni.h"
#include "obj_ptr.h"
+#include "read_barrier_option.h"
namespace art {
+class ArtField;
class ArtMethod;
namespace mirror {
class Class;
} // namespace mirror
+namespace detail {
+
+template <typename MemberType, MemberType** kMember>
+struct ClassFromMember {
+ template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
+ static ObjPtr<mirror::Class> Get() REQUIRES_SHARED(Locks::mutator_lock_);
+
+ mirror::Class* operator->() const REQUIRES_SHARED(Locks::mutator_lock_);
+};
+
+template <typename MemberType, MemberType** kMember>
+bool operator==(const ClassFromMember<MemberType, kMember> lhs, ObjPtr<mirror::Class> rhs)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+template <typename MemberType, MemberType** kMember>
+bool operator==(ObjPtr<mirror::Class> lhs, const ClassFromMember<MemberType, kMember> rhs)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+template <typename MemberType, MemberType** kMember>
+bool operator!=(const ClassFromMember<MemberType, kMember> lhs, ObjPtr<mirror::Class> rhs)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+template <typename MemberType, MemberType** kMember>
+bool operator!=(ObjPtr<mirror::Class> lhs, const ClassFromMember<MemberType, kMember> rhs)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+} // namespace detail
+
// Various classes used in JNI. We cache them so we don't have to keep looking them up.
struct WellKnownClasses {
@@ -53,144 +83,164 @@
private:
static void InitFieldsAndMethodsOnly(JNIEnv* env);
+ template <ArtMethod** kMethod>
+ using ClassFromMethod = detail::ClassFromMember<ArtMethod, kMethod>;
+
+ template <ArtField** kField>
+ using ClassFromField = detail::ClassFromMember<ArtField, kField>;
+
public:
static jclass dalvik_annotation_optimization_CriticalNative;
static jclass dalvik_annotation_optimization_FastNative;
static jclass dalvik_annotation_optimization_NeverCompile;
- static jclass dalvik_system_BaseDexClassLoader;
- static jclass dalvik_system_DelegateLastClassLoader;
- static jclass dalvik_system_DexClassLoader;
- static jclass dalvik_system_DexFile;
- static jclass dalvik_system_DexPathList;
- static jclass dalvik_system_DexPathList__Element;
- static jclass dalvik_system_EmulatedStackFrame;
- static jclass dalvik_system_InMemoryDexClassLoader;
- static jclass dalvik_system_PathClassLoader;
- static jclass dalvik_system_VMRuntime;
+ static jclass dalvik_annotation_optimization_NeverInline;
static jclass java_lang_annotation_Annotation__array;
- static jclass java_lang_BootClassLoader;
- static jclass java_lang_ClassLoader;
- static jclass java_lang_ClassNotFoundException;
- static jclass java_lang_Daemons;
- static jclass java_lang_Error;
- static jclass java_lang_IllegalAccessError;
- static jclass java_lang_NoClassDefFoundError;
- static jclass java_lang_Object;
- static jclass java_lang_OutOfMemoryError;
- static jclass java_lang_reflect_InvocationTargetException;
- static jclass java_lang_reflect_Parameter;
+ static jclass java_lang_ClassValue;
static jclass java_lang_reflect_Parameter__array;
- static jclass java_lang_reflect_Proxy;
- static jclass java_lang_RuntimeException;
- static jclass java_lang_StackOverflowError;
- static jclass java_lang_String;
static jclass java_lang_StringFactory;
static jclass java_lang_System;
- static jclass java_lang_Thread;
- static jclass java_lang_ThreadGroup;
- static jclass java_lang_Throwable;
static jclass java_lang_Void;
- static jclass java_nio_Buffer;
- static jclass java_nio_ByteBuffer;
- static jclass java_nio_DirectByteBuffer;
- static jclass java_util_Collections;
- static jclass java_util_function_Consumer;
- static jclass libcore_reflect_AnnotationFactory;
- static jclass libcore_reflect_AnnotationMember;
- static jclass libcore_util_EmptyArray;
- static jclass org_apache_harmony_dalvik_ddmc_Chunk;
- static jclass org_apache_harmony_dalvik_ddmc_DdmServer;
+ static jclass libcore_reflect_AnnotationMember__array;
- static jmethodID dalvik_system_BaseDexClassLoader_getLdLibraryPath;
- static jmethodID dalvik_system_VMRuntime_runFinalization;
- static jmethodID dalvik_system_VMRuntime_hiddenApiUsed;
- static jmethodID java_lang_Boolean_valueOf;
- static jmethodID java_lang_Byte_valueOf;
- static jmethodID java_lang_Character_valueOf;
- static jmethodID java_lang_ClassLoader_loadClass;
- static jmethodID java_lang_ClassNotFoundException_init;
- static jmethodID java_lang_Daemons_start;
- static jmethodID java_lang_Daemons_stop;
- static jmethodID java_lang_Daemons_waitForDaemonStart;
- static jmethodID java_lang_Double_doubleToRawLongBits;
- static jmethodID java_lang_Double_valueOf;
- static jmethodID java_lang_Float_floatToRawIntBits;
- static jmethodID java_lang_Float_valueOf;
- static jmethodID java_lang_Integer_valueOf;
- static jmethodID java_lang_invoke_MethodHandle_asType;
- static jmethodID java_lang_invoke_MethodHandle_invokeExact;
- static jmethodID java_lang_invoke_MethodHandles_lookup;
- static jmethodID java_lang_invoke_MethodHandles_Lookup_findConstructor;
- static jmethodID java_lang_Long_valueOf;
- static jmethodID java_lang_ref_FinalizerReference_add;
- static jmethodID java_lang_ref_ReferenceQueue_add;
- static jmethodID java_lang_reflect_InvocationTargetException_init;
- static jmethodID java_lang_reflect_Parameter_init;
- static jmethodID java_lang_reflect_Proxy_init;
- static jmethodID java_lang_reflect_Proxy_invoke;
- static jmethodID java_lang_Runtime_nativeLoad;
- static jmethodID java_lang_Short_valueOf;
- static jmethodID java_lang_String_charAt;
- static jmethodID java_lang_Thread_dispatchUncaughtException;
- static jmethodID java_lang_Thread_init;
- static jmethodID java_lang_Thread_run;
- static jmethodID java_lang_ThreadGroup_add;
- static jmethodID java_lang_ThreadGroup_removeThread;
- static jmethodID java_nio_Buffer_isDirect;
- static jmethodID java_nio_DirectByteBuffer_init;
- static jmethodID java_util_function_Consumer_accept;
- static jmethodID libcore_reflect_AnnotationFactory_createAnnotation;
- static jmethodID libcore_reflect_AnnotationMember_init;
- static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
- static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
+ static ArtMethod* dalvik_system_BaseDexClassLoader_getLdLibraryPath;
+ static ArtMethod* dalvik_system_DelegateLastClassLoader_init; // Only for the declaring class.
+ static ArtMethod* dalvik_system_DexClassLoader_init; // Only for the declaring class.
+ static ArtMethod* dalvik_system_InMemoryDexClassLoader_init; // Only for the declaring class.
+ static ArtMethod* dalvik_system_PathClassLoader_init; // Only for the declaring class.
+ static ArtMethod* dalvik_system_VMRuntime_hiddenApiUsed;
+ static ArtMethod* java_lang_Boolean_valueOf;
+ static ArtMethod* java_lang_BootClassLoader_init; // Only for the declaring class.
+ static ArtMethod* java_lang_Byte_valueOf;
+ static ArtMethod* java_lang_Character_valueOf;
+ static ArtMethod* java_lang_ClassLoader_loadClass;
+ static ArtMethod* java_lang_ClassNotFoundException_init;
+ static ArtMethod* java_lang_Daemons_start;
+ static ArtMethod* java_lang_Daemons_stop;
+ static ArtMethod* java_lang_Daemons_waitForDaemonStart;
+ static ArtMethod* java_lang_Double_doubleToRawLongBits;
+ static ArtMethod* java_lang_Double_valueOf;
+ static ArtMethod* java_lang_Error_init; // Only for the declaring class.
+ static ArtMethod* java_lang_Float_floatToRawIntBits;
+ static ArtMethod* java_lang_Float_valueOf;
+ static ArtMethod* java_lang_IllegalAccessError_init; // Only for the declaring class.
+ static ArtMethod* java_lang_Integer_valueOf;
+ static ArtMethod* java_lang_Long_valueOf;
+ static ArtMethod* java_lang_NoClassDefFoundError_init; // Only for the declaring class.
+ static ArtMethod* java_lang_OutOfMemoryError_init; // Only for the declaring class.
+ static ArtMethod* java_lang_Runtime_nativeLoad;
+ static ArtMethod* java_lang_RuntimeException_init; // Only for the declaring class.
+ static ArtMethod* java_lang_Short_valueOf;
+ static ArtMethod* java_lang_StackOverflowError_init; // Only for the declaring class.
+ static ArtMethod* java_lang_String_charAt;
+ static ArtMethod* java_lang_Thread_dispatchUncaughtException;
+ static ArtMethod* java_lang_Thread_init;
+ static ArtMethod* java_lang_Thread_run;
+ static ArtMethod* java_lang_ThreadGroup_add;
+ static ArtMethod* java_lang_ThreadGroup_threadTerminated;
+ static ArtMethod* java_lang_invoke_MethodHandle_asType;
+ static ArtMethod* java_lang_invoke_MethodHandle_invokeExact;
+ static ArtMethod* java_lang_invoke_MethodHandles_lookup;
+ static ArtMethod* java_lang_invoke_MethodHandles_Lookup_findConstructor;
+ static ArtMethod* java_lang_ref_FinalizerReference_add;
+ static ArtMethod* java_lang_ref_ReferenceQueue_add;
+ static ArtMethod* java_lang_reflect_InvocationTargetException_init;
+ static ArtMethod* java_lang_reflect_Parameter_init;
+ static ArtMethod* java_lang_reflect_Proxy_init;
+ static ArtMethod* java_lang_reflect_Proxy_invoke;
+ static ArtMethod* java_nio_Buffer_isDirect;
+ static ArtMethod* java_nio_DirectByteBuffer_init;
+ static ArtMethod* java_util_function_Consumer_accept;
+ static ArtMethod* jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_D;
+ static ArtMethod* jdk_internal_math_FloatingDecimal_getBinaryToASCIIConverter_F;
+ static ArtMethod* jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_getChars;
+ static ArtMethod* libcore_reflect_AnnotationFactory_createAnnotation;
+ static ArtMethod* libcore_reflect_AnnotationMember_init;
+ static ArtMethod* org_apache_harmony_dalvik_ddmc_DdmServer_broadcast;
+ static ArtMethod* org_apache_harmony_dalvik_ddmc_DdmServer_dispatch;
- static jfieldID dalvik_system_BaseDexClassLoader_pathList;
- static jfieldID dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
- static jfieldID dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter;
- static jfieldID dalvik_system_DexFile_cookie;
- static jfieldID dalvik_system_DexFile_fileName;
- static jfieldID dalvik_system_DexPathList_dexElements;
- static jfieldID dalvik_system_DexPathList__Element_dexFile;
- static jfieldID dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
- static jfieldID java_io_FileDescriptor_descriptor;
- static jfieldID java_lang_ClassLoader_parent;
- static jfieldID java_lang_Thread_parkBlocker;
- static jfieldID java_lang_Thread_daemon;
- static jfieldID java_lang_Thread_group;
- static jfieldID java_lang_Thread_lock;
- static jfieldID java_lang_Thread_name;
- static jfieldID java_lang_Thread_priority;
- static jfieldID java_lang_Thread_nativePeer;
- static jfieldID java_lang_Thread_systemDaemon;
- static jfieldID java_lang_Thread_unparkedBeforeStart;
- static jfieldID java_lang_ThreadGroup_groups;
- static jfieldID java_lang_ThreadGroup_ngroups;
- static jfieldID java_lang_ThreadGroup_mainThreadGroup;
- static jfieldID java_lang_ThreadGroup_name;
- static jfieldID java_lang_ThreadGroup_parent;
- static jfieldID java_lang_ThreadGroup_systemThreadGroup;
- static jfieldID java_lang_Throwable_cause;
- static jfieldID java_lang_Throwable_detailMessage;
- static jfieldID java_lang_Throwable_stackTrace;
- static jfieldID java_lang_Throwable_stackState;
- static jfieldID java_lang_Throwable_suppressedExceptions;
- static jfieldID java_nio_Buffer_address;
- static jfieldID java_nio_Buffer_capacity;
- static jfieldID java_nio_Buffer_elementSizeShift;
- static jfieldID java_nio_Buffer_limit;
- static jfieldID java_nio_Buffer_position;
- static jfieldID java_nio_ByteBuffer_address;
- static jfieldID java_nio_ByteBuffer_hb;
- static jfieldID java_nio_ByteBuffer_isReadOnly;
- static jfieldID java_nio_ByteBuffer_limit;
- static jfieldID java_nio_ByteBuffer_offset;
+ static ArtField* dalvik_system_BaseDexClassLoader_pathList;
+ static ArtField* dalvik_system_BaseDexClassLoader_sharedLibraryLoaders;
+ static ArtField* dalvik_system_BaseDexClassLoader_sharedLibraryLoadersAfter;
+ static ArtField* dalvik_system_DexFile_cookie;
+ static ArtField* dalvik_system_DexFile_fileName;
+ static ArtField* dalvik_system_DexPathList_dexElements;
+ static ArtField* dalvik_system_DexPathList__Element_dexFile;
+ static ArtField* dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
+ static ArtField* java_io_FileDescriptor_descriptor;
+ static ArtField* java_lang_ClassLoader_parent;
+ static ArtField* java_lang_Thread_parkBlocker;
+ static ArtField* java_lang_Thread_daemon;
+ static ArtField* java_lang_Thread_group;
+ static ArtField* java_lang_Thread_lock;
+ static ArtField* java_lang_Thread_name;
+ static ArtField* java_lang_Thread_priority;
+ static ArtField* java_lang_Thread_nativePeer;
+ static ArtField* java_lang_Thread_systemDaemon;
+ static ArtField* java_lang_Thread_unparkedBeforeStart;
+ static ArtField* java_lang_ThreadGroup_groups;
+ static ArtField* java_lang_ThreadGroup_ngroups;
+ static ArtField* java_lang_ThreadGroup_mainThreadGroup;
+ static ArtField* java_lang_ThreadGroup_name;
+ static ArtField* java_lang_ThreadGroup_parent;
+ static ArtField* java_lang_ThreadGroup_systemThreadGroup;
+ static ArtField* java_lang_Throwable_cause;
+ static ArtField* java_lang_Throwable_detailMessage;
+ static ArtField* java_lang_Throwable_stackTrace;
+ static ArtField* java_lang_Throwable_stackState;
+ static ArtField* java_lang_Throwable_suppressedExceptions;
+ static ArtField* java_nio_Buffer_address;
+ static ArtField* java_nio_Buffer_capacity;
+ static ArtField* java_nio_Buffer_elementSizeShift;
+ static ArtField* java_nio_Buffer_limit;
+ static ArtField* java_nio_Buffer_position;
+ static ArtField* java_nio_ByteBuffer_hb;
+ static ArtField* java_nio_ByteBuffer_isReadOnly;
+ static ArtField* java_nio_ByteBuffer_offset;
+ static ArtField* java_util_Collections_EMPTY_LIST;
+ static ArtField* jdk_internal_math_FloatingDecimal_BinaryToASCIIBuffer_buffer;
+ static ArtField* jdk_internal_math_FloatingDecimal_ExceptionalBinaryToASCIIBuffer_image;
+ static ArtField* libcore_util_EmptyArray_STACK_TRACE_ELEMENT;
+ static ArtField* org_apache_harmony_dalvik_ddmc_Chunk_data;
+ static ArtField* org_apache_harmony_dalvik_ddmc_Chunk_length;
+ static ArtField* org_apache_harmony_dalvik_ddmc_Chunk_offset;
+ static ArtField* org_apache_harmony_dalvik_ddmc_Chunk_type;
- static jfieldID java_util_Collections_EMPTY_LIST;
- static jfieldID libcore_util_EmptyArray_STACK_TRACE_ELEMENT;
- static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_data;
- static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_length;
- static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_offset;
- static jfieldID org_apache_harmony_dalvik_ddmc_Chunk_type;
+ static constexpr ClassFromField<&dalvik_system_BaseDexClassLoader_pathList>
+ dalvik_system_BaseDexClassLoader;
+ static constexpr ClassFromMethod<&dalvik_system_DelegateLastClassLoader_init>
+ dalvik_system_DelegateLastClassLoader;
+ static constexpr ClassFromMethod<&dalvik_system_DexClassLoader_init>
+ dalvik_system_DexClassLoader;
+ static constexpr ClassFromField<&dalvik_system_DexFile_cookie> dalvik_system_DexFile;
+ static constexpr ClassFromField<&dalvik_system_DexPathList_dexElements> dalvik_system_DexPathList;
+ static constexpr ClassFromField<&dalvik_system_DexPathList__Element_dexFile>
+ dalvik_system_DexPathList__Element;
+ static constexpr ClassFromMethod<&dalvik_system_InMemoryDexClassLoader_init>
+ dalvik_system_InMemoryDexClassLoader;
+ static constexpr ClassFromMethod<&dalvik_system_PathClassLoader_init>
+ dalvik_system_PathClassLoader;
+ static constexpr ClassFromMethod<&java_lang_BootClassLoader_init> java_lang_BootClassLoader;
+ static constexpr ClassFromField<&java_lang_ClassLoader_parent> java_lang_ClassLoader;
+ static constexpr ClassFromMethod<&java_lang_Daemons_start> java_lang_Daemons;
+ static constexpr ClassFromMethod<&java_lang_Error_init> java_lang_Error;
+ static constexpr ClassFromMethod<&java_lang_IllegalAccessError_init>
+ java_lang_IllegalAccessError;
+ static constexpr ClassFromMethod<&java_lang_NoClassDefFoundError_init>
+ java_lang_NoClassDefFoundError;
+ static constexpr ClassFromMethod<&java_lang_OutOfMemoryError_init> java_lang_OutOfMemoryError;
+ static constexpr ClassFromMethod<&java_lang_RuntimeException_init> java_lang_RuntimeException;
+ static constexpr ClassFromMethod<&java_lang_StackOverflowError_init>
+ java_lang_StackOverflowError;
+ static constexpr ClassFromField<&java_lang_Thread_daemon> java_lang_Thread;
+ static constexpr ClassFromField<&java_lang_ThreadGroup_groups> java_lang_ThreadGroup;
+ static constexpr ClassFromMethod<&java_lang_reflect_InvocationTargetException_init>
+ java_lang_reflect_InvocationTargetException;
+ static constexpr ClassFromMethod<&java_lang_reflect_Parameter_init>
+ java_lang_reflect_Parameter;
+ static constexpr ClassFromField<&java_nio_Buffer_address> java_nio_Buffer;
+ static constexpr ClassFromField<&java_util_Collections_EMPTY_LIST> java_util_Collections;
+ static constexpr ClassFromField<&libcore_util_EmptyArray_STACK_TRACE_ELEMENT>
+ libcore_util_EmptyArray;
};
} // namespace art
diff --git a/sigchainlib/libsigchain.map.txt b/sigchainlib/libsigchain.map.txt
index 02c20c7..377662a 100644
--- a/sigchainlib/libsigchain.map.txt
+++ b/sigchainlib/libsigchain.map.txt
@@ -19,7 +19,7 @@
# Export no symbols - the only external entry points are libc overrides.
# Since this section cannot be empty for APEX stubs generation we provide a
# phony entry.
- LibsigchainNonexistentFunction;
+ LibsigchainNonexistentFunction; # apex
local:
*;
};
diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc
index 5bad856..668d75e 100644
--- a/sigchainlib/sigchain.cc
+++ b/sigchainlib/sigchain.cc
@@ -407,7 +407,15 @@
if (handler == SIG_IGN) {
return;
} else if (handler == SIG_DFL) {
- fatal("exiting due to SIG_DFL handler for signal %d, ucontext %p", signo, ucontext);
+ // We'll only get here if debuggerd is disabled. In that case, whatever next tries to handle
+ // the crash will have no way to know our ucontext, and thus no way to dump the original crash
+ // stack (since we're on an alternate stack.) Let's remove our handler and return. Then the
+ // pre-crash state is restored, the crash happens again, and the next handler gets a chance.
+ log("reverting to SIG_DFL handler for signal %d, ucontext %p", signo, ucontext);
+ struct sigaction dfl = {};
+ dfl.sa_handler = SIG_DFL;
+ linked_sigaction(signo, &dfl, nullptr);
+ return;
} else {
handler(signo);
}
diff --git a/sigchainlib/sigchain_test.cc b/sigchainlib/sigchain_test.cc
index d879f5a..5e9c7fe 100644
--- a/sigchainlib/sigchain_test.cc
+++ b/sigchainlib/sigchain_test.cc
@@ -37,6 +37,12 @@
#include "sigchain.h"
+#if defined(__clang__) && __has_feature(hwaddress_sanitizer)
+#define DISABLE_HWASAN __attribute__((no_sanitize("hwaddress")))
+#else
+#define DISABLE_HWASAN
+#endif
+
#if !defined(__BIONIC__)
using sigset64_t = sigset_t;
@@ -75,13 +81,17 @@
void RaiseHandled() {
sigval value;
value.sival_ptr = &value;
- pthread_sigqueue(pthread_self(), SIGSEGV, value);
+ // pthread_sigqueue would guarantee the signal is delivered to this
+ // thread, but it is a nonstandard extension and does not exist in
+ // musl. Gtest is single threaded, and these tests don't create any
+ // threads, so sigqueue can be used and will deliver to this thread.
+ sigqueue(getpid(), SIGSEGV, value);
}
void RaiseUnhandled() {
sigval value;
value.sival_ptr = nullptr;
- pthread_sigqueue(pthread_self(), SIGSEGV, value);
+ sigqueue(getpid(), SIGSEGV, value);
}
};
@@ -245,9 +255,10 @@
called = 0;
}
-TEST_F(SigchainTest, fault_address_tag) {
-#define SA_EXPOSE_TAGBITS 0x00000800
#if defined(__aarch64__)
+// The test intentionally dereferences (tagged) null to trigger SIGSEGV.
+// We need to disable HWASAN since it would catch the dereference first.
+DISABLE_HWASAN void fault_address_tag_impl() {
struct sigaction action = {};
action.sa_flags = SA_SIGINFO;
action.sa_sigaction = [](int, siginfo_t* siginfo, void*) {
@@ -269,6 +280,13 @@
EXPECT_EXIT({ volatile int load __attribute__((unused)) = *tagged_null; },
testing::ExitedWithCode(0x2b), "");
}
+}
+#endif
+
+TEST_F(SigchainTest, fault_address_tag) {
+#define SA_EXPOSE_TAGBITS 0x00000800
+#if defined(__aarch64__)
+ fault_address_tag_impl();
#else
GTEST_SKIP() << "arm64 only";
#endif
diff --git a/test/000-nop/build b/test/000-nop/build
deleted file mode 100644
index 5233a2d..0000000
--- a/test/000-nop/build
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-# Nothing to do here.
diff --git a/test/000-nop/build.py b/test/000-nop/build.py
new file mode 100644
index 0000000..54f21cc
--- /dev/null
+++ b/test/000-nop/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ pass # Nothing to do here.
diff --git a/test/000-nop/run b/test/000-nop/run
deleted file mode 100644
index 210296b..0000000
--- a/test/000-nop/run
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-echo "Blort."
diff --git a/test/000-nop/run.py b/test/000-nop/run.py
new file mode 100644
index 0000000..9239ae8
--- /dev/null
+++ b/test/000-nop/run.py
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+
+def run(ctx, args):
+ ctx.echo("Blort.")
diff --git a/test/003-omnibus-opcodes/build b/test/003-omnibus-opcodes/build
deleted file mode 100644
index a14ddc9..0000000
--- a/test/003-omnibus-opcodes/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2008 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.
-
-# Stop if something fails.
-set -e
-
-./default-build "$@"
diff --git a/test/003-omnibus-opcodes/javac_post.sh b/test/003-omnibus-opcodes/javac_post.sh
new file mode 100755
index 0000000..6065423
--- /dev/null
+++ b/test/003-omnibus-opcodes/javac_post.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+set -e # Stop on error - the caller script may not have this set.
+
+rm -f classes/UnresClass.class
diff --git a/test/003-omnibus-opcodes/javac_wrapper.sh b/test/003-omnibus-opcodes/javac_wrapper.sh
deleted file mode 100755
index 62e94f8..0000000
--- a/test/003-omnibus-opcodes/javac_wrapper.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-set -e # Stop on error - the caller script may not have this set.
-
-$JAVAC "$@"
-rm -f classes/UnresClass.class
diff --git a/test/003-omnibus-opcodes/src/Goto.java b/test/003-omnibus-opcodes/src/Goto.java
index d56ceae..35b4e66 100644
--- a/test/003-omnibus-opcodes/src/Goto.java
+++ b/test/003-omnibus-opcodes/src/Goto.java
@@ -44,6 +44,7 @@
if (which) {
i += filler(i);
} else {
+ // clang-format off
i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
@@ -54,6 +55,7 @@
i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ // clang-format on
}
return i;
@@ -67,6 +69,7 @@
if (which) {
i += filler(i);
} else {
+ // clang-format off
i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
@@ -2392,6 +2395,7 @@
i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
i -= filler(i); i -= filler(i); i -= filler(i); i -= filler(i);
+ // clang-format on
}
return i;
diff --git a/test/004-JniTest/build b/test/004-JniTest/build
deleted file mode 100755
index 460f2db..0000000
--- a/test/004-JniTest/build
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/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.
-
-#
-# Perform a mostly normal build.
-# Since this test imports 'dalvik.annotation.optimization.FastNative' (and CriticalNative),
-# we put them to src-aotex/ to allow the annotations to be used at javac-compile time,
-# but remove compiled *-aotex* artifacts afterwards.
-#
-# This enables the test to compile with vanilla RI javac and work on either ART or RI.
-# TODO: The test is currently disabled for RI anyway, presumably because @CriticalNative
-# has a different ABI and cannot be tested on RI.
-#
-
-# Stop on failure.
-set -e
-
-# Use release mode to check optimizations do not break JNI.
-export D8_FLAGS=--release
-./default-build "$@"
-
-# Remove the *-aotex build artifacts (but keep src-aotex) with dalvik.* annotations.
-rm -rf classes-aotex classes-aotex.jar $TEST_NAME-aotex.jar
diff --git a/test/004-JniTest/build.py b/test/004-JniTest/build.py
new file mode 100644
index 0000000..b701d5a
--- /dev/null
+++ b/test/004-JniTest/build.py
@@ -0,0 +1,38 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import shutil, os
+
+#
+# Perform a mostly normal build.
+# Since this test imports 'dalvik.annotation.optimization.FastNative' (and CriticalNative),
+# we put them to src-aotex/ to allow the annotations to be used at javac-compile time,
+# but remove compiled *-aotex* artifacts afterwards.
+#
+# This enables the test to compile with vanilla RI javac and work on either ART or RI.
+# TODO: The test is currently disabled for RI anyway, presumably because @CriticalNative
+# has a different ABI and cannot be tested on RI.
+#
+
+
+# Use release mode to check optimizations do not break JNI.
+def build(ctx):
+ ctx.default_build(d8_flags=["--release"])
+
+ # Remove the *-aotex build artifacts (but keep src-aotex) with dalvik.* annotations.
+ shutil.rmtree(ctx.test_dir / "classes-aotex")
+ if not ctx.jvm:
+ os.remove(ctx.test_dir / "classes-aotex.jar")
+ os.remove(ctx.test_dir / "004-JniTest-aotex.jar")
diff --git a/test/004-ReferenceMap/build b/test/004-ReferenceMap/build
deleted file mode 100644
index 6203c97..0000000
--- a/test/004-ReferenceMap/build
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 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.
-
-# Stop if something fails.
-set -e
-
-# Do not invoke D8 for this test.
-export D8=':'
-
-######################################################################
-
-${SOONG_ZIP} --jar -o classes.jar -f classes.dex
-./default-build "$@"
diff --git a/test/004-ReferenceMap/build.py b/test/004-ReferenceMap/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/004-ReferenceMap/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/004-ReferenceMap/generate-sources b/test/004-ReferenceMap/generate-sources
new file mode 100755
index 0000000..9edc200
--- /dev/null
+++ b/test/004-ReferenceMap/generate-sources
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright (C) 2015 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.
+
+# Stop if something fails.
+set -e
+
+# Do not invoke D8 for this test.
+export D8=':'
+
+######################################################################
+
+${SOONG_ZIP} --jar -o classes.jar -f classes.dex
diff --git a/test/004-ReferenceMap/javac_wrapper.sh b/test/004-ReferenceMap/javac_post.sh
similarity index 100%
rename from test/004-ReferenceMap/javac_wrapper.sh
rename to test/004-ReferenceMap/javac_post.sh
diff --git a/test/004-ReferenceMap/stack_walk_refmap_jni.cc b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
index 0659c0b..c84bd88 100644
--- a/test/004-ReferenceMap/stack_walk_refmap_jni.cc
+++ b/test/004-ReferenceMap/stack_walk_refmap_jni.cc
@@ -25,9 +25,8 @@
int t_size = sizeof(t) / sizeof(*t); \
const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader(); \
uintptr_t native_quick_pc = method_header->ToNativeQuickPc(GetMethod(), \
- dex_pc, \
- /* is_catch_handler */ false, \
- abort_if_not_found); \
+ dex_pc, \
+ abort_if_not_found); \
if (native_quick_pc != UINTPTR_MAX) { \
CheckReferences(t, \
t_size, \
diff --git a/test/004-StackWalk/build b/test/004-StackWalk/build
deleted file mode 100644
index 6203c97..0000000
--- a/test/004-StackWalk/build
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 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.
-
-# Stop if something fails.
-set -e
-
-# Do not invoke D8 for this test.
-export D8=':'
-
-######################################################################
-
-${SOONG_ZIP} --jar -o classes.jar -f classes.dex
-./default-build "$@"
diff --git a/test/004-StackWalk/build.py b/test/004-StackWalk/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/004-StackWalk/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/004-StackWalk/generate-sources b/test/004-StackWalk/generate-sources
new file mode 100755
index 0000000..9edc200
--- /dev/null
+++ b/test/004-StackWalk/generate-sources
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright (C) 2015 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.
+
+# Stop if something fails.
+set -e
+
+# Do not invoke D8 for this test.
+export D8=':'
+
+######################################################################
+
+${SOONG_ZIP} --jar -o classes.jar -f classes.dex
diff --git a/test/004-StackWalk/javac_wrapper.sh b/test/004-StackWalk/javac_post.sh
similarity index 100%
rename from test/004-StackWalk/javac_wrapper.sh
rename to test/004-StackWalk/javac_post.sh
diff --git a/test/004-ThreadStress/check b/test/004-ThreadStress/check
deleted file mode 100755
index f389e6b..0000000
--- a/test/004-ThreadStress/check
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Do not compare numbers, so replace numbers with 'N'.
-# Remove all messages relating to failing to allocate a java-peer for the
-# shutdown thread. This can occasionally happen with this test but it is not
-# something we really need to worry about here.
-sed '-es/[0-9][0-9]*/N/g' "$2" \
- | diff --strip-trailing-cr -q "$1" - >/dev/null \
- && grep -v "Exception creating thread peer:" "$4" \
- | diff --strip-trailing-cr -q "$3" - >/dev/null
diff --git a/test/004-ThreadStress/run b/test/004-ThreadStress/run
deleted file mode 100755
index 8004036..0000000
--- a/test/004-ThreadStress/run
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Enable lock contention logging.
-${RUN} --runtime-option -Xlockprofthreshold:10 "${@}"
-return_status1=$?
-
-# Run locks-only mode with stack-dump lock profiling. Reduce the number of total operations from
-# the default 1000 to 100.
-${RUN} --runtime-option -Xlockprofthreshold:10 --runtime-option -Xstackdumplockprofthreshold:20 \
- "${@}" Main --locks-only -o 100
-return_status2=$?
-
-# Make sure we don't silently ignore an early failure.
-(exit $return_status1) && (exit $return_status2)
diff --git a/test/004-ThreadStress/run.py b/test/004-ThreadStress/run.py
new file mode 100644
index 0000000..7cc4d79
--- /dev/null
+++ b/test/004-ThreadStress/run.py
@@ -0,0 +1,39 @@
+#!/bin/bash
+#
+# 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.
+
+import sys
+
+
+def run(ctx, args):
+ # Enable lock contention logging.
+ ctx.default_run(args, runtime_option=["-Xlockprofthreshold:10"])
+
+ # Run locks-only mode with stack-dump lock profiling. Reduce the number of total operations from
+ # the default 1000 to 100.
+ ctx.default_run(
+ args,
+ test_args=["--locks-only -o 100"],
+ runtime_option=[
+ "-Xlockprofthreshold:10", "-Xstackdumplockprofthreshold:20"
+ ])
+
+ # Do not compare numbers, so replace numbers with 'N'.
+ ctx.run(fr"sed -i 's/[0-9][0-9]*/N/g' '{args.stdout_file}'")
+
+ # Remove all messages relating to failing to allocate a java-peer for the
+ # shutdown thread. This can occasionally happen with this test but it is not
+ # something we really need to worry about here.
+ ctx.run(fr"sed -i '/Exception creating thread peer:/d' '{args.stderr_file}'")
diff --git a/test/004-UnsafeTest/build.py b/test/004-UnsafeTest/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/004-UnsafeTest/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/004-UnsafeTest/test-metadata.json b/test/004-UnsafeTest/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/004-UnsafeTest/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/005-annotations/build b/test/005-annotations/build
deleted file mode 100644
index bdc1950..0000000
--- a/test/005-annotations/build
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2012 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.
-
-# Stop if something fails.
-set -e
-
-# Build intermediate object to preserve class-retention annotations.
-export D8_FLAGS=--intermediate
-./default-build "$@"
diff --git a/test/005-annotations/build.py b/test/005-annotations/build.py
new file mode 100644
index 0000000..a3aefce
--- /dev/null
+++ b/test/005-annotations/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 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 intermediate object to preserve class-retention annotations.
+def build(ctx):
+ ctx.default_build(d8_flags=['--intermediate'])
diff --git a/test/005-annotations/javac_post.sh b/test/005-annotations/javac_post.sh
new file mode 100755
index 0000000..abec8b0
--- /dev/null
+++ b/test/005-annotations/javac_post.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+set -e # Stop on error - the caller script may not have this set.
+
+# Classes available at compile time, but not at runtime.
+rm -f classes/android/test/anno/MissingAnnotation.class
+rm -f 'classes/android/test/anno/ClassWithInnerAnnotationClass$MissingInnerAnnotationClass.class'
+
+# overwrite RenamedEnum in classes
+if [ -f classes2/android/test/anno/RenamedEnumClass.java ] ; then
+ mv classes2/android/test/anno/RenamedEnumClass.java classes/android/test/anno/RenamedEnumClass.java
+fi
diff --git a/test/005-annotations/javac_wrapper.sh b/test/005-annotations/javac_wrapper.sh
deleted file mode 100755
index 69e6161..0000000
--- a/test/005-annotations/javac_wrapper.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-set -e # Stop on error - the caller script may not have this set.
-
-$JAVAC "$@"
-
-# Classes available at compile time, but not at runtime.
-rm -f classes/android/test/anno/MissingAnnotation.class
-rm -f 'classes/android/test/anno/ClassWithInnerAnnotationClass$MissingInnerAnnotationClass.class'
-
-# overwrite RenamedEnum in classes
-if [ -f classes2/android/test/anno/RenamedEnumClass.java ] ; then
- mv classes2/android/test/anno/RenamedEnumClass.java classes/android/test/anno/RenamedEnumClass.java
-fi
diff --git a/test/018-stack-overflow/Android.bp b/test/018-stack-overflow/Android.bp
index 504bdaa..ef817a8 100644
--- a/test/018-stack-overflow/Android.bp
+++ b/test/018-stack-overflow/Android.bp
@@ -15,12 +15,13 @@
java_test {
name: "art-run-test-018-stack-overflow",
defaults: ["art-run-test-defaults"],
- test_config_template: ":art-run-test-target-template",
+ test_config_template: ":art-run-test-target-cts-template",
srcs: ["src/**/*.java"],
data: [
":art-run-test-018-stack-overflow-expected-stdout",
":art-run-test-018-stack-overflow-expected-stderr",
],
+ test_suites: ["cts"],
}
// Test's expected standard output.
diff --git a/test/018-stack-overflow/test-metadata.json b/test/018-stack-overflow/test-metadata.json
new file mode 100644
index 0000000..975ada7
--- /dev/null
+++ b/test/018-stack-overflow/test-metadata.json
@@ -0,0 +1,3 @@
+{
+ "test_suites": ["cts"]
+}
diff --git a/test/021-string2/src/Main.java b/test/021-string2/src/Main.java
index 39595f3..5da10ca 100644
--- a/test/021-string2/src/Main.java
+++ b/test/021-string2/src/Main.java
@@ -119,6 +119,7 @@
testEqualsConstString();
testConstStringEquals();
testStringConcat();
+ testEmptyWithHighByte();
// Regression tests for String.setCharAt() breaking string compression invariants.
Locale en_US = new Locale("en", "US");
@@ -760,6 +761,11 @@
Assert.assertEquals("abc\u0440xyzw\u0440", "abc\u0440".concat("xyzw\u0440"));
}
+ public static void testEmptyWithHighByte() {
+ String empty = new String(new byte[0], 1);
+ Assert.assertEquals("", empty);
+ }
+
public static boolean $noinline$equalsConstString0(String s) {
return s.equals("");
}
diff --git a/test/023-many-interfaces/build b/test/023-many-interfaces/build
deleted file mode 100644
index 6d1c8e3..0000000
--- a/test/023-many-interfaces/build
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2008 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.
-
-# Stop if something fails.
-set -e
-
-# Write out a bunch of interface source files.
-./iface-gen
-
-./default-build "$@"
diff --git a/test/023-many-interfaces/build.py b/test/023-many-interfaces/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/023-many-interfaces/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/023-many-interfaces/generate-sources b/test/023-many-interfaces/generate-sources
new file mode 100755
index 0000000..f9ba1b8
--- /dev/null
+++ b/test/023-many-interfaces/generate-sources
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 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.
+
+# Stop if something fails.
+set -e
+
+# Write out a bunch of interface source files.
+./iface-gen
diff --git a/test/024-illegal-access/Android.bp b/test/024-illegal-access/Android.bp
new file mode 100644
index 0000000..7ed1a67
--- /dev/null
+++ b/test/024-illegal-access/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `024-illegal-access`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-024-illegal-access-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-024-illegal-access",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-024-illegal-access-src"
+ ],
+ data: [
+ ":art-run-test-024-illegal-access-expected-stdout",
+ ":art-run-test-024-illegal-access-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-024-illegal-access-expected-stdout",
+ out: ["art-run-test-024-illegal-access-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-024-illegal-access-expected-stderr",
+ out: ["art-run-test-024-illegal-access-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/030-bad-finalizer/build.py b/test/030-bad-finalizer/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/030-bad-finalizer/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/030-bad-finalizer/expected-stdout.txt b/test/030-bad-finalizer/expected-stdout.txt
index a0ae9ab..63e2895 100644
--- a/test/030-bad-finalizer/expected-stdout.txt
+++ b/test/030-bad-finalizer/expected-stdout.txt
@@ -2,4 +2,3 @@
Finalizer started and sleeping briefly...
Finalizer done snoozing.
Finalizer sleeping forever now.
-exit status: 2
diff --git a/test/030-bad-finalizer/run b/test/030-bad-finalizer/run
deleted file mode 100755
index 54747ee..0000000
--- a/test/030-bad-finalizer/run
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/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.
-
-# The test logs error messages which is expected, discard them.
-export ANDROID_LOG_TAGS='*:f'
-
-# Squash the exit status and put it in expected
-./default-run --external-log-tags "${@}"
-echo "exit status:" $?
diff --git a/test/030-bad-finalizer/run.py b/test/030-bad-finalizer/run.py
new file mode 100644
index 0000000..74fcbb9
--- /dev/null
+++ b/test/030-bad-finalizer/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, android_log_tags="*:f", expected_exit_code=2)
diff --git a/test/030-bad-finalizer/src/Main.java b/test/030-bad-finalizer/src/Main.java
index f33b3aa..bdc2b41 100644
--- a/test/030-bad-finalizer/src/Main.java
+++ b/test/030-bad-finalizer/src/Main.java
@@ -21,8 +21,10 @@
/**
* Test a class with a bad finalizer.
*
- * This test is inherently flaky. It assumes that the system will schedule the finalizer daemon
- * and finalizer watchdog daemon enough to reach the timeout and throwing the fatal exception.
+ * This test is inherently very slightly flaky. It assumes that the system will schedule the
+ * finalizer daemon and finalizer watchdog daemon soon and often enough to reach the timeout and
+ * throw the fatal exception before we time out. Since we build in a 100 second buffer, failures
+ * should be very rare.
*/
public class Main {
public static void main(String[] args) throws Exception {
@@ -76,6 +78,7 @@
try {
Thread.sleep(ms);
} catch (InterruptedException ie) {
+ System.out.println("Unexpected interrupt");
}
}
diff --git a/test/030-bad-finalizer/test-metadata.json b/test/030-bad-finalizer/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/030-bad-finalizer/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/032-concrete-sub/Android.bp b/test/032-concrete-sub/Android.bp
new file mode 100644
index 0000000..f4c43f8
--- /dev/null
+++ b/test/032-concrete-sub/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `032-concrete-sub`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-032-concrete-sub-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-032-concrete-sub",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-032-concrete-sub-src"
+ ],
+ data: [
+ ":art-run-test-032-concrete-sub-expected-stdout",
+ ":art-run-test-032-concrete-sub-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-032-concrete-sub-expected-stdout",
+ out: ["art-run-test-032-concrete-sub-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-032-concrete-sub-expected-stderr",
+ out: ["art-run-test-032-concrete-sub-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/034-call-null/expected-stdout.txt b/test/034-call-null/expected-stdout.txt
index a9bf5a0..e69de29 100644
--- a/test/034-call-null/expected-stdout.txt
+++ b/test/034-call-null/expected-stdout.txt
@@ -1 +0,0 @@
-exit status: 1
diff --git a/test/034-call-null/run b/test/034-call-null/run
deleted file mode 100755
index 7a0d0d0..0000000
--- a/test/034-call-null/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/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.
-
-# Squash the exit status and put it in expected
-./default-run "$@"
-echo "exit status:" $?
diff --git a/test/034-call-null/run.py b/test/034-call-null/run.py
new file mode 100644
index 0000000..80a8a33
--- /dev/null
+++ b/test/034-call-null/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, expected_exit_code=1)
diff --git a/test/038-inner-null/expected-stdout.txt b/test/038-inner-null/expected-stdout.txt
index 3d87b3f..24836c2 100644
--- a/test/038-inner-null/expected-stdout.txt
+++ b/test/038-inner-null/expected-stdout.txt
@@ -1,2 +1 @@
new Special()
-exit status: 1
diff --git a/test/038-inner-null/run b/test/038-inner-null/run
deleted file mode 100755
index 7a0d0d0..0000000
--- a/test/038-inner-null/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/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.
-
-# Squash the exit status and put it in expected
-./default-run "$@"
-echo "exit status:" $?
diff --git a/test/038-inner-null/run.py b/test/038-inner-null/run.py
new file mode 100644
index 0000000..80a8a33
--- /dev/null
+++ b/test/038-inner-null/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, expected_exit_code=1)
diff --git a/test/042-new-instance/Android.bp b/test/042-new-instance/Android.bp
new file mode 100644
index 0000000..fd777be
--- /dev/null
+++ b/test/042-new-instance/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `042-new-instance`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-042-new-instance-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-042-new-instance",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-042-new-instance-src"
+ ],
+ data: [
+ ":art-run-test-042-new-instance-expected-stdout",
+ ":art-run-test-042-new-instance-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-042-new-instance-expected-stdout",
+ out: ["art-run-test-042-new-instance-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-042-new-instance-expected-stderr",
+ out: ["art-run-test-042-new-instance-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/044-proxy/run b/test/044-proxy/run
deleted file mode 100644
index 4a322f3..0000000
--- a/test/044-proxy/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Use a smaller heap so it's easier to fill up.
-exec ${RUN} $@ --runtime-option -Xmx4m
diff --git a/test/044-proxy/run.py b/test/044-proxy/run.py
new file mode 100644
index 0000000..1784fc5
--- /dev/null
+++ b/test/044-proxy/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Use a smaller heap so it's easier to fill up.
+ ctx.default_run(args, runtime_option=["-Xmx4m"])
diff --git a/test/051-thread/thread_test.cc b/test/051-thread/thread_test.cc
index 079ad40..33841eb 100644
--- a/test/051-thread/thread_test.cc
+++ b/test/051-thread/thread_test.cc
@@ -22,7 +22,7 @@
extern "C" JNIEXPORT jint JNICALL Java_Main_getNativePriority(JNIEnv* env,
jclass clazz ATTRIBUTE_UNUSED) {
- return ThreadForEnv(env)->GetNativePriority();
+ return Thread::ForEnv(env)->GetNativePriority();
}
extern "C" JNIEXPORT jboolean JNICALL Java_Main_supportsThreadPriorities(
diff --git a/test/054-uncaught/expected-stdout.txt b/test/054-uncaught/expected-stdout.txt
index 878260a..7d7f03c 100644
--- a/test/054-uncaught/expected-stdout.txt
+++ b/test/054-uncaught/expected-stdout.txt
@@ -18,4 +18,3 @@
java.lang.NullPointerException: Hi diddly-ho, neighborino.
at Main.catchTheUncaught(Main.java:63)
at Main.main(Main.java:26)
-exit status: 1
diff --git a/test/054-uncaught/run b/test/054-uncaught/run
deleted file mode 100755
index 7a0d0d0..0000000
--- a/test/054-uncaught/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/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.
-
-# Squash the exit status and put it in expected
-./default-run "$@"
-echo "exit status:" $?
diff --git a/test/054-uncaught/run.py b/test/054-uncaught/run.py
new file mode 100644
index 0000000..80a8a33
--- /dev/null
+++ b/test/054-uncaught/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, expected_exit_code=1)
diff --git a/test/055-enum-performance/run b/test/055-enum-performance/run
deleted file mode 100755
index e27a622..0000000
--- a/test/055-enum-performance/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2012 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.
-
-# As this is a performance test we always use the non-debug build.
-exec ${RUN} "${@/#libartd.so/libart.so}"
diff --git a/test/055-enum-performance/run.py b/test/055-enum-performance/run.py
new file mode 100644
index 0000000..dbb09fd
--- /dev/null
+++ b/test/055-enum-performance/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # As this is a performance test we always use the non-debug build.
+ ctx.default_run(args, lib="libart.so")
diff --git a/test/056-const-string-jumbo/build b/test/056-const-string-jumbo/build
deleted file mode 100644
index c1d711b..0000000
--- a/test/056-const-string-jumbo/build
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2008 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.
-
-# Stop if something fails.
-set -e
-
-# Write out files with 32768 total static string declarations, so that
-# the reference to "zorch" in the real test file will be guaranteed to
-# need a jumbo string reference (it sorts last after all the others).
-# Note: Each string reference is stored in a separate static variable,
-# and that variable's name is also represented in the strings, which
-# is why we can just have 32768 and not 65536 declarations.
-
-awk '
-BEGIN {
- writeFile("Zorch1", 0, 16383);
- writeFile("Zorch2", 16384, 32767);
-}
-function writeFile(name, start, end) {
- fileName = "src/" name ".java";
- printf("public class %s {\n", name) > fileName;
- for (i = start; i <= end; i++) {
- printf(" static public final String s%d = \"%d\";\n",
- i, i) > fileName;
- }
- printf("}\n") > fileName;
-}'
-
-./default-build "$@"
diff --git a/test/056-const-string-jumbo/build.py b/test/056-const-string-jumbo/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/056-const-string-jumbo/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/056-const-string-jumbo/generate-sources b/test/056-const-string-jumbo/generate-sources
new file mode 100755
index 0000000..5c7ade8
--- /dev/null
+++ b/test/056-const-string-jumbo/generate-sources
@@ -0,0 +1,40 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 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.
+
+# Stop if something fails.
+set -e
+
+# Write out files with 32768 total static string declarations, so that
+# the reference to "zorch" in the real test file will be guaranteed to
+# need a jumbo string reference (it sorts last after all the others).
+# Note: Each string reference is stored in a separate static variable,
+# and that variable's name is also represented in the strings, which
+# is why we can just have 32768 and not 65536 declarations.
+
+awk '
+BEGIN {
+ writeFile("Zorch1", 0, 16383);
+ writeFile("Zorch2", 16384, 32767);
+}
+function writeFile(name, start, end) {
+ fileName = "src/" name ".java";
+ printf("public class %s {\n", name) > fileName;
+ for (i = start; i <= end; i++) {
+ printf(" static public final String s%d = \"%d\";\n",
+ i, i) > fileName;
+ }
+ printf("}\n") > fileName;
+}'
diff --git a/test/059-finalizer-throw/run b/test/059-finalizer-throw/run
deleted file mode 100644
index dda4159..0000000
--- a/test/059-finalizer-throw/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2020 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.
-
-# The test logs error messages which is expected, discard them.
-export ANDROID_LOG_TAGS='*:f'
-exec ${RUN} --external-log-tags "${@}"
diff --git a/test/059-finalizer-throw/run.py b/test/059-finalizer-throw/run.py
new file mode 100644
index 0000000..930dc04
--- /dev/null
+++ b/test/059-finalizer-throw/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, android_log_tags="*:f")
diff --git a/test/064-field-access/run b/test/064-field-access/run
deleted file mode 100644
index 4a322f3..0000000
--- a/test/064-field-access/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Use a smaller heap so it's easier to fill up.
-exec ${RUN} $@ --runtime-option -Xmx4m
diff --git a/test/064-field-access/run.py b/test/064-field-access/run.py
new file mode 100644
index 0000000..1784fc5
--- /dev/null
+++ b/test/064-field-access/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Use a smaller heap so it's easier to fill up.
+ ctx.default_run(args, runtime_option=["-Xmx4m"])
diff --git a/test/065-mismatched-implements/build b/test/065-mismatched-implements/build
deleted file mode 100755
index 41823b5..0000000
--- a/test/065-mismatched-implements/build
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/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.
-# Make us exit on a failure.
-
-set -e
-
-# Don't use desugar because the build fails when it encounters ICCE.
-#
-# Exception in thread "main" java.lang.IllegalArgumentException
-# at com.google.common.base.Preconditions.checkArgument(Preconditions.java:108)
-# at com.google.devtools.build.android.desugar.DefaultMethodClassFixer$DefaultMethodFinder.visit(DefaultMethodClassFixer.java:295)
-export USE_DESUGAR=false
-
-./default-build "$@"
diff --git a/test/065-mismatched-implements/build.py b/test/065-mismatched-implements/build.py
new file mode 100644
index 0000000..bc346b0
--- /dev/null
+++ b/test/065-mismatched-implements/build.py
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2022 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.
+
+
+# Don't use desugar because the build fails when it encounters ICCE.
+#
+# Exception in thread "main" java.lang.IllegalArgumentException
+# at com.google.common.base.Preconditions.checkArgument(Preconditions.java:108)
+# at com.google.devtools.build.android.desugar.DefaultMethodClassFixer$DefaultMethodFinder.visit(DefaultMethodClassFixer.java:295)
+def build(ctx):
+ ctx.default_build(use_desugar=False)
diff --git a/test/066-mismatched-super/build b/test/066-mismatched-super/build
deleted file mode 100644
index 50aceb4..0000000
--- a/test/066-mismatched-super/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-USE_DESUGAR=false ./default-build "$@"
diff --git a/test/066-mismatched-super/build.py b/test/066-mismatched-super/build.py
new file mode 100644
index 0000000..59b119d
--- /dev/null
+++ b/test/066-mismatched-super/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(use_desugar=False)
diff --git a/test/066-mismatched-super/src/Main.java b/test/066-mismatched-super/src/Main.java
index 6ae1198..bb042b7 100644
--- a/test/066-mismatched-super/src/Main.java
+++ b/test/066-mismatched-super/src/Main.java
@@ -28,7 +28,8 @@
try {
ExtendsFinal ef = new ExtendsFinal();
System.out.println("Succeeded unexpectedly");
- } catch (VerifyError ve) {
+ } catch (VerifyError | IncompatibleClassChangeError ve) {
+ // b/242985231 - from JDK 17 the JVM throws IncompatibleClassChangeError
System.out.println("Got expected VerifyError");
}
}
diff --git a/test/069-field-type/Android.bp b/test/069-field-type/Android.bp
new file mode 100644
index 0000000..c407a92
--- /dev/null
+++ b/test/069-field-type/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `069-field-type`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-069-field-type-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-069-field-type",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-069-field-type-src"
+ ],
+ data: [
+ ":art-run-test-069-field-type-expected-stdout",
+ ":art-run-test-069-field-type-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-069-field-type-expected-stdout",
+ out: ["art-run-test-069-field-type-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-069-field-type-expected-stderr",
+ out: ["art-run-test-069-field-type-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/071-dexfile-get-static-size/build b/test/071-dexfile-get-static-size/build
deleted file mode 100755
index 4ce24cf..0000000
--- a/test/071-dexfile-get-static-size/build
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/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.
-
-./default-build "$@"
-
-# Bundle with the test the following resources:
-# 1. test1.dex
-# 2. test2.dex
-# 3. test-jar.jar, containing test1.dex as classes.dex
-# 4. multi-jar.jar, containing test1.dex as classes.dex and test2.dex as classes2.dex
-mkdir test-jar
-cp res/test1.dex test-jar/classes.dex
-cp res/test2.dex test-jar/classes2.dex
-${SOONG_ZIP} -j -o res/test-jar.jar -f test-jar/classes.dex
-${SOONG_ZIP} -j -o res/multi-jar.jar -f test-jar/classes.dex -f test-jar/classes2.dex
diff --git a/test/071-dexfile-get-static-size/build.py b/test/071-dexfile-get-static-size/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/071-dexfile-get-static-size/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/071-dexfile-get-static-size/generate-sources b/test/071-dexfile-get-static-size/generate-sources
new file mode 100755
index 0000000..55afc03
--- /dev/null
+++ b/test/071-dexfile-get-static-size/generate-sources
@@ -0,0 +1,26 @@
+#!/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.
+
+# Bundle with the test the following resources:
+# 1. test1.dex
+# 2. test2.dex
+# 3. test-jar.jar, containing test1.dex as classes.dex
+# 4. multi-jar.jar, containing test1.dex as classes.dex and test2.dex as classes2.dex
+mkdir test-jar
+cp res/test1.dex test-jar/classes.dex
+cp res/test2.dex test-jar/classes2.dex
+${SOONG_ZIP} -j -o res/test-jar.jar -f test-jar/classes.dex
+${SOONG_ZIP} -j -o res/multi-jar.jar -f test-jar/classes.dex -f test-jar/classes2.dex
diff --git a/test/071-dexfile-map-clean/build b/test/071-dexfile-map-clean/build
deleted file mode 100755
index a171fc3..0000000
--- a/test/071-dexfile-map-clean/build
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/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.
-
-# Any JAR files used by this test shall have their classes.dex be stored, NOT compressed.
-# This is needed for our test correctness which validates classes.dex are mapped file-backed.
-#
-# In addition, align to at least 4 bytes since that's the dex alignment requirement.
-./default-build "$@" --zip-compression-method store --zip-align 4
diff --git a/test/071-dexfile-map-clean/build.py b/test/071-dexfile-map-clean/build.py
new file mode 100644
index 0000000..ab9c4c3
--- /dev/null
+++ b/test/071-dexfile-map-clean/build.py
@@ -0,0 +1,22 @@
+#
+# Copyright (C) 2022 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.
+
+
+# Any JAR files used by this test shall have their classes.dex be stored, NOT compressed.
+# This is needed for our test correctness which validates classes.dex are mapped file-backed.
+#
+# In addition, align to at least 4 bytes since that's the dex alignment requirement.
+def build(ctx):
+ ctx.default_build(zip_compression_method="store", zip_align_bytes="4")
diff --git a/test/071-dexfile-map-clean/run b/test/071-dexfile-map-clean/run
deleted file mode 100755
index afa2ff7..0000000
--- a/test/071-dexfile-map-clean/run
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/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.
-
-# Make sure we call 'sync'
-# before executing dalvikvm because otherwise
-# it's highly likely the pushed JAR files haven't
-# been committed to permanent storage yet,
-# and when we mmap them the kernel will think
-# the memory is dirty (despite being file-backed).
-# (Note: this was reproducible 100% of the time on
-# a target angler device).
-./default-run "$@" --sync
diff --git a/test/071-dexfile-map-clean/run.py b/test/071-dexfile-map-clean/run.py
new file mode 100644
index 0000000..eda60fa
--- /dev/null
+++ b/test/071-dexfile-map-clean/run.py
@@ -0,0 +1,27 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Make sure we call 'sync'
+ # before executing dalvikvm because otherwise
+ # it's highly likely the pushed JAR files haven't
+ # been committed to permanent storage yet,
+ # and when we mmap them the kernel will think
+ # the memory is dirty (despite being file-backed).
+ # (Note: this was reproducible 100% of the time on
+ # a target angler device).
+ ctx.default_run(args, sync=True)
diff --git a/test/073-mismatched-field/Android.bp b/test/073-mismatched-field/Android.bp
new file mode 100644
index 0000000..16ca7da
--- /dev/null
+++ b/test/073-mismatched-field/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `073-mismatched-field`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-073-mismatched-field-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-073-mismatched-field",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-073-mismatched-field-src"
+ ],
+ data: [
+ ":art-run-test-073-mismatched-field-expected-stdout",
+ ":art-run-test-073-mismatched-field-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-073-mismatched-field-expected-stdout",
+ out: ["art-run-test-073-mismatched-field-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-073-mismatched-field-expected-stderr",
+ out: ["art-run-test-073-mismatched-field-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/075-verification-error/Android.bp b/test/075-verification-error/Android.bp
new file mode 100644
index 0000000..ab87fc8
--- /dev/null
+++ b/test/075-verification-error/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `075-verification-error`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-075-verification-error-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-075-verification-error",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-075-verification-error-src"
+ ],
+ data: [
+ ":art-run-test-075-verification-error-expected-stdout",
+ ":art-run-test-075-verification-error-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-075-verification-error-expected-stdout",
+ out: ["art-run-test-075-verification-error-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-075-verification-error-expected-stderr",
+ out: ["art-run-test-075-verification-error-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/077-method-override/Android.bp b/test/077-method-override/Android.bp
new file mode 100644
index 0000000..f33a9ac
--- /dev/null
+++ b/test/077-method-override/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `077-method-override`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-077-method-override-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-077-method-override",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-077-method-override-src"
+ ],
+ data: [
+ ":art-run-test-077-method-override-expected-stdout",
+ ":art-run-test-077-method-override-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-077-method-override-expected-stdout",
+ out: ["art-run-test-077-method-override-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-077-method-override-expected-stderr",
+ out: ["art-run-test-077-method-override-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/080-oom-throw/run b/test/080-oom-throw/run
deleted file mode 100644
index 08db73b..0000000
--- a/test/080-oom-throw/run
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Ensure the minimum log severity is at least 'WARNING' to display the
-# stack trace shown before exception
-#
-# "java.lang.OutOfMemoryError: OutOfMemoryError thrown while trying
-# to throw OutOfMemoryError; no stack trace available"
-#
-# is set, to try to understand a recurring crash in this test (b/77567088).
-case "$ANDROID_LOG_TAGS" in
- # Lower the minimum log severity to WARNING if it was initialy set
- # to a higher level ('ERROR', 'FATAL' or 'SILENT' -- see
- # https://developer.android.com/studio/command-line/logcat#filteringOutput).
- (\*:[efs]) export ANDROID_LOG_TAGS='*:w';;
-esac
-
-exec ${RUN} $@ --runtime-option -Xmx16m
diff --git a/test/080-oom-throw/run.py b/test/080-oom-throw/run.py
new file mode 100644
index 0000000..fb10464
--- /dev/null
+++ b/test/080-oom-throw/run.py
@@ -0,0 +1,32 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Ensure the minimum log severity is at least 'WARNING' to display the
+ # stack trace shown before exception
+ #
+ # "java.lang.OutOfMemoryError: OutOfMemoryError thrown while trying
+ # to throw OutOfMemoryError; no stack trace available"
+ #
+ # is set, to try to understand a recurring crash in this test (b/77567088).
+ if ctx.env.ANDROID_LOG_TAGS in ["*:e", "*:f", "*:s"]:
+ # Lower the minimum log severity to WARNING if it was initialy set
+ # to a higher level ('ERROR', 'FATAL' or 'SILENT' -- see
+ # https://developer.android.com/studio/command-line/logcat#filteringOutput).
+ ctx.env.ANDROID_LOG_TAGS = "*:w"
+
+ ctx.default_run(args, runtime_option=["-Xmx16m"])
diff --git a/test/080-oom-throw/src/Main.java b/test/080-oom-throw/src/Main.java
index cd36eff..c9fd361 100644
--- a/test/080-oom-throw/src/Main.java
+++ b/test/080-oom-throw/src/Main.java
@@ -55,12 +55,20 @@
private static int exhaustJavaHeap(Object[] data, int index, int size) {
Runtime.getRuntime().gc();
- while (index != data.length && size != 0) {
+ while (index != data.length) {
try {
data[index] = new byte[size];
++index;
} catch (OutOfMemoryError oome) {
- size /= 2;
+ // Rapidly shrink the object size to fill any remaining space.
+ // Use few different sizes, since detecting out-of-memory is slow.
+ if (size >= 32) {
+ size /= 32;
+ } else if (size > 1) {
+ size = 1;
+ } else {
+ break;
+ }
}
}
return index;
diff --git a/test/083-compiler-regressions/src/Main.java b/test/083-compiler-regressions/src/Main.java
index 9de3f5a..53641b4 100644
--- a/test/083-compiler-regressions/src/Main.java
+++ b/test/083-compiler-regressions/src/Main.java
@@ -9756,6 +9756,7 @@
private static int ifLez(int src, int thn, int els) { return (src <= 0) ? thn : els; }
public static void testIfCcz() {
+ // clang-format off
int[] results = new int[] {
ifEqzThen0Else1(-1), 1,
ifEqzThen0Else1(0), 0,
@@ -9826,6 +9827,7 @@
ifGtzThen8Else9(0), 9,
ifGtzThen8Else9(1), 8
};
+ // clang-format on
boolean success = true;
StringBuilder fails = new StringBuilder();
diff --git a/test/088-monitor-verification/src/Main.java b/test/088-monitor-verification/src/Main.java
index 3924fa6..3ed1939 100644
--- a/test/088-monitor-verification/src/Main.java
+++ b/test/088-monitor-verification/src/Main.java
@@ -119,6 +119,7 @@
* Confirms that we can have 32 nested monitors on one method.
*/
void notExcessiveNesting() {
+ // clang-format off
assertIsManaged();
synchronized (this) { // 1
synchronized (this) { // 2
@@ -153,6 +154,7 @@
synchronized (this) { // 31
synchronized (this) { // 32
}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
+ // clang-format on
}
/**
@@ -160,6 +162,7 @@
* method.
*/
void notNested() {
+ // clang-format off
assertIsManaged();
synchronized (this) {} // 1
synchronized (this) {} // 2
@@ -195,6 +198,7 @@
synchronized (this) {} // 32
synchronized (this) {} // 33
synchronized (this) {} // 34
+ // clang-format on
}
/* does nothing but ensure that the compiler doesn't discard an object */
diff --git a/test/089-many-methods/build b/test/089-many-methods/build
deleted file mode 100644
index 5225e3f..0000000
--- a/test/089-many-methods/build
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2008 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.
-
-# Stop if something fails.
-set -e
-
-# Write out files with 65500 total static fields, instance fields, and methods
-# to exceed the dex format's limits.
-mkdir src
-awk '
-BEGIN {
- writeFileField("FillerStatic", "static public int staticInt");
- writeFileField("FillerField", "public int fieldInt");
- writeFileMethod("FillerMethod");
-}
-function writeFileField(name, type) {
- fileName = "src/" name ".java";
- printf("public class %s {\n", name) > fileName;
- for (i = 1; i <= 65500; i++) {
- printf(" %s%d;\n", type, i) > fileName;
- }
- printf("}\n") > fileName;
-}
-function writeFileMethod(name) {
- fileName = "src/" name ".java";
- printf("public class %s {\n", name) > fileName;
- for (i = 1; i <= 65500; i++) {
- printf(" public void meth%d() { }\n", i) > fileName;
- }
- printf("}\n") > fileName;
-}'
-
-# Force DEX generation so test also passes with --jvm.
-export NEED_DEX=true
-
-# Specify old API level as d8 automagically produces a multidex file
-# when the API level is above 20. Failing the build here is deliberate.
-./default-build --api-level 20 "$@" > /dev/null 2> stderr.txt || true
-
-# Check that a build failure happened (the test is not expected to run).
-EXPECTED_ERROR="Cannot fit requested classes in a single dex"
-grep -q "$EXPECTED_ERROR" stderr.txt
-rm stderr.txt # Check passed. Remove output due to non-deterministic paths.
diff --git a/test/089-many-methods/info.txt b/test/089-many-methods/info.txt
deleted file mode 100644
index 4f73bd6..0000000
--- a/test/089-many-methods/info.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-Test that we print a reasonable message when the application exceeds more
-than 65536 methods.
diff --git a/test/089-many-methods/run b/test/089-many-methods/run
deleted file mode 100644
index 867dedc..0000000
--- a/test/089-many-methods/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2021 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.
-
-# Do nothing - the build intentionally failed.
diff --git a/test/091-override-package-private-method/build b/test/091-override-package-private-method/build
deleted file mode 100755
index 6b298f7..0000000
--- a/test/091-override-package-private-method/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 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.
-
-# Stop if something fails.
-set -e
-
-./default-build "$@"
diff --git a/test/091-override-package-private-method/javac_post.sh b/test/091-override-package-private-method/javac_post.sh
new file mode 100755
index 0000000..d2dd7e6
--- /dev/null
+++ b/test/091-override-package-private-method/javac_post.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+set -e # Stop on error - the caller script may not have this set.
+
+mkdir -p classes-ex
+mv classes/OverridePackagePrivateMethodSuper.class classes-ex
diff --git a/test/091-override-package-private-method/javac_wrapper.sh b/test/091-override-package-private-method/javac_wrapper.sh
deleted file mode 100755
index 0e2b2e1..0000000
--- a/test/091-override-package-private-method/javac_wrapper.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-set -e # Stop on error - the caller script may not have this set.
-$JAVAC "$@"
-mkdir -p classes-ex
-mv classes/OverridePackagePrivateMethodSuper.class classes-ex
diff --git a/test/091-override-package-private-method/run b/test/091-override-package-private-method/run
deleted file mode 100755
index d8c3c79..0000000
--- a/test/091-override-package-private-method/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Use secondary switch to add secondary dex file to class path.
-exec ${RUN} "${@}" --secondary
diff --git a/test/091-override-package-private-method/run.py b/test/091-override-package-private-method/run.py
new file mode 100644
index 0000000..2746221
--- /dev/null
+++ b/test/091-override-package-private-method/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # Use secondary switch to add secondary dex file to class path.
+ ctx.default_run(args, secondary=True)
diff --git a/test/099-vmdebug/check b/test/099-vmdebug/check
deleted file mode 100755
index 3d92188..0000000
--- a/test/099-vmdebug/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Strip the process pids and line numbers from exact error messages.
-sed -e '/^.*dalvikvm\(\|32\|64\) E.*\] /d' "$4" > "$4.tmp"
-
-diff --strip-trailing-cr -q "$1" "$2" >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4.tmp" >/dev/null
diff --git a/test/099-vmdebug/run.py b/test/099-vmdebug/run.py
new file mode 100644
index 0000000..4492f36
--- /dev/null
+++ b/test/099-vmdebug/run.py
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # Strip the process pids and line numbers from exact error messages.
+ ctx.run(fr"sed -i '/^.*dalvikvm\(\|32\|64\) E.*\] /d' '{args.stderr_file}'")
diff --git a/test/099-vmdebug/test-metadata.json b/test/099-vmdebug/test-metadata.json
new file mode 100644
index 0000000..2d1dd45
--- /dev/null
+++ b/test/099-vmdebug/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "run-param": {
+ "default-run": true
+ }
+}
diff --git a/test/100-reflect2/expected-stdout.txt b/test/100-reflect2/expected-stdout.txt
index a59f230..ca85392 100644
--- a/test/100-reflect2/expected-stdout.txt
+++ b/test/100-reflect2/expected-stdout.txt
@@ -31,9 +31,9 @@
30 (class java.lang.Integer)
62 (class java.lang.Long)
14 (class java.lang.Short)
-[java.lang.String(int,int,char[]), public java.lang.String(), public java.lang.String(byte[]), public java.lang.String(byte[],int), public java.lang.String(byte[],int,int), public java.lang.String(byte[],int,int,int), public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],int,int,java.nio.charset.Charset), public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],java.nio.charset.Charset), public java.lang.String(char[]), public java.lang.String(char[],int,int), public java.lang.String(int[],int,int), public java.lang.String(java.lang.String), public java.lang.String(java.lang.StringBuffer), public java.lang.String(java.lang.StringBuilder)]
-[private final int java.lang.String.count, private int java.lang.String.hash, private static final java.io.ObjectStreamField[] java.lang.String.serialPersistentFields, private static final long java.lang.String.serialVersionUID, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER]
-[native void java.lang.String.getCharsNoCheck(int,int,char[],int), private boolean java.lang.String.nonSyncContentEquals(java.lang.AbstractStringBuilder), private int java.lang.String.indexOfNonWhitespace(), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private native java.lang.String java.lang.String.doRepeat(int), private native java.lang.String java.lang.String.doReplace(char,char), private native java.lang.String java.lang.String.fastSubstring(int,int), private static int java.lang.String.indexOf(java.lang.String,java.lang.String,int), private static int java.lang.String.lastIndexOf(java.lang.String,java.lang.String,int), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public boolean java.lang.String.isBlank(), public boolean java.lang.String.isEmpty(), public boolean java.lang.String.matches(java.lang.String), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public byte[] java.lang.String.getBytes(), public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public byte[] java.lang.String.getBytes(java.nio.charset.Charset), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public int java.lang.String.compareTo(java.lang.Object), public int java.lang.String.compareToIgnoreCase(java.lang.String), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public int java.lang.String.offsetByCodePoints(int,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.repeat(int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public java.lang.String java.lang.String.strip(), public java.lang.String java.lang.String.stripLeading(), public java.lang.String java.lang.String.stripTrailing(), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public java.lang.String[] java.lang.String.split(java.lang.String), public java.lang.String[] java.lang.String.split(java.lang.String,int), public java.util.stream.IntStream java.lang.String.chars(), public java.util.stream.IntStream java.lang.String.codePoints(), public java.util.stream.Stream java.lang.String.lines(), public native char java.lang.String.charAt(int), public native char[] java.lang.String.toCharArray(), public native int java.lang.String.compareTo(java.lang.String), public native java.lang.String java.lang.String.concat(java.lang.String), public native java.lang.String java.lang.String.intern(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.CharSequence[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.Iterable), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(long), public void java.lang.String.getBytes(int,int,byte[],int), public void java.lang.String.getChars(int,int,char[],int), static int java.lang.String.indexOf(char[],int,int,char[],int,int,int), static int java.lang.String.indexOf(char[],int,int,java.lang.String,int), static int java.lang.String.lastIndexOf(char[],int,int,char[],int,int,int), static int java.lang.String.lastIndexOf(char[],int,int,java.lang.String,int), static void java.lang.String.checkBoundsBeginEnd(int,int,int), static void java.lang.String.checkBoundsOffCount(int,int,int), static void java.lang.String.checkIndex(int,int), void java.lang.String.getChars(char[],int)]
+[java.lang.String(byte[],byte), java.lang.String(int,int,char[]), public java.lang.String(), public java.lang.String(byte[]), public java.lang.String(byte[],int), public java.lang.String(byte[],int,int), public java.lang.String(byte[],int,int,int), public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],int,int,java.nio.charset.Charset), public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],java.nio.charset.Charset), public java.lang.String(char[]), public java.lang.String(char[],int,int), public java.lang.String(int[],int,int), public java.lang.String(java.lang.String), public java.lang.String(java.lang.StringBuffer), public java.lang.String(java.lang.StringBuilder)]
+[private final int java.lang.String.count, private int java.lang.String.hash, private static final java.io.ObjectStreamField[] java.lang.String.serialPersistentFields, private static final long java.lang.String.serialVersionUID, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER, static final boolean java.lang.String.COMPACT_STRINGS, static final byte java.lang.String.LATIN1, static final byte java.lang.String.UTF16]
+[byte java.lang.String.coder(), native void java.lang.String.getCharsNoCheck(int,int,char[],int), private boolean java.lang.String.nonSyncContentEquals(java.lang.AbstractStringBuilder), private int java.lang.String.indexOfNonWhitespace(), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfNonWhitespace(), private int java.lang.String.lastIndexOfSupplementary(int,int), private native java.lang.String java.lang.String.doRepeat(int), private native java.lang.String java.lang.String.doReplace(char,char), private native java.lang.String java.lang.String.fastSubstring(int,int), private native void java.lang.String.fillBytesLatin1(byte[],int), private native void java.lang.String.fillBytesUTF16(byte[],int), private static int java.lang.String.indexOf(java.lang.String,java.lang.String,int), private static int java.lang.String.lastIndexOf(java.lang.String,java.lang.String,int), private static int java.lang.String.outdent(java.util.List), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public boolean java.lang.String.isBlank(), public boolean java.lang.String.isEmpty(), public boolean java.lang.String.matches(java.lang.String), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public byte[] java.lang.String.getBytes(), public byte[] java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public byte[] java.lang.String.getBytes(java.nio.charset.Charset), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public int java.lang.String.compareTo(java.lang.Object), public int java.lang.String.compareToIgnoreCase(java.lang.String), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public int java.lang.String.offsetByCodePoints(int,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.Object java.lang.String.transform(java.util.function.Function), public java.lang.String java.lang.String.formatted(java.lang.Object[]), public java.lang.String java.lang.String.indent(int), public java.lang.String java.lang.String.repeat(int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public java.lang.String java.lang.String.strip(), public java.lang.String java.lang.String.stripIndent(), public java.lang.String java.lang.String.stripLeading(), public java.lang.String java.lang.String.stripTrailing(), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.translateEscapes(), public java.lang.String java.lang.String.trim(), public java.lang.String[] java.lang.String.split(java.lang.String), public java.lang.String[] java.lang.String.split(java.lang.String,int), public java.util.stream.IntStream java.lang.String.chars(), public java.util.stream.IntStream java.lang.String.codePoints(), public java.util.stream.Stream java.lang.String.lines(), public native char java.lang.String.charAt(int), public native char[] java.lang.String.toCharArray(), public native int java.lang.String.compareTo(java.lang.String), public native java.lang.String java.lang.String.concat(java.lang.String), public native java.lang.String java.lang.String.intern(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), public static java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.CharSequence[]), public static java.lang.String java.lang.String.join(java.lang.CharSequence,java.lang.Iterable), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(long), public void java.lang.String.getBytes(int,int,byte[],int), public void java.lang.String.getChars(int,int,char[],int), static int java.lang.String.indexOf(byte[],byte,int,java.lang.String,int), static int java.lang.String.lastIndexOf(byte[],byte,int,java.lang.String,int), static int java.lang.String.lastIndexOf(char[],int,int,char[],int,int,int), static java.lang.String java.lang.String.lambda$indent$0(java.lang.String,java.lang.String), static java.lang.String java.lang.String.lambda$indent$1(java.lang.String), static java.lang.String java.lang.String.lambda$indent$2(int,java.lang.String), static java.lang.String java.lang.String.lambda$stripIndent$3(int,java.lang.String), static void java.lang.String.checkBoundsBeginEnd(int,int,int), static void java.lang.String.checkBoundsOffCount(int,int,int), static void java.lang.String.checkIndex(int,int), static void java.lang.String.checkOffset(int,int), void java.lang.String.getBytes(byte[],int,byte), void java.lang.String.getChars(char[],int)]
[]
[interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence]
0
diff --git a/test/1000-non-moving-space-stress/src-art/Main.java b/test/1000-non-moving-space-stress/src-art/Main.java
index 18bfdd3..3cd7224 100644
--- a/test/1000-non-moving-space-stress/src-art/Main.java
+++ b/test/1000-non-moving-space-stress/src-art/Main.java
@@ -15,8 +15,11 @@
*/
import dalvik.system.VMRuntime;
+import java.lang.ref.Reference; // For reachabilityFence.
+import java.util.ArrayList;
public class Main {
+ private static final boolean SHOULD_PRINT = false; // True causes failure.
public static void main(String[] args) throws Exception {
VMRuntime runtime = VMRuntime.getRuntime();
@@ -35,15 +38,40 @@
Object[] moving_array = new Object[S];
}
} catch (OutOfMemoryError e) {
- // Stop here.
+ System.out.println("Unexpected OOME");
}
- System.out.println("passed");
+ Runtime.getRuntime().gc();
+ int numAllocs = 0;
+ ArrayList<Object> chunks = new ArrayList<>();
+ try {
+ final int MAX_PLAUSIBLE_ALLOCS = 1024 * 1024;
+ for (numAllocs = 0; numAllocs < MAX_PLAUSIBLE_ALLOCS; ++numAllocs) {
+ chunks.add(runtime.newNonMovableArray(Object.class, 252)); // About 1KB
+ }
+ // If we get here, we've allocated about 1GB of nonmovable memory, which
+ // should be impossible.
+ } catch (OutOfMemoryError e) {
+ chunks.remove(0); // Give us a little space back.
+ if (((Object[]) (chunks.get(42)))[17] != null) {
+ System.out.println("Bad entry in chunks array");
+ } else {
+ chunks.clear(); // Recover remaining space.
+ if (SHOULD_PRINT) {
+ System.out.println("Successfully allocated " + numAllocs + " non-movable KBs");
+ }
+ System.out.println("passed");
+ }
+ Reference.reachabilityFence(chunks);
+ return;
+ }
+ Reference.reachabilityFence(chunks);
+ System.out.println("Failed to exhaust non-movable space");
}
// When using the Concurrent Copying (CC) collector (default collector),
// this method allocates an object in the non-moving space and an object
// in the region space, make the former reference the later, and returns
- // nothing (so that none of these objects are reachable when upon return).
+ // nothing (so that none of these objects are reachable upon return).
static void $noinline$Alloc(VMRuntime runtime) {
Object[] non_moving_array = (Object[]) runtime.newNonMovableArray(Object.class, 1);
// Small object, unlikely to trigger garbage collection.
diff --git a/test/1001-app-image-regions/build b/test/1001-app-image-regions/build
deleted file mode 100755
index 16c3a5b..0000000
--- a/test/1001-app-image-regions/build
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# make us exit on a failure
-set -e
-
-count=10000
-echo "LMain;" >> profile
-for i in $(seq 1 "$count"); do
- echo "LOther\$Inner${i};" >> "profile"
-done
-
-# Generate the other class.
-other_file="src/Other.java"
-echo "class Other {" >> "${other_file}"
-for i in $(seq 1 "$count"); do
- echo " static class Inner${i} { void test(){} }" >> "${other_file}"
-done
-echo "}" >> "${other_file}"
-
-./default-build "$@"
diff --git a/test/1001-app-image-regions/build.py b/test/1001-app-image-regions/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/1001-app-image-regions/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/1001-app-image-regions/generate-sources b/test/1001-app-image-regions/generate-sources
new file mode 100755
index 0000000..0fbe23f
--- /dev/null
+++ b/test/1001-app-image-regions/generate-sources
@@ -0,0 +1,32 @@
+#!/bin/bash
+#
+# 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.
+
+# make us exit on a failure
+set -e
+
+count=10000
+echo "LMain;" >> profile
+for i in $(seq 1 "$count"); do
+ echo "LOther\$Inner${i};" >> "profile"
+done
+
+# Generate the other class.
+other_file="src/Other.java"
+echo "class Other {" >> "${other_file}"
+for i in $(seq 1 "$count"); do
+ echo " static class Inner${i} { void test(){} }" >> "${other_file}"
+done
+echo "}" >> "${other_file}"
diff --git a/test/1001-app-image-regions/run b/test/1001-app-image-regions/run
deleted file mode 100644
index 128aa2e..0000000
--- a/test/1001-app-image-regions/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile
diff --git a/test/1001-app-image-regions/run.py b/test/1001-app-image-regions/run.py
new file mode 100644
index 0000000..d529700
--- /dev/null
+++ b/test/1001-app-image-regions/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args, profile=True, Xcompiler_option=["--compiler-filter=speed-profile"])
diff --git a/test/1002-notify-startup/check b/test/1002-notify-startup/check
deleted file mode 100644
index eadb559..0000000
--- a/test/1002-notify-startup/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2020 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Oat file manager will complain about duplicate dex files. Ignore.
-sed -e '/.*oat_file_manager.*/d' "$4" > "$4.tmp"
-
-diff --strip-trailing-cr -q "$1" "$2" >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4.tmp" >/dev/null
diff --git a/test/1002-notify-startup/run.py b/test/1002-notify-startup/run.py
new file mode 100644
index 0000000..6e15f3a
--- /dev/null
+++ b/test/1002-notify-startup/run.py
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # Oat file manager will complain about duplicate dex files. Ignore.
+ ctx.run(fr"sed -i '/.*oat_file_manager.*/d' '{args.stderr_file}'")
diff --git a/test/1003-metadata-section-strings/build b/test/1003-metadata-section-strings/build
deleted file mode 100755
index cd2cacd..0000000
--- a/test/1003-metadata-section-strings/build
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# make us exit on a failure
-set -e
-
-count=5000
-
-# Generate the other class.
-other_file="src-art/Other.java"
-cat >${other_file} <<EOF
-class Other {
-static String getString(int i) {
-switch(i) {
-EOF
-
-for i in $(seq 1 "$count"); do
- echo " case ${i}: return \"string$i\";" >> "${other_file}"
-done
-
-cat >>${other_file} <<EOF
-}
-return "not found";
-}
-}
-EOF
-
-./default-build "$@"
diff --git a/test/1003-metadata-section-strings/build.py b/test/1003-metadata-section-strings/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/1003-metadata-section-strings/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/1003-metadata-section-strings/generate-sources b/test/1003-metadata-section-strings/generate-sources
new file mode 100755
index 0000000..c3e73f5
--- /dev/null
+++ b/test/1003-metadata-section-strings/generate-sources
@@ -0,0 +1,39 @@
+#!/bin/bash
+#
+# 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.
+
+# make us exit on a failure
+set -e
+
+count=5000
+
+# Generate the other class.
+other_file="src-art/Other.java"
+cat >${other_file} <<EOF
+class Other {
+static String getString(int i) {
+switch(i) {
+EOF
+
+for i in $(seq 1 "$count"); do
+ echo " case ${i}: return \"string$i\";" >> "${other_file}"
+done
+
+cat >>${other_file} <<EOF
+}
+return "not found";
+}
+}
+EOF
diff --git a/test/1003-metadata-section-strings/run b/test/1003-metadata-section-strings/run
deleted file mode 100644
index 9762aba..0000000
--- a/test/1003-metadata-section-strings/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile -Xcompiler-option --resolve-startup-const-strings=true
diff --git a/test/1003-metadata-section-strings/run.py b/test/1003-metadata-section-strings/run.py
new file mode 100644
index 0000000..7fc97aa
--- /dev/null
+++ b/test/1003-metadata-section-strings/run.py
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args,
+ profile=True,
+ Xcompiler_option=[
+ "--compiler-filter=speed-profile",
+ "--resolve-startup-const-strings=true"
+ ])
diff --git a/test/1004-checker-volatile-ref-load/run b/test/1004-checker-volatile-ref-load/run
deleted file mode 100644
index bdaa71b..0000000
--- a/test/1004-checker-volatile-ref-load/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#! /bin/bash
-#
-# 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.
-
-# Limit the managed heap to 16 MiB to force more garbage collections.
-exec ${RUN} $@ --runtime-option -Xmx16m
diff --git a/test/1004-checker-volatile-ref-load/run.py b/test/1004-checker-volatile-ref-load/run.py
new file mode 100644
index 0000000..97dc42a
--- /dev/null
+++ b/test/1004-checker-volatile-ref-load/run.py
@@ -0,0 +1,20 @@
+#! /bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Limit the managed heap to 16 MiB to force more garbage collections.
+ ctx.default_run(args, runtime_option=["-Xmx16m"])
diff --git a/test/105-invoke/src/Main.java b/test/105-invoke/src/Main.java
index d5f3b62..d4e4c4e 100644
--- a/test/105-invoke/src/Main.java
+++ b/test/105-invoke/src/Main.java
@@ -71,6 +71,7 @@
static int invoke(int a) {
Main foo = new Main();
+ // clang-format off
return foo.virI_I(a) +
foo.virI_II(a, 1) +
foo.virI_III(a, 1, 2) +
@@ -84,6 +85,7 @@
statI_IIIII(a, 1, 2, 3, 4) +
statI_IIIIII(a, 1, 2, 3, 4, 5) +
foo.interfaceMethod(a);
+ // clang-format on
}
public static void main(String[] args) {
diff --git a/test/107-int-math2/src/Main.java b/test/107-int-math2/src/Main.java
index ec5678d..727d47c 100644
--- a/test/107-int-math2/src/Main.java
+++ b/test/107-int-math2/src/Main.java
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+// clang-format off
+
class Main extends IntMathBase {
public static boolean mBoolean1, mBoolean2;
diff --git a/test/111-unresolvable-exception/build b/test/111-unresolvable-exception/build
deleted file mode 100644
index f378df1..0000000
--- a/test/111-unresolvable-exception/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Stop if something fails.
-set -e
-
-./default-build "$@"
diff --git a/test/111-unresolvable-exception/javac_post.sh b/test/111-unresolvable-exception/javac_post.sh
new file mode 100755
index 0000000..355f792
--- /dev/null
+++ b/test/111-unresolvable-exception/javac_post.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+set -e # Stop on error - the caller script may not have this set.
+
+# Remove class available at compile time but not at run time.
+rm classes/TestException.class
diff --git a/test/111-unresolvable-exception/javac_wrapper.sh b/test/111-unresolvable-exception/javac_wrapper.sh
deleted file mode 100755
index 9e4d2a2..0000000
--- a/test/111-unresolvable-exception/javac_wrapper.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-set -e # Stop on error - the caller script may not have this set.
-
-$JAVAC "$@"
-
-# Remove class available at compile time but not at run time.
-rm classes/TestException.class
diff --git a/test/115-native-bridge/check b/test/115-native-bridge/check
deleted file mode 100755
index e01de91..0000000
--- a/test/115-native-bridge/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# ASAN prints a warning here.
-
-diff --strip-trailing-cr -q "$1" "$2" >/dev/null \
- && sed -e '/WARNING: ASan is ignoring requested __asan_handle_no_return/,+2d' "$4" \
- | diff --strip-trailing-cr -q "$3" - >/dev/null
diff --git a/test/115-native-bridge/run b/test/115-native-bridge/run
deleted file mode 100644
index 8ce44a9..0000000
--- a/test/115-native-bridge/run
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/bin/sh
-#
-# Copyright (C) 2012 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.
-
-ARGS=${@}
-
-BRIDGE_SO=libnativebridgetestd.so
-if echo ${ARGS} | grep -q " -O"; then
- BRIDGE_SO=libnativebridgetest.so
-fi
-
-# Use libnativebridgetest as a native bridge, start NativeBridgeMain (Main is JniTest main file).
-LIBPATH=$(echo ${ARGS} | sed -r 's/.*Djava.library.path=([^ ]*) .*/\1/')
-# Trim all but the last entry in LIBPATH, which will be nativetest[64]
-LIBPATH=${LIBPATH##*:}
-ln -sf ${LIBPATH}/$BRIDGE_SO .
-touch libarttest.so
-touch libarttestd.so
-touch libinvalid.so
-ln -sf ${LIBPATH}/libarttest.so libarttest2.so
-ln -sf ${LIBPATH}/libarttestd.so libarttestd2.so
-
-# pwd likely has /, so it's a pain to put that into a sed rule.
-LEFT=$(echo ${ARGS} | sed -r 's/-Djava.library.path.*//')
-RIGHT=$(echo ${ARGS} | sed -r 's/.*Djava.library.path[^ ]* //')
-MODARGS="${LEFT} -Djava.library.path=`pwd` ${RIGHT}"
-exec ${RUN} --runtime-option -Xforce-nb-testing --runtime-option -XX:NativeBridge=$BRIDGE_SO ${MODARGS} NativeBridgeMain
diff --git a/test/115-native-bridge/run.py b/test/115-native-bridge/run.py
new file mode 100644
index 0000000..619bde0
--- /dev/null
+++ b/test/115-native-bridge/run.py
@@ -0,0 +1,46 @@
+#!/bin/sh
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import re
+
+
+def run(ctx, args):
+ bridge_so = "libnativebridgetest.so" if args.O else "libnativebridgetestd.so"
+ test_dir = ctx.env.DEX_LOCATION
+
+ # Use libnativebridgetest as a native bridge, start NativeBridgeMain (Main is JniTest main file).
+ for i, opt in enumerate(args.runtime_option):
+ if opt.startswith("-Djava.library.path="):
+ libpath = opt.split(":")[-1] # last entry in libpath is nativetest[64]
+ args.runtime_option[i] = "-Djava.library.path=" + test_dir
+
+ assert libpath
+ ctx.run(f"ln -sf {libpath}/{bridge_so} {test_dir}/.")
+ ctx.run(
+ f"touch {test_dir}/libarttest.so {test_dir}/libarttestd.so {test_dir}/libinvalid.so"
+ )
+ ctx.run(f"ln -sf {libpath}/libarttest.so {test_dir}/libarttest2.so")
+ ctx.run(f"ln -sf {libpath}/libarttestd.so {test_dir}/libarttestd2.so")
+
+ ctx.default_run(
+ args,
+ runtime_option=["-Xforce-nb-testing", f"-XX:NativeBridge={bridge_so}"],
+ main="NativeBridgeMain")
+
+ # ASAN prints a warning here.
+ ctx.run(
+ fr"sed -i '/WARNING: ASan is ignoring requested __asan_handle_no_return/,+2d' '{args.stderr_file}'"
+ )
diff --git a/test/116-nodex2oat/run b/test/116-nodex2oat/run
deleted file mode 100755
index 9063685..0000000
--- a/test/116-nodex2oat/run
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-flags="${@}"
-
-# This test is supposed to test without oat files, so doesn't work for prebuild. Make sure that
-# flag isn't set, or complain.
-# Note: prebuild is the default.
-if [[ "${flags}" == *--prebuild* || "${flags}" != *--no-prebuild* ]] ; then
- echo "Test 116-nodex2oat cannot run in prebuild mode."
- exit 1
-fi
-
-${RUN} ${flags}
diff --git a/test/116-nodex2oat/run.py b/test/116-nodex2oat/run.py
new file mode 100644
index 0000000..5b38316
--- /dev/null
+++ b/test/116-nodex2oat/run.py
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # This test is supposed to test without oat files, so doesn't work for prebuild.
+ assert not args.prebuild
+
+ ctx.default_run(args)
diff --git a/test/116-nodex2oat/src/Main.java b/test/116-nodex2oat/src/Main.java
index 5491c49..0e675c8 100644
--- a/test/116-nodex2oat/src/Main.java
+++ b/test/116-nodex2oat/src/Main.java
@@ -15,10 +15,10 @@
*/
public class Main {
- public static void main(String[] args) {
- System.loadLibrary(args[0]);
- System.out.println("Has oat is " + hasOatFile() + ".");
- }
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+ System.out.println("Has oat is " + hasOatFile() + ".");
+ }
- private native static boolean hasOatFile();
+ private native static boolean hasOatFile();
}
diff --git a/test/118-noimage-dex2oat/check b/test/118-noimage-dex2oat/check
deleted file mode 100755
index 90ffdf8..0000000
--- a/test/118-noimage-dex2oat/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Strip the process pids and line numbers from exact error messages.
-sed -e '/^dalvikvm.* E.*\] /d' "$4" > "$4.tmp"
-
-diff --strip-trailing-cr -q "$1" "$2" >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4.tmp" >/dev/null
diff --git a/test/118-noimage-dex2oat/run b/test/118-noimage-dex2oat/run
deleted file mode 100644
index 1b6251d..0000000
--- a/test/118-noimage-dex2oat/run
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-flags="$@"
-
-# This test is supposed to test without oat files, so doesn't work for prebuild. Make sure that
-# flag isn't set, or complain.
-# Note: prebuild is the default.
-if [[ "${flags}" == *--prebuild* || "${flags}" != *--no-prebuild* ]] ; then
- echo "Test 118-noimage-dex2oat cannot run in prebuild mode."
- exit 1
-fi
-
-# Force relocation otherwise we will just use the already created core.oat/art pair.
-# Note: relocate is the default.
-if [[ "${flags}" == *--no-relocate* ]] ; then
- echo "Test 118-noimage-dex2oat is not intended to run in no-relocate mode."
- exit 1
-fi
-
-
-# Make sure we can run without an oat file.
-echo "Run -Xnoimage-dex2oat"
-${RUN} ${flags} --runtime-option -Xnoimage-dex2oat
-return_status1=$?
-
-# Make sure we can run with the oat file.
-echo "Run -Ximage-dex2oat"
-${RUN} ${flags} --runtime-option -Ximage-dex2oat
-return_status2=$?
-
-# Make sure we can run with the default settings.
-echo "Run default"
-${RUN} ${flags}
-return_status3=$?
-
-# Make sure we don't silently ignore an early failure.
-(exit $return_status1) && (exit $return_status2) && (exit $return_status3)
diff --git a/test/118-noimage-dex2oat/run.py b/test/118-noimage-dex2oat/run.py
new file mode 100644
index 0000000..88efcca
--- /dev/null
+++ b/test/118-noimage-dex2oat/run.py
@@ -0,0 +1,38 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # This test is supposed to test without oat files, so doesn't work for prebuild.
+ assert not args.prebuild
+
+ # Force relocation otherwise we will just use the already created core.oat/art pair.
+ assert args.relocate
+
+ # Make sure we can run without an oat file.
+ ctx.echo("Run -Xnoimage-dex2oat")
+ ctx.default_run(args, runtime_option=["-Xnoimage-dex2oat"])
+
+ # Make sure we can run with the oat file.
+ ctx.echo("Run -Ximage-dex2oat")
+ ctx.default_run(args, runtime_option=["-Ximage-dex2oat"])
+
+ # Make sure we can run with the default settings.
+ ctx.echo("Run default")
+ ctx.default_run(args)
+
+ # Strip the process pids and line numbers from exact error messages.
+ ctx.run(fr"sed -i '/^dalvikvm.* E.*\] /d' '{args.stderr_file}'")
diff --git a/test/118-noimage-dex2oat/src/Main.java b/test/118-noimage-dex2oat/src/Main.java
index cc19107..f83cadc 100644
--- a/test/118-noimage-dex2oat/src/Main.java
+++ b/test/118-noimage-dex2oat/src/Main.java
@@ -18,68 +18,68 @@
import java.lang.reflect.Method;
public class Main {
- public static void main(String[] args) throws Exception {
- System.loadLibrary(args[0]);
- boolean hasImage = hasImage();
- String instructionSet = VMRuntime.getCurrentInstructionSet();
- boolean isBootClassPathOnDisk = VMRuntime.isBootClassPathOnDisk(instructionSet);
- System.out.println(
- "Has image is " + hasImage + ", is image dex2oat enabled is "
- + isImageDex2OatEnabled() + ", is BOOTCLASSPATH on disk is "
- + isBootClassPathOnDisk + ".");
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ boolean hasImage = hasImage();
+ String instructionSet = VMRuntime.getCurrentInstructionSet();
+ boolean isBootClassPathOnDisk = VMRuntime.isBootClassPathOnDisk(instructionSet);
+ System.out.println(
+ "Has image is " + hasImage + ", is image dex2oat enabled is "
+ + isImageDex2OatEnabled() + ", is BOOTCLASSPATH on disk is "
+ + isBootClassPathOnDisk + ".");
- if (hasImage && !isImageDex2OatEnabled()) {
- throw new Error("Image with dex2oat disabled runs with an oat file");
- } else if (!hasImage && isImageDex2OatEnabled()) {
- throw new Error("Image with dex2oat enabled runs without an oat file");
- }
- if (hasImage && !isBootClassPathOnDisk) {
- throw new Error("Image with dex2oat disabled runs with an image file");
- } else if (!hasImage && isBootClassPathOnDisk) {
- throw new Error("Image with dex2oat enabled runs without an image file");
+ if (hasImage && !isImageDex2OatEnabled()) {
+ throw new Error("Image with dex2oat disabled runs with an oat file");
+ } else if (!hasImage && isImageDex2OatEnabled()) {
+ throw new Error("Image with dex2oat enabled runs without an oat file");
+ }
+ if (hasImage && !isBootClassPathOnDisk) {
+ throw new Error("Image with dex2oat disabled runs with an image file");
+ } else if (!hasImage && isBootClassPathOnDisk) {
+ throw new Error("Image with dex2oat enabled runs without an image file");
+ }
+
+ testB18485243();
}
- testB18485243();
- }
+ private native static boolean hasImage();
- private native static boolean hasImage();
+ private native static boolean isImageDex2OatEnabled();
- private native static boolean isImageDex2OatEnabled();
+ private static class VMRuntime {
+ private static final Method getCurrentInstructionSetMethod;
+ private static final Method isBootClassPathOnDiskMethod;
+ static {
+ try {
+ Class<?> c = Class.forName("dalvik.system.VMRuntime");
+ getCurrentInstructionSetMethod = c.getDeclaredMethod("getCurrentInstructionSet");
+ isBootClassPathOnDiskMethod =
+ c.getDeclaredMethod("isBootClassPathOnDisk", String.class);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
- private static class VMRuntime {
- private static final Method getCurrentInstructionSetMethod;
- private static final Method isBootClassPathOnDiskMethod;
- static {
- try {
- Class<?> c = Class.forName("dalvik.system.VMRuntime");
- getCurrentInstructionSetMethod = c.getDeclaredMethod("getCurrentInstructionSet");
- isBootClassPathOnDiskMethod = c.getDeclaredMethod("isBootClassPathOnDisk",
- String.class);
- } catch (Exception e) {
- throw new RuntimeException(e);
+ public static String getCurrentInstructionSet() throws Exception {
+ return (String) getCurrentInstructionSetMethod.invoke(null);
+ }
+ public static boolean isBootClassPathOnDisk(String instructionSet) throws Exception {
+ return (boolean) isBootClassPathOnDiskMethod.invoke(null, instructionSet);
}
}
- public static String getCurrentInstructionSet() throws Exception {
- return (String) getCurrentInstructionSetMethod.invoke(null);
+ private static void testB18485243() throws Exception {
+ Class<?> k = Class.forName("B18485243");
+ Object o = k.newInstance();
+ Method m = k.getDeclaredMethod("run");
+ try {
+ m.invoke(o);
+ } catch (InvocationTargetException e) {
+ Throwable actual = e.getTargetException();
+ if (!(actual instanceof IncompatibleClassChangeError)) {
+ throw new AssertionError("Expected IncompatibleClassChangeError", actual);
+ }
+ }
+ System.out.println("testB18485243 PASS");
}
- public static boolean isBootClassPathOnDisk(String instructionSet) throws Exception {
- return (boolean) isBootClassPathOnDiskMethod.invoke(null, instructionSet);
- }
- }
-
- private static void testB18485243() throws Exception {
- Class<?> k = Class.forName("B18485243");
- Object o = k.newInstance();
- Method m = k.getDeclaredMethod("run");
- try {
- m.invoke(o);
- } catch (InvocationTargetException e) {
- Throwable actual = e.getTargetException();
- if (!(actual instanceof IncompatibleClassChangeError)) {
- throw new AssertionError("Expected IncompatibleClassChangeError", actual);
- }
- }
- System.out.println("testB18485243 PASS");
- }
}
diff --git a/test/122-npe/build.py b/test/122-npe/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/122-npe/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/122-npe/test-metadata.json b/test/122-npe/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/122-npe/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/124-missing-classes/build b/test/124-missing-classes/build
deleted file mode 100644
index a40cbc9..0000000
--- a/test/124-missing-classes/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2012 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.
-
-# Stop if something fails.
-set -e
-
-./default-build "$@"
diff --git a/test/124-missing-classes/javac_post.sh b/test/124-missing-classes/javac_post.sh
new file mode 100755
index 0000000..39e26bb
--- /dev/null
+++ b/test/124-missing-classes/javac_post.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+set -e # Stop on error - the caller script may not have this set.
+
+# Some classes are available at compile time but not at run time.
+rm 'classes/MissingClass.class' 'classes/Main$MissingInnerClass.class'
diff --git a/test/124-missing-classes/javac_wrapper.sh b/test/124-missing-classes/javac_wrapper.sh
deleted file mode 100755
index c6c234d..0000000
--- a/test/124-missing-classes/javac_wrapper.sh
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-set -e # Stop on error - the caller script may not have this set.
-
-# Some classes are available at compile time...
-$JAVAC "$@"
-
-# ...but not at run time.
-rm 'classes/MissingClass.class' 'classes/Main$MissingInnerClass.class'
diff --git a/test/126-miranda-multidex/build b/test/126-miranda-multidex/build
deleted file mode 100644
index c827e55..0000000
--- a/test/126-miranda-multidex/build
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Stop if something fails.
-set -e
-
-# Signal to default-build that this is a multidex test.
-mkdir src-multidex
-./default-build "$@"
diff --git a/test/126-miranda-multidex/build.py b/test/126-miranda-multidex/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/126-miranda-multidex/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/126-miranda-multidex/generate-sources b/test/126-miranda-multidex/generate-sources
new file mode 100755
index 0000000..f42ba10
--- /dev/null
+++ b/test/126-miranda-multidex/generate-sources
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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.
+
+# Stop if something fails.
+set -e
+
+# Signal to default-build that this is a multidex test.
+mkdir src-multidex
+touch src-multidex/Empty.java
diff --git a/test/126-miranda-multidex/javac_post.sh b/test/126-miranda-multidex/javac_post.sh
new file mode 100755
index 0000000..79a9cef
--- /dev/null
+++ b/test/126-miranda-multidex/javac_post.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+set -e # Stop on error - the caller script may not have this set.
+
+if [[ "$1" == "classes2" ]]; then
+ mv classes/MirandaInterface.class classes2
+fi
diff --git a/test/126-miranda-multidex/javac_wrapper.sh b/test/126-miranda-multidex/javac_wrapper.sh
deleted file mode 100755
index 71cafa1..0000000
--- a/test/126-miranda-multidex/javac_wrapper.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-set -e # Stop on error - the caller script may not have this set.
-
-if [[ "$*" != *"classes2"* ]]; then
- # First invocation: compile src/ files.
- $JAVAC "$@"
-else
- # Second invocation: move MirandaInterface.class for placement in
- # a secondary dex file. There are no other source files for the
- # secondary DEX so no compilation required.
- mv classes/MirandaInterface.class classes2
-fi
-exit $?
diff --git a/test/126-miranda-multidex/run b/test/126-miranda-multidex/run
deleted file mode 100755
index abd63cb..0000000
--- a/test/126-miranda-multidex/run
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-${RUN} $@
-return_status1=$?
-
-# The problem was first exposed in a no-verify setting, as that changes the resolution path
-# taken. Make sure we also test in that environment.
-${RUN} --no-verify ${@}
-return_status2=$?
-
-# Make sure we don't silently ignore an early failure.
-(exit $return_status1) && (exit $return_status2)
diff --git a/test/126-miranda-multidex/run.py b/test/126-miranda-multidex/run.py
new file mode 100644
index 0000000..1896315
--- /dev/null
+++ b/test/126-miranda-multidex/run.py
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import sys
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # The problem was first exposed in a no-verify setting, as that changes the resolution path
+ # taken. Make sure we also test in that environment.
+ ctx.default_run(args, no_verify=True)
diff --git a/test/127-checker-secondarydex/build b/test/127-checker-secondarydex/build
deleted file mode 100755
index f378df1..0000000
--- a/test/127-checker-secondarydex/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Stop if something fails.
-set -e
-
-./default-build "$@"
diff --git a/test/127-checker-secondarydex/javac_post.sh b/test/127-checker-secondarydex/javac_post.sh
new file mode 100755
index 0000000..e9d5d18
--- /dev/null
+++ b/test/127-checker-secondarydex/javac_post.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+set -e # Stop on error - the caller script may not have this set.
+
+mkdir classes-ex
+mv classes/Super.class classes-ex
diff --git a/test/127-checker-secondarydex/javac_wrapper.sh b/test/127-checker-secondarydex/javac_wrapper.sh
deleted file mode 100755
index 7fe6a55..0000000
--- a/test/127-checker-secondarydex/javac_wrapper.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-set -e # Stop on error - the caller script may not have this set.
-
-$JAVAC "$@"
-
-mkdir classes-ex
-mv classes/Super.class classes-ex
diff --git a/test/127-checker-secondarydex/run b/test/127-checker-secondarydex/run
deleted file mode 100755
index d8c3c79..0000000
--- a/test/127-checker-secondarydex/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Use secondary switch to add secondary dex file to class path.
-exec ${RUN} "${@}" --secondary
diff --git a/test/127-checker-secondarydex/run.py b/test/127-checker-secondarydex/run.py
new file mode 100644
index 0000000..2746221
--- /dev/null
+++ b/test/127-checker-secondarydex/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # Use secondary switch to add secondary dex file to class path.
+ ctx.default_run(args, secondary=True)
diff --git a/test/130-hprof/run b/test/130-hprof/run
deleted file mode 100644
index 73a984e..0000000
--- a/test/130-hprof/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2020 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.
-
-# Currently app images aren't unloaded when dex files are unloaded.
-exec ${RUN} $@ --no-secondary-app-image
diff --git a/test/130-hprof/run.py b/test/130-hprof/run.py
new file mode 100644
index 0000000..c3fda2f
--- /dev/null
+++ b/test/130-hprof/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # Currently app images aren't unloaded when dex files are unloaded.
+ ctx.default_run(args, secondary_app_image=False)
diff --git a/test/133-static-invoke-super/run b/test/133-static-invoke-super/run
deleted file mode 100755
index e27a622..0000000
--- a/test/133-static-invoke-super/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2012 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.
-
-# As this is a performance test we always use the non-debug build.
-exec ${RUN} "${@/#libartd.so/libart.so}"
diff --git a/test/133-static-invoke-super/run.py b/test/133-static-invoke-super/run.py
new file mode 100644
index 0000000..dbb09fd
--- /dev/null
+++ b/test/133-static-invoke-super/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # As this is a performance test we always use the non-debug build.
+ ctx.default_run(args, lib="libart.so")
diff --git a/test/1336-short-finalizer-timeout/build.py b/test/1336-short-finalizer-timeout/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/1336-short-finalizer-timeout/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/1336-short-finalizer-timeout/expected-stdout.txt b/test/1336-short-finalizer-timeout/expected-stdout.txt
index 496e0e6..802b89e 100644
--- a/test/1336-short-finalizer-timeout/expected-stdout.txt
+++ b/test/1336-short-finalizer-timeout/expected-stdout.txt
@@ -3,4 +3,3 @@
Finalizer started and snoozing...
Finalizer done snoozing.
Finalizer sleeping forever now.
-exit status: 2
diff --git a/test/1336-short-finalizer-timeout/run b/test/1336-short-finalizer-timeout/run
deleted file mode 100755
index 4a07034..0000000
--- a/test/1336-short-finalizer-timeout/run
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/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.
-
-# The test logs error messages which is expected, discard them.
-export ANDROID_LOG_TAGS='*:f'
-
-# Squash the exit status and put it in expected
-./default-run --external-log-tags "$@" --runtime-option -XX:FinalizerTimeoutMs=500
-echo "exit status:" $?
diff --git a/test/1336-short-finalizer-timeout/run.py b/test/1336-short-finalizer-timeout/run.py
new file mode 100644
index 0000000..3c74a56
--- /dev/null
+++ b/test/1336-short-finalizer-timeout/run.py
@@ -0,0 +1,23 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args,
+ android_log_tags="*:f",
+ runtime_option=["-XX:FinalizerTimeoutMs=500"],
+ expected_exit_code=2)
diff --git a/test/1336-short-finalizer-timeout/src/Main.java b/test/1336-short-finalizer-timeout/src/Main.java
index 1a28a64..3f024b1 100644
--- a/test/1336-short-finalizer-timeout/src/Main.java
+++ b/test/1336-short-finalizer-timeout/src/Main.java
@@ -22,7 +22,8 @@
* Test a class with a bad finalizer in an environment with a short finalizer timeout.
*
* This test is inherently flaky. It assumes that the system will schedule the finalizer daemon
- * and finalizer watchdog daemon enough to reach the timeout and throwing the fatal exception.
+ * and finalizer watchdog daemon soon and often enough to reach the timeout and throw the fatal
+ * exception before we time out here.
* Largely cloned from 030-bad-finalizer.
*/
public class Main {
@@ -49,7 +50,7 @@
snooze(9800);
// We should not get here, since it should only take 5.5 seconds for the timed out
- // fiinalizer to kill the process.
+ // finalizer to kill the process.
System.out.println("UNREACHABLE");
System.exit(0);
}
diff --git a/test/1336-short-finalizer-timeout/test-metadata.json b/test/1336-short-finalizer-timeout/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/1336-short-finalizer-timeout/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/1337-gc-coverage/check b/test/1337-gc-coverage/check
deleted file mode 100755
index 67fcafb..0000000
--- a/test/1337-gc-coverage/check
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Check that the string "error" isn't present
-grep -vq error "$2" \
- && diff --strip-trailing-cr -q "$3" "$4" >/dev/null
diff --git a/test/1337-gc-coverage/run.py b/test/1337-gc-coverage/run.py
new file mode 100644
index 0000000..9216c06
--- /dev/null
+++ b/test/1337-gc-coverage/run.py
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # Check that the string "error" isn't present (delete all non-error lines)
+ ctx.run(fr"sed -i '/error/!d' '{args.stdout_file}'")
diff --git a/test/1338-gc-no-los/run b/test/1338-gc-no-los/run
deleted file mode 100755
index f3c43fb..0000000
--- a/test/1338-gc-no-los/run
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/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.
-./default-run "$@" --runtime-option -XX:LargeObjectSpace=disabled
diff --git a/test/1338-gc-no-los/run.py b/test/1338-gc-no-los/run.py
new file mode 100644
index 0000000..1c64d11
--- /dev/null
+++ b/test/1338-gc-no-los/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, runtime_option=["-XX:LargeObjectSpace=disabled"])
diff --git a/test/1339-dead-reference-safe/build.py b/test/1339-dead-reference-safe/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/1339-dead-reference-safe/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/1339-dead-reference-safe/check b/test/1339-dead-reference-safe/check
deleted file mode 100644
index e7f5f96..0000000
--- a/test/1339-dead-reference-safe/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# DeadReferenceSafe result differs for interpreted mode. A real failure
-# will produce an extra line anyway.
-
-diff --ignore-matching-lines="DeadReferenceSafe count:" -q $1 $2 \
- && diff --strip-trailing-cr -q "$3" "$4" >/dev/null
diff --git a/test/1339-dead-reference-safe/run.py b/test/1339-dead-reference-safe/run.py
new file mode 100644
index 0000000..6b81ed4
--- /dev/null
+++ b/test/1339-dead-reference-safe/run.py
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # DeadReferenceSafe result differs for interpreted mode. A real failure
+ # will produce an extra line anyway.
+ ctx.run(
+ fr"sed -i -E 's/^(.*DeadReferenceSafe count: )[0-9]+(.*)$/\1N\2/' '{args.stdout_file}'"
+ )
diff --git a/test/1339-dead-reference-safe/test-metadata.json b/test/1339-dead-reference-safe/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/1339-dead-reference-safe/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
index 4e756d2..cd5b4cf 100644
--- a/test/137-cfi/cfi.cc
+++ b/test/137-cfi/cfi.cc
@@ -28,7 +28,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
-#include <backtrace/Backtrace.h>
+#include <unwindstack/AndroidUnwinder.h>
#include "base/file_utils.h"
#include "base/logging.h"
@@ -106,47 +106,55 @@
// Helper to look for a sequence in the stack trace.
#if __linux__
-static bool CheckStack(Backtrace* bt, const std::vector<std::string>& seq) {
+static bool CheckStack(unwindstack::AndroidUnwinder& unwinder,
+ unwindstack::AndroidUnwinderData& data,
+ const std::vector<std::string>& seq) {
size_t cur_search_index = 0; // The currently active index in seq.
CHECK_GT(seq.size(), 0U);
- bool any_empty_name = false;
- for (size_t i = 0; i < bt->NumFrames(); i++) {
- const backtrace_frame_data_t* frame = bt->GetFrame(i);
- if (BacktraceMap::IsValid(frame->map)) {
- if (cur_search_index < seq.size()) {
- LOG(INFO) << "Got " << frame->func_name << ", looking for " << seq[cur_search_index];
- if (frame->func_name.find(seq[cur_search_index]) != std::string::npos) {
- cur_search_index++;
- }
+ bool ok = true;
+ for (const unwindstack::FrameData& frame : data.frames) {
+ if (frame.map_info == nullptr) {
+ printf("Error: No map_info for frame #%02zu\n", frame.num);
+ ok = false;
+ continue;
+ }
+ const std::string& function_name = frame.function_name;
+ if (cur_search_index < seq.size()) {
+ LOG(INFO) << "Got " << function_name << ", looking for " << seq[cur_search_index];
+ if (function_name.find(seq[cur_search_index]) != std::string::npos) {
+ cur_search_index++;
}
}
- any_empty_name |= frame->func_name.empty();
- if (frame->func_name == "main") {
+ if (function_name == "main") {
break;
}
+#if !defined(__ANDROID__) && defined(__BIONIC__ ) // host-bionic
+ // TODO(b/182810709): Unwinding is broken on host-bionic so we expect some empty frames.
+#else
+ const std::string& lib_name = frame.map_info->name();
+ if (!kIsTargetBuild && lib_name.find("libc.so") != std::string::npos) {
+ // TODO(b/254626913): Unwinding can fail for libc on host.
+ } else if (function_name.empty()) {
+ printf("Error: No function name for frame #%02zu\n", frame.num);
+ ok = false;
+ }
+#endif
}
if (cur_search_index < seq.size()) {
- printf("Cannot find %s in backtrace:\n", seq[cur_search_index].c_str());
- } else if (any_empty_name) {
-#if defined(__BIONIC__ ) && !defined(__ANDROID__)
- // TODO(b/182810709): Unwinding is broken on host-bionic so we expect some empty frames.
- return true;
-#else
- printf("Missing frames in backtrace:\n");
-#endif
- } else {
- return true;
+ printf("Error: Cannot find %s\n", seq[cur_search_index].c_str());
+ ok = false;
}
- for (Backtrace::const_iterator it = bt->begin(); it != bt->end(); ++it) {
- if (BacktraceMap::IsValid(it->map)) {
- printf(" %s\n", Backtrace::FormatFrameData(&*it).c_str());
+ if (!ok) {
+ printf("Backtrace:\n");
+ for (const unwindstack::FrameData& frame : data.frames) {
+ printf(" %s\n", unwinder.FormatFrame(frame).c_str());
}
}
- return false;
+ return ok;
}
static void MoreErrorInfo(pid_t pid, bool sig_quit_on_fail) {
@@ -166,13 +174,11 @@
#if __linux__
MutexLock mu(Thread::Current(), *GetNativeDebugInfoLock()); // Avoid races with the JIT thread.
- std::unique_ptr<Backtrace> bt(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, GetTid()));
- if (!bt->Unwind(0, nullptr)) {
+ unwindstack::AndroidLocalUnwinder unwinder;
+ unwindstack::AndroidUnwinderData data;
+ if (!unwinder.Unwind(data)) {
printf("Cannot unwind in process.\n");
return JNI_FALSE;
- } else if (bt->NumFrames() == 0) {
- printf("No frames for unwind in process.\n");
- return JNI_FALSE;
}
// We cannot really parse an exact stack, as the optimizing compiler may inline some functions.
@@ -186,7 +192,7 @@
"Main.main" // The Java entry method.
};
- bool result = CheckStack(bt.get(), seq);
+ bool result = CheckStack(unwinder, data, seq);
if (!kCauseSegfault) {
return result ? JNI_TRUE : JNI_FALSE;
} else {
@@ -260,17 +266,13 @@
return JNI_FALSE;
}
- std::unique_ptr<Backtrace> bt(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD));
bool result = true;
- if (!bt->Unwind(0, nullptr)) {
+ unwindstack::AndroidRemoteUnwinder unwinder(pid);
+ unwindstack::AndroidUnwinderData data;
+ if (!unwinder.Unwind(data)) {
printf("Cannot unwind other process.\n");
result = false;
- } else if (bt->NumFrames() == 0) {
- printf("No frames for unwind of other process.\n");
- result = false;
- }
-
- if (result) {
+ } else {
// See comment in unwindInProcess for non-exact stack matching.
// "mini-debug-info" does not include parameters to save space.
std::vector<std::string> seq = {
@@ -280,7 +282,7 @@
"Main.main" // The Java entry method.
};
- result = CheckStack(bt.get(), seq);
+ result = CheckStack(unwinder, data, seq);
}
constexpr bool kSigQuitOnFail = true;
diff --git a/test/137-cfi/run b/test/137-cfi/run
deleted file mode 100755
index f09765f..0000000
--- a/test/137-cfi/run
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2008 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.
-
-# Test with full DWARF debugging information.
-# Check full signatures of methods.
-${RUN} "$@" -Xcompiler-option --generate-debug-info \
- --args --test-local --args --test-remote
-return_status1=$?
-
-# The option jitthreshold:0 ensures that if we run the test in JIT mode,
-# there will be JITed frames on the callstack (it synchronously JITs on first use).
-${RUN} "$@" -Xcompiler-option --generate-debug-info \
- --runtime-option -Xjitthreshold:0 \
- --args --test-local --args --test-remote
-return_status2=$?
-
-# Test with minimal compressed debugging information.
-# Check only method names (parameters are omitted to save space).
-# Check only remote unwinding since decompression is disabled in local unwinds (b/27391690).
-${RUN} "$@" -Xcompiler-option --generate-mini-debug-info \
- --runtime-option -Xjitthreshold:0 \
- --args --test-remote
-return_status3=$?
-
-# Make sure we don't silently ignore an early failure.
-(exit $return_status1) && (exit $return_status2) && (exit $return_status3)
diff --git a/test/137-cfi/run.py b/test/137-cfi/run.py
new file mode 100644
index 0000000..987d147
--- /dev/null
+++ b/test/137-cfi/run.py
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import sys
+
+
+def run(ctx, args):
+ # Test with full DWARF debugging information.
+ # Check full signatures of methods.
+ ctx.default_run(
+ args,
+ Xcompiler_option=["--generate-debug-info"],
+ test_args=["--test-local", "--test-remote"])
+
+ # The option jitthreshold:0 ensures that if we run the test in JIT mode,
+ # there will be JITed frames on the callstack (it synchronously JITs on first use).
+ ctx.default_run(
+ args,
+ Xcompiler_option=["--generate-debug-info"],
+ runtime_option=["-Xjitthreshold:0"],
+ test_args=["--test-local", "--test-remote"])
+
+ # Test with minimal compressed debugging information.
+ # Check only method names (parameters are omitted to save space).
+ # Check only remote unwinding since decompression is disabled in local unwinds (b/27391690).
+ ctx.default_run(
+ args,
+ Xcompiler_option=["--generate-mini-debug-info"],
+ runtime_option=["-Xjitthreshold:0"],
+ test_args=["--test-remote"])
diff --git a/test/138-duplicate-classes-check2/build b/test/138-duplicate-classes-check2/build
deleted file mode 100755
index 6b298f7..0000000
--- a/test/138-duplicate-classes-check2/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 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.
-
-# Stop if something fails.
-set -e
-
-./default-build "$@"
diff --git a/test/138-duplicate-classes-check2/javac_post.sh b/test/138-duplicate-classes-check2/javac_post.sh
new file mode 100755
index 0000000..ce8b82f
--- /dev/null
+++ b/test/138-duplicate-classes-check2/javac_post.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+set -e # Stop on error - the caller script may not have this set.
+
+# Remove one A.class from classes-ex
+rm -f classes-ex/A.class
diff --git a/test/138-duplicate-classes-check2/javac_wrapper.sh b/test/138-duplicate-classes-check2/javac_wrapper.sh
deleted file mode 100755
index 68c352f..0000000
--- a/test/138-duplicate-classes-check2/javac_wrapper.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-set -e # Stop on error - the caller script may not have this set.
-
-$JAVAC "$@"
-
-# Remove one A.class from classes-ex
-rm -f classes-ex/A.class
diff --git a/test/139-register-natives/check b/test/139-register-natives/check
deleted file mode 100755
index f9b35f6..0000000
--- a/test/139-register-natives/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Strip any JNI registration error messages
-sed -e '/jni_entrypoints/d' -e '/jni_internal.cc/d' "$4" > "$4.tmp"
-
-diff --strip-trailing-cr -q "$1" "$2" >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4.tmp" >/dev/null
diff --git a/test/139-register-natives/run.py b/test/139-register-natives/run.py
new file mode 100644
index 0000000..a4b0436
--- /dev/null
+++ b/test/139-register-natives/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # Strip any JNI registration error messages
+ ctx.run(
+ fr"sed -i -E '/(jni_entrypoints|jni_internal.cc)/d' '{args.stderr_file}'")
diff --git a/test/141-class-unload/run b/test/141-class-unload/run
deleted file mode 100644
index 73a984e..0000000
--- a/test/141-class-unload/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2020 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.
-
-# Currently app images aren't unloaded when dex files are unloaded.
-exec ${RUN} $@ --no-secondary-app-image
diff --git a/test/141-class-unload/run.py b/test/141-class-unload/run.py
new file mode 100644
index 0000000..c3fda2f
--- /dev/null
+++ b/test/141-class-unload/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # Currently app images aren't unloaded when dex files are unloaded.
+ ctx.default_run(args, secondary_app_image=False)
diff --git a/test/141-class-unload/src/Main.java b/test/141-class-unload/src/Main.java
index 3cfe006..8031dba 100644
--- a/test/141-class-unload/src/Main.java
+++ b/test/141-class-unload/src/Main.java
@@ -147,7 +147,9 @@
public WeakReference<ClassLoader> classLoader;
}
- private static Pair testNoUnloadInstanceHelper(Constructor<?> constructor) throws Exception {
+ // Make the method not inline-able to prevent the compiler optimizing away the allocation.
+ private static Pair $noinline$testNoUnloadInstanceHelper(Constructor<?> constructor)
+ throws Exception {
ClassLoader loader = (ClassLoader) constructor.newInstance(
DEX_FILE, LIBRARY_SEARCH_PATH, ClassLoader.getSystemClassLoader());
Object o = testNoUnloadHelper(loader);
@@ -155,9 +157,8 @@
}
private static void testNoUnloadInstance(Constructor<?> constructor) throws Exception {
- Pair p = testNoUnloadInstanceHelper(constructor);
+ Pair p = $noinline$testNoUnloadInstanceHelper(constructor);
doUnloading();
- // If the class loader was unloded too early due to races, just pass the test.
boolean isNull = p.classLoader.get() == null;
System.out.println("loader null " + isNull);
}
diff --git a/test/143-string-value/check b/test/143-string-value/check
deleted file mode 100755
index 0691e88..0000000
--- a/test/143-string-value/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Strip error log messages.
-sed -e '/^.*dalvikvm\(\|32\|64\) E.*\] /d' "$4" > "$4.tmp"
-
-diff --strip-trailing-cr -q "$1" "$2" >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4.tmp" >/dev/null
diff --git a/test/143-string-value/run.py b/test/143-string-value/run.py
new file mode 100644
index 0000000..5f32b06
--- /dev/null
+++ b/test/143-string-value/run.py
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # Strip error log messages.
+ ctx.run(fr"sed -i '/^.*dalvikvm\(\|32\|64\) E.*\] /d' '{args.stderr_file}'")
diff --git a/test/143-string-value/test-metadata.json b/test/143-string-value/test-metadata.json
new file mode 100644
index 0000000..2d1dd45
--- /dev/null
+++ b/test/143-string-value/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "run-param": {
+ "default-run": true
+ }
+}
diff --git a/test/144-static-field-sigquit/Android.bp b/test/144-static-field-sigquit/Android.bp
index 0c21c07..1febd32 100644
--- a/test/144-static-field-sigquit/Android.bp
+++ b/test/144-static-field-sigquit/Android.bp
@@ -15,7 +15,7 @@
java_test {
name: "art-run-test-144-static-field-sigquit",
defaults: ["art-run-test-defaults"],
- test_config_template: ":art-run-test-target-template",
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
srcs: ["src/**/*.java"],
data: [
":art-run-test-144-static-field-sigquit-expected-stdout",
diff --git a/test/144-static-field-sigquit/run.py b/test/144-static-field-sigquit/run.py
new file mode 100644
index 0000000..c4268ce
--- /dev/null
+++ b/test/144-static-field-sigquit/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, android_log_tags="*:w")
diff --git a/test/146-bad-interface/check b/test/146-bad-interface/check
deleted file mode 100644
index eadb559..0000000
--- a/test/146-bad-interface/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2020 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Oat file manager will complain about duplicate dex files. Ignore.
-sed -e '/.*oat_file_manager.*/d' "$4" > "$4.tmp"
-
-diff --strip-trailing-cr -q "$1" "$2" >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4.tmp" >/dev/null
diff --git a/test/146-bad-interface/run b/test/146-bad-interface/run
deleted file mode 100755
index 2b4bbb0..0000000
--- a/test/146-bad-interface/run
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 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.
-
-# Use the `--secondary-class-loader-context` switch to compile the secondary dex
-# file with the right class loader context. Do not use `--secondary` as we're
-# loading the *-ex.jar file in a separate class loader.
-exec ${RUN} "${@}" \
- --secondary-class-loader-context "PCL[$DEX_LOCATION/$TEST_NAME.jar]"
diff --git a/test/146-bad-interface/run.py b/test/146-bad-interface/run.py
new file mode 100644
index 0000000..d47bde2
--- /dev/null
+++ b/test/146-bad-interface/run.py
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # Use the `--secondary-class-loader-context` switch to compile the secondary dex
+ # file with the right class loader context. Do not use `--secondary` as we're
+ # loading the *-ex.jar file in a separate class loader.
+ pcl = f"PCL[{ctx.env.DEX_LOCATION}/{ctx.env.TEST_NAME}.jar]"
+ ctx.default_run(args, secondary_class_loader_context=pcl)
+
+ # Oat file manager will complain about duplicate dex files. Ignore.
+ ctx.run(fr"sed -i '/.*oat_file_manager.*/d' '{args.stderr_file}'")
diff --git a/test/148-multithread-gc-annotations/check b/test/148-multithread-gc-annotations/check
deleted file mode 100755
index 67fcafb..0000000
--- a/test/148-multithread-gc-annotations/check
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Check that the string "error" isn't present
-grep -vq error "$2" \
- && diff --strip-trailing-cr -q "$3" "$4" >/dev/null
diff --git a/test/148-multithread-gc-annotations/run.py b/test/148-multithread-gc-annotations/run.py
new file mode 100644
index 0000000..9216c06
--- /dev/null
+++ b/test/148-multithread-gc-annotations/run.py
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # Check that the string "error" isn't present (delete all non-error lines)
+ ctx.run(fr"sed -i '/error/!d' '{args.stdout_file}'")
diff --git a/test/149-suspend-all-stress/check b/test/149-suspend-all-stress/check
deleted file mode 100755
index 0386d58..0000000
--- a/test/149-suspend-all-stress/check
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Only compare the last line.
-tail -n 1 "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4" >/dev/null
-
diff --git a/test/149-suspend-all-stress/run.py b/test/149-suspend-all-stress/run.py
new file mode 100644
index 0000000..f6f670e
--- /dev/null
+++ b/test/149-suspend-all-stress/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # Only compare the last line.
+ ctx.run(fr"tail -n 1 '{args.stdout_file}' > temp-stdout.txt")
+ ctx.run(fr"mv temp-stdout.txt '{args.stdout_file}'")
diff --git a/test/151-OpenFileLimit/Android.bp b/test/151-OpenFileLimit/Android.bp
index 946be83..6b2a8c9 100644
--- a/test/151-OpenFileLimit/Android.bp
+++ b/test/151-OpenFileLimit/Android.bp
@@ -15,7 +15,7 @@
java_test {
name: "art-run-test-151-OpenFileLimit",
defaults: ["art-run-test-defaults"],
- test_config_template: ":art-run-test-target-template",
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
srcs: ["src/**/*.java"],
data: [
":art-run-test-151-OpenFileLimit-expected-stdout",
diff --git a/test/151-OpenFileLimit/expected-stdout.txt b/test/151-OpenFileLimit/expected-stdout.txt
index 6bc45ef..18903be 100644
--- a/test/151-OpenFileLimit/expected-stdout.txt
+++ b/test/151-OpenFileLimit/expected-stdout.txt
@@ -1,3 +1,4 @@
+JNI_OnLoad called
Message includes "Too many open files"
thread run.
done.
diff --git a/test/151-OpenFileLimit/run b/test/151-OpenFileLimit/run
deleted file mode 100755
index 6faeb0d..0000000
--- a/test/151-OpenFileLimit/run
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Filter out expected error messages, which happen on device.
-export ANDROID_LOG_TAGS='*:f'
-
-flags="$@"
-
-# Reduce the file descriptor limit so the test will reach the limit sooner.
-ulimit -n 512
-${RUN} --external-log-tags ${flags}
diff --git a/test/151-OpenFileLimit/run.py b/test/151-OpenFileLimit/run.py
new file mode 100644
index 0000000..65d34ed
--- /dev/null
+++ b/test/151-OpenFileLimit/run.py
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# 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.
+
+import resource
+
+
+def run(ctx, args):
+ ctx.default_run(args, android_log_tags="*:f")
diff --git a/test/151-OpenFileLimit/src/Main.java b/test/151-OpenFileLimit/src/Main.java
index 9b16090..561c17a 100644
--- a/test/151-OpenFileLimit/src/Main.java
+++ b/test/151-OpenFileLimit/src/Main.java
@@ -24,6 +24,9 @@
private static final String TEMP_FILE_NAME_SUFFIX = ".txt";
public static void main(String[] args) throws IOException {
+ System.loadLibrary(args[0]);
+
+ setRlimitNoFile(512);
// Exhaust the number of open file descriptors.
List<File> files = new ArrayList<File>();
@@ -79,4 +82,6 @@
}
}
}
+
+ public static native void setRlimitNoFile(int value);
}
diff --git a/test/157-void-class/run b/test/157-void-class/run
deleted file mode 100755
index 8c6159f..0000000
--- a/test/157-void-class/run
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Let the test build its own core image with --no-image and use verify,
-# so that the compiler does not try to initialize classes. This leaves the
-# java.lang.Void compile-time verified but uninitialized.
-./default-run "$@" --no-image \
- --runtime-option -Ximage-compiler-option \
- --runtime-option --compiler-filter=verify
diff --git a/test/157-void-class/run.py b/test/157-void-class/run.py
new file mode 100644
index 0000000..c0cc3e9
--- /dev/null
+++ b/test/157-void-class/run.py
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Let the test build its own core image with --no-image and use verify,
+ # so that the compiler does not try to initialize classes. This leaves the
+ # java.lang.Void compile-time verified but uninitialized.
+ ctx.default_run(
+ args,
+ image=False,
+ runtime_option=["-Ximage-compiler-option", "--compiler-filter=verify"])
diff --git a/test/158-app-image-class-table/run b/test/158-app-image-class-table/run
deleted file mode 100644
index 146e180..0000000
--- a/test/158-app-image-class-table/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile
diff --git a/test/158-app-image-class-table/run.py b/test/158-app-image-class-table/run.py
new file mode 100644
index 0000000..7678899
--- /dev/null
+++ b/test/158-app-image-class-table/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args, profile=True, Xcompiler_option=["--compiler-filter=speed-profile"])
diff --git a/test/159-app-image-fields/run b/test/159-app-image-fields/run
deleted file mode 100644
index 74facb0..0000000
--- a/test/159-app-image-fields/run
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Use a profile to put specific classes in the app image.
-# Also run the compiler with -j1 to ensure specific class verification order.
-# And limit the managed heap to 16 MiB to speed up GCs.
-exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \
- -Xcompiler-option -j1 --runtime-option -Xmx16m
diff --git a/test/159-app-image-fields/run.py b/test/159-app-image-fields/run.py
new file mode 100644
index 0000000..ae99f54
--- /dev/null
+++ b/test/159-app-image-fields/run.py
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Use a profile to put specific classes in the app image.
+ # Also run the compiler with -j1 to ensure specific class verification order.
+ # And limit the managed heap to 16 MiB to speed up GCs.
+ ctx.default_run(
+ args,
+ profile=True,
+ Xcompiler_option=["--compiler-filter=speed-profile", "-j1"],
+ runtime_option=["-Xmx16m"])
diff --git a/test/159-app-image-fields/src/Main.java b/test/159-app-image-fields/src/Main.java
index e7db1e1..d238f77 100644
--- a/test/159-app-image-fields/src/Main.java
+++ b/test/159-app-image-fields/src/Main.java
@@ -115,6 +115,7 @@
// and the verification of Main is last and fills the DexCache with Derived.value.
//
class Fields {
+ // clang-format off
public static int clobberDexCache() {
return 0
+ testField0000
diff --git a/test/160-read-barrier-stress/build b/test/160-read-barrier-stress/build
deleted file mode 100755
index 90b6b95..0000000
--- a/test/160-read-barrier-stress/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2020 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-build "$@" --experimental var-handles
diff --git a/test/160-read-barrier-stress/build.py b/test/160-read-barrier-stress/build.py
new file mode 100644
index 0000000..b65e088
--- /dev/null
+++ b/test/160-read-barrier-stress/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build(api_level="var-handles")
diff --git a/test/160-read-barrier-stress/run b/test/160-read-barrier-stress/run
deleted file mode 100644
index ab82229..0000000
--- a/test/160-read-barrier-stress/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Limit the Java heap to 16MiB to force more GCs.
-exec ${RUN} $@ --runtime-option -Xmx16m
diff --git a/test/160-read-barrier-stress/run.py b/test/160-read-barrier-stress/run.py
new file mode 100644
index 0000000..fb006ea
--- /dev/null
+++ b/test/160-read-barrier-stress/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Limit the Java heap to 16MiB to force more GCs.
+ ctx.default_run(args, runtime_option=["-Xmx16m"])
diff --git a/test/160-read-barrier-stress/test-metadata.json b/test/160-read-barrier-stress/test-metadata.json
new file mode 100644
index 0000000..b7e262c
--- /dev/null
+++ b/test/160-read-barrier-stress/test-metadata.json
@@ -0,0 +1,6 @@
+{
+ "build-param": {
+ "experimental": "var-handles",
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/161-final-abstract-class/build.py b/test/161-final-abstract-class/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/161-final-abstract-class/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/161-final-abstract-class/build b/test/161-final-abstract-class/generate-sources
old mode 100644
new mode 100755
similarity index 100%
rename from test/161-final-abstract-class/build
rename to test/161-final-abstract-class/generate-sources
diff --git a/test/163-app-image-methods/run b/test/163-app-image-methods/run
deleted file mode 100644
index 74facb0..0000000
--- a/test/163-app-image-methods/run
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Use a profile to put specific classes in the app image.
-# Also run the compiler with -j1 to ensure specific class verification order.
-# And limit the managed heap to 16 MiB to speed up GCs.
-exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \
- -Xcompiler-option -j1 --runtime-option -Xmx16m
diff --git a/test/163-app-image-methods/run.py b/test/163-app-image-methods/run.py
new file mode 100644
index 0000000..ae99f54
--- /dev/null
+++ b/test/163-app-image-methods/run.py
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Use a profile to put specific classes in the app image.
+ # Also run the compiler with -j1 to ensure specific class verification order.
+ # And limit the managed heap to 16 MiB to speed up GCs.
+ ctx.default_run(
+ args,
+ profile=True,
+ Xcompiler_option=["--compiler-filter=speed-profile", "-j1"],
+ runtime_option=["-Xmx16m"])
diff --git a/test/164-resolution-trampoline-dex-cache/run b/test/164-resolution-trampoline-dex-cache/run
deleted file mode 100644
index 33a6f01..0000000
--- a/test/164-resolution-trampoline-dex-cache/run
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Make sure we compile the required method using speed-profile compiler filter.
-# Enable JIT through runtime option to avoid the compiler filter set by --jit.
-exec ${RUN} "${@}" \
- -Xcompiler-option --compiler-filter=speed-profile --profile \
- --runtime-option -Xusejit:true
diff --git a/test/164-resolution-trampoline-dex-cache/run.py b/test/164-resolution-trampoline-dex-cache/run.py
new file mode 100644
index 0000000..6e7b777
--- /dev/null
+++ b/test/164-resolution-trampoline-dex-cache/run.py
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Make sure we compile the required method using speed-profile compiler filter.
+ # Enable JIT through runtime option to avoid the compiler filter set by --jit.
+ ctx.default_run(
+ args,
+ Xcompiler_option=["--compiler-filter=speed-profile"],
+ profile=True,
+ runtime_option=["-Xusejit:true"])
diff --git a/test/165-lock-owner-proxy/run b/test/165-lock-owner-proxy/run
deleted file mode 100644
index 9365411..0000000
--- a/test/165-lock-owner-proxy/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Use a smaller heap so it's easier to potentially fill up.
-exec ${RUN} $@ --runtime-option -Xmx2m
diff --git a/test/165-lock-owner-proxy/run.py b/test/165-lock-owner-proxy/run.py
new file mode 100644
index 0000000..266f747
--- /dev/null
+++ b/test/165-lock-owner-proxy/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Use a smaller heap so it's easier to potentially fill up.
+ ctx.default_run(args, runtime_option=["-Xmx2m"])
diff --git a/test/165-lock-owner-proxy/src/Main.java b/test/165-lock-owner-proxy/src/Main.java
index fff8e58..99a6d42 100644
--- a/test/165-lock-owner-proxy/src/Main.java
+++ b/test/165-lock-owner-proxy/src/Main.java
@@ -18,6 +18,11 @@
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
+// Note that this is run with a tiny heap.
+// See b/260228356 for some discussion. It's unclear if and how this reliably forces an
+// OOME. For now, we're keeping this around because it appears to have detected some bugs
+// in the past. It may need revisiting.
+
public class Main {
static final int numberOfThreads = 5;
static final int totalOperations = 10000;
@@ -27,9 +32,16 @@
static volatile boolean finish = false;
public static void main(String[] args) throws Exception {
+ // Wait for system daemons to start, so that we minimize the chances of
+ // a system daemon still starting up if/when we run out of memory.
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ System.out.println("Unexpected interrupt:" + e);
+ }
+
inf = (SimpleInterface)Proxy.newProxyInstance(SimpleInterface.class.getClassLoader(),
new Class[] { SimpleInterface.class }, new EmptyInvocationHandler());
-
Thread garbageThread = new Thread(new GarbageRunner());
garbageThread.start();
@@ -111,6 +123,7 @@
try {
Thread.sleep(10);
} catch (Exception e) {
+ System.out.println("Unexpected exception in sleep():" + e);
}
}
}
diff --git a/test/166-bad-interface-super/build b/test/166-bad-interface-super/build
deleted file mode 100644
index bba6184..0000000
--- a/test/166-bad-interface-super/build
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Use the jasmin sources for JVM, otherwise the smali sources.
-extra_arg="--no-jasmin"
-
-for arg in "$@"; do
- if [[ "$arg" == "--jvm" ]]; then
- extra_arg="--no-smali"
- break
- fi
-done
-
-./default-build "$@" "$extra_arg"
diff --git a/test/166-bad-interface-super/build.py b/test/166-bad-interface-super/build.py
new file mode 100644
index 0000000..b2ff2d7
--- /dev/null
+++ b/test/166-bad-interface-super/build.py
@@ -0,0 +1,24 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+
+def build(ctx):
+ # Use the jasmin sources for JVM, otherwise the smali sources.
+ if ctx.jvm:
+ ctx.default_build(use_smali=False)
+ else:
+ ctx.default_build(use_jasmin=False)
diff --git a/test/167-visit-locks/run b/test/167-visit-locks/run
deleted file mode 100644
index 9365411..0000000
--- a/test/167-visit-locks/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Use a smaller heap so it's easier to potentially fill up.
-exec ${RUN} $@ --runtime-option -Xmx2m
diff --git a/test/167-visit-locks/run.py b/test/167-visit-locks/run.py
new file mode 100644
index 0000000..266f747
--- /dev/null
+++ b/test/167-visit-locks/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Use a smaller heap so it's easier to potentially fill up.
+ ctx.default_run(args, runtime_option=["-Xmx2m"])
diff --git a/test/168-vmstack-annotated/run b/test/168-vmstack-annotated/run
deleted file mode 100644
index 9365411..0000000
--- a/test/168-vmstack-annotated/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Use a smaller heap so it's easier to potentially fill up.
-exec ${RUN} $@ --runtime-option -Xmx2m
diff --git a/test/168-vmstack-annotated/run.py b/test/168-vmstack-annotated/run.py
new file mode 100644
index 0000000..266f747
--- /dev/null
+++ b/test/168-vmstack-annotated/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Use a smaller heap so it's easier to potentially fill up.
+ ctx.default_run(args, runtime_option=["-Xmx2m"])
diff --git a/test/168-vmstack-annotated/src/Main.java b/test/168-vmstack-annotated/src/Main.java
index 8234f94..5056c42 100644
--- a/test/168-vmstack-annotated/src/Main.java
+++ b/test/168-vmstack-annotated/src/Main.java
@@ -22,6 +22,7 @@
import java.util.Map;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ForkJoinPool;
public class Main {
@@ -89,6 +90,9 @@
}
public static void main(String[] args) throws Exception {
+ // Eagerly initialize ForkJoinPool as we've seen the class initialization code interfere
+ // with the logic of the test when waiting for threads to be non-runnable.
+ Class.forName(ForkJoinPool.class.getName(), true, ForkJoinPool.class.getClassLoader());
try {
testCluster1();
} catch (Exception e) {
diff --git a/test/172-app-image-twice/check b/test/172-app-image-twice/check
deleted file mode 100755
index 228bcb5..0000000
--- a/test/172-app-image-twice/check
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Remove all lines not containing "passed".
-grep "^passed" "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4" >/dev/null
diff --git a/test/172-app-image-twice/run b/test/172-app-image-twice/run
deleted file mode 100644
index 2987b4b..0000000
--- a/test/172-app-image-twice/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-#
-# 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 an app image with TestClass (specified by profile).
-
-${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile
diff --git a/test/172-app-image-twice/run.py b/test/172-app-image-twice/run.py
new file mode 100644
index 0000000..6b53067
--- /dev/null
+++ b/test/172-app-image-twice/run.py
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Build an app image with TestClass (specified by profile).
+
+ ctx.default_run(
+ args, profile=True, Xcompiler_option=["--compiler-filter=speed-profile"])
+
+ # Remove all lines not containing "passed".
+ ctx.run(fr"sed -i '/^passed/!d' '{args.stdout_file}'")
diff --git a/test/173-missing-field-type/smali/BadField.smali b/test/173-missing-field-type/smali/BadField.smali
index 7593983..1734034 100644
--- a/test/173-missing-field-type/smali/BadField.smali
+++ b/test/173-missing-field-type/smali/BadField.smali
@@ -41,6 +41,18 @@
return-void
.end method
+.method public static storeStatic(Ljava/lang/Object;)V
+ .registers 1
+ sput-object p0, LBadField;->widget:LWidget;
+ return-void
+.end method
+
+.method public static storeInstance(LBadField;Ljava/lang/Object;)V
+ .registers 2
+ iput-object p1, p0, LBadField;->iwidget:LWidget;
+ return-void
+.end method
+
.method public static storeInstanceObject()V
.registers 2
new-instance v1, LBadField;
diff --git a/test/173-missing-field-type/src-art/Main.java b/test/173-missing-field-type/src-art/Main.java
index 35dbbea..8c5a741 100644
--- a/test/173-missing-field-type/src-art/Main.java
+++ b/test/173-missing-field-type/src-art/Main.java
@@ -15,6 +15,7 @@
*/
import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws Throwable {
@@ -24,10 +25,16 @@
// Storing null is OK.
c.getMethod("storeStaticNull").invoke(null);
c.getMethod("storeInstanceNull").invoke(null);
+ c.getMethod("storeStatic", Object.class).invoke(null, new Object[]{ null });
+ c.getMethod("storeInstance", c, Object.class).invoke(
+ null, new Object[]{ c.newInstance(), null });
// Storing anything else should throw an exception.
- testStoreObject(c, "storeStaticObject");
- testStoreObject(c, "storeInstanceObject");
+ testStoreObject(c.getMethod("storeStaticObject"));
+ testStoreObject(c.getMethod("storeInstanceObject"));
+ testStoreObject(c.getMethod("storeStatic", Object.class), new Object());
+ testStoreObject(
+ c.getMethod("storeInstance", c, Object.class), c.newInstance(), new Object());
// Loading is OK.
c = Class.forName("BadFieldGet");
@@ -38,9 +45,9 @@
c.getMethod(methodName).invoke(null);
}
- public static void testStoreObject(Class<?> c, String methodName) throws Throwable {
+ public static void testStoreObject(Method method, Object... arguments) throws Throwable {
try {
- c.getMethod(methodName).invoke(null);
+ method.invoke(null, arguments);
throw new Error("Expected NoClassDefFoundError");
} catch (InvocationTargetException expected) {
Throwable e = expected.getCause();
diff --git a/test/176-app-image-string/run b/test/176-app-image-string/run
deleted file mode 100644
index 52d2b5f..0000000
--- a/test/176-app-image-string/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-exec ${RUN} $@ --profile
diff --git a/test/176-app-image-string/run.py b/test/176-app-image-string/run.py
new file mode 100644
index 0000000..0f3a0ea
--- /dev/null
+++ b/test/176-app-image-string/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, profile=True)
diff --git a/test/178-app-image-native-method/build.py b/test/178-app-image-native-method/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/178-app-image-native-method/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/178-app-image-native-method/check b/test/178-app-image-native-method/check
deleted file mode 100755
index 45d3654..0000000
--- a/test/178-app-image-native-method/check
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Filter out error messages for missing native methods.
-diff --strip-trailing-cr -q "$1" "$2" >/dev/null \
- && grep -v 'No implementation found for ' "$4" | diff -q "$3" - >/dev/null
diff --git a/test/178-app-image-native-method/run b/test/178-app-image-native-method/run
deleted file mode 100644
index 7cd0d57..0000000
--- a/test/178-app-image-native-method/run
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Use a profile to put specific classes in the app image. Increase the large
-# method threshold to compile Main.$noinline$opt$testCriticalSignatures().
-${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \
- -Xcompiler-option --large-method-max=2000
-return_status1=$?
-
-# Also run with the verify filter to avoid compiling JNI stubs.
-${RUN} ${@} --profile -Xcompiler-option --compiler-filter=verify
-return_status2=$?
-
-(exit ${return_status1}) && (exit ${return_status2})
diff --git a/test/178-app-image-native-method/run.py b/test/178-app-image-native-method/run.py
new file mode 100644
index 0000000..5f8c507
--- /dev/null
+++ b/test/178-app-image-native-method/run.py
@@ -0,0 +1,33 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Use a profile to put specific classes in the app image. Increase the large
+ # method threshold to compile Main.$noinline$opt$testCriticalSignatures().
+ ctx.default_run(
+ args,
+ profile=True,
+ Xcompiler_option=[
+ "--compiler-filter=speed-profile", "--large-method-max=2000"
+ ])
+
+ # Also run with the verify filter to avoid compiling JNI stubs.
+ ctx.default_run(
+ args, profile=True, Xcompiler_option=["--compiler-filter=verify"])
+
+ # Filter out error messages for missing native methods.
+ ctx.run(fr"sed -i '/No implementation found for/d' '{args.stderr_file}'")
diff --git a/test/178-app-image-native-method/src/Main.java b/test/178-app-image-native-method/src/Main.java
index 294ad47..877efa4 100644
--- a/test/178-app-image-native-method/src/Main.java
+++ b/test/178-app-image-native-method/src/Main.java
@@ -17,6 +17,8 @@
import dalvik.annotation.optimization.FastNative;
import dalvik.annotation.optimization.CriticalNative;
+// clang-format off
+
public class Main {
public static void main(String[] args) throws Exception {
diff --git a/test/178-app-image-native-method/test-metadata.json b/test/178-app-image-native-method/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/178-app-image-native-method/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/180-native-default-method/build b/test/180-native-default-method/build
deleted file mode 100644
index b6b604f..0000000
--- a/test/180-native-default-method/build
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2020 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.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@"
-
-if [[ $@ != *"--jvm"* ]]; then
- # Change the generated dex file to have a v35 magic number if it is version 38
- if test -f classes.dex && head -c 7 classes.dex | grep -q 038; then
- # place ascii value '035' into the classes.dex file starting at byte 4.
- printf '035' | dd status=none conv=notrunc of=classes.dex bs=1 seek=4 count=3
- rm -f $TEST_NAME.jar
- ${SOONG_ZIP} -o $TEST_NAME.jar -f classes.dex
- else
- echo Unexpected dex verison
- exit 1
- fi
-fi
diff --git a/test/180-native-default-method/build.py b/test/180-native-default-method/build.py
new file mode 100644
index 0000000..62aac82
--- /dev/null
+++ b/test/180-native-default-method/build.py
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+def build(ctx):
+ ctx.default_build()
+ if ctx.jvm:
+ return
+ # Change the generated dex file to have a v35 magic number if it is version 38
+ with open(ctx.test_dir / "classes.dex", "rb+") as f:
+ assert f.read(8) == b"dex\n038\x00"
+ f.seek(0)
+ f.write(b"dex\n035\x00")
+ (ctx.test_dir / "180-native-default-method.jar").unlink()
+ ctx.soong_zip([
+ "-o", ctx.test_dir / "180-native-default-method.jar", "-j",
+ "-f", ctx.test_dir / "classes.dex"
+ ])
diff --git a/test/181-default-methods/build b/test/181-default-methods/build
deleted file mode 100644
index 9cd5738..0000000
--- a/test/181-default-methods/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2022 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.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental default-methods
diff --git a/test/181-default-methods/build.py b/test/181-default-methods/build.py
new file mode 100644
index 0000000..3e0ecd5
--- /dev/null
+++ b/test/181-default-methods/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(api_level="default-methods")
diff --git a/test/182-method-linking/Android.bp b/test/182-method-linking/Android.bp
new file mode 100644
index 0000000..cee24a9
--- /dev/null
+++ b/test/182-method-linking/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `182-method-linking`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-182-method-linking-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-182-method-linking",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-182-method-linking-src"
+ ],
+ data: [
+ ":art-run-test-182-method-linking-expected-stdout",
+ ":art-run-test-182-method-linking-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-182-method-linking-expected-stdout",
+ out: ["art-run-test-182-method-linking-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-182-method-linking-expected-stderr",
+ out: ["art-run-test-182-method-linking-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/182-method-linking/src/Main.java b/test/182-method-linking/src/Main.java
index 3902956..638ff65 100644
--- a/test/182-method-linking/src/Main.java
+++ b/test/182-method-linking/src/Main.java
@@ -33,12 +33,6 @@
public class Main {
public static void main(String args[]) {
- try {
- Class.forName("dalvik.system.PathClassLoader");
- } catch (ClassNotFoundException e) {
- usingRI = true;
- }
-
// A single method signature can result in multiple vtable entries
// when package-private methods from different packages are involved.
// All classes here define the method `void foo()` but classes
@@ -120,7 +114,6 @@
CXI1 cxi1 = new CXI1();
I1.callI1Foo(cxi1);
} catch (IllegalAccessError expected) {
- printOnDalvik("Calling pkg1.I1.foo on pkg1.CXI1");
System.out.println("Caught IllegalAccessError");
}
@@ -128,7 +121,6 @@
CXI2 cxi2 = new CXI2();
I2.callI2Foo(cxi2);
} catch (IllegalAccessError expected) {
- printOnDalvik("Calling pkg2.I2.foo on pkg1.CXI2");
System.out.println("Caught IllegalAccessError");
}
@@ -136,7 +128,6 @@
DXI1 dxi1 = new DXI1();
I1.callI1Foo(dxi1);
} catch (IllegalAccessError expected) {
- printOnDalvik("Calling pkg1.I1.foo on pkg2.DXI1");
System.out.println("Caught IllegalAccessError");
}
@@ -144,17 +135,7 @@
DXI2 dxi2 = new DXI2();
I2.callI2Foo(dxi2);
} catch (IllegalAccessError expected) {
- printOnDalvik("Calling pkg2.I2.foo on pkg2.DXI2");
System.out.println("Caught IllegalAccessError");
}
}
-
- private static void printOnDalvik(String line) {
- if (!usingRI) {
- // FIXME: Delay IAE until calling the method. Bug: 211854716
- System.out.println(line);
- }
- }
-
- private static boolean usingRI = false;
}
diff --git a/test/1900-track-alloc/run b/test/1900-track-alloc/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1900-track-alloc/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1900-track-alloc/run.py b/test/1900-track-alloc/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1900-track-alloc/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1901-get-bytecodes/run b/test/1901-get-bytecodes/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1901-get-bytecodes/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/1901-get-bytecodes/run.py b/test/1901-get-bytecodes/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1901-get-bytecodes/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1902-suspend/run b/test/1902-suspend/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1902-suspend/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/1902-suspend/run.py b/test/1902-suspend/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1902-suspend/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1903-suspend-self/run b/test/1903-suspend-self/run
deleted file mode 100755
index 97f077b..0000000
--- a/test/1903-suspend-self/run
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
-
-return_status1=$?
-
-#Also do one run with --no-image to have one test coverage with no-image.
-./default-run "$@" --jvmti --no-image
-
-return_status2=$?
-
-# Make sure we don't silently ignore an early failure.
-(exit $return_status1) && (exit $return_status2)
diff --git a/test/1903-suspend-self/run.py b/test/1903-suspend-self/run.py
new file mode 100644
index 0000000..6f8d830
--- /dev/null
+++ b/test/1903-suspend-self/run.py
@@ -0,0 +1,22 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
+
+ #Also do one run with --no-image to have one test coverage with no-image.
+ ctx.default_run(args, jvmti=True, image=False)
diff --git a/test/1904-double-suspend/run b/test/1904-double-suspend/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1904-double-suspend/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/1904-double-suspend/run.py b/test/1904-double-suspend/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1904-double-suspend/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1905-suspend-native/run b/test/1905-suspend-native/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1905-suspend-native/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/1905-suspend-native/run.py b/test/1905-suspend-native/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1905-suspend-native/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1906-suspend-list-me-first/run b/test/1906-suspend-list-me-first/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1906-suspend-list-me-first/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/1906-suspend-list-me-first/run.py b/test/1906-suspend-list-me-first/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1906-suspend-list-me-first/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1907-suspend-list-self-twice/run b/test/1907-suspend-list-self-twice/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1907-suspend-list-self-twice/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/1907-suspend-list-self-twice/run.py b/test/1907-suspend-list-self-twice/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1907-suspend-list-self-twice/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1908-suspend-native-resume-self/run b/test/1908-suspend-native-resume-self/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1908-suspend-native-resume-self/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/1908-suspend-native-resume-self/run.py b/test/1908-suspend-native-resume-self/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1908-suspend-native-resume-self/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1909-per-agent-tls/run b/test/1909-per-agent-tls/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1909-per-agent-tls/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1909-per-agent-tls/run.py b/test/1909-per-agent-tls/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1909-per-agent-tls/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1910-transform-with-default/run b/test/1910-transform-with-default/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1910-transform-with-default/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1910-transform-with-default/run.py b/test/1910-transform-with-default/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1910-transform-with-default/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1911-get-local-var-table/run b/test/1911-get-local-var-table/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1911-get-local-var-table/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/1911-get-local-var-table/run.py b/test/1911-get-local-var-table/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1911-get-local-var-table/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1912-get-set-local-primitive/run b/test/1912-get-set-local-primitive/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1912-get-set-local-primitive/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/1912-get-set-local-primitive/run.py b/test/1912-get-set-local-primitive/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1912-get-set-local-primitive/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1913-get-set-local-objects/run b/test/1913-get-set-local-objects/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1913-get-set-local-objects/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/1913-get-set-local-objects/run.py b/test/1913-get-set-local-objects/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1913-get-set-local-objects/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1914-get-local-instance/run b/test/1914-get-local-instance/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1914-get-local-instance/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/1914-get-local-instance/run.py b/test/1914-get-local-instance/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1914-get-local-instance/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1915-get-set-local-current-thread/run b/test/1915-get-set-local-current-thread/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1915-get-set-local-current-thread/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/1915-get-set-local-current-thread/run.py b/test/1915-get-set-local-current-thread/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1915-get-set-local-current-thread/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1915-get-set-local-current-thread/src/Main.java b/test/1915-get-set-local-current-thread/src/Main.java
index 47e6767..5ca6f09 100644
--- a/test/1915-get-set-local-current-thread/src/Main.java
+++ b/test/1915-get-set-local-current-thread/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test1915.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test1915.run();
+ }
}
diff --git a/test/1915-get-set-local-current-thread/src/art/Test1915.java b/test/1915-get-set-local-current-thread/src/art/Test1915.java
index a99a487..8398afa 100644
--- a/test/1915-get-set-local-current-thread/src/art/Test1915.java
+++ b/test/1915-get-set-local-current-thread/src/art/Test1915.java
@@ -31,75 +31,75 @@
import java.util.function.Consumer;
public class Test1915 {
- public static final int SET_VALUE = 1337;
- public static final String TARGET_VAR = "TARGET";
+ public static final int SET_VALUE = 1337;
+ public static final String TARGET_VAR = "TARGET";
- public static void reportValue(Object val) {
- System.out.println("\tValue is '" + val + "'");
- }
- public static interface ThrowRunnable {
- public void run() throws Exception;
- }
-
- public static void IntMethod(ThrowRunnable safepoint) throws Exception {
- int TARGET = 42;
- safepoint.run();
- reportValue(TARGET);
- }
-
- public static void run() throws Exception {
- Locals.EnableLocalVariableAccess();
- final Method target = Test1915.class.getDeclaredMethod("IntMethod", ThrowRunnable.class);
- // Get Variable.
- System.out.println("GetLocalInt on current thread!");
- IntMethod(() -> {
- StackTrace.StackFrameData frame = FindStackFrame(target);
- int depth = FindExpectedFrameDepth(frame);
- int slot = FindSlot(frame);
- int value = Locals.GetLocalVariableInt(Thread.currentThread(), depth, slot);
- System.out.println("From GetLocalInt(), value is " + value);
- });
- // Set Variable.
- System.out.println("SetLocalInt on current thread!");
- IntMethod(() -> {
- StackTrace.StackFrameData frame = FindStackFrame(target);
- int depth = FindExpectedFrameDepth(frame);
- int slot = FindSlot(frame);
- Locals.SetLocalVariableInt(Thread.currentThread(), depth, slot, SET_VALUE);
- });
- }
-
- public static int FindSlot(StackTrace.StackFrameData frame) throws Exception {
- long loc = frame.current_location;
- for (Locals.VariableDescription var : Locals.GetLocalVariableTable(frame.method)) {
- if (var.start_location <= loc &&
- var.length + var.start_location > loc &&
- var.name.equals(TARGET_VAR)) {
- return var.slot;
- }
+ public static void reportValue(Object val) {
+ System.out.println("\tValue is '" + val + "'");
}
- throw new Error(
- "Unable to find variable " + TARGET_VAR + " in " + frame.method + " at loc " + loc);
- }
-
- public static int FindExpectedFrameDepth(StackTrace.StackFrameData frame) throws Exception {
- // Adjust the 'frame' depth since it is modified by:
- // +1 for Get/SetLocalVariableInt in future.
- // -1 for FindStackFrame
- // -1 for GetStackTrace
- // -1 for GetStackTraceNative
- // ------------------------------
- // -2
- return frame.depth - 2;
- }
-
- private static StackTrace.StackFrameData FindStackFrame(Method target) {
- for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(Thread.currentThread())) {
- if (frame.method.equals(target)) {
- return frame;
- }
+ public static interface ThrowRunnable {
+ public void run() throws Exception;
}
- throw new Error("Unable to find stack frame in method " + target);
- }
+
+ public static void IntMethod(ThrowRunnable safepoint) throws Exception {
+ int TARGET = 42;
+ safepoint.run();
+ reportValue(TARGET);
+ }
+
+ public static void run() throws Exception {
+ Locals.EnableLocalVariableAccess();
+ final Method target = Test1915.class.getDeclaredMethod("IntMethod", ThrowRunnable.class);
+ // Get Variable.
+ System.out.println("GetLocalInt on current thread!");
+ IntMethod(() -> {
+ StackTrace.StackFrameData frame = FindStackFrame(target);
+ int depth = FindExpectedFrameDepth(frame);
+ int slot = FindSlot(frame);
+ int value = Locals.GetLocalVariableInt(Thread.currentThread(), depth, slot);
+ System.out.println("From GetLocalInt(), value is " + value);
+ });
+ // Set Variable.
+ System.out.println("SetLocalInt on current thread!");
+ IntMethod(() -> {
+ StackTrace.StackFrameData frame = FindStackFrame(target);
+ int depth = FindExpectedFrameDepth(frame);
+ int slot = FindSlot(frame);
+ Locals.SetLocalVariableInt(Thread.currentThread(), depth, slot, SET_VALUE);
+ });
+ }
+
+ public static int FindSlot(StackTrace.StackFrameData frame) throws Exception {
+ long loc = frame.current_location;
+ for (Locals.VariableDescription var : Locals.GetLocalVariableTable(frame.method)) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(TARGET_VAR)) {
+ return var.slot;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + TARGET_VAR + " in " + frame.method + " at loc " + loc);
+ }
+
+ public static int FindExpectedFrameDepth(StackTrace.StackFrameData frame) throws Exception {
+ // Adjust the 'frame' depth since it is modified by:
+ // +1 for Get/SetLocalVariableInt in future.
+ // -1 for FindStackFrame
+ // -1 for GetStackTrace
+ // -1 for GetStackTraceNative
+ // ------------------------------
+ // -2
+ return frame.depth - 2;
+ }
+
+ private static StackTrace.StackFrameData FindStackFrame(Method target) {
+ for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(Thread.currentThread())) {
+ if (frame.method.equals(target)) {
+ return frame;
+ }
+ }
+ throw new Error("Unable to find stack frame in method " + target);
+ }
}
diff --git a/test/1916-get-set-current-frame/run b/test/1916-get-set-current-frame/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1916-get-set-current-frame/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/1916-get-set-current-frame/run.py b/test/1916-get-set-current-frame/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1916-get-set-current-frame/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1916-get-set-current-frame/src/Main.java b/test/1916-get-set-current-frame/src/Main.java
index 7d0cd21..2ecf0ca 100644
--- a/test/1916-get-set-current-frame/src/Main.java
+++ b/test/1916-get-set-current-frame/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test1916.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test1916.run();
+ }
}
diff --git a/test/1916-get-set-current-frame/src/art/Test1916.java b/test/1916-get-set-current-frame/src/art/Test1916.java
index 3e5bce2..6fe4ba2 100644
--- a/test/1916-get-set-current-frame/src/art/Test1916.java
+++ b/test/1916-get-set-current-frame/src/art/Test1916.java
@@ -31,118 +31,118 @@
import java.util.function.Consumer;
public class Test1916 {
- public static final int SET_VALUE = 1337;
- public static final String TARGET_VAR = "TARGET";
+ public static final int SET_VALUE = 1337;
+ public static final String TARGET_VAR = "TARGET";
- public static void reportValue(Object val) {
- System.out.println("\tValue is '" + val + "'");
- }
+ public static void reportValue(Object val) {
+ System.out.println("\tValue is '" + val + "'");
+ }
- public static class IntRunner implements Runnable {
- private volatile boolean continueBusyLoop;
- private volatile boolean inBusyLoop;
- public IntRunner() {
- this.continueBusyLoop = true;
- this.inBusyLoop = false;
+ public static class IntRunner implements Runnable {
+ private volatile boolean continueBusyLoop;
+ private volatile boolean inBusyLoop;
+ public IntRunner() {
+ this.continueBusyLoop = true;
+ this.inBusyLoop = false;
+ }
+ public void run() {
+ int TARGET = 42;
+ // We will suspend the thread during this loop.
+ while (continueBusyLoop) {
+ inBusyLoop = true;
+ }
+ reportValue(TARGET);
+ }
+ public void waitForBusyLoopStart() { while (!inBusyLoop) {} }
+ public void finish() { continueBusyLoop = false; }
}
- public void run() {
- int TARGET = 42;
- // We will suspend the thread during this loop.
- while (continueBusyLoop) {
- inBusyLoop = true;
- }
- reportValue(TARGET);
- }
- public void waitForBusyLoopStart() { while (!inBusyLoop) {} }
- public void finish() { continueBusyLoop = false; }
- }
- public static void run() throws Exception {
- Locals.EnableLocalVariableAccess();
- runGet();
- runSet();
- }
+ public static void run() throws Exception {
+ Locals.EnableLocalVariableAccess();
+ runGet();
+ runSet();
+ }
- public static void runGet() throws Exception {
- Method target = IntRunner.class.getDeclaredMethod("run");
- // Get Int
- IntRunner int_runner = new IntRunner();
- Thread target_get = new Thread(int_runner, "GetLocalInt - Target");
- target_get.start();
- int_runner.waitForBusyLoopStart();
- try {
- Suspension.suspend(target_get);
- } catch (Exception e) {
- System.out.println("FAIL: got " + e);
- e.printStackTrace();
- int_runner.finish();
- target_get.join();
- return;
+ public static void runGet() throws Exception {
+ Method target = IntRunner.class.getDeclaredMethod("run");
+ // Get Int
+ IntRunner int_runner = new IntRunner();
+ Thread target_get = new Thread(int_runner, "GetLocalInt - Target");
+ target_get.start();
+ int_runner.waitForBusyLoopStart();
+ try {
+ Suspension.suspend(target_get);
+ } catch (Exception e) {
+ System.out.println("FAIL: got " + e);
+ e.printStackTrace();
+ int_runner.finish();
+ target_get.join();
+ return;
+ }
+ try {
+ StackTrace.StackFrameData frame = FindStackFrame(target_get, target);
+ int depth = frame.depth;
+ if (depth != 0) { throw new Error("Expected depth 0 but got " + depth); }
+ int slot = FindSlot(frame);
+ int value = Locals.GetLocalVariableInt(target_get, depth, slot);
+ System.out.println("From GetLocalInt(), value is " + value);
+ } finally {
+ Suspension.resume(target_get);
+ int_runner.finish();
+ target_get.join();
+ }
}
- try {
- StackTrace.StackFrameData frame = FindStackFrame(target_get, target);
- int depth = frame.depth;
- if (depth != 0) { throw new Error("Expected depth 0 but got " + depth); }
- int slot = FindSlot(frame);
- int value = Locals.GetLocalVariableInt(target_get, depth, slot);
- System.out.println("From GetLocalInt(), value is " + value);
- } finally {
- Suspension.resume(target_get);
- int_runner.finish();
- target_get.join();
- }
- }
- public static void runSet() throws Exception {
- Method target = IntRunner.class.getDeclaredMethod("run");
- // Set Int
- IntRunner int_runner = new IntRunner();
- Thread target_set = new Thread(int_runner, "SetLocalInt - Target");
- target_set.start();
- int_runner.waitForBusyLoopStart();
- try {
- Suspension.suspend(target_set);
- } catch (Exception e) {
- System.out.println("FAIL: got " + e);
- e.printStackTrace();
- int_runner.finish();
- target_set.join();
- return;
+ public static void runSet() throws Exception {
+ Method target = IntRunner.class.getDeclaredMethod("run");
+ // Set Int
+ IntRunner int_runner = new IntRunner();
+ Thread target_set = new Thread(int_runner, "SetLocalInt - Target");
+ target_set.start();
+ int_runner.waitForBusyLoopStart();
+ try {
+ Suspension.suspend(target_set);
+ } catch (Exception e) {
+ System.out.println("FAIL: got " + e);
+ e.printStackTrace();
+ int_runner.finish();
+ target_set.join();
+ return;
+ }
+ try {
+ StackTrace.StackFrameData frame = FindStackFrame(target_set, target);
+ int depth = frame.depth;
+ if (depth != 0) { throw new Error("Expected depth 0 but got " + depth); }
+ int slot = FindSlot(frame);
+ System.out.println("Setting TARGET to " + SET_VALUE);
+ Locals.SetLocalVariableInt(target_set, depth, slot, SET_VALUE);
+ } finally {
+ Suspension.resume(target_set);
+ int_runner.finish();
+ target_set.join();
+ }
}
- try {
- StackTrace.StackFrameData frame = FindStackFrame(target_set, target);
- int depth = frame.depth;
- if (depth != 0) { throw new Error("Expected depth 0 but got " + depth); }
- int slot = FindSlot(frame);
- System.out.println("Setting TARGET to " + SET_VALUE);
- Locals.SetLocalVariableInt(target_set, depth, slot, SET_VALUE);
- } finally {
- Suspension.resume(target_set);
- int_runner.finish();
- target_set.join();
- }
- }
- public static int FindSlot(StackTrace.StackFrameData frame) throws Exception {
- long loc = frame.current_location;
- for (Locals.VariableDescription var : Locals.GetLocalVariableTable(frame.method)) {
- if (var.start_location <= loc &&
- var.length + var.start_location > loc &&
- var.name.equals(TARGET_VAR)) {
- return var.slot;
- }
+ public static int FindSlot(StackTrace.StackFrameData frame) throws Exception {
+ long loc = frame.current_location;
+ for (Locals.VariableDescription var : Locals.GetLocalVariableTable(frame.method)) {
+ if (var.start_location <= loc &&
+ var.length + var.start_location > loc &&
+ var.name.equals(TARGET_VAR)) {
+ return var.slot;
+ }
+ }
+ throw new Error(
+ "Unable to find variable " + TARGET_VAR + " in " + frame.method + " at loc " + loc);
}
- throw new Error(
- "Unable to find variable " + TARGET_VAR + " in " + frame.method + " at loc " + loc);
- }
- private static StackTrace.StackFrameData FindStackFrame(Thread thr, Method target) {
- for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) {
- if (frame.method.equals(target)) {
- return frame;
- }
+ private static StackTrace.StackFrameData FindStackFrame(Thread thr, Method target) {
+ for (StackTrace.StackFrameData frame : StackTrace.GetStackTrace(thr)) {
+ if (frame.method.equals(target)) {
+ return frame;
+ }
+ }
+ throw new Error("Unable to find stack frame in method " + target + " on thread " + thr);
}
- throw new Error("Unable to find stack frame in method " + target + " on thread " + thr);
- }
}
diff --git a/test/1917-get-stack-frame/run b/test/1917-get-stack-frame/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1917-get-stack-frame/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/1917-get-stack-frame/run.py b/test/1917-get-stack-frame/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1917-get-stack-frame/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1917-get-stack-frame/src/Main.java b/test/1917-get-stack-frame/src/Main.java
index c055a5c..c6f0853 100644
--- a/test/1917-get-stack-frame/src/Main.java
+++ b/test/1917-get-stack-frame/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test1917.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test1917.run();
+ }
}
diff --git a/test/1917-get-stack-frame/src/art/Test1917.java b/test/1917-get-stack-frame/src/art/Test1917.java
index 75af43b..47b06cf 100644
--- a/test/1917-get-stack-frame/src/art/Test1917.java
+++ b/test/1917-get-stack-frame/src/art/Test1917.java
@@ -34,124 +34,124 @@
import java.util.function.Consumer;
public class Test1917 {
- public final static boolean TEST_PRINT_ALL = false;
+ public final static boolean TEST_PRINT_ALL = false;
- public static class ThreadPauser implements Runnable {
- public Semaphore sem_wakeup_main = new Semaphore(0);
- public Semaphore sem_wait = new Semaphore(0);
+ public static class ThreadPauser implements Runnable {
+ public Semaphore sem_wakeup_main = new Semaphore(0);
+ public Semaphore sem_wait = new Semaphore(0);
- public void run() {
- try {
- sem_wakeup_main.release();
- sem_wait.acquire();
- } catch (Exception e) {
- throw new Error("Error with semaphores!", e);
- }
- }
-
- public void waitForOtherThreadToPause() throws Exception {
- sem_wakeup_main.acquire();
- while (!sem_wait.hasQueuedThreads()) {}
- }
-
- public void wakeupOtherThread() throws Exception {
- sem_wait.release();
- }
- }
-
- public static class StackTraceGenerator implements Runnable {
- private final Thread thr;
- private final Consumer<StackTrace.StackFrameData> con;
- public StackTraceGenerator(Thread thr, Consumer<StackTrace.StackFrameData> con) {
- this.thr = thr;
- this.con = con;
- }
-
- public StackTraceGenerator(Consumer<StackTrace.StackFrameData> con) {
- this(null, con);
- }
-
- public Thread getThread() {
- if (thr == null) {
- return Thread.currentThread();
- } else {
- return thr;
- }
- }
- public void run() {
- for (StackTrace.StackFrameData s : StackTrace.GetStackTrace(getThread())) {
- con.accept(s);
- }
- }
- }
-
- public static class RecurCount implements Runnable {
- private final int cnt;
- private final Runnable then;
- public RecurCount(int cnt, Runnable then) {
- this.cnt = cnt;
- this.then = then;
- }
-
- public void run() {
- doRecur(0);
- }
-
- public void doRecur(int n) {
- if (n < cnt) {
- doRecur(n + 1);
- } else {
- then.run();
- }
- }
- }
-
- public static Consumer<StackTrace.StackFrameData> makePrintStackFramesConsumer()
- throws Exception {
- final Method end_method = Test1917.class.getDeclaredMethod("run");
- return new Consumer<StackTrace.StackFrameData>() {
- public void accept(StackTrace.StackFrameData data) {
- if (TEST_PRINT_ALL) {
- System.out.println(data);
- } else {
- Package p = data.method.getDeclaringClass().getPackage();
- // Filter out anything to do with the testing harness.
- if (p != null && p.equals(Test1917.class.getPackage())) {
- System.out.printf("'%s' line: %d\n",
- data.method,
- Breakpoint.locationToLine(data.method, data.current_location));
- } else if (data.method.getDeclaringClass().equals(Semaphore.class)) {
- System.out.printf("'%s' line: <NOT-DETERMINISTIC>\n", data.method);
- }
+ public void run() {
+ try {
+ sem_wakeup_main.release();
+ sem_wait.acquire();
+ } catch (Exception e) {
+ throw new Error("Error with semaphores!", e);
+ }
}
- }
- };
- }
- public static void run() throws Exception {
- System.out.println("Recurring 5 times");
- new RecurCount(5, new StackTraceGenerator(makePrintStackFramesConsumer())).run();
+ public void waitForOtherThreadToPause() throws Exception {
+ sem_wakeup_main.acquire();
+ while (!sem_wait.hasQueuedThreads()) {}
+ }
- System.out.println("Recurring 5 times on another thread");
- Thread thr = new Thread(
- Thread.currentThread().getThreadGroup(),
- new RecurCount(5, new StackTraceGenerator(makePrintStackFramesConsumer())),
- "Recurring Thread 1",
- 10*1000000 /* 10 mb*/);
- thr.start();
- thr.join();
+ public void wakeupOtherThread() throws Exception {
+ sem_wait.release();
+ }
+ }
- System.out.println("Recurring 5 times on another thread. Stack trace from main thread!");
- ThreadPauser pause = new ThreadPauser();
- Thread thr2 = new Thread(
- Thread.currentThread().getThreadGroup(),
- new RecurCount(5, pause),
- "Recurring Thread 2",
- 10*1000000 /* 10 mb*/);
- thr2.start();
- pause.waitForOtherThreadToPause();
- new StackTraceGenerator(thr2, makePrintStackFramesConsumer()).run();
- pause.wakeupOtherThread();
- thr2.join();
- }
+ public static class StackTraceGenerator implements Runnable {
+ private final Thread thr;
+ private final Consumer<StackTrace.StackFrameData> con;
+ public StackTraceGenerator(Thread thr, Consumer<StackTrace.StackFrameData> con) {
+ this.thr = thr;
+ this.con = con;
+ }
+
+ public StackTraceGenerator(Consumer<StackTrace.StackFrameData> con) {
+ this(null, con);
+ }
+
+ public Thread getThread() {
+ if (thr == null) {
+ return Thread.currentThread();
+ } else {
+ return thr;
+ }
+ }
+ public void run() {
+ for (StackTrace.StackFrameData s : StackTrace.GetStackTrace(getThread())) {
+ con.accept(s);
+ }
+ }
+ }
+
+ public static class RecurCount implements Runnable {
+ private final int cnt;
+ private final Runnable then;
+ public RecurCount(int cnt, Runnable then) {
+ this.cnt = cnt;
+ this.then = then;
+ }
+
+ public void run() {
+ doRecur(0);
+ }
+
+ public void doRecur(int n) {
+ if (n < cnt) {
+ doRecur(n + 1);
+ } else {
+ then.run();
+ }
+ }
+ }
+
+ public static Consumer<StackTrace.StackFrameData> makePrintStackFramesConsumer()
+ throws Exception {
+ final Method end_method = Test1917.class.getDeclaredMethod("run");
+ return new Consumer<StackTrace.StackFrameData>() {
+ public void accept(StackTrace.StackFrameData data) {
+ if (TEST_PRINT_ALL) {
+ System.out.println(data);
+ } else {
+ Package p = data.method.getDeclaringClass().getPackage();
+ // Filter out anything to do with the testing harness.
+ if (p != null && p.equals(Test1917.class.getPackage())) {
+ System.out.printf("'%s' line: %d\n",
+ data.method,
+ Breakpoint.locationToLine(data.method, data.current_location));
+ } else if (data.method.getDeclaringClass().equals(Semaphore.class)) {
+ System.out.printf("'%s' line: <NOT-DETERMINISTIC>\n", data.method);
+ }
+ }
+ }
+ };
+ }
+
+ public static void run() throws Exception {
+ System.out.println("Recurring 5 times");
+ new RecurCount(5, new StackTraceGenerator(makePrintStackFramesConsumer())).run();
+
+ System.out.println("Recurring 5 times on another thread");
+ Thread thr = new Thread(
+ Thread.currentThread().getThreadGroup(),
+ new RecurCount(5, new StackTraceGenerator(makePrintStackFramesConsumer())),
+ "Recurring Thread 1",
+ 10*1000000 /* 10 mb*/);
+ thr.start();
+ thr.join();
+
+ System.out.println("Recurring 5 times on another thread. Stack trace from main thread!");
+ ThreadPauser pause = new ThreadPauser();
+ Thread thr2 = new Thread(
+ Thread.currentThread().getThreadGroup(),
+ new RecurCount(5, pause),
+ "Recurring Thread 2",
+ 10*1000000 /* 10 mb*/);
+ thr2.start();
+ pause.waitForOtherThreadToPause();
+ new StackTraceGenerator(thr2, makePrintStackFramesConsumer()).run();
+ pause.wakeupOtherThread();
+ thr2.join();
+ }
}
diff --git a/test/1919-vminit-thread-start-timing/run b/test/1919-vminit-thread-start-timing/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1919-vminit-thread-start-timing/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1919-vminit-thread-start-timing/run.py b/test/1919-vminit-thread-start-timing/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1919-vminit-thread-start-timing/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1919-vminit-thread-start-timing/src/Main.java b/test/1919-vminit-thread-start-timing/src/Main.java
index 41baf24..32a45c5 100644
--- a/test/1919-vminit-thread-start-timing/src/Main.java
+++ b/test/1919-vminit-thread-start-timing/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test1919.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test1919.run();
+ }
}
diff --git a/test/1919-vminit-thread-start-timing/src/art/Test1919.java b/test/1919-vminit-thread-start-timing/src/art/Test1919.java
index 26ff49f..f146ae6 100644
--- a/test/1919-vminit-thread-start-timing/src/art/Test1919.java
+++ b/test/1919-vminit-thread-start-timing/src/art/Test1919.java
@@ -17,49 +17,49 @@
package art;
public class Test1919 {
- public static final boolean PRINT_ALL_THREADS = false;
+ public static final boolean PRINT_ALL_THREADS = false;
- public static void run() throws Exception {
- Thread testing_thread = getTestingThread();
- // TODO(b/124284724): For unknown reasons the testing thread will sometimes SEGV after the test
- // has otherwise completed successfully. This has only been observed on the release version of
- // art (libart.so) and I haven't had any luck reproing it. I assume it has something to do with
- // racing between the DetachCurrentThread and shutdown but I'm not sure. Since the runtime
- // normally never shuts down anyway for now I'll just ensure everything gets cleaned up early to
- // prevent the problem from showing up.
- testing_thread.join();
- for (Event e : getEvents()) {
- if (e.thr != null) {
- if (PRINT_ALL_THREADS ||
- e.thr.equals(Thread.currentThread()) ||
- e.thr.equals(testing_thread)) {
- System.out.println(e.name + ": " + e.thr.getName());
+ public static void run() throws Exception {
+ Thread testing_thread = getTestingThread();
+ // TODO(b/124284724): For unknown reasons the testing thread will sometimes SEGV after the
+ // test has otherwise completed successfully. This has only been observed on the release
+ // version of art (libart.so) and I haven't had any luck reproing it. I assume it has
+ // something to do with racing between the DetachCurrentThread and shutdown but I'm not
+ // sure. Since the runtime normally never shuts down anyway for now I'll just ensure
+ // everything gets cleaned up early to prevent the problem from showing up.
+ testing_thread.join();
+ for (Event e : getEvents()) {
+ if (e.thr != null) {
+ if (PRINT_ALL_THREADS ||
+ e.thr.equals(Thread.currentThread()) ||
+ e.thr.equals(testing_thread)) {
+ System.out.println(e.name + ": " + e.thr.getName());
+ }
+ }
}
- }
}
- }
- static class Event {
- public final String name;
- public final Thread thr;
- public Event(String name, Thread thr) {
- this.name = name;
- this.thr = thr;
+ static class Event {
+ public final String name;
+ public final Thread thr;
+ public Event(String name, Thread thr) {
+ this.name = name;
+ this.thr = thr;
+ }
}
- }
- public static Event[] getEvents() {
- String[] ns = getEventNames();
- Thread[] ts = getEventThreads();
- Event[] es = new Event[Math.min(ns.length, ts.length)];
- for (int i = 0; i < es.length; i++) {
- es[i] = new Event(ns[i], ts[i]);
+ public static Event[] getEvents() {
+ String[] ns = getEventNames();
+ Thread[] ts = getEventThreads();
+ Event[] es = new Event[Math.min(ns.length, ts.length)];
+ for (int i = 0; i < es.length; i++) {
+ es[i] = new Event(ns[i], ts[i]);
+ }
+ return es;
}
- return es;
- }
- public static native String[] getEventNames();
- public static native Thread[] getEventThreads();
+ public static native String[] getEventNames();
+ public static native Thread[] getEventThreads();
- public static native Thread getTestingThread();
+ public static native Thread getTestingThread();
}
diff --git a/test/1920-suspend-native-monitor/run b/test/1920-suspend-native-monitor/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1920-suspend-native-monitor/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/1920-suspend-native-monitor/run.py b/test/1920-suspend-native-monitor/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1920-suspend-native-monitor/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1921-suspend-native-recursive-monitor/run b/test/1921-suspend-native-recursive-monitor/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1921-suspend-native-recursive-monitor/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/1921-suspend-native-recursive-monitor/run.py b/test/1921-suspend-native-recursive-monitor/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1921-suspend-native-recursive-monitor/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1922-owned-monitors-info/run b/test/1922-owned-monitors-info/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1922-owned-monitors-info/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/1922-owned-monitors-info/run.py b/test/1922-owned-monitors-info/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1922-owned-monitors-info/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1923-frame-pop/run b/test/1923-frame-pop/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1923-frame-pop/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/1923-frame-pop/run.py b/test/1923-frame-pop/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1923-frame-pop/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1924-frame-pop-toggle/run b/test/1924-frame-pop-toggle/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1924-frame-pop-toggle/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/1924-frame-pop-toggle/run.py b/test/1924-frame-pop-toggle/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1924-frame-pop-toggle/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1925-self-frame-pop/run b/test/1925-self-frame-pop/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1925-self-frame-pop/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/1925-self-frame-pop/run.py b/test/1925-self-frame-pop/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1925-self-frame-pop/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1926-missed-frame-pop/run b/test/1926-missed-frame-pop/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1926-missed-frame-pop/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/1926-missed-frame-pop/run.py b/test/1926-missed-frame-pop/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1926-missed-frame-pop/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1927-exception-event/run b/test/1927-exception-event/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1927-exception-event/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/1927-exception-event/run.py b/test/1927-exception-event/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1927-exception-event/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1928-exception-event-exception/run b/test/1928-exception-event-exception/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1928-exception-event-exception/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/1928-exception-event-exception/run.py b/test/1928-exception-event-exception/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1928-exception-event-exception/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1929-exception-catch-exception/run b/test/1929-exception-catch-exception/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1929-exception-catch-exception/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/1929-exception-catch-exception/run.py b/test/1929-exception-catch-exception/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1929-exception-catch-exception/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1930-monitor-info/run b/test/1930-monitor-info/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1930-monitor-info/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/1930-monitor-info/run.py b/test/1930-monitor-info/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1930-monitor-info/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1931-monitor-events/check b/test/1931-monitor-events/check
deleted file mode 100644
index 3f799cc..0000000
--- a/test/1931-monitor-events/check
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Art sends events for park/unpark, and the RI doesn't. Remove it from the expected output.
-if [[ "$TEST_RUNTIME" == "jvm" ]]; then
- patch -p0 expected-stdout.txt < jvm-expected.patch >/dev/null
-fi
-
-./default-check "$@"
diff --git a/test/1931-monitor-events/expected-stdout.jvm.txt b/test/1931-monitor-events/expected-stdout.jvm.txt
new file mode 100644
index 0000000..e26d459
--- /dev/null
+++ b/test/1931-monitor-events/expected-stdout.jvm.txt
@@ -0,0 +1,30 @@
+Testing contended locking.
+Locker thread 1 for NamedLock[Lock testLock] contended-LOCKING NamedLock[Lock testLock]
+Locker thread 1 for NamedLock[Lock testLock] LOCKED NamedLock[Lock testLock]
+Testing park.
+Testing monitor wait.
+Locker thread 2 for NamedLock[Lock testWait] start-monitor-wait NamedLock[Lock testWait] timeout: 0
+Locker thread 2 for NamedLock[Lock testWait] monitor-waited NamedLock[Lock testWait] timed_out: false
+Testing monitor timed wait.
+Locker thread 4 for NamedLock[Lock testTimedWait] start-monitor-wait NamedLock[Lock testTimedWait] timeout: 3600000
+Locker thread 4 for NamedLock[Lock testTimedWait] monitor-waited NamedLock[Lock testTimedWait] timed_out: false
+Testing monitor timed with timeout.
+Waiting for 10 seconds.
+Locker thread 6 for NamedLock[Lock testTimedWaitTimeout] start-monitor-wait NamedLock[Lock testTimedWaitTimeout] timeout: 10000
+Locker thread 6 for NamedLock[Lock testTimedWaitTimeout] monitor-waited NamedLock[Lock testTimedWaitTimeout] timed_out: true
+Wait finished with timeout.
+Waiting on an unlocked monitor.
+Unlocked wait thread: start-monitor-wait NamedLock[Lock testUnlockedWait] timeout: 0
+Caught exception: java.lang.reflect.InvocationTargetException
+ Caused by: class java.lang.IllegalMonitorStateException
+Waiting with an illegal argument (negative timeout)
+Locker thread 7 for NamedLock[Lock testIllegalWait] start-monitor-wait NamedLock[Lock testIllegalWait] timeout: -100
+Caught exception: art.Monitors$TestException: Exception thrown by other thread!
+ Caused by: art.Monitors$TestException: Got an error while performing action TIMED_WAIT
+ Caused by: class java.lang.IllegalArgumentException
+Interrupt a monitor being waited on.
+Locker thread 8 for NamedLock[Lock testInteruptWait] start-monitor-wait NamedLock[Lock testInteruptWait] timeout: 0
+Locker thread 8 for NamedLock[Lock testInteruptWait] monitor-waited NamedLock[Lock testInteruptWait] timed_out: false
+Caught exception: art.Monitors$TestException: Exception thrown by other thread!
+ Caused by: art.Monitors$TestException: Got an error while performing action WAIT
+ Caused by: class java.lang.InterruptedException
diff --git a/test/1931-monitor-events/jvm-expected.patch b/test/1931-monitor-events/jvm-expected.patch
deleted file mode 100644
index 7595b14..0000000
--- a/test/1931-monitor-events/jvm-expected.patch
+++ /dev/null
@@ -1,3 +0,0 @@
-5,6d4
-< ParkThread start-monitor-wait NamedLock[Parking blocker object] timeout: 1
-< ParkThread monitor-waited NamedLock[Parking blocker object] timed_out: true
diff --git a/test/1931-monitor-events/run b/test/1931-monitor-events/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1931-monitor-events/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/1931-monitor-events/run.py b/test/1931-monitor-events/run.py
new file mode 100644
index 0000000..6c5d4ad
--- /dev/null
+++ b/test/1931-monitor-events/run.py
@@ -0,0 +1,23 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
+
+ # Art sends events for park/unpark, and the RI doesn't.
+ if args.jvm:
+ ctx.expected_stdout = ctx.expected_stdout.with_suffix(".jvm.txt")
diff --git a/test/1932-monitor-events-misc/check b/test/1932-monitor-events-misc/check
deleted file mode 100644
index 0fe4429..0000000
--- a/test/1932-monitor-events-misc/check
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# The RI sends an extra event that art doesn't. Add it to the expected output.
-if [[ "$TEST_RUNTIME" == "jvm" ]]; then
- patch -p0 expected-stdout.txt < jvm-expected.patch >/dev/null
-fi
-
-./default-check "$@"
diff --git a/test/1932-monitor-events-misc/expected-stdout.jvm.txt b/test/1932-monitor-events-misc/expected-stdout.jvm.txt
new file mode 100644
index 0000000..b2b051e
--- /dev/null
+++ b/test/1932-monitor-events-misc/expected-stdout.jvm.txt
@@ -0,0 +1,105 @@
+Testing contended locking where lock is released before callback ends.
+Locker thread 1 for NamedLock[Lock testLockUncontend] contended-LOCKING NamedLock[Lock testLockUncontend]
+Releasing NamedLock[Lock testLockUncontend] during monitorEnter event.
+Locker thread 1 for NamedLock[Lock testLockUncontend] LOCKED NamedLock[Lock testLockUncontend]
+Testing throwing exceptions in monitor_enter
+Locker thread 3 for NamedLock[Lock testLockThrowEnter] contended-LOCKING NamedLock[Lock testLockThrowEnter]
+Throwing exception in MonitorEnter
+Locker thread 3 for NamedLock[Lock testLockThrowEnter] LOCKED NamedLock[Lock testLockThrowEnter]
+Caught exception: art.Monitors$TestException: Exception thrown by other thread!
+ Caused by: art.Monitors$TestException: throwing exception during monitorEnter of NamedLock[Lock testLockThrowEnter]
+lock state is: MonitorUsage{ monitor: NamedLock[Lock testLockThrowEnter], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Testing throwing exceptions in monitor_entered
+Locker thread 5 for NamedLock[Lock testLockThrowEntered] contended-LOCKING NamedLock[Lock testLockThrowEntered]
+Locker thread 5 for NamedLock[Lock testLockThrowEntered] LOCKED NamedLock[Lock testLockThrowEntered]
+Throwing exception in MonitorEntered
+Caught exception: art.Monitors$TestException: Exception thrown by other thread!
+ Caused by: art.Monitors$TestException: throwing exception during monitorEntered of NamedLock[Lock testLockThrowEntered]
+lock state is: MonitorUsage{ monitor: NamedLock[Lock testLockThrowEntered], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Testing throwing exceptions in both monitorEnter & MonitorEntered
+Locker thread 7 for NamedLock[Lock testLockThrowBoth] contended-LOCKING NamedLock[Lock testLockThrowBoth]
+Throwing exception in MonitorEnter
+Locker thread 7 for NamedLock[Lock testLockThrowBoth] LOCKED NamedLock[Lock testLockThrowBoth]
+Throwing exception in MonitorEntered
+Caught exception: art.Monitors$TestException: Exception thrown by other thread!
+ Caused by: art.Monitors$TestException: throwing exception during monitorEntered of NamedLock[Lock testLockThrowBoth]
+lock state is: MonitorUsage{ monitor: NamedLock[Lock testLockThrowBoth], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Testing throwing exception in MonitorWait event
+Locker thread 8 for NamedLock[Lock testThrowWait] start-monitor-wait NamedLock[Lock testThrowWait] timeout: 0
+Throwing exception in MonitorWait
+Locker thread 8 for NamedLock[Lock testThrowWait] monitor-waited NamedLock[Lock testThrowWait] timed_out: false
+Caught exception: art.Monitors$TestException: Exception thrown by other thread!
+ Caused by: art.Monitors$TestException: throwing exception during MonitorWait of NamedLock[Lock testThrowWait]
+lock state is: MonitorUsage{ monitor: NamedLock[Lock testThrowWait], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Testing throwing exception in MonitorWait event with illegal aruments
+Locker thread 9 for NamedLock[Lock testThrowIllegalWait] start-monitor-wait NamedLock[Lock testThrowIllegalWait] timeout: -100000
+Throwing exception in MonitorWait timeout = -100000
+Caught exception: art.Monitors$TestException: Exception thrown by other thread!
+ Caused by: art.Monitors$TestException: throwing exception during monitorWait of NamedLock[Lock testThrowIllegalWait]
+lock state is: MonitorUsage{ monitor: NamedLock[Lock testThrowIllegalWait], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Testing throwing exception in MonitorWaited event
+Locker thread 10 for NamedLock[Lock testThrowWaited] start-monitor-wait NamedLock[Lock testThrowWaited] timeout: 0
+Locker thread 10 for NamedLock[Lock testThrowWaited] monitor-waited NamedLock[Lock testThrowWaited] timed_out: false
+Throwing exception in MonitorWaited
+Caught exception: art.Monitors$TestException: Exception thrown by other thread!
+ Caused by: art.Monitors$TestException: throwing exception during monitorWaited of NamedLock[Lock testThrowWaited]
+lock state is: MonitorUsage{ monitor: NamedLock[Lock testThrowWaited], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Testing throwing exception in MonitorWaited event caused by timeout
+Locker thread 12 for NamedLock[Lock testThrowWaitedTimeout] start-monitor-wait NamedLock[Lock testThrowWaitedTimeout] timeout: 5000
+Locker thread 12 for NamedLock[Lock testThrowWaitedTimeout] monitor-waited NamedLock[Lock testThrowWaitedTimeout] timed_out: true
+Throwing exception in MonitorWaited
+Caught exception: art.Monitors$TestException: Exception thrown by other thread!
+ Caused by: art.Monitors$TestException: throwing exception during monitorWaited of NamedLock[Lock testThrowWaitedTimeout]
+lock state is: MonitorUsage{ monitor: NamedLock[Lock testThrowWaitedTimeout], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Testing throwing exception in MonitorWaited event caused by interrupt
+Locker thread 13 for NamedLock[Lock testThrowWaitedInterrupt] start-monitor-wait NamedLock[Lock testThrowWaitedInterrupt] timeout: 0
+Locker thread 13 for NamedLock[Lock testThrowWaitedInterrupt] monitor-waited NamedLock[Lock testThrowWaitedInterrupt] timed_out: false
+Throwing exception in MonitorWaited
+Caught exception: art.Monitors$TestException: Exception thrown by other thread!
+ Caused by: art.Monitors$TestException: throwing exception during monitorWaited of NamedLock[Lock testThrowWaitedInterrupt]
+lock state is: MonitorUsage{ monitor: NamedLock[Lock testThrowWaitedInterrupt], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Testing ObjectMonitorInfo inside of events
+Locker thread 15 for NamedLock[Lock testMonitorInfoInEvents] contended-LOCKING NamedLock[Lock testMonitorInfoInEvents]
+Monitor usage in MonitorEnter: MonitorUsage{ monitor: NamedLock[Lock testMonitorInfoInEvents], owner: Locker thread 14 for NamedLock[Lock testMonitorInfoInEvents], entryCount: 1, waiters: [], notify_waiters: [] }
+Locker thread 15 for NamedLock[Lock testMonitorInfoInEvents] LOCKED NamedLock[Lock testMonitorInfoInEvents]
+Monitor usage in MonitorEntered: MonitorUsage{ monitor: NamedLock[Lock testMonitorInfoInEvents], owner: Locker thread 15 for NamedLock[Lock testMonitorInfoInEvents], entryCount: 1, waiters: [], notify_waiters: [] }
+Locker thread 15 for NamedLock[Lock testMonitorInfoInEvents] start-monitor-wait NamedLock[Lock testMonitorInfoInEvents] timeout: 0
+Monitor usage in MonitorWait: MonitorUsage{ monitor: NamedLock[Lock testMonitorInfoInEvents], owner: Locker thread 15 for NamedLock[Lock testMonitorInfoInEvents], entryCount: 1, waiters: [], notify_waiters: [] }
+Locker thread 15 for NamedLock[Lock testMonitorInfoInEvents] monitor-waited NamedLock[Lock testMonitorInfoInEvents] timed_out: false
+Monitor usage in MonitorWaited: MonitorUsage{ monitor: NamedLock[Lock testMonitorInfoInEvents], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+Testing that the monitor can be stolen during the MonitorWaited event.
+Locker thread 17 for NamedLock[test testWaitEnterInterleaving] start-monitor-wait NamedLock[test testWaitEnterInterleaving] timeout: 0
+Locker thread 17 for NamedLock[test testWaitEnterInterleaving] monitor-waited NamedLock[test testWaitEnterInterleaving] timed_out: false
+locking controller3 in controller2 MonitorWaited event
+Controller3 now holds the lock the monitor wait will try to re-acquire
+Testing that we can lock and release the monitor in the MonitorWait event
+Locker thread 20 for NamedLock[test testWaitMonitorEnter] start-monitor-wait NamedLock[test testWaitMonitorEnter] timeout: 0
+In wait monitor usage: MonitorUsage{ monitor: NamedLock[test testWaitMonitorEnter], owner: Locker thread 20 for NamedLock[test testWaitMonitorEnter], entryCount: 1, waiters: [], notify_waiters: [] }
+In wait monitor usage sync: MonitorUsage{ monitor: NamedLock[test testWaitMonitorEnter], owner: Locker thread 20 for NamedLock[test testWaitMonitorEnter], entryCount: 2, waiters: [], notify_waiters: [] }
+Locker thread 20 for NamedLock[test testWaitMonitorEnter] monitor-waited NamedLock[test testWaitMonitorEnter] timed_out: false
+Testing that we can lock and release the monitor in the MonitorWaited event
+Locker thread 22 for NamedLock[test testWaitedMonitorEnter] start-monitor-wait NamedLock[test testWaitedMonitorEnter] timeout: 0
+Locker thread 22 for NamedLock[test testWaitedMonitorEnter] monitor-waited NamedLock[test testWaitedMonitorEnter] timed_out: false
+In waited monitor usage: MonitorUsage{ monitor: NamedLock[test testWaitedMonitorEnter], owner: <NULL>, entryCount: 0, waiters: [], notify_waiters: [] }
+In waited monitor usage sync: MonitorUsage{ monitor: NamedLock[test testWaitedMonitorEnter], owner: Locker thread 22 for NamedLock[test testWaitedMonitorEnter], entryCount: 1, waiters: [], notify_waiters: [] }
+Testing we can perform recursive lock in MonitorEntered
+Locker thread 25 for NamedLock[test testRecursiveMontiorEnteredLock] contended-LOCKING NamedLock[test testRecursiveMontiorEnteredLock]
+Locker thread 25 for NamedLock[test testRecursiveMontiorEnteredLock] LOCKED NamedLock[test testRecursiveMontiorEnteredLock]
+In MonitorEntered usage: MonitorUsage{ monitor: NamedLock[test testRecursiveMontiorEnteredLock], owner: Locker thread 25 for NamedLock[test testRecursiveMontiorEnteredLock], entryCount: 1, waiters: [], notify_waiters: [] }
+In MonitorEntered sync: MonitorUsage{ monitor: NamedLock[test testRecursiveMontiorEnteredLock], owner: Locker thread 25 for NamedLock[test testRecursiveMontiorEnteredLock], entryCount: 2, waiters: [], notify_waiters: [] }
+Testing the lock state if MonitorEnter throws in a native method
+NativeLockStateThrowEnter thread contended-LOCKING NamedLock[test testNativeLockStateThrowEnter]
+Unlocking controller1 in MonitorEnter
+Throwing exception in MonitorEnter
+NativeLockStateThrowEnter thread LOCKED NamedLock[test testNativeLockStateThrowEnter]
+MonitorEnter returned: -1
+Lock state is: MonitorUsage{ monitor: NamedLock[test testNativeLockStateThrowEnter], owner: NativeLockStateThrowEnter thread, entryCount: 1, waiters: [], notify_waiters: [] }
+Caught exception: art.Monitors$TestException: throwing exception during monitorEnter of NamedLock[test testNativeLockStateThrowEnter]
+Testing the lock state if MonitorEntered throws in a native method
+NativeLockStateThrowEntered thread contended-LOCKING NamedLock[test testNativeLockStateThrowEntered]
+Unlocking controller1 in MonitorEnter
+NativeLockStateThrowEntered thread LOCKED NamedLock[test testNativeLockStateThrowEntered]
+Throwing exception in MonitorEntered
+MonitorEnter returned: -1
+Lock state is: MonitorUsage{ monitor: NamedLock[test testNativeLockStateThrowEntered], owner: NativeLockStateThrowEntered thread, entryCount: 1, waiters: [], notify_waiters: [] }
+Caught exception: art.Monitors$TestException: throwing exception during monitorEntered of NamedLock[test testNativeLockStateThrowEntered]
diff --git a/test/1932-monitor-events-misc/jvm-expected.patch b/test/1932-monitor-events-misc/jvm-expected.patch
deleted file mode 100644
index f6b2285..0000000
--- a/test/1932-monitor-events-misc/jvm-expected.patch
+++ /dev/null
@@ -1,2 +0,0 @@
-29a30
-> Locker thread 8 for NamedLock[Lock testThrowWait] monitor-waited NamedLock[Lock testThrowWait] timed_out: false
diff --git a/test/1932-monitor-events-misc/run b/test/1932-monitor-events-misc/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1932-monitor-events-misc/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/1932-monitor-events-misc/run.py b/test/1932-monitor-events-misc/run.py
new file mode 100644
index 0000000..43e66fd
--- /dev/null
+++ b/test/1932-monitor-events-misc/run.py
@@ -0,0 +1,23 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
+
+ # The RI sends an extra event that art doesn't.
+ if args.jvm:
+ ctx.expected_stdout = ctx.expected_stdout.with_suffix(".jvm.txt")
diff --git a/test/1933-monitor-current-contended/run b/test/1933-monitor-current-contended/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1933-monitor-current-contended/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/1933-monitor-current-contended/run.py b/test/1933-monitor-current-contended/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1933-monitor-current-contended/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1934-jvmti-signal-thread/run b/test/1934-jvmti-signal-thread/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1934-jvmti-signal-thread/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/1934-jvmti-signal-thread/run.py b/test/1934-jvmti-signal-thread/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1934-jvmti-signal-thread/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1935-get-set-current-frame-jit/run b/test/1935-get-set-current-frame-jit/run
deleted file mode 100755
index e569d08..0000000
--- a/test/1935-get-set-current-frame-jit/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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.
-
-# Ensure the test is not subject to code collection
-./default-run "$@" --jvmti --runtime-option -Xjitinitialsize:32M
diff --git a/test/1935-get-set-current-frame-jit/run.py b/test/1935-get-set-current-frame-jit/run.py
new file mode 100644
index 0000000..fb36023
--- /dev/null
+++ b/test/1935-get-set-current-frame-jit/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ensure the test is not subject to code collection
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xjitinitialsize:32M"])
diff --git a/test/1936-thread-end-events/check b/test/1936-thread-end-events/check
deleted file mode 100644
index 0fe4429..0000000
--- a/test/1936-thread-end-events/check
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# The RI sends an extra event that art doesn't. Add it to the expected output.
-if [[ "$TEST_RUNTIME" == "jvm" ]]; then
- patch -p0 expected-stdout.txt < jvm-expected.patch >/dev/null
-fi
-
-./default-check "$@"
diff --git a/test/1936-thread-end-events/expected-stdout.jvm.txt b/test/1936-thread-end-events/expected-stdout.jvm.txt
new file mode 100644
index 0000000..a8e79a4
--- /dev/null
+++ b/test/1936-thread-end-events/expected-stdout.jvm.txt
@@ -0,0 +1,49 @@
+Entered public static void art.Test1936.foo()
+Thread: test-thread
+ | alive: true
+ | interrupted: false
+ | daemon: false
+ | group: java.lang.ThreadGroup[name=main,maxpri=10]
+
+Entered private void java.lang.Thread.exit()
+Thread: test-thread
+ | alive: true
+ | interrupted: false
+ | daemon: false
+ | group: java.lang.ThreadGroup[name=main,maxpri=10]
+
+Entered void java.lang.ThreadGroup.threadTerminated(java.lang.Thread)
+Thread: test-thread
+ | alive: true
+ | interrupted: false
+ | daemon: false
+ | group: java.lang.ThreadGroup[name=main,maxpri=10]
+
+Entered private void java.lang.ThreadGroup.remove(java.lang.Thread)
+Thread: test-thread
+ | alive: true
+ | interrupted: false
+ | daemon: false
+ | group: java.lang.ThreadGroup[name=main,maxpri=10]
+
+Entered public static native void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
+Thread: test-thread
+ | alive: true
+ | interrupted: false
+ | daemon: false
+ | group: java.lang.ThreadGroup[name=main,maxpri=10]
+
+Entered public static void art.Test1936.NotifyThreadEnd(java.lang.Thread)
+Thread: test-thread
+ | alive: true
+ | interrupted: false
+ | daemon: false
+ | group: null
+
+Entered public static void art.Test1936.foo()
+Thread: test-thread
+ | alive: true
+ | interrupted: false
+ | daemon: false
+ | group: null
+
diff --git a/test/1936-thread-end-events/jvm-expected.patch b/test/1936-thread-end-events/jvm-expected.patch
deleted file mode 100644
index ddb30a3..0000000
--- a/test/1936-thread-end-events/jvm-expected.patch
+++ /dev/null
@@ -1,16 +0,0 @@
-7a8,14
-> Entered private void java.lang.Thread.exit()
-> Thread: test-thread
-> | alive: true
-> | interrupted: false
-> | daemon: false
-> | group: java.lang.ThreadGroup[name=main,maxpri=10]
->
-34c41
-< | group: java.lang.ThreadGroup[name=main,maxpri=10]
----
-> | group: null
-41c48
-< | group: java.lang.ThreadGroup[name=main,maxpri=10]
----
-> | group: null
diff --git a/test/1936-thread-end-events/run b/test/1936-thread-end-events/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1936-thread-end-events/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/1936-thread-end-events/run.py b/test/1936-thread-end-events/run.py
new file mode 100644
index 0000000..3e1a8e9
--- /dev/null
+++ b/test/1936-thread-end-events/run.py
@@ -0,0 +1,24 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
+
+ # The RI sends an extra event that art doesn't.
+ if args.jvm:
+ ctx.expected_stdout = ctx.expected_stdout.with_suffix(".jvm.txt")
diff --git a/test/1937-transform-soft-fail/run b/test/1937-transform-soft-fail/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1937-transform-soft-fail/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1937-transform-soft-fail/run.py b/test/1937-transform-soft-fail/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1937-transform-soft-fail/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1938-transform-abstract-single-impl/run b/test/1938-transform-abstract-single-impl/run
deleted file mode 100755
index adb1a1c..0000000
--- a/test/1938-transform-abstract-single-impl/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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 --no-app-image
diff --git a/test/1938-transform-abstract-single-impl/run.py b/test/1938-transform-abstract-single-impl/run.py
new file mode 100644
index 0000000..23b8dc3
--- /dev/null
+++ b/test/1938-transform-abstract-single-impl/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, app_image=False)
diff --git a/test/1939-proxy-frames/run b/test/1939-proxy-frames/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1939-proxy-frames/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/1939-proxy-frames/run.py b/test/1939-proxy-frames/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1939-proxy-frames/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1940-ddms-ext/ddm_ext.cc b/test/1940-ddms-ext/ddm_ext.cc
index 110ad64..6461bdd 100644
--- a/test/1940-ddms-ext/ddm_ext.cc
+++ b/test/1940-ddms-ext/ddm_ext.cc
@@ -21,8 +21,8 @@
// Test infrastructure
#include "jvmti_helper.h"
-#include "nativehelper/scoped_local_ref.h"
-#include "nativehelper/scoped_primitive_array.h"
+#include "scoped_local_ref.h"
+#include "scoped_primitive_array.h"
#include "test_env.h"
namespace art {
diff --git a/test/1940-ddms-ext/run b/test/1940-ddms-ext/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1940-ddms-ext/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1940-ddms-ext/run.py b/test/1940-ddms-ext/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1940-ddms-ext/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1940-ddms-ext/src-art/art/Test1940.java b/test/1940-ddms-ext/src-art/art/Test1940.java
index 605e409..7adcd4a 100644
--- a/test/1940-ddms-ext/src-art/art/Test1940.java
+++ b/test/1940-ddms-ext/src-art/art/Test1940.java
@@ -16,6 +16,7 @@
package art;
+import java.lang.reflect.Field;
import org.apache.harmony.dalvik.ddmc.*;
import dalvik.system.VMDebug;
@@ -50,18 +51,21 @@
}
}
- private static boolean chunkEq(Chunk a, Chunk b) {
- return a.type == b.type &&
- a.offset == b.offset &&
- a.length == b.length &&
- Arrays.equals(a.data, b.data);
+ private static boolean chunkEq(Chunk c1, Chunk c2) {
+ ChunkWrapper a = new ChunkWrapper(c1);
+ ChunkWrapper b = new ChunkWrapper(c2);
+ return a.type() == b.type() &&
+ a.offset() == b.offset() &&
+ a.length() == b.length() &&
+ Arrays.equals(a.data(), b.data());
}
private static String printChunk(Chunk k) {
- byte[] out = new byte[k.length];
- System.arraycopy(k.data, k.offset, out, 0, k.length);
+ ChunkWrapper c = new ChunkWrapper(k);
+ byte[] out = new byte[c.length()];
+ System.arraycopy(c.data(), c.offset(), out, 0, c.length());
return String.format("Chunk(Type: 0x%X, Len: %d, data: %s)",
- k.type, k.length, Arrays.toString(out));
+ c.type(), c.length(), Arrays.toString(out));
}
private static final class MyDdmHandler extends ChunkHandler {
@@ -73,7 +77,8 @@
// For this test we will simply calculate the checksum
ByteBuffer b = ByteBuffer.wrap(new byte[8]);
Adler32 a = new Adler32();
- a.update(req.data, req.offset, req.length);
+ ChunkWrapper reqWrapper = new ChunkWrapper(req);
+ a.update(reqWrapper.data(), reqWrapper.offset(), reqWrapper.length());
b.order(ByteOrder.BIG_ENDIAN);
long val = a.getValue();
b.putLong(val);
@@ -92,6 +97,49 @@
}
}
+
+ /**
+ * Wrapper for accessing the hidden fields in {@link Chunk} in CTS.
+ */
+ private static class ChunkWrapper {
+ private Chunk c;
+
+ ChunkWrapper(Chunk c) {
+ this.c = c;
+ }
+
+ int type() {
+ return c.type;
+ }
+
+ int length() {
+ try {
+ Field f = Chunk.class.getField("length");
+ return (int) f.get(c);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ byte[] data() {
+ try {
+ Field f = Chunk.class.getField("data");
+ return (byte[]) f.get(c);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ int offset() {
+ try {
+ Field f = Chunk.class.getField("offset");
+ return (int) f.get(c);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
public static final ChunkHandler SINGLE_HANDLER = new MyDdmHandler();
public static DdmHandler CURRENT_HANDLER;
diff --git a/test/1941-dispose-stress/run b/test/1941-dispose-stress/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1941-dispose-stress/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/1941-dispose-stress/run.py b/test/1941-dispose-stress/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1941-dispose-stress/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1942-suspend-raw-monitor-exit/run b/test/1942-suspend-raw-monitor-exit/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1942-suspend-raw-monitor-exit/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/1942-suspend-raw-monitor-exit/run.py b/test/1942-suspend-raw-monitor-exit/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1942-suspend-raw-monitor-exit/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1943-suspend-raw-monitor-wait/run b/test/1943-suspend-raw-monitor-wait/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1943-suspend-raw-monitor-wait/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/1943-suspend-raw-monitor-wait/run.py b/test/1943-suspend-raw-monitor-wait/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/1943-suspend-raw-monitor-wait/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1946-list-descriptors/run b/test/1946-list-descriptors/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1946-list-descriptors/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1946-list-descriptors/run.py b/test/1946-list-descriptors/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1946-list-descriptors/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1947-breakpoint-redefine-deopt/run b/test/1947-breakpoint-redefine-deopt/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1947-breakpoint-redefine-deopt/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/1947-breakpoint-redefine-deopt/run.py b/test/1947-breakpoint-redefine-deopt/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1947-breakpoint-redefine-deopt/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1948-obsolete-const-method-handle/build b/test/1948-obsolete-const-method-handle/build
deleted file mode 100644
index d0e7a8c..0000000
--- a/test/1948-obsolete-const-method-handle/build
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Make us exit on a failure
-set -e
-
-mkdir classes
-./util-src/build-classes $PWD/classes
-
-./default-build --api-level 28 "$@"
diff --git a/test/1948-obsolete-const-method-handle/build.py b/test/1948-obsolete-const-method-handle/build.py
new file mode 100644
index 0000000..4eb0ccb
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build(api_level=28)
diff --git a/test/1948-obsolete-const-method-handle/generate-sources b/test/1948-obsolete-const-method-handle/generate-sources
new file mode 100755
index 0000000..9cdc749
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/generate-sources
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+# Make us exit on a failure
+set -e
+
+mkdir classes
+./util-src/build-classes $PWD/classes
diff --git a/test/1948-obsolete-const-method-handle/run b/test/1948-obsolete-const-method-handle/run
deleted file mode 100755
index 9eacb4c..0000000
--- a/test/1948-obsolete-const-method-handle/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Squash the exit status and put it in expected
-./default-run --jvmti "$@"
diff --git a/test/1948-obsolete-const-method-handle/run.py b/test/1948-obsolete-const-method-handle/run.py
new file mode 100644
index 0000000..afb1374
--- /dev/null
+++ b/test/1948-obsolete-const-method-handle/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+
+def run(ctx, args):
+ # Squash the exit status and put it in expected
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/TestGenerator.java b/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/TestGenerator.java
index 3d194b4..f20012f 100644
--- a/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/TestGenerator.java
+++ b/test/1948-obsolete-const-method-handle/util-src/src/art/constmethodhandle/TestGenerator.java
@@ -71,7 +71,7 @@
ClassReader cr = new ClassReader(initClass);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
cr.accept(
- new ClassVisitor(Opcodes.ASM7, cw) {
+ new ClassVisitor(Opcodes.ASM9, cw) {
@Override
public void visitEnd() {
generateStringAccessorMethod(
@@ -143,7 +143,7 @@
ClassReader cr = new ClassReader(inputClass);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
cr.accept(
- new ClassVisitor(Opcodes.ASM7, cw) {
+ new ClassVisitor(Opcodes.ASM9, cw) {
@Override
public void visitEnd() {
generateRunTest(cw, toCall);
diff --git a/test/1949-short-dex-file/run b/test/1949-short-dex-file/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1949-short-dex-file/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1949-short-dex-file/run.py b/test/1949-short-dex-file/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1949-short-dex-file/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1950-unprepared-transform/check b/test/1950-unprepared-transform/check
deleted file mode 100755
index 0fe4429..0000000
--- a/test/1950-unprepared-transform/check
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# The RI sends an extra event that art doesn't. Add it to the expected output.
-if [[ "$TEST_RUNTIME" == "jvm" ]]; then
- patch -p0 expected-stdout.txt < jvm-expected.patch >/dev/null
-fi
-
-./default-check "$@"
diff --git a/test/1950-unprepared-transform/expected-stdout.jvm.txt b/test/1950-unprepared-transform/expected-stdout.jvm.txt
new file mode 100644
index 0000000..cc470e1
--- /dev/null
+++ b/test/1950-unprepared-transform/expected-stdout.jvm.txt
@@ -0,0 +1,7 @@
+Redefine in ClassLoad on current thread.
+retransformClasses on an unprepared class succeeded
+Object out is: Transformed object!
+Redefine during ClassLoad on another thread.
+retransformClasses on an unprepared class succeeded
+Object out is: Transformed object!
+Redefinition thread finished.
diff --git a/test/1950-unprepared-transform/jvm-expected.patch b/test/1950-unprepared-transform/jvm-expected.patch
deleted file mode 100644
index fd0131e..0000000
--- a/test/1950-unprepared-transform/jvm-expected.patch
+++ /dev/null
@@ -1,6 +0,0 @@
-2,3c2,3
-< Trying to redefine: class Transform. Caught error class java.lang.Exception: Failed to retransform class <LTransform;> due to JVMTI_ERROR_INTERNAL
-< Object out is: NON Transformed Object
----
-> retransformClasses on an unprepared class succeeded
-> Object out is: Transformed object!
diff --git a/test/1950-unprepared-transform/run b/test/1950-unprepared-transform/run
deleted file mode 100755
index adb1a1c..0000000
--- a/test/1950-unprepared-transform/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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 --no-app-image
diff --git a/test/1950-unprepared-transform/run.py b/test/1950-unprepared-transform/run.py
new file mode 100644
index 0000000..995bbbd
--- /dev/null
+++ b/test/1950-unprepared-transform/run.py
@@ -0,0 +1,23 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, app_image=False)
+
+ # The RI sends an extra event that art doesn't.
+ if args.jvm:
+ ctx.expected_stdout = ctx.expected_stdout.with_suffix(".jvm.txt")
diff --git a/test/1951-monitor-enter-no-suspend/run b/test/1951-monitor-enter-no-suspend/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1951-monitor-enter-no-suspend/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1951-monitor-enter-no-suspend/run.py b/test/1951-monitor-enter-no-suspend/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1951-monitor-enter-no-suspend/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1953-pop-frame/check b/test/1953-pop-frame/check
deleted file mode 100755
index 24bbf07..0000000
--- a/test/1953-pop-frame/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# The RI has restrictions and bugs around some PopFrame behavior that ART lacks.
-# See b/116003018. Some configurations cannot handle the class load events in
-# quite the right way so they are disabled there too.
-./default-check "$@" || \
- (patch -p0 expected-stdout.txt < class-loading-expected.patch >/dev/null && ./default-check "$@")
diff --git a/test/1953-pop-frame/class-loading-expected.patch b/test/1953-pop-frame/class-loading-expected.patch
deleted file mode 100644
index 1a5eda7..0000000
--- a/test/1953-pop-frame/class-loading-expected.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-36a37,50
-> Test stopped during notifyFramePop without exception on pop of calledFunction
-> Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
-> result is StandardTestObject { cnt: 2 } base-call count: 1
-> Test stopped during notifyFramePop without exception on pop of doNothing
-> Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
-> result is StandardTestObject { cnt: 1 } base-call count: 1
-> Test stopped during notifyFramePop with exception on pop of calledFunction
-> Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
-> art.Test1953$ExceptionThrowTestObject$TestError thrown and caught!
-> result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
-> Test stopped during notifyFramePop with exception on pop of doThrow
-> Single call with PopFrame on ExceptionCatchTestObject { cnt: 0 } base-call-count: 0
-> art.Test1953$ExceptionCatchTestObject$TestError caught in called function.
-> result is ExceptionCatchTestObject { cnt: 1 } base-call count: 1
-60a75,94
-> Test stopped during a ClassLoad event.
-> Single call with PopFrame on ClassLoadObject { cnt: 0, curClass: 0} base-call-count: 0
-> Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
-> art.Test1953.popFrame(Native Method)
-> art.Test1953.runTestOn(Test1953.java)
-> art.Test1953.runTestOn(Test1953.java)
-> art.Test1953.runTests(Test1953.java)
-> <Additional frames hidden>
-> TC0.foo == 1
-> result is ClassLoadObject { cnt: 1, curClass: 1} base-call count: 1
-> Test stopped during a ClassPrepare event.
-> Single call with PopFrame on ClassLoadObject { cnt: 0, curClass: 1} base-call-count: 0
-> Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
-> art.Test1953.popFrame(Native Method)
-> art.Test1953.runTestOn(Test1953.java)
-> art.Test1953.runTestOn(Test1953.java)
-> art.Test1953.runTests(Test1953.java)
-> <Additional frames hidden>
-> TC1.foo == 2
-> result is ClassLoadObject { cnt: 1, curClass: 2} base-call count: 1
diff --git a/test/1953-pop-frame/expected-stdout.no-jvm.txt b/test/1953-pop-frame/expected-stdout.no-jvm.txt
new file mode 100644
index 0000000..e75ea64
--- /dev/null
+++ b/test/1953-pop-frame/expected-stdout.no-jvm.txt
@@ -0,0 +1,121 @@
+Test stopped using breakpoint
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped using breakpoint with declared synchronized function
+Single call with PopFrame on SynchronizedFunctionTestObject { cnt: 0 } base-call-count: 0
+result is SynchronizedFunctionTestObject { cnt: 2 } base-call count: 1
+Test stopped using breakpoint with synchronized block
+Single call with PopFrame on SynchronizedTestObject { cnt: 0 } base-call-count: 0
+result is SynchronizedTestObject { cnt: 2 } base-call count: 1
+Test stopped on single step
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped on field access
+Single call with PopFrame on FieldBasedTestObject { cnt: 0, TARGET_FIELD: 0 } base-call-count: 0
+result is FieldBasedTestObject { cnt: 2, TARGET_FIELD: 10 } base-call count: 1
+Test stopped on field modification
+Single call with PopFrame on FieldBasedTestObject { cnt: 0, TARGET_FIELD: 0 } base-call-count: 0
+result is FieldBasedTestObject { cnt: 2, TARGET_FIELD: 10 } base-call count: 1
+Test stopped during Method Exit of doNothing
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during Method Enter of doNothing
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during Method Exit of calledFunction
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped during Method Enter of calledFunction
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during Method Exit due to exception thrown in same function
+Single call with PopFrame on ExceptionOnceObject { cnt: 0, throwInSub: false } base-call-count: 0
+result is ExceptionOnceObject { cnt: 2, throwInSub: false } base-call count: 1
+Test stopped during Method Exit due to exception thrown in subroutine
+Single call with PopFrame on ExceptionOnceObject { cnt: 0, throwInSub: true } base-call-count: 0
+result is ExceptionOnceObject { cnt: 2, throwInSub: true } base-call count: 1
+Test stopped during notifyFramePop without exception on pop of calledFunction
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped during notifyFramePop without exception on pop of doNothing
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during notifyFramePop with exception on pop of calledFunction
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError thrown and caught!
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during notifyFramePop with exception on pop of doThrow
+Single call with PopFrame on ExceptionCatchTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionCatchTestObject$TestError caught in called function.
+result is ExceptionCatchTestObject { cnt: 1 } base-call count: 1
+Test stopped during ExceptionCatch event of calledFunction (catch in called function, throw in called function)
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError caught in same function.
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during ExceptionCatch event of calledFunction (catch in called function, throw in subroutine)
+Single call with PopFrame on ExceptionCatchTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionCatchTestObject$TestError caught in called function.
+result is ExceptionCatchTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in calling function)
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError thrown and caught!
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in called function)
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError caught in same function.
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in parent of calling function)
+Single call with PopFrame on ExceptionThrowFarTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowFarTestObject$TestError thrown and caught!
+result is ExceptionThrowFarTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in called function)
+Single call with PopFrame on ExceptionThrowFarTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowFarTestObject$TestError caught in same function.
+result is ExceptionThrowFarTestObject { cnt: 2 } base-call count: 1
+Test stopped during a ClassLoad event.
+Single call with PopFrame on ClassLoadObject { cnt: 0, curClass: 0} base-call-count: 0
+Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.Test1953.popFrame(Native Method)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTests(Test1953.java)
+ <Additional frames hidden>
+TC0.foo == 1
+result is ClassLoadObject { cnt: 1, curClass: 1} base-call count: 1
+Test stopped during a ClassPrepare event.
+Single call with PopFrame on ClassLoadObject { cnt: 0, curClass: 1} base-call-count: 0
+Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.Test1953.popFrame(Native Method)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTests(Test1953.java)
+ <Additional frames hidden>
+TC1.foo == 2
+result is ClassLoadObject { cnt: 1, curClass: 2} base-call count: 1
+Test stopped during random Suspend.
+Single call with PopFrame on SuspendSuddenlyObject { cnt: 0 } base-call-count: 0
+result is SuspendSuddenlyObject { cnt: 2 } base-call count: 1
+Test redefining frame being popped.
+Single call with PopFrame on RedefineTestObject { states: [] current: ORIGINAL } base-call-count: 0
+result is RedefineTestObject { states: [ORIGINAL, REDEFINED] current: REDEFINED } base-call count: 1
+Test stopped during a native method fails
+Single call with PopFrame on NativeCalledObject { cnt: 0 } base-call-count: 0
+Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.Test1953.popFrame(Native Method)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTests(Test1953.java)
+ <Additional frames hidden>
+result is NativeCalledObject { cnt: 1 } base-call count: 1
+Test stopped in a method called by native fails
+Single call with PopFrame on NativeCallerObject { cnt: 0 } base-call-count: 0
+Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.Test1953.popFrame(Native Method)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTests(Test1953.java)
+ <Additional frames hidden>
+result is NativeCallerObject { cnt: 1 } base-call count: 1
+Test stopped with monitor in enclosing frame.
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
diff --git a/test/1953-pop-frame/run b/test/1953-pop-frame/run
deleted file mode 100755
index d16d4e6..0000000
--- a/test/1953-pop-frame/run
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/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.
-
-# On RI we need to turn class-load tests off since those events are buggy around
-# pop-frame (see b/116003018).
-ARGS=""
-if [[ "$TEST_RUNTIME" == "jvm" ]]; then
- ARGS="--args DISABLE_CLASS_LOAD_TESTS"
-fi
-
-./default-run "$@" --jvmti $ARGS
diff --git a/test/1953-pop-frame/run.py b/test/1953-pop-frame/run.py
new file mode 100644
index 0000000..81e237f
--- /dev/null
+++ b/test/1953-pop-frame/run.py
@@ -0,0 +1,30 @@
+#!/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.
+
+
+def run(ctx, args):
+ # On RI we need to turn class-load tests off since those events are buggy around
+ # pop-frame (see b/116003018).
+ test_args = ["DISABLE_CLASS_LOAD_TESTS"] if args.jvm else []
+
+ ctx.default_run(args, jvmti=True, test_args=test_args)
+
+ # The RI has restrictions and bugs around some PopFrame behavior that ART lacks.
+ # See b/116003018. Some configurations cannot handle the class load events in
+ # quite the right way so they are disabled there too.
+ if not (args.jvm or args.verify_soft_fail or not args.prebuild or
+ (args.jvmti_redefine_stress and args.host)):
+ ctx.expected_stdout = ctx.expected_stdout.with_suffix(".no-jvm.txt")
diff --git a/test/1954-pop-frame-jit/check b/test/1954-pop-frame-jit/check
deleted file mode 100755
index e25838a..0000000
--- a/test/1954-pop-frame-jit/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# The RI has restrictions and bugs around some PopFrame behavior that ART lacks.
-# See b/116003018. Some configurations cannot handle the class load events in
-# quite the right way so they are disabled there too.
-./default-check "$@" || \
- (patch -p0 expected-stdout.txt < jvm-expected.patch >/dev/null && ./default-check "$@")
diff --git a/test/1954-pop-frame-jit/expected-stdout.jvm.txt b/test/1954-pop-frame-jit/expected-stdout.jvm.txt
new file mode 100644
index 0000000..dafc6b4
--- /dev/null
+++ b/test/1954-pop-frame-jit/expected-stdout.jvm.txt
@@ -0,0 +1,87 @@
+Test stopped using breakpoint
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped using breakpoint with declared synchronized function
+Single call with PopFrame on SynchronizedFunctionTestObject { cnt: 0 } base-call-count: 0
+result is SynchronizedFunctionTestObject { cnt: 2 } base-call count: 1
+Test stopped using breakpoint with synchronized block
+Single call with PopFrame on SynchronizedTestObject { cnt: 0 } base-call-count: 0
+result is SynchronizedTestObject { cnt: 2 } base-call count: 1
+Test stopped on single step
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped on field access
+Single call with PopFrame on FieldBasedTestObject { cnt: 0, TARGET_FIELD: 0 } base-call-count: 0
+result is FieldBasedTestObject { cnt: 2, TARGET_FIELD: 10 } base-call count: 1
+Test stopped on field modification
+Single call with PopFrame on FieldBasedTestObject { cnt: 0, TARGET_FIELD: 0 } base-call-count: 0
+result is FieldBasedTestObject { cnt: 2, TARGET_FIELD: 10 } base-call count: 1
+Test stopped during Method Exit of doNothing
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during Method Enter of doNothing
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during Method Exit of calledFunction
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped during Method Enter of calledFunction
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during Method Exit due to exception thrown in same function
+Single call with PopFrame on ExceptionOnceObject { cnt: 0, throwInSub: false } base-call-count: 0
+result is ExceptionOnceObject { cnt: 2, throwInSub: false } base-call count: 1
+Test stopped during Method Exit due to exception thrown in subroutine
+Single call with PopFrame on ExceptionOnceObject { cnt: 0, throwInSub: true } base-call-count: 0
+result is ExceptionOnceObject { cnt: 2, throwInSub: true } base-call count: 1
+Test stopped during ExceptionCatch event of calledFunction (catch in called function, throw in called function)
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError caught in same function.
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during ExceptionCatch event of calledFunction (catch in called function, throw in subroutine)
+Single call with PopFrame on ExceptionCatchTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionCatchTestObject$TestError caught in called function.
+result is ExceptionCatchTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in calling function)
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError thrown and caught!
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in called function)
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError caught in same function.
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in parent of calling function)
+Single call with PopFrame on ExceptionThrowFarTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowFarTestObject$TestError thrown and caught!
+result is ExceptionThrowFarTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in called function)
+Single call with PopFrame on ExceptionThrowFarTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowFarTestObject$TestError caught in same function.
+result is ExceptionThrowFarTestObject { cnt: 2 } base-call count: 1
+Test stopped during random Suspend.
+Single call with PopFrame on SuspendSuddenlyObject { cnt: 0 } base-call-count: 0
+result is SuspendSuddenlyObject { cnt: 2 } base-call count: 1
+Test redefining frame being popped.
+Single call with PopFrame on RedefineTestObject { states: [] current: ORIGINAL } base-call-count: 0
+result is RedefineTestObject { states: [ORIGINAL, REDEFINED] current: REDEFINED } base-call count: 1
+Test stopped during a native method fails
+Single call with PopFrame on NativeCalledObject { cnt: 0 } base-call-count: 0
+Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.Test1953.popFrame(Native Method)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTests(Test1953.java)
+ <Additional frames hidden>
+result is NativeCalledObject { cnt: 1 } base-call count: 1
+Test stopped in a method called by native fails
+Single call with PopFrame on NativeCallerObject { cnt: 0 } base-call-count: 0
+Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.Test1953.popFrame(Native Method)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTests(Test1953.java)
+ <Additional frames hidden>
+result is NativeCallerObject { cnt: 1 } base-call count: 1
+Test stopped with monitor in enclosing frame.
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
diff --git a/test/1954-pop-frame-jit/jvm-expected.patch b/test/1954-pop-frame-jit/jvm-expected.patch
deleted file mode 100644
index 6539d56..0000000
--- a/test/1954-pop-frame-jit/jvm-expected.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-37,50d36
-< Test stopped during notifyFramePop without exception on pop of calledFunction
-< Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
-< result is StandardTestObject { cnt: 2 } base-call count: 1
-< Test stopped during notifyFramePop without exception on pop of doNothing
-< Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
-< result is StandardTestObject { cnt: 1 } base-call count: 1
-< Test stopped during notifyFramePop with exception on pop of calledFunction
-< Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
-< art.Test1953$ExceptionThrowTestObject$TestError thrown and caught!
-< result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
-< Test stopped during notifyFramePop with exception on pop of doThrow
-< Single call with PopFrame on ExceptionCatchTestObject { cnt: 0 } base-call-count: 0
-< art.Test1953$ExceptionCatchTestObject$TestError caught in called function.
-< result is ExceptionCatchTestObject { cnt: 1 } base-call count: 1
-75,94d60
-< Test stopped during a ClassLoad event.
-< Single call with PopFrame on ClassLoadObject { cnt: 0, curClass: 0} base-call-count: 0
-< Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
-< art.Test1953.popFrame(Native Method)
-< art.Test1953.runTestOn(Test1953.java)
-< art.Test1953.runTestOn(Test1953.java)
-< art.Test1953.runTests(Test1953.java)
-< <Additional frames hidden>
-< TC0.foo == 1
-< result is ClassLoadObject { cnt: 1, curClass: 1} base-call count: 1
-< Test stopped during a ClassPrepare event.
-< Single call with PopFrame on ClassLoadObject { cnt: 0, curClass: 1} base-call-count: 0
-< Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
-< art.Test1953.popFrame(Native Method)
-< art.Test1953.runTestOn(Test1953.java)
-< art.Test1953.runTestOn(Test1953.java)
-< art.Test1953.runTests(Test1953.java)
-< <Additional frames hidden>
-< TC1.foo == 2
-< result is ClassLoadObject { cnt: 1, curClass: 2} base-call count: 1
diff --git a/test/1954-pop-frame-jit/run b/test/1954-pop-frame-jit/run
deleted file mode 100755
index d16d4e6..0000000
--- a/test/1954-pop-frame-jit/run
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/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.
-
-# On RI we need to turn class-load tests off since those events are buggy around
-# pop-frame (see b/116003018).
-ARGS=""
-if [[ "$TEST_RUNTIME" == "jvm" ]]; then
- ARGS="--args DISABLE_CLASS_LOAD_TESTS"
-fi
-
-./default-run "$@" --jvmti $ARGS
diff --git a/test/1954-pop-frame-jit/run.py b/test/1954-pop-frame-jit/run.py
new file mode 100644
index 0000000..459b447
--- /dev/null
+++ b/test/1954-pop-frame-jit/run.py
@@ -0,0 +1,30 @@
+#!/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.
+
+
+def run(ctx, args):
+ # On RI we need to turn class-load tests off since those events are buggy around
+ # pop-frame (see b/116003018).
+ test_args = ["DISABLE_CLASS_LOAD_TESTS"] if args.jvm else []
+
+ ctx.default_run(args, jvmti=True, test_args=test_args)
+
+ # The RI has restrictions and bugs around some PopFrame behavior that ART lacks.
+ # See b/116003018. Some configurations cannot handle the class load events in
+ # quite the right way so they are disabled there too.
+ if (args.jvm or args.verify_soft_fail or not args.prebuild or
+ (args.jvmti_redefine_stress and args.host)):
+ ctx.expected_stdout = ctx.expected_stdout.with_suffix(".jvm.txt")
diff --git a/test/1955-pop-frame-jit-called/check b/test/1955-pop-frame-jit-called/check
deleted file mode 100755
index e25838a..0000000
--- a/test/1955-pop-frame-jit-called/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# The RI has restrictions and bugs around some PopFrame behavior that ART lacks.
-# See b/116003018. Some configurations cannot handle the class load events in
-# quite the right way so they are disabled there too.
-./default-check "$@" || \
- (patch -p0 expected-stdout.txt < jvm-expected.patch >/dev/null && ./default-check "$@")
diff --git a/test/1955-pop-frame-jit-called/expected-stdout.jvm.txt b/test/1955-pop-frame-jit-called/expected-stdout.jvm.txt
new file mode 100644
index 0000000..dafc6b4
--- /dev/null
+++ b/test/1955-pop-frame-jit-called/expected-stdout.jvm.txt
@@ -0,0 +1,87 @@
+Test stopped using breakpoint
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped using breakpoint with declared synchronized function
+Single call with PopFrame on SynchronizedFunctionTestObject { cnt: 0 } base-call-count: 0
+result is SynchronizedFunctionTestObject { cnt: 2 } base-call count: 1
+Test stopped using breakpoint with synchronized block
+Single call with PopFrame on SynchronizedTestObject { cnt: 0 } base-call-count: 0
+result is SynchronizedTestObject { cnt: 2 } base-call count: 1
+Test stopped on single step
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped on field access
+Single call with PopFrame on FieldBasedTestObject { cnt: 0, TARGET_FIELD: 0 } base-call-count: 0
+result is FieldBasedTestObject { cnt: 2, TARGET_FIELD: 10 } base-call count: 1
+Test stopped on field modification
+Single call with PopFrame on FieldBasedTestObject { cnt: 0, TARGET_FIELD: 0 } base-call-count: 0
+result is FieldBasedTestObject { cnt: 2, TARGET_FIELD: 10 } base-call count: 1
+Test stopped during Method Exit of doNothing
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during Method Enter of doNothing
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during Method Exit of calledFunction
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped during Method Enter of calledFunction
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during Method Exit due to exception thrown in same function
+Single call with PopFrame on ExceptionOnceObject { cnt: 0, throwInSub: false } base-call-count: 0
+result is ExceptionOnceObject { cnt: 2, throwInSub: false } base-call count: 1
+Test stopped during Method Exit due to exception thrown in subroutine
+Single call with PopFrame on ExceptionOnceObject { cnt: 0, throwInSub: true } base-call-count: 0
+result is ExceptionOnceObject { cnt: 2, throwInSub: true } base-call count: 1
+Test stopped during ExceptionCatch event of calledFunction (catch in called function, throw in called function)
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError caught in same function.
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during ExceptionCatch event of calledFunction (catch in called function, throw in subroutine)
+Single call with PopFrame on ExceptionCatchTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionCatchTestObject$TestError caught in called function.
+result is ExceptionCatchTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in calling function)
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError thrown and caught!
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in called function)
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError caught in same function.
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in parent of calling function)
+Single call with PopFrame on ExceptionThrowFarTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowFarTestObject$TestError thrown and caught!
+result is ExceptionThrowFarTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in called function)
+Single call with PopFrame on ExceptionThrowFarTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowFarTestObject$TestError caught in same function.
+result is ExceptionThrowFarTestObject { cnt: 2 } base-call count: 1
+Test stopped during random Suspend.
+Single call with PopFrame on SuspendSuddenlyObject { cnt: 0 } base-call-count: 0
+result is SuspendSuddenlyObject { cnt: 2 } base-call count: 1
+Test redefining frame being popped.
+Single call with PopFrame on RedefineTestObject { states: [] current: ORIGINAL } base-call-count: 0
+result is RedefineTestObject { states: [ORIGINAL, REDEFINED] current: REDEFINED } base-call count: 1
+Test stopped during a native method fails
+Single call with PopFrame on NativeCalledObject { cnt: 0 } base-call-count: 0
+Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.Test1953.popFrame(Native Method)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTests(Test1953.java)
+ <Additional frames hidden>
+result is NativeCalledObject { cnt: 1 } base-call count: 1
+Test stopped in a method called by native fails
+Single call with PopFrame on NativeCallerObject { cnt: 0 } base-call-count: 0
+Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.Test1953.popFrame(Native Method)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTests(Test1953.java)
+ <Additional frames hidden>
+result is NativeCallerObject { cnt: 1 } base-call count: 1
+Test stopped with monitor in enclosing frame.
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
diff --git a/test/1955-pop-frame-jit-called/jvm-expected.patch b/test/1955-pop-frame-jit-called/jvm-expected.patch
deleted file mode 100644
index 6539d56..0000000
--- a/test/1955-pop-frame-jit-called/jvm-expected.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-37,50d36
-< Test stopped during notifyFramePop without exception on pop of calledFunction
-< Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
-< result is StandardTestObject { cnt: 2 } base-call count: 1
-< Test stopped during notifyFramePop without exception on pop of doNothing
-< Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
-< result is StandardTestObject { cnt: 1 } base-call count: 1
-< Test stopped during notifyFramePop with exception on pop of calledFunction
-< Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
-< art.Test1953$ExceptionThrowTestObject$TestError thrown and caught!
-< result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
-< Test stopped during notifyFramePop with exception on pop of doThrow
-< Single call with PopFrame on ExceptionCatchTestObject { cnt: 0 } base-call-count: 0
-< art.Test1953$ExceptionCatchTestObject$TestError caught in called function.
-< result is ExceptionCatchTestObject { cnt: 1 } base-call count: 1
-75,94d60
-< Test stopped during a ClassLoad event.
-< Single call with PopFrame on ClassLoadObject { cnt: 0, curClass: 0} base-call-count: 0
-< Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
-< art.Test1953.popFrame(Native Method)
-< art.Test1953.runTestOn(Test1953.java)
-< art.Test1953.runTestOn(Test1953.java)
-< art.Test1953.runTests(Test1953.java)
-< <Additional frames hidden>
-< TC0.foo == 1
-< result is ClassLoadObject { cnt: 1, curClass: 1} base-call count: 1
-< Test stopped during a ClassPrepare event.
-< Single call with PopFrame on ClassLoadObject { cnt: 0, curClass: 1} base-call-count: 0
-< Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
-< art.Test1953.popFrame(Native Method)
-< art.Test1953.runTestOn(Test1953.java)
-< art.Test1953.runTestOn(Test1953.java)
-< art.Test1953.runTests(Test1953.java)
-< <Additional frames hidden>
-< TC1.foo == 2
-< result is ClassLoadObject { cnt: 1, curClass: 2} base-call count: 1
diff --git a/test/1955-pop-frame-jit-called/run b/test/1955-pop-frame-jit-called/run
deleted file mode 100755
index 2984461..0000000
--- a/test/1955-pop-frame-jit-called/run
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/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.
-
-# On RI we need to turn class-load tests off since those events are buggy around
-# pop-frame (see b/116003018).
-ARGS=""
-if [[ "$TEST_RUNTIME" == "jvm" ]]; then
- ARGS="--args DISABLE_CLASS_LOAD_TESTS"
-fi
-
-# The jitthreshold prevents the jit from compiling anything except those which
-# we explicitly request.
-./default-run "$@" --android-runtime-option -Xjitthreshold:1000 --jvmti $ARGS
diff --git a/test/1955-pop-frame-jit-called/run.py b/test/1955-pop-frame-jit-called/run.py
new file mode 100644
index 0000000..a43b17d
--- /dev/null
+++ b/test/1955-pop-frame-jit-called/run.py
@@ -0,0 +1,36 @@
+#!/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.
+
+
+def run(ctx, args):
+ # On RI we need to turn class-load tests off since those events are buggy around
+ # pop-frame (see b/116003018).
+ test_args = ["DISABLE_CLASS_LOAD_TESTS"] if args.jvm else []
+
+ # The jitthreshold prevents the jit from compiling anything except those which
+ # we explicitly request.
+ ctx.default_run(
+ args,
+ android_runtime_option=["-Xjitthreshold:1000"],
+ jvmti=True,
+ test_args=test_args)
+
+ # The RI has restrictions and bugs around some PopFrame behavior that ART lacks.
+ # See b/116003018. Some configurations cannot handle the class load events in
+ # quite the right way so they are disabled there too.
+ if (args.jvm or args.verify_soft_fail or not args.prebuild or
+ (args.jvmti_redefine_stress and args.host)):
+ ctx.expected_stdout = ctx.expected_stdout.with_suffix(".jvm.txt")
diff --git a/test/1956-pop-frame-jit-calling/check b/test/1956-pop-frame-jit-calling/check
deleted file mode 100755
index e25838a..0000000
--- a/test/1956-pop-frame-jit-calling/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# The RI has restrictions and bugs around some PopFrame behavior that ART lacks.
-# See b/116003018. Some configurations cannot handle the class load events in
-# quite the right way so they are disabled there too.
-./default-check "$@" || \
- (patch -p0 expected-stdout.txt < jvm-expected.patch >/dev/null && ./default-check "$@")
diff --git a/test/1956-pop-frame-jit-calling/expected-stdout.jvm.txt b/test/1956-pop-frame-jit-calling/expected-stdout.jvm.txt
new file mode 100644
index 0000000..dafc6b4
--- /dev/null
+++ b/test/1956-pop-frame-jit-calling/expected-stdout.jvm.txt
@@ -0,0 +1,87 @@
+Test stopped using breakpoint
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped using breakpoint with declared synchronized function
+Single call with PopFrame on SynchronizedFunctionTestObject { cnt: 0 } base-call-count: 0
+result is SynchronizedFunctionTestObject { cnt: 2 } base-call count: 1
+Test stopped using breakpoint with synchronized block
+Single call with PopFrame on SynchronizedTestObject { cnt: 0 } base-call-count: 0
+result is SynchronizedTestObject { cnt: 2 } base-call count: 1
+Test stopped on single step
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped on field access
+Single call with PopFrame on FieldBasedTestObject { cnt: 0, TARGET_FIELD: 0 } base-call-count: 0
+result is FieldBasedTestObject { cnt: 2, TARGET_FIELD: 10 } base-call count: 1
+Test stopped on field modification
+Single call with PopFrame on FieldBasedTestObject { cnt: 0, TARGET_FIELD: 0 } base-call-count: 0
+result is FieldBasedTestObject { cnt: 2, TARGET_FIELD: 10 } base-call count: 1
+Test stopped during Method Exit of doNothing
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during Method Enter of doNothing
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during Method Exit of calledFunction
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
+Test stopped during Method Enter of calledFunction
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 1 } base-call count: 1
+Test stopped during Method Exit due to exception thrown in same function
+Single call with PopFrame on ExceptionOnceObject { cnt: 0, throwInSub: false } base-call-count: 0
+result is ExceptionOnceObject { cnt: 2, throwInSub: false } base-call count: 1
+Test stopped during Method Exit due to exception thrown in subroutine
+Single call with PopFrame on ExceptionOnceObject { cnt: 0, throwInSub: true } base-call-count: 0
+result is ExceptionOnceObject { cnt: 2, throwInSub: true } base-call count: 1
+Test stopped during ExceptionCatch event of calledFunction (catch in called function, throw in called function)
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError caught in same function.
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during ExceptionCatch event of calledFunction (catch in called function, throw in subroutine)
+Single call with PopFrame on ExceptionCatchTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionCatchTestObject$TestError caught in called function.
+result is ExceptionCatchTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in calling function)
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError thrown and caught!
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in called function)
+Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowTestObject$TestError caught in same function.
+result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in parent of calling function)
+Single call with PopFrame on ExceptionThrowFarTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowFarTestObject$TestError thrown and caught!
+result is ExceptionThrowFarTestObject { cnt: 2 } base-call count: 1
+Test stopped during Exception event of calledFunction (catch in called function)
+Single call with PopFrame on ExceptionThrowFarTestObject { cnt: 0 } base-call-count: 0
+art.Test1953$ExceptionThrowFarTestObject$TestError caught in same function.
+result is ExceptionThrowFarTestObject { cnt: 2 } base-call count: 1
+Test stopped during random Suspend.
+Single call with PopFrame on SuspendSuddenlyObject { cnt: 0 } base-call-count: 0
+result is SuspendSuddenlyObject { cnt: 2 } base-call count: 1
+Test redefining frame being popped.
+Single call with PopFrame on RedefineTestObject { states: [] current: ORIGINAL } base-call-count: 0
+result is RedefineTestObject { states: [ORIGINAL, REDEFINED] current: REDEFINED } base-call count: 1
+Test stopped during a native method fails
+Single call with PopFrame on NativeCalledObject { cnt: 0 } base-call-count: 0
+Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.Test1953.popFrame(Native Method)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTests(Test1953.java)
+ <Additional frames hidden>
+result is NativeCalledObject { cnt: 1 } base-call count: 1
+Test stopped in a method called by native fails
+Single call with PopFrame on NativeCallerObject { cnt: 0 } base-call-count: 0
+Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.Test1953.popFrame(Native Method)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTestOn(Test1953.java)
+ art.Test1953.runTests(Test1953.java)
+ <Additional frames hidden>
+result is NativeCallerObject { cnt: 1 } base-call count: 1
+Test stopped with monitor in enclosing frame.
+Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
+result is StandardTestObject { cnt: 2 } base-call count: 1
diff --git a/test/1956-pop-frame-jit-calling/jvm-expected.patch b/test/1956-pop-frame-jit-calling/jvm-expected.patch
deleted file mode 100644
index 6539d56..0000000
--- a/test/1956-pop-frame-jit-calling/jvm-expected.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-37,50d36
-< Test stopped during notifyFramePop without exception on pop of calledFunction
-< Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
-< result is StandardTestObject { cnt: 2 } base-call count: 1
-< Test stopped during notifyFramePop without exception on pop of doNothing
-< Single call with PopFrame on StandardTestObject { cnt: 0 } base-call-count: 0
-< result is StandardTestObject { cnt: 1 } base-call count: 1
-< Test stopped during notifyFramePop with exception on pop of calledFunction
-< Single call with PopFrame on ExceptionThrowTestObject { cnt: 0 } base-call-count: 0
-< art.Test1953$ExceptionThrowTestObject$TestError thrown and caught!
-< result is ExceptionThrowTestObject { cnt: 2 } base-call count: 1
-< Test stopped during notifyFramePop with exception on pop of doThrow
-< Single call with PopFrame on ExceptionCatchTestObject { cnt: 0 } base-call-count: 0
-< art.Test1953$ExceptionCatchTestObject$TestError caught in called function.
-< result is ExceptionCatchTestObject { cnt: 1 } base-call count: 1
-75,94d60
-< Test stopped during a ClassLoad event.
-< Single call with PopFrame on ClassLoadObject { cnt: 0, curClass: 0} base-call-count: 0
-< Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
-< art.Test1953.popFrame(Native Method)
-< art.Test1953.runTestOn(Test1953.java)
-< art.Test1953.runTestOn(Test1953.java)
-< art.Test1953.runTests(Test1953.java)
-< <Additional frames hidden>
-< TC0.foo == 1
-< result is ClassLoadObject { cnt: 1, curClass: 1} base-call count: 1
-< Test stopped during a ClassPrepare event.
-< Single call with PopFrame on ClassLoadObject { cnt: 0, curClass: 1} base-call-count: 0
-< Failed to pop frame due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
-< art.Test1953.popFrame(Native Method)
-< art.Test1953.runTestOn(Test1953.java)
-< art.Test1953.runTestOn(Test1953.java)
-< art.Test1953.runTests(Test1953.java)
-< <Additional frames hidden>
-< TC1.foo == 2
-< result is ClassLoadObject { cnt: 1, curClass: 2} base-call count: 1
diff --git a/test/1956-pop-frame-jit-calling/run b/test/1956-pop-frame-jit-calling/run
deleted file mode 100755
index 2984461..0000000
--- a/test/1956-pop-frame-jit-calling/run
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/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.
-
-# On RI we need to turn class-load tests off since those events are buggy around
-# pop-frame (see b/116003018).
-ARGS=""
-if [[ "$TEST_RUNTIME" == "jvm" ]]; then
- ARGS="--args DISABLE_CLASS_LOAD_TESTS"
-fi
-
-# The jitthreshold prevents the jit from compiling anything except those which
-# we explicitly request.
-./default-run "$@" --android-runtime-option -Xjitthreshold:1000 --jvmti $ARGS
diff --git a/test/1956-pop-frame-jit-calling/run.py b/test/1956-pop-frame-jit-calling/run.py
new file mode 100644
index 0000000..a43b17d
--- /dev/null
+++ b/test/1956-pop-frame-jit-calling/run.py
@@ -0,0 +1,36 @@
+#!/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.
+
+
+def run(ctx, args):
+ # On RI we need to turn class-load tests off since those events are buggy around
+ # pop-frame (see b/116003018).
+ test_args = ["DISABLE_CLASS_LOAD_TESTS"] if args.jvm else []
+
+ # The jitthreshold prevents the jit from compiling anything except those which
+ # we explicitly request.
+ ctx.default_run(
+ args,
+ android_runtime_option=["-Xjitthreshold:1000"],
+ jvmti=True,
+ test_args=test_args)
+
+ # The RI has restrictions and bugs around some PopFrame behavior that ART lacks.
+ # See b/116003018. Some configurations cannot handle the class load events in
+ # quite the right way so they are disabled there too.
+ if (args.jvm or args.verify_soft_fail or not args.prebuild or
+ (args.jvmti_redefine_stress and args.host)):
+ ctx.expected_stdout = ctx.expected_stdout.with_suffix(".jvm.txt")
diff --git a/test/1957-error-ext/run b/test/1957-error-ext/run
deleted file mode 100755
index 8be0ed4..0000000
--- a/test/1957-error-ext/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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/1957-error-ext/run.py b/test/1957-error-ext/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1957-error-ext/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1958-transform-try-jit/run b/test/1958-transform-try-jit/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1958-transform-try-jit/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1958-transform-try-jit/run.py b/test/1958-transform-try-jit/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1958-transform-try-jit/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1959-redefine-object-instrument/run b/test/1959-redefine-object-instrument/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1959-redefine-object-instrument/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1959-redefine-object-instrument/run.py b/test/1959-redefine-object-instrument/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1959-redefine-object-instrument/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1960-obsolete-jit-multithread-native/run b/test/1960-obsolete-jit-multithread-native/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1960-obsolete-jit-multithread-native/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1960-obsolete-jit-multithread-native/run.py b/test/1960-obsolete-jit-multithread-native/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1960-obsolete-jit-multithread-native/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1961-obsolete-jit-multithread/run b/test/1961-obsolete-jit-multithread/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1961-obsolete-jit-multithread/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1961-obsolete-jit-multithread/run.py b/test/1961-obsolete-jit-multithread/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1961-obsolete-jit-multithread/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1962-multi-thread-events/run b/test/1962-multi-thread-events/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1962-multi-thread-events/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1962-multi-thread-events/run.py b/test/1962-multi-thread-events/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1962-multi-thread-events/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1963-add-to-dex-classloader-in-memory/check b/test/1963-add-to-dex-classloader-in-memory/check
deleted file mode 100755
index d536891..0000000
--- a/test/1963-add-to-dex-classloader-in-memory/check
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Some of our test devices are so old that they don't have memfd_create and are setup in such a way
-# that tmpfile() doesn't work. In these cases this test cannot complete successfully.
-
-if grep -q -- '---NO memfd_create---' $@; then
- echo "The test device doesn't have memfd_create. Cannot verify test!" >&2
- exit 0
-fi
-
-
-./default-check "$@"
diff --git a/test/1963-add-to-dex-classloader-in-memory/run b/test/1963-add-to-dex-classloader-in-memory/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1963-add-to-dex-classloader-in-memory/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1963-add-to-dex-classloader-in-memory/run.py b/test/1963-add-to-dex-classloader-in-memory/run.py
new file mode 100644
index 0000000..ad7c1ae
--- /dev/null
+++ b/test/1963-add-to-dex-classloader-in-memory/run.py
@@ -0,0 +1,27 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
+
+ # Some of our test devices are so old that they don't have memfd_create and are setup in such a way
+ # that tmpfile() doesn't work. In these cases this test cannot complete successfully.
+ # If we see this in stdout, make the expected stdout identical.
+ ctx.run(
+ fr"grep -q -- '---NO memfd_create---' '{args.stdout_file}' &&"
+ fr" echo '---NO memfd_create---' > expected-stdout.txt",
+ check=False)
diff --git a/test/1964-add-to-dex-classloader-file/run b/test/1964-add-to-dex-classloader-file/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1964-add-to-dex-classloader-file/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1964-add-to-dex-classloader-file/run.py b/test/1964-add-to-dex-classloader-file/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1964-add-to-dex-classloader-file/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1965-get-set-local-primitive-no-tables/build b/test/1965-get-set-local-primitive-no-tables/build
deleted file mode 100644
index 6631df9..0000000
--- a/test/1965-get-set-local-primitive-no-tables/build
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# make us exit on a failure
-set -e
-
-if [[ $@ != *"--jvm"* ]]; then
- mv jasmin jasmin-unused
-else
- mv smali smali-unused
-fi
-./default-build "$@"
diff --git a/test/1965-get-set-local-primitive-no-tables/build.py b/test/1965-get-set-local-primitive-no-tables/build.py
new file mode 100644
index 0000000..3a908c0
--- /dev/null
+++ b/test/1965-get-set-local-primitive-no-tables/build.py
@@ -0,0 +1,21 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+
+def build(ctx):
+ ctx.bash("./generate-sources --" + ctx.mode)
+ ctx.default_build()
diff --git a/test/1965-get-set-local-primitive-no-tables/generate-sources b/test/1965-get-set-local-primitive-no-tables/generate-sources
new file mode 100755
index 0000000..c2f8653
--- /dev/null
+++ b/test/1965-get-set-local-primitive-no-tables/generate-sources
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# 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.
+
+# make us exit on a failure
+set -e
+
+if [[ $@ != *"--jvm"* ]]; then
+ mv jasmin jasmin-unused
+else
+ mv smali smali-unused
+fi
diff --git a/test/1965-get-set-local-primitive-no-tables/run b/test/1965-get-set-local-primitive-no-tables/run
deleted file mode 100755
index 9b741ee..0000000
--- a/test/1965-get-set-local-primitive-no-tables/run
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# If we compile the .oat files non-debuggable we could end up with dex2dex running over the files
-# which will cause some instructions to be removed from smali/TestCases1966.smali. This test relies
-# on the instructions being exactly as written so pass --debuggable to 'dex2oat' only to prevent
-# this from happening.
-./default-run "$@" --jvmti --compiler-only-option --debuggable
diff --git a/test/1965-get-set-local-primitive-no-tables/run.py b/test/1965-get-set-local-primitive-no-tables/run.py
new file mode 100644
index 0000000..77a45e9
--- /dev/null
+++ b/test/1965-get-set-local-primitive-no-tables/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # If we compile the .oat files non-debuggable we could end up with dex2dex running over the files
+ # which will cause some instructions to be removed from smali/TestCases1966.smali. This test relies
+ # on the instructions being exactly as written so pass --debuggable to 'dex2oat' only to prevent
+ # this from happening.
+ ctx.default_run(args, jvmti=True, compiler_only_option=["--debuggable"])
diff --git a/test/1966-get-set-local-objects-no-table/build b/test/1966-get-set-local-objects-no-table/build
deleted file mode 100644
index 6631df9..0000000
--- a/test/1966-get-set-local-objects-no-table/build
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# make us exit on a failure
-set -e
-
-if [[ $@ != *"--jvm"* ]]; then
- mv jasmin jasmin-unused
-else
- mv smali smali-unused
-fi
-./default-build "$@"
diff --git a/test/1966-get-set-local-objects-no-table/build.py b/test/1966-get-set-local-objects-no-table/build.py
new file mode 100644
index 0000000..3a908c0
--- /dev/null
+++ b/test/1966-get-set-local-objects-no-table/build.py
@@ -0,0 +1,21 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+
+def build(ctx):
+ ctx.bash("./generate-sources --" + ctx.mode)
+ ctx.default_build()
diff --git a/test/1966-get-set-local-objects-no-table/generate-sources b/test/1966-get-set-local-objects-no-table/generate-sources
new file mode 100755
index 0000000..c2f8653
--- /dev/null
+++ b/test/1966-get-set-local-objects-no-table/generate-sources
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# 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.
+
+# make us exit on a failure
+set -e
+
+if [[ $@ != *"--jvm"* ]]; then
+ mv jasmin jasmin-unused
+else
+ mv smali smali-unused
+fi
diff --git a/test/1966-get-set-local-objects-no-table/run b/test/1966-get-set-local-objects-no-table/run
deleted file mode 100755
index 9b741ee..0000000
--- a/test/1966-get-set-local-objects-no-table/run
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# If we compile the .oat files non-debuggable we could end up with dex2dex running over the files
-# which will cause some instructions to be removed from smali/TestCases1966.smali. This test relies
-# on the instructions being exactly as written so pass --debuggable to 'dex2oat' only to prevent
-# this from happening.
-./default-run "$@" --jvmti --compiler-only-option --debuggable
diff --git a/test/1966-get-set-local-objects-no-table/run.py b/test/1966-get-set-local-objects-no-table/run.py
new file mode 100644
index 0000000..77a45e9
--- /dev/null
+++ b/test/1966-get-set-local-objects-no-table/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # If we compile the .oat files non-debuggable we could end up with dex2dex running over the files
+ # which will cause some instructions to be removed from smali/TestCases1966.smali. This test relies
+ # on the instructions being exactly as written so pass --debuggable to 'dex2oat' only to prevent
+ # this from happening.
+ ctx.default_run(args, jvmti=True, compiler_only_option=["--debuggable"])
diff --git a/test/1967-get-set-local-bad-slot/run b/test/1967-get-set-local-bad-slot/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/1967-get-set-local-bad-slot/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/1967-get-set-local-bad-slot/run.py b/test/1967-get-set-local-bad-slot/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/1967-get-set-local-bad-slot/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1968-force-early-return/run b/test/1968-force-early-return/run
deleted file mode 100755
index d16d4e6..0000000
--- a/test/1968-force-early-return/run
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/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.
-
-# On RI we need to turn class-load tests off since those events are buggy around
-# pop-frame (see b/116003018).
-ARGS=""
-if [[ "$TEST_RUNTIME" == "jvm" ]]; then
- ARGS="--args DISABLE_CLASS_LOAD_TESTS"
-fi
-
-./default-run "$@" --jvmti $ARGS
diff --git a/test/1968-force-early-return/run.py b/test/1968-force-early-return/run.py
new file mode 100644
index 0000000..fa08256
--- /dev/null
+++ b/test/1968-force-early-return/run.py
@@ -0,0 +1,23 @@
+#!/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.
+
+
+def run(ctx, args):
+ # On RI we need to turn class-load tests off since those events are buggy around
+ # pop-frame (see b/116003018).
+ test_args = ["DISABLE_CLASS_LOAD_TESTS"] if args.jvm else []
+
+ ctx.default_run(args, jvmti=True, test_args=test_args)
diff --git a/test/1969-force-early-return-void/check b/test/1969-force-early-return-void/check
deleted file mode 100755
index 24bbf07..0000000
--- a/test/1969-force-early-return-void/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# The RI has restrictions and bugs around some PopFrame behavior that ART lacks.
-# See b/116003018. Some configurations cannot handle the class load events in
-# quite the right way so they are disabled there too.
-./default-check "$@" || \
- (patch -p0 expected-stdout.txt < class-loading-expected.patch >/dev/null && ./default-check "$@")
diff --git a/test/1969-force-early-return-void/class-loading-expected.patch b/test/1969-force-early-return-void/class-loading-expected.patch
deleted file mode 100644
index 5e13595..0000000
--- a/test/1969-force-early-return-void/class-loading-expected.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-178a179,212
-> Test stopped during class-load.
-> NORMAL RUN: Single call with no interference on (ID: 46) ClassLoadObject { cnt: 0, curClass: 0}
-> TC0.foo == 100
-> NORMAL RUN: result for (ID: 46) ClassLoadObject { cnt: 1, curClass: 1} on Test1969 target thread - 46
-> Single call with force-early-return on (ID: 47) ClassLoadObject { cnt: 0, curClass: 1}
-> Will force return of Test1969 target thread - 47
-> Failed to force-return due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
-> art.NonStandardExit.forceEarlyReturnVoid(Native Method)
-> art.Test1969$TestSuspender.performForceReturn(Test1969.java)
-> art.Test1969.runTestOn(Test1969.java)
-> art.Test1969.runTestOn(Test1969.java)
-> art.Test1969.runTestOn(Test1969.java)
-> art.Test1969.runTests(Test1969.java)
-> <Additional frames hidden>
->
-> TC1.foo == 201
-> result for (ID: 47) ClassLoadObject { cnt: 1, curClass: 2} on Test1969 target thread - 47
-> Test stopped during class-load.
-> NORMAL RUN: Single call with no interference on (ID: 48) ClassLoadObject { cnt: 0, curClass: 2}
-> TC2.foo == 302
-> NORMAL RUN: result for (ID: 48) ClassLoadObject { cnt: 1, curClass: 3} on Test1969 target thread - 48
-> Single call with force-early-return on (ID: 49) ClassLoadObject { cnt: 0, curClass: 3}
-> Will force return of Test1969 target thread - 49
-> Failed to force-return due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
-> art.NonStandardExit.forceEarlyReturnVoid(Native Method)
-> art.Test1969$TestSuspender.performForceReturn(Test1969.java)
-> art.Test1969.runTestOn(Test1969.java)
-> art.Test1969.runTestOn(Test1969.java)
-> art.Test1969.runTestOn(Test1969.java)
-> art.Test1969.runTests(Test1969.java)
-> <Additional frames hidden>
->
-> TC3.foo == 403
-> result for (ID: 49) ClassLoadObject { cnt: 1, curClass: 4} on Test1969 target thread - 49
diff --git a/test/1969-force-early-return-void/expected-stdout.no-jvm.txt b/test/1969-force-early-return-void/expected-stdout.no-jvm.txt
new file mode 100644
index 0000000..1bb3252
--- /dev/null
+++ b/test/1969-force-early-return-void/expected-stdout.no-jvm.txt
@@ -0,0 +1,212 @@
+Test stopped using breakpoint
+NORMAL RUN: Single call with no interference on (ID: 0) StandardTestObject { cnt: 0 }
+NORMAL RUN: result for (ID: 0) StandardTestObject { cnt: 2 } on Test1969 target thread - 0
+Single call with force-early-return on (ID: 1) StandardTestObject { cnt: 0 }
+Will force return of Test1969 target thread - 1
+result for (ID: 1) StandardTestObject { cnt: 1 } on Test1969 target thread - 1
+Test stopped using breakpoint with declared synchronized function
+NORMAL RUN: Single call with no interference on (ID: 2) SynchronizedFunctionTestObject { cnt: 0 }
+NORMAL RUN: result for (ID: 2) SynchronizedFunctionTestObject { cnt: 2 } on Test1969 target thread - 2
+Single call with force-early-return on (ID: 3) SynchronizedFunctionTestObject { cnt: 0 }
+Will force return of Test1969 target thread - 3
+result for (ID: 3) SynchronizedFunctionTestObject { cnt: 1 } on Test1969 target thread - 3
+Test stopped using breakpoint with synchronized block
+NORMAL RUN: Single call with no interference on (ID: 4) SynchronizedTestObject { cnt: 0 }
+NORMAL RUN: result for (ID: 4) SynchronizedTestObject { cnt: 2 } on Test1969 target thread - 4
+Single call with force-early-return on (ID: 5) SynchronizedTestObject { cnt: 0 }
+Will force return of Test1969 target thread - 5
+result for (ID: 5) SynchronizedTestObject { cnt: 1 } on Test1969 target thread - 5
+Test stopped on single step
+NORMAL RUN: Single call with no interference on (ID: 6) StandardTestObject { cnt: 0 }
+NORMAL RUN: result for (ID: 6) StandardTestObject { cnt: 2 } on Test1969 target thread - 6
+Single call with force-early-return on (ID: 7) StandardTestObject { cnt: 0 }
+Will force return of Test1969 target thread - 7
+result for (ID: 7) StandardTestObject { cnt: 1 } on Test1969 target thread - 7
+Test stopped on field access
+NORMAL RUN: Single call with no interference on (ID: 8) FieldBasedTestObject { TARGET_FIELD: 0, cnt: 0 }
+NORMAL RUN: result for (ID: 8) FieldBasedTestObject { TARGET_FIELD: 10, cnt: 2 } on Test1969 target thread - 8
+Single call with force-early-return on (ID: 9) FieldBasedTestObject { TARGET_FIELD: 0, cnt: 0 }
+Will force return of Test1969 target thread - 9
+result for (ID: 9) FieldBasedTestObject { TARGET_FIELD: 0, cnt: 1 } on Test1969 target thread - 9
+Test stopped on field modification
+NORMAL RUN: Single call with no interference on (ID: 10) FieldBasedTestObject { TARGET_FIELD: 0, cnt: 0 }
+NORMAL RUN: result for (ID: 10) FieldBasedTestObject { TARGET_FIELD: 10, cnt: 2 } on Test1969 target thread - 10
+Single call with force-early-return on (ID: 11) FieldBasedTestObject { TARGET_FIELD: 0, cnt: 0 }
+Will force return of Test1969 target thread - 11
+result for (ID: 11) FieldBasedTestObject { TARGET_FIELD: 0, cnt: 1 } on Test1969 target thread - 11
+Test stopped during Method Exit of calledFunction
+NORMAL RUN: Single call with no interference on (ID: 12) StandardTestObject { cnt: 0 }
+NORMAL RUN: result for (ID: 12) StandardTestObject { cnt: 2 } on Test1969 target thread - 12
+Single call with force-early-return on (ID: 13) StandardTestObject { cnt: 0 }
+Will force return of Test1969 target thread - 13
+result for (ID: 13) StandardTestObject { cnt: 2 } on Test1969 target thread - 13
+Test stopped during Method Enter of calledFunction
+NORMAL RUN: Single call with no interference on (ID: 14) StandardTestObject { cnt: 0 }
+NORMAL RUN: result for (ID: 14) StandardTestObject { cnt: 2 } on Test1969 target thread - 14
+Single call with force-early-return on (ID: 15) StandardTestObject { cnt: 0 }
+Will force return of Test1969 target thread - 15
+result for (ID: 15) StandardTestObject { cnt: 0 } on Test1969 target thread - 15
+Test stopped during Method Exit due to exception thrown in same function
+NORMAL RUN: Single call with no interference on (ID: 16) ExceptionOnceObject { cnt: 0, throwInSub: false }
+Uncaught exception in thread Thread[Test1969 target thread - 16,5,main] - art.Test1969$ExceptionOnceObject$TestError: null
+ art.Test1969$ExceptionOnceObject.calledFunction(Test1969.java)
+ art.Test1969$AbstractTestObject.run(Test1969.java)
+ art.Test1969$2.run(Test1969.java)
+ java.lang.Thread.run(Thread.java)
+
+NORMAL RUN: result for (ID: 16) ExceptionOnceObject { cnt: 1, throwInSub: false } on Test1969 target thread - 16
+Single call with force-early-return on (ID: 17) ExceptionOnceObject { cnt: 0, throwInSub: false }
+Will force return of Test1969 target thread - 17
+result for (ID: 17) ExceptionOnceObject { cnt: 1, throwInSub: false } on Test1969 target thread - 17
+Test stopped during Method Exit due to exception thrown in subroutine
+NORMAL RUN: Single call with no interference on (ID: 18) ExceptionOnceObject { cnt: 0, throwInSub: true }
+Uncaught exception in thread Thread[Test1969 target thread - 18,5,main] - art.Test1969$ExceptionOnceObject$TestError: null
+ art.Test1969$ExceptionOnceObject.doThrow(Test1969.java)
+ art.Test1969$ExceptionOnceObject.calledFunction(Test1969.java)
+ art.Test1969$AbstractTestObject.run(Test1969.java)
+ art.Test1969$2.run(Test1969.java)
+ java.lang.Thread.run(Thread.java)
+
+NORMAL RUN: result for (ID: 18) ExceptionOnceObject { cnt: 1, throwInSub: true } on Test1969 target thread - 18
+Single call with force-early-return on (ID: 19) ExceptionOnceObject { cnt: 0, throwInSub: true }
+Will force return of Test1969 target thread - 19
+result for (ID: 19) ExceptionOnceObject { cnt: 1, throwInSub: true } on Test1969 target thread - 19
+Test stopped during notifyFramePop with exception on pop of calledFunction
+NORMAL RUN: Single call with no interference on (ID: 20) ExceptionThrowTestObject { cnt: 0, baseCnt: 0 }
+art.Test1969$ExceptionThrowTestObject$TestError thrown and caught!
+NORMAL RUN: result for (ID: 20) ExceptionThrowTestObject { cnt: 2, baseCnt: 2 } on Test1969 target thread - 20
+Single call with force-early-return on (ID: 21) ExceptionThrowTestObject { cnt: 0, baseCnt: 0 }
+Will force return of Test1969 target thread - 21
+result for (ID: 21) ExceptionThrowTestObject { cnt: 2, baseCnt: 2 } on Test1969 target thread - 21
+Test stopped during notifyFramePop with exception on pop of doThrow
+NORMAL RUN: Single call with no interference on (ID: 22) ExceptionCatchTestObject { cnt: 0 }
+art.Test1969$ExceptionCatchTestObject$TestError caught in called function.
+NORMAL RUN: result for (ID: 22) ExceptionCatchTestObject { cnt: 2 } on Test1969 target thread - 22
+Single call with force-early-return on (ID: 23) ExceptionCatchTestObject { cnt: 0 }
+Will force return of Test1969 target thread - 23
+Failed to force-return due to java.lang.RuntimeException: JVMTI_ERROR_TYPE_MISMATCH
+ art.NonStandardExit.forceEarlyReturnVoid(Native Method)
+ art.Test1969$TestSuspender.performForceReturn(Test1969.java)
+ art.Test1969.runTestOn(Test1969.java)
+ art.Test1969.runTestOn(Test1969.java)
+ art.Test1969.runTestOn(Test1969.java)
+ art.Test1969.runTests(Test1969.java)
+ <Additional frames hidden>
+
+art.Test1969$ExceptionCatchTestObject$TestError caught in called function.
+result for (ID: 23) ExceptionCatchTestObject { cnt: 2 } on Test1969 target thread - 23
+Test stopped during ExceptionCatch event of calledFunction (catch in called function, throw in called function)
+NORMAL RUN: Single call with no interference on (ID: 24) ExceptionThrowTestObject { cnt: 0, baseCnt: 0 }
+art.Test1969$ExceptionThrowTestObject$TestError caught in same function.
+NORMAL RUN: result for (ID: 24) ExceptionThrowTestObject { cnt: 111, baseCnt: 2 } on Test1969 target thread - 24
+Single call with force-early-return on (ID: 25) ExceptionThrowTestObject { cnt: 0, baseCnt: 0 }
+Will force return of Test1969 target thread - 25
+result for (ID: 25) ExceptionThrowTestObject { cnt: 11, baseCnt: 2 } on Test1969 target thread - 25
+Test stopped during ExceptionCatch event of calledFunction (catch in called function, throw in subroutine)
+NORMAL RUN: Single call with no interference on (ID: 26) ExceptionCatchTestObject { cnt: 0 }
+art.Test1969$ExceptionCatchTestObject$TestError caught in called function.
+NORMAL RUN: result for (ID: 26) ExceptionCatchTestObject { cnt: 2 } on Test1969 target thread - 26
+Single call with force-early-return on (ID: 27) ExceptionCatchTestObject { cnt: 0 }
+Will force return of Test1969 target thread - 27
+result for (ID: 27) ExceptionCatchTestObject { cnt: 1 } on Test1969 target thread - 27
+Test stopped during Exception event of calledFunction (catch in calling function)
+NORMAL RUN: Single call with no interference on (ID: 28) ExceptionThrowTestObject { cnt: 0, baseCnt: 0 }
+art.Test1969$ExceptionThrowTestObject$TestError thrown and caught!
+NORMAL RUN: result for (ID: 28) ExceptionThrowTestObject { cnt: 2, baseCnt: 2 } on Test1969 target thread - 28
+Single call with force-early-return on (ID: 29) ExceptionThrowTestObject { cnt: 0, baseCnt: 0 }
+Will force return of Test1969 target thread - 29
+result for (ID: 29) ExceptionThrowTestObject { cnt: 2, baseCnt: 2 } on Test1969 target thread - 29
+Test stopped during Exception event of calledFunction (catch in called function)
+NORMAL RUN: Single call with no interference on (ID: 30) ExceptionThrowTestObject { cnt: 0, baseCnt: 0 }
+art.Test1969$ExceptionThrowTestObject$TestError caught in same function.
+NORMAL RUN: result for (ID: 30) ExceptionThrowTestObject { cnt: 111, baseCnt: 2 } on Test1969 target thread - 30
+Single call with force-early-return on (ID: 31) ExceptionThrowTestObject { cnt: 0, baseCnt: 0 }
+Will force return of Test1969 target thread - 31
+result for (ID: 31) ExceptionThrowTestObject { cnt: 11, baseCnt: 2 } on Test1969 target thread - 31
+Test stopped during Exception event of calledFunction (catch in parent of calling function)
+NORMAL RUN: Single call with no interference on (ID: 32) ExceptionThrowFarTestObject { cnt: 0, baseCnt: 0 }
+art.Test1969$ExceptionThrowFarTestObject$TestError thrown and caught!
+NORMAL RUN: result for (ID: 32) ExceptionThrowFarTestObject { cnt: 2, baseCnt: 2 } on Test1969 target thread - 32
+Single call with force-early-return on (ID: 33) ExceptionThrowFarTestObject { cnt: 0, baseCnt: 0 }
+Will force return of Test1969 target thread - 33
+result for (ID: 33) ExceptionThrowFarTestObject { cnt: 2, baseCnt: 2 } on Test1969 target thread - 33
+Test stopped during Exception event of calledFunction (catch in called function)
+NORMAL RUN: Single call with no interference on (ID: 34) ExceptionThrowFarTestObject { cnt: 0, baseCnt: 0 }
+art.Test1969$ExceptionThrowFarTestObject$TestError caught in same function.
+NORMAL RUN: result for (ID: 34) ExceptionThrowFarTestObject { cnt: 111, baseCnt: 2 } on Test1969 target thread - 34
+Single call with force-early-return on (ID: 35) ExceptionThrowFarTestObject { cnt: 0, baseCnt: 0 }
+Will force return of Test1969 target thread - 35
+result for (ID: 35) ExceptionThrowFarTestObject { cnt: 101, baseCnt: 2 } on Test1969 target thread - 35
+Test stopped during random Suspend.
+NORMAL RUN: Single call with no interference on (ID: 36) SuspendSuddenlyObject { cnt: 0, spun: false }
+NORMAL RUN: result for (ID: 36) SuspendSuddenlyObject { cnt: 2, spun: true } on Test1969 target thread - 36
+Single call with force-early-return on (ID: 37) SuspendSuddenlyObject { cnt: 0, spun: false }
+Will force return of Test1969 target thread - 37
+result for (ID: 37) SuspendSuddenlyObject { cnt: 1, spun: true } on Test1969 target thread - 37
+Test stopped during a native method fails
+NORMAL RUN: Single call with no interference on (ID: 38) NativeCalledObject { cnt: 0 }
+NORMAL RUN: result for (ID: 38) NativeCalledObject { cnt: 2 } on Test1969 target thread - 38
+Single call with force-early-return on (ID: 39) NativeCalledObject { cnt: 0 }
+Will force return of Test1969 target thread - 39
+Failed to force-return due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.NonStandardExit.forceEarlyReturnVoid(Native Method)
+ art.Test1969$TestSuspender.performForceReturn(Test1969.java)
+ art.Test1969.runTestOn(Test1969.java)
+ art.Test1969.runTestOn(Test1969.java)
+ art.Test1969.runTestOn(Test1969.java)
+ art.Test1969.runTests(Test1969.java)
+ <Additional frames hidden>
+
+result for (ID: 39) NativeCalledObject { cnt: 2 } on Test1969 target thread - 39
+Test stopped in a method called by native succeeds
+NORMAL RUN: Single call with no interference on (ID: 40) NativeCallerObject { cnt: 0 }
+NORMAL RUN: result for (ID: 40) NativeCallerObject { cnt: 2 } on Test1969 target thread - 40
+Single call with force-early-return on (ID: 41) NativeCallerObject { cnt: 0 }
+Will force return of Test1969 target thread - 41
+result for (ID: 41) NativeCallerObject { cnt: 2 } on Test1969 target thread - 41
+Test stopped in a static method
+NORMAL RUN: Single call with no interference on (ID: 42) StaticMethodObject { cnt: 0 }
+NORMAL RUN: result for (ID: 42) StaticMethodObject { cnt: 2 } on Test1969 target thread - 42
+Single call with force-early-return on (ID: 43) StaticMethodObject { cnt: 0 }
+Will force return of Test1969 target thread - 43
+result for (ID: 43) StaticMethodObject { cnt: 1 } on Test1969 target thread - 43
+Test stopped in a Object <init> method
+NORMAL RUN: Single call with no interference on (ID: 44) ObjectInitTestObject { cnt: 0 }
+NORMAL RUN: result for (ID: 44) ObjectInitTestObject { cnt: 2 } on Test1969 target thread - 44
+Single call with force-early-return on (ID: 45) ObjectInitTestObject { cnt: 0 }
+Will force return of Test1969 target thread - 45
+result for (ID: 45) ObjectInitTestObject { cnt: 1 } on Test1969 target thread - 45
+Test stopped during class-load.
+NORMAL RUN: Single call with no interference on (ID: 46) ClassLoadObject { cnt: 0, curClass: 0}
+TC0.foo == 100
+NORMAL RUN: result for (ID: 46) ClassLoadObject { cnt: 1, curClass: 1} on Test1969 target thread - 46
+Single call with force-early-return on (ID: 47) ClassLoadObject { cnt: 0, curClass: 1}
+Will force return of Test1969 target thread - 47
+Failed to force-return due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.NonStandardExit.forceEarlyReturnVoid(Native Method)
+ art.Test1969$TestSuspender.performForceReturn(Test1969.java)
+ art.Test1969.runTestOn(Test1969.java)
+ art.Test1969.runTestOn(Test1969.java)
+ art.Test1969.runTestOn(Test1969.java)
+ art.Test1969.runTests(Test1969.java)
+ <Additional frames hidden>
+
+TC1.foo == 201
+result for (ID: 47) ClassLoadObject { cnt: 1, curClass: 2} on Test1969 target thread - 47
+Test stopped during class-load.
+NORMAL RUN: Single call with no interference on (ID: 48) ClassLoadObject { cnt: 0, curClass: 2}
+TC2.foo == 302
+NORMAL RUN: result for (ID: 48) ClassLoadObject { cnt: 1, curClass: 3} on Test1969 target thread - 48
+Single call with force-early-return on (ID: 49) ClassLoadObject { cnt: 0, curClass: 3}
+Will force return of Test1969 target thread - 49
+Failed to force-return due to java.lang.RuntimeException: JVMTI_ERROR_OPAQUE_FRAME
+ art.NonStandardExit.forceEarlyReturnVoid(Native Method)
+ art.Test1969$TestSuspender.performForceReturn(Test1969.java)
+ art.Test1969.runTestOn(Test1969.java)
+ art.Test1969.runTestOn(Test1969.java)
+ art.Test1969.runTestOn(Test1969.java)
+ art.Test1969.runTests(Test1969.java)
+ <Additional frames hidden>
+
+TC3.foo == 403
+result for (ID: 49) ClassLoadObject { cnt: 1, curClass: 4} on Test1969 target thread - 49
diff --git a/test/1969-force-early-return-void/run b/test/1969-force-early-return-void/run
deleted file mode 100755
index e92b873..0000000
--- a/test/1969-force-early-return-void/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/1969-force-early-return-void/run.py b/test/1969-force-early-return-void/run.py
new file mode 100644
index 0000000..dda04df
--- /dev/null
+++ b/test/1969-force-early-return-void/run.py
@@ -0,0 +1,26 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
+
+ # The RI has restrictions and bugs around some PopFrame behavior that ART lacks.
+ # See b/116003018. Some configurations cannot handle the class load events in
+ # quite the right way so they are disabled there too.
+ if not (args.verify_soft_fail or not args.prebuild or
+ (args.jvmti_redefine_stress and args.host)):
+ ctx.expected_stdout = ctx.expected_stdout.with_suffix(".no-jvm.txt")
diff --git a/test/1970-force-early-return-long/run b/test/1970-force-early-return-long/run
deleted file mode 100755
index d16d4e6..0000000
--- a/test/1970-force-early-return-long/run
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/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.
-
-# On RI we need to turn class-load tests off since those events are buggy around
-# pop-frame (see b/116003018).
-ARGS=""
-if [[ "$TEST_RUNTIME" == "jvm" ]]; then
- ARGS="--args DISABLE_CLASS_LOAD_TESTS"
-fi
-
-./default-run "$@" --jvmti $ARGS
diff --git a/test/1970-force-early-return-long/run.py b/test/1970-force-early-return-long/run.py
new file mode 100644
index 0000000..fa08256
--- /dev/null
+++ b/test/1970-force-early-return-long/run.py
@@ -0,0 +1,23 @@
+#!/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.
+
+
+def run(ctx, args):
+ # On RI we need to turn class-load tests off since those events are buggy around
+ # pop-frame (see b/116003018).
+ test_args = ["DISABLE_CLASS_LOAD_TESTS"] if args.jvm else []
+
+ ctx.default_run(args, jvmti=True, test_args=test_args)
diff --git a/test/1971-multi-force-early-return/run b/test/1971-multi-force-early-return/run
deleted file mode 100755
index d16d4e6..0000000
--- a/test/1971-multi-force-early-return/run
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/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.
-
-# On RI we need to turn class-load tests off since those events are buggy around
-# pop-frame (see b/116003018).
-ARGS=""
-if [[ "$TEST_RUNTIME" == "jvm" ]]; then
- ARGS="--args DISABLE_CLASS_LOAD_TESTS"
-fi
-
-./default-run "$@" --jvmti $ARGS
diff --git a/test/1971-multi-force-early-return/run.py b/test/1971-multi-force-early-return/run.py
new file mode 100644
index 0000000..fa08256
--- /dev/null
+++ b/test/1971-multi-force-early-return/run.py
@@ -0,0 +1,23 @@
+#!/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.
+
+
+def run(ctx, args):
+ # On RI we need to turn class-load tests off since those events are buggy around
+ # pop-frame (see b/116003018).
+ test_args = ["DISABLE_CLASS_LOAD_TESTS"] if args.jvm else []
+
+ ctx.default_run(args, jvmti=True, test_args=test_args)
diff --git a/test/1972-jni-id-swap-indices/run b/test/1972-jni-id-swap-indices/run
deleted file mode 100755
index 999b92a..0000000
--- a/test/1972-jni-id-swap-indices/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/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.
-
-args=$(echo "$@" | sed 's/--runtime-option -Xopaque-jni-ids\:true//g')
-
-./default-run $args --android-runtime-option -Xopaque-jni-ids:swapable --android-runtime-option -Xauto-promote-opaque-jni-ids:false
\ No newline at end of file
diff --git a/test/1972-jni-id-swap-indices/run.py b/test/1972-jni-id-swap-indices/run.py
new file mode 100644
index 0000000..81ec061
--- /dev/null
+++ b/test/1972-jni-id-swap-indices/run.py
@@ -0,0 +1,28 @@
+#!/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.
+
+
+def run(ctx, args):
+ for i, opt in enumerate(args.runtime_option):
+ if opt == '-Xopaque-jni-ids:true':
+ args.runtime_option.pop(i)
+ break
+
+ ctx.default_run(
+ args,
+ android_runtime_option=[
+ '-Xopaque-jni-ids:swapable', '-Xauto-promote-opaque-jni-ids:false'
+ ])
diff --git a/test/1973-jni-id-swap-pointer/run b/test/1973-jni-id-swap-pointer/run
deleted file mode 100755
index 999b92a..0000000
--- a/test/1973-jni-id-swap-pointer/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/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.
-
-args=$(echo "$@" | sed 's/--runtime-option -Xopaque-jni-ids\:true//g')
-
-./default-run $args --android-runtime-option -Xopaque-jni-ids:swapable --android-runtime-option -Xauto-promote-opaque-jni-ids:false
\ No newline at end of file
diff --git a/test/1973-jni-id-swap-pointer/run.py b/test/1973-jni-id-swap-pointer/run.py
new file mode 100644
index 0000000..b7ed57a
--- /dev/null
+++ b/test/1973-jni-id-swap-pointer/run.py
@@ -0,0 +1,27 @@
+#!/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.
+
+
+def run(ctx, args):
+ for i, opt in enumerate(args.runtime_option):
+ if opt == '-Xopaque-jni-ids:true':
+ args.runtime_option.pop(i)
+
+ ctx.default_run(
+ args,
+ android_runtime_option=[
+ '-Xopaque-jni-ids:swapable', '-Xauto-promote-opaque-jni-ids:false'
+ ])
diff --git a/test/1974-resize-array/run b/test/1974-resize-array/run
deleted file mode 100755
index 96646c8..0000000
--- a/test/1974-resize-array/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-
-./default-run "$@" --jvmti
diff --git a/test/1974-resize-array/run.py b/test/1974-resize-array/run.py
new file mode 100644
index 0000000..4f5f922
--- /dev/null
+++ b/test/1974-resize-array/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1975-hello-structural-transformation/run b/test/1975-hello-structural-transformation/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1975-hello-structural-transformation/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1975-hello-structural-transformation/run.py b/test/1975-hello-structural-transformation/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1975-hello-structural-transformation/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1976-hello-structural-static-methods/run b/test/1976-hello-structural-static-methods/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1976-hello-structural-static-methods/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1976-hello-structural-static-methods/run.py b/test/1976-hello-structural-static-methods/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1976-hello-structural-static-methods/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1977-hello-structural-obsolescence/run b/test/1977-hello-structural-obsolescence/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1977-hello-structural-obsolescence/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1977-hello-structural-obsolescence/run.py b/test/1977-hello-structural-obsolescence/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1977-hello-structural-obsolescence/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1978-regular-obsolete-then-structural-obsolescence/run b/test/1978-regular-obsolete-then-structural-obsolescence/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1978-regular-obsolete-then-structural-obsolescence/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1978-regular-obsolete-then-structural-obsolescence/run.py b/test/1978-regular-obsolete-then-structural-obsolescence/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1978-regular-obsolete-then-structural-obsolescence/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1979-threaded-structural-transformation/run b/test/1979-threaded-structural-transformation/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1979-threaded-structural-transformation/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1979-threaded-structural-transformation/run.py b/test/1979-threaded-structural-transformation/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1979-threaded-structural-transformation/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1980-obsolete-object-cleared/expected-stdout.txt b/test/1980-obsolete-object-cleared/expected-stdout.txt
index 9d18b2c..43251f1 100644
--- a/test/1980-obsolete-object-cleared/expected-stdout.txt
+++ b/test/1980-obsolete-object-cleared/expected-stdout.txt
@@ -72,6 +72,13 @@
public native java.lang.annotation.Annotation java.lang.Class.getDeclaredAnnotation(java.lang.Class) with [class java.lang.Class] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
Calling public native java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotations() with params: []
public native java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotations() with [] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
+Calling public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with params: [[null, class java.lang.Object, (obsolete)class Main$Transform, class Main$Transform, long, class java.lang.Class]]
+public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with [null] throws java.lang.reflect.InvocationTargetException: java.lang.NullPointerException
+public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with [class java.lang.Object] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
+public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with [(obsolete)class Main$Transform] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
+public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with [class Main$Transform] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
+public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with [long] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
+public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with [class java.lang.Class] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
Calling public native java.lang.Class[] java.lang.Class.getDeclaredClasses() with params: []
public native java.lang.Class[] java.lang.Class.getDeclaredClasses() with [] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
Calling public java.lang.reflect.Constructor java.lang.Class.getDeclaredConstructor(java.lang.Class[]) throws java.lang.NoSuchMethodException,java.lang.SecurityException with params: [[new java.lang.Object[0], new java.lang.Class[0], null]]
@@ -154,10 +161,16 @@
public int java.lang.Class.getModifiers() with [] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
Calling public java.lang.String java.lang.Class.getName() with params: []
public java.lang.String java.lang.Class.getName() on (obsolete)class Main$Transform with [] = Main$Transform
+Calling public java.lang.Class java.lang.Class.getNestHost() with params: []
+public java.lang.Class java.lang.Class.getNestHost() with [] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
+Calling public java.lang.Class[] java.lang.Class.getNestMembers() with params: []
+public java.lang.Class[] java.lang.Class.getNestMembers() with [] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
Calling public java.lang.Package java.lang.Class.getPackage() with params: []
public java.lang.Package java.lang.Class.getPackage() on (obsolete)class Main$Transform with [] = null
Calling public java.lang.String java.lang.Class.getPackageName() with params: []
public java.lang.String java.lang.Class.getPackageName() on (obsolete)class Main$Transform with [] =
+Calling public java.lang.Class[] java.lang.Class.getPermittedSubclasses() with params: []
+public java.lang.Class[] java.lang.Class.getPermittedSubclasses() with [] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
Calling public java.security.ProtectionDomain java.lang.Class.getProtectionDomain() with params: []
public java.security.ProtectionDomain java.lang.Class.getProtectionDomain() on (obsolete)class Main$Transform with [] = null
Calling public java.net.URL java.lang.Class.getResource(java.lang.String) with params: [[NOT_USED_STRING, foo, SECRET_ARRAY]]
@@ -213,10 +226,19 @@
public boolean java.lang.Class.isLocalClass() with [] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
Calling public boolean java.lang.Class.isMemberClass() with params: []
public boolean java.lang.Class.isMemberClass() with [] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
+Calling public boolean java.lang.Class.isNestmateOf(java.lang.Class) with params: [[null, class java.lang.Object, (obsolete)class Main$Transform, class Main$Transform, long, class java.lang.Class]]
+public boolean java.lang.Class.isNestmateOf(java.lang.Class) with [null] throws java.lang.reflect.InvocationTargetException: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.Class.isPrimitive()' on a null object reference
+public boolean java.lang.Class.isNestmateOf(java.lang.Class) with [class java.lang.Object] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
+public boolean java.lang.Class.isNestmateOf(java.lang.Class) on (obsolete)class Main$Transform with [(obsolete)class Main$Transform] = true
+public boolean java.lang.Class.isNestmateOf(java.lang.Class) with [class Main$Transform] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
+public boolean java.lang.Class.isNestmateOf(java.lang.Class) on (obsolete)class Main$Transform with [long] = false
+public boolean java.lang.Class.isNestmateOf(java.lang.Class) with [class java.lang.Class] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
Calling public boolean java.lang.Class.isPrimitive() with params: []
public boolean java.lang.Class.isPrimitive() on (obsolete)class Main$Transform with [] = false
Calling public boolean java.lang.Class.isProxy() with params: []
public boolean java.lang.Class.isProxy() on (obsolete)class Main$Transform with [] = false
+Calling public boolean java.lang.Class.isSealed() with params: []
+public boolean java.lang.Class.isSealed() with [] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
Calling public boolean java.lang.Class.isSynthetic() with params: []
public boolean java.lang.Class.isSynthetic() with [] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
Calling public native java.lang.Object java.lang.Class.newInstance() throws java.lang.InstantiationException,java.lang.IllegalAccessException with params: []
@@ -285,6 +307,13 @@
public native java.lang.annotation.Annotation java.lang.Class.getDeclaredAnnotation(java.lang.Class) on class Main$Transform with [class java.lang.Class] = null
Calling public native java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotations() with params: []
public native java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotations() on class Main$Transform with [] = []
+Calling public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with params: [[null, class java.lang.Object, (obsolete)class Main$Transform, class Main$Transform, long, class java.lang.Class]]
+public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with [null] throws java.lang.reflect.InvocationTargetException: java.lang.NullPointerException
+public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with [class java.lang.Object] throws java.lang.reflect.InvocationTargetException: java.lang.ClassCastException: java.lang.Object[] cannot be cast to java.lang.annotation.Annotation[]
+public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with [(obsolete)class Main$Transform] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
+public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with [class Main$Transform] throws java.lang.reflect.InvocationTargetException: java.lang.ClassCastException: Main$Transform[] cannot be cast to java.lang.annotation.Annotation[]
+public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with [long] throws java.lang.reflect.InvocationTargetException: java.lang.ClassCastException: long[] cannot be cast to java.lang.annotation.Annotation[]
+public java.lang.annotation.Annotation[] java.lang.Class.getDeclaredAnnotationsByType(java.lang.Class) with [class java.lang.Class] throws java.lang.reflect.InvocationTargetException: java.lang.ClassCastException: java.lang.Class[] cannot be cast to java.lang.annotation.Annotation[]
Calling public native java.lang.Class[] java.lang.Class.getDeclaredClasses() with params: []
public native java.lang.Class[] java.lang.Class.getDeclaredClasses() on class Main$Transform with [] = []
Calling public java.lang.reflect.Constructor java.lang.Class.getDeclaredConstructor(java.lang.Class[]) throws java.lang.NoSuchMethodException,java.lang.SecurityException with params: [[new java.lang.Object[0], new java.lang.Class[0], null]]
@@ -367,10 +396,16 @@
public int java.lang.Class.getModifiers() on class Main$Transform with [] = 9
Calling public java.lang.String java.lang.Class.getName() with params: []
public java.lang.String java.lang.Class.getName() on class Main$Transform with [] = Main$Transform
+Calling public java.lang.Class java.lang.Class.getNestHost() with params: []
+public java.lang.Class java.lang.Class.getNestHost() on class Main$Transform with [] = class Main$Transform
+Calling public java.lang.Class[] java.lang.Class.getNestMembers() with params: []
+public java.lang.Class[] java.lang.Class.getNestMembers() on class Main$Transform with [] = [class Main$Transform]
Calling public java.lang.Package java.lang.Class.getPackage() with params: []
public java.lang.Package java.lang.Class.getPackage() on class Main$Transform with [] = null
Calling public java.lang.String java.lang.Class.getPackageName() with params: []
public java.lang.String java.lang.Class.getPackageName() on class Main$Transform with [] =
+Calling public java.lang.Class[] java.lang.Class.getPermittedSubclasses() with params: []
+public java.lang.Class[] java.lang.Class.getPermittedSubclasses() on class Main$Transform with [] = null
Calling public java.security.ProtectionDomain java.lang.Class.getProtectionDomain() with params: []
public java.security.ProtectionDomain java.lang.Class.getProtectionDomain() on class Main$Transform with [] = null
Calling public java.net.URL java.lang.Class.getResource(java.lang.String) with params: [[NOT_USED_STRING, foo, SECRET_ARRAY]]
@@ -426,10 +461,19 @@
public boolean java.lang.Class.isLocalClass() on class Main$Transform with [] = false
Calling public boolean java.lang.Class.isMemberClass() with params: []
public boolean java.lang.Class.isMemberClass() on class Main$Transform with [] = true
+Calling public boolean java.lang.Class.isNestmateOf(java.lang.Class) with params: [[null, class java.lang.Object, (obsolete)class Main$Transform, class Main$Transform, long, class java.lang.Class]]
+public boolean java.lang.Class.isNestmateOf(java.lang.Class) with [null] throws java.lang.reflect.InvocationTargetException: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.Class.isPrimitive()' on a null object reference
+public boolean java.lang.Class.isNestmateOf(java.lang.Class) on class Main$Transform with [class java.lang.Object] = false
+public boolean java.lang.Class.isNestmateOf(java.lang.Class) with [(obsolete)class Main$Transform] throws java.lang.reflect.InvocationTargetException: java.lang.RuntimeException: Obsolete Object!
+public boolean java.lang.Class.isNestmateOf(java.lang.Class) on class Main$Transform with [class Main$Transform] = true
+public boolean java.lang.Class.isNestmateOf(java.lang.Class) on class Main$Transform with [long] = false
+public boolean java.lang.Class.isNestmateOf(java.lang.Class) on class Main$Transform with [class java.lang.Class] = false
Calling public boolean java.lang.Class.isPrimitive() with params: []
public boolean java.lang.Class.isPrimitive() on class Main$Transform with [] = false
Calling public boolean java.lang.Class.isProxy() with params: []
public boolean java.lang.Class.isProxy() on class Main$Transform with [] = false
+Calling public boolean java.lang.Class.isSealed() with params: []
+public boolean java.lang.Class.isSealed() on class Main$Transform with [] = false
Calling public boolean java.lang.Class.isSynthetic() with params: []
public boolean java.lang.Class.isSynthetic() on class Main$Transform with [] = false
Calling public native java.lang.Object java.lang.Class.newInstance() throws java.lang.InstantiationException,java.lang.IllegalAccessException with params: []
diff --git a/test/1980-obsolete-object-cleared/run b/test/1980-obsolete-object-cleared/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1980-obsolete-object-cleared/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1980-obsolete-object-cleared/run.py b/test/1980-obsolete-object-cleared/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1980-obsolete-object-cleared/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1981-structural-redef-private-method-handles/build b/test/1981-structural-redef-private-method-handles/build
deleted file mode 100755
index c80d7ad..0000000
--- a/test/1981-structural-redef-private-method-handles/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Make us exit on a failure
-set -e
-
-./default-build "$@" --experimental var-handles
diff --git a/test/1981-structural-redef-private-method-handles/build.py b/test/1981-structural-redef-private-method-handles/build.py
new file mode 100644
index 0000000..7ccbe96
--- /dev/null
+++ b/test/1981-structural-redef-private-method-handles/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(api_level="var-handles")
diff --git a/test/1981-structural-redef-private-method-handles/run b/test/1981-structural-redef-private-method-handles/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1981-structural-redef-private-method-handles/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1981-structural-redef-private-method-handles/run.py b/test/1981-structural-redef-private-method-handles/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1981-structural-redef-private-method-handles/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1981-structural-redef-private-method-handles/test-metadata.json b/test/1981-structural-redef-private-method-handles/test-metadata.json
new file mode 100644
index 0000000..ed29691
--- /dev/null
+++ b/test/1981-structural-redef-private-method-handles/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "experimental": "var-handles"
+ }
+}
diff --git a/test/1982-no-virtuals-structural-redefinition/run b/test/1982-no-virtuals-structural-redefinition/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1982-no-virtuals-structural-redefinition/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1982-no-virtuals-structural-redefinition/run.py b/test/1982-no-virtuals-structural-redefinition/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1982-no-virtuals-structural-redefinition/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1983-structural-redefinition-failures/build b/test/1983-structural-redefinition-failures/build
deleted file mode 100755
index c80d7ad..0000000
--- a/test/1983-structural-redefinition-failures/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Make us exit on a failure
-set -e
-
-./default-build "$@" --experimental var-handles
diff --git a/test/1983-structural-redefinition-failures/build.py b/test/1983-structural-redefinition-failures/build.py
new file mode 100644
index 0000000..7ccbe96
--- /dev/null
+++ b/test/1983-structural-redefinition-failures/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(api_level="var-handles")
diff --git a/test/1983-structural-redefinition-failures/run b/test/1983-structural-redefinition-failures/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1983-structural-redefinition-failures/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1983-structural-redefinition-failures/run.py b/test/1983-structural-redefinition-failures/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1983-structural-redefinition-failures/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1983-structural-redefinition-failures/test-metadata.json b/test/1983-structural-redefinition-failures/test-metadata.json
new file mode 100644
index 0000000..ed29691
--- /dev/null
+++ b/test/1983-structural-redefinition-failures/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "experimental": "var-handles"
+ }
+}
diff --git a/test/1984-structural-redefine-field-trace/run b/test/1984-structural-redefine-field-trace/run
deleted file mode 100755
index a36de16..0000000
--- a/test/1984-structural-redefine-field-trace/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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/1984-structural-redefine-field-trace/run.py b/test/1984-structural-redefine-field-trace/run.py
new file mode 100644
index 0000000..69d8985
--- /dev/null
+++ b/test/1984-structural-redefine-field-trace/run.py
@@ -0,0 +1,21 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(
+ args, jvmti=True, android_runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1985-structural-redefine-stack-scope/run b/test/1985-structural-redefine-stack-scope/run
deleted file mode 100755
index a36de16..0000000
--- a/test/1985-structural-redefine-stack-scope/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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/1985-structural-redefine-stack-scope/run.py b/test/1985-structural-redefine-stack-scope/run.py
new file mode 100644
index 0000000..69d8985
--- /dev/null
+++ b/test/1985-structural-redefine-stack-scope/run.py
@@ -0,0 +1,21 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(
+ args, jvmti=True, android_runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1986-structural-redefine-multi-thread-stack-scope/run b/test/1986-structural-redefine-multi-thread-stack-scope/run
deleted file mode 100755
index a36de16..0000000
--- a/test/1986-structural-redefine-multi-thread-stack-scope/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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/1986-structural-redefine-multi-thread-stack-scope/run.py b/test/1986-structural-redefine-multi-thread-stack-scope/run.py
new file mode 100644
index 0000000..69d8985
--- /dev/null
+++ b/test/1986-structural-redefine-multi-thread-stack-scope/run.py
@@ -0,0 +1,21 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(
+ args, jvmti=True, android_runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1987-structural-redefine-recursive-stack-scope/run b/test/1987-structural-redefine-recursive-stack-scope/run
deleted file mode 100755
index a36de16..0000000
--- a/test/1987-structural-redefine-recursive-stack-scope/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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/1987-structural-redefine-recursive-stack-scope/run.py b/test/1987-structural-redefine-recursive-stack-scope/run.py
new file mode 100644
index 0000000..69d8985
--- /dev/null
+++ b/test/1987-structural-redefine-recursive-stack-scope/run.py
@@ -0,0 +1,21 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(
+ args, jvmti=True, android_runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1988-multi-structural-redefine/run b/test/1988-multi-structural-redefine/run
deleted file mode 100755
index a36de16..0000000
--- a/test/1988-multi-structural-redefine/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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/run.py b/test/1988-multi-structural-redefine/run.py
new file mode 100644
index 0000000..69d8985
--- /dev/null
+++ b/test/1988-multi-structural-redefine/run.py
@@ -0,0 +1,21 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(
+ args, jvmti=True, android_runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1989-transform-bad-monitor/run b/test/1989-transform-bad-monitor/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1989-transform-bad-monitor/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1989-transform-bad-monitor/run.py b/test/1989-transform-bad-monitor/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1989-transform-bad-monitor/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1990-structural-bad-verify/run b/test/1990-structural-bad-verify/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1990-structural-bad-verify/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/run.py b/test/1990-structural-bad-verify/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1990-structural-bad-verify/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1991-hello-structural-retransform/run b/test/1991-hello-structural-retransform/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1991-hello-structural-retransform/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/run.py b/test/1991-hello-structural-retransform/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1991-hello-structural-retransform/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1992-retransform-no-such-field/run b/test/1992-retransform-no-such-field/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/1992-retransform-no-such-field/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/run.py b/test/1992-retransform-no-such-field/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/1992-retransform-no-such-field/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/1993-fallback-non-structural/run b/test/1993-fallback-non-structural/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1993-fallback-non-structural/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/run.py b/test/1993-fallback-non-structural/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1993-fallback-non-structural/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1994-final-virtual-structural/run b/test/1994-final-virtual-structural/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1994-final-virtual-structural/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1994-final-virtual-structural/run.py b/test/1994-final-virtual-structural/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1994-final-virtual-structural/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1995-final-virtual-structural-multithread/run b/test/1995-final-virtual-structural-multithread/run
deleted file mode 100755
index e912529..0000000
--- a/test/1995-final-virtual-structural-multithread/run
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/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.
-
-# TODO(b/144168550) This test uses access patterns that can be replaced by
-# iget-object-quick during dex2dex compilation. This breaks the test since the
-# -quick opcode encodes the exact byte offset of fields. Since this test changes
-# the offset this causes problems.
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/1995-final-virtual-structural-multithread/run.py b/test/1995-final-virtual-structural-multithread/run.py
new file mode 100644
index 0000000..3fd3061
--- /dev/null
+++ b/test/1995-final-virtual-structural-multithread/run.py
@@ -0,0 +1,23 @@
+#!/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.
+
+
+def run(ctx, args):
+ # TODO(b/144168550) This test uses access patterns that can be replaced by
+ # iget-object-quick during dex2dex compilation. This breaks the test since the
+ # -quick opcode encodes the exact byte offset of fields. Since this test changes
+ # the offset this causes problems.
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1995-final-virtual-structural-multithread/src/Main.java b/test/1995-final-virtual-structural-multithread/src/Main.java
index f19358d..f3bf4f6 100644
--- a/test/1995-final-virtual-structural-multithread/src/Main.java
+++ b/test/1995-final-virtual-structural-multithread/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test1995.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test1995.run();
+ }
}
diff --git a/test/1995-final-virtual-structural-multithread/src/art/Test1995.java b/test/1995-final-virtual-structural-multithread/src/art/Test1995.java
index 7073494..a960c74 100644
--- a/test/1995-final-virtual-structural-multithread/src/art/Test1995.java
+++ b/test/1995-final-virtual-structural-multithread/src/art/Test1995.java
@@ -21,150 +21,153 @@
import java.util.Base64;
import java.util.concurrent.CountDownLatch;
public class Test1995 {
- private static final int NUM_THREADS = 20;
- // Don't perform more than this many repeats per thread to prevent OOMEs
- private static final int TASK_COUNT_LIMIT = 1000;
+ private static final int NUM_THREADS = 20;
+ // Don't perform more than this many repeats per thread to prevent OOMEs
+ private static final int TASK_COUNT_LIMIT = 1000;
- public static final class Transform {
- public String greetingEnglish;
- public Transform() {
- this.greetingEnglish = "Hello";
- }
- public String sayHi() {
- return greetingEnglish + " from " + Thread.currentThread().getName();
- }
- }
-
- /**
- * base64 encoded class/dex file for
- * public static final class Transform {
- * public String greetingEnglish;
- * public String greetingFrench;
- * public String greetingDanish;
- * public String greetingJapanese;
- *
- * public Transform() {
- * this.greetingEnglish = "Hello World";
- * this.greetingFrench = "Bonjour le Monde";
- * this.greetingDanish = "Hej Verden";
- * this.greetingJapanese = "こんにちは世界";
- * }
- * public String sayHi() {
- * return sayHiEnglish() + ", " + sayHiFrench() + ", " + sayHiDanish() + ", " + sayHiJapanese() + " from " + Thread.currentThread().getName();
- * }
- * public String sayHiEnglish() {
- * return greetingEnglish;
- * }
- * public String sayHiDanish() {
- * return greetingDanish;
- * }
- * public String sayHiJapanese() {
- * return greetingJapanese;
- * }
- * public String sayHiFrench() {
- * return greetingFrench;
- * }
- * }
- */
- private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
-"ZGV4CjAzNQCsHrUqkb8cYgT2oYN7HlVbeOxJT/kONRvgBgAAcAAAAHhWNBIAAAAAAAAAABwGAAAl" +
-"AAAAcAAAAAkAAAAEAQAABAAAACgBAAAEAAAAWAEAAAwAAAB4AQAAAQAAANgBAADoBAAA+AEAAEoD" +
-"AABSAwAAVgMAAF4DAABwAwAAfAMAAIkDAACMAwAAkAMAAKoDAAC6AwAA3gMAAP4DAAASBAAAJgQA" +
-"AEEEAABVBAAAZAQAAG8EAAByBAAAfwQAAIcEAACWBAAAnwQAAK8EAADABAAA0AQAAOIEAADoBAAA" +
-"7wQAAPwEAAAKBQAAFwUAACYFAAAwBQAANwUAAK8FAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAAO" +
-"AAAADwAAABIAAAAGAAAABQAAAAAAAAAHAAAABgAAAEQDAAAGAAAABwAAAAAAAAASAAAACAAAAAAA" +
-"AAAAAAUAFwAAAAAABQAYAAAAAAAFABkAAAAAAAUAGgAAAAAAAwACAAAAAAAAABwAAAAAAAAAHQAA" +
-"AAAAAAAeAAAAAAAAAB8AAAAAAAAAIAAAAAQAAwACAAAABgADAAIAAAAGAAEAFAAAAAYAAAAhAAAA" +
-"BwACABUAAAAHAAAAFgAAAAAAAAARAAAABAAAAAAAAAAQAAAADAYAANUFAAAAAAAABwABAAIAAAAt" +
-"AwAAQQAAAG4QAwAGAAwAbhAEAAYADAFuEAIABgAMAm4QBQAGAAwDcQAKAAAADARuEAsABAAMBCIF" +
-"BgBwEAcABQBuIAgABQAaAAEAbiAIAAUAbiAIABUAbiAIAAUAbiAIACUAbiAIAAUAbiAIADUAGgAA" +
-"AG4gCAAFAG4gCABFAG4QCQAFAAwAEQAAAAIAAQAAAAAAMQMAAAMAAABUEAAAEQAAAAIAAQAAAAAA" +
-"NQMAAAMAAABUEAEAEQAAAAIAAQAAAAAAOQMAAAMAAABUEAIAEQAAAAIAAQAAAAAAPQMAAAMAAABU" +
-"EAMAEQAAAAIAAQABAAAAJAMAABQAAABwEAYAAQAaAAUAWxABABoAAwBbEAIAGgAEAFsQAAAaACQA" +
-"WxADAA4ACQAOPEtLS0sAEAAOABYADgATAA4AHAAOABkADgAAAAABAAAABQAGIGZyb20gAAIsIAAG" +
-"PGluaXQ+ABBCb25qb3VyIGxlIE1vbmRlAApIZWogVmVyZGVuAAtIZWxsbyBXb3JsZAABTAACTEwA" +
-"GExhcnQvVGVzdDE5OTUkVHJhbnNmb3JtOwAOTGFydC9UZXN0MTk5NTsAIkxkYWx2aWsvYW5ub3Rh" +
-"dGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwASTGph" +
-"dmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVp" +
-"bGRlcjsAEkxqYXZhL2xhbmcvVGhyZWFkOwANVGVzdDE5OTUuamF2YQAJVHJhbnNmb3JtAAFWAAth" +
-"Y2Nlc3NGbGFncwAGYXBwZW5kAA1jdXJyZW50VGhyZWFkAAdnZXROYW1lAA5ncmVldGluZ0Rhbmlz" +
-"aAAPZ3JlZXRpbmdFbmdsaXNoAA5ncmVldGluZ0ZyZW5jaAAQZ3JlZXRpbmdKYXBhbmVzZQAEbmFt" +
-"ZQAFc2F5SGkAC3NheUhpRGFuaXNoAAxzYXlIaUVuZ2xpc2gAC3NheUhpRnJlbmNoAA1zYXlIaUph" +
-"cGFuZXNlAAh0b1N0cmluZwAFdmFsdWUAdn5+RDh7ImNvbXBpbGF0aW9uLW1vZGUiOiJkZWJ1ZyIs" +
-"Im1pbi1hcGkiOjEsInNoYS0xIjoiNjBkYTRkNjdiMzgxYzQyNDY3NzU3YzQ5ZmI2ZTU1NzU2ZDg4" +
-"YTJmMyIsInZlcnNpb24iOiIxLjcuMTItZGV2In0AB+OBk+OCk+OBq+OBoeOBr+S4lueVjAACAgEi" +
-"GAECAwITBBkbFxEABAEFAAEBAQEBAQEAgYAE7AUBAfgDAQGMBQEBpAUBAbwFAQHUBQAAAAAAAgAA" +
-"AMYFAADMBQAAAAYAAAAAAAAAAAAAAAAAABAAAAAAAAAAAQAAAAAAAAABAAAAJQAAAHAAAAACAAAA" +
-"CQAAAAQBAAADAAAABAAAACgBAAAEAAAABAAAAFgBAAAFAAAADAAAAHgBAAAGAAAAAQAAANgBAAAB" +
-"IAAABgAAAPgBAAADIAAABgAAACQDAAABEAAAAQAAAEQDAAACIAAAJQAAAEoDAAAEIAAAAgAAAMYF" +
-"AAAAIAAAAQAAANUFAAADEAAAAgAAAPwFAAAGIAAAAQAAAAwGAAAAEAAAAQAAABwGAAA=");
-
-
- public static void run() throws Exception {
- Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
- doTest();
- }
-
- public static final class MyThread extends Thread {
- public MyThread(CountDownLatch delay, int id) {
- super("Thread: " + id);
- this.thr_id = id;
- this.results = new ArrayList<>(TASK_COUNT_LIMIT);
- this.finish = false;
- this.delay = delay;
- }
-
- public void run() {
- delay.countDown();
- while (!finish && results.size() < TASK_COUNT_LIMIT) {
- Transform t = new Transform();
- results.add(t.sayHi());
- }
- }
-
- public void finish() throws Exception {
- finish = true;
- this.join();
- }
-
- public void Check() throws Exception {
- for (String s : results) {
- if (!s.equals("Hello from " + getName()) &&
- !s.equals("Hello, null, null, null from " + getName()) &&
- !s.equals("Hello World, Bonjour le Monde, Hej Verden, こんにちは世界 from " + getName())) {
- System.out.println("FAIL " + thr_id + ": Unexpected result: " + s);
+ public static final class Transform {
+ public String greetingEnglish;
+ public Transform() {
+ this.greetingEnglish = "Hello";
}
- }
+ public String sayHi() {
+ return greetingEnglish + " from " + Thread.currentThread().getName();
+ }
}
- public ArrayList<String> results;
- public volatile boolean finish;
- public int thr_id;
- public CountDownLatch delay;
- }
+ /**
+ * base64 encoded class/dex file for
+ * public static final class Transform {
+ * public String greetingEnglish;
+ * public String greetingFrench;
+ * public String greetingDanish;
+ * public String greetingJapanese;
+ *
+ * public Transform() {
+ * this.greetingEnglish = "Hello World";
+ * this.greetingFrench = "Bonjour le Monde";
+ * this.greetingDanish = "Hej Verden";
+ * this.greetingJapanese = "こんにちは世界";
+ * }
+ * public String sayHi() {
+ * return sayHiEnglish() + ", " + sayHiFrench() + ", " + sayHiDanish() + ", " +
+ * sayHiJapanese() + " from " + Thread.currentThread().getName();
+ * }
+ * public String sayHiEnglish() {
+ * return greetingEnglish;
+ * }
+ * public String sayHiDanish() {
+ * return greetingDanish;
+ * }
+ * public String sayHiJapanese() {
+ * return greetingJapanese;
+ * }
+ * public String sayHiFrench() {
+ * return greetingFrench;
+ * }
+ * }
+ */
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQCsHrUqkb8cYgT2oYN7HlVbeOxJT/kONRvgBgAAcAAAAHhWNBIAAAAAAAAAABwGAAAl" +
+ "AAAAcAAAAAkAAAAEAQAABAAAACgBAAAEAAAAWAEAAAwAAAB4AQAAAQAAANgBAADoBAAA+AEAAEoD" +
+ "AABSAwAAVgMAAF4DAABwAwAAfAMAAIkDAACMAwAAkAMAAKoDAAC6AwAA3gMAAP4DAAASBAAAJgQA" +
+ "AEEEAABVBAAAZAQAAG8EAAByBAAAfwQAAIcEAACWBAAAnwQAAK8EAADABAAA0AQAAOIEAADoBAAA" +
+ "7wQAAPwEAAAKBQAAFwUAACYFAAAwBQAANwUAAK8FAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAAO" +
+ "AAAADwAAABIAAAAGAAAABQAAAAAAAAAHAAAABgAAAEQDAAAGAAAABwAAAAAAAAASAAAACAAAAAAA" +
+ "AAAAAAUAFwAAAAAABQAYAAAAAAAFABkAAAAAAAUAGgAAAAAAAwACAAAAAAAAABwAAAAAAAAAHQAA" +
+ "AAAAAAAeAAAAAAAAAB8AAAAAAAAAIAAAAAQAAwACAAAABgADAAIAAAAGAAEAFAAAAAYAAAAhAAAA" +
+ "BwACABUAAAAHAAAAFgAAAAAAAAARAAAABAAAAAAAAAAQAAAADAYAANUFAAAAAAAABwABAAIAAAAt" +
+ "AwAAQQAAAG4QAwAGAAwAbhAEAAYADAFuEAIABgAMAm4QBQAGAAwDcQAKAAAADARuEAsABAAMBCIF" +
+ "BgBwEAcABQBuIAgABQAaAAEAbiAIAAUAbiAIABUAbiAIAAUAbiAIACUAbiAIAAUAbiAIADUAGgAA" +
+ "AG4gCAAFAG4gCABFAG4QCQAFAAwAEQAAAAIAAQAAAAAAMQMAAAMAAABUEAAAEQAAAAIAAQAAAAAA" +
+ "NQMAAAMAAABUEAEAEQAAAAIAAQAAAAAAOQMAAAMAAABUEAIAEQAAAAIAAQAAAAAAPQMAAAMAAABU" +
+ "EAMAEQAAAAIAAQABAAAAJAMAABQAAABwEAYAAQAaAAUAWxABABoAAwBbEAIAGgAEAFsQAAAaACQA" +
+ "WxADAA4ACQAOPEtLS0sAEAAOABYADgATAA4AHAAOABkADgAAAAABAAAABQAGIGZyb20gAAIsIAAG" +
+ "PGluaXQ+ABBCb25qb3VyIGxlIE1vbmRlAApIZWogVmVyZGVuAAtIZWxsbyBXb3JsZAABTAACTEwA" +
+ "GExhcnQvVGVzdDE5OTUkVHJhbnNmb3JtOwAOTGFydC9UZXN0MTk5NTsAIkxkYWx2aWsvYW5ub3Rh" +
+ "dGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwASTGph" +
+ "dmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVp" +
+ "bGRlcjsAEkxqYXZhL2xhbmcvVGhyZWFkOwANVGVzdDE5OTUuamF2YQAJVHJhbnNmb3JtAAFWAAth" +
+ "Y2Nlc3NGbGFncwAGYXBwZW5kAA1jdXJyZW50VGhyZWFkAAdnZXROYW1lAA5ncmVldGluZ0Rhbmlz" +
+ "aAAPZ3JlZXRpbmdFbmdsaXNoAA5ncmVldGluZ0ZyZW5jaAAQZ3JlZXRpbmdKYXBhbmVzZQAEbmFt" +
+ "ZQAFc2F5SGkAC3NheUhpRGFuaXNoAAxzYXlIaUVuZ2xpc2gAC3NheUhpRnJlbmNoAA1zYXlIaUph" +
+ "cGFuZXNlAAh0b1N0cmluZwAFdmFsdWUAdn5+RDh7ImNvbXBpbGF0aW9uLW1vZGUiOiJkZWJ1ZyIs" +
+ "Im1pbi1hcGkiOjEsInNoYS0xIjoiNjBkYTRkNjdiMzgxYzQyNDY3NzU3YzQ5ZmI2ZTU1NzU2ZDg4" +
+ "YTJmMyIsInZlcnNpb24iOiIxLjcuMTItZGV2In0AB+OBk+OCk+OBq+OBoeOBr+S4lueVjAACAgEi" +
+ "GAECAwITBBkbFxEABAEFAAEBAQEBAQEAgYAE7AUBAfgDAQGMBQEBpAUBAbwFAQHUBQAAAAAAAgAA" +
+ "AMYFAADMBQAAAAYAAAAAAAAAAAAAAAAAABAAAAAAAAAAAQAAAAAAAAABAAAAJQAAAHAAAAACAAAA" +
+ "CQAAAAQBAAADAAAABAAAACgBAAAEAAAABAAAAFgBAAAFAAAADAAAAHgBAAAGAAAAAQAAANgBAAAB" +
+ "IAAABgAAAPgBAAADIAAABgAAACQDAAABEAAAAQAAAEQDAAACIAAAJQAAAEoDAAAEIAAAAgAAAMYF" +
+ "AAAAIAAAAQAAANUFAAADEAAAAgAAAPwFAAAGIAAAAQAAAAwGAAAAEAAAAQAAABwGAAA=");
- public static MyThread[] startThreads(int num_threads) throws Exception {
- CountDownLatch cdl = new CountDownLatch(num_threads);
- MyThread[] res = new MyThread[num_threads];
- for (int i = 0; i < num_threads; i++) {
- res[i] = new MyThread(cdl, i);
- res[i].start();
- }
- cdl.await();
- return res;
- }
- public static void finishThreads(MyThread[] thrs) throws Exception {
- for (MyThread t : thrs) {
- t.finish();
- }
- for (MyThread t : thrs) {
- t.Check();
- }
- }
- public static void doTest() throws Exception {
- MyThread[] threads = startThreads(NUM_THREADS);
- Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
- finishThreads(threads);
- }
+ public static void run() throws Exception {
+ Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+ doTest();
+ }
+
+ public static final class MyThread extends Thread {
+ public MyThread(CountDownLatch delay, int id) {
+ super("Thread: " + id);
+ this.thr_id = id;
+ this.results = new ArrayList<>(TASK_COUNT_LIMIT);
+ this.finish = false;
+ this.delay = delay;
+ }
+
+ public void run() {
+ delay.countDown();
+ while (!finish && results.size() < TASK_COUNT_LIMIT) {
+ Transform t = new Transform();
+ results.add(t.sayHi());
+ }
+ }
+
+ public void finish() throws Exception {
+ finish = true;
+ this.join();
+ }
+
+ public void Check() throws Exception {
+ for (String s : results) {
+ if (!s.equals("Hello from " + getName()) &&
+ !s.equals("Hello, null, null, null from " + getName()) &&
+ !s.equals(
+ "Hello World, Bonjour le Monde, Hej Verden, こんにちは世界 from " +
+ getName())) {
+ System.out.println("FAIL " + thr_id + ": Unexpected result: " + s);
+ }
+ }
+ }
+
+ public ArrayList<String> results;
+ public volatile boolean finish;
+ public int thr_id;
+ public CountDownLatch delay;
+ }
+
+ public static MyThread[] startThreads(int num_threads) throws Exception {
+ CountDownLatch cdl = new CountDownLatch(num_threads);
+ MyThread[] res = new MyThread[num_threads];
+ for (int i = 0; i < num_threads; i++) {
+ res[i] = new MyThread(cdl, i);
+ res[i].start();
+ }
+ cdl.await();
+ return res;
+ }
+ public static void finishThreads(MyThread[] thrs) throws Exception {
+ for (MyThread t : thrs) {
+ t.finish();
+ }
+ for (MyThread t : thrs) {
+ t.Check();
+ }
+ }
+
+ public static void doTest() throws Exception {
+ MyThread[] threads = startThreads(NUM_THREADS);
+ Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
+ finishThreads(threads);
+ }
}
diff --git a/test/1996-final-override-virtual-structural/run b/test/1996-final-override-virtual-structural/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1996-final-override-virtual-structural/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1996-final-override-virtual-structural/run.py b/test/1996-final-override-virtual-structural/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1996-final-override-virtual-structural/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1996-final-override-virtual-structural/src/Main.java b/test/1996-final-override-virtual-structural/src/Main.java
index ade69cf..ff10798 100644
--- a/test/1996-final-override-virtual-structural/src/Main.java
+++ b/test/1996-final-override-virtual-structural/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test1996.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test1996.run();
+ }
}
diff --git a/test/1996-final-override-virtual-structural/src/art/Test1996.java b/test/1996-final-override-virtual-structural/src/art/Test1996.java
index c2b1125..2649476 100644
--- a/test/1996-final-override-virtual-structural/src/art/Test1996.java
+++ b/test/1996-final-override-virtual-structural/src/art/Test1996.java
@@ -19,76 +19,76 @@
import java.util.Base64;
public class Test1996 {
- public static class SuperTransform {
- public String hiValue = "Hi";
- public String sayHi() {
- return this.hiValue;
+ public static class SuperTransform {
+ public String hiValue = "Hi";
+ public String sayHi() {
+ return this.hiValue;
+ }
}
- }
- public static final class Transform extends SuperTransform {
- public void PostTransform() { }
- public String sayHiTwice(Runnable run) {
- run.run();
- return "super: " + super.sayHi() + " this: " + sayHi();
+ public static final class Transform extends SuperTransform {
+ public void PostTransform() { }
+ public String sayHiTwice(Runnable run) {
+ run.run();
+ return "super: " + super.sayHi() + " this: " + sayHi();
+ }
}
- }
- /**
- * base64 encoded class/dex file for
- * public static final class Transform extends SuperTransform {
- * public String myGreeting;
- * public void PostTransform() {
- * myGreeting = "SALUTATIONS";
- * }
- * public String sayHiTwice(Runnable run) {
- * run.run();
- * return "super: " + super.sayHi() + " and then this: " + sayHi();
- * }
- * public String sayHi() {
- * return myGreeting;
- * }
- * }
- */
- private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
-"ZGV4CjAzNQAO4Dwurw97RcUtfH7np7S5RR8gsJYOfmeABQAAcAAAAHhWNBIAAAAAAAAAALwEAAAc" +
-"AAAAcAAAAAkAAADgAAAABAAAAAQBAAABAAAANAEAAAoAAAA8AQAAAQAAAIwBAADUAwAArAEAAHYC" +
-"AACIAgAAkAIAAJMCAACXAgAAtgIAANACAADgAgAABAMAACQDAAA6AwAATgMAAGkDAAB4AwAAhQMA" +
-"AJQDAACfAwAAogMAAK8DAAC3AwAAwwMAAMkDAADOAwAA1QMAAOEDAADqAwAA9AMAAPsDAAAEAAAA" +
-"BQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAABAAAAACAAAABgAAAAAAAAADAAAABgAAAGgCAAAD" +
-"AAAABwAAAHACAAAQAAAACAAAAAAAAAABAAYAEwAAAAAAAwABAAAAAAAAABYAAAABAAMAAQAAAAEA" +
-"AwAMAAAAAQAAABYAAAABAAEAFwAAAAUAAwAVAAAABwADAAEAAAAHAAIAEgAAAAcAAAAZAAAAAQAA" +
-"ABEAAAAAAAAAAAAAAA4AAACsBAAAggQAAAAAAAACAAEAAAAAAFsCAAADAAAAVBAAABEAAAAFAAIA" +
-"AgAAAF8CAAAlAAAAchAGAAQAbxABAAMADARuEAQAAwAMACIBBwBwEAcAAQAaAhgAbiAIACEAbiAI" +
-"AEEAGgQAAG4gCABBAG4gCAABAG4QCQABAAwEEQQAAAEAAQABAAAAUgIAAAQAAABwEAAAAAAOAAIA" +
-"AQAAAAAAVgIAAAUAAAAaAA0AWxAAAA4ACgAOAA0ADksAFAAOABABAA48AAAAAAEAAAAFAAAAAQAA" +
-"AAYAECBhbmQgdGhlbiB0aGlzOiAABjxpbml0PgABTAACTEwAHUxhcnQvVGVzdDE5OTYkU3VwZXJU" +
-"cmFuc2Zvcm07ABhMYXJ0L1Rlc3QxOTk2JFRyYW5zZm9ybTsADkxhcnQvVGVzdDE5OTY7ACJMZGFs" +
-"dmlrL2Fubm90YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90YXRpb24vSW5uZXJD" +
-"bGFzczsAFExqYXZhL2xhbmcvUnVubmFibGU7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xh" +
-"bmcvU3RyaW5nQnVpbGRlcjsADVBvc3RUcmFuc2Zvcm0AC1NBTFVUQVRJT05TAA1UZXN0MTk5Ni5q" +
-"YXZhAAlUcmFuc2Zvcm0AAVYAC2FjY2Vzc0ZsYWdzAAZhcHBlbmQACm15R3JlZXRpbmcABG5hbWUA" +
-"A3J1bgAFc2F5SGkACnNheUhpVHdpY2UAB3N1cGVyOiAACHRvU3RyaW5nAAV2YWx1ZQB2fn5EOHsi" +
-"Y29tcGlsYXRpb24tbW9kZSI6ImRlYnVnIiwibWluLWFwaSI6MSwic2hhLTEiOiI2MGRhNGQ2N2Iz" +
-"ODFjNDI0Njc3NTdjNDlmYjZlNTU3NTZkODhhMmYzIiwidmVyc2lvbiI6IjEuNy4xMi1kZXYifQAC" +
-"AwEaGAICBAIRBBkUFw8AAQEDAAECgYAEoAQDAbgEAQGsAwEBxAMAAAAAAAACAAAAcwQAAHkEAACg" +
-"BAAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAcAAAAcAAAAAIAAAAJAAAA4AAAAAMA" +
-"AAAEAAAABAEAAAQAAAABAAAANAEAAAUAAAAKAAAAPAEAAAYAAAABAAAAjAEAAAEgAAAEAAAArAEA" +
-"AAMgAAAEAAAAUgIAAAEQAAACAAAAaAIAAAIgAAAcAAAAdgIAAAQgAAACAAAAcwQAAAAgAAABAAAA" +
-"ggQAAAMQAAACAAAAnAQAAAYgAAABAAAArAQAAAAQAAABAAAAvAQAAA==");
+ /**
+ * base64 encoded class/dex file for
+ * public static final class Transform extends SuperTransform {
+ * public String myGreeting;
+ * public void PostTransform() {
+ * myGreeting = "SALUTATIONS";
+ * }
+ * public String sayHiTwice(Runnable run) {
+ * run.run();
+ * return "super: " + super.sayHi() + " and then this: " + sayHi();
+ * }
+ * public String sayHi() {
+ * return myGreeting;
+ * }
+ * }
+ */
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQAO4Dwurw97RcUtfH7np7S5RR8gsJYOfmeABQAAcAAAAHhWNBIAAAAAAAAAALwEAAAc" +
+ "AAAAcAAAAAkAAADgAAAABAAAAAQBAAABAAAANAEAAAoAAAA8AQAAAQAAAIwBAADUAwAArAEAAHYC" +
+ "AACIAgAAkAIAAJMCAACXAgAAtgIAANACAADgAgAABAMAACQDAAA6AwAATgMAAGkDAAB4AwAAhQMA" +
+ "AJQDAACfAwAAogMAAK8DAAC3AwAAwwMAAMkDAADOAwAA1QMAAOEDAADqAwAA9AMAAPsDAAAEAAAA" +
+ "BQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAABAAAAACAAAABgAAAAAAAAADAAAABgAAAGgCAAAD" +
+ "AAAABwAAAHACAAAQAAAACAAAAAAAAAABAAYAEwAAAAAAAwABAAAAAAAAABYAAAABAAMAAQAAAAEA" +
+ "AwAMAAAAAQAAABYAAAABAAEAFwAAAAUAAwAVAAAABwADAAEAAAAHAAIAEgAAAAcAAAAZAAAAAQAA" +
+ "ABEAAAAAAAAAAAAAAA4AAACsBAAAggQAAAAAAAACAAEAAAAAAFsCAAADAAAAVBAAABEAAAAFAAIA" +
+ "AgAAAF8CAAAlAAAAchAGAAQAbxABAAMADARuEAQAAwAMACIBBwBwEAcAAQAaAhgAbiAIACEAbiAI" +
+ "AEEAGgQAAG4gCABBAG4gCAABAG4QCQABAAwEEQQAAAEAAQABAAAAUgIAAAQAAABwEAAAAAAOAAIA" +
+ "AQAAAAAAVgIAAAUAAAAaAA0AWxAAAA4ACgAOAA0ADksAFAAOABABAA48AAAAAAEAAAAFAAAAAQAA" +
+ "AAYAECBhbmQgdGhlbiB0aGlzOiAABjxpbml0PgABTAACTEwAHUxhcnQvVGVzdDE5OTYkU3VwZXJU" +
+ "cmFuc2Zvcm07ABhMYXJ0L1Rlc3QxOTk2JFRyYW5zZm9ybTsADkxhcnQvVGVzdDE5OTY7ACJMZGFs" +
+ "dmlrL2Fubm90YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90YXRpb24vSW5uZXJD" +
+ "bGFzczsAFExqYXZhL2xhbmcvUnVubmFibGU7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xh" +
+ "bmcvU3RyaW5nQnVpbGRlcjsADVBvc3RUcmFuc2Zvcm0AC1NBTFVUQVRJT05TAA1UZXN0MTk5Ni5q" +
+ "YXZhAAlUcmFuc2Zvcm0AAVYAC2FjY2Vzc0ZsYWdzAAZhcHBlbmQACm15R3JlZXRpbmcABG5hbWUA" +
+ "A3J1bgAFc2F5SGkACnNheUhpVHdpY2UAB3N1cGVyOiAACHRvU3RyaW5nAAV2YWx1ZQB2fn5EOHsi" +
+ "Y29tcGlsYXRpb24tbW9kZSI6ImRlYnVnIiwibWluLWFwaSI6MSwic2hhLTEiOiI2MGRhNGQ2N2Iz" +
+ "ODFjNDI0Njc3NTdjNDlmYjZlNTU3NTZkODhhMmYzIiwidmVyc2lvbiI6IjEuNy4xMi1kZXYifQAC" +
+ "AwEaGAICBAIRBBkUFw8AAQEDAAECgYAEoAQDAbgEAQGsAwEBxAMAAAAAAAACAAAAcwQAAHkEAACg" +
+ "BAAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAAAAEAAAAcAAAAcAAAAAIAAAAJAAAA4AAAAAMA" +
+ "AAAEAAAABAEAAAQAAAABAAAANAEAAAUAAAAKAAAAPAEAAAYAAAABAAAAjAEAAAEgAAAEAAAArAEA" +
+ "AAMgAAAEAAAAUgIAAAEQAAACAAAAaAIAAAIgAAAcAAAAdgIAAAQgAAACAAAAcwQAAAAgAAABAAAA" +
+ "ggQAAAMQAAACAAAAnAQAAAYgAAABAAAArAQAAAAQAAABAAAAvAQAAA==");
- public static void run() {
- Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
- doTest(new Transform());
- }
+ public static void run() {
+ Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+ doTest(new Transform());
+ }
- public static void doTest(final Transform t) {
- System.out.println(t.sayHiTwice(() -> { System.out.println("Not doing anything"); }));
- System.out.println(t.sayHiTwice(
- () -> {
- System.out.println("Redefining calling class");
- Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
- t.PostTransform();
- }));
- System.out.println(t.sayHiTwice(() -> { System.out.println("Not doing anything"); }));
- }
+ public static void doTest(final Transform t) {
+ System.out.println(t.sayHiTwice(() -> { System.out.println("Not doing anything"); }));
+ System.out.println(t.sayHiTwice(
+ () -> {
+ System.out.println("Redefining calling class");
+ Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
+ t.PostTransform();
+ }));
+ System.out.println(t.sayHiTwice(() -> { System.out.println("Not doing anything"); }));
+ }
}
diff --git a/test/1997-structural-shadow-method/run b/test/1997-structural-shadow-method/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1997-structural-shadow-method/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/run.py b/test/1997-structural-shadow-method/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1997-structural-shadow-method/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, 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
index 3c9bc85..f6552ee 100644
--- a/test/1997-structural-shadow-method/src/Main.java
+++ b/test/1997-structural-shadow-method/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test1997.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test1997.run();
+ }
}
diff --git a/test/1997-structural-shadow-method/src/art/Test1997.java b/test/1997-structural-shadow-method/src/art/Test1997.java
index 7309a31..5186518 100644
--- a/test/1997-structural-shadow-method/src/art/Test1997.java
+++ b/test/1997-structural-shadow-method/src/art/Test1997.java
@@ -20,65 +20,63 @@
public class Test1997 {
- public static class SuperTransform {
- // We will be shadowing this function.
- public static void sayHi() {
- System.out.println("Hello!");
+ 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();
+ // 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 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 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();
- }
+ 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/run b/test/1998-structural-shadow-field/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1998-structural-shadow-field/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/run.py b/test/1998-structural-shadow-field/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1998-structural-shadow-field/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, 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
index f6aeca5..e35d1b1 100644
--- a/test/1998-structural-shadow-field/src/Main.java
+++ b/test/1998-structural-shadow-field/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test1998.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test1998.run();
+ }
}
diff --git a/test/1998-structural-shadow-field/src/art/Test1998.java b/test/1998-structural-shadow-field/src/art/Test1998.java
index 3fda936..3aaa9f3 100644
--- a/test/1998-structural-shadow-field/src/art/Test1998.java
+++ b/test/1998-structural-shadow-field/src/art/Test1998.java
@@ -20,46 +20,44 @@
public class Test1998 {
- public static class SuperTransform {
- public static String greeting = "Hello";
- }
+ public static class SuperTransform {
+ public static String greeting = "Hello";
+ }
- // The class we will be transforming.
- public static class Transform extends SuperTransform { }
+ // 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 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 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);
- }
+ 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/1999-virtual-structural/run b/test/1999-virtual-structural/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/1999-virtual-structural/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/1999-virtual-structural/run.py b/test/1999-virtual-structural/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/1999-virtual-structural/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/1999-virtual-structural/src/Main.java b/test/1999-virtual-structural/src/Main.java
index 86a492b..f4a1bd6 100644
--- a/test/1999-virtual-structural/src/Main.java
+++ b/test/1999-virtual-structural/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test1999.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test1999.run();
+ }
}
diff --git a/test/1999-virtual-structural/src/art/Test1999.java b/test/1999-virtual-structural/src/art/Test1999.java
index f6811a9..6943442 100644
--- a/test/1999-virtual-structural/src/art/Test1999.java
+++ b/test/1999-virtual-structural/src/art/Test1999.java
@@ -19,67 +19,67 @@
import java.util.Base64;
public class Test1999 {
- public static class Transform {
- public String getGreeting() {
- return "Hi";
+ public static class Transform {
+ public String getGreeting() {
+ return "Hi";
+ }
}
- }
- public static class SubTransform extends Transform {
- private int count = 0;
- public void sayHi() {
- System.out.println(getGreeting() + "(SubTransform called " + (++count) + " times)");
+ public static class SubTransform extends Transform {
+ private int count = 0;
+ public void sayHi() {
+ System.out.println(getGreeting() + "(SubTransform called " + (++count) + " times)");
+ }
}
- }
- /**
- * base64 encoded class/dex file for
- * public static class Transform {
- * private int count;
- * public String getGreeting() {
- * return "Hello (Transform called " + incrCount() + " times)";
- * }
- * protected int incrCount() {
- * return ++count;
- * }
- * }
- */
- private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
-"ZGV4CjAzNQAwwbMpPdPdWkU+6UJnvqa7v4VBdcuq2vkMBQAAcAAAAHhWNBIAAAAAAAAAAEgEAAAa" +
-"AAAAcAAAAAkAAADYAAAABQAAAPwAAAABAAAAOAEAAAgAAABAAQAAAQAAAIABAABsAwAAoAEAADoC" +
-"AABDAgAASwIAAGUCAABoAgAAawIAAG8CAABzAgAAjQIAAJ0CAADBAgAA4QIAAPUCAAAJAwAAJAMA" +
-"ADMDAAA+AwAAQQMAAE4DAABWAwAAXQMAAGoDAAB1AwAAewMAAIUDAACMAwAAAwAAAAcAAAAIAAAA" +
-"CQAAAAoAAAALAAAADAAAAA0AAAAQAAAAAwAAAAAAAAAAAAAABAAAAAYAAAAAAAAABQAAAAcAAAAs" +
-"AgAABgAAAAcAAAA0AgAAEAAAAAgAAAAAAAAAAQAAABMAAAABAAQAAQAAAAEAAQAUAAAAAQAAABUA" +
-"AAAFAAQAAQAAAAcABAABAAAABwACABIAAAAHAAMAEgAAAAcAAQAXAAAAAQAAAAEAAAAFAAAAAAAA" +
-"AA4AAAA4BAAAEwQAAAAAAAACAAEAAAAAACgCAAAHAAAAUhAAANgAAAFZEAAADwAAAAQAAQACAAAA" +
-"JAIAABsAAABuEAIAAwAKACIBBwBwEAQAAQAaAgIAbiAGACEAbiAFAAEAGgAAAG4gBgABAG4QBwAB" +
-"AAwAEQAAAAEAAQABAAAAIAIAAAQAAABwEAMAAAAOAAMADgAGAA4ACQAOAAEAAAAAAAAAAQAAAAYA" +
-"ByB0aW1lcykABjxpbml0PgAYSGVsbG8gKFRyYW5zZm9ybSBjYWxsZWQgAAFJAAFMAAJMSQACTEwA" +
-"GExhcnQvVGVzdDE5OTkkVHJhbnNmb3JtOwAOTGFydC9UZXN0MTk5OTsAIkxkYWx2aWsvYW5ub3Rh" +
-"dGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwASTGph" +
-"dmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVp" +
-"bGRlcjsADVRlc3QxOTk5LmphdmEACVRyYW5zZm9ybQABVgALYWNjZXNzRmxhZ3MABmFwcGVuZAAF" +
-"Y291bnQAC2dldEdyZWV0aW5nAAlpbmNyQ291bnQABG5hbWUACHRvU3RyaW5nAAV2YWx1ZQB2fn5E" +
-"OHsiY29tcGlsYXRpb24tbW9kZSI6ImRlYnVnIiwibWluLWFwaSI6MSwic2hhLTEiOiI2MGRhNGQ2" +
-"N2IzODFjNDI0Njc3NTdjNDlmYjZlNTU3NTZkODhhMmYzIiwidmVyc2lvbiI6IjEuNy4xMi1kZXYi" +
-"fQACAwEYGAICBAIRBAkWFw8AAQECAAIAgYAEiAQBAcADAQSgAwAAAAAAAgAAAAQEAAAKBAAALAQA" +
-"AAAAAAAAAAAAAAAAABAAAAAAAAAAAQAAAAAAAAABAAAAGgAAAHAAAAACAAAACQAAANgAAAADAAAA" +
-"BQAAAPwAAAAEAAAAAQAAADgBAAAFAAAACAAAAEABAAAGAAAAAQAAAIABAAABIAAAAwAAAKABAAAD" +
-"IAAAAwAAACACAAABEAAAAgAAACwCAAACIAAAGgAAADoCAAAEIAAAAgAAAAQEAAAAIAAAAQAAABME" +
-"AAADEAAAAgAAACgEAAAGIAAAAQAAADgEAAAAEAAAAQAAAEgEAAA=");
+ /**
+ * base64 encoded class/dex file for
+ * public static class Transform {
+ * private int count;
+ * public String getGreeting() {
+ * return "Hello (Transform called " + incrCount() + " times)";
+ * }
+ * protected int incrCount() {
+ * return ++count;
+ * }
+ * }
+ */
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQAwwbMpPdPdWkU+6UJnvqa7v4VBdcuq2vkMBQAAcAAAAHhWNBIAAAAAAAAAAEgEAAAa" +
+ "AAAAcAAAAAkAAADYAAAABQAAAPwAAAABAAAAOAEAAAgAAABAAQAAAQAAAIABAABsAwAAoAEAADoC" +
+ "AABDAgAASwIAAGUCAABoAgAAawIAAG8CAABzAgAAjQIAAJ0CAADBAgAA4QIAAPUCAAAJAwAAJAMA" +
+ "ADMDAAA+AwAAQQMAAE4DAABWAwAAXQMAAGoDAAB1AwAAewMAAIUDAACMAwAAAwAAAAcAAAAIAAAA" +
+ "CQAAAAoAAAALAAAADAAAAA0AAAAQAAAAAwAAAAAAAAAAAAAABAAAAAYAAAAAAAAABQAAAAcAAAAs" +
+ "AgAABgAAAAcAAAA0AgAAEAAAAAgAAAAAAAAAAQAAABMAAAABAAQAAQAAAAEAAQAUAAAAAQAAABUA" +
+ "AAAFAAQAAQAAAAcABAABAAAABwACABIAAAAHAAMAEgAAAAcAAQAXAAAAAQAAAAEAAAAFAAAAAAAA" +
+ "AA4AAAA4BAAAEwQAAAAAAAACAAEAAAAAACgCAAAHAAAAUhAAANgAAAFZEAAADwAAAAQAAQACAAAA" +
+ "JAIAABsAAABuEAIAAwAKACIBBwBwEAQAAQAaAgIAbiAGACEAbiAFAAEAGgAAAG4gBgABAG4QBwAB" +
+ "AAwAEQAAAAEAAQABAAAAIAIAAAQAAABwEAMAAAAOAAMADgAGAA4ACQAOAAEAAAAAAAAAAQAAAAYA" +
+ "ByB0aW1lcykABjxpbml0PgAYSGVsbG8gKFRyYW5zZm9ybSBjYWxsZWQgAAFJAAFMAAJMSQACTEwA" +
+ "GExhcnQvVGVzdDE5OTkkVHJhbnNmb3JtOwAOTGFydC9UZXN0MTk5OTsAIkxkYWx2aWsvYW5ub3Rh" +
+ "dGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwASTGph" +
+ "dmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAGUxqYXZhL2xhbmcvU3RyaW5nQnVp" +
+ "bGRlcjsADVRlc3QxOTk5LmphdmEACVRyYW5zZm9ybQABVgALYWNjZXNzRmxhZ3MABmFwcGVuZAAF" +
+ "Y291bnQAC2dldEdyZWV0aW5nAAlpbmNyQ291bnQABG5hbWUACHRvU3RyaW5nAAV2YWx1ZQB2fn5E" +
+ "OHsiY29tcGlsYXRpb24tbW9kZSI6ImRlYnVnIiwibWluLWFwaSI6MSwic2hhLTEiOiI2MGRhNGQ2" +
+ "N2IzODFjNDI0Njc3NTdjNDlmYjZlNTU3NTZkODhhMmYzIiwidmVyc2lvbiI6IjEuNy4xMi1kZXYi" +
+ "fQACAwEYGAICBAIRBAkWFw8AAQECAAIAgYAEiAQBAcADAQSgAwAAAAAAAgAAAAQEAAAKBAAALAQA" +
+ "AAAAAAAAAAAAAAAAABAAAAAAAAAAAQAAAAAAAAABAAAAGgAAAHAAAAACAAAACQAAANgAAAADAAAA" +
+ "BQAAAPwAAAAEAAAAAQAAADgBAAAFAAAACAAAAEABAAAGAAAAAQAAAIABAAABIAAAAwAAAKABAAAD" +
+ "IAAAAwAAACACAAABEAAAAgAAACwCAAACIAAAGgAAADoCAAAEIAAAAgAAAAQEAAAAIAAAAQAAABME" +
+ "AAADEAAAAgAAACgEAAAGIAAAAQAAADgEAAAAEAAAAQAAAEgEAAA=");
- public static void run() {
- Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
- doTest(new SubTransform());
- }
+ public static void run() {
+ Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+ doTest(new SubTransform());
+ }
- public static void doTest(SubTransform t) {
- t.sayHi();
- t.sayHi();
- t.sayHi();
- Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
- t.sayHi();
- }
+ public static void doTest(SubTransform t) {
+ t.sayHi();
+ t.sayHi();
+ t.sayHi();
+ Redefinition.doCommonStructuralClassRedefinition(Transform.class, DEX_BYTES);
+ t.sayHi();
+ }
}
diff --git a/test/2000-virtual-list-structural/build b/test/2000-virtual-list-structural/build
deleted file mode 100755
index f8496bec..0000000
--- a/test/2000-virtual-list-structural/build
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Stop on failure.
-set -e
-
-# Deref the symlink.
-mv src-ex/java/util/AbstractCollection.java src-ex/java/util/AbstractCollection.bak
-cp src-ex/java/util/AbstractCollection.bak src-ex/java/util/AbstractCollection.java
-
-# Patch the copied version.
-patch src-ex/java/util/AbstractCollection.java AbstractCollection.patch
-
-USE_DESUGAR=false ./default-build "$@"
-
-# restore the symlink
-rm src-ex/java/util/AbstractCollection.java
-mv src-ex/java/util/AbstractCollection.bak src-ex/java/util/AbstractCollection.java
diff --git a/test/2000-virtual-list-structural/build.py b/test/2000-virtual-list-structural/build.py
new file mode 100644
index 0000000..4791995
--- /dev/null
+++ b/test/2000-virtual-list-structural/build.py
@@ -0,0 +1,24 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build(use_desugar=False)
+
+ os.rename(ctx.test_dir / "src-ex/java/util/AbstractCollection.bak",
+ ctx.test_dir / "src-ex/java/util/AbstractCollection.java")
diff --git a/test/2000-virtual-list-structural/generate-sources b/test/2000-virtual-list-structural/generate-sources
new file mode 100755
index 0000000..5857b40
--- /dev/null
+++ b/test/2000-virtual-list-structural/generate-sources
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# 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.
+
+# Stop on failure.
+set -e
+
+# Deref the symlink.
+mv src-ex/java/util/AbstractCollection.java src-ex/java/util/AbstractCollection.bak
+cp src-ex/java/util/AbstractCollection.bak src-ex/java/util/AbstractCollection.java
+
+# Patch the copied version.
+patch -s src-ex/java/util/AbstractCollection.java AbstractCollection.patch
diff --git a/test/2000-virtual-list-structural/run b/test/2000-virtual-list-structural/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/2000-virtual-list-structural/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/2000-virtual-list-structural/run.py b/test/2000-virtual-list-structural/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/2000-virtual-list-structural/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/2001-virtual-structural-multithread/run b/test/2001-virtual-structural-multithread/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/2001-virtual-structural-multithread/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/2001-virtual-structural-multithread/run.py b/test/2001-virtual-structural-multithread/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/2001-virtual-structural-multithread/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/2002-virtual-structural-initializing/run b/test/2002-virtual-structural-initializing/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/2002-virtual-structural-initializing/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/2002-virtual-structural-initializing/run.py b/test/2002-virtual-structural-initializing/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/2002-virtual-structural-initializing/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/2003-double-virtual-structural/run b/test/2003-double-virtual-structural/run
deleted file mode 100755
index b59f97c..0000000
--- a/test/2003-double-virtual-structural/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/2003-double-virtual-structural/run.py b/test/2003-double-virtual-structural/run.py
new file mode 100644
index 0000000..882774b
--- /dev/null
+++ b/test/2003-double-virtual-structural/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/2004-double-virtual-structural-abstract/run b/test/2004-double-virtual-structural-abstract/run
deleted file mode 100755
index b59f97c..0000000
--- a/test/2004-double-virtual-structural-abstract/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/2004-double-virtual-structural-abstract/run.py b/test/2004-double-virtual-structural-abstract/run.py
new file mode 100644
index 0000000..882774b
--- /dev/null
+++ b/test/2004-double-virtual-structural-abstract/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/2005-pause-all-redefine-multithreaded/build.py b/test/2005-pause-all-redefine-multithreaded/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/2005-pause-all-redefine-multithreaded/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/2005-pause-all-redefine-multithreaded/run b/test/2005-pause-all-redefine-multithreaded/run
deleted file mode 100755
index b59f97c..0000000
--- a/test/2005-pause-all-redefine-multithreaded/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-./default-run "$@" --jvmti --runtime-option -Xopaque-jni-ids:true
diff --git a/test/2005-pause-all-redefine-multithreaded/run.py b/test/2005-pause-all-redefine-multithreaded/run.py
new file mode 100644
index 0000000..882774b
--- /dev/null
+++ b/test/2005-pause-all-redefine-multithreaded/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/2005-pause-all-redefine-multithreaded/test-metadata.json b/test/2005-pause-all-redefine-multithreaded/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/2005-pause-all-redefine-multithreaded/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/2006-virtual-structural-finalizing/run b/test/2006-virtual-structural-finalizing/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/2006-virtual-structural-finalizing/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/2006-virtual-structural-finalizing/run.py b/test/2006-virtual-structural-finalizing/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/2006-virtual-structural-finalizing/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/2007-virtual-structural-finalizable/run b/test/2007-virtual-structural-finalizable/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/2007-virtual-structural-finalizable/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/2007-virtual-structural-finalizable/run.py b/test/2007-virtual-structural-finalizable/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/2007-virtual-structural-finalizable/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/2008-redefine-then-old-reflect-field/run b/test/2008-redefine-then-old-reflect-field/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/2008-redefine-then-old-reflect-field/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/2008-redefine-then-old-reflect-field/run.py b/test/2008-redefine-then-old-reflect-field/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/2008-redefine-then-old-reflect-field/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/2009-structural-local-ref/run b/test/2009-structural-local-ref/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/2009-structural-local-ref/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/2009-structural-local-ref/run.py b/test/2009-structural-local-ref/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/2009-structural-local-ref/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/2011-stack-walk-concurrent-instrument/stack_walk_concurrent.cc b/test/2011-stack-walk-concurrent-instrument/stack_walk_concurrent.cc
index 5eaaa05..78bb772 100644
--- a/test/2011-stack-walk-concurrent-instrument/stack_walk_concurrent.cc
+++ b/test/2011-stack-walk-concurrent-instrument/stack_walk_concurrent.cc
@@ -89,9 +89,8 @@
ScopedSuspendAll ssa(__FUNCTION__);
Runtime::Current()->GetInstrumentation()->InstrumentThreadStack(other,
/* deopt_all_frames= */ false);
- MutexLock mu(Thread::Current(), *Locks::thread_suspend_count_lock_);
- bool updated = other->ModifySuspendCount(Thread::Current(), -1, nullptr, SuspendReason::kInternal);
- CHECK(updated);
+ bool resumed = art::Runtime::Current()->GetThreadList()->Resume(other, SuspendReason::kInternal);
+ CHECK(resumed);
instrumented = true;
return;
}
diff --git a/test/2012-structural-redefinition-failures-jni-id/run b/test/2012-structural-redefinition-failures-jni-id/run
deleted file mode 100755
index 03e41a5..0000000
--- a/test/2012-structural-redefinition-failures-jni-id/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/2012-structural-redefinition-failures-jni-id/run.py b/test/2012-structural-redefinition-failures-jni-id/run.py
new file mode 100644
index 0000000..9ef412d
--- /dev/null
+++ b/test/2012-structural-redefinition-failures-jni-id/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/2031-zygote-compiled-frame-deopt/native-wait.cc b/test/2031-zygote-compiled-frame-deopt/native-wait.cc
index bd1d224..fb45345 100644
--- a/test/2031-zygote-compiled-frame-deopt/native-wait.cc
+++ b/test/2031-zygote-compiled-frame-deopt/native-wait.cc
@@ -42,7 +42,7 @@
}
runtime->SetAsZygoteChild(/*is_system_server=*/false, /*is_zygote=*/false);
runtime->AddCompilerOption("--debuggable");
- runtime->SetJavaDebuggable(true);
+ runtime->SetRuntimeDebugState(Runtime::RuntimeDebugState::kJavaDebuggableAtInit);
{
// Deoptimize the boot image as it may be non-debuggable.
ScopedSuspendAll ssa(__FUNCTION__);
diff --git a/test/2031-zygote-compiled-frame-deopt/run b/test/2031-zygote-compiled-frame-deopt/run
deleted file mode 100755
index 900099f..0000000
--- a/test/2031-zygote-compiled-frame-deopt/run
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2020 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.
-
-# The -Xopaque-jni-ids makes sure we can do structural redefinition. The --add-libdir-argument tells
-# default-run to pass the directory where the jvmti-agent is so we can load it later. The others
-# set the process to zygote mode and setup the jit cache size. We use a larger than normal jit-size
-# to avoid having to deal with jit-gc, a complication that's not relevant to this test.
-./default-run "$@" --runtime-option -Xopaque-jni-ids:true --add-libdir-argument --runtime-option -Xzygote --runtime-option -Xjitinitialsize:64M
diff --git a/test/2031-zygote-compiled-frame-deopt/run.py b/test/2031-zygote-compiled-frame-deopt/run.py
new file mode 100644
index 0000000..1dedf3b
--- /dev/null
+++ b/test/2031-zygote-compiled-frame-deopt/run.py
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# Copyright 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # The -Xopaque-jni-ids makes sure we can do structural redefinition. The --add-libdir-argument tells
+ # default-run to pass the directory where the jvmti-agent is so we can load it later. The others
+ # set the process to zygote mode and setup the jit cache size. We use a larger than normal jit-size
+ # to avoid having to deal with jit-gc, a complication that's not relevant to this test.
+ ctx.default_run(
+ args,
+ runtime_option=[
+ "-Xopaque-jni-ids:true", "-Xzygote", "-Xjitinitialsize:64M"
+ ],
+ add_libdir_argument=True)
diff --git a/test/2033-shutdown-mechanics/check b/test/2033-shutdown-mechanics/check
deleted file mode 100755
index 9523159..0000000
--- a/test/2033-shutdown-mechanics/check
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# The daemon thread seems to occasionally get stopped before finishing.
-# Check that the actual output is a line-by-line prefix of expected.
-head -n $(wc -l < $2) $1 | diff --strip-trailing-cr -q - "$2" >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4" >/dev/null
diff --git a/test/2033-shutdown-mechanics/run.py b/test/2033-shutdown-mechanics/run.py
new file mode 100644
index 0000000..3dfc965
--- /dev/null
+++ b/test/2033-shutdown-mechanics/run.py
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # The daemon thread seems to occasionally get stopped before finishing.
+ # Check that the actual output is a line-by-line prefix of expected.
+ ctx.run(
+ fr"head -n $(wc -l < '{args.stdout_file}') expected-stdout.txt > expected-stdout.txt.tmp &&"
+ fr"mv expected-stdout.txt.tmp expected-stdout.txt")
diff --git a/test/2034-spaces-in-SimpleName/build b/test/2034-spaces-in-SimpleName/build
deleted file mode 100755
index 8261ed2..0000000
--- a/test/2034-spaces-in-SimpleName/build
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Stop on failure and be verbose.
-set -e -x
-
-export ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-9.2.jar"
-
-# generate Java bytecode with ASM
-cd src_gen
-${JAVA:-java} -cp "$ASM_JAR:." SpacesInSimpleName.java
-mkdir ../classes && mv Main.class ../classes/Main.class
-cd ..
-
-# Use API level 10000 for spaces in SimpleName
-USE_DESUGAR=false ./default-build "$@" --api-level 10000
diff --git a/test/2034-spaces-in-SimpleName/build.py b/test/2034-spaces-in-SimpleName/build.py
new file mode 100644
index 0000000..c392e24
--- /dev/null
+++ b/test/2034-spaces-in-SimpleName/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+# Use API level 10000 for spaces in SimpleName
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build(use_desugar=False, api_level=10000)
diff --git a/test/2034-spaces-in-SimpleName/generate-sources b/test/2034-spaces-in-SimpleName/generate-sources
new file mode 100755
index 0000000..2e921a7
--- /dev/null
+++ b/test/2034-spaces-in-SimpleName/generate-sources
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# 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.
+
+# Stop on failure
+set -e
+
+export ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-9.2.jar"
+
+# generate Java bytecode with ASM
+cd src_gen
+${JAVA:-java} -cp "$ASM_JAR:." SpacesInSimpleName.java
+mkdir ../classes && mv Main.class ../classes/Main.class
+cd ..
diff --git a/test/2035-structural-native-method/run b/test/2035-structural-native-method/run
deleted file mode 100755
index ff387ff..0000000
--- a/test/2035-structural-native-method/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2020 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/2035-structural-native-method/run.py b/test/2035-structural-native-method/run.py
new file mode 100644
index 0000000..3774a6c
--- /dev/null
+++ b/test/2035-structural-native-method/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/2036-structural-subclass-shadow/run b/test/2036-structural-subclass-shadow/run
deleted file mode 100755
index ff387ff..0000000
--- a/test/2036-structural-subclass-shadow/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2020 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/2036-structural-subclass-shadow/run.py b/test/2036-structural-subclass-shadow/run.py
new file mode 100644
index 0000000..3774a6c
--- /dev/null
+++ b/test/2036-structural-subclass-shadow/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, runtime_option=["-Xopaque-jni-ids:true"])
diff --git a/test/2038-hiddenapi-jvmti-ext/build b/test/2038-hiddenapi-jvmti-ext/build
deleted file mode 100644
index f4b029f..0000000
--- a/test/2038-hiddenapi-jvmti-ext/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-USE_HIDDENAPI=true ./default-build "$@"
diff --git a/test/2038-hiddenapi-jvmti-ext/build.py b/test/2038-hiddenapi-jvmti-ext/build.py
new file mode 100644
index 0000000..942bb00
--- /dev/null
+++ b/test/2038-hiddenapi-jvmti-ext/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(use_hiddenapi=True)
diff --git a/test/2038-hiddenapi-jvmti-ext/run b/test/2038-hiddenapi-jvmti-ext/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/2038-hiddenapi-jvmti-ext/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/2038-hiddenapi-jvmti-ext/run.py b/test/2038-hiddenapi-jvmti-ext/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/2038-hiddenapi-jvmti-ext/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/2039-load-transform-larger/run b/test/2039-load-transform-larger/run
deleted file mode 100755
index 144c17d..0000000
--- a/test/2039-load-transform-larger/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2021 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 --no-app-image
diff --git a/test/2039-load-transform-larger/run.py b/test/2039-load-transform-larger/run.py
new file mode 100644
index 0000000..157a24b
--- /dev/null
+++ b/test/2039-load-transform-larger/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, app_image=False)
diff --git a/test/2040-huge-native-alloc/build.py b/test/2040-huge-native-alloc/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/2040-huge-native-alloc/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/2040-huge-native-alloc/src/Main.java b/test/2040-huge-native-alloc/src/Main.java
index 59e1266..3c8ae23 100644
--- a/test/2040-huge-native-alloc/src/Main.java
+++ b/test/2040-huge-native-alloc/src/Main.java
@@ -24,7 +24,7 @@
int allocated = 0;
int deallocated = 0;
static Object lock = new Object();
- final static int MAX_TRIES = 4;
+ final static int MAX_TRIES = 10;
WeakReference<BufferHolder>[] references = new WeakReference[HOW_MANY_HUGE];
class BufferHolder {
@@ -61,6 +61,14 @@
if (new Main().tryToRun(i == MAX_TRIES)) {
break;
}
+ if (i == MAX_TRIES / 2) {
+ // Maybe some transient CPU load is causing issues here?
+ try {
+ Thread.sleep(3000);
+ } catch (InterruptedException ignored) {
+ System.out.println("Unexpected interrupt");
+ }
+ }
// Clean up and try again.
Runtime.getRuntime().gc();
System.runFinalization();
@@ -84,7 +92,10 @@
if (startingGcNum != getGcNum()) {
// Happens rarely, fail and retry.
- return false;
+ if (!lastChance) {
+ return false;
+ }
+ System.out.println("Triggered early GC");
}
// One of the notifications should block for GC to catch up.
long actualTime = timeNotifications();
diff --git a/test/2040-huge-native-alloc/test-metadata.json b/test/2040-huge-native-alloc/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/2040-huge-native-alloc/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/2041-bad-cleaner/build.py b/test/2041-bad-cleaner/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/2041-bad-cleaner/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/2041-bad-cleaner/expected-stdout.txt b/test/2041-bad-cleaner/expected-stdout.txt
index 848a352..db36097 100644
--- a/test/2041-bad-cleaner/expected-stdout.txt
+++ b/test/2041-bad-cleaner/expected-stdout.txt
@@ -2,4 +2,3 @@
Cleaner started and sleeping briefly...
Cleaner done snoozing.
Cleaner sleeping forever now.
-exit status: 2
diff --git a/test/2041-bad-cleaner/run b/test/2041-bad-cleaner/run
deleted file mode 100755
index 54747ee..0000000
--- a/test/2041-bad-cleaner/run
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/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.
-
-# The test logs error messages which is expected, discard them.
-export ANDROID_LOG_TAGS='*:f'
-
-# Squash the exit status and put it in expected
-./default-run --external-log-tags "${@}"
-echo "exit status:" $?
diff --git a/test/2041-bad-cleaner/run.py b/test/2041-bad-cleaner/run.py
new file mode 100644
index 0000000..74fcbb9
--- /dev/null
+++ b/test/2041-bad-cleaner/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, android_log_tags="*:f", expected_exit_code=2)
diff --git a/test/2041-bad-cleaner/test-metadata.json b/test/2041-bad-cleaner/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/2041-bad-cleaner/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/2042-checker-dce-always-throw/src/Main.java b/test/2042-checker-dce-always-throw/src/Main.java
index 99738a7..e164917 100644
--- a/test/2042-checker-dce-always-throw/src/Main.java
+++ b/test/2042-checker-dce-always-throw/src/Main.java
@@ -20,12 +20,31 @@
assertEquals(0, $noinline$testSimplifyThrow(1));
// Basic test for non-trivial blocks (i.e. not just an invoke and a Goto)
+ assertEquals(0, $noinline$testSimplifyThrowAndPrint(1));
assertEquals(0, $noinline$testSimplifyTwoThrows(1));
+ assertEquals(0, $noinline$testSimplifyWithArgument(1));
// Try catch tests
assertEquals(0, $noinline$testDoNotSimplifyInTry(1));
assertEquals(0, $noinline$testSimplifyInCatch(1));
- assertEquals(0, $noinline$testDoNotSimplifyInCatchInOuterTry(1));
+ assertEquals(0, $noinline$testDoNotSimplifyInCatchInOuterTry(1, 1));
+
+ // Test that we update the phis correctly after simplifying an always throwing method, and
+ // recomputing dominance.
+ assertEquals(0, $noinline$testUpdatePhisCorrectly(1));
+ assertEquals(0, $noinline$testDeleteAllUsesBeforeDeletingInstruction(1));
+
+ // SimplifyAlwaysThrows for blocks without a goto at the end
+ assertEquals(0, $noinline$testEndsWithIf(1, 1));
+ assertEquals(0, $noinline$testEndsWithReturn(1));
+ // Since we cannot use `assertEquals`, not throwing would be the success.
+ $noinline$testEndsWithReturnVoid(1);
+ assertEquals(0, $noinline$testEndsWithSwitch(1, 1));
+ assertEquals(0, $noinline$testEndsWithThrow(1));
+ assertEquals(0, $noinline$testEndsWithTryBoundary(1));
+
+ // SimplifyAlwaysThrows for invokes in catch blocks
+ assertEquals(0, $noinline$testInsideCatch(1));
}
private static void alwaysThrows() throws Error {
@@ -51,6 +70,28 @@
return 0;
}
+ /// CHECK-START: int Main.$noinline$testSimplifyThrowAndPrint(int) dead_code_elimination$after_inlining (before)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: InvokeVirtual method_name:java.io.PrintStream.println
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
+ /// CHECK-EVAL: "<<ExitBlock>>" != "<<TargetBlock>>"
+
+ /// CHECK-START: int Main.$noinline$testSimplifyThrowAndPrint(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<ExitBlock>>
+
+ /// CHECK-START: int Main.$noinline$testSimplifyThrowAndPrint(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: InvokeVirtual method_name:java.io.PrintStream.println
+ private static int $noinline$testSimplifyThrowAndPrint(int num) {
+ if (num == 0) {
+ alwaysThrows();
+ System.out.println("I am unrechable!");
+ }
+ return 0;
+ }
+
/// CHECK-START: int Main.$noinline$testSimplifyTwoThrows(int) dead_code_elimination$after_inlining (before)
/// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
/// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock>> method_name:Main.alwaysThrows always_throws:true
@@ -60,10 +101,14 @@
/// CHECK-START: int Main.$noinline$testSimplifyTwoThrows(int) dead_code_elimination$after_inlining (after)
/// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
- /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock>> method_name:Main.alwaysThrows always_throws:true
/// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
/// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<ExitBlock>>
+ // Check that the second `alwaysThrows` gets removed.
+ /// CHECK-START: int Main.$noinline$testSimplifyTwoThrows(int) dead_code_elimination$after_inlining (after)
+ /// CHECK: InvokeStaticOrDirect method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-NOT: InvokeStaticOrDirect method_name:Main.alwaysThrows always_throws:true
+
// Tests that we simplify the always throwing branch directly to the exit, even with blocks that
// are not just the throwing instruction and a Goto.
private static int $noinline$testSimplifyTwoThrows(int num) {
@@ -74,6 +119,35 @@
return 0;
}
+ private static int throwIfZero(int num) {
+ if (num == 0) {
+ throw new Error("num is 0!");
+ }
+ return num / num;
+ }
+
+ /// CHECK-START: int Main.$noinline$testSimplifyWithArgument(int) dead_code_elimination$after_inlining (before)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.throwIfZero always_throws:true
+ /// CHECK-DAG: InvokeVirtual method_name:java.io.PrintStream.println
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
+ /// CHECK-EVAL: "<<ExitBlock>>" != "<<TargetBlock>>"
+
+ /// CHECK-START: int Main.$noinline$testSimplifyWithArgument(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.throwIfZero always_throws:true
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<ExitBlock>>
+
+ /// CHECK-START: int Main.$noinline$testSimplifyWithArgument(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: InvokeVirtual method_name:java.io.PrintStream.println
+ private static int $noinline$testSimplifyWithArgument(int num) {
+ if (num == 0) {
+ throwIfZero(0);
+ System.out.println("I am unrechable!");
+ }
+ return 0;
+ }
+
/// CHECK-START: int Main.$noinline$testSimplifyThrowWithTryCatch(int) dead_code_elimination$after_inlining (before)
/// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
/// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
@@ -140,7 +214,7 @@
/// CHECK: TryBoundary kind:entry
// Consistency check to that we do not simplify it by the last DCE pass either
- /// CHECK-START: int Main.$noinline$testDoNotSimplifyInTry(int) dead_code_elimination$final (after)
+ /// CHECK-START: int Main.$noinline$testDoNotSimplifyInTry(int) dead_code_elimination$after_bce (after)
/// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
/// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
/// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
@@ -190,25 +264,25 @@
}
}
- /// CHECK-START: int Main.$noinline$testDoNotSimplifyInCatchInOuterTry(int) dead_code_elimination$after_inlining (before)
+ /// CHECK-START: int Main.$noinline$testDoNotSimplifyInCatchInOuterTry(int, int) dead_code_elimination$after_inlining (before)
/// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
/// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
/// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
/// CHECK-EVAL: "<<ExitBlock>>" != "<<TargetBlock>>"
- /// CHECK-START: int Main.$noinline$testDoNotSimplifyInCatchInOuterTry(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-START: int Main.$noinline$testDoNotSimplifyInCatchInOuterTry(int, int) dead_code_elimination$after_inlining (after)
/// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
/// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
/// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
/// CHECK-EVAL: "<<ExitBlock>>" != "<<TargetBlock>>"
// Consistency check to make sure we have the try catches in the graph at this stage.
- /// CHECK-START: int Main.$noinline$testDoNotSimplifyInCatchInOuterTry(int) dead_code_elimination$after_inlining (before)
+ /// CHECK-START: int Main.$noinline$testDoNotSimplifyInCatchInOuterTry(int, int) dead_code_elimination$after_inlining (before)
/// CHECK-DAG: TryBoundary kind:entry
/// CHECK-DAG: TryBoundary kind:entry
// Consistency check to that we do not simplify it by the last DCE pass either
- /// CHECK-START: int Main.$noinline$testDoNotSimplifyInCatchInOuterTry(int) dead_code_elimination$final (after)
+ /// CHECK-START: int Main.$noinline$testDoNotSimplifyInCatchInOuterTry(int, int) dead_code_elimination$after_bce (after)
/// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
/// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
/// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
@@ -217,13 +291,15 @@
// Similar to testSimplifyInCatch, but now the throw is in an outer try and we shouldn't simplify
// it. Like in testDoNotSimplifyInTry, we need the help of the inliner to have an invoke followed
// by a Goto.
- private static int $noinline$testDoNotSimplifyInCatchInOuterTry(int num) {
+ private static int $noinline$testDoNotSimplifyInCatchInOuterTry(int num, int other_num) {
try {
try {
throw new Error();
} catch (Error e) {
if (num == 0) {
- $inline$testDoNotSimplifyInner(num);
+ // We use `other_num` here because otherwise we propagate the knowledge that `num` equals
+ // zero.
+ $inline$testDoNotSimplifyInner(other_num);
}
return 0;
}
@@ -232,6 +308,254 @@
}
}
+ // Check that when we perform SimplifyAlwaysThrows, that the phi for `phi_value` exists, and that
+ // we correctly update it after running DCE.
+
+ /// CHECK-START: int Main.$noinline$testUpdatePhisCorrectly(int) dead_code_elimination$after_inlining (before)
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Const5:i\d+>> IntConstant 5
+ /// CHECK-DAG: <<ReturnValue:i\d+>> Phi [<<Const0>>,<<Const5>>]
+ /// CHECK-DAG: Return [<<ReturnValue>>]
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<TargetBlock:B\d+>>
+ /// CHECK-EVAL: "<<ExitBlock>>" != "<<TargetBlock>>"
+
+ /// CHECK-START: int Main.$noinline$testUpdatePhisCorrectly(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: Return [<<Const0>>]
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<ExitBlock>>
+
+ /// CHECK-START: int Main.$noinline$testUpdatePhisCorrectly(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: Phi
+ private static int $noinline$testUpdatePhisCorrectly(int num) {
+ int phi_value = 0;
+ if (num == 0) {
+ alwaysThrows();
+ phi_value = 5;
+ }
+ return phi_value;
+ }
+
+ // Test to check that we delete all uses before the instruction.
+ private static int $noinline$foo(int num) {
+ return num;
+ }
+
+ /// CHECK-START: int Main.$noinline$testDeleteAllUsesBeforeDeletingInstruction(int) dead_code_elimination$after_inlining (before)
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Invoke:i\d+>> InvokeStaticOrDirect method_name:Main.$noinline$foo
+ /// CHECK-DAG: <<ReturnValue:i\d+>> Phi [<<Const0>>,<<Invoke>>]
+ /// CHECK-DAG: Return [<<ReturnValue>>]
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: TryBoundary block:<<InvokeBlock>>
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+
+ /// CHECK-START: int Main.$noinline$testDeleteAllUsesBeforeDeletingInstruction(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0
+ /// CHECK-DAG: Return [<<Const0>>]
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<ExitBlock>>
+
+ /// CHECK-START: int Main.$noinline$testDeleteAllUsesBeforeDeletingInstruction(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: Phi
+ private static int $noinline$testDeleteAllUsesBeforeDeletingInstruction(int num) {
+ int phi_value = 0;
+ if (num == 0) {
+ alwaysThrows();
+ try {
+ phi_value = $noinline$foo(2);
+ } catch (Error e) {
+ throw new Error("We shouldn't hit this");
+ }
+ }
+ return phi_value;
+ }
+
+ /// CHECK-START: int Main.$noinline$testEndsWithIf(int, int) dead_code_elimination$after_inlining (before)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: If block:<<InvokeBlock>>
+ /// CHECK-DAG: InvokeVirtual method_name:java.io.PrintStream.println
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+
+ /// CHECK-START: int Main.$noinline$testEndsWithIf(int, int) dead_code_elimination$after_inlining (after)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<ExitBlock>>
+
+ /// CHECK-START: int Main.$noinline$testEndsWithIf(int, int) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: InvokeVirtual method_name:java.io.PrintStream.println
+ private static int $noinline$testEndsWithIf(int num, int other_num) {
+ if (num == 0) {
+ alwaysThrows();
+ if (other_num == 0) {
+ System.out.println("I am unrechable!");
+ }
+ }
+ return 0;
+ }
+
+ /// CHECK-START: int Main.$noinline$testEndsWithReturn(int) dead_code_elimination$after_inlining (before)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Return block:<<InvokeBlock>>
+ /// CHECK-DAG: InvokeVirtual method_name:java.io.PrintStream.println
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+
+ /// CHECK-START: int Main.$noinline$testEndsWithReturn(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<ExitBlock>>
+
+ /// CHECK-START: int Main.$noinline$testEndsWithReturn(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: InvokeVirtual method_name:java.io.PrintStream.println
+ private static int $noinline$testEndsWithReturn(int num) {
+ if (num == 0) {
+ alwaysThrows();
+ System.out.println("I am unrechable!");
+ return 1;
+ }
+ return 0;
+ }
+
+ /// CHECK-START: void Main.$noinline$testEndsWithReturnVoid(int) dead_code_elimination$after_inlining (before)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: ReturnVoid block:<<InvokeBlock>>
+ /// CHECK-DAG: InvokeVirtual method_name:java.io.PrintStream.println
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+
+ /// CHECK-START: void Main.$noinline$testEndsWithReturnVoid(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<ExitBlock>>
+
+ /// CHECK-START: void Main.$noinline$testEndsWithReturnVoid(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: InvokeVirtual method_name:java.io.PrintStream.println
+ private static void $noinline$testEndsWithReturnVoid(int num) {
+ if (num == 0) {
+ alwaysThrows();
+ System.out.println("I am unrechable!");
+ return;
+ }
+ return;
+ }
+
+ /// CHECK-START: int Main.$noinline$testEndsWithSwitch(int, int) dead_code_elimination$after_inlining (before)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: PackedSwitch block:<<InvokeBlock>>
+ /// CHECK-DAG: InvokeVirtual method_name:java.io.PrintStream.println
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+
+ /// CHECK-START: int Main.$noinline$testEndsWithSwitch(int, int) dead_code_elimination$after_inlining (after)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<ExitBlock>>
+
+ /// CHECK-START: int Main.$noinline$testEndsWithSwitch(int, int) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: InvokeVirtual method_name:java.io.PrintStream.println
+ private static int $noinline$testEndsWithSwitch(int num, int other_num) {
+ if (num == 0) {
+ alwaysThrows();
+ System.out.println("I am unrechable!");
+ int result = 10;
+ switch (other_num) {
+ case 1:
+ result = 100;
+ break;
+ case 2:
+ result = 300;
+ break;
+ case 3:
+ result = 500;
+ break;
+ case 4:
+ result = 700;
+ break;
+ }
+ return result;
+ }
+ return 0;
+ }
+
+ /// CHECK-START: int Main.$noinline$testEndsWithThrow(int) dead_code_elimination$after_inlining (before)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Throw block:<<InvokeBlock>>
+ /// CHECK-DAG: InvokeVirtual method_name:java.io.PrintStream.println
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+
+ /// CHECK-START: int Main.$noinline$testEndsWithThrow(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<ExitBlock>>
+
+ /// CHECK-START: int Main.$noinline$testEndsWithThrow(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: InvokeVirtual method_name:java.io.PrintStream.println
+ private static int $noinline$testEndsWithThrow(int num) {
+ if (num == 0) {
+ alwaysThrows();
+ System.out.println("I am unrechable!");
+ throw new Error("Other error");
+ }
+ return 0;
+ }
+
+ /// CHECK-START: int Main.$noinline$testEndsWithTryBoundary(int) dead_code_elimination$after_inlining (before)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: TryBoundary block:<<InvokeBlock>>
+ /// CHECK-DAG: InvokeVirtual method_name:java.io.PrintStream.println
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+
+ /// CHECK-START: int Main.$noinline$testEndsWithTryBoundary(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<ExitBlock>>
+
+ /// CHECK-START: int Main.$noinline$testEndsWithTryBoundary(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: InvokeVirtual method_name:java.io.PrintStream.println
+ private static int $noinline$testEndsWithTryBoundary(int num) {
+ if (num == 0) {
+ alwaysThrows();
+ System.out.println("I am unrechable!");
+ try {
+ alwaysThrows();
+ } catch (Error e) {
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ // Empty method to forbid the try from disappearing
+ private static void $noinline$emptyMethod() {}
+
+ /// CHECK-START: int Main.$noinline$testInsideCatch(int) dead_code_elimination$after_inlining (before)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Return block:<<InvokeBlock>>
+ /// CHECK-DAG: InvokeVirtual method_name:java.io.PrintStream.println
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+
+ /// CHECK-START: int Main.$noinline$testInsideCatch(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-DAG: InvokeStaticOrDirect block:<<InvokeBlock:B\d+>> method_name:Main.alwaysThrows always_throws:true
+ /// CHECK-DAG: Exit block:<<ExitBlock:B\d+>>
+ /// CHECK-DAG: Goto block:<<InvokeBlock>> target:<<ExitBlock>>
+
+ /// CHECK-START: int Main.$noinline$testInsideCatch(int) dead_code_elimination$after_inlining (after)
+ /// CHECK-NOT: InvokeVirtual method_name:java.io.PrintStream.println
+ private static int $noinline$testInsideCatch(int num) {
+ if (num == 0) {
+ try {
+ $noinline$emptyMethod();
+ } catch (Error e) {
+ alwaysThrows();
+ System.out.println("I am unrechable!");
+ return 1;
+ }
+ }
+ return 0;
+ }
+
static void assertEquals(int expected, int actual) {
if (expected != actual) {
throw new AssertionError("Expected " + expected + " got " + actual);
diff --git a/test/2042-reference-processing/Android.bp b/test/2042-reference-processing/Android.bp
new file mode 100644
index 0000000..a73b8d0
--- /dev/null
+++ b/test/2042-reference-processing/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2042-reference-processing`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-2042-reference-processing",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-2042-reference-processing-expected-stdout",
+ ":art-run-test-2042-reference-processing-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-2042-reference-processing-expected-stdout",
+ out: ["art-run-test-2042-reference-processing-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-2042-reference-processing-expected-stderr",
+ out: ["art-run-test-2042-reference-processing-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2042-reference-processing/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2042-reference-processing/expected-stderr.txt
diff --git a/test/2042-reference-processing/expected-stdout.txt b/test/2042-reference-processing/expected-stdout.txt
new file mode 100644
index 0000000..8b3e832
--- /dev/null
+++ b/test/2042-reference-processing/expected-stdout.txt
@@ -0,0 +1,2 @@
+Starting
+Finished
diff --git a/test/2042-reference-processing/info.txt b/test/2042-reference-processing/info.txt
new file mode 100644
index 0000000..e2d7a99
--- /dev/null
+++ b/test/2042-reference-processing/info.txt
@@ -0,0 +1,6 @@
+A test for reference processing correctness.
+
+The emphasis here is on fundamental properties. In particular, references to
+unreachable referents should be enqueued, and this should ensure that uncleared
+References don't point to objects for which References were enqueued. We also
+check various other ordering properties for java.lang.ref.References.
diff --git a/test/2042-reference-processing/src/Main.java b/test/2042-reference-processing/src/Main.java
new file mode 100644
index 0000000..ed67052
--- /dev/null
+++ b/test/2042-reference-processing/src/Main.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.math.BigInteger;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.HashMap;
+import java.util.TreeMap;
+
+/**
+ * Test that objects get finalized and their references cleared in the right order.
+ *
+ * We maintain a list of nominally MAX_LIVE_OBJS numbered finalizable objects.
+ * We then alternately drop the last 50, and add 50 more. When we see an object finalized
+ * or its reference cleared, we make sure that the preceding objects in its group of 50
+ * have also had their references cleared. We also perform a number of other more
+ * straightforward checks, such as ensuring that all references are eventually cleared,
+ * and all objects are finalized.
+ */
+public class Main {
+ // TODO(b/216481630) Enable CHECK_PHANTOM_REFS. This currently occasionally reports a few
+ // PhantomReferences as not enqueued. If this report is correct, this needs to be tracked
+ // down and fixed.
+ static final boolean CHECK_PHANTOM_REFS = false;
+
+ static final int MAX_LIVE_OBJS = 150;
+ static final int DROP_OBJS = 50; // Number of linked objects dropped in each batch.
+ static final int MIN_LIVE_OBJS = MAX_LIVE_OBJS - DROP_OBJS;
+ static final int TOTAL_OBJS = 200_000; // Allocate this many finalizable objects in total.
+ static final boolean REPORT_DROPS = false;
+ static volatile boolean pleaseStop;
+
+ AtomicInteger totalFinalized = new AtomicInteger(0);
+ Object phantomRefsLock = new Object();
+ int maxDropped = 0;
+ int liveObjects = 0;
+
+ // Number of next finalizable object to be allocated.
+ int nextAllocated = 0;
+
+ // List of finalizable objects in descending order. We add to the front and drop
+ // from the rear.
+ FinalizableObject listHead;
+
+ // A possibly incomplete list of FinalizableObject indices that were finalized, but
+ // have yet to be checked for consistency with reference processing.
+ ArrayBlockingQueue<Integer> finalized = new ArrayBlockingQueue<>(20_000);
+
+ // Maps from object number to Reference; Cleared references are deleted when queues are
+ // processed.
+ TreeMap<Integer, MyWeakReference> weakRefs = new TreeMap<>();
+ HashMap<Integer, MyPhantomReference> phantomRefs = new HashMap<>();
+
+ class FinalizableObject {
+ int n;
+ FinalizableObject next;
+ FinalizableObject(int num, FinalizableObject nextObj) {
+ n = num;
+ next = nextObj;
+ }
+ protected void finalize() {
+ if (!inPhantomRefs(n)) {
+ System.out.println("PhantomRef enqueued before finalizer ran");
+ }
+ totalFinalized.incrementAndGet();
+ if (!finalized.offer(n) && REPORT_DROPS) {
+ System.out.println("Dropped finalization of " + n);
+ }
+ }
+ }
+ ReferenceQueue<FinalizableObject> refQueue = new ReferenceQueue<>();
+ class MyWeakReference extends WeakReference<FinalizableObject> {
+ int n;
+ MyWeakReference(FinalizableObject obj) {
+ super(obj, refQueue);
+ n = obj.n;
+ }
+ };
+ class MyPhantomReference extends PhantomReference<FinalizableObject> {
+ int n;
+ MyPhantomReference(FinalizableObject obj) {
+ super(obj, refQueue);
+ n = obj.n;
+ }
+ }
+ boolean inPhantomRefs(int n) {
+ synchronized(phantomRefsLock) {
+ MyPhantomReference ref = phantomRefs.get(n);
+ if (ref == null) {
+ return false;
+ }
+ if (ref.n != n) {
+ System.out.println("phantomRef retrieval failed");
+ }
+ return true;
+ }
+ }
+
+ void CheckOKToClearWeak(int num) {
+ if (num > maxDropped) {
+ System.out.println("WeakRef to live object " + num + " was cleared/enqueued.");
+ }
+ int batchEnd = (num / DROP_OBJS + 1) * DROP_OBJS;
+ for (MyWeakReference wr : weakRefs.subMap(num + 1, batchEnd).values()) {
+ if (wr.n <= num || wr.n / DROP_OBJS != num / DROP_OBJS) {
+ throw new AssertionError("MyWeakReference logic error!");
+ }
+ // wr referent was dropped in same batch and precedes it in list.
+ if (wr.get() != null) {
+ // This violates the WeakReference spec, and can result in strong references
+ // to objects that have been cleaned.
+ System.out.println("WeakReference to " + wr.n
+ + " was erroneously cleared after " + num);
+ }
+ }
+ }
+
+ void CheckOKToClearPhantom(int num) {
+ if (num > maxDropped) {
+ System.out.println("PhantomRef to live object " + num + " was enqueued.");
+ }
+ MyWeakReference wr = weakRefs.get(num);
+ if (wr != null && wr.get() != null) {
+ System.out.println("PhantomRef cleared before WeakRef for " + num);
+ }
+ }
+
+ void emptyAndCheckQueues() {
+ // Check recently finalized objects for consistency with cleared references.
+ while (true) {
+ Integer num = finalized.poll();
+ if (num == null) {
+ break;
+ }
+ MyWeakReference wr = weakRefs.get(num);
+ if (wr != null) {
+ if (wr.n != num) {
+ System.out.println("Finalization logic error!");
+ }
+ if (wr.get() != null) {
+ System.out.println("Finalizing object with uncleared reference");
+ }
+ }
+ CheckOKToClearWeak(num);
+ }
+ // Check recently enqueued references for consistency.
+ while (true) {
+ Reference<FinalizableObject> ref = (Reference<FinalizableObject>) refQueue.poll();
+ if (ref == null) {
+ break;
+ }
+ if (ref instanceof MyWeakReference) {
+ MyWeakReference wr = (MyWeakReference) ref;
+ if (wr.get() != null) {
+ System.out.println("WeakRef " + wr.n + " enqueued but not cleared");
+ }
+ CheckOKToClearWeak(wr.n);
+ if (weakRefs.remove(Integer.valueOf(wr.n)) != ref) {
+ System.out.println("Missing WeakReference: " + wr.n);
+ }
+ } else if (ref instanceof MyPhantomReference) {
+ MyPhantomReference pr = (MyPhantomReference) ref;
+ CheckOKToClearPhantom(pr.n);
+ if (phantomRefs.remove(Integer.valueOf(pr.n)) != ref) {
+ System.out.println("Missing PhantomReference: " + pr.n);
+ }
+ } else {
+ System.out.println("Found unrecognized reference in queue");
+ }
+ }
+ }
+
+
+ /**
+ * Add n objects to the head of the list. These will be assigned the next n consecutive
+ * numbers after the current head of the list.
+ */
+ void addObjects(int n) {
+ for (int i = 0; i < n; ++i) {
+ int me = nextAllocated++;
+ listHead = new FinalizableObject(me, listHead);
+ weakRefs.put(me, new MyWeakReference(listHead));
+ synchronized(phantomRefsLock) {
+ phantomRefs.put(me, new MyPhantomReference(listHead));
+ }
+ }
+ liveObjects += n;
+ }
+
+ /**
+ * Drop n finalizable objects from the tail of the list. These are the lowest-numbered objects
+ * in the list.
+ */
+ void dropObjects(int n) {
+ FinalizableObject list = listHead;
+ FinalizableObject last = null;
+ if (n > liveObjects) {
+ System.out.println("Removing too many elements");
+ }
+ if (liveObjects == n) {
+ maxDropped = list.n;
+ listHead = null;
+ } else {
+ final int skip = liveObjects - n;
+ for (int i = 0; i < skip; ++i) {
+ last = list;
+ list = list.next;
+ }
+ int expected = nextAllocated - skip - 1;
+ if (list.n != expected) {
+ System.out.println("dropObjects found " + list.n + " but expected " + expected);
+ }
+ maxDropped = expected;
+ last.next = null;
+ }
+ liveObjects -= n;
+ }
+
+ void testLoop() {
+ System.out.println("Starting");
+ addObjects(MIN_LIVE_OBJS);
+ final int ITERS = (TOTAL_OBJS - MIN_LIVE_OBJS) / DROP_OBJS;
+ for (int i = 0; i < ITERS; ++i) {
+ addObjects(DROP_OBJS);
+ if (liveObjects != MAX_LIVE_OBJS) {
+ System.out.println("Unexpected live object count");
+ }
+ dropObjects(DROP_OBJS);
+ emptyAndCheckQueues();
+ }
+ dropObjects(MIN_LIVE_OBJS);
+ if (liveObjects != 0 || listHead != null) {
+ System.out.println("Unexpected live objecs at end");
+ }
+ if (maxDropped != TOTAL_OBJS - 1) {
+ System.out.println("Unexpected dropped object count: " + maxDropped);
+ }
+ for (int i = 0; i < 2; ++i) {
+ Runtime.getRuntime().gc();
+ System.runFinalization();
+ emptyAndCheckQueues();
+ }
+ if (!weakRefs.isEmpty()) {
+ System.out.println("Weak Reference map nonempty size = " + weakRefs.size());
+ }
+ if (CHECK_PHANTOM_REFS && !phantomRefs.isEmpty()) {
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ System.out.println("Unexpected interrupt");
+ }
+ if (!phantomRefs.isEmpty()) {
+ System.out.println("Phantom Reference map nonempty size = " + phantomRefs.size());
+ System.out.print("First elements:");
+ int i = 0;
+ for (MyPhantomReference pr : phantomRefs.values()) {
+ System.out.print(" " + pr.n);
+ if (++i > 10) {
+ break;
+ }
+ }
+ System.out.println("");
+ }
+ }
+ if (totalFinalized.get() != TOTAL_OBJS) {
+ System.out.println("Finalized only " + totalFinalized + " objects");
+ }
+ }
+
+ static Runnable causeGCs = new Runnable() {
+ public void run() {
+ // Allocate a lot.
+ BigInteger counter = BigInteger.ZERO;
+ while (!pleaseStop) {
+ counter = counter.add(BigInteger.TEN);
+ }
+ // Look at counter to reduce chance of optimizing out the allocation.
+ if (counter.longValue() % 10 != 0) {
+ System.out.println("Bad causeGCs counter value: " + counter);
+ }
+ }
+ };
+
+ public static void main(String[] args) throws Exception {
+ Main theTest = new Main();
+ Thread gcThread = new Thread(causeGCs);
+ gcThread.setDaemon(true); // Terminate if main thread dies.
+ gcThread.start();
+ theTest.testLoop();
+ pleaseStop = true;
+ gcThread.join();
+ System.out.println("Finished");
+ }
+}
diff --git a/test/2043-reference-pauses/Android.bp b/test/2043-reference-pauses/Android.bp
new file mode 100644
index 0000000..a84aea2
--- /dev/null
+++ b/test/2043-reference-pauses/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2043-reference-pauses`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-2043-reference-pauses",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-2043-reference-pauses-expected-stdout",
+ ":art-run-test-2043-reference-pauses-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-2043-reference-pauses-expected-stdout",
+ out: ["art-run-test-2043-reference-pauses-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-2043-reference-pauses-expected-stderr",
+ out: ["art-run-test-2043-reference-pauses-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2043-reference-pauses/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2043-reference-pauses/expected-stderr.txt
diff --git a/test/2043-reference-pauses/expected-stdout.txt b/test/2043-reference-pauses/expected-stdout.txt
new file mode 100644
index 0000000..8b3e832
--- /dev/null
+++ b/test/2043-reference-pauses/expected-stdout.txt
@@ -0,0 +1,2 @@
+Starting
+Finished
diff --git a/test/2043-reference-pauses/info.txt b/test/2043-reference-pauses/info.txt
new file mode 100644
index 0000000..f76fa32
--- /dev/null
+++ b/test/2043-reference-pauses/info.txt
@@ -0,0 +1,5 @@
+Tests WeakReference processing and retention of objects needed by finalizers.
+
+Can be used as Reference.get() blocking benchmark by setting PRINT_TIMES to
+true. This will print maximum observed latencies for Reference.get() when
+significant memory is only reachable from SoftReferences and Finalizers.
diff --git a/test/2043-reference-pauses/src/Main.java b/test/2043-reference-pauses/src/Main.java
new file mode 100644
index 0000000..e390155
--- /dev/null
+++ b/test/2043-reference-pauses/src/Main.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.lang.ref.SoftReference;
+import java.math.BigInteger;
+import java.util.ArrayList;
+
+/**
+ * Basic test of WeakReferences with large amounts of memory that's only reachable through
+ * finalizers. Also makes sure that finalizer-reachable data is not collected.
+ * Can easily be modified to time Reference.get() blocking.
+ */
+public class Main {
+ static final boolean PRINT_TIMES = false; // true will cause benchmark failure.
+ // Data structures repeatedly allocated in background to trigger GC.
+ // Size of finalizer reachable trees.
+ static final int TREE_HEIGHT = 15; // Trees contain 2^TREE_HEIGHT -1 allocated objects.
+ // Number of finalizable tree-owning objects that exist at one point.
+ static final int N_RESURRECTING_OBJECTS = 10;
+ // Number of short-lived, not finalizer-reachable, objects allocated between trees.
+ static final int N_PLAIN_OBJECTS = 20_000;
+ // Number of SoftReferences to CBTs we allocate.
+ static final int N_SOFTREFS = 10;
+
+ static final boolean BACKGROUND_GC_THREAD = true;
+ static final int NBATCHES = 10;
+ static final int NREFS = PRINT_TIMES ? 1_000_000 : 300_000; // Multiple of NBATCHES.
+ static final int REFS_PER_BATCH = NREFS / NBATCHES;
+
+ static volatile boolean pleaseStop = false;
+
+ // Large array of WeakReferences filled and accessed by tests below.
+ ArrayList<WeakReference<Integer>> weakRefs = new ArrayList<>(NREFS);
+
+ /**
+ * Complete binary tree data structure. make(n) takes O(2^n) space.
+ */
+ static class CBT {
+ CBT left;
+ CBT right;
+ CBT(CBT l, CBT r) {
+ left = l;
+ right = r;
+ }
+ static CBT make(int n) {
+ if (n == 0) {
+ return null;
+ }
+ return new CBT(make(n - 1), make(n - 1));
+ }
+ /**
+ * Check that path described by bit-vector path has the correct length.
+ */
+ void check(int n, int path) {
+ CBT current = this;
+ for (int i = 0; i < n; i++, path = path >>> 1) {
+ // Unexpectedly short paths result in NPE.
+ if ((path & 1) == 0) {
+ current = current.left;
+ } else {
+ current = current.right;
+ }
+ }
+ if (current != null) {
+ System.out.println("Complete binary tree path too long");
+ }
+ }
+ }
+
+
+ /**
+ * A finalizable object that refers to O(2^TREE_HEIGHT) otherwise unreachable memory.
+ * When finalized, it creates a new identical object, making sure that one always stays
+ * around.
+ */
+ static class ResurrectingObject {
+ CBT stuff;
+ ResurrectingObject() {
+ stuff = CBT.make(TREE_HEIGHT);
+ }
+ static ResurrectingObject a[] = new ResurrectingObject[2];
+ static int i = 0;
+ static synchronized void allocOne() {
+ a[(++i) % 2] = new ResurrectingObject();
+ // Check the previous one to make it hard to optimize anything out.
+ if (i > 1) {
+ a[(i + 1) % 2].stuff.check(TREE_HEIGHT, i /* weirdly interpreted as path */);
+ }
+ }
+ protected void finalize() {
+ stuff.check(TREE_HEIGHT, 42 /* Some path descriptor */);
+ // Allocate a new one to replace this one.
+ allocOne();
+ }
+ }
+
+ void fillWeakRefs() {
+ for (int i = 0; i < NREFS; ++i) {
+ weakRefs.add(null);
+ }
+ }
+
+ /*
+ * Return maximum observed time in nanos to dereference a WeakReference to an unreachable
+ * object. weakRefs is presumed to be pre-filled to have the correct size.
+ */
+ long timeUnreachableInner() {
+ long maxNanos = 0;
+ // Fill weakRefs with WeakReferences to unreachable integers, a batch at a time.
+ // Then time and test .get() calls on carefully sampled array entries, some of which
+ // will have been cleared.
+ for (int i = 0; i < NBATCHES; ++i) {
+ for (int j = 0; j < REFS_PER_BATCH; ++j) {
+ weakRefs.set(i * REFS_PER_BATCH + j,
+ new WeakReference(new Integer(i * REFS_PER_BATCH + j)));
+ }
+ try {
+ Thread.sleep(50);
+ } catch (InterruptedException e) {
+ System.out.println("Unexpected exception");
+ }
+ // Iterate over the filled-in section of weakRefs, but look only at a subset of the
+ // elements, making sure the subsets for different top-level iterations are disjoint.
+ // Otherwise the get() calls here will extend the lifetimes of the referents, and we
+ // may never see any cleared WeakReferences.
+ for (int j = (i + 1) * REFS_PER_BATCH - i - 1; j >= 0; j -= NBATCHES) {
+ WeakReference<Integer> wr = weakRefs.get(j);
+ if (wr != null) {
+ long startNanos = System.nanoTime();
+ Integer referent = wr.get();
+ long totalNanos = System.nanoTime() - startNanos;
+ if (referent == null) {
+ // Optimization to reduce max space use and scanning time.
+ weakRefs.set(j, null);
+ }
+ maxNanos = Math.max(maxNanos, totalNanos);
+ if (referent != null && referent.intValue() != j) {
+ System.out.println("Unexpected referent; expected " + j + " got "
+ + referent.intValue());
+ }
+ }
+ }
+ }
+ return maxNanos;
+ }
+
+ /*
+ * Wrapper for the above that also checks that references were reclaimed.
+ * We do this separately to make sure any stack references from the core of the
+ * test are gone. Empirically, we otherwise sometimes see the zeroth WeakReference
+ * not reclaimed.
+ */
+ long timeUnreachable() {
+ long maxNanos = timeUnreachableInner();
+ Runtime.getRuntime().gc();
+ System.runFinalization(); // Presumed to wait for reference clearing.
+ for (int i = 0; i < NREFS; ++i) {
+ if (weakRefs.get(i) != null && weakRefs.get(i).get() != null) {
+ System.out.println("WeakReference to " + i + " wasn't cleared");
+ }
+ }
+ return maxNanos;
+ }
+
+ /**
+ * Return maximum observed time in nanos to dereference a WeakReference to a reachable
+ * object. Overwrites weakRefs, which is presumed to have NREFS entries already.
+ */
+ long timeReachable() {
+ long maxNanos = 0;
+ // Similar to the above, but we use WeakReferences to otherwise reachable objects,
+ // which should thus not get cleared.
+ Integer[] strongRefs = new Integer[NREFS];
+ for (int i = 0; i < NBATCHES; ++i) {
+ for (int j = i * REFS_PER_BATCH; j < (i + 1) * REFS_PER_BATCH; ++j) {
+ Integer newObj = new Integer(j);
+ strongRefs[j] = newObj;
+ weakRefs.set(j, new WeakReference(newObj));
+ }
+ for (int j = (i + 1) * REFS_PER_BATCH - 1; j >= 0; --j) {
+ WeakReference<Integer> wr = weakRefs.get(j);
+ long startNanos = System.nanoTime();
+ Integer referent = wr.get();
+ long totalNanos = System.nanoTime() - startNanos;
+ maxNanos = Math.max(maxNanos, totalNanos);
+ if (referent == null) {
+ System.out.println("Unexpectedly cleared referent at " + j);
+ } else if (referent.intValue() != j) {
+ System.out.println("Unexpected reachable referent; expected " + j + " got "
+ + referent.intValue());
+ }
+ }
+ }
+ Reference.reachabilityFence(strongRefs);
+ return maxNanos;
+ }
+
+ void runTest() {
+ System.out.println("Starting");
+ fillWeakRefs();
+ long unreachableNanos = timeUnreachable();
+ if (PRINT_TIMES) {
+ System.out.println("Finished timeUnrechable()");
+ }
+ long reachableNanos = timeReachable();
+ String unreachableMillis =
+ String. format("%,.3f", ((double) unreachableNanos) / 1_000_000);
+ String reachableMillis =
+ String. format("%,.3f", ((double) reachableNanos) / 1_000_000);
+ if (PRINT_TIMES) {
+ System.out.println(
+ "Max time for WeakReference.get (unreachable): " + unreachableMillis);
+ System.out.println(
+ "Max time for WeakReference.get (reachable): " + reachableMillis);
+ }
+ // Only report extremely egregious pauses to avoid spurious failures.
+ if (unreachableNanos > 10_000_000_000L) {
+ System.out.println("WeakReference.get (unreachable) time unreasonably long");
+ }
+ if (reachableNanos > 10_000_000_000L) {
+ System.out.println("WeakReference.get (reachable) time unreasonably long");
+ }
+ }
+
+ /**
+ * Allocate and GC a lot, while keeping significant amounts of finalizer and
+ * SoftReference-reachable memory around.
+ */
+ static Runnable allocFinalizable = new Runnable() {
+ public void run() {
+ // Allocate and drop some finalizable objects that take a long time
+ // to mark. Designed to be hard to optimize away. Each of these objects will
+ // build a new one in its finalizer before really going away.
+ ArrayList<SoftReference<CBT>> softRefs = new ArrayList<>(N_SOFTREFS);
+ for (int i = 0; i < N_SOFTREFS; ++i) {
+ // These should not normally get reclaimed, since we shouldn't run out of
+ // memory. They do increase tracing time.
+ softRefs.add(new SoftReference(CBT.make(TREE_HEIGHT)));
+ }
+ for (int i = 0; i < N_RESURRECTING_OBJECTS; ++i) {
+ ResurrectingObject.allocOne();
+ }
+ BigInteger counter = BigInteger.ZERO;
+ for (int i = 1; !pleaseStop; ++i) {
+ // Allocate a lot of short-lived objects, using BigIntegers to minimize the chance
+ // of the allocation getting optimized out. This makes things slightly more
+ // realistic, since not all objects will be finalizer reachable.
+ for (int j = 0; j < N_PLAIN_OBJECTS / 2; ++j) {
+ counter = counter.add(BigInteger.TEN);
+ }
+ // Look at counter to reduce chance of optimizing out the allocation.
+ if (counter.longValue() % 10 != 0) {
+ System.out.println("Bad allocFinalizable counter value: " + counter);
+ }
+ // Explicitly collect here, mostly to prevent heap growth. Otherwise we get
+ // ahead of the GC and eventually block on it.
+ Runtime.getRuntime().gc();
+ if (PRINT_TIMES && i % 100 == 0) {
+ System.out.println("Collected " + i + " times");
+ }
+ }
+ // To be safe, access softRefs.
+ final CBT sample = softRefs.get(N_SOFTREFS / 2).get();
+ if (sample != null) {
+ sample.check(TREE_HEIGHT, 47 /* some path descriptor */);
+ }
+ }
+ };
+
+ public static void main(String[] args) throws Exception {
+ Main theTest = new Main();
+ Thread allocThread = null;
+ if (BACKGROUND_GC_THREAD) {
+ allocThread = new Thread(allocFinalizable);
+ allocThread.setDaemon(true); // Terminate if main thread dies.
+ allocThread.start();
+ }
+ theTest.runTest();
+ if (BACKGROUND_GC_THREAD) {
+ pleaseStop = true;
+ allocThread.join();
+ }
+ System.out.println("Finished");
+ }
+}
diff --git a/test/2044-get-stack-traces/Android.bp b/test/2044-get-stack-traces/Android.bp
new file mode 100644
index 0000000..79aea7c
--- /dev/null
+++ b/test/2044-get-stack-traces/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2044-get-stack-traces`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-2044-get-stack-traces",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-2044-get-stack-traces-expected-stdout",
+ ":art-run-test-2044-get-stack-traces-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-2044-get-stack-traces-expected-stdout",
+ out: ["art-run-test-2044-get-stack-traces-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-2044-get-stack-traces-expected-stderr",
+ out: ["art-run-test-2044-get-stack-traces-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2044-get-stack-traces/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2044-get-stack-traces/expected-stderr.txt
diff --git a/test/2044-get-stack-traces/expected-stdout.txt b/test/2044-get-stack-traces/expected-stdout.txt
new file mode 100644
index 0000000..d396083
--- /dev/null
+++ b/test/2044-get-stack-traces/expected-stdout.txt
@@ -0,0 +1,8 @@
+Starting
+Starting helper
+Starting helper
+Starting helper
+Starting helper
+Starting helper
+Finished worker stack traces
+Finished
diff --git a/test/2044-get-stack-traces/info.txt b/test/2044-get-stack-traces/info.txt
new file mode 100644
index 0000000..f9cac7a
--- /dev/null
+++ b/test/2044-get-stack-traces/info.txt
@@ -0,0 +1,4 @@
+Tests multiple simultaneous calls to Thread.getStackTrace()
+
+This is a stress test for code under suspicion in the context of b/234542166
+and others.
diff --git a/test/2044-get-stack-traces/src/Main.java b/test/2044-get-stack-traces/src/Main.java
new file mode 100644
index 0000000..7840389
--- /dev/null
+++ b/test/2044-get-stack-traces/src/Main.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.lang.ref.SoftReference;
+import java.math.BigInteger;
+import java.util.ArrayList;
+
+/**
+ * We construct a main thread and worker threads, each retrieving stack traces
+ * from the other. Since there are multiple workers, we may get a large number
+ * of simultaneous stack trace attempts.
+ */
+public class Main {
+ static final int NUM_THREADS = 5;
+ static Thread mainThread;
+ static volatile boolean pleaseStop = false;
+
+ private static void getTrace(Thread t) {
+ StackTraceElement trace[] = t.getStackTrace();
+ if (!pleaseStop && (trace.length < 1 || trace.length > 20)) {
+ // If called from traceGetter, we were started by the main thread, and it was still
+ // running after the trace, so the main thread should have at least one frame on
+ // the stack. If called by main(), we waited for all the traceGetters to start,
+ // and didn't yet allow them to stop, so the same should be true.
+ System.out.println("Stack trace for " + t.getName() + " has size " + trace.length);
+ for (StackTraceElement e : trace) {
+ System.out.println(e.toString());
+ }
+ }
+ }
+
+ /**
+ * Repeatedly get and minimally check stack trace of main thread.
+ */
+ static Runnable traceGetter = new Runnable() {
+ public void run() {
+ System.out.println("Starting helper");
+ while (!pleaseStop) {
+ getTrace(mainThread);
+ }
+ }
+ };
+
+ public static void main(String[] args) throws Exception {
+ System.out.println("Starting");
+ Thread[] t = new Thread[NUM_THREADS];
+ mainThread = Thread.currentThread();
+ for (int i = 0; i < NUM_THREADS; ++i) {
+ t[i] = new Thread(traceGetter);
+ t[i].start();
+ }
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ System.out.println("Unexpectedly interrupted");
+ }
+ for (int i = 0; i < NUM_THREADS; ++i) {
+ getTrace(t[i]);
+ }
+ System.out.println("Finished worker stack traces");
+ long now = System.currentTimeMillis();
+ while (System.currentTimeMillis() - now < 2000) {
+ try {
+ Thread.sleep(1);
+ } catch (InterruptedException e) {
+ System.out.println("Unexpectedly interrupted");
+ }
+ }
+ pleaseStop = true;
+ System.out.println("Finished");
+ }
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2045-uffd-kernelfault/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2045-uffd-kernelfault/expected-stderr.txt
diff --git a/test/2045-uffd-kernelfault/expected-stdout.txt b/test/2045-uffd-kernelfault/expected-stdout.txt
new file mode 100644
index 0000000..a965a70
--- /dev/null
+++ b/test/2045-uffd-kernelfault/expected-stdout.txt
@@ -0,0 +1 @@
+Done
diff --git a/test/2045-uffd-kernelfault/info.txt b/test/2045-uffd-kernelfault/info.txt
new file mode 100644
index 0000000..c0967d5
--- /dev/null
+++ b/test/2045-uffd-kernelfault/info.txt
@@ -0,0 +1,2 @@
+Test that fault-handler doesn't cause userfaultfd kernel-faults, which are not
+allowed in unpriviledged processes.
diff --git a/test/2045-uffd-kernelfault/run.py b/test/2045-uffd-kernelfault/run.py
new file mode 100644
index 0000000..5b262bb
--- /dev/null
+++ b/test/2045-uffd-kernelfault/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # Limit the Java heap to 20MiB to force more GCs.
+ ctx.default_run(args, runtime_option=["-Xmx20m"])
diff --git a/test/2045-uffd-kernelfault/src/Main.java b/test/2045-uffd-kernelfault/src/Main.java
new file mode 100644
index 0000000..c5fac30
--- /dev/null
+++ b/test/2045-uffd-kernelfault/src/Main.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 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 {
+ // TODO: Reduce it once the userfaultfd GC is tested long enough.
+ static final long DURATION_IN_MILLIS = 10_000;
+
+ static public Object obj = null;
+ static public Object[] array = new Object[4096];
+
+ public static void main(String args[]) {
+ final long start_time = System.currentTimeMillis();
+ long end_time = start_time;
+ int idx = 0;
+ while (end_time - start_time < DURATION_IN_MILLIS) {
+ try {
+ // Trigger a null-pointer exception
+ System.out.println(obj.toString());
+ } catch (NullPointerException npe) {
+ // Small enough to be not allocated in large-object space and hence keep the compaction
+ // phase longer, while keeping marking phase shorter (as there aren't any references to
+ // chase).
+ array[idx++] = new byte[3000];
+ idx %= array.length;
+ }
+ end_time = System.currentTimeMillis();
+ }
+ System.out.println("Done");
+ }
+}
diff --git a/test/2230-profile-save-hotness/run b/test/2230-profile-save-hotness/run
deleted file mode 100644
index d0c49b6..0000000
--- a/test/2230-profile-save-hotness/run
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-#
-# 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.
-${RUN} \
- -Xcompiler-option --count-hotness-in-compiled-code \
- -Xcompiler-option --compiler-filter=speed \
- --runtime-option -Xps-profile-aot-code \
- --runtime-option -Xjitsaveprofilinginfo \
- --runtime-option -Xusejit:true "${@}"
diff --git a/test/2230-profile-save-hotness/run.py b/test/2230-profile-save-hotness/run.py
new file mode 100644
index 0000000..526b841
--- /dev/null
+++ b/test/2230-profile-save-hotness/run.py
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# 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.
+def run(ctx, args):
+ ctx.default_run(
+ args,
+ Xcompiler_option=[
+ "--count-hotness-in-compiled-code", "--compiler-filter=speed"
+ ],
+ runtime_option=[
+ "-Xps-profile-aot-code", "-Xjitsaveprofilinginfo", "-Xusejit:true"
+ ],
+ )
diff --git a/test/2232-write-metrics-to-log/Android.bp b/test/2232-write-metrics-to-log/Android.bp
index a64567e..6a1e68c 100644
--- a/test/2232-write-metrics-to-log/Android.bp
+++ b/test/2232-write-metrics-to-log/Android.bp
@@ -15,7 +15,7 @@
java_test {
name: "art-run-test-2232-write-metrics-to-log",
defaults: ["art-run-test-defaults"],
- test_config_template: ":art-run-test-target-template",
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
srcs: ["src/**/*.java"],
data: [
":art-run-test-2232-write-metrics-to-log-expected-stdout",
diff --git a/test/2232-write-metrics-to-log/check b/test/2232-write-metrics-to-log/check
deleted file mode 100755
index d12e8b1..0000000
--- a/test/2232-write-metrics-to-log/check
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2020 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Check that one of the metrics appears in stderr.
-grep 'ClassVerificationTotalTime' "$4" >/dev/null
-MSG_FOUND=$?
-
-if [[ $MSG_FOUND -ne 0 ]] ; then
- # Print out the log and return with error.
- cat "$4"
- exit 1
-fi
-
-# Success.
-exit 0
diff --git a/test/2232-write-metrics-to-log/expected-stderr.txt b/test/2232-write-metrics-to-log/expected-stderr.txt
index e69de29..5bc27fe 100644
--- a/test/2232-write-metrics-to-log/expected-stderr.txt
+++ b/test/2232-write-metrics-to-log/expected-stderr.txt
@@ -0,0 +1 @@
+ClassVerificationTotalTimeDelta
diff --git a/test/2232-write-metrics-to-log/run b/test/2232-write-metrics-to-log/run
deleted file mode 100755
index d34ec6c..0000000
--- a/test/2232-write-metrics-to-log/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2020 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.
-
-export ANDROID_LOG_TAGS="*:i"
-exec ${RUN} $@ --external-log-tags --runtime-option -Xmetrics-write-to-logcat:true --runtime-option -Xmetrics-reporting-mods:100
diff --git a/test/2232-write-metrics-to-log/run.py b/test/2232-write-metrics-to-log/run.py
new file mode 100644
index 0000000..89fe040
--- /dev/null
+++ b/test/2232-write-metrics-to-log/run.py
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args,
+ android_log_tags="*:i",
+ diff_min_log_tag="i",
+ runtime_option=[
+ "-Xmetrics-write-to-logcat:true", "-Xmetrics-reporting-mods:100"
+ ])
+
+ # Check that one of the metrics appears in stderr.
+ ctx.run(
+ fr"sed -i -n 's/.*\(ClassVerificationTotalTimeDelta\).*/\1/p' '{args.stderr_file}'"
+ )
diff --git a/test/2233-checker-remove-loop-suspend-check/Android.bp b/test/2233-checker-remove-loop-suspend-check/Android.bp
new file mode 100644
index 0000000..f581ee3
--- /dev/null
+++ b/test/2233-checker-remove-loop-suspend-check/Android.bp
@@ -0,0 +1,43 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2233-checker-remove-loop-suspend-check`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-2233-checker-remove-loop-suspend-check",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-2233-checker-remove-loop-suspend-check-expected-stdout",
+ ":art-run-test-2233-checker-remove-loop-suspend-check-expected-stderr",
+ ],
+ // Include the Java source files in the test's artifacts, to make Checker assertions
+ // available to the TradeFed test runner.
+ include_srcs: true,
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-2233-checker-remove-loop-suspend-check-expected-stdout",
+ out: ["art-run-test-2233-checker-remove-loop-suspend-check-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-2233-checker-remove-loop-suspend-check-expected-stderr",
+ out: ["art-run-test-2233-checker-remove-loop-suspend-check-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2233-checker-remove-loop-suspend-check/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2233-checker-remove-loop-suspend-check/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2233-checker-remove-loop-suspend-check/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2233-checker-remove-loop-suspend-check/expected-stdout.txt
diff --git a/test/2233-checker-remove-loop-suspend-check/info.txt b/test/2233-checker-remove-loop-suspend-check/info.txt
new file mode 100644
index 0000000..fe8d8eb
--- /dev/null
+++ b/test/2233-checker-remove-loop-suspend-check/info.txt
@@ -0,0 +1 @@
+Test to check the removal of SuspendCheck for finite simple plain loops.
diff --git a/test/2233-checker-remove-loop-suspend-check/src/Main.java b/test/2233-checker-remove-loop-suspend-check/src/Main.java
new file mode 100644
index 0000000..bea67e7
--- /dev/null
+++ b/test/2233-checker-remove-loop-suspend-check/src/Main.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2022 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 {
+
+ static final int ITERATIONS = 16;
+
+ // Test 1: This test checks whether the SuspendCheck is removed from the
+ // header.
+
+ /// CHECK-START-ARM64: void Main.$noinline$testRemoveSuspendCheck(int[]) disassembly (after)
+ /// CHECK: SuspendCheck loop:<<LoopId:B\d+>>
+ /// CHECK-NEXT: dex_pc:{{.*}}
+ /// CHECK: Goto loop:<<LoopId>>
+ /// CHECK-NEXT: b
+ /// CHECK-NOT: SuspendCheckSlowPathARM64
+
+ public static void $noinline$testRemoveSuspendCheck(int[] a) {
+ for (int i = 0; i < ITERATIONS; i++) {
+ a[i++] = i;
+ }
+ }
+
+ // Test 2: This test checks that the SuspendCheck is not removed from the
+ // header because it contains a call to another function.
+
+ /// CHECK-START-ARM64: void Main.testRemoveSuspendCheckWithCall(int[]) disassembly (after)
+ /// CHECK: SuspendCheck loop:<<LoopId:B\d+>>
+ /// CHECK: Goto loop:<<LoopId>>
+ /// CHECK-NEXT: ldr
+ /// CHECK: SuspendCheckSlowPathARM64
+ /// CHECK: SuspendCheckSlowPathARM64
+
+ public static void testRemoveSuspendCheckWithCall(int[] a) {
+ for (int i = 0; i < ITERATIONS; i++) {
+ a[i++] = i;
+ $noinline$testRemoveSuspendCheck(a);
+ }
+ }
+
+ // Test 3: This test checks that the SuspendCheck is not removed from the
+ // header because INSTR_COUNT * TRIP_COUNT exceeds the defined heuristic.
+
+ /// CHECK-START-ARM64: void Main.testRemoveSuspendCheckAboveHeuristic(int[]) disassembly (after)
+ /// CHECK: SuspendCheck loop:<<LoopId:B\d+>>
+ /// CHECK: Goto loop:<<LoopId>>
+ /// CHECK-NEXT: ldr
+ /// CHECK: SuspendCheckSlowPathARM64
+
+ public static void testRemoveSuspendCheckAboveHeuristic(int[] a) {
+ for (int i = 0; i < ITERATIONS * 6; i++) {
+ a[i++] = i;
+ }
+ }
+
+ // Test 4: This test checks that the SuspendCheck is not removed from the
+ // header because the trip count is not known at compile time.
+
+ /// CHECK-START-ARM64: void Main.testRemoveSuspendCheckUnknownCount(int[], int) disassembly (after)
+ /// CHECK: SuspendCheck loop:<<LoopId:B\d+>>
+ /// CHECK: Goto loop:<<LoopId>>
+ /// CHECK-NEXT: ldr
+ /// CHECK: SuspendCheckSlowPathARM64
+
+ public static void testRemoveSuspendCheckUnknownCount(int[] a, int n) {
+ for (int i = 0; i < n; i++) {
+ a[i++] = i;
+ }
+ }
+
+ public static void main(String[] args) {
+ int[] a = new int[100];
+ $noinline$testRemoveSuspendCheck(a);
+ testRemoveSuspendCheckWithCall(a);
+ testRemoveSuspendCheckAboveHeuristic(a);
+ testRemoveSuspendCheckUnknownCount(a, 4);
+ }
+}
diff --git a/test/2235-JdkUnsafeTest/build.py b/test/2235-JdkUnsafeTest/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/2235-JdkUnsafeTest/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/2235-JdkUnsafeTest/test-metadata.json b/test/2235-JdkUnsafeTest/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/2235-JdkUnsafeTest/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/2237-checker-inline-multidex/expected-stdout.txt b/test/2237-checker-inline-multidex/expected-stdout.txt
index 571349a..c2c127f 100644
--- a/test/2237-checker-inline-multidex/expected-stdout.txt
+++ b/test/2237-checker-inline-multidex/expected-stdout.txt
@@ -2,3 +2,4 @@
def
ghi
class Multi$Multi2
+122
diff --git a/test/2237-checker-inline-multidex/info.txt b/test/2237-checker-inline-multidex/info.txt
index fc6dc60..2e639ba 100644
--- a/test/2237-checker-inline-multidex/info.txt
+++ b/test/2237-checker-inline-multidex/info.txt
@@ -1 +1 @@
-Checks that we inline across dex files, even when we need an environment.
+Checks that we inline across dex files.
diff --git a/test/2237-checker-inline-multidex/src-multidex/Multi.java b/test/2237-checker-inline-multidex/src-multidex/Multi.java
index cd234c3..c830c59 100644
--- a/test/2237-checker-inline-multidex/src-multidex/Multi.java
+++ b/test/2237-checker-inline-multidex/src-multidex/Multi.java
@@ -38,4 +38,12 @@
}
private class Multi2 {}
+
+ public static int $inline$TryCatch(String str) {
+ try {
+ return Integer.parseInt(str);
+ } catch (NumberFormatException ex) {
+ return -1;
+ }
+ }
}
diff --git a/test/2237-checker-inline-multidex/src/Main.java b/test/2237-checker-inline-multidex/src/Main.java
index 7ab2e7f..7a2ca82 100644
--- a/test/2237-checker-inline-multidex/src/Main.java
+++ b/test/2237-checker-inline-multidex/src/Main.java
@@ -16,57 +16,73 @@
public class Main {
public static void main(String[] args) {
- System.out.println(testNeedsEnvironment());
- System.out.println(testNeedsBssEntryString());
- System.out.println(testNeedsBssEntryInvoke());
- System.out.println(testClass());
+ // Test that the cross-dex inlining is working for HInstructions that need an environment.
+ System.out.println($noinline$testNeedsEnvironment());
+
+ // Test that the cross-dex inlining is working for HInstructions that need a bss entry.
+ System.out.println($noinline$testNeedsBssEntryString());
+ System.out.println($noinline$testNeedsBssEntryInvoke());
+ System.out.println($noinline$testClass());
+
+ // Test that we are able to inline try catches across dex files.
+ System.out.println($noinline$testTryCatch());
}
- /// CHECK-START: java.lang.String Main.testNeedsEnvironment() inliner (before)
+ /// CHECK-START: java.lang.String Main.$noinline$testNeedsEnvironment() inliner (before)
/// CHECK: InvokeStaticOrDirect method_name:Multi.$inline$NeedsEnvironmentMultiDex
- /// CHECK-START: java.lang.String Main.testNeedsEnvironment() inliner (after)
+ /// CHECK-START: java.lang.String Main.$noinline$testNeedsEnvironment() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect method_name:Multi.$inline$NeedsEnvironmentMultiDex
- /// CHECK-START: java.lang.String Main.testNeedsEnvironment() inliner (after)
+ /// CHECK-START: java.lang.String Main.$noinline$testNeedsEnvironment() inliner (after)
/// CHECK: StringBuilderAppend
- public static String testNeedsEnvironment() {
+ private static String $noinline$testNeedsEnvironment() {
return Multi.$inline$NeedsEnvironmentMultiDex("abc");
}
- /// CHECK-START: java.lang.String Main.testNeedsBssEntryString() inliner (before)
+ /// CHECK-START: java.lang.String Main.$noinline$testNeedsBssEntryString() inliner (before)
/// CHECK: InvokeStaticOrDirect method_name:Multi.$inline$NeedsBssEntryStringMultiDex
- /// CHECK-START: java.lang.String Main.testNeedsBssEntryString() inliner (after)
+ /// CHECK-START: java.lang.String Main.$noinline$testNeedsBssEntryString() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect method_name:Multi.$inline$NeedsBssEntryStringMultiDex
- /// CHECK-START: java.lang.String Main.testNeedsBssEntryString() inliner (after)
+ /// CHECK-START: java.lang.String Main.$noinline$testNeedsBssEntryString() inliner (after)
/// CHECK: LoadString load_kind:BssEntry
- public static String testNeedsBssEntryString() {
+ private static String $noinline$testNeedsBssEntryString() {
return Multi.$inline$NeedsBssEntryStringMultiDex();
}
- /// CHECK-START: java.lang.String Main.testNeedsBssEntryInvoke() inliner (before)
+ /// CHECK-START: java.lang.String Main.$noinline$testNeedsBssEntryInvoke() inliner (before)
/// CHECK: InvokeStaticOrDirect method_name:Multi.$inline$NeedsBssEntryInvokeMultiDex
- /// CHECK-START: java.lang.String Main.testNeedsBssEntryInvoke() inliner (after)
+ /// CHECK-START: java.lang.String Main.$noinline$testNeedsBssEntryInvoke() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect method_name:Multi.$inline$NeedsBssEntryInvokeMultiDex
- /// CHECK-START: java.lang.String Main.testNeedsBssEntryInvoke() inliner (after)
+ /// CHECK-START: java.lang.String Main.$noinline$testNeedsBssEntryInvoke() inliner (after)
/// CHECK: InvokeStaticOrDirect method_name:Multi.$noinline$InnerInvokeMultiDex method_load_kind:BssEntry
- public static String testNeedsBssEntryInvoke() {
+ private static String $noinline$testNeedsBssEntryInvoke() {
return Multi.$inline$NeedsBssEntryInvokeMultiDex();
}
- /// CHECK-START: java.lang.Class Main.testClass() inliner (before)
+ /// CHECK-START: java.lang.Class Main.$noinline$testClass() inliner (before)
/// CHECK: InvokeStaticOrDirect method_name:Multi.NeedsBssEntryClassMultiDex
- /// CHECK-START: java.lang.Class Main.testClass() inliner (after)
+ /// CHECK-START: java.lang.Class Main.$noinline$testClass() inliner (after)
/// CHECK-NOT: InvokeStaticOrDirect method_name:Multi.NeedsBssEntryClassMultiDex
- /// CHECK-START: java.lang.Class Main.testClass() inliner (after)
+ /// CHECK-START: java.lang.Class Main.$noinline$testClass() inliner (after)
/// CHECK: LoadClass load_kind:BssEntry class_name:Multi$Multi2
- public static Class<?> testClass() {
+ private static Class<?> $noinline$testClass() {
return Multi.NeedsBssEntryClassMultiDex();
}
+
+
+ /// CHECK-START: int Main.$noinline$testTryCatch() inliner (before)
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testTryCatch() inliner (after)
+ /// CHECK: TryBoundary
+ private static int $noinline$testTryCatch() {
+ return Multi.$inline$TryCatch("123") + Multi.$inline$TryCatch("abc");
+ }
}
diff --git a/test/2238-checker-polymorphic-recursive-inlining/run b/test/2238-checker-polymorphic-recursive-inlining/run
deleted file mode 100644
index a4e2692..0000000
--- a/test/2238-checker-polymorphic-recursive-inlining/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-# Use a profile to put specific classes in the app image to trigger polymorphic inlining.
-exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile
diff --git a/test/2238-checker-polymorphic-recursive-inlining/run.py b/test/2238-checker-polymorphic-recursive-inlining/run.py
new file mode 100644
index 0000000..b85f926
--- /dev/null
+++ b/test/2238-checker-polymorphic-recursive-inlining/run.py
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # Use a profile to put specific classes in the app image to trigger polymorphic inlining.
+ ctx.default_run(
+ args, profile=True, Xcompiler_option=["--compiler-filter=speed-profile"])
diff --git a/test/2239-varhandle-perf/build b/test/2239-varhandle-perf/build
deleted file mode 100755
index 115a0fb..0000000
--- a/test/2239-varhandle-perf/build
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2022 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.
-
-# Make us exit on a failure
-set -e
-
-# Set variables for source directories. Using src-art so we use
-# VarHandles in the bootclasspath and can compile with the Java 8
-# compiler.
-MANUAL_SRC=src
-GENERATED_SRC=src2
-
-# Build the Java files
-mkdir -p src2
-
-# Generate tests and Main that covers both the generated tests and manual tests
-python3 ./util-src/generate_java.py "${GENERATED_SRC}"
-
-./default-build "$@" --experimental var-handles
diff --git a/test/2239-varhandle-perf/build.py b/test/2239-varhandle-perf/build.py
new file mode 100644
index 0000000..3466dfe
--- /dev/null
+++ b/test/2239-varhandle-perf/build.py
@@ -0,0 +1,22 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ if ctx.jvm:
+ return # The test does not build on JVM
+
+ ctx.default_build(api_level="var-handles")
diff --git a/test/2239-varhandle-perf/check b/test/2239-varhandle-perf/check
deleted file mode 100644
index 8ea102d..0000000
--- a/test/2239-varhandle-perf/check
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2020 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.
-
-# Dump the output of the benchmarks that run and report success. The
-# benchmarks ran successfully if we get as far as this script.
-
-cat "$2"
-exit 0
diff --git a/test/2239-varhandle-perf/generate-sources b/test/2239-varhandle-perf/generate-sources
new file mode 100755
index 0000000..310ad06
--- /dev/null
+++ b/test/2239-varhandle-perf/generate-sources
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Copyright 2022 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.
+
+# Make us exit on a failure
+set -e
+
+# Set variables for source directories. Using src-art so we use
+# VarHandles in the bootclasspath and can compile with the Java 8
+# compiler.
+MANUAL_SRC=src
+GENERATED_SRC=src2
+
+# Build the Java files
+mkdir -p src2
+
+# Generate tests and Main that covers both the generated tests and manual tests
+python3 ./util-src/generate_java.py "${GENERATED_SRC}"
diff --git a/test/2239-varhandle-perf/run.py b/test/2239-varhandle-perf/run.py
new file mode 100644
index 0000000..4e8755d
--- /dev/null
+++ b/test/2239-varhandle-perf/run.py
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# Copyright 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # Dump the output of the benchmarks that run and report success. The
+ # benchmarks ran successfully if we get as far as this script.
+ ctx.run(fr"cat '{args.stdout_file}'")
+
+ # Delete all output to make the diff unconditionally pass.
+ ctx.run(fr"> '{args.stdout_file}'")
diff --git a/test/2240-tracing-non-invokable-method/Android.bp b/test/2240-tracing-non-invokable-method/Android.bp
new file mode 100644
index 0000000..b3a2c79
--- /dev/null
+++ b/test/2240-tracing-non-invokable-method/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2240-tracing-non-invokable-method`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-2240-tracing-non-invokable-method-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-2240-tracing-non-invokable-method",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-2240-tracing-non-invokable-method-src"
+ ],
+ data: [
+ ":art-run-test-2240-tracing-non-invokable-method-expected-stdout",
+ ":art-run-test-2240-tracing-non-invokable-method-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-2240-tracing-non-invokable-method-expected-stdout",
+ out: ["art-run-test-2240-tracing-non-invokable-method-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-2240-tracing-non-invokable-method-expected-stderr",
+ out: ["art-run-test-2240-tracing-non-invokable-method-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2240-tracing-non-invokable-method/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2240-tracing-non-invokable-method/expected-stderr.txt
diff --git a/test/2240-tracing-non-invokable-method/expected-stdout.txt b/test/2240-tracing-non-invokable-method/expected-stdout.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/2240-tracing-non-invokable-method/expected-stdout.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/2240-tracing-non-invokable-method/info.txt b/test/2240-tracing-non-invokable-method/info.txt
new file mode 100644
index 0000000..27877aa
--- /dev/null
+++ b/test/2240-tracing-non-invokable-method/info.txt
@@ -0,0 +1,2 @@
+Tests that tracing handles non-invokable methods correctly without updating the
+entrypoints for non-invokable methods.
diff --git a/test/2240-tracing-non-invokable-method/run.py b/test/2240-tracing-non-invokable-method/run.py
new file mode 100644
index 0000000..ac17e68
--- /dev/null
+++ b/test/2240-tracing-non-invokable-method/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args, runtime_option=["-Xmethod-trace", "-Xmethod-trace-file:/dev/null"])
diff --git a/test/2240-tracing-non-invokable-method/src/Main.java b/test/2240-tracing-non-invokable-method/src/Main.java
new file mode 100644
index 0000000..e5a4e43
--- /dev/null
+++ b/test/2240-tracing-non-invokable-method/src/Main.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+interface Itf {
+ public default void m() throws Exception {
+ throw new Exception("Don't inline me");
+ }
+ public default void mConflict() throws Exception {
+ throw new Exception("Don't inline me");
+ }
+}
+
+// This is redefined in src2 with a mConflict method.
+interface Itf2 {
+}
+
+public class Main implements Itf, Itf2 {
+
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+
+ try {
+ itf.mConflict();
+ throw new Error("Expected IncompatibleClassChangeError");
+ } catch (Exception e) {
+ throw new Error("Unexpected exception");
+ } catch (IncompatibleClassChangeError e) {
+ // Expected.
+ }
+ }
+
+ static Itf itf = new Main();
+}
diff --git a/test/2240-tracing-non-invokable-method/src2/Itf2.java b/test/2240-tracing-non-invokable-method/src2/Itf2.java
new file mode 100644
index 0000000..e962411
--- /dev/null
+++ b/test/2240-tracing-non-invokable-method/src2/Itf2.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+interface Itf2 {
+ public default void mConflict(){
+ }
+}
diff --git a/test/2241-checker-inline-try-catch/Android.bp b/test/2241-checker-inline-try-catch/Android.bp
new file mode 100644
index 0000000..15708d6
--- /dev/null
+++ b/test/2241-checker-inline-try-catch/Android.bp
@@ -0,0 +1,43 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2241-checker-inline-try-catch`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-2241-checker-inline-try-catch",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-2241-checker-inline-try-catch-expected-stdout",
+ ":art-run-test-2241-checker-inline-try-catch-expected-stderr",
+ ],
+ // Include the Java source files in the test's artifacts, to make Checker assertions
+ // available to the TradeFed test runner.
+ include_srcs: true,
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-2241-checker-inline-try-catch-expected-stdout",
+ out: ["art-run-test-2241-checker-inline-try-catch-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-2241-checker-inline-try-catch-expected-stderr",
+ out: ["art-run-test-2241-checker-inline-try-catch-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2241-checker-inline-try-catch/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2241-checker-inline-try-catch/expected-stderr.txt
diff --git a/test/2241-checker-inline-try-catch/expected-stdout.txt b/test/2241-checker-inline-try-catch/expected-stdout.txt
new file mode 100644
index 0000000..6d127d1
--- /dev/null
+++ b/test/2241-checker-inline-try-catch/expected-stdout.txt
@@ -0,0 +1,2 @@
+Finally, a worthy opponent!
+Our battle it will be legendary!
diff --git a/test/2241-checker-inline-try-catch/info.txt b/test/2241-checker-inline-try-catch/info.txt
new file mode 100644
index 0000000..3b86006
--- /dev/null
+++ b/test/2241-checker-inline-try-catch/info.txt
@@ -0,0 +1 @@
+Tests that we inline try catches, as long as we don't inline inside of other try catches.
diff --git a/test/2241-checker-inline-try-catch/src/Main.java b/test/2241-checker-inline-try-catch/src/Main.java
new file mode 100644
index 0000000..a80fbd7
--- /dev/null
+++ b/test/2241-checker-inline-try-catch/src/Main.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2022 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) {
+ $noinline$testSingleTryCatch();
+ $noinline$testSingleTryCatchTwice();
+ $noinline$testSingleTryCatchDifferentInputs();
+ $noinline$testDifferentTryCatches();
+ $noinline$testTryCatchFinally();
+ $noinline$testTryCatchFinallyDifferentInputs();
+ $noinline$testRecursiveTryCatch();
+ $noinline$testDoNotInlineInsideTryInlineInsideCatch();
+ $noinline$testInlineInsideNestedCatches();
+ $noinline$testBeforeAfterTryCatch();
+ $noinline$testDifferentTypes();
+ $noinline$testRawThrow();
+ $noinline$testRawThrowTwice();
+ $noinline$testThrowCaughtInOuterMethod();
+ }
+
+ public static void $noinline$assertEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ // Basic try catch inline.
+ private static void $noinline$testSingleTryCatch() {
+ int[] numbers = {};
+ $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
+ }
+
+ // Two instances of the same method with a try catch.
+ private static void $noinline$testSingleTryCatchTwice() {
+ int[] numbers = {};
+ $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
+ $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
+ }
+
+ // Triggering both normal and the exceptional flow.
+ private static void $noinline$testSingleTryCatchDifferentInputs() {
+ $noinline$assertEquals(1, $inline$OOBTryCatch(null));
+ int[] numbers = {};
+ $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
+ int[] filled_numbers = {42};
+ $noinline$assertEquals(42, $inline$OOBTryCatch(filled_numbers));
+ }
+
+
+ // Two different try catches, with the same catch's dex_pc.
+ private static void $noinline$testDifferentTryCatches() {
+ int[] numbers = {};
+ $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
+ $noinline$assertEquals(2, $inline$OtherOOBTryCatch(numbers));
+ }
+
+ // Basic try/catch/finally.
+ private static void $noinline$testTryCatchFinally() {
+ int[] numbers = {};
+ $noinline$assertEquals(3, $inline$OOBTryCatchFinally(numbers));
+ }
+
+ // Triggering both normal and the exceptional flow.
+ private static void $noinline$testTryCatchFinallyDifferentInputs() {
+ $noinline$assertEquals(3, $inline$OOBTryCatchFinally(null));
+ int[] numbers = {};
+ $noinline$assertEquals(3, $inline$OOBTryCatchFinally(numbers));
+ int[] filled_numbers = {42};
+ $noinline$assertEquals(42, $inline$OOBTryCatchFinally(filled_numbers));
+ }
+
+ // Test that we can inline even when the try catch is several levels deep.
+ private static void $noinline$testRecursiveTryCatch() {
+ int[] numbers = {};
+ $noinline$assertEquals(1, $inline$OOBTryCatchLevel4(numbers));
+ }
+
+ // Tests that we don't inline inside outer tries, but we do inline inside of catches.
+ /// CHECK-START: void Main.$noinline$testDoNotInlineInsideTryInlineInsideCatch() inliner (before)
+ /// CHECK: InvokeStaticOrDirect method_name:Main.DoNotInlineOOBTryCatch
+ /// CHECK: InvokeStaticOrDirect method_name:Main.$inline$OOBTryCatch
+
+ /// CHECK-START: void Main.$noinline$testDoNotInlineInsideTryInlineInsideCatch() inliner (after)
+ /// CHECK: InvokeStaticOrDirect method_name:Main.DoNotInlineOOBTryCatch
+ private static void $noinline$testDoNotInlineInsideTryInlineInsideCatch() {
+ int val = 0;
+ try {
+ int[] numbers = {};
+ val = DoNotInlineOOBTryCatch(numbers);
+ } catch (Exception ex) {
+ unreachable();
+ // This is unreachable but we will still compile it so it works for checking that it inlines.
+ int[] numbers = {};
+ $inline$OOBTryCatch(numbers);
+ }
+ $noinline$assertEquals(1, val);
+ }
+
+ private static void $noinline$emptyMethod() {}
+
+ private static void $inline$testInlineInsideNestedCatches_inner() {
+ try {
+ $noinline$emptyMethod();
+ } catch (Exception ex) {
+ int[] numbers = {};
+ $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
+ }
+ }
+
+ private static void $noinline$testInlineInsideNestedCatches() {
+ try {
+ $noinline$emptyMethod();
+ } catch (Exception ex) {
+ $inline$testInlineInsideNestedCatches_inner();
+ }
+ }
+
+ // Tests that outer tries or catches don't affect as long as we are not inlining the inner
+ // try/catch inside of them.
+ private static void $noinline$testBeforeAfterTryCatch() {
+ int[] numbers = {};
+ $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
+
+ // Unrelated try catch does not block inlining outside of it. We fill it in to make sure it is
+ // still there by the time the inliner runs.
+ int val = 0;
+ try {
+ int[] other_array = {};
+ val = other_array[0];
+ } catch (Exception ex) {
+ $noinline$assertEquals(0, val);
+ val = 1;
+ }
+ $noinline$assertEquals(1, val);
+
+ $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
+ }
+
+ // Tests different try catch types in the same outer method.
+ private static void $noinline$testDifferentTypes() {
+ int[] numbers = {};
+ $noinline$assertEquals(1, $inline$OOBTryCatch(numbers));
+ $noinline$assertEquals(2, $inline$OtherOOBTryCatch(numbers));
+ $noinline$assertEquals(123, $inline$ParseIntTryCatch("123"));
+ $noinline$assertEquals(-1, $inline$ParseIntTryCatch("abc"));
+ }
+
+ // Tests a raw throw (rather than an instruction that happens to throw).
+ private static void $noinline$testRawThrow() {
+ $noinline$assertEquals(1, $inline$rawThrowCaught());
+ }
+
+ // Tests a raw throw twice.
+ private static void $noinline$testRawThrowTwice() {
+ $noinline$assertEquals(1, $inline$rawThrowCaught());
+ $noinline$assertEquals(1, $inline$rawThrowCaught());
+ }
+
+ // Tests that the outer method can successfully catch the throw in the inner method.
+ private static void $noinline$testThrowCaughtInOuterMethod() {
+ int[] numbers = {};
+ $noinline$assertEquals(1, $inline$testThrowCaughtInOuterMethod_simpleTryCatch(numbers));
+ $noinline$assertEquals(1, $inline$testThrowCaughtInOuterMethod_simpleTryCatch_inliningInner(numbers));
+ $noinline$assertEquals(1, $inline$testThrowCaughtInOuterMethod_withFinally(numbers));
+ }
+
+ // Building blocks for the test functions.
+ private static int $inline$OOBTryCatch(int[] array) {
+ try {
+ return array[0];
+ } catch (Exception e) {
+ return 1;
+ }
+ }
+
+ private static int $inline$OtherOOBTryCatch(int[] array) {
+ try {
+ return array[0];
+ } catch (Exception e) {
+ return 2;
+ }
+ }
+
+ private static int $inline$OOBTryCatchFinally(int[] array) {
+ int val = 0;
+ try {
+ val = 1;
+ return array[0];
+ } catch (Exception e) {
+ val = 2;
+ } finally {
+ val = 3;
+ }
+ return val;
+ }
+
+ // If we make the depthness a parameter, we wouldn't be able to mark as $inline$ and we would
+ // need extra CHECKer statements.
+ private static int $inline$OOBTryCatchLevel4(int[] array) {
+ return $inline$OOBTryCatchLevel3(array);
+ }
+
+ private static int $inline$OOBTryCatchLevel3(int[] array) {
+ return $inline$OOBTryCatchLevel2(array);
+ }
+
+ private static int $inline$OOBTryCatchLevel2(int[] array) {
+ return $inline$OOBTryCatchLevel1(array);
+ }
+
+ private static int $inline$OOBTryCatchLevel1(int[] array) {
+ return $inline$OOBTryCatch(array);
+ }
+
+ private static int DoNotInlineOOBTryCatch(int[] array) {
+ try {
+ return array[0];
+ } catch (Exception e) {
+ return 1;
+ }
+ }
+
+ private static void unreachable() {
+ throw new Error("Unreachable");
+ }
+
+ private static int $inline$ParseIntTryCatch(String str) {
+ try {
+ return Integer.parseInt(str);
+ } catch (NumberFormatException ex) {
+ return -1;
+ }
+ }
+
+ private static int $inline$rawThrowCaught() {
+ try {
+ throw new Error();
+ } catch (Error e) {
+ return 1;
+ }
+ }
+
+ private static int $inline$testThrowCaughtInOuterMethod_simpleTryCatch(int[] array) {
+ int val = 0;
+ try {
+ $noinline$throwingMethod(array);
+ } catch (Exception ex) {
+ val = 1;
+ }
+ return val;
+ }
+
+ private static int $noinline$throwingMethod(int[] array) {
+ return array[0];
+ }
+
+ private static int $inline$testThrowCaughtInOuterMethod_simpleTryCatch_inliningInner(int[] array) {
+ int val = 0;
+ try {
+ $inline$throwingMethod(array);
+ } catch (Exception ex) {
+ val = 1;
+ }
+ return val;
+ }
+
+ private static int $inline$throwingMethod(int[] array) {
+ return array[0];
+ }
+
+ private static int $inline$testThrowCaughtInOuterMethod_withFinally(int[] array) {
+ int val = 0;
+ try {
+ $noinline$throwingMethodWithFinally(array);
+ } catch (Exception ex) {
+ System.out.println("Our battle it will be legendary!");
+ val = 1;
+ }
+ return val;
+ }
+
+ private static int $noinline$throwingMethodWithFinally(int[] array) {
+ try {
+ return array[0];
+ } finally {
+ System.out.println("Finally, a worthy opponent!");
+ }
+ }
+}
diff --git a/test/2242-checker-lse-acquire-release-operations/Android.bp b/test/2242-checker-lse-acquire-release-operations/Android.bp
new file mode 100644
index 0000000..bb493ce
--- /dev/null
+++ b/test/2242-checker-lse-acquire-release-operations/Android.bp
@@ -0,0 +1,43 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2242-checker-lse-acquire-release-operations`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-2242-checker-lse-acquire-release-operations",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-2242-checker-lse-acquire-release-operations-expected-stdout",
+ ":art-run-test-2242-checker-lse-acquire-release-operations-expected-stderr",
+ ],
+ // Include the Java source files in the test's artifacts, to make Checker assertions
+ // available to the TradeFed test runner.
+ include_srcs: true,
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-2242-checker-lse-acquire-release-operations-expected-stdout",
+ out: ["art-run-test-2242-checker-lse-acquire-release-operations-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-2242-checker-lse-acquire-release-operations-expected-stderr",
+ out: ["art-run-test-2242-checker-lse-acquire-release-operations-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2242-checker-lse-acquire-release-operations/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2242-checker-lse-acquire-release-operations/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2242-checker-lse-acquire-release-operations/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2242-checker-lse-acquire-release-operations/expected-stdout.txt
diff --git a/test/2242-checker-lse-acquire-release-operations/info.txt b/test/2242-checker-lse-acquire-release-operations/info.txt
new file mode 100644
index 0000000..efdc63f
--- /dev/null
+++ b/test/2242-checker-lse-acquire-release-operations/info.txt
@@ -0,0 +1,3 @@
+Tests that we perform LSE with graphs with acquire loads
+(i.e. monitor enter and volatile load) and release stores
+(i.e. monitor exit and voaltile stores)
diff --git a/test/2242-checker-lse-acquire-release-operations/src/Main.java b/test/2242-checker-lse-acquire-release-operations/src/Main.java
new file mode 100644
index 0000000..433a4cd
--- /dev/null
+++ b/test/2242-checker-lse-acquire-release-operations/src/Main.java
@@ -0,0 +1,703 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class TestClass {
+ TestClass() {}
+ int i;
+ int j;
+ volatile int vi;
+}
+
+public class Main {
+ public static void main(String[] args) {
+ // Volatile accesses.
+ assertEquals($noinline$testVolatileAccessesMustBeKept(new TestClass()), 3);
+
+ // Volatile loads - Different fields shouldn't alias.
+ assertEquals($noinline$testVolatileLoadDifferentFields(new TestClass(), new TestClass()), 3);
+ assertEquals(
+ $noinline$testVolatileLoadDifferentFieldsBlocking(new TestClass(), new TestClass()),
+ 3);
+
+ // Volatile loads - Redundant store.
+ assertEquals($noinline$testVolatileLoadRedundantStore(new TestClass()), 2);
+ assertEquals($noinline$testVolatileLoadRedundantStoreBlocking(new TestClass()), 2);
+ assertEquals($noinline$testVolatileLoadRedundantStoreBlockingOnlyLoad(new TestClass()), 2);
+
+ // Volatile loads - Set and merge values.
+ assertEquals($noinline$testVolatileLoadSetAndMergeValues(new TestClass(), true), 1);
+ assertEquals($noinline$testVolatileLoadSetAndMergeValues(new TestClass(), false), 2);
+ assertEquals($noinline$testVolatileLoadSetAndMergeValuesBlocking(new TestClass(), true), 1);
+ assertEquals($noinline$testVolatileLoadSetAndMergeValuesBlocking(new TestClass(), false), 2);
+
+ // Volatile stores - Different fields shouldn't alias.
+ assertEquals($noinline$testVolatileStoreDifferentFields(new TestClass(), new TestClass()), 3);
+ assertEquals(
+ $noinline$testVolatileStoreDifferentFieldsBlocking(new TestClass(), new TestClass()),
+ 3);
+
+ // Volatile stores - Redundant store.
+ assertEquals($noinline$testVolatileStoreRedundantStore(new TestClass()), 2);
+ assertEquals($noinline$testVolatileStoreRedundantStoreBlocking(new TestClass()), 2);
+ assertEquals($noinline$testVolatileStoreRedundantStoreBlockingOnlyLoad(new TestClass()), 2);
+
+ // Volatile stores - Set and merge values.
+ assertEquals($noinline$testVolatileStoreSetAndMergeValues(new TestClass(), true), 1);
+ assertEquals($noinline$testVolatileStoreSetAndMergeValues(new TestClass(), false), 2);
+ assertEquals($noinline$testVolatileStoreSetAndMergeValuesNotBlocking(new TestClass(), true), 1);
+ assertEquals($noinline$testVolatileStoreSetAndMergeValuesNotBlocking(new TestClass(), false), 2);
+
+ // Monitor Operations - Different fields shouldn't alias.
+ assertEquals(
+ $noinline$testMonitorOperationDifferentFields(new TestClass(), new TestClass()), 3);
+ assertEquals($noinline$testMonitorOperationDifferentFieldsBlocking(
+ new TestClass(), new TestClass()),
+ 3);
+
+ // Monitor Operations - Redundant store.
+ assertEquals($noinline$testMonitorOperationRedundantStore(new TestClass()), 2);
+ assertEquals($noinline$testMonitorOperationRedundantStoreBlocking(new TestClass()), 2);
+ assertEquals(
+ $noinline$testMonitorOperationRedundantStoreBlockingOnlyLoad(new TestClass()), 2);
+ assertEquals($noinline$testMonitorOperationRedundantStoreBlockingExit(new TestClass()), 2);
+
+ // Monitor Operations - Set and merge values.
+ assertEquals($noinline$testMonitorOperationSetAndMergeValues(new TestClass(), true), 1);
+ assertEquals($noinline$testMonitorOperationSetAndMergeValues(new TestClass(), false), 2);
+ assertEquals(
+ $noinline$testMonitorOperationSetAndMergeValuesBlocking(new TestClass(), true), 1);
+ assertEquals(
+ $noinline$testMonitorOperationSetAndMergeValuesBlocking(new TestClass(), false), 2);
+ }
+
+ public static void assertEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileAccessesMustBeKept(TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testVolatileAccessesMustBeKept(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ static int $noinline$testVolatileAccessesMustBeKept(TestClass obj1) {
+ int result;
+ obj1.vi = 3;
+ // Redundant load that has to be kept.
+ result = obj1.vi;
+ result = obj1.vi;
+ // Redundant store that has to be kept.
+ obj1.vi = 3;
+ result = obj1.vi;
+ return result;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadDifferentFields(TestClass, TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldGet field_name:TestClass.vi
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet field_name:TestClass.i
+ /// CHECK: InstanceFieldGet field_name:TestClass.j
+ /// CHECK: InstanceFieldGet field_name:TestClass.vi
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadDifferentFields(TestClass, TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldGet field_name:TestClass.vi
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet field_name:TestClass.vi
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadDifferentFields(TestClass, TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet field_name:TestClass.i
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadDifferentFields(TestClass, TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet field_name:TestClass.j
+
+ // Unrelated volatile loads shouldn't block LSE.
+ static int $noinline$testVolatileLoadDifferentFields(TestClass obj1, TestClass obj2) {
+ int unused = obj1.vi;
+ obj1.i = 1;
+ obj2.j = 2;
+ int result = obj1.i + obj2.j;
+ unused = obj1.vi;
+ return result;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadDifferentFieldsBlocking(TestClass, TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadDifferentFieldsBlocking(TestClass, TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+
+ // A volatile load blocks load elimination.
+ static int $noinline$testVolatileLoadDifferentFieldsBlocking(TestClass obj1, TestClass obj2) {
+ obj1.i = 1;
+ obj2.j = 2;
+ int unused = obj1.vi;
+ return obj1.i + obj2.j;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadRedundantStore(TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldGet field_name:TestClass.vi
+ /// CHECK: InstanceFieldSet field_name:TestClass.j
+ /// CHECK: InstanceFieldSet field_name:TestClass.j
+ /// CHECK: InstanceFieldGet field_name:TestClass.j
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadRedundantStore(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldGet field_name:TestClass.vi
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadRedundantStore(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet field_name:TestClass.j
+ /// CHECK-NOT: InstanceFieldSet field_name:TestClass.j
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadRedundantStore(TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet field_name:TestClass.j
+ static int $noinline$testVolatileLoadRedundantStore(TestClass obj) {
+ int unused = obj.vi;
+ obj.j = 1;
+ obj.j = 2;
+ return obj.j;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadRedundantStoreBlocking(TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadRedundantStoreBlocking(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet field_name:TestClass.vi
+ /// CHECK: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadRedundantStoreBlocking(TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet field_name:TestClass.j
+ static int $noinline$testVolatileLoadRedundantStoreBlocking(TestClass obj) {
+ // This store must be kept due to the volatile load.
+ obj.j = 1;
+ int unused = obj.vi;
+ obj.j = 2;
+ return obj.j;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadRedundantStoreBlockingOnlyLoad(TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadRedundantStoreBlockingOnlyLoad(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadRedundantStoreBlockingOnlyLoad(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+ static int $noinline$testVolatileLoadRedundantStoreBlockingOnlyLoad(TestClass obj) {
+ // This store can be safely removed.
+ obj.j = 1;
+ obj.j = 2;
+ int unused = obj.vi;
+ // This load remains due to the volatile load in the middle.
+ return obj.j;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadSetAndMergeValues(TestClass, boolean) load_store_elimination (before)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldGet field_name:TestClass.i
+ /// CHECK-DAG: InstanceFieldGet field_name:TestClass.vi
+ /// CHECK-DAG: InstanceFieldGet field_name:TestClass.vi
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadSetAndMergeValues(TestClass, boolean) load_store_elimination (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldGet field_name:TestClass.vi
+ /// CHECK-DAG: InstanceFieldGet field_name:TestClass.vi
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadSetAndMergeValues(TestClass, boolean) load_store_elimination (after)
+ /// CHECK: Phi
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadSetAndMergeValues(TestClass, boolean) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet field_name:TestClass.i
+
+ static int $noinline$testVolatileLoadSetAndMergeValues(TestClass obj, boolean b) {
+ if (b) {
+ int unused = obj.vi;
+ obj.i = 1;
+ } else {
+ int unused = obj.vi;
+ obj.i = 2;
+ }
+ return obj.i;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadSetAndMergeValuesBlocking(TestClass, boolean) load_store_elimination (before)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldGet field_name:TestClass.i
+ /// CHECK-DAG: InstanceFieldGet field_name:TestClass.vi
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadSetAndMergeValuesBlocking(TestClass, boolean) load_store_elimination (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldGet field_name:TestClass.vi
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadSetAndMergeValuesBlocking(TestClass, boolean) load_store_elimination (after)
+ /// CHECK-NOT: Phi
+
+ /// CHECK-START: int Main.$noinline$testVolatileLoadSetAndMergeValuesBlocking(TestClass, boolean) load_store_elimination (after)
+ /// CHECK: InstanceFieldGet
+
+ static int $noinline$testVolatileLoadSetAndMergeValuesBlocking(TestClass obj, boolean b) {
+ if (b) {
+ obj.i = 1;
+ } else {
+ obj.i = 2;
+ }
+ int unused = obj.vi;
+ return obj.i;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreDifferentFields(TestClass, TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet field_name:TestClass.vi
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet field_name:TestClass.i
+ /// CHECK: InstanceFieldGet field_name:TestClass.j
+ /// CHECK: InstanceFieldSet field_name:TestClass.vi
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreDifferentFields(TestClass, TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet field_name:TestClass.vi
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet field_name:TestClass.vi
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreDifferentFields(TestClass, TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet field_name:TestClass.i
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreDifferentFields(TestClass, TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet field_name:TestClass.j
+
+ // Unrelated volatile stores shouldn't block LSE.
+ static int $noinline$testVolatileStoreDifferentFields(TestClass obj1, TestClass obj2) {
+ obj1.vi = 123;
+ obj1.i = 1;
+ obj2.j = 2;
+ int result = obj1.i + obj2.j;
+ obj1.vi = 123;
+ return result;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreDifferentFieldsBlocking(TestClass, TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreDifferentFieldsBlocking(TestClass, TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreDifferentFieldsBlocking(TestClass, TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet
+
+ // A volatile store doesn't block load elimination, as it doesn't clobber existing values.
+ static int $noinline$testVolatileStoreDifferentFieldsBlocking(TestClass obj1, TestClass obj2) {
+ obj1.i = 1;
+ obj2.j = 2;
+ obj1.vi = 123;
+ return obj1.i + obj2.j;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreRedundantStore(TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet field_name:TestClass.vi
+ /// CHECK: InstanceFieldSet field_name:TestClass.j
+ /// CHECK: InstanceFieldSet field_name:TestClass.j
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreRedundantStore(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet field_name:TestClass.vi
+ /// CHECK: InstanceFieldSet field_name:TestClass.j
+ /// CHECK-NOT: InstanceFieldSet field_name:TestClass.j
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreRedundantStore(TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet
+ static int $noinline$testVolatileStoreRedundantStore(TestClass obj) {
+ obj.vi = 123;
+ obj.j = 1;
+ obj.j = 2;
+ return obj.j;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreRedundantStoreBlocking(TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreRedundantStoreBlocking(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreRedundantStoreBlocking(TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet
+ static int $noinline$testVolatileStoreRedundantStoreBlocking(TestClass obj) {
+ // This store must be kept due to the volatile store.
+ obj.j = 1;
+ obj.vi = 123;
+ obj.j = 2;
+ return obj.j;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreRedundantStoreBlockingOnlyLoad(TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet field_name:TestClass.j
+ /// CHECK: InstanceFieldSet field_name:TestClass.j
+ /// CHECK: InstanceFieldSet field_name:TestClass.vi
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreRedundantStoreBlockingOnlyLoad(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet field_name:TestClass.j
+ /// CHECK-NOT: InstanceFieldSet field_name:TestClass.j
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreRedundantStoreBlockingOnlyLoad(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet field_name:TestClass.vi
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreRedundantStoreBlockingOnlyLoad(TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet
+ static int $noinline$testVolatileStoreRedundantStoreBlockingOnlyLoad(TestClass obj) {
+ // This store can be safely removed.
+ obj.j = 1;
+ obj.j = 2;
+ obj.vi = 123;
+ // This load can also be safely eliminated as the volatile store doesn't clobber values.
+ return obj.j;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreSetAndMergeValues(TestClass, boolean) load_store_elimination (before)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreSetAndMergeValues(TestClass, boolean) load_store_elimination (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreSetAndMergeValues(TestClass, boolean) load_store_elimination (after)
+ /// CHECK: Phi
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreSetAndMergeValues(TestClass, boolean) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet
+ static int $noinline$testVolatileStoreSetAndMergeValues(TestClass obj, boolean b) {
+ if (b) {
+ obj.vi = 123;
+ obj.i = 1;
+ } else {
+ obj.vi = 123;
+ obj.i = 2;
+ }
+ return obj.i;
+ }
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreSetAndMergeValuesNotBlocking(TestClass, boolean) load_store_elimination (before)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreSetAndMergeValuesNotBlocking(TestClass, boolean) load_store_elimination (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreSetAndMergeValues(TestClass, boolean) load_store_elimination (after)
+ /// CHECK: Phi
+
+ /// CHECK-START: int Main.$noinline$testVolatileStoreSetAndMergeValues(TestClass, boolean) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet
+ static int $noinline$testVolatileStoreSetAndMergeValuesNotBlocking(TestClass obj, boolean b) {
+ if (b) {
+ obj.i = 1;
+ } else {
+ obj.i = 2;
+ }
+ // This volatile store doesn't block the load elimination
+ obj.vi = 123;
+ return obj.i;
+ }
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationDifferentFields(TestClass, TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationDifferentFields(TestClass, TestClass) load_store_elimination (before)
+ /// CHECK: MonitorOperation kind:enter
+ /// CHECK: MonitorOperation kind:exit
+ /// CHECK: MonitorOperation kind:enter
+ /// CHECK: MonitorOperation kind:exit
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationDifferentFields(TestClass, TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationDifferentFields(TestClass, TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet
+
+ // Unrelated monitor operations shouldn't block LSE.
+ static int $noinline$testMonitorOperationDifferentFields(TestClass obj1, TestClass obj2) {
+ Main m = new Main();
+ synchronized (m) {}
+
+ obj1.i = 1;
+ obj2.j = 2;
+ int result = obj1.i + obj2.j;
+
+ synchronized (m) {}
+
+ return result;
+ }
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationDifferentFieldsBlocking(TestClass, TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationDifferentFieldsBlocking(TestClass, TestClass) load_store_elimination (before)
+ /// CHECK: MonitorOperation kind:enter
+ /// CHECK: MonitorOperation kind:exit
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationDifferentFieldsBlocking(TestClass, TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+ /// CHECK: InstanceFieldGet
+
+ // A synchronized operation blocks loads.
+ static int $noinline$testMonitorOperationDifferentFieldsBlocking(TestClass obj1, TestClass obj2) {
+ Main m = new Main();
+
+ obj1.i = 1;
+ obj2.j = 2;
+ synchronized (m) {
+ return obj1.i + obj2.j;
+ }
+ }
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStore(TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStore(TestClass) load_store_elimination (before)
+ /// CHECK: MonitorOperation kind:enter
+ /// CHECK: MonitorOperation kind:exit
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStore(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStore(TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet
+
+ static int $noinline$testMonitorOperationRedundantStore(TestClass obj) {
+ Main m = new Main();
+ synchronized (m) {
+ obj.j = 1;
+ obj.j = 2;
+ }
+
+ return obj.j;
+ }
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStoreBlocking(TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStoreBlocking(TestClass) load_store_elimination (before)
+ /// CHECK: MonitorOperation kind:enter
+ /// CHECK: MonitorOperation kind:exit
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStoreBlocking(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStoreBlocking(TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet
+
+ static int $noinline$testMonitorOperationRedundantStoreBlocking(TestClass obj) {
+ Main m = new Main();
+
+ // This store must be kept due to the monitor operation.
+ obj.j = 1;
+ synchronized (m) {}
+ obj.j = 2;
+
+ return obj.j;
+ }
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStoreBlockingOnlyLoad(TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStoreBlockingOnlyLoad(TestClass) load_store_elimination (before)
+ /// CHECK: MonitorOperation kind:enter
+ /// CHECK: MonitorOperation kind:exit
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStoreBlockingOnlyLoad(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStoreBlockingOnlyLoad(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldGet
+
+ static int $noinline$testMonitorOperationRedundantStoreBlockingOnlyLoad(TestClass obj) {
+ Main m = new Main();
+
+ // This store can be safely removed.
+ obj.j = 1;
+ obj.j = 2;
+ synchronized (m) {}
+
+ // This load remains due to the monitor operation.
+ return obj.j;
+ }
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStoreBlockingExit(TestClass) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStoreBlockingExit(TestClass) load_store_elimination (before)
+ /// CHECK: MonitorOperation kind:enter
+ /// CHECK: MonitorOperation kind:exit
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStoreBlockingExit(TestClass) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet
+ /// CHECK: InstanceFieldSet
+ /// CHECK-NOT: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationRedundantStoreBlockingExit(TestClass) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet
+
+ static int $noinline$testMonitorOperationRedundantStoreBlockingExit(TestClass obj) {
+ Main m = new Main();
+
+ synchronized (m) {
+ // This store can be removed.
+ obj.j = 0;
+ // This store must be kept due to the monitor exit operation.
+ obj.j = 1;
+ }
+ obj.j = 2;
+
+ return obj.j;
+ }
+
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationSetAndMergeValues(TestClass, boolean) load_store_elimination (before)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationSetAndMergeValues(TestClass, boolean) load_store_elimination (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationSetAndMergeValues(TestClass, boolean) load_store_elimination (before)
+ /// CHECK-DAG: MonitorOperation kind:enter
+ /// CHECK-DAG: MonitorOperation kind:exit
+ /// CHECK-DAG: MonitorOperation kind:enter
+ /// CHECK-DAG: MonitorOperation kind:exit
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationSetAndMergeValues(TestClass, boolean) load_store_elimination (after)
+ /// CHECK: Phi
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationSetAndMergeValues(TestClass, boolean) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldGet
+
+ static int $noinline$testMonitorOperationSetAndMergeValues(TestClass obj, boolean b) {
+ Main m = new Main();
+
+ if (b) {
+ synchronized (m) {}
+ obj.i = 1;
+ } else {
+ synchronized (m) {}
+ obj.i = 2;
+ }
+ return obj.i;
+ }
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationSetAndMergeValuesBlocking(TestClass, boolean) load_store_elimination (before)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldGet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationSetAndMergeValuesBlocking(TestClass, boolean) load_store_elimination (after)
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationSetAndMergeValuesBlocking(TestClass, boolean) load_store_elimination (before)
+ /// CHECK-DAG: MonitorOperation kind:enter
+ /// CHECK-DAG: MonitorOperation kind:exit
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationSetAndMergeValuesBlocking(TestClass, boolean) load_store_elimination (after)
+ /// CHECK-NOT: Phi
+
+ /// CHECK-START: int Main.$noinline$testMonitorOperationSetAndMergeValuesBlocking(TestClass, boolean) load_store_elimination (after)
+ /// CHECK: InstanceFieldGet
+
+ static int $noinline$testMonitorOperationSetAndMergeValuesBlocking(TestClass obj, boolean b) {
+ Main m = new Main();
+
+ if (b) {
+ obj.i = 1;
+ } else {
+ obj.i = 2;
+ }
+ synchronized (m) {}
+ return obj.i;
+ }
+}
diff --git a/test/2243-checker-not-inline-into-throw/Android.bp b/test/2243-checker-not-inline-into-throw/Android.bp
new file mode 100644
index 0000000..78c5c66
--- /dev/null
+++ b/test/2243-checker-not-inline-into-throw/Android.bp
@@ -0,0 +1,43 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2243-checker-not-inline-into-throw`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-2243-checker-not-inline-into-throw",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-2243-checker-not-inline-into-throw-expected-stdout",
+ ":art-run-test-2243-checker-not-inline-into-throw-expected-stderr",
+ ],
+ // Include the Java source files in the test's artifacts, to make Checker assertions
+ // available to the TradeFed test runner.
+ include_srcs: true,
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-2243-checker-not-inline-into-throw-expected-stdout",
+ out: ["art-run-test-2243-checker-not-inline-into-throw-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-2243-checker-not-inline-into-throw-expected-stderr",
+ out: ["art-run-test-2243-checker-not-inline-into-throw-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2243-checker-not-inline-into-throw/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2243-checker-not-inline-into-throw/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2243-checker-not-inline-into-throw/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2243-checker-not-inline-into-throw/expected-stdout.txt
diff --git a/test/2243-checker-not-inline-into-throw/info.txt b/test/2243-checker-not-inline-into-throw/info.txt
new file mode 100644
index 0000000..a2ded81
--- /dev/null
+++ b/test/2243-checker-not-inline-into-throw/info.txt
@@ -0,0 +1 @@
+Tests that we don't inline methods if their basic blocks end with a throw.
diff --git a/test/2243-checker-not-inline-into-throw/src/Main.java b/test/2243-checker-not-inline-into-throw/src/Main.java
new file mode 100644
index 0000000..6f1280c
--- /dev/null
+++ b/test/2243-checker-not-inline-into-throw/src/Main.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 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 {
+ try {
+ $noinline$testEndsWithThrow();
+ throw new Exception("Unreachable");
+ } catch (Error expected) {
+ }
+
+ try {
+ $noinline$testEndsWithThrowButNotDirectly();
+ throw new Exception("Unreachable");
+ } catch (Error expected) {
+ }
+ }
+
+ // Empty methods are easy to inline anywhere.
+ private static void easyToInline() {}
+ private static void $inline$easyToInline() {}
+
+ /// CHECK-START: int Main.$noinline$testEndsWithThrow() inliner (before)
+ /// CHECK: InvokeStaticOrDirect method_name:Main.easyToInline
+
+ /// CHECK-START: int Main.$noinline$testEndsWithThrow() inliner (after)
+ /// CHECK: InvokeStaticOrDirect method_name:Main.easyToInline
+ static int $noinline$testEndsWithThrow() {
+ easyToInline();
+ throw new Error("");
+ }
+
+ // Currently we only stop inlining if the method's basic block ends with a throw. We do not stop
+ // inlining for methods that eventually always end with a throw.
+ static int $noinline$testEndsWithThrowButNotDirectly() {
+ $inline$easyToInline();
+ if (justABoolean) {
+ $inline$easyToInline();
+ } else {
+ $inline$easyToInline();
+ }
+ throw new Error("");
+ }
+
+ static boolean justABoolean;
+}
diff --git a/test/2244-checker-remove-try-boundary/Android.bp b/test/2244-checker-remove-try-boundary/Android.bp
new file mode 100644
index 0000000..9e03692
--- /dev/null
+++ b/test/2244-checker-remove-try-boundary/Android.bp
@@ -0,0 +1,43 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2244-checker-remove-try-boundary`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-2244-checker-remove-try-boundary",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-2244-checker-remove-try-boundary-expected-stdout",
+ ":art-run-test-2244-checker-remove-try-boundary-expected-stderr",
+ ],
+ // Include the Java source files in the test's artifacts, to make Checker assertions
+ // available to the TradeFed test runner.
+ include_srcs: true,
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-2244-checker-remove-try-boundary-expected-stdout",
+ out: ["art-run-test-2244-checker-remove-try-boundary-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-2244-checker-remove-try-boundary-expected-stderr",
+ out: ["art-run-test-2244-checker-remove-try-boundary-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2244-checker-remove-try-boundary/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2244-checker-remove-try-boundary/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2244-checker-remove-try-boundary/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2244-checker-remove-try-boundary/expected-stdout.txt
diff --git a/test/2244-checker-remove-try-boundary/info.txt b/test/2244-checker-remove-try-boundary/info.txt
new file mode 100644
index 0000000..4247e14
--- /dev/null
+++ b/test/2244-checker-remove-try-boundary/info.txt
@@ -0,0 +1,2 @@
+Tests that we remove TryBoundary instructions if doesn't contain instructions that can throw.
+Sometimes we can remove the catch blocks too.
diff --git a/test/2244-checker-remove-try-boundary/src/Main.java b/test/2244-checker-remove-try-boundary/src/Main.java
new file mode 100644
index 0000000..1b616a5
--- /dev/null
+++ b/test/2244-checker-remove-try-boundary/src/Main.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2022 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 {
+ assertEquals(2, $noinline$testDivideOverTen(20));
+ assertEquals(-2, $noinline$testDivideOverTen(-20));
+ assertEquals(0, $noinline$testSimpleDivisionInLoop(0));
+ assertEquals(1, $noinline$testSimpleDivisionInLoop(81));
+ assertEquals(10, $noinline$testOptimizeSeparateBranches(60, true));
+ assertEquals(10, $noinline$testOptimizeSeparateBranches(80, false));
+ assertEquals(1, $noinline$testDoNotOptimizeOneBranchThrows(81, false));
+ assertEquals(-1000, $noinline$testDoNotOptimizeOneBranchThrows(81, true));
+ assertEquals(1, $noinline$testOptimizeAfterOneBranchDisappears(81, false));
+ assertEquals(10, $noinline$testRemoveTryBoundaryNested(60));
+ assertEquals(-2000, $noinline$testRemoveTryBoundaryNestedButNotCatch(60, true));
+ assertEquals(30, $noinline$testRemoveTryBoundaryNestedButNotCatch(60, false));
+ assertEquals(30, $noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(60, false));
+ }
+
+ public static void assertEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ // Check that this version cannot remove the TryBoundary instructions since we may throw.
+
+ /// CHECK-START: int Main.$inline$division(int, int) register (after)
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$inline$division(int, int) register (after)
+ /// CHECK: flags "catch_block"
+ private static int $inline$division(int a, int b) {
+ try {
+ return a / b;
+ } catch (Error unexpected) {
+ return -1000;
+ }
+ }
+
+ // Check that we can remove the TryBoundary afer inlining since we know we can't throw.
+
+ /// CHECK-START: int Main.$noinline$testDivideOverTen(int) inliner (after)
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testDivideOverTen(int) inliner (after)
+ /// CHECK-NOT: flags "catch_block"
+ private static int $noinline$testDivideOverTen(int a) {
+ return $inline$division(a, 10);
+ }
+
+ /// CHECK-START: int Main.$noinline$testSimpleDivisionInLoop(int) dead_code_elimination$initial (before)
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testSimpleDivisionInLoop(int) dead_code_elimination$initial (before)
+ /// CHECK: flags "catch_block"
+
+ /// CHECK-START: int Main.$noinline$testSimpleDivisionInLoop(int) dead_code_elimination$initial (after)
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testSimpleDivisionInLoop(int) dead_code_elimination$initial (after)
+ /// CHECK-NOT: flags "catch_block"
+ private static int $noinline$testSimpleDivisionInLoop(int a) {
+ try {
+ for (int i = 0; i < 4; i++) {
+ a /= 3;
+ }
+ } catch (Error unexpected) {
+ return -1000;
+ }
+ return a;
+ }
+
+ // Even though the `TryBoundary`s are split, we can remove them as nothing in the try can throw.
+
+ /// CHECK-START: int Main.$noinline$testOptimizeSeparateBranches(int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testOptimizeSeparateBranches(int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: flags "catch_block"
+ /// CHECK-NOT: flags "catch_block"
+
+ /// CHECK-START: int Main.$noinline$testOptimizeSeparateBranches(int, boolean) dead_code_elimination$initial (after)
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testOptimizeSeparateBranches(int, boolean) dead_code_elimination$initial (after)
+ /// CHECK-NOT: flags "catch_block"
+ private static int $noinline$testOptimizeSeparateBranches(int a, boolean val) {
+ try {
+ if (val) {
+ // TryBoundary kind:entry
+ a /= 3;
+ } else {
+ // TryBoundary kind:entry
+ a /= 4;
+ }
+ a /= 2;
+ // TryBoundary kind:exit
+ } catch (Error unexpected) {
+ return -1000;
+ }
+ return a;
+ }
+
+ // Even though the `a /= 3;` can't throw, we don't eliminate any `TryBoundary` instructions. This
+ // is because we have the `throw new Error();` in the try as well. We could potentially support
+ // removing some `TryBoundary` instructions and not all in the try, but this would complicate the
+ // code and wouldn't bring code size reductions since we would be unable to remove the catch
+ // block.
+
+ /// CHECK-START: int Main.$noinline$testDoNotOptimizeOneBranchThrows(int, boolean) register (after)
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testDoNotOptimizeOneBranchThrows(int, boolean) register (after)
+ /// CHECK: flags "catch_block"
+ public static int $noinline$testDoNotOptimizeOneBranchThrows(int a, boolean val) {
+ try {
+ for (int i = 0; i < 4; i++) {
+ // TryBoundary kind:entry
+ a /= 3;
+ // TryBoundary kind:exit
+ }
+
+ if (val) {
+ // TryBoundary kind:entry
+ throw new Error();
+ // TryBoundary kind:exit
+ }
+ } catch (Error e) {
+ return -1000;
+ }
+ return a;
+ }
+
+ // The throw gets eliminated by `SimplifyIfs` in DCE, so we can detect that nothing can throw in
+ // the graph and eliminate the `TryBoundary` instructions.
+
+ /// CHECK-START: int Main.$noinline$testOptimizeAfterOneBranchDisappears(int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: Throw
+
+ /// CHECK-START: int Main.$noinline$testOptimizeAfterOneBranchDisappears(int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testOptimizeAfterOneBranchDisappears(int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: flags "catch_block"
+ /// CHECK-NOT: flags "catch_block"
+
+ /// CHECK-START: int Main.$noinline$testOptimizeAfterOneBranchDisappears(int, boolean) dead_code_elimination$initial (after)
+ /// CHECK-NOT: Throw
+
+ /// CHECK-START: int Main.$noinline$testOptimizeAfterOneBranchDisappears(int, boolean) dead_code_elimination$initial (after)
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testOptimizeAfterOneBranchDisappears(int, boolean) dead_code_elimination$initial (after)
+ /// CHECK-NOT: flags "catch_block"
+ public static int $noinline$testOptimizeAfterOneBranchDisappears(int a, boolean val) {
+ try {
+ for (int i = 0; i < 4; i++) {
+ // TryBoundary kind:entry
+ a /= 3;
+ // TryBoundary kind:exit
+ }
+
+ if (val && !val) {
+ // TryBoundary kind:entry
+ throw new Error();
+ // TryBoundary kind:exit
+ }
+ } catch (Error e) {
+ return -1000;
+ }
+ return a;
+ }
+
+ /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNested(int) dead_code_elimination$initial (before)
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNested(int) dead_code_elimination$initial (before)
+ /// CHECK: flags "catch_block"
+ /// CHECK: flags "catch_block"
+ /// CHECK-NOT: flags "catch_block"
+
+ /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNested(int) dead_code_elimination$initial (after)
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNested(int) dead_code_elimination$initial (after)
+ /// CHECK-NOT: flags "catch_block"
+ public static int $noinline$testRemoveTryBoundaryNested(int a) {
+ try {
+ // TryBoundary kind:entry
+ a /= 2;
+ // TryBoundary kind:exit
+ try {
+ // TryBoundary kind:entry
+ a /= 3;
+ // TryBoundary kind:exit
+ } catch (Error e) {
+ return -2000;
+ }
+ } catch (Exception e) {
+ return -1000;
+ }
+ return a;
+ }
+
+ // We can remove the `TryBoundary` instructions surrounding `a /= 2;` but since the inner try can
+ // throw, we must keep both the inner and outer catches as they are catch handlers of the inner
+ // try.
+
+ /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNestedButNotCatch(int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNestedButNotCatch(int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: flags "catch_block"
+ /// CHECK: flags "catch_block"
+ /// CHECK-NOT: flags "catch_block"
+
+ /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNestedButNotCatch(int, boolean) dead_code_elimination$initial (after)
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testRemoveTryBoundaryNestedButNotCatch(int, boolean) dead_code_elimination$initial (after)
+ /// CHECK: flags "catch_block"
+ /// CHECK: flags "catch_block"
+ /// CHECK-NOT: flags "catch_block"
+ public static int $noinline$testRemoveTryBoundaryNestedButNotCatch(int a, boolean val) {
+ try {
+ // TryBoundary kind:entry
+ a /= 2;
+ // TryBoundary kind:exit
+ try {
+ if (val) {
+ // TryBoundary kind:entry
+ throw new Error();
+ // TryBoundary kind:exit
+ }
+ // TryBoundary kind:exit
+ } catch (Error e) {
+ return -2000;
+ }
+ } catch (Exception e) {
+ return -1000;
+ }
+ return a;
+ }
+
+ // We eliminate the return -1000 catch block which is outside of the loop in
+ // dead_code_elimination$initial. We can do so since we eliminated the TryBoundary of `a /= 2;`.
+
+ /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: flags "catch_block"
+ /// CHECK: flags "catch_block"
+ /// CHECK: flags "catch_block"
+ /// CHECK-NOT: flags "catch_block"
+
+ /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: IntConstant -1000
+
+ /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (after)
+ /// CHECK: TryBoundary
+ /// CHECK: TryBoundary
+ /// CHECK-NOT: TryBoundary
+
+ /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (after)
+ /// CHECK: flags "catch_block"
+ /// CHECK: flags "catch_block"
+ /// CHECK-NOT: flags "catch_block"
+
+ /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (after)
+ /// CHECK-NOT: IntConstant -1000
+
+ // When removing that block, we are removing a block outside of a loop but we still need to update
+ // the loop information in the graph since we removed TryBoundary instructions inside of a loop
+ // and now `a /= 2;` is not considered part of a loop (Cannot throw so it will not `continue` and
+ // will always return).
+
+ /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (before)
+ /// CHECK: Div loop:B2
+
+ /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (after)
+ /// CHECK-NOT: Div loop:B2
+
+ /// CHECK-START: int Main.$noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(int, boolean) dead_code_elimination$initial (after)
+ /// CHECK: Div
+ /// CHECK-NOT: Div
+ public static int $noinline$testNestedTryBoundariesWithLoopAndCatchOutsideOfLoop(
+ int a, boolean val) {
+ try {
+ for (int i = 0; i < 4; ++i) {
+ try {
+ try {
+ if (val) {
+ // TryBoundary kind:entry
+ throw new Error();
+ // TryBoundary kind:exit
+ }
+ // TryBoundary kind:exit
+ } catch (Exception e) {
+ continue;
+ }
+ // TryBoundary kind:entry
+ a /= 2;
+ // TryBoundary kind:exit
+ return a;
+ } catch (Error e) {
+ continue;
+ }
+ }
+ } catch (Exception e) {
+ return -1000;
+ }
+ return a;
+ }
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2245-checker-smali-instance-of-comparison/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2245-checker-smali-instance-of-comparison/expected-stderr.txt
diff --git a/test/2245-checker-smali-instance-of-comparison/expected-stdout.txt b/test/2245-checker-smali-instance-of-comparison/expected-stdout.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/2245-checker-smali-instance-of-comparison/expected-stdout.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/2245-checker-smali-instance-of-comparison/info.txt b/test/2245-checker-smali-instance-of-comparison/info.txt
new file mode 100644
index 0000000..5c6df8e
--- /dev/null
+++ b/test/2245-checker-smali-instance-of-comparison/info.txt
@@ -0,0 +1 @@
+Smali test comparing instance-of (which returns 0 or 1) with a constant 2.
diff --git a/test/2245-checker-smali-instance-of-comparison/smali/b_252804549.smali b/test/2245-checker-smali-instance-of-comparison/smali/b_252804549.smali
new file mode 100644
index 0000000..38d4d0c
--- /dev/null
+++ b/test/2245-checker-smali-instance-of-comparison/smali/b_252804549.smali
@@ -0,0 +1,34 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+.class public LB252804549;
+
+.super Ljava/lang/Object;
+
+## CHECK-START: int B252804549.compareInstanceOfWithTwo(java.lang.Object) builder (after)
+## CHECK-DAG: <<Const2:i\d+>> IntConstant 2
+## CHECK-DAG: <<InstanceOf:z\d+>> InstanceOf
+## CHECK-DAG: <<Eq:z\d+>> Equal [<<InstanceOf>>,<<Const2>>]
+## CHECK-DAG: If [<<Eq>>]
+.method public static compareInstanceOfWithTwo(Ljava/lang/Object;)I
+ .registers 2
+ instance-of v0, p0, Ljava/lang/String;
+ const/4 v1, 0x2
+ # Compare instance-of with 2 (i.e. neither 0 nor 1)
+ if-eq v0, v1, :Lequal
+ const/4 v1, 0x3
+ return v1
+:Lequal
+ return v1
+.end method
diff --git a/test/2245-checker-smali-instance-of-comparison/src/Main.java b/test/2245-checker-smali-instance-of-comparison/src/Main.java
new file mode 100644
index 0000000..cd9003a
--- /dev/null
+++ b/test/2245-checker-smali-instance-of-comparison/src/Main.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+
+/**
+ * Smali exercise, copied from 800-smali and modified for this test case.
+ */
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ Object retValue = Class.forName("B252804549")
+ .getDeclaredMethod("compareInstanceOfWithTwo", Object.class)
+ .invoke(null, new Object[] {new Object()});
+ if (retValue == null || !retValue.equals(3)) {
+ throw new Exception("Expected 3, but got " + retValue);
+ }
+ }
+}
diff --git a/test/2246-trace-stream/Android.bp b/test/2246-trace-stream/Android.bp
new file mode 100644
index 0000000..bef9deb
--- /dev/null
+++ b/test/2246-trace-stream/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2246-trace-stream`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-2246-trace-stream",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-2246-trace-stream-expected-stdout",
+ ":art-run-test-2246-trace-stream-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-2246-trace-stream-expected-stdout",
+ out: ["art-run-test-2246-trace-stream-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-2246-trace-stream-expected-stderr",
+ out: ["art-run-test-2246-trace-stream-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2246-trace-stream/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2246-trace-stream/expected-stderr.txt
diff --git a/test/2246-trace-stream/expected-stdout.txt b/test/2246-trace-stream/expected-stdout.txt
new file mode 100644
index 0000000..c9d7405
--- /dev/null
+++ b/test/2246-trace-stream/expected-stdout.txt
@@ -0,0 +1,72 @@
+.>> TestThread2246 java.lang.Thread run ()V Thread.java
+..>> TestThread2246 Main$$ExternalSyntheticLambda0 run ()V D8$$SyntheticClass
+...>> TestThread2246 Main lambda$main$0 ()V Main.java
+....>> TestThread2246 Main <init> ()V Main.java
+.....>> TestThread2246 java.lang.Object <init> ()V Object.java
+.....<< TestThread2246 java.lang.Object <init> ()V Object.java
+....<< TestThread2246 Main <init> ()V Main.java
+....>> TestThread2246 Main $noinline$doSomeWork ()V Main.java
+.....>> TestThread2246 Main callOuterFunction ()V Main.java
+......>> TestThread2246 Main callLeafFunction ()V Main.java
+......<< TestThread2246 Main callLeafFunction ()V Main.java
+.....<< TestThread2246 Main callOuterFunction ()V Main.java
+.....>> TestThread2246 Main callLeafFunction ()V Main.java
+.....<< TestThread2246 Main callLeafFunction ()V Main.java
+....<< TestThread2246 Main $noinline$doSomeWork ()V Main.java
+...<< TestThread2246 Main lambda$main$0 ()V Main.java
+..<< TestThread2246 Main$$ExternalSyntheticLambda0 run ()V D8$$SyntheticClass
+.<< TestThread2246 java.lang.Thread run ()V Thread.java
+.>> main Main main ([Ljava/lang/String;)V Main.java
+..>> main Main$VMDebug startMethodTracing (Ljava/lang/String;Ljava/io/FileDescriptor;IIZIZ)V Main.java
+...>> main java.lang.reflect.Method invoke (Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; Method.java
+....>> main dalvik.system.VMDebug startMethodTracing (Ljava/lang/String;Ljava/io/FileDescriptor;IIZIZ)V VMDebug.java
+.....>> main dalvik.system.VMDebug startMethodTracingFd (Ljava/lang/String;IIIZIZ)V VMDebug.java
+.....<< main dalvik.system.VMDebug startMethodTracingFd (Ljava/lang/String;IIIZIZ)V VMDebug.java
+....<< main dalvik.system.VMDebug startMethodTracing (Ljava/lang/String;Ljava/io/FileDescriptor;IIZIZ)V VMDebug.java
+...<< main java.lang.reflect.Method invoke (Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; Method.java
+..<< main Main$VMDebug startMethodTracing (Ljava/lang/String;Ljava/io/FileDescriptor;IIZIZ)V Main.java
+..>> main java.lang.Thread start ()V Thread.java
+...>> main java.lang.ThreadGroup add (Ljava/lang/Thread;)V ThreadGroup.java
+...<< main java.lang.ThreadGroup add (Ljava/lang/Thread;)V ThreadGroup.java
+...>> main java.lang.Thread nativeCreate (Ljava/lang/Thread;JZ)V Thread.java
+...<< main java.lang.Thread nativeCreate (Ljava/lang/Thread;JZ)V Thread.java
+..<< main java.lang.Thread start ()V Thread.java
+..>> main java.lang.Thread join ()V Thread.java
+...>> main java.lang.Thread join (J)V Thread.java
+....>> main java.lang.System currentTimeMillis ()J System.java
+....<< main java.lang.System currentTimeMillis ()J System.java
+....>> main java.lang.Thread isAlive ()Z Thread.java
+....<< main java.lang.Thread isAlive ()Z Thread.java
+....>> main java.lang.Object wait (J)V Object.java
+.....>> main java.lang.Object wait (JI)V Object.java
+.....<< main java.lang.Object wait (JI)V Object.java
+....<< main java.lang.Object wait (J)V Object.java
+....>> main java.lang.Thread isAlive ()Z Thread.java
+....<< main java.lang.Thread isAlive ()Z Thread.java
+...<< main java.lang.Thread join (J)V Thread.java
+..<< main java.lang.Thread join ()V Thread.java
+..>> main Main $noinline$doSomeWork ()V Main.java
+...>> main Main callOuterFunction ()V Main.java
+....>> main Main callLeafFunction ()V Main.java
+....<< main Main callLeafFunction ()V Main.java
+...<< main Main callOuterFunction ()V Main.java
+...>> main Main callLeafFunction ()V Main.java
+...<< main Main callLeafFunction ()V Main.java
+..<< main Main $noinline$doSomeWork ()V Main.java
+..>> main Main doSomeWorkThrow ()V Main.java
+...>> main Main callThrowFunction ()V Main.java
+....>> main java.lang.Exception <init> (Ljava/lang/String;)V Exception.java
+.....>> main java.lang.Throwable <init> (Ljava/lang/String;)V Throwable.java
+......>> main java.lang.Object <init> ()V Object.java
+......<< main java.lang.Object <init> ()V Object.java
+......>> main java.util.Collections emptyList ()Ljava/util/List; Collections.java
+......<< main java.util.Collections emptyList ()Ljava/util/List; Collections.java
+......>> main java.lang.Throwable fillInStackTrace ()Ljava/lang/Throwable; Throwable.java
+.......>> main java.lang.Throwable nativeFillInStackTrace ()Ljava/lang/Object; Throwable.java
+.......<< main java.lang.Throwable nativeFillInStackTrace ()Ljava/lang/Object; Throwable.java
+......<< main java.lang.Throwable fillInStackTrace ()Ljava/lang/Throwable; Throwable.java
+.....<< main java.lang.Throwable <init> (Ljava/lang/String;)V Throwable.java
+....<< main java.lang.Exception <init> (Ljava/lang/String;)V Exception.java
+...<<E main Main callThrowFunction ()V Main.java
+..<< main Main doSomeWorkThrow ()V Main.java
+..>> main Main$VMDebug $noinline$stopMethodTracing ()V Main.java
diff --git a/test/2246-trace-stream/info.txt b/test/2246-trace-stream/info.txt
new file mode 100644
index 0000000..fa93a97
--- /dev/null
+++ b/test/2246-trace-stream/info.txt
@@ -0,0 +1,2 @@
+Tests streaming method tracing. It verifies the format of the generated file is
+as expected.
diff --git a/test/2246-trace-stream/run.py b/test/2246-trace-stream/run.py
new file mode 100644
index 0000000..955f39a
--- /dev/null
+++ b/test/2246-trace-stream/run.py
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # The expected output non debuggable isn't consistent in all configurations.
+ # Investigate why the output is different and update the test to work for non
+ # debuggable runtimes too.
+ ctx.default_run(args, Xcompiler_option=["--debuggable"])
diff --git a/test/2246-trace-stream/src/Main.java b/test/2246-trace-stream/src/Main.java
new file mode 100644
index 0000000..37870f1
--- /dev/null
+++ b/test/2246-trace-stream/src/Main.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Method;
+
+public class Main {
+ private static final String TEMP_FILE_NAME_PREFIX = "test";
+ private static final String TEMP_FILE_NAME_SUFFIX = ".trace";
+ private static File file;
+
+ public static void main(String[] args) throws Exception {
+ String name = System.getProperty("java.vm.name");
+ if (!"Dalvik".equals(name)) {
+ System.out.println("This test is not supported on " + name);
+ return;
+ }
+ file = createTempFile();
+ FileOutputStream out_file = new FileOutputStream(file);
+ Main m = new Main();
+ Thread t = new Thread(() -> {
+ Main m1 = new Main();
+ m1.$noinline$doSomeWork();
+ }, "TestThread2246");
+ try {
+ if (VMDebug.getMethodTracingMode() != 0) {
+ VMDebug.$noinline$stopMethodTracing();
+ }
+
+ VMDebug.startMethodTracing(file.getPath(), out_file.getFD(), 0, 0, false, 0, true);
+ t.start();
+ t.join();
+ m.$noinline$doSomeWork();
+ m.doSomeWorkThrow();
+ VMDebug.$noinline$stopMethodTracing();
+ out_file.close();
+ m.CheckTraceFileFormat(file);
+ } finally {
+ if (out_file != null) {
+ out_file.close();
+ }
+ }
+ }
+
+ private void CheckTraceFileFormat(File trace_file) throws Exception {
+ StreamTraceParser parser = new StreamTraceParser(trace_file);
+ parser.validateTraceHeader(StreamTraceParser.TRACE_VERSION_DUAL_CLOCK);
+ boolean has_entries = true;
+ boolean seen_stop_tracing_method = false;
+ while (has_entries) {
+ int header_type = parser.GetEntryHeader();
+ switch (header_type) {
+ case 1:
+ parser.ProcessMethodInfoEntry();
+ break;
+ case 2:
+ parser.ProcessThreadInfoEntry();
+ break;
+ case 3:
+ // TODO(mythria): Add test to also check format of trace summary.
+ has_entries = false;
+ break;
+ default:
+ String event_string = parser.ProcessEventEntry(header_type);
+ // Ignore events after method tracing was stopped. The code that is executed
+ // later could be non-deterministic.
+ if (!seen_stop_tracing_method) {
+ System.out.println(event_string);
+ }
+ if (event_string.contains("Main$VMDebug $noinline$stopMethodTracing")) {
+ seen_stop_tracing_method = true;
+ }
+ }
+ }
+ parser.closeFile();
+ }
+
+ private static File createTempFile() throws Exception {
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e) {
+ System.setProperty("java.io.tmpdir", "/data/local/tmp");
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e2) {
+ System.setProperty("java.io.tmpdir", "/sdcard");
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ }
+ }
+ }
+
+ public void callOuterFunction() {
+ callLeafFunction();
+ }
+
+ public void callLeafFunction() {}
+
+ public void $noinline$doSomeWork() {
+ callOuterFunction();
+ callLeafFunction();
+ }
+
+ public void callThrowFunction() throws Exception {
+ throw new Exception("test");
+ }
+
+ public void doSomeWorkThrow() {
+ try {
+ callThrowFunction();
+ } catch (Exception e) {
+ }
+ }
+
+ private static class VMDebug {
+ private static final Method startMethodTracingMethod;
+ private static final Method stopMethodTracingMethod;
+ private static final Method getMethodTracingModeMethod;
+ static {
+ try {
+ Class<?> c = Class.forName("dalvik.system.VMDebug");
+ startMethodTracingMethod = c.getDeclaredMethod("startMethodTracing", String.class,
+ FileDescriptor.class, Integer.TYPE, Integer.TYPE, Boolean.TYPE,
+ Integer.TYPE, Boolean.TYPE);
+ stopMethodTracingMethod = c.getDeclaredMethod("stopMethodTracing");
+ getMethodTracingModeMethod = c.getDeclaredMethod("getMethodTracingMode");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void startMethodTracing(String filename, FileDescriptor fd, int bufferSize,
+ int flags, boolean samplingEnabled, int intervalUs, boolean streaming)
+ throws Exception {
+ startMethodTracingMethod.invoke(
+ null, filename, fd, bufferSize, flags, samplingEnabled, intervalUs, streaming);
+ }
+ public static void $noinline$stopMethodTracing() throws Exception {
+ stopMethodTracingMethod.invoke(null);
+ }
+ public static int getMethodTracingMode() throws Exception {
+ return (int) getMethodTracingModeMethod.invoke(null);
+ }
+ }
+}
diff --git a/test/2246-trace-stream/src/StreamTraceParser.java b/test/2246-trace-stream/src/StreamTraceParser.java
new file mode 100644
index 0000000..8995342
--- /dev/null
+++ b/test/2246-trace-stream/src/StreamTraceParser.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+
+public class StreamTraceParser {
+ public static final int MAGIC_NUMBER = 0x574f4c53;
+ public static final int DUAL_CLOCK_VERSION = 3;
+ public static final int TRACE_VERSION_DUAL_CLOCK = 0xF3;
+
+ public StreamTraceParser(File file) throws IOException {
+ dataStream = new DataInputStream(new FileInputStream(file));
+ method_id_map = new HashMap<Integer, String>();
+ thread_id_map = new HashMap<Integer, String>();
+ }
+
+ public void closeFile() throws IOException {
+ dataStream.close();
+ }
+
+ public String readString(int num_bytes) throws IOException {
+ byte[] buffer = new byte[num_bytes];
+ dataStream.readFully(buffer);
+ return new String(buffer, StandardCharsets.UTF_8);
+ }
+
+ public int readNumber(int num_bytes) throws IOException {
+ int number = 0;
+ for (int i = 0; i < num_bytes; i++) {
+ number += dataStream.readUnsignedByte() << (i * 8);
+ }
+ return number;
+ }
+
+ public void validateTraceHeader(int expected_version) throws Exception {
+ // Read 4-byte magic_number
+ int magic_number = readNumber(4);
+ if (magic_number != MAGIC_NUMBER) {
+ throw new Exception("Magic number doesn't match. Expected "
+ + Integer.toHexString(MAGIC_NUMBER) + " Got "
+ + Integer.toHexString(magic_number));
+ }
+ // Read 2-byte version
+ int version = readNumber(2);
+ if (version != expected_version) {
+ throw new Exception(
+ "Unexpected version. Expected " + expected_version + " Got " + version);
+ }
+ trace_format_version = version & 0xF;
+ // Read 2-byte header_length length
+ int header_length = readNumber(2);
+ // Read 8-byte starting time - Ignore timestamps since they are not deterministic
+ dataStream.skipBytes(8);
+ // 4 byte magic_number + 2 byte version + 2 byte offset + 8 byte timestamp
+ int num_bytes_read = 16;
+ if (version >= DUAL_CLOCK_VERSION) {
+ // Read 2-byte record size.
+ // TODO(mythria): Check why this is needed. We can derive record_size from version. Not
+ // sure why this is needed.
+ record_size = readNumber(2);
+ num_bytes_read += 2;
+ }
+ // Skip any padding
+ if (header_length > num_bytes_read) {
+ dataStream.skipBytes(header_length - num_bytes_read);
+ }
+ }
+
+ public int GetEntryHeader() throws IOException {
+ // Read 2-byte thread-id. On host thread-ids can be greater than 16-bit.
+ int thread_id = readNumber(2);
+ if (thread_id != 0) {
+ return thread_id;
+ }
+ // Read 1-byte header type
+ return readNumber(1);
+ }
+
+ public void ProcessMethodInfoEntry() throws IOException {
+ // Read 2-byte method info size
+ int header_length = readNumber(2);
+ // Read header_size data.
+ String method_info = readString(header_length);
+ String[] tokens = method_info.split("\t", 2);
+ // Get method_id and record method_id -> method_name map.
+ int method_id = Integer.decode(tokens[0]);
+ String method_line = tokens[1].replace('\t', ' ');
+ method_line = method_line.substring(0, method_line.length() - 1);
+ method_id_map.put(method_id, method_line);
+ }
+
+ public void ProcessThreadInfoEntry() throws IOException {
+ // Read 2-byte thread id
+ int thread_id = readNumber(2);
+ // Read 2-byte thread info size
+ int header_length = readNumber(2);
+ // Read header_size data.
+ String thread_info = readString(header_length);
+ thread_id_map.put(thread_id, thread_info);
+ }
+
+ public String eventTypeToString(int event_type) {
+ String str = "";
+ for (int i = 0; i < nesting_level; i++) {
+ str += ".";
+ }
+ switch (event_type) {
+ case 0:
+ nesting_level++;
+ str += ".>>";
+ break;
+ case 1:
+ nesting_level--;
+ str += "<<";
+ break;
+ case 2:
+ nesting_level--;
+ str += "<<E";
+ break;
+ default:
+ str += "??";
+ }
+ return str;
+ }
+
+ public String ProcessEventEntry(int thread_id) throws IOException {
+ // Read 4-byte method value
+ int method_and_event = readNumber(4);
+ int method_id = method_and_event & ~0x3;
+ int event_type = method_and_event & 0x3;
+
+ String str = eventTypeToString(event_type) + " " + thread_id_map.get(thread_id) + " "
+ + method_id_map.get(method_id);
+ // Depending on the version skip either one or two timestamps.
+ // TODO(mythria): Probably add a check that time stamps are always greater than initial
+ // timestamp.
+ int num_bytes_timestamp = (trace_format_version == 2) ? 4 : 8;
+ dataStream.skipBytes(num_bytes_timestamp);
+ return str;
+ }
+
+ DataInputStream dataStream;
+ HashMap<Integer, String> method_id_map;
+ HashMap<Integer, String> thread_id_map;
+ int record_size = 0;
+ int trace_format_version = 0;
+ int nesting_level = 0;
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2247-checker-write-barrier-elimination/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2247-checker-write-barrier-elimination/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2247-checker-write-barrier-elimination/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2247-checker-write-barrier-elimination/expected-stdout.txt
diff --git a/test/2247-checker-write-barrier-elimination/info.txt b/test/2247-checker-write-barrier-elimination/info.txt
new file mode 100644
index 0000000..2515317
--- /dev/null
+++ b/test/2247-checker-write-barrier-elimination/info.txt
@@ -0,0 +1 @@
+Tests that we eliminate unneeded write barriers.
diff --git a/test/2247-checker-write-barrier-elimination/src/Main.java b/test/2247-checker-write-barrier-elimination/src/Main.java
new file mode 100644
index 0000000..76fb05a
--- /dev/null
+++ b/test/2247-checker-write-barrier-elimination/src/Main.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class MultipleObject {
+ Object inner;
+ Object inner2;
+
+ static Object inner_static;
+}
+
+public class Main {
+ public static void main(String[] args) throws Error {
+ // Several sets, same receiver.
+ $noinline$testInstanceFieldSets(new Main(), new Object(), new Object(), new Object());
+ $noinline$testStaticFieldSets(new Object(), new Object(), new Object());
+ // Object ArraySets can throw since they need a type check so we cannot perform the
+ // optimization.
+ $noinline$testArraySets(new Object[3], new Object(), new Object(), new Object());
+ // If we are swapping elements in the array, no need for a type check.
+ $noinline$testSwapArray(new Object[3]);
+ // If the array and the values have the same RTI, no need for a type check.
+ $noinline$testArraySetsSameRTI();
+
+ // We cannot rely on `null` sets to perform the optimization.
+ $noinline$testNullInstanceFieldSets(new Main(), new Object());
+ $noinline$testNullStaticFieldSets(new Object());
+ $noinline$testNullArraySets(new Object[3], new Object());
+
+ // Several sets, multiple receivers. (set obj1, obj2, obj1 and see that the card of obj1
+ // gets eliminated)
+ $noinline$testInstanceFieldSetsMultipleReceivers(
+ new Main(), new Object(), new Object(), new Object());
+ $noinline$testStaticFieldSetsMultipleReceivers(new Object(), new Object(), new Object());
+ $noinline$testArraySetsMultipleReceiversSameRTI();
+
+ // The write barrier elimination optimization is blocked by invokes, suspend checks, and
+ // instructions that can throw.
+ $noinline$testInstanceFieldSetsBlocked(
+ new Main(), new Object(), new Object(), new Object());
+ $noinline$testStaticFieldSetsBlocked(new Object(), new Object(), new Object());
+ $noinline$testArraySetsSameRTIBlocked();
+ }
+
+ /// CHECK-START: Main Main.$noinline$testInstanceFieldSets(Main, java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: InstanceFieldSet field_name:Main.inner field_type:Reference write_barrier_kind:EmitNoNullCheck
+ /// CHECK: InstanceFieldSet field_name:Main.inner2 field_type:Reference write_barrier_kind:DontEmit
+ /// CHECK: InstanceFieldSet field_name:Main.inner3 field_type:Reference write_barrier_kind:DontEmit
+
+ /// CHECK-START: Main Main.$noinline$testInstanceFieldSets(Main, java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static Main $noinline$testInstanceFieldSets(Main m, Object o, Object o2, Object o3) {
+ m.inner = o;
+ m.inner2 = o2;
+ m.inner3 = o3;
+ return m;
+ }
+
+ /// CHECK-START: void Main.$noinline$testStaticFieldSets(java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: StaticFieldSet field_name:Main.inner_static field_type:Reference write_barrier_kind:EmitNoNullCheck
+ /// CHECK: StaticFieldSet field_name:Main.inner_static2 field_type:Reference write_barrier_kind:DontEmit
+ /// CHECK: StaticFieldSet field_name:Main.inner_static3 field_type:Reference write_barrier_kind:DontEmit
+
+ /// CHECK-START: void Main.$noinline$testStaticFieldSets(java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static void $noinline$testStaticFieldSets(Object o, Object o2, Object o3) {
+ inner_static = o;
+ inner_static2 = o2;
+ inner_static3 = o3;
+ }
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testArraySets(java.lang.Object[], java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: ArraySet needs_type_check:true can_trigger_gc:true write_barrier_kind:EmitNoNullCheck
+ /// CHECK: ArraySet needs_type_check:true can_trigger_gc:true write_barrier_kind:EmitNoNullCheck
+ /// CHECK: ArraySet needs_type_check:true can_trigger_gc:true write_barrier_kind:EmitNoNullCheck
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testArraySets(java.lang.Object[], java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static java.lang.Object[] $noinline$testArraySets(
+ Object[] arr, Object o, Object o2, Object o3) {
+ arr[0] = o;
+ arr[1] = o2;
+ arr[2] = o3;
+ return arr;
+ }
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testSwapArray(java.lang.Object[]) disassembly (after)
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNoNullCheck
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testSwapArray(java.lang.Object[]) disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static java.lang.Object[] $noinline$testSwapArray(Object[] arr) {
+ arr[0] = arr[1];
+ arr[1] = arr[2];
+ arr[2] = arr[0];
+ return arr;
+ }
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testArraySetsSameRTI() disassembly (after)
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNoNullCheck
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testArraySetsSameRTI() disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static java.lang.Object[] $noinline$testArraySetsSameRTI() {
+ Object[] arr = new Object[3];
+ arr[0] = inner_static;
+ arr[1] = inner_static2;
+ arr[2] = inner_static3;
+ return arr;
+ }
+
+ /// CHECK-START: Main Main.$noinline$testNullInstanceFieldSets(Main, java.lang.Object) disassembly (after)
+ /// CHECK: InstanceFieldSet field_name:Main.inner field_type:Reference write_barrier_kind:DontEmit
+ /// CHECK: InstanceFieldSet field_name:Main.inner2 field_type:Reference write_barrier_kind:EmitWithNullCheck
+ /// CHECK: InstanceFieldSet field_name:Main.inner3 field_type:Reference write_barrier_kind:DontEmit
+
+ /// CHECK-START: Main Main.$noinline$testNullInstanceFieldSets(Main, java.lang.Object) disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static Main $noinline$testNullInstanceFieldSets(Main m, Object o) {
+ m.inner = null;
+ m.inner2 = o;
+ m.inner3 = null;
+ return m;
+ }
+
+ /// CHECK-START: void Main.$noinline$testNullStaticFieldSets(java.lang.Object) disassembly (after)
+ /// CHECK: StaticFieldSet field_name:Main.inner_static field_type:Reference write_barrier_kind:DontEmit
+ /// CHECK: StaticFieldSet field_name:Main.inner_static2 field_type:Reference write_barrier_kind:EmitWithNullCheck
+ /// CHECK: StaticFieldSet field_name:Main.inner_static3 field_type:Reference write_barrier_kind:DontEmit
+
+ /// CHECK-START: void Main.$noinline$testNullStaticFieldSets(java.lang.Object) disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static void $noinline$testNullStaticFieldSets(Object o) {
+ inner_static = null;
+ inner_static2 = o;
+ inner_static3 = null;
+ }
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testNullArraySets(java.lang.Object[], java.lang.Object) disassembly (after)
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit
+ /// CHECK: ArraySet needs_type_check:true can_trigger_gc:true write_barrier_kind:EmitNoNullCheck
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testNullArraySets(java.lang.Object[], java.lang.Object) disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static Object[] $noinline$testNullArraySets(Object[] arr, Object o) {
+ arr[0] = null;
+ arr[1] = o;
+ arr[2] = null;
+ return arr;
+ }
+
+ /// CHECK-START: Main Main.$noinline$testInstanceFieldSetsMultipleReceivers(Main, java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ // There are two extra card_tables for the initialization of the MultipleObject.
+ /// CHECK: InstanceFieldSet field_name:MultipleObject.inner field_type:Reference write_barrier_kind:EmitNoNullCheck
+ /// CHECK: InstanceFieldSet field_name:MultipleObject.inner field_type:Reference write_barrier_kind:EmitWithNullCheck
+ /// CHECK: InstanceFieldSet field_name:MultipleObject.inner2 field_type:Reference write_barrier_kind:DontEmit
+
+ // Each one of the two NewInstance instructions have their own `card_table` reference.
+ /// CHECK-START: Main Main.$noinline$testInstanceFieldSetsMultipleReceivers(Main, java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static Main $noinline$testInstanceFieldSetsMultipleReceivers(
+ Main m, Object o, Object o2, Object o3) throws Error {
+ m.mo = new MultipleObject();
+ m.mo2 = new MultipleObject();
+
+ m.mo.inner = o;
+ // This card table for `m.mo2` can't me removed. Note that in `m.mo2 = new
+ // MultipleObject();` above the receiver is `m`, not `m.mo2.
+ m.mo2.inner = o2;
+ // This card table for `m.mo` can me removed.
+ m.mo.inner2 = o3;
+ return m;
+ }
+
+ /// CHECK-START: void Main.$noinline$testStaticFieldSetsMultipleReceivers(java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: StaticFieldSet field_name:MultipleObject.inner_static field_type:Reference write_barrier_kind:EmitWithNullCheck
+ /// CHECK: StaticFieldSet field_name:Main.inner_static2 field_type:Reference write_barrier_kind:EmitNoNullCheck
+ /// CHECK: StaticFieldSet field_name:Main.inner_static3 field_type:Reference write_barrier_kind:DontEmit
+
+ /// CHECK-START: void Main.$noinline$testStaticFieldSetsMultipleReceivers(java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static void $noinline$testStaticFieldSetsMultipleReceivers(
+ Object o, Object o2, Object o3) {
+ MultipleObject.inner_static = o;
+ inner_static2 = o2;
+ inner_static3 = o3;
+ }
+
+ /// CHECK-START: java.lang.Object[][] Main.$noinline$testArraySetsMultipleReceiversSameRTI() disassembly (after)
+ // Initializing the values
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNoNullCheck
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNoNullCheck
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit
+ // Setting the `array_of_arrays`.
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNoNullCheck
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:DontEmit
+
+ /// CHECK-START: java.lang.Object[][] Main.$noinline$testArraySetsMultipleReceiversSameRTI() disassembly (after)
+ // Two array sets can't eliminate the write barrier
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ // One write barrier for the array of arrays' sets
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static java.lang.Object[][] $noinline$testArraySetsMultipleReceiversSameRTI() {
+ Object[] arr = new Object[3];
+ Object[] other_arr = new Object[3];
+
+ arr[0] = inner_static;
+ other_arr[1] = inner_static2;
+ arr[2] = inner_static3;
+
+ // Return them so that LSE doesn't delete them
+ Object[][] array_of_arrays = {arr, other_arr};
+ return array_of_arrays;
+ }
+
+ private static void $noinline$emptyMethod() {}
+
+ /// CHECK-START: Main Main.$noinline$testInstanceFieldSetsBlocked(Main, java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: InstanceFieldSet field_name:Main.inner field_type:Reference write_barrier_kind:EmitWithNullCheck
+ /// CHECK: InvokeStaticOrDirect method_name:Main.$noinline$emptyMethod
+ /// CHECK: InstanceFieldSet field_name:Main.inner2 field_type:Reference write_barrier_kind:EmitWithNullCheck
+ /// CHECK: MonitorOperation kind:enter
+ /// CHECK: InstanceFieldSet field_name:Main.inner3 field_type:Reference write_barrier_kind:EmitWithNullCheck
+
+ /// CHECK-START: Main Main.$noinline$testInstanceFieldSetsBlocked(Main, java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static Main $noinline$testInstanceFieldSetsBlocked(
+ Main m, Object o, Object o2, Object o3) {
+ m.inner = o;
+ $noinline$emptyMethod();
+ m.inner2 = o2;
+ synchronized (m) {
+ m.inner3 = o3;
+ }
+ return m;
+ }
+
+ /// CHECK-START: void Main.$noinline$testStaticFieldSetsBlocked(java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: StaticFieldSet field_name:Main.inner_static field_type:Reference write_barrier_kind:EmitWithNullCheck
+ /// CHECK: InvokeStaticOrDirect method_name:Main.$noinline$emptyMethod
+ /// CHECK: StaticFieldSet field_name:Main.inner_static2 field_type:Reference write_barrier_kind:EmitWithNullCheck
+ /// CHECK: MonitorOperation kind:enter
+ /// CHECK: StaticFieldSet field_name:Main.inner_static3 field_type:Reference write_barrier_kind:EmitWithNullCheck
+
+ /// CHECK-START: void Main.$noinline$testStaticFieldSetsBlocked(java.lang.Object, java.lang.Object, java.lang.Object) disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static void $noinline$testStaticFieldSetsBlocked(Object o, Object o2, Object o3) {
+ inner_static = o;
+ $noinline$emptyMethod();
+ inner_static2 = o2;
+ Main m = new Main();
+ synchronized (m) {
+ inner_static3 = o3;
+ }
+ }
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testArraySetsSameRTIBlocked() disassembly (after)
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNoNullCheck
+ /// CHECK: InvokeStaticOrDirect method_name:Main.$noinline$emptyMethod
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNoNullCheck
+ /// CHECK: MonitorOperation kind:enter
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false write_barrier_kind:EmitNoNullCheck
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testArraySetsSameRTIBlocked() disassembly (after)
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ /// CHECK: ; card_table
+ /// CHECK-NOT: ; card_table
+ private static java.lang.Object[] $noinline$testArraySetsSameRTIBlocked() {
+ Object[] arr = new Object[3];
+ arr[0] = inner_static;
+ $noinline$emptyMethod();
+ arr[1] = inner_static2;
+ Main m = new Main();
+ synchronized (m) {
+ arr[2] = inner_static3;
+ }
+ return arr;
+ }
+
+ Object inner;
+ Object inner2;
+ Object inner3;
+
+ MultipleObject mo;
+ MultipleObject mo2;
+
+ static Object inner_static;
+ static Object inner_static2;
+ static Object inner_static3;
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2248-checker-smali-remove-try-until-the-end/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2248-checker-smali-remove-try-until-the-end/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2248-checker-smali-remove-try-until-the-end/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2248-checker-smali-remove-try-until-the-end/expected-stdout.txt
diff --git a/test/2248-checker-smali-remove-try-until-the-end/info.txt b/test/2248-checker-smali-remove-try-until-the-end/info.txt
new file mode 100644
index 0000000..0d7ded0
--- /dev/null
+++ b/test/2248-checker-smali-remove-try-until-the-end/info.txt
@@ -0,0 +1,2 @@
+Smali test checking that we correctly set the domination graph when having a
+try until the end of the method, and the catch doesn't flow to the exit.
diff --git a/test/2248-checker-smali-remove-try-until-the-end/smali/b_260387991.smali b/test/2248-checker-smali-remove-try-until-the-end/smali/b_260387991.smali
new file mode 100644
index 0000000..26d4de7
--- /dev/null
+++ b/test/2248-checker-smali-remove-try-until-the-end/smali/b_260387991.smali
@@ -0,0 +1,45 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+.class public LB260387991;
+
+.super Ljava/lang/Object;
+
+# When eliminating the unnecessary try and its catch block we turn the
+# TryBoundary instructions into Goto instructions. If one of these
+# instructions is pointing to the exit block, we use its single
+# predecessor instead. If this TryBoundary-turned-into-Goto instruction
+# was the only one pointing to the Exit, we also have to update the dominators.
+
+## CHECK-START: void B260387991.testInfiniteCatch() dead_code_elimination$initial (before)
+## CHECK: TryBoundary
+## CHECK: TryBoundary
+
+## CHECK-START: void B260387991.testInfiniteCatch() dead_code_elimination$initial (after)
+## CHECK-NOT: TryBoundary
+.method public static testInfiniteCatch()V
+ .registers 4
+ const/4 v0, 0x2
+ const/4 v1, 0x4
+ :try_start
+ div-int v0, v1, v0
+ return-void
+ :try_end
+ .catchall {:try_start .. :try_end} :catch_all
+
+ # Infinite catch block which does not lead to the exit block.
+ :catch_all
+ nop
+ goto :catch_all
+.end method
diff --git a/test/2248-checker-smali-remove-try-until-the-end/src/Main.java b/test/2248-checker-smali-remove-try-until-the-end/src/Main.java
new file mode 100644
index 0000000..1ad1a26
--- /dev/null
+++ b/test/2248-checker-smali-remove-try-until-the-end/src/Main.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2022 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) {}
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2249-checker-return-try-boundary-exit-in-loop/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2249-checker-return-try-boundary-exit-in-loop/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2249-checker-return-try-boundary-exit-in-loop/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2249-checker-return-try-boundary-exit-in-loop/expected-stdout.txt
diff --git a/test/2249-checker-return-try-boundary-exit-in-loop/info.txt b/test/2249-checker-return-try-boundary-exit-in-loop/info.txt
new file mode 100644
index 0000000..db79b12
--- /dev/null
+++ b/test/2249-checker-return-try-boundary-exit-in-loop/info.txt
@@ -0,0 +1,3 @@
+Tests that there is a case in which we have a
+ Return->TryBoundary kind:exit->Exit
+chain inside of a loop, and that said TryBoundary has loop information.
diff --git a/test/2249-checker-return-try-boundary-exit-in-loop/src/Main.java b/test/2249-checker-return-try-boundary-exit-in-loop/src/Main.java
new file mode 100644
index 0000000..070cd9f
--- /dev/null
+++ b/test/2249-checker-return-try-boundary-exit-in-loop/src/Main.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 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 {
+ assertNotNull($noinline$testReturnTryBoundaryExitInLoop(new Object()));
+ }
+
+ public static void assertNotNull(Object o) {
+ if (o == null) {
+ throw new Error("Expected not null!");
+ }
+ }
+
+ // Simple method to have a call inside of the synchronized block.
+ private static Object $noinline$call() {
+ return new Object();
+ }
+
+ // Consistency check: Three try boundary kind:exit. One for the explicit try catch, and two for
+ // the synchronized block (normal, and exceptional path).
+
+ /// CHECK-START: java.lang.Object Main.$inline$inner(java.lang.Object) builder (after)
+ /// CHECK: TryBoundary kind:exit
+ /// CHECK: TryBoundary kind:exit
+ /// CHECK: TryBoundary kind:exit
+ /// CHECK-NOT: TryBoundary kind:exit
+
+ /// CHECK-START: java.lang.Object Main.$inline$inner(java.lang.Object) builder (after)
+ /// CHECK: Return loop:B2
+
+ /// CHECK-START: java.lang.Object Main.$inline$inner(java.lang.Object) builder (after)
+ /// CHECK: TryBoundary kind:exit loop:B2
+ /// CHECK: TryBoundary kind:exit loop:B2
+ /// CHECK: TryBoundary kind:exit loop:B2
+ private static Object $inline$inner(Object o) {
+ for (int i = 0; i < 4; i++) {
+ try {
+ synchronized (o) {
+ return $noinline$call();
+ }
+ } catch (Error e) {
+ continue;
+ }
+ }
+ return null;
+ }
+
+ // Simple outer to inline `inner`.
+ private static Object $noinline$testReturnTryBoundaryExitInLoop(Object o) {
+ return $inline$inner(o);
+ }
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2250-inline-throw-into-try/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2250-inline-throw-into-try/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2250-inline-throw-into-try/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2250-inline-throw-into-try/expected-stdout.txt
diff --git a/test/2250-inline-throw-into-try/info.txt b/test/2250-inline-throw-into-try/info.txt
new file mode 100644
index 0000000..4d4cab5
--- /dev/null
+++ b/test/2250-inline-throw-into-try/info.txt
@@ -0,0 +1 @@
+Tests that we inline methods that end with a throw, inside of try blocks.
diff --git a/test/2250-inline-throw-into-try/src/Main.java b/test/2250-inline-throw-into-try/src/Main.java
new file mode 100644
index 0000000..d562279
--- /dev/null
+++ b/test/2250-inline-throw-into-try/src/Main.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 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 {
+ // Inline methods that sometimes throw.
+ $noinline$assertEquals(-1000, $noinline$testThrowsWithZero(0));
+ $noinline$assertEquals(1, $noinline$testThrowsWithZero(1));
+
+ // Tests that we can correctly inline even when the throw is not caught.
+ try {
+ $noinline$testThrowNotCaught(0);
+ unreachable();
+ } catch (Error expected) {
+ }
+ }
+
+ public static void $noinline$assertEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static int $noinline$testThrowsWithZero(int value) {
+ try {
+ return $inline$throwsWithZeroOrReturns(value);
+ } catch (Error e) {
+ return -1000;
+ }
+ }
+
+ private static int $inline$throwsWithZeroOrReturns(int value) {
+ if (value == 0) {
+ throw new Error("Zero!");
+ } else {
+ return value;
+ }
+ }
+
+ private static int $noinline$testThrowNotCaught(int value) {
+ try {
+ return $inline$throwsWithZeroOrReturns(value);
+ } catch (Exception e) {
+ return -1000;
+ }
+ }
+
+ private static void unreachable() throws Exception{
+ throw new Exception("Unreachable");
+ }
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2251-checker-irreducible-loop-do-not-inline/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2251-checker-irreducible-loop-do-not-inline/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2251-checker-irreducible-loop-do-not-inline/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2251-checker-irreducible-loop-do-not-inline/expected-stdout.txt
diff --git a/test/2251-checker-irreducible-loop-do-not-inline/info.txt b/test/2251-checker-irreducible-loop-do-not-inline/info.txt
new file mode 100644
index 0000000..6feb5a5
--- /dev/null
+++ b/test/2251-checker-irreducible-loop-do-not-inline/info.txt
@@ -0,0 +1,3 @@
+Tests that we don't inline a callee with
+ Return -> TryBoundary ->Exit
+chain if the caller has irreducible loops.
diff --git a/test/2251-checker-irreducible-loop-do-not-inline/smali/IrreducibleLoop.smali b/test/2251-checker-irreducible-loop-do-not-inline/smali/IrreducibleLoop.smali
new file mode 100644
index 0000000..700f73c
--- /dev/null
+++ b/test/2251-checker-irreducible-loop-do-not-inline/smali/IrreducibleLoop.smali
@@ -0,0 +1,58 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LIrreducibleLoop;
+
+.super Ljava/lang/Object;
+
+# Back-edges in the ascii-art graphs are represented with dash '-'.
+#
+# Check that testDoNotInlineInner has a simple irreducible loop
+#
+# entry
+# / \
+# / \
+# loop_entry \
+# / \- \
+# try_start\- \
+# other_loop_entry
+#
+# Consistency check: we didn't optimize away the irreducible loop
+## CHECK-START: java.lang.Object IrreducibleLoop.testDoNotInlineInner(java.lang.Object) register (after)
+## CHECK: irreducible:true
+#
+# We shouldn't inline `inner`.
+## CHECK-START: java.lang.Object IrreducibleLoop.testDoNotInlineInner(java.lang.Object) inliner (before)
+## CHECK: InvokeStaticOrDirect method_name:Main.inner
+#
+## CHECK-START: java.lang.Object IrreducibleLoop.testDoNotInlineInner(java.lang.Object) inliner (after)
+## CHECK: InvokeStaticOrDirect method_name:Main.inner
+.method public static testDoNotInlineInner(Ljava/lang/Object;)Ljava/lang/Object;
+ .registers 3
+ const/16 v0, 42
+ const/16 v1, 21
+ # Irreducible loop
+ if-eq v1, v0, :other_loop_entry
+ :loop_entry
+ if-ne v1, v0, :continue
+ add-int v0, v0, v0
+ :other_loop_entry
+ add-int v0, v0, v0
+ goto :loop_entry
+
+ :continue
+ invoke-static {p0}, LMain;->inner(Ljava/lang/Object;)Ljava/lang/Object;
+ move-result-object v0
+ return-object v0
+.end method
diff --git a/test/2251-checker-irreducible-loop-do-not-inline/src/Main.java b/test/2251-checker-irreducible-loop-do-not-inline/src/Main.java
new file mode 100644
index 0000000..94815db
--- /dev/null
+++ b/test/2251-checker-irreducible-loop-do-not-inline/src/Main.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Method;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ Object[] arguments = {new Object()};
+ Object result = Class.forName("IrreducibleLoop")
+ .getMethod("testDoNotInlineInner", Object.class)
+ .invoke(null, arguments);
+ if (result == null) {
+ throw new Exception("Expected non-null result");
+ }
+ }
+
+ // Simple method to have a call inside of the synchronized block.
+ private static Object $noinline$call() {
+ return new Object();
+ }
+
+ // `inner` has a Return -> TryBoundary -> Exit chain, which means that when we inline it we
+ // would need to recompute the loop information.
+
+ // Consistency check: Three try boundary kind:exit. One for the explicit try catch, and two for
+ // the synchronized block (normal, and exceptional path).
+
+ /// CHECK-START: java.lang.Object Main.inner(java.lang.Object) builder (after)
+ /// CHECK: TryBoundary kind:exit
+ /// CHECK: TryBoundary kind:exit
+ /// CHECK: TryBoundary kind:exit
+ /// CHECK-NOT: TryBoundary kind:exit
+
+ /// CHECK-START: java.lang.Object Main.inner(java.lang.Object) builder (after)
+ /// CHECK: Return loop:B2
+
+ /// CHECK-START: java.lang.Object Main.inner(java.lang.Object) builder (after)
+ /// CHECK: TryBoundary kind:exit loop:B2
+ /// CHECK: TryBoundary kind:exit loop:B2
+ /// CHECK: TryBoundary kind:exit loop:B2
+ public static Object inner(Object o) {
+ for (int i = 0; i < 4; i++) {
+ try {
+ synchronized (o) {
+ return $noinline$call();
+ }
+ } catch (Error e) {
+ continue;
+ }
+ }
+ return null;
+ }
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2252-class-value-before-and-after-u/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2252-class-value-before-and-after-u/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2252-class-value-before-and-after-u/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2252-class-value-before-and-after-u/expected-stdout.txt
diff --git a/test/2252-class-value-before-and-after-u/info.txt b/test/2252-class-value-before-and-after-u/info.txt
new file mode 100644
index 0000000..3a8d31f
--- /dev/null
+++ b/test/2252-class-value-before-and-after-u/info.txt
@@ -0,0 +1,2 @@
+java.lang.ClassValue should be visible to U or newer Android, but on older versions
+Class.forName("java.lang.ClassValue") should throw CNFE. See b/259501764.
\ No newline at end of file
diff --git a/test/2252-class-value-before-and-after-u/src-art/Main.java b/test/2252-class-value-before-and-after-u/src-art/Main.java
new file mode 100644
index 0000000..88746e1
--- /dev/null
+++ b/test/2252-class-value-before-and-after-u/src-art/Main.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import dalvik.system.VMRuntime;
+
+public class Main {
+ public static void main(String[] args) {
+ VMRuntime.getRuntime().setTargetSdkVersion(34);
+
+ try {
+ Class classValueClass = Class.forName("java.lang.ClassValue");
+ } catch (ClassNotFoundException ignored) {
+ throw new Error("java.lang.ClassValue should be available on targetSdkLevel 34");
+ }
+
+ VMRuntime.getRuntime().setTargetSdkVersion(33);
+ try {
+ Class classValueClass = Class.forName("java.lang.ClassValue");
+ throw new Error("Was able to find " + classValueClass + " on targetSdkLevel 33");
+ } catch (ClassNotFoundException expected) {}
+ }
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2252-rem-optimization-dividend-divisor/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2252-rem-optimization-dividend-divisor/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2252-rem-optimization-dividend-divisor/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2252-rem-optimization-dividend-divisor/expected-stdout.txt
diff --git a/test/2252-rem-optimization-dividend-divisor/info.txt b/test/2252-rem-optimization-dividend-divisor/info.txt
new file mode 100644
index 0000000..57e8b2e
--- /dev/null
+++ b/test/2252-rem-optimization-dividend-divisor/info.txt
@@ -0,0 +1,2 @@
+Test checking that FindDivWithInputsInBasicBlock works correctly
+if the dividend equals the divisor.
diff --git a/test/2252-rem-optimization-dividend-divisor/src/Main.java b/test/2252-rem-optimization-dividend-divisor/src/Main.java
new file mode 100644
index 0000000..1e1a674
--- /dev/null
+++ b/test/2252-rem-optimization-dividend-divisor/src/Main.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) {
+ $noinline$assertEquals(0, $noinline$testRemCaller());
+ }
+
+ public static void $noinline$assertEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static int $noinline$testRemCaller() {
+ return $inline$remMethod(50);
+ }
+
+ private static int $inline$remMethod(int param) {
+ // We were replacing this Rem with the div below when both the dividend and the
+ // divisor were the same. We shouldn't do that since we didn't find a Div(50, 50).
+ int result = param % 50;
+ return result / 50;
+ }
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/2253-checker-devirtualize-always-throws/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/2253-checker-devirtualize-always-throws/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/2253-checker-devirtualize-always-throws/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/2253-checker-devirtualize-always-throws/expected-stdout.txt
diff --git a/test/2253-checker-devirtualize-always-throws/info.txt b/test/2253-checker-devirtualize-always-throws/info.txt
new file mode 100644
index 0000000..ea76478
--- /dev/null
+++ b/test/2253-checker-devirtualize-always-throws/info.txt
@@ -0,0 +1,3 @@
+Tests that if we devirtualize a method that throws given the
+parameters, then we should also set the devirtualization as always
+throws.
diff --git a/test/2253-checker-devirtualize-always-throws/src/Main.java b/test/2253-checker-devirtualize-always-throws/src/Main.java
new file mode 100644
index 0000000..b6d2424
--- /dev/null
+++ b/test/2253-checker-devirtualize-always-throws/src/Main.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public final class Main {
+ public static void main(String[] args) {
+ try {
+ $noinline$testAlwaysThrowsDevirtualization();
+ System.out.println("Expected to throw error.");
+ } catch (Error expected) {
+ }
+ }
+
+ public void throwsIfParamIsZero(int param) {
+ if (param == 0) {
+ throw new Error("");
+ }
+ }
+
+ /// CHECK-START: void Main.$noinline$testAlwaysThrowsDevirtualization() inliner (before)
+ /// CHECK: InvokeVirtual method_name:Main.throwsIfParamIsZero always_throws:false
+ /// CHECK: ReturnVoid
+
+ /// CHECK-START: void Main.$noinline$testAlwaysThrowsDevirtualization() inliner (after)
+ /// CHECK-NOT: InvokeVirtual
+
+ /// CHECK-START: void Main.$noinline$testAlwaysThrowsDevirtualization() inliner (after)
+ /// CHECK: InvokeStaticOrDirect method_name:Main.throwsIfParamIsZero always_throws:true
+
+ public static void $noinline$testAlwaysThrowsDevirtualization() {
+ Main m = new Main();
+ m.throwsIfParamIsZero(0);
+ }
+}
diff --git a/test/303-verification-stress/build b/test/303-verification-stress/build
deleted file mode 100644
index 6e4a1d6..0000000
--- a/test/303-verification-stress/build
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2013 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.
-
-# Stop if something fails.
-set -e
-
-# Write out a bunch of source files.
-./classes-gen
-
-./default-build "$@"
diff --git a/test/303-verification-stress/build.py b/test/303-verification-stress/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/303-verification-stress/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/303-verification-stress/generate-sources b/test/303-verification-stress/generate-sources
new file mode 100755
index 0000000..2a2cdb6
--- /dev/null
+++ b/test/303-verification-stress/generate-sources
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2013 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.
+
+# Stop if something fails.
+set -e
+
+# Write out a bunch of source files.
+./classes-gen
diff --git a/test/304-method-tracing/run b/test/304-method-tracing/run
deleted file mode 100755
index 7bd1895..0000000
--- a/test/304-method-tracing/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Runs the test with method tracing enabled.
-exec ${RUN} "$@" --runtime-option -Xmethod-trace --runtime-option -Xmethod-trace-file:${DEX_LOCATION}/trace.bin
diff --git a/test/304-method-tracing/run.py b/test/304-method-tracing/run.py
new file mode 100644
index 0000000..2fb72d3
--- /dev/null
+++ b/test/304-method-tracing/run.py
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # Runs the test with method tracing enabled.
+ ctx.default_run(
+ args,
+ runtime_option=[
+ "-Xmethod-trace", "-Xmethod-trace-file:${DEX_LOCATION}/trace.bin"
+ ])
diff --git a/test/370-dex-v37/build b/test/370-dex-v37/build
deleted file mode 100755
index f472428..0000000
--- a/test/370-dex-v37/build
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2015 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.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@"
-
-if [[ $@ != *"--jvm"* ]]; then
- # Change the generated dex file to have a v36 magic number if it is version 35
- if test -f classes.dex && head -c 7 classes.dex | grep -q 035; then
- # place ascii value '037' into the classes.dex file starting at byte 4.
- printf '037' | dd status=none conv=notrunc of=classes.dex bs=1 seek=4 count=3
- rm -f $TEST_NAME.jar
- zip $TEST_NAME.jar classes.dex
- fi
-fi
diff --git a/test/370-dex-v37/build.py b/test/370-dex-v37/build.py
new file mode 100644
index 0000000..096ee82
--- /dev/null
+++ b/test/370-dex-v37/build.py
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+def build(ctx):
+ ctx.default_build()
+ if ctx.jvm:
+ return
+ # Change the generated dex file to have a v37 magic number if it is version 35
+ with open(ctx.test_dir / "classes.dex", "rb+") as f:
+ if f.read(8) == b"dex\n035\x00":
+ f.seek(0)
+ f.write(b"dex\n037\x00")
+ (ctx.test_dir / "370-dex-v37.jar").unlink()
+ ctx.soong_zip([
+ "-o", ctx.test_dir / "370-dex-v37.jar", "-j", "-f",
+ ctx.test_dir / "classes.dex"
+ ])
diff --git a/test/416-optimizing-arith-not/src/Main.java b/test/416-optimizing-arith-not/src/Main.java
index 44c7d3c..6bded7b 100644
--- a/test/416-optimizing-arith-not/src/Main.java
+++ b/test/416-optimizing-arith-not/src/Main.java
@@ -18,62 +18,62 @@
public class Main {
- public static void expectEquals(int expected, int result) {
- if (expected != result) {
- throw new Error("Expected: " + expected + ", found: " + result);
+ public static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
}
- }
- public static void expectEquals(long expected, long result) {
- if (expected != result) {
- throw new Error("Expected: " + expected + ", found: " + result);
+ public static void expectEquals(long expected, long result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
}
- }
- public static void main(String[] args) throws Exception {
- notInt();
- notLong();
- }
+ public static void main(String[] args) throws Exception {
+ notInt();
+ notLong();
+ }
- private static void notInt() throws Exception {
- expectEquals(1, smaliNotInt(-2));
- expectEquals(0, smaliNotInt(-1));
- expectEquals(-1, smaliNotInt(0));
- expectEquals(-2, smaliNotInt(1));
- expectEquals(2147483647, smaliNotInt(-2147483648)); // -(2^31)
- expectEquals(2147483646, smaliNotInt(-2147483647)); // -(2^31 - 1)
- expectEquals(-2147483647, smaliNotInt(2147483646)); // 2^31 - 2
- expectEquals(-2147483648, smaliNotInt(2147483647)); // 2^31 - 1
- }
+ private static void notInt() throws Exception {
+ expectEquals(1, smaliNotInt(-2));
+ expectEquals(0, smaliNotInt(-1));
+ expectEquals(-1, smaliNotInt(0));
+ expectEquals(-2, smaliNotInt(1));
+ expectEquals(2147483647, smaliNotInt(-2147483648)); // -(2^31)
+ expectEquals(2147483646, smaliNotInt(-2147483647)); // -(2^31 - 1)
+ expectEquals(-2147483647, smaliNotInt(2147483646)); // 2^31 - 2
+ expectEquals(-2147483648, smaliNotInt(2147483647)); // 2^31 - 1
+ }
- private static void notLong() throws Exception {
- expectEquals(1L, smaliNotLong(-2L));
- expectEquals(0L, smaliNotLong(-1L));
- expectEquals(-1L, smaliNotLong(0L));
- expectEquals(-2L, smaliNotLong(1L));
- expectEquals(2147483647L, smaliNotLong(-2147483648L)); // -(2^31)
- expectEquals(2147483646L, smaliNotLong(-2147483647L)); // -(2^31 - 1)
- expectEquals(-2147483647L, smaliNotLong(2147483646L)); // 2^31 - 2
- expectEquals(-2147483648L, smaliNotLong(2147483647L)); // 2^31 - 1
- expectEquals(9223372036854775807L, smaliNotLong(-9223372036854775808L)); // -(2^63)
- expectEquals(9223372036854775806L, smaliNotLong(-9223372036854775807L)); // -(2^63 - 1)
- expectEquals(-9223372036854775807L, smaliNotLong(9223372036854775806L)); // 2^63 - 2
- expectEquals(-9223372036854775808L, smaliNotLong(9223372036854775807L)); // 2^63 - 1
- }
+ private static void notLong() throws Exception {
+ expectEquals(1L, smaliNotLong(-2L));
+ expectEquals(0L, smaliNotLong(-1L));
+ expectEquals(-1L, smaliNotLong(0L));
+ expectEquals(-2L, smaliNotLong(1L));
+ expectEquals(2147483647L, smaliNotLong(-2147483648L)); // -(2^31)
+ expectEquals(2147483646L, smaliNotLong(-2147483647L)); // -(2^31 - 1)
+ expectEquals(-2147483647L, smaliNotLong(2147483646L)); // 2^31 - 2
+ expectEquals(-2147483648L, smaliNotLong(2147483647L)); // 2^31 - 1
+ expectEquals(9223372036854775807L, smaliNotLong(-9223372036854775808L)); // -(2^63)
+ expectEquals(9223372036854775806L, smaliNotLong(-9223372036854775807L)); // -(2^63 - 1)
+ expectEquals(-9223372036854775807L, smaliNotLong(9223372036854775806L)); // 2^63 - 2
+ expectEquals(-9223372036854775808L, smaliNotLong(9223372036854775807L)); // 2^63 - 1
+ }
- // Wrappers around methods located in file not.smali.
+ // Wrappers around methods located in file not.smali.
- private static int smaliNotInt(int a) throws Exception {
- Class<?> c = Class.forName("TestNot");
- Method m = c.getMethod("$opt$NotInt", int.class);
- int result = (Integer)m.invoke(null, a);
- return result;
- }
+ private static int smaliNotInt(int a) throws Exception {
+ Class<?> c = Class.forName("TestNot");
+ Method m = c.getMethod("$opt$NotInt", int.class);
+ int result = (Integer)m.invoke(null, a);
+ return result;
+ }
- private static long smaliNotLong(long a) throws Exception {
- Class<?> c = Class.forName("TestNot");
- Method m = c.getMethod("$opt$NotLong", long.class);
- long result = (Long)m.invoke(null, a);
- return result;
- }
+ private static long smaliNotLong(long a) throws Exception {
+ Class<?> c = Class.forName("TestNot");
+ Method m = c.getMethod("$opt$NotLong", long.class);
+ long result = (Long)m.invoke(null, a);
+ return result;
+ }
}
diff --git a/test/418-const-string/src/Main.java b/test/418-const-string/src/Main.java
index 7c1ffec..3b7d8e2 100644
--- a/test/418-const-string/src/Main.java
+++ b/test/418-const-string/src/Main.java
@@ -15,14 +15,14 @@
*/
public class Main {
- public static void main(String[] args) {
- // First call: may go in slow path.
- System.out.println($opt$ReturnHelloWorld());
- // Second call: no slow path.
- System.out.println($opt$ReturnHelloWorld());
- }
+ public static void main(String[] args) {
+ // First call: may go in slow path.
+ System.out.println($opt$ReturnHelloWorld());
+ // Second call: no slow path.
+ System.out.println($opt$ReturnHelloWorld());
+ }
- public static String $opt$ReturnHelloWorld() {
- return "Hello World";
- }
+ public static String $opt$ReturnHelloWorld() {
+ return "Hello World";
+ }
}
diff --git a/test/419-long-parameter/src/Main.java b/test/419-long-parameter/src/Main.java
index 808b7f6..f83b3f7 100644
--- a/test/419-long-parameter/src/Main.java
+++ b/test/419-long-parameter/src/Main.java
@@ -15,20 +15,20 @@
*/
public class Main {
- public static void main(String[] args) {
- if ($opt$TestCallee(1.0, 2.0, 1L, 2L) != 1L) {
- throw new Error("Unexpected result");
+ public static void main(String[] args) {
+ if ($opt$TestCallee(1.0, 2.0, 1L, 2L) != 1L) {
+ throw new Error("Unexpected result");
+ }
+ if ($opt$TestCaller() != 1L) {
+ throw new Error("Unexpected result");
+ }
}
- if ($opt$TestCaller() != 1L) {
- throw new Error("Unexpected result");
+
+ public static long $opt$TestCallee(double a, double b, long c, long d) {
+ return d - c;
}
- }
- public static long $opt$TestCallee(double a, double b, long c, long d) {
- return d - c;
- }
-
- public static long $opt$TestCaller() {
- return $opt$TestCallee(1.0, 2.0, 1L, 2L);
- }
+ public static long $opt$TestCaller() {
+ return $opt$TestCallee(1.0, 2.0, 1L, 2L);
+ }
}
diff --git a/test/442-checker-constant-folding/src/Main.java b/test/442-checker-constant-folding/src/Main.java
index 1bdf7b5..168ebbf 100644
--- a/test/442-checker-constant-folding/src/Main.java
+++ b/test/442-checker-constant-folding/src/Main.java
@@ -1577,6 +1577,282 @@
return (double) imm;
}
+ /// CHECK-START: int Main.$inline$SpecialCaseForZeroInt(int) constant_folding (before)
+ /// CHECK-DAG: Add
+ /// CHECK-DAG: Mul
+
+ /// CHECK-START: int Main.$inline$SpecialCaseForZeroInt(int) constant_folding (before)
+ /// CHECK-NOT: IntConstant 6
+
+ /// CHECK-START: int Main.$inline$SpecialCaseForZeroInt(int) constant_folding (after)
+ /// CHECK-NOT: Add
+
+ /// CHECK-START: int Main.$inline$SpecialCaseForZeroInt(int) constant_folding (after)
+ /// CHECK-NOT: Mul
+
+ /// CHECK-START: int Main.$inline$SpecialCaseForZeroInt(int) constant_folding (after)
+ /// CHECK-DAG: <<Const:i\d+>> IntConstant 6
+ /// CHECK-DAG: Return [<<Const>>]
+ private static int $inline$SpecialCaseForZeroInt(int value) {
+ if (value == 0) {
+ return (value + 2) * 3;
+ }
+ return value;
+ }
+
+ /// CHECK-START: long Main.$inline$SpecialCaseForZeroLong(long) constant_folding (before)
+ /// CHECK-DAG: Add
+ /// CHECK-DAG: Mul
+
+ /// CHECK-START: long Main.$inline$SpecialCaseForZeroLong(long) constant_folding (before)
+ /// CHECK-NOT: LongConstant 6
+
+ /// CHECK-START: long Main.$inline$SpecialCaseForZeroLong(long) constant_folding (after)
+ /// CHECK-NOT: Add
+
+ /// CHECK-START: long Main.$inline$SpecialCaseForZeroLong(long) constant_folding (after)
+ /// CHECK-NOT: Mul
+
+ /// CHECK-START: long Main.$inline$SpecialCaseForZeroLong(long) constant_folding (after)
+ /// CHECK-DAG: <<Const:j\d+>> LongConstant 6
+ /// CHECK-DAG: Return [<<Const>>]
+ private static long $inline$SpecialCaseForZeroLong(long value) {
+ if (value == 0L) {
+ return (value + 2) * 3;
+ }
+ return value;
+ }
+
+ /// CHECK-START: float Main.$noinline$SpecialCaseForZeroFloat(float) constant_folding (before)
+ /// CHECK-DAG: Add
+ /// CHECK-DAG: Mul
+
+ /// CHECK-START: float Main.$noinline$SpecialCaseForZeroFloat(float) constant_folding (after)
+ /// CHECK-NOT: FloatConstant 6
+
+ /// CHECK-START: float Main.$noinline$SpecialCaseForZeroFloat(float) constant_folding (after)
+ /// CHECK-DAG: Add
+ /// CHECK-DAG: Mul
+ private static float $noinline$SpecialCaseForZeroFloat(float value) {
+ if (value == 0F) {
+ return (value + 2F) * 3F;
+ }
+ return value;
+ }
+
+ /// CHECK-START: double Main.$noinline$SpecialCaseForZeroDouble(double) constant_folding (before)
+ /// CHECK-DAG: Add
+ /// CHECK-DAG: Mul
+
+ /// CHECK-START: double Main.$noinline$SpecialCaseForZeroDouble(double) constant_folding (after)
+ /// CHECK-NOT: DoubleConstant 6
+
+ /// CHECK-START: double Main.$noinline$SpecialCaseForZeroDouble(double) constant_folding (after)
+ /// CHECK-DAG: Add
+ /// CHECK-DAG: Mul
+ private static double $noinline$SpecialCaseForZeroDouble(double value) {
+ if (value == 0D) {
+ return (value + 2D) * 3D;
+ }
+ return value;
+ }
+
+ // Note that we have Add instead of sub since internally we do `Add(value, -1)`.
+ /// CHECK-START: int Main.$noinline$NotEqualsPropagationInt(int) constant_folding (before)
+ /// CHECK-DAG: Add
+ /// CHECK-DAG: Div
+
+ /// CHECK-START: int Main.$noinline$NotEqualsPropagationInt(int) constant_folding (after)
+ /// CHECK-NOT: Add
+
+ /// CHECK-START: int Main.$noinline$NotEqualsPropagationInt(int) constant_folding (after)
+ /// CHECK-NOT: Div
+
+ /// CHECK-START: int Main.$noinline$NotEqualsPropagationInt(int) constant_folding (after)
+ /// CHECK-DAG: <<Const:i\d+>> IntConstant 1
+ /// CHECK-DAG: Return [<<Const>>]
+ private static int $noinline$NotEqualsPropagationInt(int value) {
+ if (value != 3) {
+ return value;
+ } else {
+ return (value - 1) / 2;
+ }
+ }
+
+ /// CHECK-START: long Main.$noinline$NotEqualsPropagationLong(long) constant_folding (before)
+ /// CHECK-DAG: Sub
+ /// CHECK-DAG: Div
+
+ /// CHECK-START: long Main.$noinline$NotEqualsPropagationLong(long) constant_folding (after)
+ /// CHECK-NOT: Sub
+
+ /// CHECK-START: long Main.$noinline$NotEqualsPropagationLong(long) constant_folding (after)
+ /// CHECK-NOT: Div
+
+ /// CHECK-START: long Main.$noinline$NotEqualsPropagationLong(long) constant_folding (after)
+ /// CHECK-DAG: <<Const:j\d+>> LongConstant 1
+ /// CHECK-DAG: Return [<<Const>>]
+ private static long $noinline$NotEqualsPropagationLong(long value) {
+ if (value != 3L) {
+ return value;
+ } else {
+ return (value - 1L) / 2L;
+ }
+ }
+
+ /// CHECK-START: float Main.$noinline$NotEqualsPropagationFloat(float) constant_folding (before)
+ /// CHECK-DAG: Sub
+ /// CHECK-DAG: Div
+
+ /// CHECK-START: float Main.$noinline$NotEqualsPropagationFloat(float) constant_folding (after)
+ /// CHECK-DAG: Sub
+ /// CHECK-DAG: Div
+ private static float $noinline$NotEqualsPropagationFloat(float value) {
+ if (value != 3F) {
+ return value;
+ } else {
+ return (value - 1F) / 2F;
+ }
+ }
+
+ /// CHECK-START: double Main.$noinline$NotEqualsPropagationDouble(double) constant_folding (before)
+ /// CHECK-DAG: Sub
+ /// CHECK-DAG: Div
+
+ /// CHECK-START: double Main.$noinline$NotEqualsPropagationDouble(double) constant_folding (after)
+ /// CHECK-DAG: Sub
+ /// CHECK-DAG: Div
+ private static double $noinline$NotEqualsPropagationDouble(double value) {
+ if (value != 3D) {
+ return value;
+ } else {
+ return (value - 1D) / 2D;
+ }
+ }
+
+ /// CHECK-START: int Main.$noinline$InlineCaleeWithSpecialCaseForZeroInt(int) inliner (after)
+ /// CHECK-NOT: Add
+
+ /// CHECK-START: int Main.$noinline$InlineCaleeWithSpecialCaseForZeroInt(int) inliner (after)
+ /// CHECK-NOT: Mul
+
+ /// CHECK-START: int Main.$noinline$InlineCaleeWithSpecialCaseForZeroInt(int) inliner (after)
+ /// CHECK-DAG: <<Const:i\d+>> IntConstant 6
+ /// CHECK-DAG: Return [<<Const>>]
+ private static int $noinline$InlineCaleeWithSpecialCaseForZeroInt(int value) {
+ if (value == 0) {
+ return $inline$SpecialCaseForZeroInt(value);
+ }
+ return value;
+ }
+
+ /// CHECK-START: long Main.$noinline$InlineCaleeWithSpecialCaseForZeroLong(long) inliner (after)
+ /// CHECK-NOT: Add
+
+ /// CHECK-START: long Main.$noinline$InlineCaleeWithSpecialCaseForZeroLong(long) inliner (after)
+ /// CHECK-NOT: Mul
+
+ /// CHECK-START: long Main.$noinline$InlineCaleeWithSpecialCaseForZeroLong(long) inliner (after)
+ /// CHECK-DAG: <<Const:j\d+>> LongConstant 6
+ /// CHECK-DAG: Return [<<Const>>]
+ private static long $noinline$InlineCaleeWithSpecialCaseForZeroLong(long value) {
+ if (value == 0L) {
+ return $inline$SpecialCaseForZeroLong(value);
+ }
+ return value;
+ }
+
+ // Check that don't propagate the value == 3 on `if not true` branch, as the `if true` branch also
+ // flows into the same block.
+ /// CHECK-START: int Main.$noinline$NotEqualsImplicitElseInt(int) constant_folding (before)
+ /// CHECK-DAG: Add
+ /// CHECK-DAG: Div
+
+ /// CHECK-START: int Main.$noinline$NotEqualsImplicitElseInt(int) constant_folding (after)
+ /// CHECK-DAG: Add
+ /// CHECK-DAG: Div
+ private static int $noinline$NotEqualsImplicitElseInt(int value) {
+ if (value != 3) {
+ value++;
+ }
+ return (value - 1) / 2;
+ }
+
+ /// CHECK-START: long Main.$noinline$NotEqualsImplicitElseLong(long) constant_folding (before)
+ /// CHECK-DAG: Sub
+ /// CHECK-DAG: Div
+
+ /// CHECK-START: long Main.$noinline$NotEqualsImplicitElseLong(long) constant_folding (after)
+ /// CHECK-DAG: Sub
+ /// CHECK-DAG: Div
+ private static long $noinline$NotEqualsImplicitElseLong(long value) {
+ if (value != 3L) {
+ value += 1L;
+ }
+ return (value - 1L) / 2L;
+ }
+
+ /// CHECK-START: float Main.$noinline$NotEqualsImplicitElseFloat(float) constant_folding (before)
+ /// CHECK-DAG: Sub
+ /// CHECK-DAG: Div
+
+ /// CHECK-START: float Main.$noinline$NotEqualsImplicitElseFloat(float) constant_folding (after)
+ /// CHECK-DAG: Sub
+ /// CHECK-DAG: Div
+ private static float $noinline$NotEqualsImplicitElseFloat(float value) {
+ if (value != 3F) {
+ value += 1F;
+ }
+ return (value - 1F) / 2F;
+ }
+
+ /// CHECK-START: double Main.$noinline$NotEqualsImplicitElseDouble(double) constant_folding (before)
+ /// CHECK-DAG: Sub
+ /// CHECK-DAG: Div
+
+ /// CHECK-START: double Main.$noinline$NotEqualsImplicitElseDouble(double) constant_folding (after)
+ /// CHECK-DAG: Sub
+ /// CHECK-DAG: Div
+ private static double $noinline$NotEqualsImplicitElseDouble(double value) {
+ if (value != 3D) {
+ value += 1D;
+ }
+ return (value - 1D) / 2D;
+ }
+
+ // By propagating the boolean we can elimniate some equality comparisons as we already know their
+ // result. In turn, we also enable DeadCodeElimination to eliminate more code.
+ /// CHECK-START: int Main.$noinline$PropagatingParameterValue(boolean) constant_folding (before)
+ /// CHECK-DAG: Equal
+ /// CHECK-DAG: Equal
+ /// CHECK-DAG: Equal
+
+ /// CHECK-START: int Main.$noinline$PropagatingParameterValue(boolean) constant_folding (after)
+ /// CHECK: Equal
+ /// CHECK-NOT: Equal
+
+ /// CHECK-START: int Main.$noinline$PropagatingParameterValue(boolean) dead_code_elimination$initial (before)
+ /// CHECK-DAG: IntConstant 1
+ /// CHECK-DAG: IntConstant 2
+ /// CHECK-DAG: IntConstant 3
+ /// CHECK-DAG: IntConstant 4
+
+ /// CHECK-START: int Main.$noinline$PropagatingParameterValue(boolean) dead_code_elimination$initial (after)
+ /// CHECK-DAG: IntConstant 1
+ /// CHECK-DAG: IntConstant 4
+
+ /// CHECK-START: int Main.$noinline$PropagatingParameterValue(boolean) dead_code_elimination$initial (after)
+ /// CHECK-NOT: IntConstant 2
+
+ /// CHECK-START: int Main.$noinline$PropagatingParameterValue(boolean) dead_code_elimination$initial (after)
+ /// CHECK-NOT: IntConstant 3
+ private static int $noinline$PropagatingParameterValue(boolean value) {
+ if (value) {
+ return value ? 1 : 2;
+ } else {
+ return value ? 3 : 4;
+ }
+ }
public static void main(String[] args) throws Exception {
assertIntEquals(-42, IntNegation());
@@ -1708,6 +1984,51 @@
assertDoubleEquals(33, ReturnDouble33());
assertDoubleEquals(34, ReturnDouble34());
assertDoubleEquals(99.25, ReturnDouble99P25());
+
+ // Tests for propagating known values due to if clauses.
+
+ // Propagating within the same method. These are marked $inline$ since we used them in
+ // `InlineCaleeWithSpecialCaseForZeroInt`.
+ assertIntEquals(6, $inline$SpecialCaseForZeroInt(0));
+ assertIntEquals(3, $inline$SpecialCaseForZeroInt(3));
+ assertLongEquals(6L, $inline$SpecialCaseForZeroLong(0L));
+ assertLongEquals(3L, $inline$SpecialCaseForZeroLong(3L));
+ // Floats and doubles we do not optimize (here and below). These methods are here to guarantee
+ // that.
+ assertFloatEquals(6F, $noinline$SpecialCaseForZeroFloat(0F));
+ assertFloatEquals(3F, $noinline$SpecialCaseForZeroFloat(3F));
+ assertDoubleEquals(6D, $noinline$SpecialCaseForZeroDouble(0D));
+ assertDoubleEquals(3D, $noinline$SpecialCaseForZeroDouble(3D));
+
+ // Propagating within the same method, with not equals
+ assertIntEquals(0, $noinline$NotEqualsPropagationInt(0));
+ assertIntEquals(1, $noinline$NotEqualsPropagationInt(3));
+ assertLongEquals(0L, $noinline$NotEqualsPropagationLong(0L));
+ assertLongEquals(1L, $noinline$NotEqualsPropagationLong(3L));
+ assertFloatEquals(0F, $noinline$NotEqualsPropagationFloat(0F));
+ assertFloatEquals(1F, $noinline$NotEqualsPropagationFloat(3F));
+ assertDoubleEquals(0D, $noinline$NotEqualsPropagationDouble(0D));
+ assertDoubleEquals(1D, $noinline$NotEqualsPropagationDouble(3D));
+
+ // Propagating so that the inliner can use it.
+ assertIntEquals(6, $noinline$InlineCaleeWithSpecialCaseForZeroInt(0));
+ assertIntEquals(3, $noinline$InlineCaleeWithSpecialCaseForZeroInt(3));
+ assertLongEquals(6L, $noinline$InlineCaleeWithSpecialCaseForZeroLong(0L));
+ assertLongEquals(3L, $noinline$InlineCaleeWithSpecialCaseForZeroLong(3L));
+
+ // Propagating within the same method, with not equals
+ assertIntEquals(0, $noinline$NotEqualsImplicitElseInt(0));
+ assertIntEquals(1, $noinline$NotEqualsImplicitElseInt(3));
+ assertLongEquals(0L, $noinline$NotEqualsImplicitElseLong(0L));
+ assertLongEquals(1L, $noinline$NotEqualsImplicitElseLong(3L));
+ assertFloatEquals(0F, $noinline$NotEqualsImplicitElseFloat(0F));
+ assertFloatEquals(1F, $noinline$NotEqualsImplicitElseFloat(3F));
+ assertDoubleEquals(0D, $noinline$NotEqualsImplicitElseDouble(0D));
+ assertDoubleEquals(1D, $noinline$NotEqualsImplicitElseDouble(3D));
+
+ // Propagating parameters.
+ assertIntEquals(1, $noinline$PropagatingParameterValue(true));
+ assertIntEquals(4, $noinline$PropagatingParameterValue(false));
}
Main() throws ClassNotFoundException {
diff --git a/test/449-checker-bce/src/Main.java b/test/449-checker-bce/src/Main.java
index 1144366..fbe3586 100644
--- a/test/449-checker-bce/src/Main.java
+++ b/test/449-checker-bce/src/Main.java
@@ -1125,7 +1125,7 @@
/// CHECK-DAG: <<Len:i\d+>> ArrayLength [<<Nul>>] loop:none
/// CHECK-DAG: Equal [<<Len>>,<<Val>>] loop:none
/// CHECK-DAG: <<Idx:i\d+>> Phi loop:<<Loop:B\d+>>
- /// CHECK-DAG: BoundsCheck [<<Idx>>,<<Len>>] loop:<<Loop>>
+ /// CHECK-DAG: BoundsCheck [<<Idx>>,<<Val>>] loop:<<Loop>>
//
/// CHECK-START: void Main.lengthAlias4(int[]) BCE (after)
/// CHECK-NOT: BoundsCheck
diff --git a/test/457-regs/run b/test/457-regs/run
deleted file mode 100644
index 2591855..0000000
--- a/test/457-regs/run
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2020 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.
-
-${RUN} "$@"
-return_status1=$?
-
-# Force baseline JIT compilation as the test explicitly requests JIT
-# compilation, which by default is 'optimizing'.
-${RUN} "$@" -Xcompiler-option --baseline
-return_status2=$?
-
-# Make sure we don't silently ignore an early failure.
-(exit $return_status1) && (exit $return_status2)
diff --git a/test/457-regs/run.py b/test/457-regs/run.py
new file mode 100644
index 0000000..7c18068
--- /dev/null
+++ b/test/457-regs/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # Force baseline JIT compilation as the test explicitly requests JIT
+ # compilation, which by default is 'optimizing'.
+ ctx.default_run(args, Xcompiler_option=["--baseline"])
diff --git a/test/458-checker-instruct-simplification/smali/SmaliTests.smali b/test/458-checker-instruct-simplification/smali/SmaliTests.smali
index d987398..f0436d2 100644
--- a/test/458-checker-instruct-simplification/smali/SmaliTests.smali
+++ b/test/458-checker-instruct-simplification/smali/SmaliTests.smali
@@ -415,7 +415,7 @@
## CHECK-DAG: <<NotArg:z\d+>> BooleanNot [<<Arg>>]
## CHECK-DAG: Return [<<Arg>>]
-## CHECK-START: boolean SmaliTests.$noinline$NotNotBool(boolean) dead_code_elimination$final (after)
+## CHECK-START: boolean SmaliTests.$noinline$NotNotBool(boolean) dead_code_elimination$after_bce (after)
## CHECK-DAG: <<Arg:z\d+>> ParameterValue
## CHECK-DAG: Return [<<Arg>>]
diff --git a/test/458-checker-instruct-simplification/src/Main.java b/test/458-checker-instruct-simplification/src/Main.java
index 22b6dfe..8ab059d 100644
--- a/test/458-checker-instruct-simplification/src/Main.java
+++ b/test/458-checker-instruct-simplification/src/Main.java
@@ -2751,6 +2751,49 @@
return (byte) ((value & mask) >> 8);
}
+ /// CHECK-START: int Main.$noinline$deadAddAfterUnrollingAndSimplification(int[]) dead_code_elimination$before_codegen (before)
+ /// CHECK-DAG: <<Param:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2 loop:none
+ /// CHECK-DAG: <<IndexPhi:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ // Induction variable:
+ /// CHECK-DAG: Add [<<IndexPhi>>,<<Const2>>] loop:<<Loop>> outer_loop:none
+ // Array Element Addition:
+ /// CHECK-DAG: <<Store1:i\d+>> Add [{{i\d+}},<<Const1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Store2:i\d+>> Add [{{i\d+}},<<Const2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Param>>,<<Const0>>,<<Store2>>] loop:<<Loop>> outer_loop:none
+
+ /// CHECK-START: int Main.$noinline$deadAddAfterUnrollingAndSimplification(int[]) dead_code_elimination$before_codegen (before)
+ /// CHECK: Add
+ /// CHECK: Add
+ /// CHECK: Add
+ /// CHECK: Add
+ /// CHECK-NOT: Add
+
+ /// CHECK-START: int Main.$noinline$deadAddAfterUnrollingAndSimplification(int[]) dead_code_elimination$before_codegen (after)
+ /// CHECK-DAG: <<Param:l\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Const2:i\d+>> IntConstant 2 loop:none
+ /// CHECK-DAG: <<IndexPhi:i\d+>> Phi [<<Const0>>,{{i\d+}}] loop:<<Loop:B\d+>> outer_loop:none
+ // Induction variable:
+ /// CHECK-DAG: Add [<<IndexPhi>>,<<Const2>>] loop:<<Loop>> outer_loop:none
+ // Array Element Addition:
+ /// CHECK-DAG: <<Store:i\d+>> Add [{{i\d+}},<<Const2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [<<Param>>,<<Const0>>,<<Store>>] loop:<<Loop>> outer_loop:none
+
+ /// CHECK-START: int Main.$noinline$deadAddAfterUnrollingAndSimplification(int[]) dead_code_elimination$before_codegen (after)
+ /// CHECK: Add
+ /// CHECK: Add
+ /// CHECK-NOT: Add
+ public static int $noinline$deadAddAfterUnrollingAndSimplification(int[] array) {
+ for (int i = 0; i < 50; ++i) {
+ // Array access prevents transformation to closed-form expression
+ array[0]++;
+ }
+ return array[0];
+ }
+
public static void main(String[] args) throws Exception {
Class smaliTests2 = Class.forName("SmaliTests2");
Method $noinline$XorAllOnes = smaliTests2.getMethod("$noinline$XorAllOnes", int.class);
@@ -3058,6 +3101,8 @@
assertIntEquals(-1, $noinline$redundantAndIntToByteShortAndConstant(0x7fffff45));
assertIntEquals(-1, $noinline$redundantAndIntToByteShortAndConstant(0xffffff45));
assertIntEquals(111, $noinline$redundantAndRegressionNotConstant(-1, 0x6f45));
+
+ assertIntEquals(50, $noinline$deadAddAfterUnrollingAndSimplification(new int[] { 0 }));
}
private static boolean $inline$true() { return true; }
diff --git a/test/463-checker-boolean-simplifier/smali/Main2.smali b/test/463-checker-boolean-simplifier/smali/Main2.smali
index 5fc553e..e8ebb23 100644
--- a/test/463-checker-boolean-simplifier/smali/Main2.smali
+++ b/test/463-checker-boolean-simplifier/smali/Main2.smali
@@ -211,6 +211,9 @@
goto :goto_4
.end method
+# This test currently checks that we don't perform select generation due to
+# having multiple phis.
+
## CHECK-START: int Main2.MultiplePhis() select_generator (before)
## CHECK-DAG: <<Const0:i\d+>> IntConstant 0
## CHECK-DAG: <<Const1:i\d+>> IntConstant 1
@@ -228,11 +231,11 @@
## CHECK-DAG: <<Const1:i\d+>> IntConstant 1
## CHECK-DAG: <<Const13:i\d+>> IntConstant 13
## CHECK-DAG: <<Const42:i\d+>> IntConstant 42
-## CHECK-DAG: <<PhiX:i\d+>> Phi [<<Const0>>,<<Select:i\d+>>]
-## CHECK-DAG: <<PhiY:i\d+>> Phi [<<Const1>>,<<Add:i\d+>>]
+## CHECK-DAG: <<PhiX:i\d+>> Phi [<<Const0>>,<<Const13>>,<<Const42>>]
+## CHECK-DAG: <<PhiY:i\d+>> Phi [<<Const1>>,<<Add:i\d+>>,<<Add>>]
## CHECK-DAG: <<Add>> Add [<<PhiY>>,<<Const1>>]
## CHECK-DAG: <<Cond:z\d+>> LessThanOrEqual [<<Add>>,<<Const1>>]
-## CHECK-DAG: <<Select>> Select [<<Const13>>,<<Const42>>,<<Cond>>]
+## CHECK-DAG: If [<<Cond>>]
## CHECK-DAG: Return [<<PhiX>>]
# The original java source of this method:
diff --git a/test/465-checker-clinit-gvn/src/Main.java b/test/465-checker-clinit-gvn/src/Main.java
index 9c77acc..213437c 100644
--- a/test/465-checker-clinit-gvn/src/Main.java
+++ b/test/465-checker-clinit-gvn/src/Main.java
@@ -54,11 +54,11 @@
public static int accessTwoStaticsCallInBetween() {
int b = OtherClass.b;
- foo();
+ $noinline$foo();
return b - OtherClass.a;
}
- public static void foo() {
+ public static void $noinline$foo() {
try {
Thread.sleep(0);
} catch (Exception e) {
diff --git a/test/485-checker-dce-loop-update/smali/TestCase.smali b/test/485-checker-dce-loop-update/smali/TestCase.smali
index 5290bad..3e7bca9 100644
--- a/test/485-checker-dce-loop-update/smali/TestCase.smali
+++ b/test/485-checker-dce-loop-update/smali/TestCase.smali
@@ -224,7 +224,7 @@
## CHECK-DAG: If [<<ArgY>>] loop:<<HeaderY>>
#
# ### Inner loop ###
-## CHECK-DAG: <<PhiZ2:i\d+>> Phi [<<PhiZ1>>,<<XorZ>>] loop:<<HeaderZ:B\d+>>
+## CHECK-DAG: <<PhiZ2:i\d+>> Phi [<<PhiZ1>>,<<Cst0>>] loop:<<HeaderZ:B\d+>>
## CHECK-DAG: <<XorZ>> Xor [<<PhiZ2>>,<<Cst1>>] loop:<<HeaderZ>>
## CHECK-DAG: <<CondZ:z\d+>> Equal [<<XorZ>>,<<Cst0>>] loop:<<HeaderZ>>
## CHECK-DAG: If [<<CondZ>>] loop:<<HeaderZ>>
@@ -246,8 +246,8 @@
## CHECK-DAG: <<Add7>> Add [<<PhiX>>,<<Cst7>>] loop:<<HeaderY>>
#
# ### Inner loop ###
-## CHECK-DAG: <<PhiZ:i\d+>> Phi [<<ArgZ>>,<<XorZ:i\d+>>] loop:<<HeaderZ:B\d+>>
-## CHECK-DAG: <<XorZ>> Xor [<<PhiZ>>,<<Cst1>>] loop:<<HeaderZ>>
+## CHECK-DAG: <<PhiZ:i\d+>> Phi [<<ArgZ>>,<<Cst0>>] loop:<<HeaderZ:B\d+>>
+## CHECK-DAG: <<XorZ:i\d+>> Xor [<<PhiZ>>,<<Cst1>>] loop:<<HeaderZ>>
## CHECK-DAG: <<CondZ:z\d+>> Equal [<<XorZ>>,<<Cst0>>] loop:<<HeaderZ>>
## CHECK-DAG: If [<<CondZ>>] loop:<<HeaderZ>>
#
diff --git a/test/495-checker-checkcast-tests/src/Main.java b/test/495-checker-checkcast-tests/src/Main.java
index 6011c7c..2c0126a 100644
--- a/test/495-checker-checkcast-tests/src/Main.java
+++ b/test/495-checker-checkcast-tests/src/Main.java
@@ -15,207 +15,207 @@
*/
public class Main {
- public static boolean $inline$classTypeTest(Object o) {
- return ((SubMain)o) == o;
- }
+ public static boolean $inline$classTypeTest(Object o) {
+ return ((SubMain) o) == o;
+ }
- public static boolean $inline$interfaceTypeTest(Object o) {
- return ((Itf)o) == o;
- }
+ public static boolean $inline$interfaceTypeTest(Object o) {
+ return ((Itf) o) == o;
+ }
- public static SubMain subMain;
- public static Main mainField;
- public static Unrelated unrelatedField;
- public static FinalUnrelated finalUnrelatedField;
+ public static SubMain subMain;
+ public static Main mainField;
+ public static Unrelated unrelatedField;
+ public static FinalUnrelated finalUnrelatedField;
- /// CHECK-START: boolean Main.classTypeTestNull() register (after)
- /// CHECK-NOT: CheckCast
- public static boolean classTypeTestNull() {
- return $inline$classTypeTest(null);
- }
+ /// CHECK-START: boolean Main.classTypeTestNull() register (after)
+ /// CHECK-NOT: CheckCast
+ public static boolean classTypeTestNull() {
+ return $inline$classTypeTest(null);
+ }
- /// CHECK-START: boolean Main.classTypeTestExactMain() register (after)
- /// CHECK: CheckCast
- public static boolean classTypeTestExactMain() {
- return $inline$classTypeTest(new Main());
- }
+ /// CHECK-START: boolean Main.classTypeTestExactMain() register (after)
+ /// CHECK: CheckCast
+ public static boolean classTypeTestExactMain() {
+ return $inline$classTypeTest(new Main());
+ }
- /// CHECK-START: boolean Main.classTypeTestExactSubMain() register (after)
- /// CHECK-NOT: CheckCast
- public static boolean classTypeTestExactSubMain() {
- return $inline$classTypeTest(new SubMain());
- }
+ /// CHECK-START: boolean Main.classTypeTestExactSubMain() register (after)
+ /// CHECK-NOT: CheckCast
+ public static boolean classTypeTestExactSubMain() {
+ return $inline$classTypeTest(new SubMain());
+ }
- /// CHECK-START: boolean Main.classTypeTestSubMainOrNull() register (after)
- /// CHECK-NOT: CheckCast
- public static boolean classTypeTestSubMainOrNull() {
- return $inline$classTypeTest(subMain);
- }
+ /// CHECK-START: boolean Main.classTypeTestSubMainOrNull() register (after)
+ /// CHECK-NOT: CheckCast
+ public static boolean classTypeTestSubMainOrNull() {
+ return $inline$classTypeTest(subMain);
+ }
- /// CHECK-START: boolean Main.classTypeTestMainOrNull() register (after)
- /// CHECK: CheckCast
- public static boolean classTypeTestMainOrNull() {
- return $inline$classTypeTest(mainField);
- }
+ /// CHECK-START: boolean Main.classTypeTestMainOrNull() register (after)
+ /// CHECK: CheckCast
+ public static boolean classTypeTestMainOrNull() {
+ return $inline$classTypeTest(mainField);
+ }
- /// CHECK-START: boolean Main.classTypeTestUnrelated() register (after)
- /// CHECK: CheckCast
- public static boolean classTypeTestUnrelated() {
- return $inline$classTypeTest(unrelatedField);
- }
+ /// CHECK-START: boolean Main.classTypeTestUnrelated() register (after)
+ /// CHECK: CheckCast
+ public static boolean classTypeTestUnrelated() {
+ return $inline$classTypeTest(unrelatedField);
+ }
- /// CHECK-START: boolean Main.classTypeTestFinalUnrelated() register (after)
- /// CHECK: CheckCast
- public static boolean classTypeTestFinalUnrelated() {
- return $inline$classTypeTest(finalUnrelatedField);
- }
+ /// CHECK-START: boolean Main.classTypeTestFinalUnrelated() register (after)
+ /// CHECK: CheckCast
+ public static boolean classTypeTestFinalUnrelated() {
+ return $inline$classTypeTest(finalUnrelatedField);
+ }
- /// CHECK-START: boolean Main.interfaceTypeTestNull() register (after)
- /// CHECK-NOT: CheckCast
- public static boolean interfaceTypeTestNull() {
- return $inline$interfaceTypeTest(null);
- }
+ /// CHECK-START: boolean Main.interfaceTypeTestNull() register (after)
+ /// CHECK-NOT: CheckCast
+ public static boolean interfaceTypeTestNull() {
+ return $inline$interfaceTypeTest(null);
+ }
- /// CHECK-START: boolean Main.interfaceTypeTestExactMain() register (after)
- /// CHECK: CheckCast
- public static boolean interfaceTypeTestExactMain() {
- return $inline$interfaceTypeTest(new Main());
- }
+ /// CHECK-START: boolean Main.interfaceTypeTestExactMain() register (after)
+ /// CHECK: CheckCast
+ public static boolean interfaceTypeTestExactMain() {
+ return $inline$interfaceTypeTest(new Main());
+ }
- /// CHECK-START: boolean Main.interfaceTypeTestExactSubMain() register (after)
- /// CHECK-NOT: CheckCast
- public static boolean interfaceTypeTestExactSubMain() {
- return $inline$interfaceTypeTest(new SubMain());
- }
+ /// CHECK-START: boolean Main.interfaceTypeTestExactSubMain() register (after)
+ /// CHECK-NOT: CheckCast
+ public static boolean interfaceTypeTestExactSubMain() {
+ return $inline$interfaceTypeTest(new SubMain());
+ }
- /// CHECK-START: boolean Main.interfaceTypeTestSubMainOrNull() register (after)
- /// CHECK-NOT: CheckCast
- public static boolean interfaceTypeTestSubMainOrNull() {
- return $inline$interfaceTypeTest(subMain);
- }
+ /// CHECK-START: boolean Main.interfaceTypeTestSubMainOrNull() register (after)
+ /// CHECK-NOT: CheckCast
+ public static boolean interfaceTypeTestSubMainOrNull() {
+ return $inline$interfaceTypeTest(subMain);
+ }
- /// CHECK-START: boolean Main.interfaceTypeTestMainOrNull() register (after)
- /// CHECK: CheckCast
- public static boolean interfaceTypeTestMainOrNull() {
- return $inline$interfaceTypeTest(mainField);
- }
+ /// CHECK-START: boolean Main.interfaceTypeTestMainOrNull() register (after)
+ /// CHECK: CheckCast
+ public static boolean interfaceTypeTestMainOrNull() {
+ return $inline$interfaceTypeTest(mainField);
+ }
- /// CHECK-START: boolean Main.interfaceTypeTestUnrelated() register (after)
- /// CHECK: CheckCast
- public static boolean interfaceTypeTestUnrelated() {
- return $inline$interfaceTypeTest(unrelatedField);
- }
+ /// CHECK-START: boolean Main.interfaceTypeTestUnrelated() register (after)
+ /// CHECK: CheckCast
+ public static boolean interfaceTypeTestUnrelated() {
+ return $inline$interfaceTypeTest(unrelatedField);
+ }
- /// CHECK-START: boolean Main.interfaceTypeTestFinalUnrelated() register (after)
- /// CHECK: CheckCast
- public static boolean interfaceTypeTestFinalUnrelated() {
- return $inline$interfaceTypeTest(finalUnrelatedField);
- }
+ /// CHECK-START: boolean Main.interfaceTypeTestFinalUnrelated() register (after)
+ /// CHECK: CheckCast
+ public static boolean interfaceTypeTestFinalUnrelated() {
+ return $inline$interfaceTypeTest(finalUnrelatedField);
+ }
- /// CHECK-START: java.lang.String Main.knownTestWithLoadedClass() register (after)
- /// CHECK-NOT: CheckCast
- public static String knownTestWithLoadedClass() {
- return (String)$inline$getString();
- }
+ /// CHECK-START: java.lang.String Main.knownTestWithLoadedClass() register (after)
+ /// CHECK-NOT: CheckCast
+ public static String knownTestWithLoadedClass() {
+ return (String)$inline$getString();
+ }
- /// CHECK-START: Itf Main.knownTestWithUnloadedClass() register (after)
- /// CHECK: CheckCast
- public static Itf knownTestWithUnloadedClass() {
- return (Itf)$inline$getString();
- }
+ /// CHECK-START: Itf Main.knownTestWithUnloadedClass() register (after)
+ /// CHECK: CheckCast
+ public static Itf knownTestWithUnloadedClass() {
+ return (Itf)$inline$getString();
+ }
- public static Object $inline$getString() {
- return new String();
- }
+ public static Object $inline$getString() {
+ return new String();
+ }
- public static Object $inline$getMain() {
- return new Main();
- }
+ public static Object $inline$getMain() {
+ return new Main();
+ }
- /// CHECK-START: void Main.nonNullBoundType() register (after)
- /// CHECK-NOT: NullCheck
- public static void nonNullBoundType() {
- Main main = (Main)$inline$getMain();
- main.getClass();
- }
+ /// CHECK-START: void Main.nonNullBoundType() register (after)
+ /// CHECK-NOT: NullCheck
+ public static void nonNullBoundType() {
+ Main main = (Main)$inline$getMain();
+ main.getClass();
+ }
- public static void main(String[] args) {
- classTypeTestNull();
- try {
- classTypeTestExactMain();
- throw new Error("ClassCastException expected");
- } catch (ClassCastException e) {}
- classTypeTestExactSubMain();
+ public static void main(String[] args) {
+ classTypeTestNull();
+ try {
+ classTypeTestExactMain();
+ throw new Error("ClassCastException expected");
+ } catch (ClassCastException e) {}
+ classTypeTestExactSubMain();
- subMain = null;
- classTypeTestSubMainOrNull();
- subMain = new SubMain();
- classTypeTestSubMainOrNull();
+ subMain = null;
+ classTypeTestSubMainOrNull();
+ subMain = new SubMain();
+ classTypeTestSubMainOrNull();
- mainField = null;
- classTypeTestMainOrNull();
- mainField = new Main();
- try {
- classTypeTestMainOrNull();
- throw new Error("ClassCastException expected");
- } catch (ClassCastException e) {}
- mainField = new SubMain();
- classTypeTestMainOrNull();
+ mainField = null;
+ classTypeTestMainOrNull();
+ mainField = new Main();
+ try {
+ classTypeTestMainOrNull();
+ throw new Error("ClassCastException expected");
+ } catch (ClassCastException e) {}
+ mainField = new SubMain();
+ classTypeTestMainOrNull();
- unrelatedField = null;
- classTypeTestUnrelated();
- unrelatedField = new Unrelated();
- try {
- classTypeTestUnrelated();
- throw new Error("ClassCastException expected");
- } catch (ClassCastException e) {}
+ unrelatedField = null;
+ classTypeTestUnrelated();
+ unrelatedField = new Unrelated();
+ try {
+ classTypeTestUnrelated();
+ throw new Error("ClassCastException expected");
+ } catch (ClassCastException e) {}
- finalUnrelatedField = null;
- classTypeTestFinalUnrelated();
- finalUnrelatedField = new FinalUnrelated();
- try {
- classTypeTestFinalUnrelated();
- throw new Error("ClassCastException expected");
- } catch (ClassCastException e) {}
+ finalUnrelatedField = null;
+ classTypeTestFinalUnrelated();
+ finalUnrelatedField = new FinalUnrelated();
+ try {
+ classTypeTestFinalUnrelated();
+ throw new Error("ClassCastException expected");
+ } catch (ClassCastException e) {}
- interfaceTypeTestNull();
- try {
- interfaceTypeTestExactMain();
- throw new Error("ClassCastException expected");
- } catch (ClassCastException e) {}
- interfaceTypeTestExactSubMain();
+ interfaceTypeTestNull();
+ try {
+ interfaceTypeTestExactMain();
+ throw new Error("ClassCastException expected");
+ } catch (ClassCastException e) {}
+ interfaceTypeTestExactSubMain();
- subMain = null;
- interfaceTypeTestSubMainOrNull();
- subMain = new SubMain();
- interfaceTypeTestSubMainOrNull();
+ subMain = null;
+ interfaceTypeTestSubMainOrNull();
+ subMain = new SubMain();
+ interfaceTypeTestSubMainOrNull();
- mainField = null;
- interfaceTypeTestMainOrNull();
- mainField = new Main();
- try {
- interfaceTypeTestMainOrNull();
- throw new Error("ClassCastException expected");
- } catch (ClassCastException e) {}
- mainField = new SubMain();
- interfaceTypeTestMainOrNull();
+ mainField = null;
+ interfaceTypeTestMainOrNull();
+ mainField = new Main();
+ try {
+ interfaceTypeTestMainOrNull();
+ throw new Error("ClassCastException expected");
+ } catch (ClassCastException e) {}
+ mainField = new SubMain();
+ interfaceTypeTestMainOrNull();
- unrelatedField = null;
- interfaceTypeTestUnrelated();
- unrelatedField = new Unrelated();
- try {
- interfaceTypeTestUnrelated();
- throw new Error("ClassCastException expected");
- } catch (ClassCastException e) {}
+ unrelatedField = null;
+ interfaceTypeTestUnrelated();
+ unrelatedField = new Unrelated();
+ try {
+ interfaceTypeTestUnrelated();
+ throw new Error("ClassCastException expected");
+ } catch (ClassCastException e) {}
- finalUnrelatedField = null;
- interfaceTypeTestFinalUnrelated();
- finalUnrelatedField = new FinalUnrelated();
- try {
- interfaceTypeTestFinalUnrelated();
- throw new Error("ClassCastException expected");
- } catch (ClassCastException e) {}
- }
+ finalUnrelatedField = null;
+ interfaceTypeTestFinalUnrelated();
+ finalUnrelatedField = new FinalUnrelated();
+ try {
+ interfaceTypeTestFinalUnrelated();
+ throw new Error("ClassCastException expected");
+ } catch (ClassCastException e) {}
+ }
}
interface Itf {
diff --git a/test/496-checker-inlining-class-loader/src/FirstSeenByMyClassLoader.java b/test/496-checker-inlining-class-loader/src/FirstSeenByMyClassLoader.java
index e97b4e3..fe0582e 100644
--- a/test/496-checker-inlining-class-loader/src/FirstSeenByMyClassLoader.java
+++ b/test/496-checker-inlining-class-loader/src/FirstSeenByMyClassLoader.java
@@ -15,12 +15,12 @@
*/
public class FirstSeenByMyClassLoader {
- public static void $inline$bar() {
- }
+ public static void $inline$bar() {
+ }
- public static void $noinline$bar() {
- try {
- System.out.println("In $noinline$bar");
- } catch (Throwable t) { /* Ignore */ }
- }
+ public static void $noinline$bar() {
+ try {
+ System.out.println("In $noinline$bar");
+ } catch (Throwable t) { /* Ignore */ }
+ }
}
diff --git a/test/496-checker-inlining-class-loader/src/Main.java b/test/496-checker-inlining-class-loader/src/Main.java
index 4fe4723..feb8fdc 100644
--- a/test/496-checker-inlining-class-loader/src/Main.java
+++ b/test/496-checker-inlining-class-loader/src/Main.java
@@ -20,115 +20,116 @@
import java.util.List;
class MyClassLoader extends ClassLoader {
- MyClassLoader() throws Exception {
- super(MyClassLoader.class.getClassLoader());
+ MyClassLoader() throws Exception {
+ super(MyClassLoader.class.getClassLoader());
- // Some magic to get access to the pathList field of BaseDexClassLoader.
- ClassLoader loader = getClass().getClassLoader();
- Class<?> baseDexClassLoader = loader.getClass().getSuperclass();
- Field f = baseDexClassLoader.getDeclaredField("pathList");
- f.setAccessible(true);
- Object pathList = f.get(loader);
+ // Some magic to get access to the pathList field of BaseDexClassLoader.
+ ClassLoader loader = getClass().getClassLoader();
+ Class<?> baseDexClassLoader = loader.getClass().getSuperclass();
+ Field f = baseDexClassLoader.getDeclaredField("pathList");
+ f.setAccessible(true);
+ Object pathList = f.get(loader);
- // Some magic to get access to the dexField field of pathList.
- // Need to make a copy of the dex elements since we don't want an app image with pre-resolved
- // things.
- f = pathList.getClass().getDeclaredField("dexElements");
- f.setAccessible(true);
- Object[] dexElements = (Object[]) f.get(pathList);
- f = dexElements[0].getClass().getDeclaredField("dexFile");
- f.setAccessible(true);
- for (Object element : dexElements) {
- Object dexFile = f.get(element);
- // Make copy.
- Field fileNameField = dexFile.getClass().getDeclaredField("mFileName");
- fileNameField.setAccessible(true);
- dexFiles.add(dexFile.getClass().getDeclaredConstructor(String.class).newInstance(
- fileNameField.get(dexFile)));
- }
- }
-
- ArrayList<Object> dexFiles = new ArrayList<Object>();
- Field dexFileField;
-
- protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
- // Other classes may also get loaded, ignore those.
- if (className.equals("LoadedByMyClassLoader") || className.equals("FirstSeenByMyClassLoader")) {
- System.out.println("Request for " + className);
- }
-
- // We're only going to handle LoadedByMyClassLoader.
- if (className != "LoadedByMyClassLoader") {
- return getParent().loadClass(className);
- }
-
- // Mimic what DexPathList.findClass is doing.
- try {
- for (Object dexFile : dexFiles) {
- Method method = dexFile.getClass().getDeclaredMethod(
- "loadClassBinaryName", String.class, ClassLoader.class, List.class);
-
- if (dexFile != null) {
- Class<?> clazz = (Class<?>)method.invoke(dexFile, className, this, null);
- if (clazz != null) {
- return clazz;
- }
+ // Some magic to get access to the dexField field of pathList.
+ // Need to make a copy of the dex elements since we don't want an app image
+ // with pre-resolved things.
+ f = pathList.getClass().getDeclaredField("dexElements");
+ f.setAccessible(true);
+ Object[] dexElements = (Object[]) f.get(pathList);
+ f = dexElements[0].getClass().getDeclaredField("dexFile");
+ f.setAccessible(true);
+ for (Object element : dexElements) {
+ Object dexFile = f.get(element);
+ // Make copy.
+ Field fileNameField = dexFile.getClass().getDeclaredField("mFileName");
+ fileNameField.setAccessible(true);
+ dexFiles.add(dexFile.getClass().getDeclaredConstructor(String.class).newInstance(
+ fileNameField.get(dexFile)));
}
- }
- } catch (Exception e) { /* Ignore */ }
- return null;
- }
+ }
+
+ ArrayList<Object> dexFiles = new ArrayList<Object>();
+ Field dexFileField;
+
+ protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
+ // Other classes may also get loaded, ignore those.
+ if (className.equals("LoadedByMyClassLoader")
+ || className.equals("FirstSeenByMyClassLoader")) {
+ System.out.println("Request for " + className);
+ }
+
+ // We're only going to handle LoadedByMyClassLoader.
+ if (className != "LoadedByMyClassLoader") {
+ return getParent().loadClass(className);
+ }
+
+ // Mimic what DexPathList.findClass is doing.
+ try {
+ for (Object dexFile : dexFiles) {
+ Method method = dexFile.getClass().getDeclaredMethod(
+ "loadClassBinaryName", String.class, ClassLoader.class, List.class);
+
+ if (dexFile != null) {
+ Class<?> clazz = (Class<?>) method.invoke(dexFile, className, this, null);
+ if (clazz != null) {
+ return clazz;
+ }
+ }
+ }
+ } catch (Exception e) { /* Ignore */ }
+ return null;
+ }
}
class LoadedByMyClassLoader {
- /// CHECK-START: void LoadedByMyClassLoader.bar() inliner (before)
- /// CHECK: LoadClass class_name:FirstSeenByMyClassLoader
- /// CHECK-NEXT: ClinitCheck
- /// CHECK-NEXT: InvokeStaticOrDirect
- /// CHECK-NEXT: LoadClass class_name:java.lang.System
- /// CHECK-NEXT: ClinitCheck
- /// CHECK-NEXT: StaticFieldGet
- /// CHECK-NEXT: LoadString
- /// CHECK-NEXT: NullCheck
- /// CHECK-NEXT: InvokeVirtual
+ /// CHECK-START: void LoadedByMyClassLoader.bar() inliner (before)
+ /// CHECK: LoadClass class_name:FirstSeenByMyClassLoader
+ /// CHECK-NEXT: ClinitCheck
+ /// CHECK-NEXT: InvokeStaticOrDirect
+ /// CHECK-NEXT: LoadClass class_name:java.lang.System
+ /// CHECK-NEXT: ClinitCheck
+ /// CHECK-NEXT: StaticFieldGet
+ /// CHECK-NEXT: LoadString
+ /// CHECK-NEXT: NullCheck
+ /// CHECK-NEXT: InvokeVirtual
- /// CHECK-START: void LoadedByMyClassLoader.bar() inliner (after)
- /// CHECK: LoadClass class_name:FirstSeenByMyClassLoader
- /// CHECK-NEXT: ClinitCheck
- /* We inlined FirstSeenByMyClassLoader.$inline$bar */
- /// CHECK-NEXT: LoadClass class_name:java.lang.System
- /// CHECK-NEXT: ClinitCheck
- /// CHECK-NEXT: StaticFieldGet
- /// CHECK-NEXT: LoadString
- /// CHECK-NEXT: NullCheck
- /// CHECK-NEXT: InvokeVirtual
+ /// CHECK-START: void LoadedByMyClassLoader.bar() inliner (after)
+ /// CHECK: LoadClass class_name:FirstSeenByMyClassLoader
+ /// CHECK-NEXT: ClinitCheck
+ /* We inlined FirstSeenByMyClassLoader.$inline$bar */
+ /// CHECK-NEXT: LoadClass class_name:java.lang.System
+ /// CHECK-NEXT: ClinitCheck
+ /// CHECK-NEXT: StaticFieldGet
+ /// CHECK-NEXT: LoadString
+ /// CHECK-NEXT: NullCheck
+ /// CHECK-NEXT: InvokeVirtual
- /// CHECK-START: void LoadedByMyClassLoader.bar() register (before)
- /* Load and initialize FirstSeenByMyClassLoader */
- /// CHECK: LoadClass class_name:FirstSeenByMyClassLoader gen_clinit_check:true
- /* Load and initialize System */
- // There may be HX86ComputeBaseMethodAddress here.
- /// CHECK: LoadClass class_name:java.lang.System
- // The ClinitCheck may (PIC) or may not (non-PIC) be merged into the LoadClass.
- // (The merging checks for environment match but HLoadClass/kBootImageAddress
- // used for non-PIC mode does not have an environment at all.)
- /// CHECK: StaticFieldGet
- // There may be HX86ComputeBaseMethodAddress here.
- /// CHECK: LoadString
- /// CHECK-NEXT: NullCheck
- /// CHECK-NEXT: InvokeVirtual
- public static void bar() {
- FirstSeenByMyClassLoader.$inline$bar();
- System.out.println("In between the two calls.");
- FirstSeenByMyClassLoader.$noinline$bar();
- }
+ /// CHECK-START: void LoadedByMyClassLoader.bar() register (before)
+ /* Load and initialize FirstSeenByMyClassLoader */
+ /// CHECK: LoadClass class_name:FirstSeenByMyClassLoader gen_clinit_check:true
+ /* Load and initialize System */
+ // There may be HX86ComputeBaseMethodAddress here.
+ /// CHECK: LoadClass class_name:java.lang.System
+ // The ClinitCheck may (PIC) or may not (non-PIC) be merged into the LoadClass.
+ // (The merging checks for environment match but HLoadClass/kBootImageAddress
+ // used for non-PIC mode does not have an environment at all.)
+ /// CHECK: StaticFieldGet
+ // There may be HX86ComputeBaseMethodAddress here.
+ /// CHECK: LoadString
+ /// CHECK-NEXT: NullCheck
+ /// CHECK-NEXT: InvokeVirtual
+ public static void bar() {
+ FirstSeenByMyClassLoader.$inline$bar();
+ System.out.println("In between the two calls.");
+ FirstSeenByMyClassLoader.$noinline$bar();
+ }
}
public class Main {
- public static void main(String[] args) throws Exception {
- MyClassLoader o = new MyClassLoader();
- Class<?> foo = o.loadClass("LoadedByMyClassLoader");
- Method m = foo.getDeclaredMethod("bar");
- m.invoke(null);
- }
+ public static void main(String[] args) throws Exception {
+ MyClassLoader o = new MyClassLoader();
+ Class<?> foo = o.loadClass("LoadedByMyClassLoader");
+ Method m = foo.getDeclaredMethod("bar");
+ m.invoke(null);
+ }
}
diff --git a/test/497-inlining-and-class-loader/clear_dex_cache.cc b/test/497-inlining-and-class-loader/clear_dex_cache.cc
index 36ec4eb..ddbcef5 100644
--- a/test/497-inlining-and-class-loader/clear_dex_cache.cc
+++ b/test/497-inlining-and-class-loader/clear_dex_cache.cc
@@ -34,7 +34,7 @@
ScopedObjectAccess soa(Thread::Current());
ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::Class>(cls)->GetDexCache();
size_t num_methods = dex_cache->NumResolvedMethods();
- mirror::MethodDexCacheType* methods = dex_cache->GetResolvedMethods();
+ auto* methods = dex_cache->GetResolvedMethods();
CHECK_EQ(num_methods != 0u, methods != nullptr);
if (num_methods == 0u) {
return nullptr;
@@ -48,7 +48,7 @@
CHECK(array != nullptr);
ObjPtr<mirror::Array> decoded_array = soa.Decode<mirror::Array>(array);
for (size_t i = 0; i != num_methods; ++i) {
- auto pair = mirror::DexCache::GetNativePair(methods, i);
+ auto pair = methods->GetNativePair(i);
uint32_t index = pair.index;
ArtMethod* method = pair.object;
if (sizeof(void*) == 4) {
@@ -69,7 +69,7 @@
ScopedObjectAccess soa(Thread::Current());
ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::Class>(cls)->GetDexCache();
size_t num_methods = dex_cache->NumResolvedMethods();
- mirror::MethodDexCacheType* methods = dex_cache->GetResolvedMethods();
+ auto* methods = dex_cache->GetResolvedMethods();
CHECK_EQ(num_methods != 0u, methods != nullptr);
ObjPtr<mirror::Array> old = soa.Decode<mirror::Array>(old_cache);
CHECK_EQ(methods != nullptr, old != nullptr);
@@ -86,8 +86,8 @@
index = dchecked_integral_cast<uint32_t>(long_array->Get(2u * i));
method = reinterpret_cast64<ArtMethod*>(long_array->Get(2u * i + 1u));
}
- mirror::MethodDexCachePair pair(method, index);
- mirror::DexCache::SetNativePair(methods, i, pair);
+ mirror::NativeDexCachePair<ArtMethod> pair(method, index);
+ methods->SetNativePair(i, pair);
}
}
diff --git a/test/497-inlining-and-class-loader/src/Level1.java b/test/497-inlining-and-class-loader/src/Level1.java
index 977af83..18f79ce 100644
--- a/test/497-inlining-and-class-loader/src/Level1.java
+++ b/test/497-inlining-and-class-loader/src/Level1.java
@@ -15,13 +15,13 @@
*/
public class Level1 {
- public static void $inline$bar() {
- Level2.$inline$bar();
- }
+ public static void $inline$bar() {
+ Level2.$inline$bar();
+ }
}
class Level2 {
- public static void $inline$bar() {
- Main.$noinline$bar();
- }
+ public static void $inline$bar() {
+ Main.$noinline$bar();
+ }
}
diff --git a/test/497-inlining-and-class-loader/src/Main.java b/test/497-inlining-and-class-loader/src/Main.java
index 01b4bcd..66a3f6e 100644
--- a/test/497-inlining-and-class-loader/src/Main.java
+++ b/test/497-inlining-and-class-loader/src/Main.java
@@ -19,112 +19,112 @@
import java.util.List;
class MyClassLoader extends ClassLoader {
- MyClassLoader() throws Exception {
- super(MyClassLoader.class.getClassLoader());
+ MyClassLoader() throws Exception {
+ super(MyClassLoader.class.getClassLoader());
- // Some magic to get access to the pathList field of BaseDexClassLoader.
- ClassLoader loader = getClass().getClassLoader();
- Class<?> baseDexClassLoader = loader.getClass().getSuperclass();
- Field f = baseDexClassLoader.getDeclaredField("pathList");
- f.setAccessible(true);
- Object pathList = f.get(loader);
+ // Some magic to get access to the pathList field of BaseDexClassLoader.
+ ClassLoader loader = getClass().getClassLoader();
+ Class<?> baseDexClassLoader = loader.getClass().getSuperclass();
+ Field f = baseDexClassLoader.getDeclaredField("pathList");
+ f.setAccessible(true);
+ Object pathList = f.get(loader);
- // Some magic to get access to the dexField field of pathList.
- f = pathList.getClass().getDeclaredField("dexElements");
- f.setAccessible(true);
- dexElements = (Object[]) f.get(pathList);
- dexFileField = dexElements[0].getClass().getDeclaredField("dexFile");
- dexFileField.setAccessible(true);
- }
-
- Object[] dexElements;
- Field dexFileField;
-
- static ClassLoader level1ClassLoader;
-
- protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
- if (this != level1ClassLoader) {
- if (className.equals("Level1")) {
- return level1ClassLoader.loadClass(className);
- } else if (className.equals("Level2")) {
- throw new ClassNotFoundException("None of my methods require Level2!");
- } else if (!className.equals("LoadedByMyClassLoader")) {
- // We're only going to handle LoadedByMyClassLoader.
- return getParent().loadClass(className);
- }
- } else {
- if (className != "Level1" && className != "Level2") {
- return getParent().loadClass(className);
- }
+ // Some magic to get access to the dexField field of pathList.
+ f = pathList.getClass().getDeclaredField("dexElements");
+ f.setAccessible(true);
+ dexElements = (Object[]) f.get(pathList);
+ dexFileField = dexElements[0].getClass().getDeclaredField("dexFile");
+ dexFileField.setAccessible(true);
}
- // Mimic what DexPathList.findClass is doing.
- try {
- for (Object element : dexElements) {
- Object dex = dexFileField.get(element);
- Method method = dex.getClass().getDeclaredMethod(
- "loadClassBinaryName", String.class, ClassLoader.class, List.class);
+ Object[] dexElements;
+ Field dexFileField;
- if (dex != null) {
- Class<?> clazz = (Class<?>)method.invoke(dex, className, this, null);
- if (clazz != null) {
- return clazz;
- }
+ static ClassLoader level1ClassLoader;
+
+ protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
+ if (this != level1ClassLoader) {
+ if (className.equals("Level1")) {
+ return level1ClassLoader.loadClass(className);
+ } else if (className.equals("Level2")) {
+ throw new ClassNotFoundException("None of my methods require Level2!");
+ } else if (!className.equals("LoadedByMyClassLoader")) {
+ // We're only going to handle LoadedByMyClassLoader.
+ return getParent().loadClass(className);
+ }
+ } else {
+ if (className != "Level1" && className != "Level2") {
+ return getParent().loadClass(className);
+ }
}
- }
- } catch (Exception e) { /* Ignore */ }
- return null;
- }
+
+ // Mimic what DexPathList.findClass is doing.
+ try {
+ for (Object element : dexElements) {
+ Object dex = dexFileField.get(element);
+ Method method = dex.getClass().getDeclaredMethod(
+ "loadClassBinaryName", String.class, ClassLoader.class, List.class);
+
+ if (dex != null) {
+ Class<?> clazz = (Class<?>) method.invoke(dex, className, this, null);
+ if (clazz != null) {
+ return clazz;
+ }
+ }
+ }
+ } catch (Exception e) { /* Ignore */ }
+ return null;
+ }
}
class LoadedByMyClassLoader {
- public static void bar() {
- Level1.$inline$bar();
- }
+ public static void bar() {
+ Level1.$inline$bar();
+ }
}
class Main {
- public static void main(String[] args) throws Exception {
- System.loadLibrary(args[0]);
- // Clone resolved methods, to restore the original version just
- // before we walk the stack in $noinline$bar.
- savedResolvedMethods = cloneResolvedMethods(Main.class);
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ // Clone resolved methods, to restore the original version just
+ // before we walk the stack in $noinline$bar.
+ savedResolvedMethods = cloneResolvedMethods(Main.class);
- MyClassLoader o = new MyClassLoader();
- MyClassLoader.level1ClassLoader = new MyClassLoader();
- Class<?> foo = o.loadClass("LoadedByMyClassLoader");
- Method m = foo.getDeclaredMethod("bar");
- try {
- m.invoke(null);
- } catch (Error e) { /* Ignore */ }
- }
+ MyClassLoader o = new MyClassLoader();
+ MyClassLoader.level1ClassLoader = new MyClassLoader();
+ Class<?> foo = o.loadClass("LoadedByMyClassLoader");
+ Method m = foo.getDeclaredMethod("bar");
+ try {
+ m.invoke(null);
+ } catch (Error e) { /* Ignore */ }
+ }
- public static void $inline$bar() {
- }
+ public static void $inline$bar() {
+ }
- public static void $noinline$bar() {
- try {
- // Be evil and clear all dex cache entries.
- Field f = Class.class.getDeclaredField("dexCache");
- f.setAccessible(true);
- Object dexCache = f.get(Main.class);
- f = dexCache.getClass().getDeclaredField("resolvedTypes");
- f.setAccessible(true);
- Object[] array = (Object[]) f.get(dexCache);
- for (int i = 0; i < array.length; i++) {
- array[i] = null;
- }
- restoreResolvedMethods(Main.class, savedResolvedMethods);
- } catch (Throwable t) { /* Ignore */ }
+ public static void $noinline$bar() {
+ try {
+ // Be evil and clear all dex cache entries.
+ Field f = Class.class.getDeclaredField("dexCache");
+ f.setAccessible(true);
+ Object dexCache = f.get(Main.class);
+ f = dexCache.getClass().getDeclaredField("resolvedTypes");
+ f.setAccessible(true);
+ Object[] array = (Object[]) f.get(dexCache);
+ for (int i = 0; i < array.length; i++) {
+ array[i] = null;
+ }
+ restoreResolvedMethods(Main.class, savedResolvedMethods);
+ } catch (Throwable t) { /* Ignore */ }
- // This will walk the stack, trying to resolve methods in it.
- // Because we cleared dex cache entries, we will have to find
- // classes again, which require to use the correct class loader
- // in the presence of inlining.
- new Exception().printStackTrace(System.out);
- }
- static Object savedResolvedMethods;
+ // This will walk the stack, trying to resolve methods in it.
+ // Because we cleared dex cache entries, we will have to find
+ // classes again, which require to use the correct class loader
+ // in the presence of inlining.
+ new Exception().printStackTrace(System.out);
+ }
+ static Object savedResolvedMethods;
- static native Object cloneResolvedMethods(Class<?> cls);
- static native void restoreResolvedMethods(Class<?> cls, Object saved);
+ static native Object cloneResolvedMethods(Class<?> cls);
+ static native void restoreResolvedMethods(Class<?> cls, Object saved);
}
diff --git a/test/498-type-propagation/src/Main.java b/test/498-type-propagation/src/Main.java
index b20794c..ceb35eb 100644
--- a/test/498-type-propagation/src/Main.java
+++ b/test/498-type-propagation/src/Main.java
@@ -17,11 +17,11 @@
import java.lang.reflect.Method;
public class Main {
- public static void main(String[] args) throws Exception {
- Class<?> c = Class.forName("TypePropagation");
- Method m = c.getMethod("method", int[].class);
- int[] array = new int[7];
- Object[] arguments = { array };
- m.invoke(null, arguments);
- }
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("TypePropagation");
+ Method m = c.getMethod("method", int[].class);
+ int[] array = new int[7];
+ Object[] arguments = { array };
+ m.invoke(null, arguments);
+ }
}
diff --git a/test/499-bce-phi-array-length/src/Main.java b/test/499-bce-phi-array-length/src/Main.java
index e917bc1..4e99771 100644
--- a/test/499-bce-phi-array-length/src/Main.java
+++ b/test/499-bce-phi-array-length/src/Main.java
@@ -15,50 +15,50 @@
*/
public class Main {
- public static int foo(int start, int[] array) {
- int result = 0;
- // We will create HDeoptimize nodes for this first loop, and a phi
- // for the array length which will only be used within the loop.
- for (int i = start; i < 3; i++) {
- result += array[i];
- for (int j = 0; j < 2; ++j) {
- // The HBoundsCheck for this array access will be updated to access
- // the array length phi created for the deoptimization checks of the
- // first loop. This crashed the compiler which used to DCHECK an array
- // length in a bounds check cannot be a phi.
- result += array[j];
- }
- }
- return result;
- }
-
- public static int bar(int start, int[] array) {
- int result = 0;
- for (int i = start; i < 3; i++) {
- result += array[i];
- for (int j = 0; j < 2; ++j) {
- result += array[j];
- // The following operations would lead to BCE wanting to add another
- // deoptimization, but it crashed assuming the input of a `HBoundsCheck`
- // must be a `HArrayLength`.
- result += array[0];
- result += array[1];
- result += array[2];
- }
- }
- return result;
- }
-
- public static void main(String[] args) {
- int[] a = new int[] { 1, 2, 3, 4, 5 };
- int result = foo(1, a);
- if (result != 11) {
- throw new Error("Got " + result + ", expected " + 11);
+ public static int foo(int start, int[] array) {
+ int result = 0;
+ // We will create HDeoptimize nodes for this first loop, and a phi
+ // for the array length which will only be used within the loop.
+ for (int i = start; i < 3; i++) {
+ result += array[i];
+ for (int j = 0; j < 2; ++j) {
+ // The HBoundsCheck for this array access will be updated to access
+ // the array length phi created for the deoptimization checks of the
+ // first loop. This crashed the compiler which used to DCHECK an array
+ // length in a bounds check cannot be a phi.
+ result += array[j];
+ }
+ }
+ return result;
}
- result = bar(1, a);
- if (result != 35) {
- throw new Error("Got " + result + ", expected " + 35);
+ public static int bar(int start, int[] array) {
+ int result = 0;
+ for (int i = start; i < 3; i++) {
+ result += array[i];
+ for (int j = 0; j < 2; ++j) {
+ result += array[j];
+ // The following operations would lead to BCE wanting to add another
+ // deoptimization, but it crashed assuming the input of a `HBoundsCheck`
+ // must be a `HArrayLength`.
+ result += array[0];
+ result += array[1];
+ result += array[2];
+ }
+ }
+ return result;
}
- }
+
+ public static void main(String[] args) {
+ int[] a = new int[] { 1, 2, 3, 4, 5 };
+ int result = foo(1, a);
+ if (result != 11) {
+ throw new Error("Got " + result + ", expected " + 11);
+ }
+
+ result = bar(1, a);
+ if (result != 35) {
+ throw new Error("Got " + result + ", expected " + 35);
+ }
+ }
}
diff --git a/test/515-dce-dominator/src/Main.java b/test/515-dce-dominator/src/Main.java
index 8c6ce75..f36b90c 100644
--- a/test/515-dce-dominator/src/Main.java
+++ b/test/515-dce-dominator/src/Main.java
@@ -17,10 +17,10 @@
import java.lang.reflect.Method;
public class Main {
- public static void main(String[] args) throws Exception {
- Class<?> c = Class.forName("Dominator");
- Method m = c.getMethod("method", int.class);
- Object[] arguments = { 5 };
- m.invoke(null, arguments);
- }
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("Dominator");
+ Method m = c.getMethod("method", int.class);
+ Object[] arguments = { 5 };
+ m.invoke(null, arguments);
+ }
}
diff --git a/test/516-dead-move-result/src/Main.java b/test/516-dead-move-result/src/Main.java
index 8f0891a..c2f34c7 100644
--- a/test/516-dead-move-result/src/Main.java
+++ b/test/516-dead-move-result/src/Main.java
@@ -17,10 +17,10 @@
import java.lang.reflect.Method;
public class Main {
- public static void main(String[] args) throws Exception {
- Class<?> c = Class.forName("MoveResult");
- Method m = c.getMethod("method");
- Object[] arguments = { };
- m.invoke(null, arguments);
- }
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("MoveResult");
+ Method m = c.getMethod("method");
+ Object[] arguments = { };
+ m.invoke(null, arguments);
+ }
}
diff --git a/test/517-checker-builder-fallthrough/src/Main.java b/test/517-checker-builder-fallthrough/src/Main.java
index 14170f5..5bec073 100644
--- a/test/517-checker-builder-fallthrough/src/Main.java
+++ b/test/517-checker-builder-fallthrough/src/Main.java
@@ -18,15 +18,15 @@
public class Main {
- public static int runTest(int input) throws Exception {
- Class<?> c = Class.forName("TestCase");
- Method m = c.getMethod("testCase", int.class);
- return (Integer) m.invoke(null, input);
- }
-
- public static void main(String[] args) throws Exception {
- if (runTest(42) != 42) {
- throw new Error("Expected 42");
+ public static int runTest(int input) throws Exception {
+ Class<?> c = Class.forName("TestCase");
+ Method m = c.getMethod("testCase", int.class);
+ return (Integer) m.invoke(null, input);
}
- }
+
+ public static void main(String[] args) throws Exception {
+ if (runTest(42) != 42) {
+ throw new Error("Expected 42");
+ }
+ }
}
diff --git a/test/518-null-array-get/src/Main.java b/test/518-null-array-get/src/Main.java
index 7090194..12e2442 100644
--- a/test/518-null-array-get/src/Main.java
+++ b/test/518-null-array-get/src/Main.java
@@ -18,37 +18,37 @@
import java.lang.reflect.Method;
public class Main {
- public static void main(String[] args) throws Exception {
- checkLoad("NullArrayFailInt2Object", true);
- checkLoad("NullArrayFailObject2Int", true);
- checkLoad("NullArraySuccessInt", false);
- checkLoad("NullArraySuccessInt2Float", false);
- checkLoad("NullArraySuccessShort", false);
- checkLoad("NullArraySuccessRef", false);
- }
-
- private static void checkLoad(String className, boolean expectError) throws Exception {
- Class<?> c;
- try {
- c = Class.forName(className);
- if (expectError) {
- throw new RuntimeException("Expected error for " + className);
- }
- Method m = c.getMethod("method");
- try {
- m.invoke(null);
- throw new RuntimeException("Expected an InvocationTargetException");
- } catch (InvocationTargetException e) {
- if (!(e.getCause() instanceof NullPointerException)) {
- throw new RuntimeException("Expected a NullPointerException");
- }
- System.out.println(className);
- }
- } catch (VerifyError e) {
- if (!expectError) {
- throw new RuntimeException(e);
- }
- System.out.println(className);
+ public static void main(String[] args) throws Exception {
+ checkLoad("NullArrayFailInt2Object", true);
+ checkLoad("NullArrayFailObject2Int", true);
+ checkLoad("NullArraySuccessInt", false);
+ checkLoad("NullArraySuccessInt2Float", false);
+ checkLoad("NullArraySuccessShort", false);
+ checkLoad("NullArraySuccessRef", false);
}
- }
+
+ private static void checkLoad(String className, boolean expectError) throws Exception {
+ Class<?> c;
+ try {
+ c = Class.forName(className);
+ if (expectError) {
+ throw new RuntimeException("Expected error for " + className);
+ }
+ Method m = c.getMethod("method");
+ try {
+ m.invoke(null);
+ throw new RuntimeException("Expected an InvocationTargetException");
+ } catch (InvocationTargetException e) {
+ if (!(e.getCause() instanceof NullPointerException)) {
+ throw new RuntimeException("Expected a NullPointerException");
+ }
+ System.out.println(className);
+ }
+ } catch (VerifyError e) {
+ if (!expectError) {
+ throw new RuntimeException(e);
+ }
+ System.out.println(className);
+ }
+ }
}
diff --git a/test/519-bound-load-class/src/Main.java b/test/519-bound-load-class/src/Main.java
index cddeb09..c507aec 100644
--- a/test/519-bound-load-class/src/Main.java
+++ b/test/519-bound-load-class/src/Main.java
@@ -15,25 +15,25 @@
*/
public class Main {
- public static void main(String[] args) {
- testInstanceOf();
- try {
- testNull();
- throw new Error("Expected ClassClastException");
- } catch (ClassCastException e) { /* ignore */ }
- }
-
- public static void testInstanceOf() {
- Object o = Main.class;
- if (o instanceof Main) {
- System.out.println((Main)o);
+ public static void main(String[] args) {
+ testInstanceOf();
+ try {
+ testNull();
+ throw new Error("Expected ClassClastException");
+ } catch (ClassCastException e) { /* ignore */ }
}
- }
- public static void testNull() {
- Object o = Main.class;
- if (o != null) {
- System.out.println((Main)o);
+ public static void testInstanceOf() {
+ Object o = Main.class;
+ if (o instanceof Main) {
+ System.out.println((Main) o);
+ }
}
- }
+
+ public static void testNull() {
+ Object o = Main.class;
+ if (o != null) {
+ System.out.println((Main) o);
+ }
+ }
}
diff --git a/test/521-checker-array-set-null/src/Main.java b/test/521-checker-array-set-null/src/Main.java
index f166b92..12a1985 100644
--- a/test/521-checker-array-set-null/src/Main.java
+++ b/test/521-checker-array-set-null/src/Main.java
@@ -16,26 +16,64 @@
public class Main {
public static void main(String[] args) {
- testWithNull(new Object[2]);
- testWithUnknown(new Object[2], new Object());
- testWithSame(new Object[2]);
+ $noinline$testWithNull(new Object[2]);
+ $noinline$testWithUnknown(new Object[2], new Object());
+ $noinline$testWithSame(new Object[2]);
+ $noinline$testWithSameRTI();
}
- /// CHECK-START: void Main.testWithNull(java.lang.Object[]) disassembly (after)
- /// CHECK: ArraySet needs_type_check:false
- public static void testWithNull(Object[] o) {
+ // Known null can eliminate the type check in early stages.
+
+ /// CHECK-START: void Main.$noinline$testWithNull(java.lang.Object[]) instruction_simplifier (before)
+ /// CHECK: ArraySet needs_type_check:true can_trigger_gc:true
+
+ /// CHECK-START: void Main.$noinline$testWithNull(java.lang.Object[]) instruction_simplifier (after)
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false
+
+ /// CHECK-START: void Main.$noinline$testWithNull(java.lang.Object[]) disassembly (after)
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false
+ public static void $noinline$testWithNull(Object[] o) {
o[0] = null;
}
- /// CHECK-START: void Main.testWithUnknown(java.lang.Object[], java.lang.Object) disassembly (after)
- /// CHECK: ArraySet needs_type_check:true
- public static void testWithUnknown(Object[] o, Object obj) {
+ /// CHECK-START: void Main.$noinline$testWithUnknown(java.lang.Object[], java.lang.Object) disassembly (after)
+ /// CHECK: ArraySet needs_type_check:true can_trigger_gc:true
+ public static void $noinline$testWithUnknown(Object[] o, Object obj) {
o[0] = obj;
}
- /// CHECK-START: void Main.testWithSame(java.lang.Object[]) disassembly (after)
- /// CHECK: ArraySet needs_type_check:false
- public static void testWithSame(Object[] o) {
+ // After GVN we know that we are setting values from the same array so there's no need for a type
+ // check.
+
+ /// CHECK-START: void Main.$noinline$testWithSame(java.lang.Object[]) instruction_simplifier$after_gvn (before)
+ /// CHECK: ArraySet needs_type_check:true can_trigger_gc:true
+
+ /// CHECK-START: void Main.$noinline$testWithSame(java.lang.Object[]) instruction_simplifier$after_gvn (after)
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false
+
+ /// CHECK-START: void Main.$noinline$testWithSame(java.lang.Object[]) disassembly (after)
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false
+ public static void $noinline$testWithSame(Object[] o) {
o[0] = o[1];
}
+
+ // We know that the array and the static Object have the same RTI in early stages. No need for a
+ // type check.
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testWithSameRTI() instruction_simplifier (before)
+ /// CHECK: ArraySet needs_type_check:true can_trigger_gc:true
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testWithSameRTI() instruction_simplifier (after)
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false
+
+ /// CHECK-START: java.lang.Object[] Main.$noinline$testWithSameRTI() disassembly (after)
+ /// CHECK: ArraySet needs_type_check:false can_trigger_gc:false
+ public static Object[] $noinline$testWithSameRTI() {
+ Object[] arr = new Object[1];
+ arr[0] = static_obj;
+ // Return so that LSE doesn't eliminate the ArraySet.
+ return arr;
+ }
+
+ static Object static_obj;
}
diff --git a/test/521-regression-integer-field-set/src/Main.java b/test/521-regression-integer-field-set/src/Main.java
index 9924e09..90890a8 100644
--- a/test/521-regression-integer-field-set/src/Main.java
+++ b/test/521-regression-integer-field-set/src/Main.java
@@ -31,24 +31,12 @@
assertIntEquals(456789, s);
}
- private static boolean doThrow = false;
-
private void $noinline$SetInstanceField() {
- if (doThrow) {
- // Try defeating inlining.
- throw new Error();
- }
-
// Set a value than does not fit in a 16-bit (signed) integer.
i = 123456;
}
private static void $noinline$SetStaticField() {
- if (doThrow) {
- // Try defeating inlining.
- throw new Error();
- }
-
// Set a value than does not fit in a 16-bit (signed) integer.
s = 456789;
}
diff --git a/test/526-checker-caller-callee-regs/src/Main.java b/test/526-checker-caller-callee-regs/src/Main.java
index f402c2c..9bd08ff 100644
--- a/test/526-checker-caller-callee-regs/src/Main.java
+++ b/test/526-checker-caller-callee-regs/src/Main.java
@@ -22,12 +22,8 @@
}
}
- static boolean doThrow = false;
-
// This function always returns 1.
- // We use 'throw' to prevent the function from being inlined.
public static int $opt$noinline$function_call(int arg) {
- if (doThrow) throw new Error();
return 1 % arg;
}
diff --git a/test/526-long-regalloc/src/Main.java b/test/526-long-regalloc/src/Main.java
index e8b3096..489db05 100644
--- a/test/526-long-regalloc/src/Main.java
+++ b/test/526-long-regalloc/src/Main.java
@@ -54,12 +54,9 @@
}
public static long $noinline$bar() {
- if (doThrow) throw new Error();
return 42;
}
- public static boolean doThrow = false;
-
public static int myField1 = 0;
public static int myField2 = 0;
public static int myField3 = 0;
diff --git a/test/527-checker-array-access-split/src/Main.java b/test/527-checker-array-access-split/src/Main.java
index f39b5e2..cc1dc6b 100644
--- a/test/527-checker-array-access-split/src/Main.java
+++ b/test/527-checker-array-access-split/src/Main.java
@@ -593,12 +593,16 @@
/// CHECK: ArrayGet [<<IntAddr1>>,{{i\d+}}]
/// CHECK: <<IntAddr2:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>]
/// CHECK: ArrayGet [<<IntAddr2>>,{{i\d+}}]
- /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
+ /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}] needs_type_check:false can_trigger_gc:false
/// CHECK: <<IntAddr3:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>]
/// CHECK: ArrayGet [<<IntAddr3>>,{{i\d+}}]
/// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
/// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
//
+ /// CHECK-START-ARM64: int Main.checkObjectArrayGet(int, java.lang.Integer[], java.lang.Integer[]) instruction_simplifier_arm64 (after)
+ /// CHECK: IntermediateAddress
+ /// CHECK: IntermediateAddress
+ /// CHECK: IntermediateAddress
/// CHECK-NOT: IntermediateAddress
/// CHECK-START-ARM64: int Main.checkObjectArrayGet(int, java.lang.Integer[], java.lang.Integer[]) GVN$after_arch (after)
@@ -608,12 +612,13 @@
/// CHECK: <<IntAddr1:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>]
/// CHECK: ArrayGet [<<IntAddr1>>,{{i\d+}}]
/// CHECK: ArrayGet [<<IntAddr1>>,{{i\d+}}]
- /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
- /// CHECK: <<IntAddr3:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>]
- /// CHECK: ArrayGet [<<IntAddr3>>,{{i\d+}}]
+ /// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}] needs_type_check:false can_trigger_gc:false
+ /// CHECK: ArrayGet [<<IntAddr1>>,{{i\d+}}]
/// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
/// CHECK: ArraySet [<<Array>>,{{i\d+}},{{l\d+}}]
//
+ /// CHECK-START-ARM64: int Main.checkObjectArrayGet(int, java.lang.Integer[], java.lang.Integer[]) GVN$after_arch (after)
+ /// CHECK: IntermediateAddress
/// CHECK-NOT: IntermediateAddress
public final static int checkObjectArrayGet(int index, Integer[] a, Integer[] b) {
Integer five = Integer.valueOf(5);
diff --git a/test/529-long-split/src/Main.java b/test/529-long-split/src/Main.java
index dc52d88..3493f77 100644
--- a/test/529-long-split/src/Main.java
+++ b/test/529-long-split/src/Main.java
@@ -167,14 +167,11 @@
}
public static void $noinline$doCall() {
- if (doThrow) throw new Error();
}
public static void $noinline$doCall(long e) {
- if (doThrow) throw new Error();
}
- public static boolean doThrow;
public static int myField1;
public static int myField2;
public static int myField3;
diff --git a/test/530-checker-lse-simd/src/Main.java b/test/530-checker-lse-simd/src/Main.java
index d6049d0..619ac28 100644
--- a/test/530-checker-lse-simd/src/Main.java
+++ b/test/530-checker-lse-simd/src/Main.java
@@ -231,7 +231,6 @@
/// CHECK: BoundsCheck loop:none
/// CHECK: ArrayGet
/// CHECK: Add
- /// CHECK: ArrayLength
//
/// CHECK: VecLoad loop:{{B\d+}}
/// CHECK: VecStore
@@ -244,7 +243,6 @@
/// CHECK-START-ARM64: double[] Main.$noinline$test06(int) load_store_elimination (after)
/// CHECK: BoundsCheck loop:none
/// CHECK: Add
- /// CHECK: ArrayLength
//
/// CHECK: VecLoad loop:{{B\d+}}
/// CHECK: VecAdd
diff --git a/test/530-checker-lse-try-catch/src/Main.java b/test/530-checker-lse-try-catch/src/Main.java
index 56ef8bd..3f4fa25 100644
--- a/test/530-checker-lse-try-catch/src/Main.java
+++ b/test/530-checker-lse-try-catch/src/Main.java
@@ -30,7 +30,19 @@
assertEquals(1, $noinline$testTryCatchBlocking(new Point(), boolean_throw));
assertEquals(1, $noinline$testTryCatchPhi(new Point(), boolean_throw));
assertEquals(2, $noinline$testTryCatchPhiWithTwoCatches(new Point(), new int[0]));
- assertEquals(1, $noinline$testKeepStoreInsideTryCatch());
+ assertEquals(1, $noinline$testKeepStoreInsideTry());
+ assertEquals(10, $noinline$testDontKeepStoreInsideCatch(new int[]{10}));
+ assertEquals(30, $noinline$testDontKeepStoreInsideCatch(new int[]{}));
+ assertEquals(10, $noinline$testKeepStoreInsideCatchWithOuterTry(new int[]{10}));
+ assertEquals(30, $noinline$testKeepStoreInsideCatchWithOuterTry(new int[]{}));
+ assertEquals(40, $noinline$testDontKeepStoreInsideFinally(new int[]{10}));
+ try {
+ assertEquals(30, $noinline$testDontKeepStoreInsideFinally(new int[]{}));
+ throw new Error("Unreachable");
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ }
+ assertEquals(10, $noinline$testDontKeepStoreInsideOuterCatch(new int[]{10}));
+ assertEquals(100030, $noinline$testDontKeepStoreInsideOuterCatch(new int[]{}));
assertEquals(150, $noinline$test40());
}
@@ -205,17 +217,17 @@
// is observable.
// Consistency check to make sure the try/catch wasn't removed by an earlier pass.
- /// CHECK-START: int Main.$noinline$testKeepStoreInsideTryCatch() load_store_elimination (after)
+ /// CHECK-START: int Main.$noinline$testKeepStoreInsideTry() load_store_elimination (after)
/// CHECK-DAG: TryBoundary kind:entry
- /// CHECK-START: int Main.$noinline$testKeepStoreInsideTryCatch() load_store_elimination (before)
+ /// CHECK-START: int Main.$noinline$testKeepStoreInsideTry() load_store_elimination (before)
/// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
/// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
- /// CHECK-START: int Main.$noinline$testKeepStoreInsideTryCatch() load_store_elimination (after)
+ /// CHECK-START: int Main.$noinline$testKeepStoreInsideTry() load_store_elimination (after)
/// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
/// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
- private static int $noinline$testKeepStoreInsideTryCatch() {
+ private static int $noinline$testKeepStoreInsideTry() {
Main main = new Main();
main.sumForKeepStoreInsideTryCatch = 0;
try {
@@ -228,6 +240,118 @@
}
}
+ private static int $noinline$returnValue(int value) {
+ return value;
+ }
+
+ /// CHECK-START: int Main.$noinline$testDontKeepStoreInsideCatch(int[]) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+
+ /// CHECK-START: int Main.$noinline$testDontKeepStoreInsideCatch(int[]) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ private static int $noinline$testDontKeepStoreInsideCatch(int[] array) {
+ Main main = new Main();
+ int value = 0;
+ try {
+ value = array[0];
+ } catch (Exception e) {
+ // These sets can be eliminated even though we have invokes since this catch is not part of an
+ // outer try.
+ main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(10);
+ main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(20);
+ }
+ return main.sumForKeepStoreInsideTryCatch + value;
+ }
+
+ /// CHECK-START: int Main.$noinline$testKeepStoreInsideCatchWithOuterTry(int[]) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+
+ /// CHECK-START: int Main.$noinline$testKeepStoreInsideCatchWithOuterTry(int[]) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ private static int $noinline$testKeepStoreInsideCatchWithOuterTry(int[] array) {
+ Main main = new Main();
+ int value = 0;
+ try {
+ try {
+ value = array[0];
+ } catch (Exception e) {
+ // These sets can't be eliminated since this catch is part of a outer try.
+ main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(10);
+ main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(20);
+ }
+ } catch (Exception e) {
+ value = 100000;
+ }
+
+ return main.sumForKeepStoreInsideTryCatch + value;
+ }
+
+ // Note that there are four `InstanceFieldSet` instead of two since we split the `finally` block
+ // into the normal path, and the exceptional path.
+
+ /// CHECK-START: int Main.$noinline$testDontKeepStoreInsideFinally(int[]) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+
+ /// CHECK-START: int Main.$noinline$testDontKeepStoreInsideFinally(int[]) load_store_elimination (after)
+ /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ private static int $noinline$testDontKeepStoreInsideFinally(int[] array) {
+ Main main = new Main();
+ int value = 0;
+ try {
+ value = array[0];
+ } finally {
+ // These sets can be eliminated even though we have invokes since this catch is not part of an
+ // outer try.
+ main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(10);
+ main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(20);
+ }
+ return main.sumForKeepStoreInsideTryCatch + value;
+ }
+
+ // Checks that we are able to do LSE inside of catches which are outside of try blocks.
+
+ /// CHECK-START: int Main.$noinline$testDontKeepStoreInsideOuterCatch(int[]) load_store_elimination (before)
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+
+ // This store potentially can be eliminated too, but our phi creation logic doesn't realize it can
+ // create a Phi for `main.sumForKeepStoreInsideTryCatch` and skip a store+load.
+
+ /// CHECK-START: int Main.$noinline$testDontKeepStoreInsideOuterCatch(int[]) load_store_elimination (after)
+ /// CHECK: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+ /// CHECK-NOT: InstanceFieldSet field_name:Main.sumForKeepStoreInsideTryCatch
+
+ private static int $noinline$testDontKeepStoreInsideOuterCatch(int[] array) {
+ Main main = new Main();
+ int value = 0;
+ try {
+ value = array[0];
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ // These sets and gets are not considered to be part of a try so they are free to be
+ // eliminated.
+ main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(10);
+ main.sumForKeepStoreInsideTryCatch += $noinline$returnValue(20);
+ try {
+ value = array[0];
+ } catch (ArrayIndexOutOfBoundsException expectedToo) {
+ value = 100000;
+ }
+ }
+
+ return main.sumForKeepStoreInsideTryCatch + value;
+ }
+
/// CHECK-START: int Main.$noinline$test40() load_store_elimination (before)
/// CHECK: ArraySet
/// CHECK: DivZeroCheck
diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java
index 5d55d3e..8754449 100644
--- a/test/530-checker-lse/src/Main.java
+++ b/test/530-checker-lse/src/Main.java
@@ -217,12 +217,9 @@
/// CHECK-DAG: Return
/// CHECK-START: int Main.test4(TestClass, boolean) load_store_elimination (after)
- /// CHECK: NullCheck
- /// CHECK: NullCheck
- /// CHECK-NOT: NullCheck
+ /// CHECK-NOT: InstanceFieldGet
/// CHECK-START: int Main.test4(TestClass, boolean) load_store_elimination (after)
- /// CHECK-NOT: InstanceFieldGet
/// CHECK-NOT: Phi
// Set and merge the same value in two branches.
@@ -554,16 +551,15 @@
}
/// CHECK-START: void Main.test21(TestClass) load_store_elimination (before)
- /// CHECK: NewInstance
- /// CHECK: InstanceFieldSet
- /// CHECK: InstanceFieldSet
- /// CHECK: InstanceFieldSet
- /// CHECK: InstanceFieldGet
- /// CHECK: InstanceFieldGet
+ /// CHECK-DAG: NewInstance
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldSet
+ /// CHECK-DAG: InstanceFieldGet
+ /// CHECK-DAG: InstanceFieldGet
/// CHECK-START: void Main.test21(TestClass) load_store_elimination (after)
/// CHECK-DAG: InstanceFieldSet
- /// CHECK-DAG: Phi
/// CHECK-START: void Main.test21(TestClass) load_store_elimination (after)
/// CHECK-NOT: NewInstance
@@ -1853,14 +1849,22 @@
/// CHECK: ArraySet
/// CHECK: ArrayGet
- /// CHECK-START: int Main.testAllocationEliminationOfArray2() load_store_elimination (after)
+ /// CHECK-START-{ARM64,X86,X86_64}: int Main.testAllocationEliminationOfArray2() load_store_elimination (after)
+ /// CHECK-NOT: NewArray
+ /// CHECK-NOT: ArraySet
+ /// CHECK-NOT: ArraySet
+ /// CHECK-NOT: ArrayGet
+
+ // The loop optimization doesn't happen in ARM which leads to LSE not being able to optimize this
+ // case.
+ /// CHECK-START-ARM: int Main.testAllocationEliminationOfArray2() load_store_elimination (after)
/// CHECK: NewArray
/// CHECK: ArraySet
/// CHECK: ArraySet
/// CHECK: ArrayGet
private static int testAllocationEliminationOfArray2() {
- // Cannot eliminate array allocation since array is accessed with non-constant
- // index (only 3 elements to prevent vectorization of the reduction).
+ // Array can be eliminated because LSE can reduce the array accesses into
+ // integer constants.
int[] array = new int[3];
array[1] = 4;
array[2] = 7;
@@ -1924,6 +1928,36 @@
return array[1];
}
+ /// CHECK-START: int Main.testAllocationEliminationOfArray6(boolean) load_store_elimination (before)
+ /// CHECK: NewArray
+ /// CHECK: ArraySet
+ /// CHECK: ArraySet
+ /// CHECK: ArrayGet
+
+ /// CHECK-START: int Main.testAllocationEliminationOfArray6(boolean) load_store_elimination (after)
+ /// CHECK: NewArray
+ /// CHECK: ArraySet
+ /// CHECK: ArraySet
+ /// CHECK: ArrayGet
+ private static int testAllocationEliminationOfArray6(boolean prevent_loop_opt) {
+ // Cannot eliminate array allocation since array is accessed with non-constant
+ // index (only 3 elements to prevent vectorization of the reduction).
+ int[] array = new int[3];
+ array[1] = 4;
+ array[2] = 7;
+ int sum = 0;
+ for (int e : array) {
+ sum += e;
+
+ // Prevent the loop from being optimized away before LSE. This should
+ // never be false.
+ if (!prevent_loop_opt) {
+ return -1;
+ }
+ }
+ return sum;
+ }
+
/// CHECK-START: int Main.testExitMerge(boolean) load_store_elimination (before)
/// CHECK-DAG: NewInstance
/// CHECK-DAG: InstanceFieldSet field_name:TestClass.i
@@ -4213,6 +4247,7 @@
} catch (NegativeArraySizeException e) {
System.out.println("Got NegativeArraySizeException.");
}
+ assertIntEquals(testAllocationEliminationOfArray6(true), 11);
assertIntEquals(testStoreStore().i, 41);
assertIntEquals(testStoreStore().j, 43);
diff --git a/test/530-checker-peel-unroll/src/Main.java b/test/530-checker-peel-unroll/src/Main.java
index f909af3..d97de2a 100644
--- a/test/530-checker-peel-unroll/src/Main.java
+++ b/test/530-checker-peel-unroll/src/Main.java
@@ -1015,7 +1015,7 @@
/// CHECK: ArraySet
/// CHECK-NOT: ArraySet
- /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$final (after)
+ /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$after_bce (after)
/// CHECK-DAG: <<Param:z\d+>> ParameterValue loop:none
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none
@@ -1030,21 +1030,21 @@
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
/// CHECK-DAG: <<IndAdd:i\d+>> Add [<<Phi>>,<<Const1>>] loop:<<Loop>> outer_loop:none
- /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$final (after)
+ /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$after_bce (after)
/// CHECK: GreaterThanOrEqual
/// CHECK-NOT: GreaterThanOrEqual
- /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$final (after)
+ /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$after_bce (after)
/// CHECK: If
/// CHECK: If
/// CHECK-NOT: If
- /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$final (after)
+ /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$after_bce (after)
/// CHECK: ArrayGet
/// CHECK: ArrayGet
/// CHECK-NOT: ArrayGet
- /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$final (after)
+ /// CHECK-START: void Main.peelingSimple(int[], boolean) dead_code_elimination$after_bce (after)
/// CHECK: ArraySet
/// CHECK: ArraySet
/// CHECK-NOT: ArraySet
@@ -1069,7 +1069,7 @@
/// CHECK-DAG: If [<<Check>>] loop:<<Loop>> outer_loop:none
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
- /// CHECK-START: void Main.peelingAddInts(int[]) dead_code_elimination$final (after)
+ /// CHECK-START: void Main.peelingAddInts(int[]) dead_code_elimination$after_bce (after)
/// CHECK-DAG: <<Param:l\d+>> ParameterValue loop:none
/// CHECK-DAG: <<ConstNull:l\d+>> NullConstant loop:none
/// CHECK-DAG: <<Eq:z\d+>> Equal [<<Param>>,<<ConstNull>>] loop:none
@@ -1081,7 +1081,7 @@
/// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
// There's a 3rd `if` due to bounds checks.
- /// CHECK-START: void Main.peelingAddInts(int[]) dead_code_elimination$final (after)
+ /// CHECK-START: void Main.peelingAddInts(int[]) dead_code_elimination$after_bce (after)
/// CHECK: If
/// CHECK: If
/// CHECK: If
@@ -1118,7 +1118,7 @@
/// CHECK: ArraySet
/// CHECK-NOT: ArraySet
- /// CHECK-START: void Main.peelingBreakFromNest(int[], boolean) dead_code_elimination$final (after)
+ /// CHECK-START: void Main.peelingBreakFromNest(int[], boolean) dead_code_elimination$after_bce (after)
/// CHECK-DAG: <<Param:z\d+>> ParameterValue loop:none
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 loop:none
@@ -1135,13 +1135,13 @@
/// CHECK-DAG: <<IndAdd1:i\d+>> Add [<<Phi1>>,<<Const1>>] loop:<<Loop1>> outer_loop:<<Loop0>>
/// CHECK-DAG: <<IndAdd0:i\d+>> Add [<<Phi0>>,<<Const1>>] loop:<<Loop0>> outer_loop:none
- /// CHECK-START: void Main.peelingBreakFromNest(int[], boolean) dead_code_elimination$final (after)
+ /// CHECK-START: void Main.peelingBreakFromNest(int[], boolean) dead_code_elimination$after_bce (after)
/// CHECK: If
/// CHECK: If
/// CHECK: If
/// CHECK-NOT: If
- /// CHECK-START: void Main.peelingBreakFromNest(int[], boolean) dead_code_elimination$final (after)
+ /// CHECK-START: void Main.peelingBreakFromNest(int[], boolean) dead_code_elimination$after_bce (after)
/// CHECK: ArraySet
/// CHECK: ArraySet
/// CHECK-NOT: ArraySet
@@ -1170,7 +1170,7 @@
/// CHECK: If
/// CHECK-NOT: If
- /// CHECK-START: int Main.peelingHoistOneControl(int) dead_code_elimination$final (after)
+ /// CHECK-START: int Main.peelingHoistOneControl(int) dead_code_elimination$after_bce (after)
/// CHECK-DAG: <<Param:i\d+>> ParameterValue loop:none
/// CHECK-DAG: <<Const0:i\d+>> IntConstant 0 loop:none
/// CHECK-DAG: <<Check:z\d+>> NotEqual [<<Param>>,<<Const0>>] loop:none
@@ -1181,14 +1181,14 @@
// Check that the loop has no instruction except SuspendCheck and Goto (indefinite loop).
/// CHECK-NOT: loop:<<Loop>> outer_loop:none
- /// CHECK-START: int Main.peelingHoistOneControl(int) dead_code_elimination$final (after)
+ /// CHECK-START: int Main.peelingHoistOneControl(int) dead_code_elimination$after_bce (after)
/// CHECK: If
/// CHECK-NOT: If
- /// CHECK-START: int Main.peelingHoistOneControl(int) dead_code_elimination$final (after)
+ /// CHECK-START: int Main.peelingHoistOneControl(int) dead_code_elimination$after_bce (after)
/// CHECK-NOT: Phi
- /// CHECK-START: int Main.peelingHoistOneControl(int) dead_code_elimination$final (after)
+ /// CHECK-START: int Main.peelingHoistOneControl(int) dead_code_elimination$after_bce (after)
/// CHECK-NOT: Add
private static final int peelingHoistOneControl(int x) {
int i = 0;
@@ -1204,12 +1204,12 @@
/// CHECK-DAG: If loop:<<Loop>> outer_loop:none
/// CHECK-DAG: If loop:<<Loop>> outer_loop:none
- /// CHECK-START: int Main.peelingHoistOneControl(int, int) dead_code_elimination$final (after)
+ /// CHECK-START: int Main.peelingHoistOneControl(int, int) dead_code_elimination$after_bce (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: If loop:<<Loop>> outer_loop:none
// One `if` inside the loop (the one no longer invariant), two outside of it.
- /// CHECK-START: int Main.peelingHoistOneControl(int, int) dead_code_elimination$final (after)
+ /// CHECK-START: int Main.peelingHoistOneControl(int, int) dead_code_elimination$after_bce (after)
/// CHECK: If
/// CHECK: If
/// CHECK: If
@@ -1230,12 +1230,12 @@
/// CHECK-DAG: If loop:<<Loop>> outer_loop:none
/// CHECK-DAG: If loop:<<Loop>> outer_loop:none
- /// CHECK-START: int Main.peelingHoistTwoControl(int, int, int) dead_code_elimination$final (after)
+ /// CHECK-START: int Main.peelingHoistTwoControl(int, int, int) dead_code_elimination$after_bce (after)
/// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: If loop:<<Loop>> outer_loop:none
// One `if` inside the loop (the one no longer invariant), three outside of it.
- /// CHECK-START: int Main.peelingHoistTwoControl(int, int, int) dead_code_elimination$final (after)
+ /// CHECK-START: int Main.peelingHoistTwoControl(int, int, int) dead_code_elimination$after_bce (after)
/// CHECK: If
/// CHECK: If
/// CHECK: If
diff --git a/test/530-checker-regression-reftyp-final/src/Main.java b/test/530-checker-regression-reftyp-final/src/Main.java
index f86b515..69d99e6 100644
--- a/test/530-checker-regression-reftyp-final/src/Main.java
+++ b/test/530-checker-regression-reftyp-final/src/Main.java
@@ -58,9 +58,6 @@
}
public static Object[] $noinline$getArray() {
- if (doThrow) throw new Error();
return new MyClassB[2];
}
-
- static boolean doThrow = false;
}
diff --git a/test/534-checker-bce-deoptimization/src/Main.java b/test/534-checker-bce-deoptimization/src/Main.java
index c4e4cbf..45dcbce 100644
--- a/test/534-checker-bce-deoptimization/src/Main.java
+++ b/test/534-checker-bce-deoptimization/src/Main.java
@@ -89,7 +89,6 @@
/// CHECK-NOT: BoundsCheck
public static void $noinline$FloatFill(float f1, float f2, float[] array, int n) {
- if (doThrow) { throw new Error(); }
for (int i = 0; i < n; ++i) {
array[i] = ((i & 1) == 1) ? f1 : f2;
f1 += 1.5f;
@@ -118,14 +117,11 @@
/// CHECK-NOT: BoundsCheck
public static void $noinline$DoubleFill(double d1, double d2, double[] array, int n) {
- if (doThrow) { throw new Error(); }
for (int i = 0; i < n; ++i) {
array[i] = ((i & 1) == 1) ? d1 : d2;
d1 += 1.5;
d2 += 2.25;
}
}
-
- public static boolean doThrow = false;
}
diff --git a/test/536-checker-needs-access-check/Android.bp b/test/536-checker-needs-access-check/Android.bp
new file mode 100644
index 0000000..ccf524e
--- /dev/null
+++ b/test/536-checker-needs-access-check/Android.bp
@@ -0,0 +1,53 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `536-checker-needs-access-check`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-536-checker-needs-access-check-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-536-checker-needs-access-check",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-536-checker-needs-access-check-src"
+ ],
+ data: [
+ ":art-run-test-536-checker-needs-access-check-expected-stdout",
+ ":art-run-test-536-checker-needs-access-check-expected-stderr",
+ ],
+ // Include the Java source files in the test's artifacts, to make Checker assertions
+ // available to the TradeFed test runner.
+ include_srcs: true,
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-536-checker-needs-access-check-expected-stdout",
+ out: ["art-run-test-536-checker-needs-access-check-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-536-checker-needs-access-check-expected-stderr",
+ out: ["art-run-test-536-checker-needs-access-check-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/536-checker-needs-access-check/src2/other/InaccessibleClass.java b/test/536-checker-needs-access-check/src2/other/InaccessibleClass.java
index 646cc7e..6fd99ef 100644
--- a/test/536-checker-needs-access-check/src2/other/InaccessibleClass.java
+++ b/test/536-checker-needs-access-check/src2/other/InaccessibleClass.java
@@ -31,7 +31,7 @@
Class<?> klass = null;
try {
klass = GetInaccessibleClass.$inline$get();
- throw new Error("Unreachable");
+ System.out.println("Unreachable");
} catch (IllegalAccessError expected) {}
return klass;
}
diff --git a/test/537-checker-inline-and-unverified/Android.bp b/test/537-checker-inline-and-unverified/Android.bp
new file mode 100644
index 0000000..8078e92
--- /dev/null
+++ b/test/537-checker-inline-and-unverified/Android.bp
@@ -0,0 +1,53 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `537-checker-inline-and-unverified`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-537-checker-inline-and-unverified-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-537-checker-inline-and-unverified",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-537-checker-inline-and-unverified-src"
+ ],
+ data: [
+ ":art-run-test-537-checker-inline-and-unverified-expected-stdout",
+ ":art-run-test-537-checker-inline-and-unverified-expected-stderr",
+ ],
+ // Include the Java source files in the test's artifacts, to make Checker assertions
+ // available to the TradeFed test runner.
+ include_srcs: true,
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-537-checker-inline-and-unverified-expected-stdout",
+ out: ["art-run-test-537-checker-inline-and-unverified-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-537-checker-inline-and-unverified-expected-stderr",
+ out: ["art-run-test-537-checker-inline-and-unverified-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/542-bitfield-rotates/src/Main.java b/test/542-bitfield-rotates/src/Main.java
index f2bc153..4308439 100644
--- a/test/542-bitfield-rotates/src/Main.java
+++ b/test/542-bitfield-rotates/src/Main.java
@@ -48,12 +48,7 @@
test_Long_left_constant_v();
}
- public static boolean doThrow = false;
-
public static int $noinline$rotate_int_right_reg_v_csubv(int value, int distance) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> distance) | (value << (32 - distance));
}
@@ -74,9 +69,6 @@
}
public static long $noinline$rotate_long_right_reg_v_csubv(long value, int distance) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> distance) | (value << (64 - distance));
}
@@ -97,9 +89,6 @@
}
public static int $noinline$rotate_int_left_reg_csubv_v(int value, int distance) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> (32 - distance)) | (value << distance);
}
@@ -120,9 +109,6 @@
}
public static long $noinline$rotate_long_left_reg_csubv_v(long value, int distance) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> (64 - distance)) | (value << distance);
}
@@ -143,9 +129,6 @@
}
public static int $noinline$rotate_int_right_reg_v_negv(int value, int distance) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> distance) | (value << -distance);
}
@@ -166,9 +149,6 @@
}
public static long $noinline$rotate_long_right_reg_v_negv(long value, int distance) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> distance) | (value << -distance);
}
@@ -189,9 +169,6 @@
}
public static int $noinline$rotate_int_left_reg_negv_v(int value, int distance) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> -distance) | (value << distance);
}
@@ -212,9 +189,6 @@
}
public static long $noinline$rotate_long_left_reg_negv_v(long value, int distance) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> -distance) | (value << distance);
}
@@ -235,30 +209,18 @@
}
public static int $noinline$rotate_int_right_constant_0(int value) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> 0) | (value << 0);
}
public static int $noinline$rotate_int_right_constant_1(int value) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> 1) | (value << -1);
}
public static int $noinline$rotate_int_right_constant_m1(int value) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> -1) | (value << 1);
}
public static int $noinline$rotate_int_right_constant_16(int value) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> 16) | (value << -16);
}
@@ -270,51 +232,30 @@
}
public static long $noinline$rotate_long_right_constant_0(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> 0) | (value << 0);
}
public static long $noinline$rotate_long_right_constant_1(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> 1) | (value << -1);
}
public static long $noinline$rotate_long_right_constant_m1(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> -1) | (value << 1);
}
public static long $noinline$rotate_long_right_constant_16(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> 16) | (value << -16);
}
public static long $noinline$rotate_long_right_constant_32(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> 32) | (value << -32);
}
public static long $noinline$rotate_long_right_constant_48(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> 48) | (value << -48);
}
public static long $noinline$rotate_long_right_constant_64(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value >>> 64) | (value << -64);
}
@@ -328,30 +269,18 @@
}
public static int $noinline$rotate_int_left_constant_0(int value) {
- if (doThrow) {
- throw new Error();
- }
return (value << 0) | (value >>> 0);
}
public static int $noinline$rotate_int_left_constant_1(int value) {
- if (doThrow) {
- throw new Error();
- }
return (value << 1) | (value >>> -1);
}
public static int $noinline$rotate_int_left_constant_m1(int value) {
- if (doThrow) {
- throw new Error();
- }
return (value << -1) | (value >>> 1);
}
public static int $noinline$rotate_int_left_constant_16(int value) {
- if (doThrow) {
- throw new Error();
- }
return (value << 16) | (value >>> -16);
}
@@ -363,51 +292,30 @@
}
public static long $noinline$rotate_long_left_constant_0(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value << 0) | (value >>> 0);
}
public static long $noinline$rotate_long_left_constant_1(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value << 1) | (value >>> -1);
}
public static long $noinline$rotate_long_left_constant_m1(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value << -1) | (value >>> 1);
}
public static long $noinline$rotate_long_left_constant_16(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value << 16) | (value >>> -16);
}
public static long $noinline$rotate_long_left_constant_32(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value << 32) | (value >>> -32);
}
public static long $noinline$rotate_long_left_constant_48(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value << 48) | (value >>> -48);
}
public static long $noinline$rotate_long_left_constant_64(long value) {
- if (doThrow) {
- throw new Error();
- }
return (value << 64) | (value >>> -64);
}
diff --git a/test/542-inline-trycatch/src/Main.java b/test/542-inline-trycatch/src/Main.java
index 5a6e06f..5e33ed6 100644
--- a/test/542-inline-trycatch/src/Main.java
+++ b/test/542-inline-trycatch/src/Main.java
@@ -33,17 +33,6 @@
return is_hex ? Integer.parseInt(str, 16) : Integer.parseInt(str);
}
- // We expect methods with try/catch to not be inlined. Inlined try/catch
- // blocks are not supported at the moment.
-
- private static int $noinline$TryCatch(String str) {
- try {
- return Integer.parseInt(str);
- } catch (NumberFormatException ex) {
- return -1;
- }
- }
-
public static void testSingleBlockFromTry() {
int val = 0;
@@ -117,49 +106,11 @@
assertEquals(32, val);
}
- public static void testTryCatchFromTry() {
- int val = 0;
-
- try {
- val = $noinline$TryCatch("42");
- } catch (NumberFormatException ex) {
- unreachable();
- }
- assertEquals(42, val);
-
- try {
- val = $noinline$TryCatch("xyz");
- } catch (NumberFormatException ex) {
- unreachable();
- }
- assertEquals(-1, val);
- }
-
- public static void testTryCatchFromCatch() {
- int val = 0;
-
- try {
- throwException();
- } catch (Exception ex) {
- val = $noinline$TryCatch("42");
- }
- assertEquals(42, val);
-
- try {
- throwException();
- } catch (Exception ex) {
- val = $noinline$TryCatch("xyz");
- }
- assertEquals(-1, val);
- }
-
public static void main(String[] args) {
testSingleBlockFromTry();
testSingleBlockFromCatch();
testMultipleBlocksFromTry();
testMultipleBlocksFromCatch();
- testTryCatchFromTry();
- testTryCatchFromCatch();
}
private static void assertEquals(int expected, int actual) {
diff --git a/test/543-checker-dce-trycatch/smali/TestCase.smali b/test/543-checker-dce-trycatch/smali/TestCase.smali
index 7ad9ba8..15eaabb 100644
--- a/test/543-checker-dce-trycatch/smali/TestCase.smali
+++ b/test/543-checker-dce-trycatch/smali/TestCase.smali
@@ -206,6 +206,7 @@
## CHECK-START: int TestCase.testCatchPhiInputs_DefinedInTryBlock(int, int, int, int) dead_code_elimination$after_inlining (before)
## CHECK-DAG: <<Arg0:i\d+>> ParameterValue
## CHECK-DAG: <<Arg1:i\d+>> ParameterValue
+## CHECK-DAG: <<Const0x0:i\d+>> IntConstant 0
## CHECK-DAG: <<Const0xa:i\d+>> IntConstant 10
## CHECK-DAG: <<Const0xb:i\d+>> IntConstant 11
## CHECK-DAG: <<Const0xc:i\d+>> IntConstant 12
@@ -215,7 +216,7 @@
## CHECK-DAG: <<Const0x10:i\d+>> IntConstant 16
## CHECK-DAG: <<Const0x11:i\d+>> IntConstant 17
## CHECK-DAG: <<Add:i\d+>> Add [<<Arg0>>,<<Arg1>>]
-## CHECK-DAG: <<Phi:i\d+>> Phi [<<Add>>,<<Const0xf>>] reg:3 is_catch_phi:false
+## CHECK-DAG: <<Phi:i\d+>> Phi [<<Const0x0>>,<<Const0xf>>] reg:3 is_catch_phi:false
## CHECK-DAG: Phi [<<Const0xa>>,<<Const0xb>>,<<Const0xd>>] reg:1 is_catch_phi:true
## CHECK-DAG: Phi [<<Add>>,<<Const0xc>>,<<Const0xe>>] reg:2 is_catch_phi:true
## CHECK-DAG: Phi [<<Phi>>,<<Const0x10>>,<<Const0x11>>] reg:3 is_catch_phi:true
@@ -248,7 +249,8 @@
if-eqz v3, :define_phi
const v3, 0xf
:define_phi
- # v3 = Phi [Add, 0xf] # dead catch phi input, defined in the dead block (HPhi)
+ # v3 = Phi [Add, 0xf] # dead catch phi input, defined in the dead block (HPhi).
+ # Note that the Add has to be equal to 0 since we do `if-eqz v3`
div-int/2addr p0, v2
:else
diff --git a/test/543-env-long-ref/env_long_ref.cc b/test/543-env-long-ref/env_long_ref.cc
index 1c30d46..7084714 100644
--- a/test/543-env-long-ref/env_long_ref.cc
+++ b/test/543-env-long-ref/env_long_ref.cc
@@ -36,7 +36,8 @@
found = true;
// For optimized non-debuggable code do not expect dex register info to be present.
if (stack_visitor->GetCurrentShadowFrame() == nullptr &&
- !Runtime::Current()->IsAsyncDeoptimizeable(stack_visitor->GetCurrentQuickFramePc())) {
+ !Runtime::Current()->IsAsyncDeoptimizeable(stack_visitor->GetOuterMethod(),
+ stack_visitor->GetCurrentQuickFramePc())) {
return true;
}
uint32_t stack_value = 0;
diff --git a/test/551-checker-clinit/src/Main.java b/test/551-checker-clinit/src/Main.java
index 0eea800..77a9825 100644
--- a/test/551-checker-clinit/src/Main.java
+++ b/test/551-checker-clinit/src/Main.java
@@ -17,14 +17,14 @@
public class Main {
public static void main(String[] args) {}
- public static int foo = 42;
+ public static long foo = 42;
// Primitive array initialization is trivial for purposes of the ClinitCheck. It cannot
// leak instances of erroneous classes or initialize subclasses of erroneous classes.
- public static int[] array1 = new int[] { 1, 2, 3 };
- public static int[] array2;
+ public static long[] array1 = new long[] { 1, 2, 3 };
+ public static long[] array2;
static {
- int[] a = new int[4];
+ long[] a = new long[4];
a[0] = 42;
array2 = a;
}
@@ -46,35 +46,35 @@
/// CHECK-NOT: ClinitCheck
public void invokeSuperClass() {
// No Class initialization check as Main.<clinit> is trivial. b/62478025
- int a = Main.foo;
+ long a = Main.foo;
}
/// CHECK-START: void Sub.invokeItself() builder (after)
/// CHECK-NOT: ClinitCheck
public void invokeItself() {
// No Class initialization check as Sub.<clinit> and Main.<clinit> are trivial. b/62478025
- int a = foo;
+ long a = foo;
}
/// CHECK-START: void Sub.invokeSubClass() builder (after)
/// CHECK: ClinitCheck
public void invokeSubClass() {
- int a = SubSub.foo;
+ long a = SubSub.foo;
}
- public static int foo = 42;
+ public static long foo = 42;
}
class SubSub {
public static void bar() {
- int a = Main.foo;
+ long a = Main.foo;
}
- public static int foo = 42;
+ public static long foo = 42;
}
class NonTrivial {
- public static int staticFoo = 42;
- public int instanceFoo;
+ public static long staticFoo = 42;
+ public long instanceFoo;
static {
System.out.println("NonTrivial.<clinit>");
diff --git a/test/552-checker-sharpening/src/Main.java b/test/552-checker-sharpening/src/Main.java
index 15ff8a6..b017ab0 100644
--- a/test/552-checker-sharpening/src/Main.java
+++ b/test/552-checker-sharpening/src/Main.java
@@ -34,10 +34,7 @@
}
}
- public static boolean doThrow = false;
-
private static int $noinline$foo(int x) {
- if (doThrow) { throw new Error(); }
return x;
}
@@ -135,8 +132,6 @@
/// CHECK: LoadString load_kind:BootImageRelRo
public static String $noinline$getBootImageString() {
- // Prevent inlining to avoid the string comparison being optimized away.
- if (doThrow) { throw new Error(); }
// Empty string is known to be in the boot image.
return "";
}
@@ -152,8 +147,6 @@
/// CHECK-DAG: LoadString load_kind:BssEntry
public static String $noinline$getNonBootImageString() {
- // Prevent inlining to avoid the string comparison being optimized away.
- if (doThrow) { throw new Error(); }
// This string is not in the boot image.
return "non-boot-image-string";
}
@@ -162,8 +155,6 @@
/// CHECK: LoadClass load_kind:BootImageRelRo class_name:java.lang.String
public static Class<?> $noinline$getStringClass() {
- // Prevent inlining to avoid the string comparison being optimized away.
- if (doThrow) { throw new Error(); }
// String class is known to be in the boot image.
return String.class;
}
@@ -179,8 +170,6 @@
/// CHECK-DAG: LoadClass load_kind:BssEntry class_name:Other
public static Class<?> $noinline$getOtherClass() {
- // Prevent inlining to avoid the string comparison being optimized away.
- if (doThrow) { throw new Error(); }
// Other class is not in the boot image.
return Other.class;
}
diff --git a/test/553-invoke-super/src/SuperClass.java b/test/553-invoke-super/src/SuperClass.java
index 36ce093..c3e4a4e 100644
--- a/test/553-invoke-super/src/SuperClass.java
+++ b/test/553-invoke-super/src/SuperClass.java
@@ -15,12 +15,7 @@
*/
public class SuperClass {
- boolean doThrow = false;
-
public int $noinline$returnInt() {
- if (doThrow) {
- throw new Error();
- }
return 42;
}
}
diff --git a/test/559-checker-irreducible-loop/expected-stdout.txt b/test/559-checker-irreducible-loop/expected-stdout.txt
index b64be7a..992fd87 100644
--- a/test/559-checker-irreducible-loop/expected-stdout.txt
+++ b/test/559-checker-irreducible-loop/expected-stdout.txt
@@ -5,3 +5,5 @@
class Main
42
-42
+1
+-1
diff --git a/test/559-checker-irreducible-loop/smali/IrreducibleLoop.smali b/test/559-checker-irreducible-loop/smali/IrreducibleLoop.smali
index a30a11a..a88422f 100644
--- a/test/559-checker-irreducible-loop/smali/IrreducibleLoop.smali
+++ b/test/559-checker-irreducible-loop/smali/IrreducibleLoop.smali
@@ -140,24 +140,23 @@
# other_loop_entry
# i1 = phi(p0, i0)
#
-## CHECK-START: int IrreducibleLoop.liveness(int) liveness (after)
+## CHECK-START: int IrreducibleLoop.liveness(int, int) liveness (after)
## CHECK-DAG: <<Arg:i\d+>> ParameterValue liveness:<<ArgLiv:\d+>> ranges:{[<<ArgLiv>>,<<ArgLoopPhiUse:\d+>>)}
## CHECK-DAG: <<LoopPhi:i\d+>> Phi [<<Arg>>,<<PhiInLoop:i\d+>>] liveness:<<ArgLoopPhiUse>> ranges:{[<<ArgLoopPhiUse>>,<<PhiInLoopUse:\d+>>)}
## CHECK-DAG: <<PhiInLoop>> Phi [<<Arg>>,<<LoopPhi>>] liveness:<<PhiInLoopUse>> ranges:{[<<PhiInLoopUse>>,<<BackEdgeLifetimeEnd:\d+>>)}
## CHECK: Return liveness:<<ReturnLiveness:\d+>>
## CHECK-EVAL: <<ReturnLiveness>> == <<BackEdgeLifetimeEnd>> + 2
-.method public static liveness(I)I
+.method public static liveness(II)I
.registers 2
- const/16 v0, 42
- if-eq p0, v0, :other_loop_entry
+ if-eq p0, p1, :other_loop_entry
:loop_entry
- add-int v0, v0, p0
- if-ne v1, v0, :exit
+ add-int p1, p1, p0
+ if-ne v0, p1, :exit
:other_loop_entry
- add-int v0, v0, v0
+ add-int p1, p1, p1
goto :loop_entry
:exit
- return v0
+ return p1
.end method
# Check that we don't GVN across irreducible loops:
@@ -547,3 +546,57 @@
:exit
return-void
.end method
+
+## CHECK-START: int IrreducibleLoop.testDoNotInlineIrreducible(int) inliner (before)
+## CHECK: InvokeStaticOrDirect method_name:IrreducibleLoop.doNotInlineIrreducible
+#
+## CHECK-START: int IrreducibleLoop.testDoNotInlineIrreducible(int) inliner (after)
+## CHECK: InvokeStaticOrDirect method_name:IrreducibleLoop.doNotInlineIrreducible
+.method public static testDoNotInlineIrreducible(I)I
+ .registers 2
+ invoke-static {p0}, LIrreducibleLoop;->doNotInlineIrreducible(I)I
+ move-result v0
+ return v0
+.end method
+
+# Check that doNotInlineIrreducible has a simple irreducible loop
+#
+# entry
+# / \
+# / \
+# loop_entry \
+# / \- \
+# try_start\- \
+# other_loop_entry
+#
+## CHECK-START: int IrreducibleLoop.doNotInlineIrreducible(int) register (after)
+## CHECK: irreducible:true
+#
+# Check that we didn't optimized away the try.
+## CHECK-START: int IrreducibleLoop.doNotInlineIrreducible(int) register (after)
+## CHECK: TryBoundary kind:exit
+.method public static doNotInlineIrreducible(I)I
+ .registers 3
+ const/16 v0, 42
+ const/16 v1, 21
+ # Irreducible loop
+ if-eq v1, v0, :other_loop_entry
+ :loop_entry
+ if-ne v1, v0, :try_start
+ add-int v0, v0, v0
+ :other_loop_entry
+ add-int v0, v0, v0
+ goto :loop_entry
+
+ :try_start
+ # We make this division to make sure the try doesn't get optimized out
+ div-int v0, v0, p0
+ return v0
+ :try_end
+ .catchall {:try_start .. :try_end} :catch_all
+
+ :catch_all
+ # This is only reachable if the parameter is 0
+ const/4 v0, -0x1
+ return v0
+.end method
diff --git a/test/559-checker-irreducible-loop/src/Main.java b/test/559-checker-irreducible-loop/src/Main.java
index 97165ec..16b8415 100644
--- a/test/559-checker-irreducible-loop/src/Main.java
+++ b/test/559-checker-irreducible-loop/src/Main.java
@@ -38,8 +38,8 @@
}
{
- Method m = c.getMethod("liveness", int.class);
- Object[] arguments = { 42 };
+ Method m = c.getMethod("liveness", int.class, int.class);
+ Object[] arguments = { 42, 42 };
System.out.println(m.invoke(null, arguments));
}
@@ -60,6 +60,18 @@
Object[] arguments = { 42 };
System.out.println(m.invoke(null, arguments));
}
+
+ {
+ Method m = c.getMethod("testDoNotInlineIrreducible", int.class);
+ Object[] arguments = { 42 };
+ System.out.println(m.invoke(null, arguments));
+ }
+
+ {
+ Method m = c.getMethod("testDoNotInlineIrreducible", int.class);
+ Object[] arguments = { 0 };
+ System.out.println(m.invoke(null, arguments));
+ }
}
int myField;
diff --git a/test/561-divrem/src/Main.java b/test/561-divrem/src/Main.java
index 082783d..ec125e9 100644
--- a/test/561-divrem/src/Main.java
+++ b/test/561-divrem/src/Main.java
@@ -72,32 +72,18 @@
}
public static int $noinline$divInt(int value) {
- if (doThrow) {
- throw new Error("");
- }
return value / Integer.MIN_VALUE;
}
public static int $noinline$remInt(int value) {
- if (doThrow) {
- throw new Error("");
- }
return value % Integer.MIN_VALUE;
}
public static long $noinline$divLong(long value) {
- if (doThrow) {
- throw new Error("");
- }
return value / Long.MIN_VALUE;
}
public static long $noinline$remLong(long value) {
- if (doThrow) {
- throw new Error("");
- }
return value % Long.MIN_VALUE;
}
-
- static boolean doThrow = false;
}
diff --git a/test/563-checker-fakestring/smali/TestCase.smali b/test/563-checker-fakestring/smali/TestCase.smali
index 4721eca..c6561d5 100644
--- a/test/563-checker-fakestring/smali/TestCase.smali
+++ b/test/563-checker-fakestring/smali/TestCase.smali
@@ -310,7 +310,7 @@
## CHECK-NOT: NewInstance
## CHECK-DAG: <<Invoke1:l\d+>> InvokeStaticOrDirect method_name:java.lang.String.<init>
## CHECK-DAG: <<Invoke2:l\d+>> InvokeStaticOrDirect method_name:java.lang.String.<init>
-## CHECK-DAG: <<Phi:l\d+>> Phi [<<Invoke2>>,<<Invoke1>>]
+## CHECK-DAG: <<Phi:l\d+>> Phi [<<Invoke1>>,<<Invoke2>>]
## CHECK-DAG: Return [<<Phi>>]
.method public static loopAndStringInitAndPhi([BZ)Ljava/lang/String;
.registers 4
diff --git a/test/563-checker-fakestring/src/Main.java b/test/563-checker-fakestring/src/Main.java
index 7e775b3..e2773c8 100644
--- a/test/563-checker-fakestring/src/Main.java
+++ b/test/563-checker-fakestring/src/Main.java
@@ -34,6 +34,11 @@
}
}
+ // Create an empty int[] to force loading the int[] class before compiling
+ // TestCase.deoptimizeNewInstance.
+ // This makes sure the compiler can properly type int[] and not bail.
+ static int[] emptyArray = new int[0];
+
public static void main(String[] args) throws Throwable {
System.loadLibrary(args[0]);
Class<?> c = Class.forName("TestCase");
@@ -160,10 +165,7 @@
}
}
- public static boolean doThrow = false;
-
public static Object $noinline$HiddenNull() {
- if (doThrow) { throw new Error(); }
return null;
}
}
diff --git a/test/564-checker-bitcount/src/Main.java b/test/564-checker-bitcount/src/Main.java
index e022d9d..460a31a 100644
--- a/test/564-checker-bitcount/src/Main.java
+++ b/test/564-checker-bitcount/src/Main.java
@@ -25,7 +25,6 @@
/// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerBitCount
/// CHECK-DAG: Return [<<Result>>]
private static int $noinline$BitCountBoolean(boolean x) {
- if (doThrow) { throw new Error(); } // Try defeating inlining.
return Integer.bitCount(x ? 1 : 0);
}
@@ -33,7 +32,6 @@
/// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerBitCount
/// CHECK-DAG: Return [<<Result>>]
private static int $noinline$BitCountByte(byte x) {
- if (doThrow) { throw new Error(); } // Try defeating inlining.
return Integer.bitCount(x);
}
@@ -41,7 +39,6 @@
/// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerBitCount
/// CHECK-DAG: Return [<<Result>>]
private static int $noinline$BitCountShort(short x) {
- if (doThrow) { throw new Error(); } // Try defeating inlining.
return Integer.bitCount(x);
}
@@ -49,7 +46,6 @@
/// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerBitCount
/// CHECK-DAG: Return [<<Result>>]
private static int $noinline$BitCountChar(char x) {
- if (doThrow) { throw new Error(); } // Try defeating inlining.
return Integer.bitCount(x);
}
@@ -57,7 +53,6 @@
/// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerBitCount
/// CHECK-DAG: Return [<<Result>>]
private static int $noinline$BitCountInt(int x) {
- if (doThrow) { throw new Error(); } // Try defeating inlining.
return Integer.bitCount(x);
}
@@ -65,7 +60,6 @@
/// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:LongBitCount
/// CHECK-DAG: Return [<<Result>>]
private static int $noinline$BitCountLong(long x) {
- if (doThrow) { throw new Error(); } // Try defeating inlining.
return Long.bitCount(x);
}
@@ -201,6 +195,4 @@
throw new Error("Expected: " + expected + ", found: " + result);
}
}
-
- private static boolean doThrow = false;
}
diff --git a/test/564-checker-inline-loop/src/Main.java b/test/564-checker-inline-loop/src/Main.java
index 6929913..41eca35 100644
--- a/test/564-checker-inline-loop/src/Main.java
+++ b/test/564-checker-inline-loop/src/Main.java
@@ -21,9 +21,6 @@
/// CHECK-DAG: Return [<<Invoke>>]
/// CHECK-START: int Main.inlineLoop() inliner (after)
- /// CHECK-NOT: InvokeStaticOrDirect
-
- /// CHECK-START: int Main.inlineLoop() inliner (after)
/// CHECK-DAG: <<Constant:i\d+>> IntConstant 42
/// CHECK-DAG: Return [<<Constant>>]
@@ -31,31 +28,31 @@
/// CHECK: Goto loop:{{B\d+}}
public static int inlineLoop() {
- return loopMethod();
+ return $inline$loopMethod();
}
/// CHECK-START: void Main.inlineWithinLoop() inliner (before)
/// CHECK: InvokeStaticOrDirect
- /// CHECK-START: void Main.inlineWithinLoop() inliner (after)
- /// CHECK-NOT: InvokeStaticOrDirect
-
/// CHECK-START: void Main.inlineWithinLoop() licm (after)
/// CHECK-DAG: Goto loop:<<OuterLoop:B\d+>> outer_loop:none
/// CHECK-DAG: Goto outer_loop:<<OuterLoop>>
public static void inlineWithinLoop() {
while (doLoop) {
- loopMethod();
+ $inline$loopMethod();
}
}
- public static int loopMethod() {
- while (doLoop) {}
+ public static int $inline$loopMethod() {
+ // We use `otherDoLoop` here so we don't propagate the knowledge that `doLoop` is true when
+ // inlining from `inlineWithinLoop`.
+ while (otherDoLoop) {}
return 42;
}
public static boolean doLoop = false;
+ public static boolean otherDoLoop = false;
public static void main(String[] args) {
inlineLoop();
diff --git a/test/565-checker-condition-liveness/src/Main.java b/test/565-checker-condition-liveness/src/Main.java
index 17a8613..4abf66d 100644
--- a/test/565-checker-condition-liveness/src/Main.java
+++ b/test/565-checker-condition-liveness/src/Main.java
@@ -33,8 +33,8 @@
/// CHECK-START-{ARM,ARM64}: void Main.testThrowIntoCatchBlock(int, java.lang.Object, int[]) liveness (after)
/// CHECK-DAG: <<IntArg:i\d+>> ParameterValue env_uses:[23,25]
- /// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[11,23,25]
- /// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[11,23,25]
+ /// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[11,23,25,33]
+ /// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[11,23,25,33]
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 env_uses:[23,25]
/// CHECK-DAG: SuspendCheck env:[[_,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:10
/// CHECK-DAG: NullCheck env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:20
@@ -43,10 +43,10 @@
/// CHECK-DAG: TryBoundary
/// CHECK-START-{ARM,ARM64}-DEBUGGABLE: void Main.testThrowIntoCatchBlock(int, java.lang.Object, int[]) liveness (after)
- /// CHECK-DAG: <<IntArg:i\d+>> ParameterValue env_uses:[11,23,25]
- /// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[11,23,25]
- /// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[11,23,25]
- /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 env_uses:[23,25]
+ /// CHECK-DAG: <<IntArg:i\d+>> ParameterValue env_uses:[11,23,25,33]
+ /// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[11,23,25,33]
+ /// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[11,23,25,33]
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 env_uses:[23,25,33]
/// CHECK-DAG: SuspendCheck env:[[_,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:10
/// CHECK-DAG: NullCheck env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:20
/// CHECK-DAG: ArrayLength liveness:22
@@ -56,8 +56,8 @@
// X86 and X86_64 generate at use site the ArrayLength, meaning only the BoundsCheck will have environment uses.
/// CHECK-START-{X86,X86_64}: void Main.testThrowIntoCatchBlock(int, java.lang.Object, int[]) liveness (after)
/// CHECK-DAG: <<IntArg:i\d+>> ParameterValue env_uses:[25,25]
- /// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[11,25,25]
- /// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[11,25,25]
+ /// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[11,25,25,33]
+ /// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[11,25,25,33]
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 env_uses:[25,25]
/// CHECK-DAG: SuspendCheck env:[[_,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:10
/// CHECK-DAG: NullCheck env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:20
@@ -66,10 +66,10 @@
/// CHECK-DAG: TryBoundary
/// CHECK-START-{X86,X86_64}-DEBUGGABLE: void Main.testThrowIntoCatchBlock(int, java.lang.Object, int[]) liveness (after)
- /// CHECK-DAG: <<IntArg:i\d+>> ParameterValue env_uses:[11,25,25]
- /// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[11,25,25]
- /// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[11,25,25]
- /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 env_uses:[25,25]
+ /// CHECK-DAG: <<IntArg:i\d+>> ParameterValue env_uses:[11,25,25,33]
+ /// CHECK-DAG: <<RefArg:l\d+>> ParameterValue env_uses:[11,25,25,33]
+ /// CHECK-DAG: <<Array:l\d+>> ParameterValue env_uses:[11,25,25,33]
+ /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 env_uses:[25,25,33]
/// CHECK-DAG: SuspendCheck env:[[_,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:10
/// CHECK-DAG: NullCheck env:[[<<Const1>>,<<IntArg>>,<<RefArg>>,<<Array>>]] liveness:20
/// CHECK-DAG: ArrayLength liveness:22
diff --git a/test/566-checker-codegen-select/src/Main.java b/test/566-checker-codegen-select/src/Main.java
index e215ab0..7b44580 100644
--- a/test/566-checker-codegen-select/src/Main.java
+++ b/test/566-checker-codegen-select/src/Main.java
@@ -21,7 +21,6 @@
/// CHECK-NEXT: Select [{{j\d+}},{{j\d+}},<<Cond>>]
public long $noinline$longSelect(long param) {
- if (doThrow) { throw new Error(); }
long val_true = longB;
long val_false = longC;
return (param > longA) ? val_true : val_false;
@@ -46,7 +45,6 @@
/// CHECK: cmovle/ngq
public long $noinline$longSelect_Constant(long param) {
- if (doThrow) { throw new Error(); }
long val_true = longB;
long val_false = longC;
return (param > 3L) ? val_true : val_false;
@@ -60,7 +58,6 @@
/// CHECK: cmovl/nge
public int $noinline$intSelect_Constant(int param) {
- if (doThrow) { throw new Error(); }
int val_true = intB;
int val_false = intC;
return (param >= 3) ? val_true : val_false;
@@ -88,8 +85,6 @@
}
}
- public boolean doThrow = false;
-
public long longA = 3L;
public long longB = 5L;
public long longC = 7L;
diff --git a/test/566-polymorphic-inlining/run b/test/566-polymorphic-inlining/run
deleted file mode 100644
index 2919f46..0000000
--- a/test/566-polymorphic-inlining/run
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/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.
-
-# -Xjitinitialsize:32M to prevent profiling info creation failure.
-exec ${RUN} \
- --runtime-option -Xjitinitialsize:32M \
- "${@}"
diff --git a/test/566-polymorphic-inlining/run.py b/test/566-polymorphic-inlining/run.py
new file mode 100644
index 0000000..0bfd5dc
--- /dev/null
+++ b/test/566-polymorphic-inlining/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # -Xjitinitialsize:32M to prevent profiling info creation failure.
+ ctx.default_run(args, runtime_option=["-Xjitinitialsize:32M"])
diff --git a/test/566-polymorphic-inlining/src/Main.java b/test/566-polymorphic-inlining/src/Main.java
index 595a056..691d48f 100644
--- a/test/566-polymorphic-inlining/src/Main.java
+++ b/test/566-polymorphic-inlining/src/Main.java
@@ -118,7 +118,6 @@
}
public static void $noinline$testInlineToSameTarget(Main m) {
- if (doThrow) throw new Error("");
m.increment();
}
@@ -137,7 +136,6 @@
counter++;
}
public static int counter = 0;
- public static boolean doThrow = false;
}
class Subclass extends Main {
diff --git a/test/567-checker-builder-intrinsics/src/TestFpAbs.java b/test/567-checker-builder-intrinsics/src/TestFpAbs.java
index e6c338d..f36432d 100644
--- a/test/567-checker-builder-intrinsics/src/TestFpAbs.java
+++ b/test/567-checker-builder-intrinsics/src/TestFpAbs.java
@@ -29,8 +29,6 @@
private static final int SPQUIET = 1 << 22;
private static final long DPQUIET = 1L << 51;
- public static boolean doThrow = false;
-
/// CHECK-START: float TestFpAbs.$opt$noinline$absSP(float) builder (after)
/// CHECK-DAG: <<Result:f\d+>> Abs
/// CHECK-DAG: Return [<<Result>>]
diff --git a/test/567-checker-builder-intrinsics/src/TestMinMax.java b/test/567-checker-builder-intrinsics/src/TestMinMax.java
index 0e88517..7207006 100644
--- a/test/567-checker-builder-intrinsics/src/TestMinMax.java
+++ b/test/567-checker-builder-intrinsics/src/TestMinMax.java
@@ -564,6 +564,16 @@
return x;
}
+ /// CHECK-START: int TestMinMax.minmax3(int) select_generator (after)
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P100:i\d+>> IntConstant 100
+ /// CHECK-DAG: <<M100:i\d+>> IntConstant -100
+ /// CHECK-DAG: <<Cnd1:z\d+>> LessThanOrEqual [<<Par>>,<<P100>>]
+ /// CHECK-DAG: <<Cnd2:z\d+>> GreaterThanOrEqual [<<Par>>,<<M100>>]
+ /// CHECK-DAG: <<Sel1:i\d+>> Select [<<M100>>,<<Par>>,<<Cnd2>>]
+ /// CHECK-DAG: <<Sel2:i\d+>> Select [<<P100>>,<<Sel1>>,<<Cnd1>>]
+ /// CHECK-DAG: Return [<<Sel2>>]
+
/// CHECK-START: int TestMinMax.minmax3(int) instruction_simplifier$after_gvn (after)
/// CHECK-DAG: <<Par:i\d+>> ParameterValue
/// CHECK-DAG: <<P100:i\d+>> IntConstant 100
@@ -578,6 +588,16 @@
return (x > 100) ? 100 : ((x < -100) ? -100 : x);
}
+ /// CHECK-START: int TestMinMax.minmax4(int) select_generator (after)
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue
+ /// CHECK-DAG: <<P100:i\d+>> IntConstant 100
+ /// CHECK-DAG: <<M100:i\d+>> IntConstant -100
+ /// CHECK-DAG: <<Cnd1:z\d+>> GreaterThanOrEqual [<<Par>>,<<M100>>]
+ /// CHECK-DAG: <<Cnd2:z\d+>> LessThanOrEqual [<<Par>>,<<P100>>]
+ /// CHECK-DAG: <<Sel1:i\d+>> Select [<<P100>>,<<Par>>,<<Cnd2>>]
+ /// CHECK-DAG: <<Sel2:i\d+>> Select [<<M100>>,<<Sel1>>,<<Cnd1>>]
+ /// CHECK-DAG: Return [<<Sel2>>]
+
/// CHECK-START: int TestMinMax.minmax4(int) instruction_simplifier$after_gvn (after)
/// CHECK-DAG: <<Par:i\d+>> ParameterValue
/// CHECK-DAG: <<P100:i\d+>> IntConstant 100
diff --git a/test/569-checker-pattern-replacement/run b/test/569-checker-pattern-replacement/run
deleted file mode 100755
index 8ab6527..0000000
--- a/test/569-checker-pattern-replacement/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-exec ${RUN} "$@" \
- -Xcompiler-option --no-inline-from="core-oj,569-checker-pattern-replacement.jar!classes2.dex"
diff --git a/test/569-checker-pattern-replacement/run.py b/test/569-checker-pattern-replacement/run.py
new file mode 100644
index 0000000..d763e54
--- /dev/null
+++ b/test/569-checker-pattern-replacement/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args,
+ Xcompiler_option=[
+ "--no-inline-from=core-oj,569-checker-pattern-replacement.jar!classes2.dex"
+ ])
diff --git a/test/570-checker-osr-locals/run b/test/570-checker-osr-locals/run
deleted file mode 100755
index 6cef13f..0000000
--- a/test/570-checker-osr-locals/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Ensure this test is not subject to code collection.
-exec ${RUN} "$@" --runtime-option -Xjitinitialsize:32M
diff --git a/test/570-checker-osr-locals/run.py b/test/570-checker-osr-locals/run.py
new file mode 100644
index 0000000..d876de2
--- /dev/null
+++ b/test/570-checker-osr-locals/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Ensure this test is not subject to code collection.
+ ctx.default_run(args, runtime_option=["-Xjitinitialsize:32M"])
diff --git a/test/570-checker-osr/run b/test/570-checker-osr/run
deleted file mode 100755
index 24d69b4..0000000
--- a/test/570-checker-osr/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Ensure this test is not subject to code collection.
-exec ${RUN} "$@" --runtime-option -Xjitinitialsize:32M
diff --git a/test/570-checker-osr/run.py b/test/570-checker-osr/run.py
new file mode 100644
index 0000000..8109c62
--- /dev/null
+++ b/test/570-checker-osr/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Ensure this test is not subject to code collection.
+ ctx.default_run(args, runtime_option=["-Xjitinitialsize:32M"])
diff --git a/test/570-checker-osr/smali/Osr.smali b/test/570-checker-osr/smali/Osr.smali
index 6592b7b..857cf64 100644
--- a/test/570-checker-osr/smali/Osr.smali
+++ b/test/570-checker-osr/smali/Osr.smali
@@ -19,7 +19,7 @@
# Check that blocks only havig nops are not merged when they are loop headers.
# This ensures we can do on-stack replacement for branches to those nop blocks.
-## CHECK-START: int Osr.simpleLoop(int, int) dead_code_elimination$final (after)
+## CHECK-START: int Osr.simpleLoop(int, int) dead_code_elimination$after_bce (after)
## CHECK-DAG: SuspendCheck loop:<<OuterLoop:B\d+>> outer_loop:none
## CHECK-DAG: SuspendCheck loop:{{B\d+}} outer_loop:<<OuterLoop>>
.method public static simpleLoop(II)I
diff --git a/test/574-irreducible-and-constant-area/run b/test/574-irreducible-and-constant-area/run
deleted file mode 100755
index ffdbcc9..0000000
--- a/test/574-irreducible-and-constant-area/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Don't do relocation, as this affects this test.
-exec ${RUN} "$@" --no-relocate
diff --git a/test/574-irreducible-and-constant-area/run.py b/test/574-irreducible-and-constant-area/run.py
new file mode 100644
index 0000000..3c04d9e
--- /dev/null
+++ b/test/574-irreducible-and-constant-area/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Don't do relocation, as this affects this test.
+ ctx.default_run(args, relocate=False)
diff --git a/test/578-polymorphic-inlining/src/Main.java b/test/578-polymorphic-inlining/src/Main.java
index 22d33d0..4798922 100644
--- a/test/578-polymorphic-inlining/src/Main.java
+++ b/test/578-polymorphic-inlining/src/Main.java
@@ -44,10 +44,7 @@
}
public static void $noinline$foo() {
- if (doThrow) throw new Error("");
}
-
- public static boolean doThrow;
}
class SubMain extends Main {
diff --git a/test/590-checker-arr-set-null-regression/src/Main.java b/test/590-checker-arr-set-null-regression/src/Main.java
index 792ee4e..ad47716 100644
--- a/test/590-checker-arr-set-null-regression/src/Main.java
+++ b/test/590-checker-arr-set-null-regression/src/Main.java
@@ -33,7 +33,7 @@
/// CHECK-DAG: <<CheckedArray:l\d+>> NullCheck [<<Array>>]
/// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<CheckedArray>>]
/// CHECK-DAG: <<CheckedIndex:i\d+>> BoundsCheck [<<Index>>,<<Length>>]
- /// CHECK-DAG: <<ArraySet:v\d+>> ArraySet [<<CheckedArray>>,<<CheckedIndex>>,<<CheckedValue>>] needs_type_check:true
+ /// CHECK-DAG: <<ArraySet:v\d+>> ArraySet [<<CheckedArray>>,<<CheckedIndex>>,<<CheckedValue>>] needs_type_check:true can_trigger_gc:true
/// CHECK-START: void Main.testArraySetCheckCastNull(Main$Element[]) instruction_simplifier (after)
/// CHECK-NOT: CheckCast
@@ -47,7 +47,7 @@
/// CHECK-DAG: <<CheckedArray:l\d+>> NullCheck [<<Array>>]
/// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<CheckedArray>>]
/// CHECK-DAG: <<CheckedIndex:i\d+>> BoundsCheck [<<Index>>,<<Length>>]
- /// CHECK-DAG: <<ArraySet:v\d+>> ArraySet [<<CheckedArray>>,<<CheckedIndex>>,<<CheckedValue>>] needs_type_check:true
+ /// CHECK-DAG: <<ArraySet:v\d+>> ArraySet [<<CheckedArray>>,<<CheckedIndex>>,<<CheckedValue>>] needs_type_check:true can_trigger_gc:true
/// CHECK-START: void Main.testArraySetCheckCastNull(Main$Element[]) prepare_for_register_allocation (after)
/// CHECK: <<Array:l\d+>> ParameterValue
@@ -55,7 +55,7 @@
/// CHECK-DAG: <<Null:l\d+>> NullConstant
/// CHECK-DAG: <<Class:l\d+>> LoadClass
/// CHECK-DAG: <<Length:i\d+>> ArrayLength [<<Array>>]
- /// CHECK-DAG: <<ArraySet:v\d+>> ArraySet [<<Array>>,<<Index>>,<<Null>>] needs_type_check:false
+ /// CHECK-DAG: <<ArraySet:v\d+>> ArraySet [<<Array>>,<<Index>>,<<Null>>] needs_type_check:false can_trigger_gc:false
static void testArraySetCheckCastNull(Element[] elements) {
Object object = null;
diff --git a/test/593-checker-long-2-float-regression/src/Main.java b/test/593-checker-long-2-float-regression/src/Main.java
index b31cbde..d55d0fd 100644
--- a/test/593-checker-long-2-float-regression/src/Main.java
+++ b/test/593-checker-long-2-float-regression/src/Main.java
@@ -16,7 +16,6 @@
public class Main {
- static boolean doThrow = false;
static long longValue;
public static void assertEquals(float expected, float result) {
@@ -35,10 +34,9 @@
/// CHECK-DAG: Return [<<Convert>>]
static float $noinline$longToFloat() {
- if (doThrow) { throw new Error(); }
longValue = $inline$returnConst();
// This call prevents D8 from replacing the result of the sget instruction
- // in line 43 by the result of the call to $inline$returnConst() in line 39.
+ // in line 41 by the result of the call to $inline$returnConst() in line 39.
$inline$preventRedundantFieldLoadEliminationInD8();
return (float) longValue;
}
diff --git a/test/594-load-string-regression/src/Main.java b/test/594-load-string-regression/src/Main.java
index 0b9f7b5..5903130 100644
--- a/test/594-load-string-regression/src/Main.java
+++ b/test/594-load-string-regression/src/Main.java
@@ -15,8 +15,6 @@
*/
public class Main {
- static boolean doThrow = false;
-
// Note: We're not doing checker tests as we cannot do them specifically for a non-PIC
// configuration. The check here would be "prepare_for_register_allocation (before)"
// CHECK: LoadClass
@@ -28,8 +26,6 @@
// CHECK-NEXT: NewInstance
// but the order of instructions for non-PIC mode is different.
public static int $noinline$test() {
- if (doThrow) { throw new Error(); }
-
int r = 0x12345678;
do {
// LICM pulls the LoadClass and ClinitCheck out of the loop, leaves NewInstance in the loop.
@@ -67,11 +63,7 @@
}
class Helper {
- static boolean doThrow = false;
-
public void $noinline$printString(String s) {
- if (doThrow) { throw new Error(); }
-
System.out.println("String: \"" + s + "\"");
}
}
diff --git a/test/595-profile-saving/run b/test/595-profile-saving/run
deleted file mode 100644
index 851be09..0000000
--- a/test/595-profile-saving/run
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/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.
-
-# Use
-# --compiler-filter=quicken to make sure that the test is not compiled AOT
-# and to make sure the test is not compiled when loaded (by PathClassLoader)
-# -Xjitsaveprofilinginfo to enable profile saving
-# -Xusejit:false to disable jit and only test profiles.
-# -Xjitinitialsize:32M to prevent profiling info creation failure.
-exec ${RUN} \
- -Xcompiler-option --compiler-filter=quicken \
- --runtime-option '-Xcompiler-option --compiler-filter=quicken' \
- --runtime-option -Xjitinitialsize:32M \
- --runtime-option -Xjitsaveprofilinginfo \
- --runtime-option -Xusejit:false \
- --runtime-option -Xps-profile-boot-class-path \
- "${@}"
diff --git a/test/595-profile-saving/run.py b/test/595-profile-saving/run.py
new file mode 100644
index 0000000..96de281
--- /dev/null
+++ b/test/595-profile-saving/run.py
@@ -0,0 +1,34 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Use
+ # --compiler-filter=quicken to make sure that the test is not compiled AOT
+ # and to make sure the test is not compiled when loaded (by PathClassLoader)
+ # -Xjitsaveprofilinginfo to enable profile saving
+ # -Xusejit:false to disable jit and only test profiles.
+ # -Xjitinitialsize:32M to prevent profiling info creation failure.
+ ctx.default_run(
+ args,
+ Xcompiler_option=["--compiler-filter=quicken"],
+ runtime_option=[
+ "-Xcompiler-option --compiler-filter=quicken",
+ "-Xjitinitialsize:32M",
+ "-Xjitsaveprofilinginfo",
+ "-Xusejit:false",
+ "-Xps-profile-boot-class-path",
+ ])
diff --git a/test/596-app-images/run b/test/596-app-images/run
deleted file mode 100644
index dbdcd1c..0000000
--- a/test/596-app-images/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2020 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.
-
-# We need a profile to tell dex2oat to include classes in the final app image
-exec ${RUN} --profile $@
diff --git a/test/596-app-images/run.py b/test/596-app-images/run.py
new file mode 100644
index 0000000..402fbb5
--- /dev/null
+++ b/test/596-app-images/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # We need a profile to tell dex2oat to include classes in the final app image
+ ctx.default_run(args, profile=True)
diff --git a/test/596-checker-dead-phi/smali/IrreducibleLoop.smali b/test/596-checker-dead-phi/smali/IrreducibleLoop.smali
index bab2ba9..9f822bf 100644
--- a/test/596-checker-dead-phi/smali/IrreducibleLoop.smali
+++ b/test/596-checker-dead-phi/smali/IrreducibleLoop.smali
@@ -20,18 +20,19 @@
# not adjacent. This revealed a bug in our SSA builder, where a dead loop phi would
# be replaced by its incoming input during SsaRedundantPhiElimination.
-# Check that the outer loop suspend check environment only has the parameter vreg.
-## CHECK-START: int IrreducibleLoop.liveness(int) builder (after)
-## CHECK-DAG: <<Phi:i\d+>> Phi reg:4 loop:{{B\d+}} irreducible:false
-## CHECK-DAG: SuspendCheck env:[[_,_,_,_,<<Phi>>]] loop:{{B\d+}} irreducible:false
+# Check that the outer loop suspend check environment only has the two parameter vregs.
+## CHECK-START: int IrreducibleLoop.liveness(int, int) builder (after)
+## CHECK-DAG: <<Phi1:i\d+>> Phi reg:3 loop:{{B\d+}} irreducible:false
+## CHECK-DAG: <<Phi2:i\d+>> Phi reg:4 loop:{{B\d+}} irreducible:false
+## CHECK-DAG: SuspendCheck env:[[_,_,_,<<Phi1>>,<<Phi2>>]] loop:{{B\d+}} irreducible:false
# Check that the linear order has non-adjacent loop blocks.
-## CHECK-START: int IrreducibleLoop.liveness(int) liveness (after)
+## CHECK-START: int IrreducibleLoop.liveness(int, int) liveness (after)
## CHECK-DAG: Mul liveness:<<LPreEntry2:\d+>>
## CHECK-DAG: Add liveness:<<LBackEdge1:\d+>>
## CHECK-EVAL: <<LBackEdge1>> < <<LPreEntry2>>
-.method public static liveness(I)I
+.method public static liveness(II)I
.registers 5
const-string v1, "MyString"
@@ -50,8 +51,9 @@
if-ne v2, v3, :pre_header2
:pre_entry2
- # Add a marker on the irreducible loop entry.
- mul-int/2addr p0, p0
+ # Add a marker on the irreducible loop entry. Here we use p1 because p0 is a
+ # known constant and we eliminate the Mul otherwise.
+ mul-int/2addr p1, p1
goto :back_edge2
:back_edge2
@@ -61,8 +63,9 @@
if-eqz p0, :back_edge2
:back_edge1
- # Add a marker on the outer loop back edge.
- add-int/2addr p0, p0
+ # Add a marker on the outer loop back edge. Here we use p1 because p0 is a
+ # known constant and we eliminate the Add otherwise.
+ add-int/2addr p1, p1
# Set a wide register, to have v1 undefined at the back edge.
const-wide/16 v0, 0x1
goto :header1
diff --git a/test/596-checker-dead-phi/src/Main.java b/test/596-checker-dead-phi/src/Main.java
index f3a55df..ec2fbe5 100644
--- a/test/596-checker-dead-phi/src/Main.java
+++ b/test/596-checker-dead-phi/src/Main.java
@@ -17,13 +17,13 @@
import java.lang.reflect.Method;
public class Main {
- public static void main(String[] args) throws Exception {
- Class<?> c = Class.forName("IrreducibleLoop");
- // Note that we don't actually enter the loops in the 'liveness'
- // method, so this is just a verification that that part of the code we
- // generated for that method is correct.
- Method m = c.getMethod("liveness", int.class);
- Object[] arguments = { 42 };
- System.out.println(m.invoke(null, arguments));
- }
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("IrreducibleLoop");
+ // Note that we don't actually enter the loops in the 'liveness'
+ // method, so this is just a verification that that part of the code we
+ // generated for that method is correct.
+ Method m = c.getMethod("liveness", int.class, int.class);
+ Object[] arguments = {42, 12};
+ System.out.println(m.invoke(null, arguments));
+ }
}
diff --git a/test/597-app-images-same-classloader/run b/test/597-app-images-same-classloader/run
deleted file mode 100644
index 496273f..0000000
--- a/test/597-app-images-same-classloader/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2020 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.
-
-# We need a profile to tell dex2oat to include classes in the final app image
-exec ${RUN} --profile --secondary-class-loader-context "PCL[$DEX_LOCATION/$TEST_NAME.jar]" $@
diff --git a/test/597-app-images-same-classloader/run.py b/test/597-app-images-same-classloader/run.py
new file mode 100644
index 0000000..a335777
--- /dev/null
+++ b/test/597-app-images-same-classloader/run.py
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # We need a profile to tell dex2oat to include classes in the final app image
+ pcl = f"PCL[{ctx.env.DEX_LOCATION}/{ctx.env.TEST_NAME}.jar]"
+ ctx.default_run(args, profile=True, secondary_class_loader_context=pcl)
diff --git a/test/597-deopt-busy-loop/run b/test/597-deopt-busy-loop/run
deleted file mode 100644
index bc04498..0000000
--- a/test/597-deopt-busy-loop/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# We want to run in debuggable mode and compiled.
-exec ${RUN} --jit -Xcompiler-option --debuggable "${@}"
diff --git a/test/597-deopt-busy-loop/run.py b/test/597-deopt-busy-loop/run.py
new file mode 100644
index 0000000..6107702
--- /dev/null
+++ b/test/597-deopt-busy-loop/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # We want to run in debuggable mode and compiled.
+ ctx.default_run(args, jit=True, Xcompiler_option=["--debuggable"])
diff --git a/test/597-deopt-busy-loop/src/FloatLoop.java b/test/597-deopt-busy-loop/src/FloatLoop.java
index 57667a6..7aadce4 100644
--- a/test/597-deopt-busy-loop/src/FloatLoop.java
+++ b/test/597-deopt-busy-loop/src/FloatLoop.java
@@ -51,6 +51,10 @@
}
}
+ // Create an empty int[] to force loading the int[] class before compiling $noinline$busyLoop.
+ // This makes sure the compiler can properly type int[] and not bail.
+ static int[] emptyArray = new int[0];
+
public void $noinline$busyLoop() {
Main.assertIsManaged();
diff --git a/test/597-deopt-invoke-stub/run b/test/597-deopt-invoke-stub/run
deleted file mode 100644
index 990c30e..0000000
--- a/test/597-deopt-invoke-stub/run
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# In order to test deoptimizing at quick-to-interpreter bridge,
-# we want to run in debuggable mode with jit compilation.
-# We also bump up the jit threshold to 10000 to make sure that the method
-# that should be interpreted is not compiled.
-exec ${RUN} "${@}" --jit --runtime-option -Xjitthreshold:10000 -Xcompiler-option --debuggable
diff --git a/test/597-deopt-invoke-stub/run.py b/test/597-deopt-invoke-stub/run.py
new file mode 100644
index 0000000..b52c177
--- /dev/null
+++ b/test/597-deopt-invoke-stub/run.py
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # In order to test deoptimizing at quick-to-interpreter bridge,
+ # we want to run in debuggable mode with jit compilation.
+ # We also bump up the jit threshold to 10000 to make sure that the method
+ # that should be interpreted is not compiled.
+ ctx.default_run(
+ args,
+ jit=True,
+ runtime_option=["-Xjitthreshold:10000"],
+ Xcompiler_option=["--debuggable"])
diff --git a/test/597-deopt-new-string/run b/test/597-deopt-new-string/run
deleted file mode 100644
index 9776ab3..0000000
--- a/test/597-deopt-new-string/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# We want to run in debuggable mode which keeps the call into StringFactory.newEmptyString().
-exec ${RUN} -Xcompiler-option --debuggable "${@}"
diff --git a/test/597-deopt-new-string/run.py b/test/597-deopt-new-string/run.py
new file mode 100644
index 0000000..fa958dc
--- /dev/null
+++ b/test/597-deopt-new-string/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # We want to run in debuggable mode which keeps the call into StringFactory.newEmptyString().
+ ctx.default_run(args, Xcompiler_option=["--debuggable"])
diff --git a/test/598-checker-irreducible-dominance/src/Main.java b/test/598-checker-irreducible-dominance/src/Main.java
index 4b1f490..cd38d3e 100644
--- a/test/598-checker-irreducible-dominance/src/Main.java
+++ b/test/598-checker-irreducible-dominance/src/Main.java
@@ -15,8 +15,8 @@
*/
public class Main {
- public static void main(String[] args) {
- // Nothing to run. This regression test merely makes sure the smali test
- // case successfully compiles.
- }
+ public static void main(String[] args) {
+ // Nothing to run. This regression test merely makes sure the smali test
+ // case successfully compiles.
+ }
}
diff --git a/test/599-checker-irreducible-loop/src/Main.java b/test/599-checker-irreducible-loop/src/Main.java
index 643e3a1..8a428a0 100644
--- a/test/599-checker-irreducible-loop/src/Main.java
+++ b/test/599-checker-irreducible-loop/src/Main.java
@@ -17,11 +17,11 @@
import java.lang.reflect.Method;
public class Main {
- public static void main(String[] args) throws Exception {
- Class<?> c = Class.forName("IrreducibleLoop");
- Method m = c.getMethod("test", int.class);
- Object[] arguments = { 42 };
- // Invoke the code just for validation purposes.
- System.out.println(m.invoke(null, arguments));
- }
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("IrreducibleLoop");
+ Method m = c.getMethod("test", int.class);
+ Object[] arguments = { 42 };
+ // Invoke the code just for validation purposes.
+ System.out.println(m.invoke(null, arguments));
+ }
}
diff --git a/test/612-jit-dex-cache/build.py b/test/612-jit-dex-cache/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/612-jit-dex-cache/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/612-jit-dex-cache/test-metadata.json b/test/612-jit-dex-cache/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/612-jit-dex-cache/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/613-inlining-dex-cache/build.py b/test/613-inlining-dex-cache/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/613-inlining-dex-cache/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/613-inlining-dex-cache/run b/test/613-inlining-dex-cache/run
deleted file mode 100644
index 9c1e7aa..0000000
--- a/test/613-inlining-dex-cache/run
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-flags="$@"
-# We need the dex files pre-verified to avoid running the verifier
-# at runtime which will update the dex cache.
-exec ${RUN} ${flags/verify-at-runtime/interpret-only}
diff --git a/test/613-inlining-dex-cache/run.py b/test/613-inlining-dex-cache/run.py
new file mode 100644
index 0000000..f0b061b
--- /dev/null
+++ b/test/613-inlining-dex-cache/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
diff --git a/test/613-inlining-dex-cache/test-metadata.json b/test/613-inlining-dex-cache/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/613-inlining-dex-cache/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/616-cha-abstract/run b/test/616-cha-abstract/run
deleted file mode 100644
index d8b4f0d..0000000
--- a/test/616-cha-abstract/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Run without an app image to prevent the classes to be loaded at startup.
-exec ${RUN} "${@}" --no-app-image
diff --git a/test/616-cha-abstract/run.py b/test/616-cha-abstract/run.py
new file mode 100644
index 0000000..1e797a8
--- /dev/null
+++ b/test/616-cha-abstract/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Run without an app image to prevent the classes to be loaded at startup.
+ ctx.default_run(args, app_image=False)
diff --git a/test/616-cha-abstract/src/Main.java b/test/616-cha-abstract/src/Main.java
index c8093e6..1a06337 100644
--- a/test/616-cha-abstract/src/Main.java
+++ b/test/616-cha-abstract/src/Main.java
@@ -15,145 +15,145 @@
*/
abstract class Base {
- abstract void foo(int i);
+ abstract void foo(int i);
- void printError(String msg) {
- System.out.println(msg);
- }
+ void printError(String msg) {
+ System.out.println(msg);
+ }
}
class Main1 extends Base {
- void foo(int i) {
- if (i != 1) {
- printError("error1");
+ void foo(int i) {
+ if (i != 1) {
+ printError("error1");
+ }
}
- }
}
class Main2 extends Main1 {
- void foo(int i) {
- if (i != 2) {
- printError("error2");
+ void foo(int i) {
+ if (i != 2) {
+ printError("error2");
+ }
}
- }
}
public class Main {
- static Base sMain1;
- static Base sMain2;
+ static Base sMain1;
+ static Base sMain2;
- static boolean sIsOptimizing = true;
- static boolean sHasJIT = true;
- static volatile boolean sOtherThreadStarted;
+ static boolean sIsOptimizing = true;
+ static boolean sHasJIT = true;
+ static volatile boolean sOtherThreadStarted;
- private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
- if (hasSingleImplementation(clazz, method_name) != b) {
- System.out.println(clazz + "." + method_name +
- " doesn't have single implementation value of " + b);
- }
- }
-
- // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
- // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
- // After Helper.createMain2() which links in Main2, live testOverride() on stack
- // should be deoptimized.
- static void testOverride(boolean createMain2, boolean wait, boolean setHasJIT) {
- if (setHasJIT) {
- if (isInterpreted()) {
- sHasJIT = false;
- }
- return;
- }
-
- if (createMain2 && (sIsOptimizing || sHasJIT)) {
- assertIsManaged();
- }
-
- sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
-
- if (createMain2) {
- // Wait for the other thread to start.
- while (!sOtherThreadStarted);
- // Create an Main2 instance and assign it to sMain2.
- // sMain1 is kept the same.
- sMain2 = Helper.createMain2();
- // Wake up the other thread.
- synchronized(Main.class) {
- Main.class.notify();
- }
- } else if (wait) {
- // This is the other thread.
- synchronized(Main.class) {
- sOtherThreadStarted = true;
- // Wait for Main2 to be linked and deoptimization is triggered.
- try {
- Main.class.wait();
- } catch (Exception e) {
+ private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
+ if (hasSingleImplementation(clazz, method_name) != b) {
+ System.out.println(clazz + "." + method_name +
+ " doesn't have single implementation value of " + b);
}
- }
}
- // There should be a deoptimization here right after Main2 is linked by
- // calling Helper.createMain2(), even though sMain1 didn't change.
- // The behavior here would be different if inline-cache is used, which
- // doesn't deoptimize since sMain1 still hits the type cache.
- sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
- if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
- // This method should be deoptimized right after Main2 is created.
- assertIsInterpreted();
+ // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
+ // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
+ // After Helper.createMain2() which links in Main2, live testOverride() on stack
+ // should be deoptimized.
+ static void testOverride(boolean createMain2, boolean wait, boolean setHasJIT) {
+ if (setHasJIT) {
+ if (isInterpreted()) {
+ sHasJIT = false;
+ }
+ return;
+ }
+
+ if (createMain2 && (sIsOptimizing || sHasJIT)) {
+ assertIsManaged();
+ }
+
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+
+ if (createMain2) {
+ // Wait for the other thread to start.
+ while (!sOtherThreadStarted);
+ // Create an Main2 instance and assign it to sMain2.
+ // sMain1 is kept the same.
+ sMain2 = Helper.createMain2();
+ // Wake up the other thread.
+ synchronized(Main.class) {
+ Main.class.notify();
+ }
+ } else if (wait) {
+ // This is the other thread.
+ synchronized(Main.class) {
+ sOtherThreadStarted = true;
+ // Wait for Main2 to be linked and deoptimization is triggered.
+ try {
+ Main.class.wait();
+ } catch (Exception e) {
+ }
+ }
+ }
+
+ // There should be a deoptimization here right after Main2 is linked by
+ // calling Helper.createMain2(), even though sMain1 didn't change.
+ // The behavior here would be different if inline-cache is used, which
+ // doesn't deoptimize since sMain1 still hits the type cache.
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+ if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
+ // This method should be deoptimized right after Main2 is created.
+ assertIsInterpreted();
+ }
+
+ if (sMain2 != null) {
+ sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
+ }
}
- if (sMain2 != null) {
- sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
- }
- }
+ // Test scenarios under which CHA-based devirtualization happens,
+ // and class loading that overrides a method can invalidate compiled code.
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
- // Test scenarios under which CHA-based devirtualization happens,
- // and class loading that overrides a method can invalidate compiled code.
- public static void main(String[] args) {
- System.loadLibrary(args[0]);
+ if (isInterpreted()) {
+ sIsOptimizing = false;
+ }
- if (isInterpreted()) {
- sIsOptimizing = false;
+ // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
+ sMain1 = new Main1();
+
+ ensureJitCompiled(Main.class, "testOverride");
+ testOverride(false, false, true);
+
+ if (sHasJIT && !sIsOptimizing) {
+ assertSingleImplementation(Base.class, "foo", true);
+ assertSingleImplementation(Main1.class, "foo", true);
+ } else {
+ // Main2 is verified ahead-of-time so it's linked in already.
+ }
+
+ // Create another thread that also calls sMain1.foo().
+ // Try to test suspend and deopt another thread.
+ new Thread() {
+ public void run() {
+ testOverride(false, true, false);
+ }
+ }.start();
+
+ // This will create Main2 instance in the middle of testOverride().
+ testOverride(true, false, false);
+ assertSingleImplementation(Base.class, "foo", false);
+ assertSingleImplementation(Main1.class, "foo", false);
}
- // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
- sMain1 = new Main1();
-
- ensureJitCompiled(Main.class, "testOverride");
- testOverride(false, false, true);
-
- if (sHasJIT && !sIsOptimizing) {
- assertSingleImplementation(Base.class, "foo", true);
- assertSingleImplementation(Main1.class, "foo", true);
- } else {
- // Main2 is verified ahead-of-time so it's linked in already.
- }
-
- // Create another thread that also calls sMain1.foo().
- // Try to test suspend and deopt another thread.
- new Thread() {
- public void run() {
- testOverride(false, true, false);
- }
- }.start();
-
- // This will create Main2 instance in the middle of testOverride().
- testOverride(true, false, false);
- assertSingleImplementation(Base.class, "foo", false);
- assertSingleImplementation(Main1.class, "foo", false);
- }
-
- private static native void ensureJitCompiled(Class<?> itf, String method_name);
- private static native void assertIsInterpreted();
- private static native void assertIsManaged();
- private static native boolean isInterpreted();
- private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
+ private static native void ensureJitCompiled(Class<?> itf, String method_name);
+ private static native void assertIsInterpreted();
+ private static native void assertIsManaged();
+ private static native boolean isInterpreted();
+ private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
}
// Put createMain2() in another class to avoid class loading due to verifier.
class Helper {
- static Main1 createMain2() {
- return new Main2();
- }
+ static Main1 createMain2() {
+ return new Main2();
+ }
}
diff --git a/test/616-cha-interface-default/build b/test/616-cha-interface-default/build
deleted file mode 100644
index d9654f8..0000000
--- a/test/616-cha-interface-default/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-build "$@" --experimental default-methods
diff --git a/test/616-cha-interface-default/build.py b/test/616-cha-interface-default/build.py
new file mode 100644
index 0000000..3e0ecd5
--- /dev/null
+++ b/test/616-cha-interface-default/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(api_level="default-methods")
diff --git a/test/616-cha-interface-default/run b/test/616-cha-interface-default/run
deleted file mode 100644
index d8b4f0d..0000000
--- a/test/616-cha-interface-default/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Run without an app image to prevent the classes to be loaded at startup.
-exec ${RUN} "${@}" --no-app-image
diff --git a/test/616-cha-interface-default/run.py b/test/616-cha-interface-default/run.py
new file mode 100644
index 0000000..1e797a8
--- /dev/null
+++ b/test/616-cha-interface-default/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Run without an app image to prevent the classes to be loaded at startup.
+ ctx.default_run(args, app_image=False)
diff --git a/test/616-cha-interface-default/src-multidex/Base.java b/test/616-cha-interface-default/src-multidex/Base.java
index 2cbcb50..e3222a1 100644
--- a/test/616-cha-interface-default/src-multidex/Base.java
+++ b/test/616-cha-interface-default/src-multidex/Base.java
@@ -15,27 +15,27 @@
*/
interface Base {
- default public int foo(int i) {
- if (i != 1) {
- return -2;
+ default public int foo(int i) {
+ if (i != 1) {
+ return -2;
+ }
+ return i + 10;
}
- return i + 10;
- }
- // Test default method that's not inlined.
- default public int $noinline$bar() {
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- return -1;
- }
+ // Test default method that's not inlined.
+ default public int $noinline$bar() {
+ System.out.print("");
+ System.out.print("");
+ System.out.print("");
+ System.out.print("");
+ System.out.print("");
+ System.out.print("");
+ System.out.print("");
+ System.out.print("");
+ return -1;
+ }
- default void printError(String msg) {
- System.out.println(msg);
- }
+ default void printError(String msg) {
+ System.out.println(msg);
+ }
}
diff --git a/test/616-cha-interface-default/src/Main.java b/test/616-cha-interface-default/src/Main.java
index ce02cf0..40b815a 100644
--- a/test/616-cha-interface-default/src/Main.java
+++ b/test/616-cha-interface-default/src/Main.java
@@ -18,159 +18,159 @@
}
class Main2 extends Main1 {
- public void foobar() {}
+ public void foobar() {}
}
class Main3 implements Base {
- public int foo(int i) {
- if (i != 3) {
- printError("error3");
+ public int foo(int i) {
+ if (i != 3) {
+ printError("error3");
+ }
+ return -(i + 10);
}
- return -(i + 10);
- }
}
public class Main {
- static Base sMain1;
- static Base sMain2;
- static Base sMain3;
+ static Base sMain1;
+ static Base sMain2;
+ static Base sMain3;
- static boolean sIsOptimizing = true;
- static boolean sHasJIT = true;
- static volatile boolean sOtherThreadStarted;
+ static boolean sIsOptimizing = true;
+ static boolean sHasJIT = true;
+ static volatile boolean sOtherThreadStarted;
- private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
- if (hasSingleImplementation(clazz, method_name) != b) {
- System.out.println(clazz + "." + method_name +
- " doesn't have single implementation value of " + b);
- }
- }
-
- static int getValue(Class<?> cls) {
- if (cls == Main1.class || cls == Main2.class) {
- return 1;
- }
- return 3;
- }
-
- // sMain1.foo()/sMain2.foo() will be always be Base.foo() before Main3 is loaded/linked.
- // So sMain1.foo() can be devirtualized to Base.foo() and be inlined.
- // After Helper.createMain3() which links in Main3, live testImplement() on stack
- // should be deoptimized.
- static void testImplement(boolean createMain3, boolean wait, boolean setHasJIT) {
- if (setHasJIT) {
- if (isInterpreted()) {
- sHasJIT = false;
- }
- return;
- }
-
- if (createMain3 && (sIsOptimizing || sHasJIT)) {
- assertIsManaged();
- }
-
- if (sMain1.foo(getValue(sMain1.getClass())) != 11) {
- System.out.println("11 expected.");
- }
- if (sMain1.$noinline$bar() != -1) {
- System.out.println("-1 expected.");
- }
- if (sMain2.foo(getValue(sMain2.getClass())) != 11) {
- System.out.println("11 expected.");
- }
-
- if (createMain3) {
- // Wait for the other thread to start.
- while (!sOtherThreadStarted);
- // Create an Main2 instance and assign it to sMain2.
- // sMain1 is kept the same.
- sMain3 = Helper.createMain3();
- // Wake up the other thread.
- synchronized(Main.class) {
- Main.class.notify();
- }
- } else if (wait) {
- // This is the other thread.
- synchronized(Main.class) {
- sOtherThreadStarted = true;
- // Wait for Main2 to be linked and deoptimization is triggered.
- try {
- Main.class.wait();
- } catch (Exception e) {
+ private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
+ if (hasSingleImplementation(clazz, method_name) != b) {
+ System.out.println(clazz + "." + method_name +
+ " doesn't have single implementation value of " + b);
}
- }
}
- // There should be a deoptimization here right after Main3 is linked by
- // calling Helper.createMain3(), even though sMain1 didn't change.
- // The behavior here would be different if inline-cache is used, which
- // doesn't deoptimize since sMain1 still hits the type cache.
- if (sMain1.foo(getValue(sMain1.getClass())) != 11) {
- System.out.println("11 expected.");
- }
- if ((createMain3 || wait) && sHasJIT && !sIsOptimizing) {
- // This method should be deoptimized right after Main3 is created.
- assertIsInterpreted();
+ static int getValue(Class<?> cls) {
+ if (cls == Main1.class || cls == Main2.class) {
+ return 1;
+ }
+ return 3;
}
- if (sMain3 != null) {
- if (sMain3.foo(getValue(sMain3.getClass())) != -13) {
- System.out.println("-13 expected.");
- }
- }
- }
+ // sMain1.foo()/sMain2.foo() will be always be Base.foo() before Main3 is loaded/linked.
+ // So sMain1.foo() can be devirtualized to Base.foo() and be inlined.
+ // After Helper.createMain3() which links in Main3, live testImplement() on stack
+ // should be deoptimized.
+ static void testImplement(boolean createMain3, boolean wait, boolean setHasJIT) {
+ if (setHasJIT) {
+ if (isInterpreted()) {
+ sHasJIT = false;
+ }
+ return;
+ }
- // Test scenarios under which CHA-based devirtualization happens,
- // and class loading that implements a method can invalidate compiled code.
- public static void main(String[] args) {
- System.loadLibrary(args[0]);
+ if (createMain3 && (sIsOptimizing || sHasJIT)) {
+ assertIsManaged();
+ }
- if (isInterpreted()) {
- sIsOptimizing = false;
+ if (sMain1.foo(getValue(sMain1.getClass())) != 11) {
+ System.out.println("11 expected.");
+ }
+ if (sMain1.$noinline$bar() != -1) {
+ System.out.println("-1 expected.");
+ }
+ if (sMain2.foo(getValue(sMain2.getClass())) != 11) {
+ System.out.println("11 expected.");
+ }
+
+ if (createMain3) {
+ // Wait for the other thread to start.
+ while (!sOtherThreadStarted);
+ // Create an Main2 instance and assign it to sMain2.
+ // sMain1 is kept the same.
+ sMain3 = Helper.createMain3();
+ // Wake up the other thread.
+ synchronized(Main.class) {
+ Main.class.notify();
+ }
+ } else if (wait) {
+ // This is the other thread.
+ synchronized(Main.class) {
+ sOtherThreadStarted = true;
+ // Wait for Main2 to be linked and deoptimization is triggered.
+ try {
+ Main.class.wait();
+ } catch (Exception e) {
+ }
+ }
+ }
+
+ // There should be a deoptimization here right after Main3 is linked by
+ // calling Helper.createMain3(), even though sMain1 didn't change.
+ // The behavior here would be different if inline-cache is used, which
+ // doesn't deoptimize since sMain1 still hits the type cache.
+ if (sMain1.foo(getValue(sMain1.getClass())) != 11) {
+ System.out.println("11 expected.");
+ }
+ if ((createMain3 || wait) && sHasJIT && !sIsOptimizing) {
+ // This method should be deoptimized right after Main3 is created.
+ assertIsInterpreted();
+ }
+
+ if (sMain3 != null) {
+ if (sMain3.foo(getValue(sMain3.getClass())) != -13) {
+ System.out.println("-13 expected.");
+ }
+ }
}
- // sMain1 is an instance of Main1.
- // sMain2 is an instance of Main2.
- // Neither Main1 nor Main2 override default method Base.foo().
- // Main3 hasn't bee loaded yet.
- sMain1 = new Main1();
- sMain2 = new Main2();
+ // Test scenarios under which CHA-based devirtualization happens,
+ // and class loading that implements a method can invalidate compiled code.
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
- ensureJitCompiled(Main.class, "testImplement");
- testImplement(false, false, true);
+ if (isInterpreted()) {
+ sIsOptimizing = false;
+ }
- if (sHasJIT && !sIsOptimizing) {
- assertSingleImplementation(Base.class, "foo", true);
- assertSingleImplementation(Main1.class, "foo", true);
- } else {
- // Main3 is verified ahead-of-time so it's linked in already.
+ // sMain1 is an instance of Main1.
+ // sMain2 is an instance of Main2.
+ // Neither Main1 nor Main2 override default method Base.foo().
+ // Main3 hasn't bee loaded yet.
+ sMain1 = new Main1();
+ sMain2 = new Main2();
+
+ ensureJitCompiled(Main.class, "testImplement");
+ testImplement(false, false, true);
+
+ if (sHasJIT && !sIsOptimizing) {
+ assertSingleImplementation(Base.class, "foo", true);
+ assertSingleImplementation(Main1.class, "foo", true);
+ } else {
+ // Main3 is verified ahead-of-time so it's linked in already.
+ }
+
+ // Create another thread that also calls sMain1.foo().
+ // Try to test suspend and deopt another thread.
+ new Thread() {
+ public void run() {
+ testImplement(false, true, false);
+ }
+ }.start();
+
+ // This will create Main3 instance in the middle of testImplement().
+ testImplement(true, false, false);
+ assertSingleImplementation(Base.class, "foo", false);
+ assertSingleImplementation(Main1.class, "foo", true);
+ assertSingleImplementation(sMain3.getClass(), "foo", true);
}
- // Create another thread that also calls sMain1.foo().
- // Try to test suspend and deopt another thread.
- new Thread() {
- public void run() {
- testImplement(false, true, false);
- }
- }.start();
-
- // This will create Main3 instance in the middle of testImplement().
- testImplement(true, false, false);
- assertSingleImplementation(Base.class, "foo", false);
- assertSingleImplementation(Main1.class, "foo", true);
- assertSingleImplementation(sMain3.getClass(), "foo", true);
- }
-
- private static native void ensureJitCompiled(Class<?> itf, String method_name);
- private static native void assertIsInterpreted();
- private static native void assertIsManaged();
- private static native boolean isInterpreted();
- private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
+ private static native void ensureJitCompiled(Class<?> itf, String method_name);
+ private static native void assertIsInterpreted();
+ private static native void assertIsManaged();
+ private static native boolean isInterpreted();
+ private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
}
// Put createMain3() in another class to avoid class loading due to verifier.
class Helper {
- static Base createMain3() {
- return new Main3();
- }
+ static Base createMain3() {
+ return new Main3();
+ }
}
diff --git a/test/616-cha-interface/run b/test/616-cha-interface/run
deleted file mode 100644
index d8b4f0d..0000000
--- a/test/616-cha-interface/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Run without an app image to prevent the classes to be loaded at startup.
-exec ${RUN} "${@}" --no-app-image
diff --git a/test/616-cha-interface/run.py b/test/616-cha-interface/run.py
new file mode 100644
index 0000000..1e797a8
--- /dev/null
+++ b/test/616-cha-interface/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Run without an app image to prevent the classes to be loaded at startup.
+ ctx.default_run(args, app_image=False)
diff --git a/test/616-cha-interface/src/Main.java b/test/616-cha-interface/src/Main.java
index c55ed6e..0c21ea0 100644
--- a/test/616-cha-interface/src/Main.java
+++ b/test/616-cha-interface/src/Main.java
@@ -15,159 +15,159 @@
*/
interface Base {
- void foo(int i);
- void $noinline$bar();
+ void foo(int i);
+ void $noinline$bar();
}
class Main1 implements Base {
- public void foo(int i) {
- if (i != 1) {
- printError("error1");
+ public void foo(int i) {
+ if (i != 1) {
+ printError("error1");
+ }
}
- }
- // Test rewriting invoke-interface into invoke-virtual when inlining fails.
- public void $noinline$bar() {
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- System.out.print("");
- }
+ // Test rewriting invoke-interface into invoke-virtual when inlining fails.
+ public void $noinline$bar() {
+ System.out.print("");
+ System.out.print("");
+ System.out.print("");
+ System.out.print("");
+ System.out.print("");
+ System.out.print("");
+ System.out.print("");
+ System.out.print("");
+ }
- void printError(String msg) {
- System.out.println(msg);
- }
+ void printError(String msg) {
+ System.out.println(msg);
+ }
}
class Main2 extends Main1 {
- public void foo(int i) {
- if (i != 2) {
- printError("error2");
+ public void foo(int i) {
+ if (i != 2) {
+ printError("error2");
+ }
}
- }
}
public class Main {
- static Base sMain1;
- static Base sMain2;
+ static Base sMain1;
+ static Base sMain2;
- static boolean sIsOptimizing = true;
- static boolean sHasJIT = true;
- static volatile boolean sOtherThreadStarted;
+ static boolean sIsOptimizing = true;
+ static boolean sHasJIT = true;
+ static volatile boolean sOtherThreadStarted;
- private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
- if (hasSingleImplementation(clazz, method_name) != b) {
- System.out.println(clazz + "." + method_name +
- " doesn't have single implementation value of " + b);
- }
- }
-
- // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
- // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
- // After Helper.createMain2() which links in Main2, live testImplement() on stack
- // should be deoptimized.
- static void testImplement(boolean createMain2, boolean wait, boolean setHasJIT) {
- if (setHasJIT) {
- if (isInterpreted()) {
- sHasJIT = false;
- }
- return;
- }
-
- if (createMain2 && (sIsOptimizing || sHasJIT)) {
- assertIsManaged();
- }
-
- sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
- sMain1.$noinline$bar();
-
- if (createMain2) {
- // Wait for the other thread to start.
- while (!sOtherThreadStarted);
- // Create an Main2 instance and assign it to sMain2.
- // sMain1 is kept the same.
- sMain2 = Helper.createMain2();
- // Wake up the other thread.
- synchronized(Main.class) {
- Main.class.notify();
- }
- } else if (wait) {
- // This is the other thread.
- synchronized(Main.class) {
- sOtherThreadStarted = true;
- // Wait for Main2 to be linked and deoptimization is triggered.
- try {
- Main.class.wait();
- } catch (Exception e) {
+ private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
+ if (hasSingleImplementation(clazz, method_name) != b) {
+ System.out.println(clazz + "." + method_name +
+ " doesn't have single implementation value of " + b);
}
- }
}
- // There should be a deoptimization here right after Main2 is linked by
- // calling Helper.createMain2(), even though sMain1 didn't change.
- // The behavior here would be different if inline-cache is used, which
- // doesn't deoptimize since sMain1 still hits the type cache.
- sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
- if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
- // This method should be deoptimized right after Main2 is created.
- assertIsInterpreted();
+ // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
+ // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
+ // After Helper.createMain2() which links in Main2, live testImplement() on stack
+ // should be deoptimized.
+ static void testImplement(boolean createMain2, boolean wait, boolean setHasJIT) {
+ if (setHasJIT) {
+ if (isInterpreted()) {
+ sHasJIT = false;
+ }
+ return;
+ }
+
+ if (createMain2 && (sIsOptimizing || sHasJIT)) {
+ assertIsManaged();
+ }
+
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+ sMain1.$noinline$bar();
+
+ if (createMain2) {
+ // Wait for the other thread to start.
+ while (!sOtherThreadStarted);
+ // Create an Main2 instance and assign it to sMain2.
+ // sMain1 is kept the same.
+ sMain2 = Helper.createMain2();
+ // Wake up the other thread.
+ synchronized(Main.class) {
+ Main.class.notify();
+ }
+ } else if (wait) {
+ // This is the other thread.
+ synchronized(Main.class) {
+ sOtherThreadStarted = true;
+ // Wait for Main2 to be linked and deoptimization is triggered.
+ try {
+ Main.class.wait();
+ } catch (Exception e) {
+ }
+ }
+ }
+
+ // There should be a deoptimization here right after Main2 is linked by
+ // calling Helper.createMain2(), even though sMain1 didn't change.
+ // The behavior here would be different if inline-cache is used, which
+ // doesn't deoptimize since sMain1 still hits the type cache.
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+ if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
+ // This method should be deoptimized right after Main2 is created.
+ assertIsInterpreted();
+ }
+
+ if (sMain2 != null) {
+ sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
+ }
}
- if (sMain2 != null) {
- sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
- }
- }
+ // Test scenarios under which CHA-based devirtualization happens,
+ // and class loading that overrides a method can invalidate compiled code.
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
- // Test scenarios under which CHA-based devirtualization happens,
- // and class loading that overrides a method can invalidate compiled code.
- public static void main(String[] args) {
- System.loadLibrary(args[0]);
+ if (isInterpreted()) {
+ sIsOptimizing = false;
+ }
- if (isInterpreted()) {
- sIsOptimizing = false;
+ // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
+ sMain1 = new Main1();
+
+ ensureJitCompiled(Main.class, "testImplement");
+ testImplement(false, false, true);
+
+ if (sHasJIT && !sIsOptimizing) {
+ assertSingleImplementation(Base.class, "foo", true);
+ assertSingleImplementation(Main1.class, "foo", true);
+ } else {
+ // Main2 is verified ahead-of-time so it's linked in already.
+ }
+
+ // Create another thread that also calls sMain1.foo().
+ // Try to test suspend and deopt another thread.
+ new Thread() {
+ public void run() {
+ testImplement(false, true, false);
+ }
+ }.start();
+
+ // This will create Main2 instance in the middle of testImplement().
+ testImplement(true, false, false);
+ assertSingleImplementation(Base.class, "foo", false);
+ assertSingleImplementation(Main1.class, "foo", false);
}
- // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
- sMain1 = new Main1();
-
- ensureJitCompiled(Main.class, "testImplement");
- testImplement(false, false, true);
-
- if (sHasJIT && !sIsOptimizing) {
- assertSingleImplementation(Base.class, "foo", true);
- assertSingleImplementation(Main1.class, "foo", true);
- } else {
- // Main2 is verified ahead-of-time so it's linked in already.
- }
-
- // Create another thread that also calls sMain1.foo().
- // Try to test suspend and deopt another thread.
- new Thread() {
- public void run() {
- testImplement(false, true, false);
- }
- }.start();
-
- // This will create Main2 instance in the middle of testImplement().
- testImplement(true, false, false);
- assertSingleImplementation(Base.class, "foo", false);
- assertSingleImplementation(Main1.class, "foo", false);
- }
-
- private static native void ensureJitCompiled(Class<?> itf, String method_name);
- private static native void assertIsInterpreted();
- private static native void assertIsManaged();
- private static native boolean isInterpreted();
- private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
+ private static native void ensureJitCompiled(Class<?> itf, String method_name);
+ private static native void assertIsInterpreted();
+ private static native void assertIsManaged();
+ private static native boolean isInterpreted();
+ private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
}
// Put createMain2() in another class to avoid class loading due to verifier.
class Helper {
- static Main1 createMain2() {
- return new Main2();
- }
+ static Main1 createMain2() {
+ return new Main2();
+ }
}
diff --git a/test/616-cha-miranda/run b/test/616-cha-miranda/run
deleted file mode 100644
index d8b4f0d..0000000
--- a/test/616-cha-miranda/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Run without an app image to prevent the classes to be loaded at startup.
-exec ${RUN} "${@}" --no-app-image
diff --git a/test/616-cha-miranda/run.py b/test/616-cha-miranda/run.py
new file mode 100644
index 0000000..1e797a8
--- /dev/null
+++ b/test/616-cha-miranda/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Run without an app image to prevent the classes to be loaded at startup.
+ ctx.default_run(args, app_image=False)
diff --git a/test/616-cha-miranda/src/Main.java b/test/616-cha-miranda/src/Main.java
index 323e92e..eede844 100644
--- a/test/616-cha-miranda/src/Main.java
+++ b/test/616-cha-miranda/src/Main.java
@@ -15,149 +15,142 @@
*/
interface Iface {
- public void foo(int i);
+ public void foo(int i);
}
abstract class Base implements Iface {
- // Iface.foo(int) will be added as a miranda method.
+ // Iface.foo(int) will be added as a miranda method.
- void printError(String msg) {
- System.out.println(msg);
- }
+ void printError(String msg) {
+ System.out.println(msg);
+ }
}
class Main1 extends Base {
- public void foo(int i) {
- if (i != 1) {
- printError("error1");
+ public void foo(int i) {
+ if (i != 1) {
+ printError("error1");
+ }
}
- }
}
class Main2 extends Main1 {
- public void foo(int i) {
- if (i != 2) {
- printError("error2");
+ public void foo(int i) {
+ if (i != 2) {
+ printError("error2");
+ }
}
- }
}
public class Main {
- static Base sMain1;
- static Base sMain2;
+ static Base sMain1;
+ static Base sMain2;
- static boolean sIsOptimizing = true;
- static boolean sHasJIT = true;
- static volatile boolean sOtherThreadStarted;
+ static boolean sIsOptimizing = true;
+ static boolean sHasJIT = true;
+ static volatile boolean sOtherThreadStarted;
- private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
- if (hasSingleImplementation(clazz, method_name) != b) {
- System.out.println(clazz + "." + method_name +
- " doesn't have single implementation value of " + b);
- }
- }
-
- // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
- // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
- // After Helper.createMain2() which links in Main2, live testOverride() on stack
- // should be deoptimized.
- static void testOverride(boolean createMain2, boolean wait, boolean setHasJIT) {
- if (setHasJIT) {
- if (isInterpreted()) {
- sHasJIT = false;
- }
- return;
- }
-
- if (createMain2 && (sIsOptimizing || sHasJIT)) {
- assertIsManaged();
- }
-
- sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
-
- if (createMain2) {
- // Wait for the other thread to start.
- while (!sOtherThreadStarted);
- // Create an Main2 instance and assign it to sMain2.
- // sMain1 is kept the same.
- sMain2 = Helper.createMain2();
- // Wake up the other thread.
- synchronized(Main.class) {
- Main.class.notify();
- }
- } else if (wait) {
- // This is the other thread.
- synchronized(Main.class) {
- sOtherThreadStarted = true;
- // Wait for Main2 to be linked and deoptimization is triggered.
- try {
- Main.class.wait();
- } catch (Exception e) {
+ private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
+ if (hasSingleImplementation(clazz, method_name) != b) {
+ System.out.println(clazz + "." + method_name +
+ " doesn't have single implementation value of " + b);
}
- }
}
- // There should be a deoptimization here right after Main2 is linked by
- // calling Helper.createMain2(), even though sMain1 didn't change.
- // The behavior here would be different if inline-cache is used, which
- // doesn't deoptimize since sMain1 still hits the type cache.
- sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
- if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
- // This method should be deoptimized right after Main2 is created.
- assertIsInterpreted();
+ // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
+ // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
+ // After Helper.createMain2() which links in Main2, live testOverride() on stack
+ // should be deoptimized.
+ static void testOverride(boolean createMain2, boolean wait) {
+ if (createMain2 && (sIsOptimizing || sHasJIT)) {
+ assertIsManaged();
+ }
+
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+
+ if (createMain2) {
+ // Wait for the other thread to start.
+ while (!sOtherThreadStarted);
+ // Create an Main2 instance and assign it to sMain2.
+ // sMain1 is kept the same.
+ sMain2 = Helper.createMain2();
+ // Wake up the other thread.
+ synchronized(Main.class) {
+ Main.class.notify();
+ }
+ } else if (wait) {
+ // This is the other thread.
+ synchronized(Main.class) {
+ sOtherThreadStarted = true;
+ // Wait for Main2 to be linked and deoptimization is triggered.
+ try {
+ Main.class.wait();
+ } catch (Exception e) {
+ }
+ }
+ }
+
+ // There should be a deoptimization here right after Main2 is linked by
+ // calling Helper.createMain2(), even though sMain1 didn't change.
+ // The behavior here would be different if inline-cache is used, which
+ // doesn't deoptimize since sMain1 still hits the type cache.
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+ if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
+ // This method should be deoptimized right after Main2 is created.
+ assertIsInterpreted();
+ }
+
+ if (sMain2 != null) {
+ sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
+ }
}
- if (sMain2 != null) {
- sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
- }
- }
+ // Test scenarios under which CHA-based devirtualization happens,
+ // and class loading that overrides a method can invalidate compiled code.
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
- // Test scenarios under which CHA-based devirtualization happens,
- // and class loading that overrides a method can invalidate compiled code.
- public static void main(String[] args) {
- System.loadLibrary(args[0]);
+ sIsOptimizing = isAotCompiled(Main.class, "testOverride");
+ sHasJIT = hasJit();
- if (isInterpreted()) {
- sIsOptimizing = false;
+ // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
+ sMain1 = new Main1();
+
+ ensureJitCompiled(Main.class, "testOverride");
+
+ if (sHasJIT && !sIsOptimizing) {
+ assertSingleImplementation(Base.class, "foo", true);
+ assertSingleImplementation(Main1.class, "foo", true);
+ } else {
+ // Main2 is verified ahead-of-time so it's linked in already.
+ }
+
+ // Create another thread that also calls sMain1.foo().
+ // Try to test suspend and deopt another thread.
+ new Thread() {
+ public void run() {
+ testOverride(false, true);
+ }
+ }.start();
+
+ // This will create Main2 instance in the middle of testOverride().
+ testOverride(true, false);
+ assertSingleImplementation(Base.class, "foo", false);
+ assertSingleImplementation(Main1.class, "foo", false);
}
- // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
- sMain1 = new Main1();
-
- ensureJitCompiled(Main.class, "testOverride");
- testOverride(false, false, true);
-
- if (sHasJIT && !sIsOptimizing) {
- assertSingleImplementation(Base.class, "foo", true);
- assertSingleImplementation(Main1.class, "foo", true);
- } else {
- // Main2 is verified ahead-of-time so it's linked in already.
- }
-
- // Create another thread that also calls sMain1.foo().
- // Try to test suspend and deopt another thread.
- new Thread() {
- public void run() {
- testOverride(false, true, false);
- }
- }.start();
-
- // This will create Main2 instance in the middle of testOverride().
- testOverride(true, false, false);
- assertSingleImplementation(Base.class, "foo", false);
- assertSingleImplementation(Main1.class, "foo", false);
- }
-
- private static native void ensureJitCompiled(Class<?> itf, String method_name);
- private static native void assertIsInterpreted();
- private static native void assertIsManaged();
- private static native boolean isInterpreted();
- private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
+ private static native boolean hasJit();
+ private native static boolean isAotCompiled(Class<?> cls, String methodName);
+ private static native void ensureJitCompiled(Class<?> itf, String method_name);
+ private static native void assertIsInterpreted();
+ private static native void assertIsManaged();
+ private static native boolean isInterpreted();
+ private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
}
// Put createMain2() in another class to avoid class loading due to verifier.
class Helper {
- static Main1 createMain2() {
- return new Main2();
- }
+ static Main1 createMain2() {
+ return new Main2();
+ }
}
diff --git a/test/616-cha-native/src/Main.java b/test/616-cha-native/src/Main.java
index 53a463c..145cc42 100644
--- a/test/616-cha-native/src/Main.java
+++ b/test/616-cha-native/src/Main.java
@@ -15,19 +15,19 @@
*/
abstract class A {
- public abstract void foo();
+ public abstract void foo();
}
class B extends A {
- public native void foo();
+ public native void foo();
}
class C extends B {
- public void foo() {}
+ public void foo() {}
}
public class Main {
- public static void main(String[] args) {
- System.loadLibrary(args[0]);
- }
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+ }
}
diff --git a/test/616-cha-proxy-method-inline/run b/test/616-cha-proxy-method-inline/run
deleted file mode 100644
index d8b4f0d..0000000
--- a/test/616-cha-proxy-method-inline/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Run without an app image to prevent the classes to be loaded at startup.
-exec ${RUN} "${@}" --no-app-image
diff --git a/test/616-cha-proxy-method-inline/run.py b/test/616-cha-proxy-method-inline/run.py
new file mode 100644
index 0000000..1e797a8
--- /dev/null
+++ b/test/616-cha-proxy-method-inline/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Run without an app image to prevent the classes to be loaded at startup.
+ ctx.default_run(args, app_image=False)
diff --git a/test/616-cha-proxy-method-inline/src-multidex/Foo.java b/test/616-cha-proxy-method-inline/src-multidex/Foo.java
index 9deca3e..135214d 100644
--- a/test/616-cha-proxy-method-inline/src-multidex/Foo.java
+++ b/test/616-cha-proxy-method-inline/src-multidex/Foo.java
@@ -15,5 +15,5 @@
*/
interface Foo {
- public Object bar(Object obj);
+ public Object bar(Object obj);
}
diff --git a/test/616-cha-proxy-method-inline/src/Main.java b/test/616-cha-proxy-method-inline/src/Main.java
index be7bc82..627c15c 100644
--- a/test/616-cha-proxy-method-inline/src/Main.java
+++ b/test/616-cha-proxy-method-inline/src/Main.java
@@ -18,53 +18,51 @@
import java.lang.reflect.InvocationTargetException;
class DebugProxy implements java.lang.reflect.InvocationHandler {
- private Object obj;
- static Class<?>[] interfaces = {Foo.class};
+ private Object obj;
+ static Class<?>[] interfaces = {Foo.class};
- public static Object newInstance(Object obj) {
- return java.lang.reflect.Proxy.newProxyInstance(
- Foo.class.getClassLoader(),
- interfaces,
- new DebugProxy(obj));
- }
-
- private DebugProxy(Object obj) {
- this.obj = obj;
- }
-
- public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
- Object result;
- if (obj == null) {
- return null;
+ public static Object newInstance(Object obj) {
+ return java.lang.reflect.Proxy.newProxyInstance(
+ Foo.class.getClassLoader(), interfaces, new DebugProxy(obj));
}
- try {
- System.out.println("before invoking method " + m.getName());
- result = m.invoke(obj, args);
- } catch (InvocationTargetException e) {
- throw e.getTargetException();
- } catch (Exception e) {
- throw new RuntimeException("unexpected invocation exception: " + e.getMessage());
- } finally {
- System.out.println("after invoking method " + m.getName());
+
+ private DebugProxy(Object obj) {
+ this.obj = obj;
}
- return result;
- }
+
+ public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
+ Object result;
+ if (obj == null) {
+ return null;
+ }
+ try {
+ System.out.println("before invoking method " + m.getName());
+ result = m.invoke(obj, args);
+ } catch (InvocationTargetException e) {
+ throw e.getTargetException();
+ } catch (Exception e) {
+ throw new RuntimeException("unexpected invocation exception: " + e.getMessage());
+ } finally {
+ System.out.println("after invoking method " + m.getName());
+ }
+ return result;
+ }
}
public class Main {
- public static void call(Foo foo) {
- if (foo == null) {
- return;
+ public static void call(Foo foo) {
+ if (foo == null) {
+ return;
+ }
+ foo.bar(null);
}
- foo.bar(null);
- }
- public static void main(String[] args) {
- System.loadLibrary(args[0]);
- Foo foo = (Foo)DebugProxy.newInstance(null);
- ensureJitCompiled(Main.class, "call");
- call(foo);
- }
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+ Foo foo = (Foo)DebugProxy.newInstance(null);
+ ensureJitCompiled(Main.class, "call");
+ call(foo);
+ }
- private static native void ensureJitCompiled(Class<?> itf, String method_name);
+ private static native void ensureJitCompiled(Class<?> itf, String method_name);
}
diff --git a/test/616-cha-regression-proxy-method/src/Main.java b/test/616-cha-regression-proxy-method/src/Main.java
index 176f80b..14bc7a3 100644
--- a/test/616-cha-regression-proxy-method/src/Main.java
+++ b/test/616-cha-regression-proxy-method/src/Main.java
@@ -19,113 +19,113 @@
import java.lang.reflect.Proxy;
class Main1 {
- void foo(int i) {
- if (i != 1) {
- printError("error1");
+ void foo(int i) {
+ if (i != 1) {
+ printError("error1");
+ }
}
- }
- void printError(String msg) {
- System.out.println(msg);
- }
+ void printError(String msg) {
+ System.out.println(msg);
+ }
}
class Main2 extends Main1 {
- void foo(int i) {
- if (i != 2) {
- printError("error2");
+ void foo(int i) {
+ if (i != 2) {
+ printError("error2");
+ }
}
- }
}
class Proxied implements Runnable {
- public void run() {
- synchronized(Main.class) {
- Main.sOtherThreadStarted = true;
- // Wait for Main2 to be linked and deoptimization is triggered.
- try {
- Main.class.wait();
- } catch (Exception e) {
- }
+ public void run() {
+ synchronized(Main.class) {
+ Main.sOtherThreadStarted = true;
+ // Wait for Main2 to be linked and deoptimization is triggered.
+ try {
+ Main.class.wait();
+ } catch (Exception e) {
+ }
+ }
}
- }
}
class MyInvocationHandler implements InvocationHandler {
- private final Proxied proxied;
+ private final Proxied proxied;
- public MyInvocationHandler(Proxied proxied) {
- this.proxied = proxied;
- }
+ public MyInvocationHandler(Proxied proxied) {
+ this.proxied = proxied;
+ }
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- return method.invoke(proxied, args);
- }
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ return method.invoke(proxied, args);
+ }
}
public class Main {
- static Main1 sMain1;
- static Main1 sMain2;
- static volatile boolean sOtherThreadStarted;
+ static Main1 sMain1;
+ static Main1 sMain2;
+ static volatile boolean sOtherThreadStarted;
- // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
- // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
- // After Helper.createMain2() which links in Main2, live testOverride() on stack
- // should be deoptimized.
- static void testOverride() {
- sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+ // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
+ // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
+ // After Helper.createMain2() which links in Main2, live testOverride() on stack
+ // should be deoptimized.
+ static void testOverride() {
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
- // Wait for the other thread to start.
- while (!sOtherThreadStarted);
- // Create an Main2 instance and assign it to sMain2.
- // sMain1 is kept the same.
- sMain2 = Helper.createMain2();
- // Wake up the other thread.
- synchronized(Main.class) {
- Main.class.notify();
+ // Wait for the other thread to start.
+ while (!sOtherThreadStarted);
+ // Create an Main2 instance and assign it to sMain2.
+ // sMain1 is kept the same.
+ sMain2 = Helper.createMain2();
+ // Wake up the other thread.
+ synchronized(Main.class) {
+ Main.class.notify();
+ }
+
+ // There should be a deoptimization here right after Main2 is linked by
+ // calling Helper.createMain2(), even though sMain1 didn't change.
+ // The behavior here would be different if inline-cache is used, which
+ // doesn't deoptimize since sMain1 still hits the type cache.
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+ if (sMain2 != null) {
+ sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
+ }
}
- // There should be a deoptimization here right after Main2 is linked by
- // calling Helper.createMain2(), even though sMain1 didn't change.
- // The behavior here would be different if inline-cache is used, which
- // doesn't deoptimize since sMain1 still hits the type cache.
- sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
- if (sMain2 != null) {
- sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
+ // Test scenarios under which CHA-based devirtualization happens,
+ // and class loading that overrides a method can invalidate compiled code.
+ // Also create a proxy method such that a proxy method's frame is visited
+ // during stack walking.
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+ // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
+ sMain1 = new Main1();
+
+ // Create another thread that calls a proxy method.
+ new Thread() {
+ public void run() {
+ Runnable proxy = (Runnable)Proxy.newProxyInstance(
+ Proxied.class.getClassLoader(),
+ new Class[] { Runnable.class },
+ new MyInvocationHandler(new Proxied()));
+ proxy.run();
+ }
+ }.start();
+
+ ensureJitCompiled(Main.class, "testOverride");
+ // This will create Main2 instance in the middle of testOverride().
+ testOverride();
}
- }
- // Test scenarios under which CHA-based devirtualization happens,
- // and class loading that overrides a method can invalidate compiled code.
- // Also create a proxy method such that a proxy method's frame is visited
- // during stack walking.
- public static void main(String[] args) {
- System.loadLibrary(args[0]);
- // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
- sMain1 = new Main1();
-
- // Create another thread that calls a proxy method.
- new Thread() {
- public void run() {
- Runnable proxy = (Runnable)Proxy.newProxyInstance(
- Proxied.class.getClassLoader(),
- new Class[] { Runnable.class },
- new MyInvocationHandler(new Proxied()));
- proxy.run();
- }
- }.start();
-
- ensureJitCompiled(Main.class, "testOverride");
- // This will create Main2 instance in the middle of testOverride().
- testOverride();
- }
-
- private static native void ensureJitCompiled(Class<?> itf, String method_name);
+ private static native void ensureJitCompiled(Class<?> itf, String method_name);
}
// Put createMain2() in another class to avoid class loading due to verifier.
class Helper {
- static Main1 createMain2() {
- return new Main2();
- }
+ static Main1 createMain2() {
+ return new Main2();
+ }
}
diff --git a/test/616-cha-unloading/cha_unload.cc b/test/616-cha-unloading/cha_unload.cc
index f9d3874..d776023 100644
--- a/test/616-cha-unloading/cha_unload.cc
+++ b/test/616-cha-unloading/cha_unload.cc
@@ -22,7 +22,7 @@
#include "base/casts.h"
#include "class_linker.h"
#include "jit/jit.h"
-#include "linear_alloc.h"
+#include "linear_alloc-inl.h"
#include "nativehelper/ScopedUtfChars.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
@@ -79,8 +79,8 @@
// a reused one that covers the art_method pointer.
std::unique_ptr<LinearAlloc> alloc(Runtime::Current()->CreateLinearAlloc());
do {
- // Ask for a byte - it's sufficient to get an arena.
- alloc->Alloc(Thread::Current(), 1);
+ // Ask for a word - it's sufficient to get an arena.
+ alloc->Alloc(Thread::Current(), sizeof(void*), LinearAllocKind::kNoGCRoots);
} while (!alloc->Contains(ptr));
}
diff --git a/test/616-cha-unloading/run b/test/616-cha-unloading/run
deleted file mode 100644
index d8b4f0d..0000000
--- a/test/616-cha-unloading/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Run without an app image to prevent the classes to be loaded at startup.
-exec ${RUN} "${@}" --no-app-image
diff --git a/test/616-cha-unloading/run.py b/test/616-cha-unloading/run.py
new file mode 100644
index 0000000..1e797a8
--- /dev/null
+++ b/test/616-cha-unloading/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Run without an app image to prevent the classes to be loaded at startup.
+ ctx.default_run(args, app_image=False)
diff --git a/test/616-cha/run b/test/616-cha/run
deleted file mode 100644
index 9c64c7d..0000000
--- a/test/616-cha/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Run without an app image to prevent the classes to be loaded at startup.
-exec ${RUN} "${@}" --no-app-image
diff --git a/test/616-cha/run.py b/test/616-cha/run.py
new file mode 100644
index 0000000..38164a3
--- /dev/null
+++ b/test/616-cha/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Run without an app image to prevent the classes to be loaded at startup.
+ ctx.default_run(args, app_image=False)
diff --git a/test/616-cha/src/Main.java b/test/616-cha/src/Main.java
index 39f47fa..d2d4bcf 100644
--- a/test/616-cha/src/Main.java
+++ b/test/616-cha/src/Main.java
@@ -15,239 +15,239 @@
*/
class Main1 {
- String getName() {
- return "Main1";
- }
-
- void printError(String msg) {
- System.out.println(msg);
- }
-
- void foo(int i) {
- if (i != 1) {
- printError("error1");
+ String getName() {
+ return "Main1";
}
- }
- int getValue1() {
- return 1;
- }
- int getValue2() {
- return 2;
- }
- int getValue3() {
- return 3;
- }
- int getValue4() {
- return 4;
- }
- int getValue5() {
- return 5;
- }
- int getValue6() {
- return 6;
- }
+ void printError(String msg) {
+ System.out.println(msg);
+ }
+
+ void foo(int i) {
+ if (i != 1) {
+ printError("error1");
+ }
+ }
+
+ int getValue1() {
+ return 1;
+ }
+ int getValue2() {
+ return 2;
+ }
+ int getValue3() {
+ return 3;
+ }
+ int getValue4() {
+ return 4;
+ }
+ int getValue5() {
+ return 5;
+ }
+ int getValue6() {
+ return 6;
+ }
}
class Main2 extends Main1 {
- String getName() {
- return "Main2";
- }
-
- void foo(int i) {
- if (i != 2) {
- printError("error2");
+ String getName() {
+ return "Main2";
}
- }
+
+ void foo(int i) {
+ if (i != 2) {
+ printError("error2");
+ }
+ }
}
class Main3 extends Main1 {
- String getName() {
- return "Main3";
- }
+ String getName() {
+ return "Main3";
+ }
}
public class Main {
- static Main1 sMain1;
- static Main1 sMain2;
+ static Main1 sMain1;
+ static Main1 sMain2;
- static boolean sIsOptimizing = true;
- static boolean sHasJIT = true;
- static volatile boolean sOtherThreadStarted;
+ static boolean sIsOptimizing = true;
+ static boolean sHasJIT = true;
+ static volatile boolean sOtherThreadStarted;
- // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
- // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
- // After Helper.createMain2() which links in Main2, live testOverride() on stack
- // should be deoptimized.
- static void testOverride(boolean createMain2, boolean wait, boolean setHasJIT) {
- if (setHasJIT) {
- if (isInterpreted()) {
- sHasJIT = false;
- }
- return;
- }
-
- if (createMain2 && (sIsOptimizing || sHasJIT)) {
- assertIsManaged();
- }
-
- sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
-
- if (createMain2) {
- // Wait for the other thread to start.
- while (!sOtherThreadStarted);
- // Create an Main2 instance and assign it to sMain2.
- // sMain1 is kept the same.
- sMain2 = Helper.createMain2();
- // Wake up the other thread.
- synchronized(Main.class) {
- Main.class.notify();
- }
- } else if (wait) {
- // This is the other thread.
- synchronized(Main.class) {
- sOtherThreadStarted = true;
- // Wait for Main2 to be linked and deoptimization is triggered.
- try {
- Main.class.wait();
- } catch (Exception e) {
+ // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
+ // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
+ // After Helper.createMain2() which links in Main2, live testOverride() on stack
+ // should be deoptimized.
+ static void testOverride(boolean createMain2, boolean wait, boolean setHasJIT) {
+ if (setHasJIT) {
+ if (isInterpreted()) {
+ sHasJIT = false;
+ }
+ return;
}
- }
+
+ if (createMain2 && (sIsOptimizing || sHasJIT)) {
+ assertIsManaged();
+ }
+
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+
+ if (createMain2) {
+ // Wait for the other thread to start.
+ while (!sOtherThreadStarted);
+ // Create an Main2 instance and assign it to sMain2.
+ // sMain1 is kept the same.
+ sMain2 = Helper.createMain2();
+ // Wake up the other thread.
+ synchronized(Main.class) {
+ Main.class.notify();
+ }
+ } else if (wait) {
+ // This is the other thread.
+ synchronized(Main.class) {
+ sOtherThreadStarted = true;
+ // Wait for Main2 to be linked and deoptimization is triggered.
+ try {
+ Main.class.wait();
+ } catch (Exception e) {
+ }
+ }
+ }
+
+ // There should be a deoptimization here right after Main2 is linked by
+ // calling Helper.createMain2(), even though sMain1 didn't change.
+ // The behavior here would be different if inline-cache is used, which
+ // doesn't deoptimize since sMain1 still hits the type cache.
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+ if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
+ // This method should be deoptimized right after Main2 is created.
+ assertIsInterpreted();
+ }
+
+ if (sMain2 != null) {
+ sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
+ }
}
- // There should be a deoptimization here right after Main2 is linked by
- // calling Helper.createMain2(), even though sMain1 didn't change.
- // The behavior here would be different if inline-cache is used, which
- // doesn't deoptimize since sMain1 still hits the type cache.
- sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
- if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
- // This method should be deoptimized right after Main2 is created.
- assertIsInterpreted();
+ static Main1[] sArray;
+
+ static long calcValue(Main1 m) {
+ return m.getValue1()
+ + m.getValue2() * 2
+ + m.getValue3() * 3
+ + m.getValue4() * 4
+ + m.getValue5() * 5
+ + m.getValue6() * 6;
}
- if (sMain2 != null) {
- sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
- }
- }
-
- static Main1[] sArray;
-
- static long calcValue(Main1 m) {
- return m.getValue1()
- + m.getValue2() * 2
- + m.getValue3() * 3
- + m.getValue4() * 4
- + m.getValue5() * 5
- + m.getValue6() * 6;
- }
-
- static long testNoOverrideLoop(int count) {
- long sum = 0;
- for (int i=0; i<count; i++) {
- sum += calcValue(sArray[0]);
- sum += calcValue(sArray[1]);
- sum += calcValue(sArray[2]);
- }
- return sum;
- }
-
- static void testNoOverride() {
- sArray = new Main1[3];
- sArray[0] = new Main1();
- sArray[1] = Helper.createMain2();
- sArray[2] = Helper.createMain3();
- long sum = 0;
- // Loop enough to get methods JITed.
- for (int i=0; i<100; i++) {
- testNoOverrideLoop(1);
- }
- ensureJitCompiled(Main.class, "testNoOverrideLoop");
- ensureJitCompiled(Main.class, "calcValue");
-
- long t1 = System.currentTimeMillis();
- sum = testNoOverrideLoop(100000);
- long t2 = System.currentTimeMillis();
- if (sum != 27300000L) {
- System.out.println("Unexpected result.");
- }
- }
-
- private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
- if (hasSingleImplementation(clazz, method_name) != b) {
- System.out.println(clazz + "." + method_name +
- " doesn't have single implementation value of " + b);
- }
- }
-
- // Test scenarios under which CHA-based devirtualization happens,
- // and class loading that overrides a method can invalidate compiled code.
- // Also test pure non-overriding case, which is more for checking generated
- // code form.
- public static void main(String[] args) {
- System.loadLibrary(args[0]);
-
- // CHeck some boot-image methods.
-
- // We would want to have this, but currently setting single-implementation in the boot image
- // does not work well with app images. b/34193647
- final boolean ARRAYLIST_SIZE_EXPECTED = false;
- assertSingleImplementation(java.util.ArrayList.class, "size", ARRAYLIST_SIZE_EXPECTED);
-
- // java.util.LinkedHashMap overrides get().
- assertSingleImplementation(java.util.HashMap.class, "get", false);
-
- // We don't set single-implementation modifier bit for final classes or methods
- // since we can devirtualize without CHA for those cases. However hasSingleImplementation()
- // should return true for those cases.
- assertSingleImplementation(java.lang.String.class, "charAt", true);
- assertSingleImplementation(java.lang.Thread.class, "join", true);
-
- if (isInterpreted()) {
- sIsOptimizing = false;
+ static long testNoOverrideLoop(int count) {
+ long sum = 0;
+ for (int i=0; i<count; i++) {
+ sum += calcValue(sArray[0]);
+ sum += calcValue(sArray[1]);
+ sum += calcValue(sArray[2]);
+ }
+ return sum;
}
- // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
- sMain1 = new Main1();
+ static void testNoOverride() {
+ sArray = new Main1[3];
+ sArray[0] = new Main1();
+ sArray[1] = Helper.createMain2();
+ sArray[2] = Helper.createMain3();
+ long sum = 0;
+ // Loop enough to get methods JITed.
+ for (int i=0; i<100; i++) {
+ testNoOverrideLoop(1);
+ }
+ ensureJitCompiled(Main.class, "testNoOverrideLoop");
+ ensureJitCompiled(Main.class, "calcValue");
- ensureJitCompiled(Main.class, "testOverride");
- testOverride(false, false, true);
-
- if (sHasJIT && !sIsOptimizing) {
- assertSingleImplementation(Main1.class, "foo", true);
- } else {
- // Main2 is verified ahead-of-time so it's linked in already.
+ long t1 = System.currentTimeMillis();
+ sum = testNoOverrideLoop(100000);
+ long t2 = System.currentTimeMillis();
+ if (sum != 27300000L) {
+ System.out.println("Unexpected result.");
+ }
}
- assertSingleImplementation(Main1.class, "getValue1", true);
- // Create another thread that also calls sMain1.foo().
- // Try to test suspend and deopt another thread.
- new Thread() {
- public void run() {
- testOverride(false, true, false);
- }
- }.start();
+ private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
+ if (hasSingleImplementation(clazz, method_name) != b) {
+ System.out.println(clazz + "." + method_name +
+ " doesn't have single implementation value of " + b);
+ }
+ }
- // This will create Main2 instance in the middle of testOverride().
- testOverride(true, false, false);
- assertSingleImplementation(Main1.class, "foo", false);
- assertSingleImplementation(Main1.class, "getValue1", true);
+ // Test scenarios under which CHA-based devirtualization happens,
+ // and class loading that overrides a method can invalidate compiled code.
+ // Also test pure non-overriding case, which is more for checking generated
+ // code form.
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
- testNoOverride();
- }
+ // CHeck some boot-image methods.
- private static native void ensureJitCompiled(Class<?> itf, String method_name);
- private static native void assertIsInterpreted();
- private static native void assertIsManaged();
- private static native boolean isInterpreted();
- private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
+ // We would want to have this, but currently setting single-implementation in the boot image
+ // does not work well with app images. b/34193647
+ final boolean ARRAYLIST_SIZE_EXPECTED = false;
+ assertSingleImplementation(java.util.ArrayList.class, "size", ARRAYLIST_SIZE_EXPECTED);
+
+ // java.util.LinkedHashMap overrides get().
+ assertSingleImplementation(java.util.HashMap.class, "get", false);
+
+ // We don't set single-implementation modifier bit for final classes or methods
+ // since we can devirtualize without CHA for those cases. However hasSingleImplementation()
+ // should return true for those cases.
+ assertSingleImplementation(java.lang.String.class, "charAt", true);
+ assertSingleImplementation(java.lang.Thread.class, "join", true);
+
+ if (isInterpreted()) {
+ sIsOptimizing = false;
+ }
+
+ // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
+ sMain1 = new Main1();
+
+ ensureJitCompiled(Main.class, "testOverride");
+ testOverride(false, false, true);
+
+ if (sHasJIT && !sIsOptimizing) {
+ assertSingleImplementation(Main1.class, "foo", true);
+ } else {
+ // Main2 is verified ahead-of-time so it's linked in already.
+ }
+ assertSingleImplementation(Main1.class, "getValue1", true);
+
+ // Create another thread that also calls sMain1.foo().
+ // Try to test suspend and deopt another thread.
+ new Thread() {
+ public void run() {
+ testOverride(false, true, false);
+ }
+ }.start();
+
+ // This will create Main2 instance in the middle of testOverride().
+ testOverride(true, false, false);
+ assertSingleImplementation(Main1.class, "foo", false);
+ assertSingleImplementation(Main1.class, "getValue1", true);
+
+ testNoOverride();
+ }
+
+ private static native void ensureJitCompiled(Class<?> itf, String method_name);
+ private static native void assertIsInterpreted();
+ private static native void assertIsManaged();
+ private static native boolean isInterpreted();
+ private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
}
// Put createMain2() in another class to avoid class loading due to verifier.
class Helper {
- static Main1 createMain2() {
- return new Main2();
- }
- static Main1 createMain3() {
- return new Main3();
- }
+ static Main1 createMain2() {
+ return new Main2();
+ }
+ static Main1 createMain3() {
+ return new Main3();
+ }
}
diff --git a/test/617-clinit-oome/src/Main.java b/test/617-clinit-oome/src/Main.java
index bab344f..005f0df 100644
--- a/test/617-clinit-oome/src/Main.java
+++ b/test/617-clinit-oome/src/Main.java
@@ -15,43 +15,43 @@
*/
public class Main {
- private static int exhaustJavaHeap(Object[] data, int index, int size) {
- Runtime.getRuntime().gc();
- while (size > 0) {
+ private static int exhaustJavaHeap(Object[] data, int index, int size) {
+ Runtime.getRuntime().gc();
+ while (size > 0) {
+ try {
+ data[index] = new byte[size];
+ index++;
+ } catch (OutOfMemoryError e) {
+ size /= 2;
+ }
+ }
+ return index;
+ }
+
+ public static void main(String[] args) {
+ Class klass = Other.class;
+ Object[] data = new Object[100000];
try {
- data[index] = new byte[size];
- index++;
+ System.out.println("Filling heap");
+
+ // Make sure that there is no reclaimable memory in the heap. Otherwise we may throw
+ // OOME to prevent GC thrashing, even if later allocations may succeed.
+ Runtime.getRuntime().gc();
+ System.runFinalization();
+ // NOTE: There is a GC invocation in the exhaustJavaHeap(). So we don't need one here.
+
+ int index = 0;
+ int initial_size = 256 * 1024 * 1024;
+ // Repeat to ensure there is no space left on the heap.
+ index = exhaustJavaHeap(data, index, initial_size);
+ index = exhaustJavaHeap(data, index, /*size*/ 4);
+ index = exhaustJavaHeap(data, index, /*size*/ 4);
+
+ // Initialize now that the heap is full.
+ Other.print();
} catch (OutOfMemoryError e) {
- size /= 2;
+ } catch (Exception e) {
+ System.out.println(e);
}
}
- return index;
- }
-
- public static void main(String[] args) {
- Class klass = Other.class;
- Object[] data = new Object[100000];
- try {
- System.out.println("Filling heap");
-
- // Make sure that there is no reclaimable memory in the heap. Otherwise we may throw
- // OOME to prevent GC thrashing, even if later allocations may succeed.
- Runtime.getRuntime().gc();
- System.runFinalization();
- // NOTE: There is a GC invocation in the exhaustJavaHeap(). So we don't need one here.
-
- int index = 0;
- int initial_size = 256 * 1024 * 1024;
- // Repeat to ensure there is no space left on the heap.
- index = exhaustJavaHeap(data, index, initial_size);
- index = exhaustJavaHeap(data, index, /*size*/ 4);
- index = exhaustJavaHeap(data, index, /*size*/ 4);
-
- // Initialize now that the heap is full.
- Other.print();
- } catch (OutOfMemoryError e) {
- } catch (Exception e) {
- System.out.println(e);
- }
- }
}
diff --git a/test/618-checker-induction/src/Main.java b/test/618-checker-induction/src/Main.java
index dd76e41..d15de18 100644
--- a/test/618-checker-induction/src/Main.java
+++ b/test/618-checker-induction/src/Main.java
@@ -19,930 +19,1027 @@
*/
public class Main {
- static int[] a = new int[10];
+ static int[] a = new int[10];
- static int[] novec = new int[20]; // to prevent vectorization
+ static int[] novec = new int[20]; // to prevent vectorization
- /// CHECK-START: void Main.deadSingleLoop() loop_optimization (before)
- /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
- //
- /// CHECK-START: void Main.deadSingleLoop() loop_optimization (after)
- /// CHECK-NOT: Phi
- static void deadSingleLoop() {
- for (int i = 0; i < 4; i++) {
- }
- }
-
- /// CHECK-START: void Main.deadSingleLoop() loop_optimization (before)
- /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
- //
- /// CHECK-START: void Main.deadSingleLoop() loop_optimization (after)
- /// CHECK-NOT: Phi
- static void deadSingleLoopN(int n) {
- for (int i = 0; i < n; i++) {
- }
- }
-
- /// CHECK-START: void Main.potentialInfiniteLoop(int) loop_optimization (before)
- /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
- //
- /// CHECK-START: void Main.potentialInfiniteLoop(int) loop_optimization (after)
- /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
- static void potentialInfiniteLoop(int n) {
- for (int i = 0; i <= n; i++) { // loops forever when n = MAX_INT
- }
- }
-
- /// CHECK-START: void Main.deadNestedLoops() loop_optimization (before)
- /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:<<Loop>>
- //
- /// CHECK-START: void Main.deadNestedLoops() loop_optimization (after)
- /// CHECK-NOT: Phi
- static void deadNestedLoops() {
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < 4; j++) {
- }
- }
- }
-
- /// CHECK-START: void Main.deadNestedAndFollowingLoops() loop_optimization (before)
- /// CHECK-DAG: Phi loop:<<Loop1:B\d+>> outer_loop:none
- /// CHECK-DAG: Phi loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
- /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:<<Loop2>>
- /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:<<Loop2>>
- /// CHECK-DAG: Phi loop:<<Loop3:B\d+>> outer_loop:<<Loop1>>
- /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:<<Loop3>>
- /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
- //
- /// CHECK-START: void Main.deadNestedAndFollowingLoops() loop_optimization (after)
- /// CHECK-NOT: Phi
- static void deadNestedAndFollowingLoops() {
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < 4; j++) {
- for (int k = 0; k < 4; k++) {
+ /// CHECK-START: void Main.deadSingleLoop() loop_optimization (before)
+ /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
+ //
+ /// CHECK-START: void Main.deadSingleLoop() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ static void deadSingleLoop() {
+ for (int i = 0; i < 4; i++) {
}
- for (int k = 0; k < 4; k++) {
+ }
+
+ /// CHECK-START: void Main.deadSingleLoop() loop_optimization (before)
+ /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
+ //
+ /// CHECK-START: void Main.deadSingleLoop() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ static void deadSingleLoopN(int n) {
+ for (int i = 0; i < n; i++) {
}
- }
- for (int j = 0; j < 4; j++) {
- for (int k = 0; k < 4; k++) {
+ }
+
+ /// CHECK-START: void Main.potentialInfiniteLoop(int) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
+ //
+ /// CHECK-START: void Main.potentialInfiniteLoop(int) loop_optimization (after)
+ /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
+ static void potentialInfiniteLoop(int n) {
+ for (int i = 0; i <= n; i++) { // loops forever when n = MAX_INT
}
- }
- }
- for (int i = 0; i < 4; i++) {
- }
- }
-
- /// CHECK-START: void Main.deadConditional(int) loop_optimization (before)
- /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
- //
- /// CHECK-START: void Main.deadConditional(int) loop_optimization (after)
- /// CHECK-NOT: Phi
- public static void deadConditional(int n) {
- int k = 0;
- int m = 0;
- for (int i = 0; i < n; i++) {
- if (i == 3)
- k = i;
- else
- m = i;
- }
- }
-
- /// CHECK-START: void Main.deadConditionalCycle(int) loop_optimization (before)
- /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
- //
- /// CHECK-START: void Main.deadConditionalCycle(int) loop_optimization (after)
- /// CHECK-NOT: Phi
- public static void deadConditionalCycle(int n) {
- int k = 0;
- int m = 0;
- for (int i = 0; i < n; i++) {
- if (i == 3)
- k--;
- else
- m++;
- }
- }
-
-
- /// CHECK-START: void Main.deadInduction() loop_optimization (before)
- /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
- //
- /// CHECK-START: void Main.deadInduction() loop_optimization (after)
- /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-NOT: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
- static void deadInduction() {
- int dead = 0;
- for (int i = 0; i < a.length; i++) {
- a[i] = novec[2 * i] + 1;
- dead += 5;
- }
- }
-
- /// CHECK-START: void Main.deadManyInduction() loop_optimization (before)
- /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
- //
- /// CHECK-START: void Main.deadManyInduction() loop_optimization (after)
- /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-NOT: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
- static void deadManyInduction() {
- int dead1 = 0, dead2 = 1, dead3 = 3;
- for (int i = 0; i < a.length; i++) {
- dead1 += 5;
- a[i] = novec[2 * i] + 2;
- dead2 += 10;
- dead3 += 100;
- }
- }
-
- /// CHECK-START: void Main.deadSequence() loop_optimization (before)
- /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
- //
- /// CHECK-START: void Main.deadSequence() loop_optimization (after)
- /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-NOT: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
- static void deadSequence() {
- int dead = 0;
- for (int i = 0; i < a.length; i++) {
- a[i] = novec[2 * i] + 3;
- // Increment value defined inside loop,
- // but sequence itself not used anywhere.
- dead += i;
- }
- }
-
- /// CHECK-START: void Main.deadCycleWithException(int) loop_optimization (before)
- /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
- /// CHECK-NOT: BoundsCheck
- //
- /// CHECK-START: void Main.deadCycleWithException(int) loop_optimization (after)
- /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-NOT: Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
- /// CHECK-NOT: ArrayGet loop:<<Loop>> outer_loop:none
- static void deadCycleWithException(int k) {
- int dead = 0;
- for (int i = 0; i < a.length; i++) {
- a[i] = novec[2 * i] + 4;
- // Increment value of dead cycle may throw exception. Dynamic
- // BCE takes care of the bounds check though, which enables
- // removing the ArrayGet after removing the dead cycle.
- dead += a[k];
- }
- }
-
- /// CHECK-START: int Main.closedFormInductionUp() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
- //
- /// CHECK-START: int Main.closedFormInductionUp() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.closedFormInductionUp() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 12395 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- static int closedFormInductionUp() {
- int closed = 12345;
- for (int i = 0; i < 10; i++) {
- closed += 5;
- }
- return closed; // only needs last value
- }
-
- /// CHECK-START: int Main.closedFormInductionInAndDown(int) loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
- //
- /// CHECK-START: int Main.closedFormInductionInAndDown(int) loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.closedFormInductionInAndDown(int) instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Par:i\d+>> ParameterValue loop:none
- /// CHECK-DAG: <<Int:i\d+>> IntConstant -50 loop:none
- /// CHECK-DAG: <<Add:i\d+>> Add [<<Int>>,<<Par>>] loop:none
- /// CHECK-DAG: Return [<<Add>>] loop:none
- static int closedFormInductionInAndDown(int closed) {
- for (int i = 0; i < 10; i++) {
- closed -= 5;
- }
- return closed; // only needs last value
- }
-
- /// CHECK-START: int Main.closedFormInductionTrivialIf() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Select loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
- //
- /// CHECK-START: int Main.closedFormInductionTrivialIf() loop_optimization (after)
- /// CHECK-NOT: Phi
- /// CHECK-NOT: Select
- //
- /// CHECK-START: int Main.closedFormInductionTrivialIf() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 81 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- static int closedFormInductionTrivialIf() {
- int closed = 11;
- for (int i = 0; i < 10; i++) {
- // Trivial if becomes trivial select at HIR level.
- // Make sure this is still recognized as induction.
- if (i < 5) {
- closed += 7;
- } else {
- closed += 7;
- }
- }
- return closed; // only needs last value
- }
-
- /// CHECK-START: int Main.closedFormNested() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop1:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop1>> outer_loop:none
- /// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
- /// CHECK-DAG: <<Phi4:i\d+>> Phi loop:<<Loop2>> outer_loop:<<Loop1>>
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
- //
- /// CHECK-START: int Main.closedFormNested() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.closedFormNested() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 100 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- static int closedFormNested() {
- int closed = 0;
- for (int i = 0; i < 10; i++) {
- for (int j = 0; j < 10; j++) {
- closed++;
- }
- }
- return closed; // only needs last-value
- }
-
- /// CHECK-START: int Main.closedFormNestedAlt() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop1:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop1>> outer_loop:none
- /// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
- /// CHECK-DAG: <<Phi4:i\d+>> Phi loop:<<Loop2>> outer_loop:<<Loop1>>
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
- //
- /// CHECK-START: int Main.closedFormNestedAlt() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.closedFormNestedAlt() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 15082 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- static int closedFormNestedAlt() {
- int closed = 12345;
- for (int i = 0; i < 17; i++) {
- for (int j = 0; j < 23; j++) {
- closed += 7;
- }
- }
- return closed; // only needs last-value
- }
-
- // TODO: taken test around closed form?
- static int closedFormInductionUpN(int n) {
- int closed = 12345;
- for (int i = 0; i < n; i++) {
- closed += 5;
- }
- return closed; // only needs last value
- }
-
- // TODO: taken test around closed form?
- static int closedFormInductionInAndDownN(int closed, int n) {
- for (int i = 0; i < n; i++) {
- closed -= 5;
- }
- return closed; // only needs last value
- }
-
- // TODO: move closed form even further out?
- static int closedFormNestedN(int n) {
- int closed = 0;
- for (int i = 0; i < n; i++) {
- for (int j = 0; j < 10; j++) {
- closed++;
- }
- }
- return closed; // only needs last-value
- }
-
- // TODO: move closed form even further out?
- static int closedFormNestedNAlt(int n) {
- int closed = 12345;
- for (int i = 0; i < n; i++) {
- for (int j = 0; j < 23; j++) {
- closed += 7;
- }
- }
- return closed; // only needs last-value
- }
-
- // TODO: move closed form even further out?
- static int closedFormNestedMN(int m, int n) {
- int closed = 0;
- for (int i = 0; i < m; i++) {
- for (int j = 0; j < n; j++) {
- closed++;
- }
- }
- return closed; // only needs last-value
- }
-
- // TODO: move closed form even further out?
- static int closedFormNestedMNAlt(int m, int n) {
- int closed = 12345;
- for (int i = 0; i < m; i++) {
- for (int j = 0; j < n; j++) {
- closed += 7;
- }
- }
- return closed; // only needs last-value
- }
-
- /// CHECK-START: int Main.mainIndexReturned() loop_optimization (before)
- /// CHECK-DAG: <<Phi:i\d+>> Phi loop:{{B\d+}} outer_loop:none
- /// CHECK-DAG: Return [<<Phi>>] loop:none
- //
- /// CHECK-START: int Main.mainIndexReturned() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.mainIndexReturned() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 10 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- static int mainIndexReturned() {
- int i;
- for (i = 0; i < 10; i++);
- return i;
- }
-
- /// CHECK-START: int Main.periodicReturned9() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
- //
- /// CHECK-START: int Main.periodicReturned9() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.periodicReturned9() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 1 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- static int periodicReturned9() {
- int k = 0;
- for (int i = 0; i < 9; i++) {
- k = 1 - k;
- }
- return k;
- }
-
- /// CHECK-START: int Main.periodicReturned10() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
- //
- /// CHECK-START: int Main.periodicReturned10() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.periodicReturned10() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- static int periodicReturned10() {
- int k = 0;
- for (int i = 0; i < 10; i++) {
- k = 1 - k;
- }
- return k;
- }
-
- /// CHECK-START: int Main.getSum21() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
- //
- /// CHECK-START: int Main.getSum21() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.getSum21() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 21 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- private static int getSum21() {
- int k = 0;
- int sum = 0;
- for (int i = 0; i < 6; i++) {
- k++;
- sum += k;
- }
- return sum;
- }
-
- // Ensure double induction does not "overshoot" the subscript range.
- private static int getIncr2(int[] arr) {
- for (int i = 0; i < 12; ) {
- arr[i++] = 30;
- arr[i++] = 29;
- }
- int sum = 0;
- for (int i = 0; i < 12; i++) {
- sum += arr[i];
- }
- return sum;
- }
-
- // TODO: handle as closed/empty eventually?
- static int mainIndexReturnedN(int n) {
- int i;
- for (i = 0; i < n; i++);
- return i;
- }
-
- // TODO: handle as closed/empty eventually?
- static int mainIndexShort1(short s) {
- int i = 0;
- for (i = 0; i < s; i++) { }
- return i;
- }
-
- // TODO: handle as closed/empty eventually?
- static int mainIndexShort2(short s) {
- int i = 0;
- for (i = 0; s > i; i++) { }
- return i;
- }
-
- /// CHECK-START: int Main.periodicReturnedN(int) loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
- //
- /// CHECK-START: int Main.periodicReturnedN(int) loop_optimization (after)
- /// CHECK-NOT: Phi
- static int periodicReturnedN(int n) {
- int k = 0;
- for (int i = 0; i < n; i++) {
- k = 1 - k;
- }
- return k;
- }
-
- // If ever replaced by closed form, last value should be correct!
- private static int getSumN(int n) {
- int k = 0;
- int sum = 0;
- for (int i = 0; i < n; i++) {
- k++;
- sum += k;
- }
- return sum;
- }
-
- // If ever replaced by closed form, last value should be correct!
- private static int closedTwice() {
- int closed = 0;
- for (int i = 0; i < 10; i++) {
- closed++;
- }
- // Closed form of first loop defines trip count of second loop.
- int other_closed = 0;
- for (int i = 0; i < closed; i++) {
- other_closed++;
- }
- return other_closed;
- }
-
- /// CHECK-START: int Main.closedFeed() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop1:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop1>> outer_loop:none
- /// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi4:i\d+>> Phi loop:<<Loop2>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi3>>] loop:none
- /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>"
- //
- /// CHECK-START: int Main.closedFeed() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.closedFeed() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 20 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- private static int closedFeed() {
- int closed = 0;
- for (int i = 0; i < 10; i++) {
- closed++;
- }
- // Closed form of first loop feeds into initial value of second loop,
- // used when generating closed form for the latter.
- for (int i = 0; i < 10; i++) {
- closed++;
- }
- return closed;
- }
-
- /// CHECK-START: int Main.closedLargeUp() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
- //
- /// CHECK-START: int Main.closedLargeUp() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.closedLargeUp() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant -10 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- private static int closedLargeUp() {
- int closed = 0;
- for (int i = 0; i < 10; i++) {
- closed += 0x7fffffff;
- }
- return closed;
- }
-
- /// CHECK-START: int Main.closedLargeDown() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
- //
- /// CHECK-START: int Main.closedLargeDown() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.closedLargeDown() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 10 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- private static int closedLargeDown() {
- int closed = 0;
- for (int i = 0; i < 10; i++) {
- closed -= 0x7fffffff;
- }
- return closed;
- }
-
- /// CHECK-START: int Main.waterFall() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop1:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop3:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi4:i\d+>> Phi loop:<<Loop4:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi5:i\d+>> Phi loop:<<Loop5:B\d+>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi5>>] loop:none
- //
- /// CHECK-START: int Main.waterFall() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: int Main.waterFall() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 50 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- private static int waterFall() {
- int i = 0;
- for (; i < 10; i++);
- for (; i < 20; i++);
- for (; i < 30; i++);
- for (; i < 40; i++);
- for (; i < 50; i++);
- return i; // this should become just 50
- }
-
- /// CHECK-START: boolean Main.periodicBoolIdiom1() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
- //
- /// CHECK-START: boolean Main.periodicBoolIdiom1() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: boolean Main.periodicBoolIdiom1() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- private static boolean periodicBoolIdiom1() {
- boolean x = true;
- for (int i = 0; i < 7; i++) {
- x = !x;
- }
- return x;
- }
-
- /// CHECK-START: boolean Main.periodicBoolIdiom2() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
- //
- /// CHECK-START: boolean Main.periodicBoolIdiom2() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: boolean Main.periodicBoolIdiom2() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- private static boolean periodicBoolIdiom2() {
- boolean x = true;
- for (int i = 0; i < 7; i++) {
- x = (x != true);
- }
- return x;
- }
-
- /// CHECK-START: boolean Main.periodicBoolIdiom3() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi1>>] loop:none
- //
- /// CHECK-START: boolean Main.periodicBoolIdiom3() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: boolean Main.periodicBoolIdiom3() instruction_simplifier$after_bce (after)
- /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 loop:none
- /// CHECK-DAG: Return [<<Int>>] loop:none
- private static boolean periodicBoolIdiom3() {
- boolean x = true;
- for (int i = 0; i < 7; i++) {
- x = (x == false);
- }
- return x;
- }
-
- /// CHECK-START: boolean Main.periodicBoolIdiom1N(boolean, int) loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
- //
- /// CHECK-START: boolean Main.periodicBoolIdiom1N(boolean, int) loop_optimization (after)
- /// CHECK-NOT: Phi
- private static boolean periodicBoolIdiom1N(boolean x, int n) {
- for (int i = 0; i < n; i++) {
- x = !x;
- }
- return x;
- }
-
- /// CHECK-START: boolean Main.periodicBoolIdiom2N(boolean, int) loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
- //
- /// CHECK-START: boolean Main.periodicBoolIdiom2N(boolean, int) loop_optimization (after)
- /// CHECK-NOT: Phi
- private static boolean periodicBoolIdiom2N(boolean x, int n) {
- for (int i = 0; i < n; i++) {
- x = (x != true);
- }
- return x;
- }
-
- /// CHECK-START: boolean Main.periodicBoolIdiom3N(boolean, int) loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
- //
- /// CHECK-START: boolean Main.periodicBoolIdiom3N(boolean, int) loop_optimization (after)
- /// CHECK-NOT: Phi
- private static boolean periodicBoolIdiom3N(boolean x, int n) {
- for (int i = 0; i < n; i++) {
- x = (x == false);
- }
- return x;
- }
-
- /// CHECK-START: float Main.periodicFloat10() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:f\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: <<Phi3:f\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: <<Phi4:f\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
- //
- /// CHECK-START: float Main.periodicFloat10() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: float Main.periodicFloat10() loop_optimization (after)
- /// CHECK-DAG: <<Float:f\d+>> FloatConstant 2 loop:none
- /// CHECK-DAG: Return [<<Float>>] loop:none
- private static float periodicFloat10() {
- float r = 4.5f;
- float s = 2.0f;
- float t = -1.0f;
- for (int i = 0; i < 10; i++) {
- float tmp = t; t = r; r = s; s = tmp;
- }
- return r;
- }
-
- /// CHECK-START: float Main.periodicFloat11() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:f\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: <<Phi3:f\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: <<Phi4:f\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
- //
- /// CHECK-START: float Main.periodicFloat11() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: float Main.periodicFloat11() loop_optimization (after)
- /// CHECK-DAG: <<Float:f\d+>> FloatConstant -1 loop:none
- /// CHECK-DAG: Return [<<Float>>] loop:none
- private static float periodicFloat11() {
- float r = 4.5f;
- float s = 2.0f;
- float t = -1.0f;
- for (int i = 0; i < 11; i++) {
- float tmp = t; t = r; r = s; s = tmp;
- }
- return r;
- }
-
- /// CHECK-START: float Main.periodicFloat12() loop_optimization (before)
- /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
- /// CHECK-DAG: <<Phi2:f\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: <<Phi3:f\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: <<Phi4:f\d+>> Phi loop:<<Loop>> outer_loop:none
- /// CHECK-DAG: Return [<<Phi2>>] loop:none
- //
- /// CHECK-START: float Main.periodicFloat12() loop_optimization (after)
- /// CHECK-NOT: Phi
- //
- /// CHECK-START: float Main.periodicFloat12() loop_optimization (after)
- /// CHECK-DAG: <<Float:f\d+>> FloatConstant 4.5 loop:none
- /// CHECK-DAG: Return [<<Float>>] loop:none
- private static float periodicFloat12() {
- float r = 4.5f;
- float s = 2.0f;
- float t = -1.0f;
- for (int i = 0; i < 12; i++) {
- float tmp = t; t = r; r = s; s = tmp;
- }
- return r;
- }
-
- private static int exceptionExitBeforeAdd() {
- int k = 0;
- try {
- for (int i = 0; i < 10; i++) {
- a[i] = 0;
- k += 10; // increment last
- }
- } catch(Exception e) {
- // Flag error by returning current
- // value of k negated.
- return -k-1;
- }
- return k;
- }
-
- private static int exceptionExitAfterAdd() {
- int k = 0;
- try {
- for (int i = 0; i < 10; i++) {
- k += 10; // increment first
- a[i] = 0;
- }
- } catch(Exception e) {
- // Flag error by returning current
- // value of k negated.
- return -k-1;
- }
- return k;
- }
-
- public static void main(String[] args) {
- deadSingleLoop();
- deadSingleLoopN(4);
- potentialInfiniteLoop(4);
- deadNestedLoops();
- deadNestedAndFollowingLoops();
- deadConditional(4);
- deadConditionalCycle(4);
-
- deadInduction();
- for (int i = 0; i < a.length; i++) {
- expectEquals(1, a[i]);
- }
- deadManyInduction();
- for (int i = 0; i < a.length; i++) {
- expectEquals(2, a[i]);
- }
- deadSequence();
- for (int i = 0; i < a.length; i++) {
- expectEquals(3, a[i]);
- }
- try {
- deadCycleWithException(-1);
- throw new Error("Expected: IOOB exception");
- } catch (IndexOutOfBoundsException e) {
- }
- for (int i = 0; i < a.length; i++) {
- expectEquals(i == 0 ? 4 : 3, a[i]);
- }
- deadCycleWithException(0);
- for (int i = 0; i < a.length; i++) {
- expectEquals(4, a[i]);
}
- expectEquals(12395, closedFormInductionUp());
- expectEquals(12295, closedFormInductionInAndDown(12345));
- expectEquals(81, closedFormInductionTrivialIf());
- expectEquals(10 * 10, closedFormNested());
- expectEquals(12345 + 17 * 23 * 7, closedFormNestedAlt());
- for (int n = -4; n < 10; n++) {
- int tc = (n <= 0) ? 0 : n;
- expectEquals(12345 + tc * 5, closedFormInductionUpN(n));
- expectEquals(12345 - tc * 5, closedFormInductionInAndDownN(12345, n));
- expectEquals(tc * 10, closedFormNestedN(n));
- expectEquals(12345 + tc * 23 * 7, closedFormNestedNAlt(n));
- expectEquals(tc * (tc + 1), closedFormNestedMN(n, n + 1));
- expectEquals(12345 + tc * (tc + 1) * 7, closedFormNestedMNAlt(n, n + 1));
+ /// CHECK-START: void Main.deadNestedLoops() loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:<<Loop>>
+ //
+ /// CHECK-START: void Main.deadNestedLoops() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ static void deadNestedLoops() {
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ }
+ }
}
- expectEquals(10, mainIndexReturned());
- expectEquals(1, periodicReturned9());
- expectEquals(0, periodicReturned10());
- expectEquals(21, getSum21());
- expectEquals(354, getIncr2(new int[12]));
- for (int n = -4; n < 4; n++) {
- int tc = (n <= 0) ? 0 : n;
- expectEquals(tc, mainIndexReturnedN(n));
- expectEquals(tc, mainIndexShort1((short) n));
- expectEquals(tc, mainIndexShort2((short) n));
- expectEquals(tc & 1, periodicReturnedN(n));
- expectEquals((tc * (tc + 1)) / 2, getSumN(n));
+ /// CHECK-START: void Main.deadNestedAndFollowingLoops() loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop1:B\d+>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
+ /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:<<Loop2>>
+ /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:<<Loop2>>
+ /// CHECK-DAG: Phi loop:<<Loop3:B\d+>> outer_loop:<<Loop1>>
+ /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:<<Loop3>>
+ /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
+ //
+ /// CHECK-START: void Main.deadNestedAndFollowingLoops() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ static void deadNestedAndFollowingLoops() {
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ for (int k = 0; k < 4; k++) {
+ }
+ for (int k = 0; k < 4; k++) {
+ }
+ }
+ for (int j = 0; j < 4; j++) {
+ for (int k = 0; k < 4; k++) {
+ }
+ }
+ }
+ for (int i = 0; i < 4; i++) {
+ }
}
- expectEquals(10, closedTwice());
- expectEquals(20, closedFeed());
- expectEquals(-10, closedLargeUp());
- expectEquals(10, closedLargeDown());
- expectEquals(50, waterFall());
-
- expectEquals(false, periodicBoolIdiom1());
- expectEquals(false, periodicBoolIdiom2());
- expectEquals(false, periodicBoolIdiom3());
- for (int n = -4; n < 10; n++) {
- int tc = (n <= 0) ? 0 : n;
- boolean even = (tc & 1) == 0;
- expectEquals(even, periodicBoolIdiom1N(true, n));
- expectEquals(!even, periodicBoolIdiom1N(false, n));
- expectEquals(even, periodicBoolIdiom2N(true, n));
- expectEquals(!even, periodicBoolIdiom2N(false, n));
- expectEquals(even, periodicBoolIdiom3N(true, n));
- expectEquals(!even, periodicBoolIdiom3N(false, n));
+ /// CHECK-START: void Main.deadConditional(int) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:{{B\d+}} outer_loop:none
+ //
+ /// CHECK-START: void Main.deadConditional(int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ public static void deadConditional(int n) {
+ int k = 0;
+ int m = 0;
+ for (int i = 0; i < n; i++) {
+ if (i == 3)
+ k = i;
+ else
+ m = i;
+ }
}
- expectEquals( 2.0f, periodicFloat10());
- expectEquals(-1.0f, periodicFloat11());
- expectEquals( 4.5f, periodicFloat12());
-
- expectEquals(100, exceptionExitBeforeAdd());
- expectEquals(100, exceptionExitAfterAdd());
- a = null;
- expectEquals(-1, exceptionExitBeforeAdd());
- expectEquals(-11, exceptionExitAfterAdd());
- a = new int[4];
- expectEquals(-41, exceptionExitBeforeAdd());
- expectEquals(-51, exceptionExitAfterAdd());
-
- System.out.println("passed");
- }
-
- private static void expectEquals(float expected, float result) {
- if (expected != result) {
- throw new Error("Expected: " + expected + ", found: " + result);
+ /// CHECK-START: void Main.deadConditionalCycle(int) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START: void Main.deadConditionalCycle(int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ public static void deadConditionalCycle(int n) {
+ int k = 0;
+ int m = 0;
+ for (int i = 0; i < n; i++) {
+ if (i == 3)
+ k--;
+ else
+ m++;
+ }
}
- }
- private static void expectEquals(int expected, int result) {
- if (expected != result) {
- throw new Error("Expected: " + expected + ", found: " + result);
- }
- }
- private static void expectEquals(boolean expected, boolean result) {
- if (expected != result) {
- throw new Error("Expected: " + expected + ", found: " + result);
+ /// CHECK-START: void Main.deadInduction() loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START: void Main.deadInduction() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-NOT: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
+ static void deadInduction() {
+ int dead = 0;
+ for (int i = 0; i < a.length; i++) {
+ a[i] = novec[2 * i] + 1;
+ dead += 5;
+ }
}
- }
+
+ /// CHECK-START: void Main.deadManyInduction() loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START: void Main.deadManyInduction() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-NOT: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
+ static void deadManyInduction() {
+ int dead1 = 0, dead2 = 1, dead3 = 3;
+ for (int i = 0; i < a.length; i++) {
+ dead1 += 5;
+ a[i] = novec[2 * i] + 2;
+ dead2 += 10;
+ dead3 += 100;
+ }
+ }
+
+ /// CHECK-START: void Main.deadSequence() loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START: void Main.deadSequence() loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-NOT: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
+ static void deadSequence() {
+ int dead = 0;
+ for (int i = 0; i < a.length; i++) {
+ a[i] = novec[2 * i] + 3;
+ // Increment value defined inside loop,
+ // but sequence itself not used anywhere.
+ dead += i;
+ }
+ }
+
+ /// CHECK-START: void Main.deadCycleWithException(int) loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-NOT: BoundsCheck
+ //
+ /// CHECK-START: void Main.deadCycleWithException(int) loop_optimization (after)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-NOT: Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-NOT: ArrayGet loop:<<Loop>> outer_loop:none
+ static void deadCycleWithException(int k) {
+ int dead = 0;
+ for (int i = 0; i < a.length; i++) {
+ a[i] = novec[2 * i] + 4;
+ // Increment value of dead cycle may throw exception. Dynamic
+ // BCE takes care of the bounds check though, which enables
+ // removing the ArrayGet after removing the dead cycle.
+ dead += a[k];
+ }
+ }
+
+ /// CHECK-START: int Main.closedFormInductionUp() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.closedFormInductionUp() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.closedFormInductionUp() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 12395 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ static int closedFormInductionUp() {
+ int closed = 12345;
+ for (int i = 0; i < 10; i++) {
+ closed += 5;
+ }
+ return closed; // only needs last value
+ }
+
+ /// CHECK-START: int Main.closedFormInductionInAndDown(int) loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ //
+ /// CHECK-START: int Main.closedFormInductionInAndDown(int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.closedFormInductionInAndDown(int) instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Par:i\d+>> ParameterValue loop:none
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant -50 loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Int>>,<<Par>>] loop:none
+ /// CHECK-DAG: Return [<<Add>>] loop:none
+ static int closedFormInductionInAndDown(int closed) {
+ for (int i = 0; i < 10; i++) {
+ closed -= 5;
+ }
+ return closed; // only needs last value
+ }
+
+ /// CHECK-START: int Main.closedFormInductionTrivialIf() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Select loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.closedFormInductionTrivialIf() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ /// CHECK-NOT: Select
+ //
+ /// CHECK-START: int Main.closedFormInductionTrivialIf() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 81 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ static int closedFormInductionTrivialIf() {
+ int closed = 11;
+ for (int i = 0; i < 10; i++) {
+ // Trivial if becomes trivial select at HIR level.
+ // Make sure this is still recognized as induction.
+ if (i < 5) {
+ closed += 7;
+ } else {
+ closed += 7;
+ }
+ }
+ return closed; // only needs last value
+ }
+
+ /// CHECK-START: int Main.closedFormNested() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop1:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
+ /// CHECK-DAG: <<Phi4:i\d+>> Phi loop:<<Loop2>> outer_loop:<<Loop1>>
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.closedFormNested() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.closedFormNested() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 100 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ static int closedFormNested() {
+ int closed = 0;
+ for (int i = 0; i < 10; i++) {
+ for (int j = 0; j < 10; j++) {
+ closed++;
+ }
+ }
+ return closed; // only needs last-value
+ }
+
+ /// CHECK-START: int Main.closedFormNestedAlt() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop1:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:<<Loop1>>
+ /// CHECK-DAG: <<Phi4:i\d+>> Phi loop:<<Loop2>> outer_loop:<<Loop1>>
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.closedFormNestedAlt() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.closedFormNestedAlt() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 15082 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ static int closedFormNestedAlt() {
+ int closed = 12345;
+ for (int i = 0; i < 17; i++) {
+ for (int j = 0; j < 23; j++) {
+ closed += 7;
+ }
+ }
+ return closed; // only needs last-value
+ }
+
+ // TODO: taken test around closed form?
+ static int closedFormInductionUpN(int n) {
+ int closed = 12345;
+ for (int i = 0; i < n; i++) {
+ closed += 5;
+ }
+ return closed; // only needs last value
+ }
+
+ // TODO: taken test around closed form?
+ static int closedFormInductionInAndDownN(int closed, int n) {
+ for (int i = 0; i < n; i++) {
+ closed -= 5;
+ }
+ return closed; // only needs last value
+ }
+
+ // TODO: move closed form even further out?
+ static int closedFormNestedN(int n) {
+ int closed = 0;
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < 10; j++) {
+ closed++;
+ }
+ }
+ return closed; // only needs last-value
+ }
+
+ // TODO: move closed form even further out?
+ static int closedFormNestedNAlt(int n) {
+ int closed = 12345;
+ for (int i = 0; i < n; i++) {
+ for (int j = 0; j < 23; j++) {
+ closed += 7;
+ }
+ }
+ return closed; // only needs last-value
+ }
+
+ // TODO: move closed form even further out?
+ static int closedFormNestedMN(int m, int n) {
+ int closed = 0;
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < n; j++) {
+ closed++;
+ }
+ }
+ return closed; // only needs last-value
+ }
+
+ // TODO: move closed form even further out?
+ static int closedFormNestedMNAlt(int m, int n) {
+ int closed = 12345;
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < n; j++) {
+ closed += 7;
+ }
+ }
+ return closed; // only needs last-value
+ }
+
+ /// CHECK-START: int Main.mainIndexReturned() loop_optimization (before)
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:{{B\d+}} outer_loop:none
+ /// CHECK-DAG: Return [<<Phi>>] loop:none
+ //
+ /// CHECK-START: int Main.mainIndexReturned() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.mainIndexReturned() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 10 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ static int mainIndexReturned() {
+ int i;
+ for (i = 0; i < 10; i++);
+ return i;
+ }
+
+ /// CHECK-START: int Main.periodicReturned9() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.periodicReturned9() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.periodicReturned9() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 1 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ static int periodicReturned9() {
+ int k = 0;
+ for (int i = 0; i < 9; i++) {
+ k = 1 - k;
+ }
+ return k;
+ }
+
+ /// CHECK-START: int Main.periodicReturned10() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.periodicReturned10() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.periodicReturned10() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ static int periodicReturned10() {
+ int k = 0;
+ for (int i = 0; i < 10; i++) {
+ k = 1 - k;
+ }
+ return k;
+ }
+
+ /// CHECK-START: int Main.getSum21() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ //
+ /// CHECK-START: int Main.getSum21() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.getSum21() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 21 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ private static int getSum21() {
+ int k = 0;
+ int sum = 0;
+ for (int i = 0; i < 6; i++) {
+ k++;
+ sum += k;
+ }
+ return sum;
+ }
+
+ // Ensure double induction does not "overshoot" the subscript range.
+ private static int getIncr2(int[] arr) {
+ for (int i = 0; i < 12; ) {
+ arr[i++] = 30;
+ arr[i++] = 29;
+ }
+ int sum = 0;
+ for (int i = 0; i < 12; i++) {
+ sum += arr[i];
+ }
+ return sum;
+ }
+
+ // TODO: handle as closed/empty eventually?
+ static int mainIndexReturnedN(int n) {
+ int i;
+ for (i = 0; i < n; i++);
+ return i;
+ }
+
+ // TODO: handle as closed/empty eventually?
+ static int mainIndexShort1(short s) {
+ int i = 0;
+ for (i = 0; i < s; i++) { }
+ return i;
+ }
+
+ // TODO: handle as closed/empty eventually?
+ static int mainIndexShort2(short s) {
+ int i = 0;
+ for (i = 0; s > i; i++) { }
+ return i;
+ }
+
+ /// CHECK-START: int Main.periodicReturnedN(int) loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.periodicReturnedN(int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ static int periodicReturnedN(int n) {
+ int k = 0;
+ for (int i = 0; i < n; i++) {
+ k = 1 - k;
+ }
+ return k;
+ }
+
+ // If ever replaced by closed form, last value should be correct!
+ private static int getSumN(int n) {
+ int k = 0;
+ int sum = 0;
+ for (int i = 0; i < n; i++) {
+ k++;
+ sum += k;
+ }
+ return sum;
+ }
+
+ // If ever replaced by closed form, last value should be correct!
+ private static int closedTwice() {
+ int closed = 0;
+ for (int i = 0; i < 10; i++) {
+ closed++;
+ }
+ // Closed form of first loop defines trip count of second loop.
+ int other_closed = 0;
+ for (int i = 0; i < closed; i++) {
+ other_closed++;
+ }
+ return other_closed;
+ }
+
+ /// CHECK-START: int Main.closedFeed() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop1:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop1>> outer_loop:none
+ /// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi4:i\d+>> Phi loop:<<Loop2>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi3>>] loop:none
+ /// CHECK-EVAL: "<<Loop1>>" != "<<Loop2>>"
+ //
+ /// CHECK-START: int Main.closedFeed() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.closedFeed() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 20 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ private static int closedFeed() {
+ int closed = 0;
+ for (int i = 0; i < 10; i++) {
+ closed++;
+ }
+ // Closed form of first loop feeds into initial value of second loop,
+ // used when generating closed form for the latter.
+ for (int i = 0; i < 10; i++) {
+ closed++;
+ }
+ return closed;
+ }
+
+ /// CHECK-START: int Main.closedLargeUp() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.closedLargeUp() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.closedLargeUp() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant -10 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ private static int closedLargeUp() {
+ int closed = 0;
+ for (int i = 0; i < 10; i++) {
+ closed += 0x7fffffff;
+ }
+ return closed;
+ }
+
+ /// CHECK-START: int Main.closedLargeDown() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.closedLargeDown() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.closedLargeDown() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 10 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ private static int closedLargeDown() {
+ int closed = 0;
+ for (int i = 0; i < 10; i++) {
+ closed -= 0x7fffffff;
+ }
+ return closed;
+ }
+
+ // Checks that we do not loop optimize if the calculation of the trip count would overflow.
+ /// CHECK-START: int Main.closedLinearStepOverflow() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.closedLinearStepOverflow() loop_optimization (after)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ private static int closedLinearStepOverflow() {
+ int closed = 0;
+ // Note that this isn't a "one-off" error.
+ // We are using MIN and MAX to make sure we overflow.
+ for (int i = Integer.MIN_VALUE; i < (Integer.MAX_VALUE - 80); i += 79) {
+ closed++;
+ }
+ return closed;
+ }
+
+ // Since we cannot guarantee that the start/end wouldn't overflow we do not perform loop
+ // optimization.
+ /// CHECK-START: int Main.$inline$closedByParameters(int, int) loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.$inline$closedByParameters(int, int) loop_optimization (after)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ private static int $inline$closedByParameters(int start, int end) {
+ int closed = 0;
+ for (int i = start; i < end; i++) {
+ closed++;
+ }
+ return closed;
+ }
+
+ // Since we are inlining `closedByParameters` we know that the parameters are fixed and
+ // therefore we can perform loop optimization.
+ /// CHECK-START: int Main.closedByParametersWithInline() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.closedByParametersWithInline() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.closedByParametersWithInline() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 10 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ private static int closedByParametersWithInline() {
+ return $inline$closedByParameters(0, 10);
+ }
+
+ /// CHECK-START: int Main.waterFall() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop1:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop2:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi3:i\d+>> Phi loop:<<Loop3:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi4:i\d+>> Phi loop:<<Loop4:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi5:i\d+>> Phi loop:<<Loop5:B\d+>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi5>>] loop:none
+ //
+ /// CHECK-START: int Main.waterFall() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: int Main.waterFall() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 50 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ private static int waterFall() {
+ int i = 0;
+ for (; i < 10; i++);
+ for (; i < 20; i++);
+ for (; i < 30; i++);
+ for (; i < 40; i++);
+ for (; i < 50; i++);
+ return i; // this should become just 50
+ }
+
+ /// CHECK-START: boolean Main.periodicBoolIdiom1() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: boolean Main.periodicBoolIdiom1() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: boolean Main.periodicBoolIdiom1() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ private static boolean periodicBoolIdiom1() {
+ boolean x = true;
+ for (int i = 0; i < 7; i++) {
+ x = !x;
+ }
+ return x;
+ }
+
+ /// CHECK-START: boolean Main.periodicBoolIdiom2() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: boolean Main.periodicBoolIdiom2() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: boolean Main.periodicBoolIdiom2() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ private static boolean periodicBoolIdiom2() {
+ boolean x = true;
+ for (int i = 0; i < 7; i++) {
+ x = (x != true);
+ }
+ return x;
+ }
+
+ /// CHECK-START: boolean Main.periodicBoolIdiom3() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: boolean Main.periodicBoolIdiom3() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: boolean Main.periodicBoolIdiom3() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ private static boolean periodicBoolIdiom3() {
+ boolean x = true;
+ for (int i = 0; i < 7; i++) {
+ x = (x == false);
+ }
+ return x;
+ }
+
+ /// CHECK-START: boolean Main.periodicBoolIdiom1N(boolean, int) loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ //
+ /// CHECK-START: boolean Main.periodicBoolIdiom1N(boolean, int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ private static boolean periodicBoolIdiom1N(boolean x, int n) {
+ for (int i = 0; i < n; i++) {
+ x = !x;
+ }
+ return x;
+ }
+
+ /// CHECK-START: boolean Main.periodicBoolIdiom2N(boolean, int) loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ //
+ /// CHECK-START: boolean Main.periodicBoolIdiom2N(boolean, int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ private static boolean periodicBoolIdiom2N(boolean x, int n) {
+ for (int i = 0; i < n; i++) {
+ x = (x != true);
+ }
+ return x;
+ }
+
+ /// CHECK-START: boolean Main.periodicBoolIdiom3N(boolean, int) loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ //
+ /// CHECK-START: boolean Main.periodicBoolIdiom3N(boolean, int) loop_optimization (after)
+ /// CHECK-NOT: Phi
+ private static boolean periodicBoolIdiom3N(boolean x, int n) {
+ for (int i = 0; i < n; i++) {
+ x = (x == false);
+ }
+ return x;
+ }
+
+ /// CHECK-START: float Main.periodicFloat10() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi3:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi4:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ //
+ /// CHECK-START: float Main.periodicFloat10() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: float Main.periodicFloat10() loop_optimization (after)
+ /// CHECK-DAG: <<Float:f\d+>> FloatConstant 2 loop:none
+ /// CHECK-DAG: Return [<<Float>>] loop:none
+ private static float periodicFloat10() {
+ float r = 4.5f;
+ float s = 2.0f;
+ float t = -1.0f;
+ for (int i = 0; i < 10; i++) {
+ float tmp = t;
+ t = r;
+ r = s;
+ s = tmp;
+ }
+ return r;
+ }
+
+ /// CHECK-START: float Main.periodicFloat11() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi3:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi4:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ //
+ /// CHECK-START: float Main.periodicFloat11() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: float Main.periodicFloat11() loop_optimization (after)
+ /// CHECK-DAG: <<Float:f\d+>> FloatConstant -1 loop:none
+ /// CHECK-DAG: Return [<<Float>>] loop:none
+ private static float periodicFloat11() {
+ float r = 4.5f;
+ float s = 2.0f;
+ float t = -1.0f;
+ for (int i = 0; i < 11; i++) {
+ float tmp = t;
+ t = r;
+ r = s;
+ s = tmp;
+ }
+ return r;
+ }
+
+ /// CHECK-START: float Main.periodicFloat12() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi3:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi4:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ //
+ /// CHECK-START: float Main.periodicFloat12() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: float Main.periodicFloat12() loop_optimization (after)
+ /// CHECK-DAG: <<Float:f\d+>> FloatConstant 4.5 loop:none
+ /// CHECK-DAG: Return [<<Float>>] loop:none
+ private static float periodicFloat12() {
+ float r = 4.5f;
+ float s = 2.0f;
+ float t = -1.0f;
+ for (int i = 0; i < 12; i++) {
+ float tmp = t;
+ t = r;
+ r = s;
+ s = tmp;
+ }
+ return r;
+ }
+
+ private static int exceptionExitBeforeAdd() {
+ int k = 0;
+ try {
+ for (int i = 0; i < 10; i++) {
+ a[i] = 0;
+ k += 10; // increment last
+ }
+ } catch (Exception e) {
+ // Flag error by returning current
+ // value of k negated.
+ return -k - 1;
+ }
+ return k;
+ }
+
+ private static int exceptionExitAfterAdd() {
+ int k = 0;
+ try {
+ for (int i = 0; i < 10; i++) {
+ k += 10; // increment first
+ a[i] = 0;
+ }
+ } catch (Exception e) {
+ // Flag error by returning current
+ // value of k negated.
+ return -k - 1;
+ }
+ return k;
+ }
+
+ /// CHECK-START: long Main.closedLinearInductionUnmatchedTypesNotOptimized() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:j\d+>> Phi loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START: long Main.closedLinearInductionUnmatchedTypesNotOptimized() loop_optimization (after)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:j\d+>> Phi loop:<<Loop>> outer_loop:none
+ private static long closedLinearInductionUnmatchedTypesNotOptimized() {
+ long sum = 0;
+ for (int i = 0; i < 10; ++i) {
+ ++sum;
+ }
+ return sum;
+ }
+
+ /// CHECK-START: short Main.closedLinearInductionNarrowingNotOptimized() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ //
+ /// CHECK-START: short Main.closedLinearInductionNarrowingNotOptimized() loop_optimization (after)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ private static short closedLinearInductionNarrowingNotOptimized() {
+ short i = 0;
+ for (; i < 10; ++i);
+ return i;
+ }
+
+ public static void main(String[] args) {
+ deadSingleLoop();
+ deadSingleLoopN(4);
+ potentialInfiniteLoop(4);
+ deadNestedLoops();
+ deadNestedAndFollowingLoops();
+ deadConditional(4);
+ deadConditionalCycle(4);
+
+ deadInduction();
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(1, a[i]);
+ }
+ deadManyInduction();
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(2, a[i]);
+ }
+ deadSequence();
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(3, a[i]);
+ }
+ try {
+ deadCycleWithException(-1);
+ throw new Error("Expected: IOOB exception");
+ } catch (IndexOutOfBoundsException e) {
+ }
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(i == 0 ? 4 : 3, a[i]);
+ }
+ deadCycleWithException(0);
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(4, a[i]);
+ }
+
+ expectEquals(12395, closedFormInductionUp());
+ expectEquals(12295, closedFormInductionInAndDown(12345));
+ expectEquals(81, closedFormInductionTrivialIf());
+ expectEquals(10 * 10, closedFormNested());
+ expectEquals(12345 + 17 * 23 * 7, closedFormNestedAlt());
+ for (int n = -4; n < 10; n++) {
+ int tc = (n <= 0) ? 0 : n;
+ expectEquals(12345 + tc * 5, closedFormInductionUpN(n));
+ expectEquals(12345 - tc * 5, closedFormInductionInAndDownN(12345, n));
+ expectEquals(tc * 10, closedFormNestedN(n));
+ expectEquals(12345 + tc * 23 * 7, closedFormNestedNAlt(n));
+ expectEquals(tc * (tc + 1), closedFormNestedMN(n, n + 1));
+ expectEquals(12345 + tc * (tc + 1) * 7, closedFormNestedMNAlt(n, n + 1));
+ }
+
+ expectEquals(10, mainIndexReturned());
+ expectEquals(1, periodicReturned9());
+ expectEquals(0, periodicReturned10());
+ expectEquals(21, getSum21());
+ expectEquals(354, getIncr2(new int[12]));
+ for (int n = -4; n < 4; n++) {
+ int tc = (n <= 0) ? 0 : n;
+ expectEquals(tc, mainIndexReturnedN(n));
+ expectEquals(tc, mainIndexShort1((short) n));
+ expectEquals(tc, mainIndexShort2((short) n));
+ expectEquals(tc & 1, periodicReturnedN(n));
+ expectEquals((tc * (tc + 1)) / 2, getSumN(n));
+ }
+
+ expectEquals(10, closedTwice());
+ expectEquals(20, closedFeed());
+ expectEquals(-10, closedLargeUp());
+ expectEquals(10, closedLargeDown());
+ expectEquals(54366674, closedLinearStepOverflow());
+ expectEquals(10, $inline$closedByParameters(0, 10));
+ expectEquals(10, closedByParametersWithInline());
+ expectEquals(50, waterFall());
+
+ expectEquals(false, periodicBoolIdiom1());
+ expectEquals(false, periodicBoolIdiom2());
+ expectEquals(false, periodicBoolIdiom3());
+ for (int n = -4; n < 10; n++) {
+ int tc = (n <= 0) ? 0 : n;
+ boolean even = (tc & 1) == 0;
+ expectEquals(even, periodicBoolIdiom1N(true, n));
+ expectEquals(!even, periodicBoolIdiom1N(false, n));
+ expectEquals(even, periodicBoolIdiom2N(true, n));
+ expectEquals(!even, periodicBoolIdiom2N(false, n));
+ expectEquals(even, periodicBoolIdiom3N(true, n));
+ expectEquals(!even, periodicBoolIdiom3N(false, n));
+ }
+
+ expectEquals( 2.0f, periodicFloat10());
+ expectEquals(-1.0f, periodicFloat11());
+ expectEquals( 4.5f, periodicFloat12());
+
+ expectEquals(100, exceptionExitBeforeAdd());
+ expectEquals(100, exceptionExitAfterAdd());
+ a = null;
+ expectEquals(-1, exceptionExitBeforeAdd());
+ expectEquals(-11, exceptionExitAfterAdd());
+ a = new int[4];
+ expectEquals(-41, exceptionExitBeforeAdd());
+ expectEquals(-51, exceptionExitAfterAdd());
+
+ expectEquals(10, closedLinearInductionUnmatchedTypesNotOptimized());
+ expectEquals(10, closedLinearInductionNarrowingNotOptimized());
+
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(float expected, float result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
+ private static void expectEquals(boolean expected, boolean result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
}
diff --git a/test/619-checker-current-method/src/Main.java b/test/619-checker-current-method/src/Main.java
index d829370..3ab73fa 100644
--- a/test/619-checker-current-method/src/Main.java
+++ b/test/619-checker-current-method/src/Main.java
@@ -15,19 +15,18 @@
*/
public class Main {
+ // Check that there is no instruction storing to stack.
+ /// CHECK-START-X86: int Main.foo(int, int, int, int, int, int) disassembly (after)
+ /// CHECK-NOT: mov [{{\w+}}], {{\w+}}
- // Check that there is no instruction storing to stack.
- /// CHECK-START-X86: int Main.foo(int, int, int, int, int, int) disassembly (after)
- /// CHECK-NOT: mov [{{\w+}}], {{\w+}}
-
- // Use enough parameters to ensure we'll need a frame.
- public static int foo(int a, int b, int c, int d, int e, int f) {
- return a + b + c + d + e + f;
- }
-
- public static void main(String[] args) {
- if (foo(1, 2, 3, 4, 5, 6) != 21) {
- throw new Error("Expected 21");
+ // Use enough parameters to ensure we'll need a frame.
+ public static int foo(int a, int b, int c, int d, int e, int f) {
+ return a + b + c + d + e + f;
}
- }
+
+ public static void main(String[] args) {
+ if (foo(1, 2, 3, 4, 5, 6) != 21) {
+ throw new Error("Expected 21");
+ }
+ }
}
diff --git a/test/626-const-class-linking/clear_dex_cache_types.cc b/test/626-const-class-linking/clear_dex_cache_types.cc
index 1aa3cce..ef230ad 100644
--- a/test/626-const-class-linking/clear_dex_cache_types.cc
+++ b/test/626-const-class-linking/clear_dex_cache_types.cc
@@ -28,8 +28,7 @@
ScopedObjectAccess soa(Thread::Current());
ObjPtr<mirror::DexCache> dex_cache = soa.Decode<mirror::Class>(cls)->GetDexCache();
for (size_t i = 0, num_types = dex_cache->NumResolvedTypes(); i != num_types; ++i) {
- mirror::TypeDexCachePair cleared(nullptr, mirror::TypeDexCachePair::InvalidIndexForSlot(i));
- dex_cache->GetResolvedTypes()[i].store(cleared, std::memory_order_relaxed);
+ dex_cache->GetResolvedTypes()->Clear(i);
}
}
diff --git a/test/628-vdex/run b/test/628-vdex/run
deleted file mode 100644
index bf0ac91..0000000
--- a/test/628-vdex/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-exec ${RUN} -Xcompiler-option --compiler-filter=verify --vdex "${@}"
diff --git a/test/628-vdex/run.py b/test/628-vdex/run.py
new file mode 100644
index 0000000..066152c
--- /dev/null
+++ b/test/628-vdex/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args, Xcompiler_option=["--compiler-filter=verify"], vdex=True)
diff --git a/test/629-vdex-speed/run b/test/629-vdex-speed/run
deleted file mode 100644
index 1477e3d..0000000
--- a/test/629-vdex-speed/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-exec ${RUN} --vdex --vdex-filter speed "${@}"
diff --git a/test/629-vdex-speed/run.py b/test/629-vdex-speed/run.py
new file mode 100644
index 0000000..1d4f199
--- /dev/null
+++ b/test/629-vdex-speed/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, vdex=True, vdex_filter="speed")
diff --git a/test/634-vdex-duplicate/run b/test/634-vdex-duplicate/run
deleted file mode 100644
index 571ccd9..0000000
--- a/test/634-vdex-duplicate/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-exec ${RUN} -Xcompiler-option --compiler-filter=verify --vdex-filter speed --vdex "${@}"
diff --git a/test/634-vdex-duplicate/run.py b/test/634-vdex-duplicate/run.py
new file mode 100644
index 0000000..c99522d
--- /dev/null
+++ b/test/634-vdex-duplicate/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args,
+ Xcompiler_option=["--compiler-filter=verify"],
+ vdex_filter="speed",
+ vdex=True)
diff --git a/test/636-wrong-static-access/run b/test/636-wrong-static-access/run
deleted file mode 100755
index 5e99920..0000000
--- a/test/636-wrong-static-access/run
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Make verification soft fail, to ensure the verifier does not flag
-# the method we want to compile as "non-compilable" because it sees
-# the method will throw IncompatibleClassChangeError.
-exec ${RUN} $@ --verify-soft-fail
diff --git a/test/636-wrong-static-access/run.py b/test/636-wrong-static-access/run.py
new file mode 100644
index 0000000..70a7fca
--- /dev/null
+++ b/test/636-wrong-static-access/run.py
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Make verification soft fail, to ensure the verifier does not flag
+ # the method we want to compile as "non-compilable" because it sees
+ # the method will throw IncompatibleClassChangeError.
+ ctx.default_run(args, verify_soft_fail=True)
diff --git a/test/638-checker-inline-cache-intrinsic/run b/test/638-checker-inline-cache-intrinsic/run
deleted file mode 100644
index 9016107..0000000
--- a/test/638-checker-inline-cache-intrinsic/run
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Set threshold to 1000 to match the iterations done in the test.
-# Pass --verbose-methods to only generate the CFG of these methods.
-# The test is for JIT, but we run in "optimizing" (AOT) mode, so that the Checker
-# stanzas in test/638-checker-inline-cache-intrinsic/src/Main.java will be checked.
-# Also pass a large JIT code cache size to avoid getting the inline caches GCed.
-exec ${RUN} --jit --runtime-option -Xjitinitialsize:32M --runtime-option -Xjitthreshold:1000 -Xcompiler-option --verbose-methods=inlineMonomorphic,inlinePolymorphic,knownReceiverType,stringEquals $@
diff --git a/test/638-checker-inline-cache-intrinsic/run.py b/test/638-checker-inline-cache-intrinsic/run.py
new file mode 100644
index 0000000..48f0f3f
--- /dev/null
+++ b/test/638-checker-inline-cache-intrinsic/run.py
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Set threshold to 1000 to match the iterations done in the test.
+ # Pass --verbose-methods to only generate the CFG of these methods.
+ # The test is for JIT, but we run in "optimizing" (AOT) mode, so that the Checker
+ # stanzas in test/638-checker-inline-cache-intrinsic/src/Main.java will be checked.
+ # Also pass a large JIT code cache size to avoid getting the inline caches GCed.
+ ctx.default_run(
+ args,
+ jit=True,
+ runtime_option=["-Xjitinitialsize:32M", "-Xjitthreshold:1000"],
+ Xcompiler_option=[
+ "--verbose-methods=inlineMonomorphic,inlinePolymorphic,knownReceiverType,stringEquals"
+ ])
diff --git a/test/638-checker-inline-caches/expected-stdout.txt b/test/638-checker-inline-caches/expected-stdout.txt
index e69de29..8f1cb06 100644
--- a/test/638-checker-inline-caches/expected-stdout.txt
+++ b/test/638-checker-inline-caches/expected-stdout.txt
@@ -0,0 +1 @@
+I don't throw
diff --git a/test/638-checker-inline-caches/profile b/test/638-checker-inline-caches/profile
index 7756a16..4630a8d 100644
--- a/test/638-checker-inline-caches/profile
+++ b/test/638-checker-inline-caches/profile
@@ -4,3 +4,4 @@
HSLMain;->inlineMegamorphic(LSuper;)I+LSubA;,LSubB;,LSubC;,LSubD;,LSubE;
HSLMain;->inlineMissingTypes(LSuper;)I+missing_types
HSLMain;->noInlineCache(LSuper;)I
+HSLMain;->noInlineSomeSubclassesThrow(LSuper;)V+LSubA;
diff --git a/test/638-checker-inline-caches/run b/test/638-checker-inline-caches/run
deleted file mode 100644
index 146e180..0000000
--- a/test/638-checker-inline-caches/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile
diff --git a/test/638-checker-inline-caches/run.py b/test/638-checker-inline-caches/run.py
new file mode 100644
index 0000000..7678899
--- /dev/null
+++ b/test/638-checker-inline-caches/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args, profile=True, Xcompiler_option=["--compiler-filter=speed-profile"])
diff --git a/test/638-checker-inline-caches/src-multidex/SubC.java b/test/638-checker-inline-caches/src-multidex/SubC.java
index f7e3c08..e0f571e 100644
--- a/test/638-checker-inline-caches/src-multidex/SubC.java
+++ b/test/638-checker-inline-caches/src-multidex/SubC.java
@@ -16,4 +16,5 @@
public class SubC extends Super {
public int getValue() { return 24; }
+ void someSubclassesThrow() { System.out.println("I don't throw"); }
}
diff --git a/test/638-checker-inline-caches/src/Main.java b/test/638-checker-inline-caches/src/Main.java
index f104e6a..7a0fb26 100644
--- a/test/638-checker-inline-caches/src/Main.java
+++ b/test/638-checker-inline-caches/src/Main.java
@@ -16,18 +16,22 @@
class SubA extends Super {
int getValue() { return 42; }
+ void someSubclassesThrow() throws Error { throw new Error("I always throw"); }
}
class SubB extends Super {
int getValue() { return 38; }
+ void someSubclassesThrow() { System.out.println("I don't throw"); }
}
class SubD extends Super {
int getValue() { return 10; }
+ void someSubclassesThrow() { System.out.println("I don't throw"); }
}
class SubE extends Super {
int getValue() { return -4; }
+ void someSubclassesThrow() { System.out.println("I don't throw"); }
}
public class Main {
@@ -134,6 +138,20 @@
return a.getValue();
}
+ // We shouldn't inline `someSubclassesThrow` since we are trying a monomorphic inline and it
+ // always throws for SubA. However, we shouldn't mark it as `always_throws` since we speculatively
+ // tried to inline and other subclasses (e.g. SubB) can call noInlineSomeSubclassesThrow and they
+ // don't throw.
+
+ /// CHECK-START: void Main.noInlineSomeSubclassesThrow(Super) inliner (before)
+ /// CHECK: InvokeVirtual method_name:Super.someSubclassesThrow always_throws:false
+
+ /// CHECK-START: void Main.noInlineSomeSubclassesThrow(Super) inliner (after)
+ /// CHECK: InvokeVirtual method_name:Super.someSubclassesThrow always_throws:false
+ public static void noInlineSomeSubclassesThrow(Super a) throws Error {
+ a.someSubclassesThrow();
+ }
+
public static void testInlineMonomorphic() {
if (inlineMonomorphicSubA(new SubA()) != 42) {
throw new Error("Expected 42");
@@ -186,11 +204,20 @@
}
}
- public static void main(String[] args) {
+ private static void $noinline$testsomeSubclassesThrow() throws Exception {
+ try {
+ noInlineSomeSubclassesThrow(new SubA());
+ throw new Exception("Unreachable");
+ } catch (Error expected) {
+ }
+ noInlineSomeSubclassesThrow(new SubB());
+ }
+
+ public static void main(String[] args) throws Exception {
testInlineMonomorphic();
testInlinePolymorhic();
testInlineMegamorphic();
testNoInlineCache();
+ $noinline$testsomeSubclassesThrow();
}
-
}
diff --git a/test/638-checker-inline-caches/src/Super.java b/test/638-checker-inline-caches/src/Super.java
index 30cdf30..1964866 100644
--- a/test/638-checker-inline-caches/src/Super.java
+++ b/test/638-checker-inline-caches/src/Super.java
@@ -16,4 +16,5 @@
public abstract class Super {
abstract int getValue();
+ abstract void someSubclassesThrow();
}
diff --git a/test/638-no-line-number/build b/test/638-no-line-number/build
deleted file mode 100644
index 9cd1955..0000000
--- a/test/638-no-line-number/build
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Stop if something fails.
-set -e
-
-# Only keep the source name, to make sure we do remove it in the stack trace
-# when there is no line number mapping.
-JAVAC_ARGS="$JAVAC_ARGS -g:source" ./default-build "$@"
diff --git a/test/638-no-line-number/build.py b/test/638-no-line-number/build.py
new file mode 100644
index 0000000..3643347
--- /dev/null
+++ b/test/638-no-line-number/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+# Only keep the source name, to make sure we do remove it in the stack trace
+# when there is no line number mapping.
+def build(ctx):
+ ctx.default_build(javac_args=["-g:source"])
diff --git a/test/639-checker-code-sinking/src/Main.java b/test/639-checker-code-sinking/src/Main.java
index 5e465f2..a42e40d 100644
--- a/test/639-checker-code-sinking/src/Main.java
+++ b/test/639-checker-code-sinking/src/Main.java
@@ -27,6 +27,7 @@
testPhiInput();
testVolatileStore();
testCatchBlock();
+ $noinline$testTwoThrowingPathsAndStringBuilderAppend();
doThrow = true;
try {
testInstanceSideEffects();
@@ -392,45 +393,13 @@
}
private static void testCatchBlock() {
- assertEquals(456, testSinkToCatchBlock());
assertEquals(456, testDoNotSinkToTry());
- assertEquals(456, testDoNotSinkToCatchInsideTry());
assertEquals(456, testSinkWithinTryBlock());
assertEquals(456, testSinkRightBeforeTryBlock());
- assertEquals(456, testSinkToSecondCatch());
assertEquals(456, testDoNotSinkToCatchInsideTryWithMoreThings(false, false));
- assertEquals(456, testSinkToCatchBlockCustomClass());
assertEquals(456, DoNotSinkWithOOMThrow());
}
- /// CHECK-START: int Main.testSinkToCatchBlock() code_sinking (before)
- /// CHECK: <<ObjLoadClass:l\d+>> LoadClass class_name:java.lang.Object
- /// CHECK: NewInstance [<<ObjLoadClass>>]
- /// CHECK: TryBoundary kind:entry
-
- /// CHECK-START: int Main.testSinkToCatchBlock() code_sinking (after)
- /// CHECK: TryBoundary kind:entry
- /// CHECK: <<ObjLoadClass:l\d+>> LoadClass class_name:java.lang.Object
- /// CHECK: NewInstance [<<ObjLoadClass>>]
-
- // Consistency check to make sure there's only one entry TryBoundary.
- /// CHECK-START: int Main.testSinkToCatchBlock() code_sinking (after)
- /// CHECK: TryBoundary kind:entry
- /// CHECK-NOT: TryBoundary kind:entry
-
- // Tests that we can sink the Object creation to the catch block.
- private static int testSinkToCatchBlock() {
- Object o = new Object();
- try {
- if (doEarlyReturn) {
- return 123;
- }
- } catch (Error e) {
- throw new Error(o.toString());
- }
- return 456;
- }
-
/// CHECK-START: int Main.testDoNotSinkToTry() code_sinking (before)
/// CHECK: <<ObjLoadClass:l\d+>> LoadClass class_name:java.lang.Object
/// CHECK: NewInstance [<<ObjLoadClass>>]
@@ -459,41 +428,6 @@
return 456;
}
- /// CHECK-START: int Main.testDoNotSinkToCatchInsideTry() code_sinking (before)
- /// CHECK: <<ObjLoadClass:l\d+>> LoadClass class_name:java.lang.Object
- /// CHECK: NewInstance [<<ObjLoadClass>>]
- /// CHECK: TryBoundary kind:entry
- /// CHECK: TryBoundary kind:entry
-
- /// CHECK-START: int Main.testDoNotSinkToCatchInsideTry() code_sinking (after)
- /// CHECK: <<ObjLoadClass:l\d+>> LoadClass class_name:java.lang.Object
- /// CHECK: NewInstance [<<ObjLoadClass>>]
- /// CHECK: TryBoundary kind:entry
- /// CHECK: TryBoundary kind:entry
-
- // Consistency check to make sure there's exactly two entry TryBoundary.
- /// CHECK-START: int Main.testDoNotSinkToCatchInsideTry() code_sinking (after)
- /// CHECK: TryBoundary kind:entry
- /// CHECK: TryBoundary kind:entry
- /// CHECK-NOT: TryBoundary kind:entry
-
- // Tests that we don't sink the Object creation into a catch handler surrounded by try/catch.
- private static int testDoNotSinkToCatchInsideTry() {
- Object o = new Object();
- try {
- try {
- if (doEarlyReturn) {
- return 123;
- }
- } catch (Error e) {
- throw new Error(o.toString());
- }
- } catch (Error e) {
- throw new Error();
- }
- return 456;
- }
-
/// CHECK-START: int Main.testSinkWithinTryBlock() code_sinking (before)
/// CHECK: <<ObjLoadClass:l\d+>> LoadClass class_name:java.lang.Object
/// CHECK: NewInstance [<<ObjLoadClass>>]
@@ -538,46 +472,6 @@
return 456;
}
- /// CHECK-START: int Main.testSinkToSecondCatch() code_sinking (before)
- /// CHECK: <<ObjLoadClass:l\d+>> LoadClass class_name:java.lang.Object
- /// CHECK: NewInstance [<<ObjLoadClass>>]
- /// CHECK: TryBoundary kind:entry
- /// CHECK: TryBoundary kind:entry
-
- /// CHECK-START: int Main.testSinkToSecondCatch() code_sinking (after)
- /// CHECK: TryBoundary kind:entry
- /// CHECK: TryBoundary kind:entry
- /// CHECK: <<ObjLoadClass:l\d+>> LoadClass class_name:java.lang.Object
- /// CHECK: NewInstance [<<ObjLoadClass>>]
-
- // Consistency check to make sure there's exactly two entry TryBoundary.
- /// CHECK-START: int Main.testSinkToSecondCatch() code_sinking (after)
- /// CHECK: TryBoundary kind:entry
- /// CHECK: TryBoundary kind:entry
- /// CHECK-NOT: TryBoundary kind:entry
- private static int testSinkToSecondCatch() {
- Object o = new Object();
- try {
- if (doEarlyReturn) {
- return 123;
- }
- } catch (Error e) {
- throw new Error();
- }
-
- try {
- // We need a different boolean to the one above, so that the compiler cannot optimize this
- // return away.
- if (doOtherEarlyReturn) {
- return 789;
- }
- } catch (Error e) {
- throw new Error(o.toString());
- }
-
- return 456;
- }
-
/// CHECK-START: int Main.testDoNotSinkToCatchInsideTryWithMoreThings(boolean, boolean) code_sinking (before)
/// CHECK-NOT: TryBoundary kind:entry
/// CHECK: <<ObjLoadClass:l\d+>> LoadClass class_name:java.lang.Object
@@ -616,38 +510,6 @@
int x;
}
- /// CHECK-START: int Main.testSinkToCatchBlockCustomClass() code_sinking (before)
- /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main$ObjectWithInt
- /// CHECK: <<Clinit:l\d+>> ClinitCheck [<<LoadClass>>]
- /// CHECK: NewInstance [<<Clinit>>]
- /// CHECK: TryBoundary kind:entry
-
- /// CHECK-START: int Main.testSinkToCatchBlockCustomClass() code_sinking (after)
- /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main$ObjectWithInt
- /// CHECK: <<Clinit:l\d+>> ClinitCheck [<<LoadClass>>]
- /// CHECK: TryBoundary kind:entry
- /// CHECK: NewInstance [<<Clinit>>]
-
- // Consistency check to make sure there's only one entry TryBoundary.
- /// CHECK-START: int Main.testSinkToCatchBlockCustomClass() code_sinking (after)
- /// CHECK: TryBoundary kind:entry
- /// CHECK-NOT: TryBoundary kind:entry
-
- // Similar to testSinkToCatchBlock, but using a custom class. CLinit check is not an instruction
- // that we sink since it can throw and it is not in the allow list. We can sink the NewInstance
- // nevertheless.
- private static int testSinkToCatchBlockCustomClass() {
- ObjectWithInt obj = new ObjectWithInt();
- try {
- if (doEarlyReturn) {
- return 123;
- }
- } catch (Error e) {
- throw new Error(Integer.toString(obj.x));
- }
- return 456;
- }
-
/// CHECK-START: int Main.DoNotSinkWithOOMThrow() code_sinking (before)
/// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main$ObjectWithInt
/// CHECK: <<Clinit:l\d+>> ClinitCheck [<<LoadClass>>]
@@ -687,12 +549,77 @@
return x;
}
+ private static void $noinline$testTwoThrowingPathsAndStringBuilderAppend() {
+ try {
+ $noinline$twoThrowingPathsAndStringBuilderAppend(null);
+ throw new Error("Unreachable");
+ } catch (Error expected) {
+ assertEquals("Object is null", expected.getMessage());
+ }
+ try {
+ $noinline$twoThrowingPathsAndStringBuilderAppend(new Object());
+ throw new Error("Unreachable");
+ } catch (Error expected) {
+ assertEquals("s1s2", expected.getMessage());
+ }
+ }
+
+ // We currently do not inline the `StringBuilder` constructor.
+ // When we did, the `StringBuilderAppend` pattern recognition was looking for
+ // the inlined `NewArray` (and its associated `LoadClass`) and checked in
+ // debug build that the `StringBuilder` has an environment use from this
+ // `NewArray` (and maybe from `LoadClass`). However, code sinking was pruning
+ // the environment of the `NewArray`, leading to a crash when compiling the
+ // code below on the device (we do not inline `core-oj` on host). b/252799691
+
+ // We currently have a heuristic that disallows inlining methods if their basic blocks end with a
+ // throw. We could add code so that `requireNonNull`'s block doesn't end with a throw but that
+ // would mean that the string builder optimization wouldn't fire as it requires all uses to be in
+ // the same block. If `requireNonNull` is inlined at some point, we need to re-mark it as $inline$
+ // so that the test is operational again.
+
+ /// CHECK-START: void Main.$noinline$twoThrowingPathsAndStringBuilderAppend(java.lang.Object) inliner (before)
+ /// CHECK: InvokeStaticOrDirect method_name:Main.requireNonNull
+
+ /// CHECK-START: void Main.$noinline$twoThrowingPathsAndStringBuilderAppend(java.lang.Object) inliner (after)
+ /// CHECK: InvokeStaticOrDirect method_name:Main.requireNonNull
+ private static void $noinline$twoThrowingPathsAndStringBuilderAppend(Object o) {
+ String s1 = "s1";
+ String s2 = "s2";
+ StringBuilder sb = new StringBuilder();
+
+ // Before inlining, the environment use from this invoke prevents the
+ // `StringBuilderAppend` pattern recognition. After inlining, we end up
+ // with two paths ending with a `Throw` and we could sink the `sb`
+ // instructions from above down to those below, enabling the
+ // `StringBuilderAppend` pattern recognition.
+ // (But that does not happen when the `StringBuilder` constructor is
+ // not inlined, see above.)
+ requireNonNull(o);
+
+ String s1s2 = sb.append(s1).append(s2).toString();
+ sb = null;
+ throw new Error(s1s2);
+ }
+
+ private static void requireNonNull(Object o) {
+ if (o == null) {
+ throw new Error("Object is null");
+ }
+ }
+
private static void assertEquals(int expected, int actual) {
if (expected != actual) {
throw new AssertionError("Expected: " + expected + ", Actual: " + actual);
}
}
+ private static void assertEquals(String expected, String actual) {
+ if (!expected.equals(actual)) {
+ throw new AssertionError("Expected: " + expected + ", Actual: " + actual);
+ }
+ }
+
volatile int volatileField;
int intField;
int intField2;
diff --git a/test/643-checker-bogus-ic/run b/test/643-checker-bogus-ic/run
deleted file mode 100644
index 146e180..0000000
--- a/test/643-checker-bogus-ic/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile
diff --git a/test/643-checker-bogus-ic/run.py b/test/643-checker-bogus-ic/run.py
new file mode 100644
index 0000000..7678899
--- /dev/null
+++ b/test/643-checker-bogus-ic/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args, profile=True, Xcompiler_option=["--compiler-filter=speed-profile"])
diff --git a/test/648-inline-caches-unresolved/run b/test/648-inline-caches-unresolved/run
deleted file mode 100644
index d24ef42..0000000
--- a/test/648-inline-caches-unresolved/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-exec ${RUN} $@ --profile
diff --git a/test/648-inline-caches-unresolved/run.py b/test/648-inline-caches-unresolved/run.py
new file mode 100644
index 0000000..5ef44b3
--- /dev/null
+++ b/test/648-inline-caches-unresolved/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, profile=True)
diff --git a/test/648-many-direct-methods/build b/test/648-many-direct-methods/build
deleted file mode 100755
index 7e888e5..0000000
--- a/test/648-many-direct-methods/build
+++ /dev/null
@@ -1,25 +0,0 @@
-#! /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.
-
-# Exit on a failure.
-set -e
-
-mkdir -p ./src
-
-# Generate the Java file or fail.
-./util-src/generate_java.py ./src
-
-./default-build "$@"
diff --git a/test/648-many-direct-methods/build.py b/test/648-many-direct-methods/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/648-many-direct-methods/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/648-many-direct-methods/generate-sources b/test/648-many-direct-methods/generate-sources
new file mode 100755
index 0000000..8907645
--- /dev/null
+++ b/test/648-many-direct-methods/generate-sources
@@ -0,0 +1,23 @@
+#! /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.
+
+# Exit on a failure.
+set -e
+
+mkdir -p ./src
+
+# Generate the Java file or fail.
+./util-src/generate_java.py ./src
diff --git a/test/652-deopt-intrinsic/run b/test/652-deopt-intrinsic/run
deleted file mode 100755
index 1acedf9..0000000
--- a/test/652-deopt-intrinsic/run
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Ensure this test is not subject to code collection.
-# We also need at least a few invocations of the method Main.$noinline$doCall
-# to ensure the inline cache sees the two types being passed to the method. Pass
-# a large number in case there's some weights on some invocation kinds (eg
-# compiler to interpreter transitions).
-exec ${RUN} "$@" --runtime-option -Xjitinitialsize:32M --runtime-option -Xjitthreshold:1000
diff --git a/test/652-deopt-intrinsic/run.py b/test/652-deopt-intrinsic/run.py
new file mode 100644
index 0000000..82f8ed0
--- /dev/null
+++ b/test/652-deopt-intrinsic/run.py
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Ensure this test is not subject to code collection.
+ # We also need at least a few invocations of the method Main.$noinline$doCall
+ # to ensure the inline cache sees the two types being passed to the method. Pass
+ # a large number in case there's some weights on some invocation kinds (eg
+ # compiler to interpreter transitions).
+ ctx.default_run(
+ args, runtime_option=["-Xjitinitialsize:32M", "-Xjitthreshold:1000"])
diff --git a/test/656-annotation-lookup-generic-jni/check b/test/656-annotation-lookup-generic-jni/check
deleted file mode 100755
index e02c84d..0000000
--- a/test/656-annotation-lookup-generic-jni/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# On gcstress configurations, an extra "JNI_OnUnload called" line may
-# be emitted. If so, remove it.
-sed -e '${/^JNI_OnUnload called$/d;}' "$2" > "$2.tmp"
-
-./default-check "$1" "$2.tmp" "$3" "$4"
diff --git a/test/656-annotation-lookup-generic-jni/run.py b/test/656-annotation-lookup-generic-jni/run.py
new file mode 100644
index 0000000..0ebb768
--- /dev/null
+++ b/test/656-annotation-lookup-generic-jni/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # On gcstress configurations, an extra "JNI_OnUnload called" line may
+ # be emitted. If so, remove it.
+ ctx.run(fr"sed -i '/^JNI_OnUnload called$/d' '{args.stdout_file}'")
diff --git a/test/660-clinit/run b/test/660-clinit/run
deleted file mode 100644
index a0e79ee..0000000
--- a/test/660-clinit/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-exec ${RUN} $@ --profile -Xcompiler-option --initialize-app-image-classes=true
diff --git a/test/660-clinit/run.py b/test/660-clinit/run.py
new file mode 100644
index 0000000..75c2bcb
--- /dev/null
+++ b/test/660-clinit/run.py
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args,
+ profile=True,
+ Xcompiler_option=["--initialize-app-image-classes=true"])
diff --git a/test/661-oat-writer-layout/run b/test/661-oat-writer-layout/run
deleted file mode 100644
index 087cd20..0000000
--- a/test/661-oat-writer-layout/run
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Always use the 'profile'.
-# Note that this test only works with --compiler-filter=speed
-# -- we accomplish this by blocklisting other compiler variants
-# and we also have to pass the option explicitly as dex2oat
-# defaults to speed-profile if a profile is specified.
-"${RUN}" "$@" --profile -Xcompiler-option --compiler-filter=speed
diff --git a/test/661-oat-writer-layout/run.py b/test/661-oat-writer-layout/run.py
new file mode 100644
index 0000000..838f3b1
--- /dev/null
+++ b/test/661-oat-writer-layout/run.py
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Always use the 'profile'.
+ # Note that this test only works with --compiler-filter=speed
+ # -- we accomplish this by blocklisting other compiler variants
+ # and we also have to pass the option explicitly as dex2oat
+ # defaults to speed-profile if a profile is specified.
+ ctx.default_run(
+ args, profile=True, Xcompiler_option=["--compiler-filter=speed"])
diff --git a/test/663-checker-select-generator/Android.bp b/test/663-checker-select-generator/Android.bp
new file mode 100644
index 0000000..c6b9087
--- /dev/null
+++ b/test/663-checker-select-generator/Android.bp
@@ -0,0 +1,43 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `663-checker-select-generator`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-663-checker-select-generator",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-663-checker-select-generator-expected-stdout",
+ ":art-run-test-663-checker-select-generator-expected-stderr",
+ ],
+ // Include the Java source files in the test's artifacts, to make Checker assertions
+ // available to the TradeFed test runner.
+ include_srcs: true,
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-663-checker-select-generator-expected-stdout",
+ out: ["art-run-test-663-checker-select-generator-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-663-checker-select-generator-expected-stderr",
+ out: ["art-run-test-663-checker-select-generator-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/663-checker-select-generator/info.txt b/test/663-checker-select-generator/info.txt
index 792779f..6a5ac27 100644
--- a/test/663-checker-select-generator/info.txt
+++ b/test/663-checker-select-generator/info.txt
@@ -1,14 +1,26 @@
Test for select generation for conditional returns.
-Tests the rewriting from:
-
+For example rewrites a simpled diamond pattern e.g.:
If [ Condition ]
/ \
false branch true branch
- return FalseValue return TrueValue
+ \ /
+ Return Phi[FalseValue, TrueValue]
to:
-
true branch
false branch
return Select [FalseValue, TrueValue, Condition]
+
+It tests:
+* Simple diamond pattern with:
+ * Same value on each branch
+ * Different value
+* Double diamond pattern (i.e. nested simple diamonds) with:
+ * Same value
+ * All different values
+ * Same value in some cases but not all
+
+For all cases it tests:
+* Branches merging with a Phi.
+* Branches returning instead of having a Phi.
\ No newline at end of file
diff --git a/test/663-checker-select-generator/smali/TestCase.smali b/test/663-checker-select-generator/smali/TestCase.smali
deleted file mode 100644
index 844a9cf..0000000
--- a/test/663-checker-select-generator/smali/TestCase.smali
+++ /dev/null
@@ -1,72 +0,0 @@
-# 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.
-
-.class public LTestCase;
-
-.super Ljava/lang/Object;
-
-## CHECK-START: boolean TestCase.testCase(boolean) select_generator (before)
-## CHECK-DAG: <<Param:z\d+>> ParameterValue
-## CHECK-DAG: <<Int0:i\d+>> IntConstant 0
-## CHECK-DAG: <<Int1:i\d+>> IntConstant 1
-## CHECK-DAG: If [<<Param>>]
-## CHECK-DAG: Return [<<Int0>>]
-## CHECK-DAG: Return [<<Int1>>]
-
-## CHECK-START: boolean TestCase.testCase(boolean) select_generator (after)
-## CHECK-DAG: <<Param:z\d+>> ParameterValue
-## CHECK-DAG: <<Int0:i\d+>> IntConstant 0
-## CHECK-DAG: <<Int1:i\d+>> IntConstant 1
-## CHECK-DAG: <<Select:i\d+>> Select [<<Int0>>,<<Int1>>,<<Param>>]
-## CHECK-DAG: Return [<<Select>>]
-
-.method public static testCase(Z)Z
- .registers 1
-
- # The select generation will replace this with a select
- # instruction and a return.
- if-eqz v0, :else
- const v0, 0x1
- return v0
-
- :else
- const v0, 0x0
- return v0
-.end method
-
-
-## CHECK-START: java.lang.Object TestCase.referenceTypeTestCase(Main$Sub1, Main$Sub2, boolean) select_generator (before)
-## CHECK-DAG: <<Param0:l\d+>> ParameterValue
-## CHECK-DAG: <<Param1:l\d+>> ParameterValue
-## CHECK-DAG: <<Param2:z\d+>> ParameterValue
-## CHECK-DAG: If [<<Param2>>]
-## CHECK-DAG: Return [<<Param1>>]
-## CHECK-DAG: Return [<<Param0>>]
-
-## CHECK-START: java.lang.Object TestCase.referenceTypeTestCase(Main$Sub1, Main$Sub2, boolean) select_generator (after)
-## CHECK-DAG: <<Param0:l\d+>> ParameterValue
-## CHECK-DAG: <<Param1:l\d+>> ParameterValue
-## CHECK-DAG: <<Param2:z\d+>> ParameterValue
-## CHECK-DAG: <<Select:l\d+>> Select [<<Param1>>,<<Param0>>,<<Param2>>]
-## CHECK-DAG: Return [<<Select>>]
-
-.method public static referenceTypeTestCase(LMain$Sub1;LMain$Sub2;Z)Ljava/lang/Object;
- .registers 3
-
- if-eqz v2, :else
- return-object v0
-
- :else
- return-object v1
-.end method
diff --git a/test/663-checker-select-generator/src/Main.java b/test/663-checker-select-generator/src/Main.java
index c5c7a43..0ab3aef 100644
--- a/test/663-checker-select-generator/src/Main.java
+++ b/test/663-checker-select-generator/src/Main.java
@@ -14,49 +14,353 @@
* limitations under the License.
*/
-import java.lang.reflect.Method;
-
public class Main {
- public static class Super {}
- public static class Sub1 {}
- public static class Sub2 {}
+ // Check that we don't generate a select since we don't have no Phi (not even at the builder
+ // stage) since both values are the same.
- public static void assertTrue(boolean result) {
- if (!result) {
- throw new Error("Expected true");
+ /// CHECK-START: int Main.$noinline$testSimpleDiamondSameValue(boolean) builder (after)
+ /// CHECK-NOT: Phi
+
+ /// CHECK-START: int Main.$noinline$testSimpleDiamondSameValue(boolean) select_generator (before)
+ /// CHECK-NOT: Phi
+
+ /// CHECK-START: int Main.$noinline$testSimpleDiamondSameValue(boolean) select_generator (after)
+ /// CHECK-NOT: Phi
+
+ /// CHECK-START: int Main.$noinline$testSimpleDiamondSameValue(boolean) select_generator (after)
+ /// CHECK-NOT: Select
+ private static int $noinline$testSimpleDiamondSameValue(boolean bool_param) {
+ int return_value;
+ if (bool_param) {
+ return_value = 10;
+ } else {
+ return_value = 10;
+ }
+ return return_value;
+ }
+
+ // Check that we generate a select for a simple diamond pattern, with different values.
+
+ /// CHECK-START: int Main.$noinline$testSimpleDiamondDifferentValue(boolean) select_generator (before)
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Arg1:i\d+>>,<<Arg2:i\d+>>]
+ /// CHECK-DAG: Return [<<Phi>>]
+ /// CHECK-EVAL: set(["<<Arg1>>","<<Arg2>>"]) == set(["<<Const10>>","<<Const20>>"])
+
+ /// CHECK-START: int Main.$noinline$testSimpleDiamondDifferentValue(boolean) select_generator (after)
+ /// CHECK-DAG: <<Bool:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const20>>,<<Const10>>,<<Bool>>]
+ /// CHECK-DAG: Return [<<Select>>]
+ private static int $noinline$testSimpleDiamondDifferentValue(boolean bool_param) {
+ int return_value;
+ if (bool_param) {
+ return_value = 10;
+ } else {
+ return_value = 20;
+ }
+ return return_value;
+ }
+
+ // Check that we don't generate a select since we don't have no Phi (not even at the builder
+ // stage) since all values are the same.
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValue(boolean, boolean) builder (after)
+ /// CHECK-NOT: Phi
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValue(boolean, boolean) select_generator (before)
+ /// CHECK-NOT: Phi
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValue(boolean, boolean) select_generator (after)
+ /// CHECK-NOT: Phi
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValue(boolean, boolean) select_generator (after)
+ /// CHECK-NOT: Select
+ private static int $noinline$testDoubleDiamondSameValue(boolean bool_param_1, boolean bool_param_2) {
+ int return_value;
+ if (bool_param_1) {
+ return_value = 10;
+ } else {
+ if (bool_param_2) {
+ return_value = 10;
+ } else {
+ return_value = 10;
+ }
+ }
+ return return_value;
+ }
+
+ // Check that we generate a select for a double diamond pattern, with a different value in the outer branch.
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValueButNotAllOuter(boolean, boolean) select_generator (before)
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Arg1:i\d+>>,<<Arg2:i\d+>>,<<Arg3:i\d+>>]
+ /// CHECK-DAG: Return [<<Phi>>]
+ /// CHECK-EVAL: set(["<<Arg1>>","<<Arg2>>","<<Arg3>>"]) == set(["<<Const10>>","<<Const20>>","<<Const20>>"])
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValueButNotAllOuter(boolean, boolean) select_generator (after)
+ /// CHECK-DAG: <<Bool1:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Bool2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const20>>,<<Const20>>,<<Bool2>>]
+ /// CHECK-DAG: <<Select2:i\d+>> Select [<<Select>>,<<Const10>>,<<Bool1>>]
+ /// CHECK-DAG: Return [<<Select2>>]
+ private static int $noinline$testDoubleDiamondSameValueButNotAllOuter(boolean bool_param_1, boolean bool_param_2) {
+ int return_value;
+ if (bool_param_1) {
+ return_value = 10;
+ } else {
+ if (bool_param_2) {
+ return_value = 20;
+ } else {
+ return_value = 20;
+ }
+ }
+ return return_value;
+ }
+
+ // Check that we generate a select for a double diamond pattern, with a different value in the inner branch.
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValueButNotAllInner(boolean, boolean) select_generator (before)
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Arg1:i\d+>>,<<Arg2:i\d+>>,<<Arg3:i\d+>>]
+ /// CHECK-DAG: Return [<<Phi>>]
+ /// CHECK-EVAL: set(["<<Arg1>>","<<Arg2>>","<<Arg3>>"]) == set(["<<Const10>>","<<Const20>>","<<Const20>>"])
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValueButNotAllInner(boolean, boolean) select_generator (after)
+ /// CHECK-DAG: <<Bool1:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Bool2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const20>>,<<Const10>>,<<Bool2>>]
+ /// CHECK-DAG: <<Select2:i\d+>> Select [<<Select>>,<<Const20>>,<<Bool1>>]
+ /// CHECK-DAG: Return [<<Select2>>]
+ private static int $noinline$testDoubleDiamondSameValueButNotAllInner(boolean bool_param_1, boolean bool_param_2) {
+ int return_value;
+ if (bool_param_1) {
+ return_value = 20;
+ } else {
+ if (bool_param_2) {
+ return_value = 10;
+ } else {
+ return_value = 20;
+ }
+ }
+ return return_value;
+ }
+
+ // Check that we generate a select for a double diamond pattern, with a all different values.
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondDifferentValue(boolean, boolean) select_generator (before)
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Const30:i\d+>> IntConstant 30
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<Arg1:i\d+>>,<<Arg2:i\d+>>,<<Arg3:i\d+>>]
+ /// CHECK-DAG: Return [<<Phi>>]
+ /// CHECK-EVAL: set(["<<Arg1>>","<<Arg2>>","<<Arg3>>"]) == set(["<<Const10>>","<<Const20>>","<<Const30>>"])
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondDifferentValue(boolean, boolean) select_generator (after)
+ /// CHECK-DAG: <<Bool1:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Bool2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Const30:i\d+>> IntConstant 30
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const30>>,<<Const20>>,<<Bool2>>]
+ /// CHECK-DAG: <<Select2:i\d+>> Select [<<Select>>,<<Const10>>,<<Bool1>>]
+ /// CHECK-DAG: Return [<<Select2>>]
+ private static int $noinline$testDoubleDiamondDifferentValue(boolean bool_param_1, boolean bool_param_2) {
+ int return_value;
+ if (bool_param_1) {
+ return_value = 10;
+ } else {
+ if (bool_param_2) {
+ return_value = 20;
+ } else {
+ return_value = 30;
+ }
+ }
+ return return_value;
+ }
+
+ private static void assertEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new AssertionError("Expected " + expected + " got " + actual);
}
}
- public static void assertFalse(boolean result) {
- if (result) {
- throw new Error("Expected false");
+ // Check that we don't generate a select since we only have a single return.
+
+ /// CHECK-START: int Main.$noinline$testSimpleDiamondSameValueWithReturn(boolean) builder (after)
+ /// CHECK: <<Const10:i\d+>> IntConstant 10
+ /// CHECK: Return [<<Const10>>]
+
+ /// CHECK-START: int Main.$noinline$testSimpleDiamondSameValueWithReturn(boolean) builder (after)
+ /// CHECK: Return
+ /// CHECK-NOT: Return
+
+ private static int $noinline$testSimpleDiamondSameValueWithReturn(boolean bool_param) {
+ if (bool_param) {
+ return 10;
+ } else {
+ return 10;
}
}
- public static void assertInstanceOfSub1(Object result) {
- if (!(result instanceof Sub1)) {
- throw new Error("Expected instance of Sub1");
+ // Same as testSimpleDiamondDifferentValue, but branches return.
+
+ /// CHECK-START: int Main.$noinline$testSimpleDiamondDifferentValueWithReturn(boolean) select_generator (before)
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: Return [<<Const10>>]
+ /// CHECK-DAG: Return [<<Const20>>]
+
+ /// CHECK-START: int Main.$noinline$testSimpleDiamondDifferentValueWithReturn(boolean) select_generator (after)
+ /// CHECK-DAG: <<Bool:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const20>>,<<Const10>>,<<Bool>>]
+ /// CHECK-DAG: Return [<<Select>>]
+ private static int $noinline$testSimpleDiamondDifferentValueWithReturn(boolean bool_param) {
+ if (bool_param) {
+ return 10;
+ } else {
+ return 20;
}
}
- public static void assertInstanceOfSub2(Object result) {
- if (!(result instanceof Sub2)) {
- throw new Error("Expected instance of Sub2");
+ // Check that we don't generate a select since we only have a single return.
+
+ /// CHECK-START: int Main.$noinline$testSimpleDiamondSameValueWithReturn(boolean) builder (after)
+ /// CHECK: <<Const10:i\d+>> IntConstant 10
+ /// CHECK: Return [<<Const10>>]
+
+ /// CHECK-START: int Main.$noinline$testSimpleDiamondSameValueWithReturn(boolean) builder (after)
+ /// CHECK: Return
+ /// CHECK-NOT: Return
+ private static int $noinline$testDoubleDiamondSameValueWithReturn(boolean bool_param_1, boolean bool_param_2) {
+ if (bool_param_1) {
+ return 10;
+ } else {
+ if (bool_param_2) {
+ return 10;
+ } else {
+ return 10;
+ }
+ }
+ }
+
+ // Same as testDoubleDiamondSameValueButNotAllOuter, but branches return.
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValueButNotAllOuterWithReturn(boolean, boolean) select_generator (before)
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: Return [<<Const10>>]
+ /// CHECK-DAG: Return [<<Const20>>]
+
+ // Note that we have 2 returns instead of 3 as the two `return 20;` get merged into one before `select_generator`.
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValueButNotAllOuterWithReturn(boolean, boolean) select_generator (before)
+ /// CHECK: Return
+ /// CHECK: Return
+ /// CHECK-NOT: Return
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValueButNotAllOuterWithReturn(boolean, boolean) select_generator (after)
+ /// CHECK-DAG: <<Bool1:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Select2:i\d+>> Select [<<Const20>>,<<Const10>>,<<Bool1>>]
+ /// CHECK-DAG: Return [<<Select2>>]
+ private static int $noinline$testDoubleDiamondSameValueButNotAllOuterWithReturn(boolean bool_param_1, boolean bool_param_2) {
+ if (bool_param_1) {
+ return 10;
+ } else {
+ if (bool_param_2) {
+ return 20;
+ } else {
+ return 20;
+ }
+ }
+ }
+
+ // Same as testDoubleDiamondSameValueButNotAllInner, but branches return.
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValueButNotAllInnerWithReturn(boolean, boolean) select_generator (before)
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: Return [<<Const10>>]
+ /// CHECK-DAG: Return [<<Const20>>]
+ /// CHECK-DAG: Return [<<Const20>>]
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondSameValueButNotAllInnerWithReturn(boolean, boolean) select_generator (after)
+ /// CHECK-DAG: <<Bool1:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Bool2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const20>>,<<Const10>>,<<Bool2>>]
+ /// CHECK-DAG: <<Select2:i\d+>> Select [<<Select>>,<<Const20>>,<<Bool1>>]
+ /// CHECK-DAG: Return [<<Select2>>]
+ private static int $noinline$testDoubleDiamondSameValueButNotAllInnerWithReturn(boolean bool_param_1, boolean bool_param_2) {
+ if (bool_param_1) {
+ return 20;
+ } else {
+ if (bool_param_2) {
+ return 10;
+ } else {
+ return 20;
+ }
+ }
+ }
+
+ // Same as testDoubleDiamondDifferentValue, but branches return.
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondDifferentValueWithReturn(boolean, boolean) select_generator (before)
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Const30:i\d+>> IntConstant 30
+ /// CHECK-DAG: Return [<<Const10>>]
+ /// CHECK-DAG: Return [<<Const20>>]
+ /// CHECK-DAG: Return [<<Const30>>]
+
+ /// CHECK-START: int Main.$noinline$testDoubleDiamondDifferentValueWithReturn(boolean, boolean) select_generator (after)
+ /// CHECK-DAG: <<Bool1:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Bool2:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Const10:i\d+>> IntConstant 10
+ /// CHECK-DAG: <<Const20:i\d+>> IntConstant 20
+ /// CHECK-DAG: <<Const30:i\d+>> IntConstant 30
+ /// CHECK-DAG: <<Select:i\d+>> Select [<<Const30>>,<<Const20>>,<<Bool2>>]
+ /// CHECK-DAG: <<Select2:i\d+>> Select [<<Select>>,<<Const10>>,<<Bool1>>]
+ /// CHECK-DAG: Return [<<Select2>>]
+ private static int $noinline$testDoubleDiamondDifferentValueWithReturn(boolean bool_param_1, boolean bool_param_2) {
+ if (bool_param_1) {
+ return 10;
+ } else {
+ if (bool_param_2) {
+ return 20;
+ } else {
+ return 30;
+ }
}
}
public static void main(String[] args) throws Throwable {
- Class<?> c = Class.forName("TestCase");
- Method m = c.getMethod("testCase", boolean.class);
- Method m2 = c.getMethod("referenceTypeTestCase", Sub1.class, Sub2.class, boolean.class);
+ // With phi
+ assertEquals(10, $noinline$testSimpleDiamondSameValue(false));
+ assertEquals(20, $noinline$testSimpleDiamondDifferentValue(false));
+ assertEquals(10, $noinline$testDoubleDiamondSameValue(false, false));
+ assertEquals(20, $noinline$testDoubleDiamondSameValueButNotAllOuter(false, false));
+ assertEquals(20, $noinline$testDoubleDiamondSameValueButNotAllInner(false, false));
+ assertEquals(30, $noinline$testDoubleDiamondDifferentValue(false, false));
- try {
- assertTrue((Boolean) m.invoke(null, true));
- assertFalse((Boolean) m.invoke(null, false));
- assertInstanceOfSub1(m2.invoke(null, new Sub1(), new Sub2(), true));
- assertInstanceOfSub2(m2.invoke(null, new Sub1(), new Sub2(), false));
- } catch (Exception e) {
- throw new Error(e);
- }
+ // With return
+ assertEquals(10, $noinline$testSimpleDiamondSameValueWithReturn(false));
+ assertEquals(20, $noinline$testSimpleDiamondDifferentValueWithReturn(false));
+ assertEquals(10, $noinline$testDoubleDiamondSameValueWithReturn(false, false));
+ assertEquals(20, $noinline$testDoubleDiamondSameValueButNotAllOuterWithReturn(false, false));
+ assertEquals(20, $noinline$testDoubleDiamondSameValueButNotAllInnerWithReturn(false, false));
+ assertEquals(30, $noinline$testDoubleDiamondDifferentValueWithReturn(false, false));
}
}
diff --git a/test/663-odd-dex-size/run b/test/663-odd-dex-size/run
deleted file mode 100644
index 51777ca..0000000
--- a/test/663-odd-dex-size/run
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2021 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.
-
-# Run normally.
-${RUN} $@
-return_status1=$?
-
-# Run without cdex to trigger the unalignment in the vdex file.
-${RUN} ${@} -Xcompiler-option --compact-dex-level=none
-return_status2=$?
-
-(exit ${return_status1}) && (exit ${return_status2})
diff --git a/test/663-odd-dex-size/run.py b/test/663-odd-dex-size/run.py
new file mode 100644
index 0000000..635d23b
--- /dev/null
+++ b/test/663-odd-dex-size/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # Run normally.
+ ctx.default_run(args)
+
+ # Run without cdex to trigger the unalignment in the vdex file.
+ ctx.default_run(args, Xcompiler_option=["--compact-dex-level=none"])
diff --git a/test/663-odd-dex-size2/build b/test/663-odd-dex-size2/build
deleted file mode 100644
index 5636558..0000000
--- a/test/663-odd-dex-size2/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Nothing to do
diff --git a/test/663-odd-dex-size2/build.py b/test/663-odd-dex-size2/build.py
new file mode 100644
index 0000000..f304d95
--- /dev/null
+++ b/test/663-odd-dex-size2/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ pass # Nothing to do.
diff --git a/test/663-odd-dex-size3/build b/test/663-odd-dex-size3/build
deleted file mode 100644
index 5636558..0000000
--- a/test/663-odd-dex-size3/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Nothing to do
diff --git a/test/663-odd-dex-size3/build.py b/test/663-odd-dex-size3/build.py
new file mode 100644
index 0000000..f304d95
--- /dev/null
+++ b/test/663-odd-dex-size3/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ pass # Nothing to do.
diff --git a/test/663-odd-dex-size4/build b/test/663-odd-dex-size4/build
deleted file mode 100644
index 5636558..0000000
--- a/test/663-odd-dex-size4/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Nothing to do
diff --git a/test/663-odd-dex-size4/build.py b/test/663-odd-dex-size4/build.py
new file mode 100644
index 0000000..f304d95
--- /dev/null
+++ b/test/663-odd-dex-size4/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ pass # Nothing to do.
diff --git a/test/667-jit-jni-stub/run b/test/667-jit-jni-stub/run
deleted file mode 100755
index b7ce913..0000000
--- a/test/667-jit-jni-stub/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Disable AOT compilation of JNI stubs.
-# Ensure this test is not subject to unexpected code collection.
-${RUN} "${@}" --no-prebuild --runtime-option -Xjitinitialsize:32M
diff --git a/test/667-jit-jni-stub/run.py b/test/667-jit-jni-stub/run.py
new file mode 100644
index 0000000..e211a9b
--- /dev/null
+++ b/test/667-jit-jni-stub/run.py
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Disable AOT compilation of JNI stubs.
+ # Ensure this test is not subject to unexpected code collection.
+ ctx.default_run(args, prebuild=False, runtime_option=["-Xjitinitialsize:32M"])
diff --git a/test/670-bitstring-type-check/build b/test/670-bitstring-type-check/build
deleted file mode 100644
index 38307f2..0000000
--- a/test/670-bitstring-type-check/build
+++ /dev/null
@@ -1,216 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Stop if something fails.
-set -e
-
-# Write out the source file.
-
-mkdir src
-cat >src/Main.java <<EOF
-/*
- * 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.
- */
-
-EOF
-
-for i in {0..8192}; do echo "class Level1Class$i { }" >>src/Main.java; done
-for i in {0..1024}; do echo "class Level2Class$i extends Level1Class0 { }" >>src/Main.java; done
-
-cat >>src/Main.java <<EOF
-class Level3Class0 extends Level2Class0 { }
-class Level4Class0 extends Level3Class0 { }
-class Level5Class0 extends Level4Class0 { }
-class Level6Class0 extends Level5Class0 { }
-class Level7Class0 extends Level6Class0 { }
-class Level8Class0 extends Level7Class0 { }
-class Level9Class0 extends Level8Class0 { }
-
-public class Main {
- public static void main(String[] args) throws Exception {
- // 8193 classes at level 1 make sure we shall have an overflow if there are 13 or
- // less bits for the level 1 character. 1025 classes at level 2 similarly guarantees
- // an overflow if the number of bits for level 2 character is 10 or less. To test
- // type checks also for the depth overflow, we provide a hierarchy 9 levels deep.
-
- // Make sure the bitstrings are initialized.
- for (int i = 0; i <= 8192; ++i) {
- Class.forName("Level1Class" + i).newInstance();
- }
- for (int i = 0; i <= 1024; ++i) {
- Class.forName("Level2Class" + i).newInstance();
- }
-
- // Note: Using a different class for tests so that verification of Main.main() does
- // not try to resolve classes used by the tests. This guarantees uninitialized type
- // check bitstrings when we enter Main.main() and start initializing them above.
- Helper.testInstanceOf();
- Helper.testCheckCast();
- }
-}
-
-class Helper {
- public static void testInstanceOf() throws Exception {
- for (int i = 1; i <= 9; ++i) {
- Object o = createInstance("Level" + i + "Class0");
- assertTrue(o instanceof Level1Class0);
- if (o instanceof Level2Class0) {
- assertFalse(i < 2);
- } else {
- assertTrue(i < 2);
- }
- if (o instanceof Level3Class0) {
- assertFalse(i < 3);
- } else {
- assertTrue(i < 3);
- }
- if (o instanceof Level4Class0) {
- assertFalse(i < 4);
- } else {
- assertTrue(i < 4);
- }
- if (o instanceof Level5Class0) {
- assertFalse(i < 5);
- } else {
- assertTrue(i < 5);
- }
- if (o instanceof Level6Class0) {
- assertFalse(i < 6);
- } else {
- assertTrue(i < 6);
- }
- if (o instanceof Level7Class0) {
- assertFalse(i < 7);
- } else {
- assertTrue(i < 7);
- }
- if (o instanceof Level8Class0) {
- assertFalse(i < 8);
- } else {
- assertTrue(i < 8);
- }
- if (o instanceof Level9Class0) {
- assertFalse(i < 9);
- } else {
- assertTrue(i < 9);
- }
- }
-
- assertTrue(createInstance("Level1Class8192") instanceof Level1Class8192);
- assertFalse(createInstance("Level1Class8192") instanceof Level1Class0);
- assertTrue(createInstance("Level2Class1024") instanceof Level2Class1024);
- assertTrue(createInstance("Level2Class1024") instanceof Level1Class0);
- assertFalse(createInstance("Level2Class1024") instanceof Level2Class0);
- }
-
- public static void testCheckCast() throws Exception {
- for (int i = 1; i <= 9; ++i) {
- Object o = createInstance("Level" + i + "Class0");
- Level1Class0 l1c0 = (Level1Class0) o;
- try {
- Level2Class0 l2c0 = (Level2Class0) o;
- assertFalse(i < 2);
- } catch (ClassCastException cce) {
- assertTrue(i < 2);
- }
- try {
- Level3Class0 l3c0 = (Level3Class0) o;
- assertFalse(i < 3);
- } catch (ClassCastException cce) {
- assertTrue(i < 3);
- }
- try {
- Level4Class0 l4c0 = (Level4Class0) o;
- assertFalse(i < 4);
- } catch (ClassCastException cce) {
- assertTrue(i < 4);
- }
- try {
- Level5Class0 l5c0 = (Level5Class0) o;
- assertFalse(i < 5);
- } catch (ClassCastException cce) {
- assertTrue(i < 5);
- }
- try {
- Level6Class0 l6c0 = (Level6Class0) o;
- assertFalse(i < 6);
- } catch (ClassCastException cce) {
- assertTrue(i < 6);
- }
- try {
- Level7Class0 l7c0 = (Level7Class0) o;
- assertFalse(i < 7);
- } catch (ClassCastException cce) {
- assertTrue(i < 7);
- }
- try {
- Level8Class0 l8c0 = (Level8Class0) o;
- assertFalse(i < 8);
- } catch (ClassCastException cce) {
- assertTrue(i < 8);
- }
- try {
- Level9Class0 l9c0 = (Level9Class0) o;
- assertFalse(i < 9);
- } catch (ClassCastException cce) {
- assertTrue(i < 9);
- }
- }
-
- Level1Class8192 l1c8192 = (Level1Class8192) createInstance("Level1Class8192");
- try {
- Level1Class0 l1c0 = (Level1Class0) createInstance("Level1Class8192");
- throw new AssertionError("Unexpected");
- } catch (ClassCastException expected) {}
- Level2Class1024 l2c1024 = (Level2Class1024) createInstance("Level2Class1024");
- Level1Class0 l1c0 = (Level1Class0) createInstance("Level2Class1024");
- try {
- Level2Class0 l2c0 = (Level2Class0) createInstance("Level2Class1024");
- throw new AssertionError("Unexpected");
- } catch (ClassCastException expected) {}
- }
-
- public static Object createInstance(String className) throws Exception {
- return Class.forName(className).newInstance();
- }
-
- public static void assertTrue(boolean value) throws Exception {
- if (!value) {
- throw new AssertionError();
- }
- }
-
- public static void assertFalse(boolean value) throws Exception {
- if (value) {
- throw new AssertionError();
- }
- }
-}
-EOF
-
-./default-build "$@"
diff --git a/test/670-bitstring-type-check/build.py b/test/670-bitstring-type-check/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/670-bitstring-type-check/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/670-bitstring-type-check/generate-sources b/test/670-bitstring-type-check/generate-sources
new file mode 100755
index 0000000..4d88839
--- /dev/null
+++ b/test/670-bitstring-type-check/generate-sources
@@ -0,0 +1,214 @@
+#!/bin/bash
+#
+# 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.
+
+# Stop if something fails.
+set -e
+
+# Write out the source file.
+
+mkdir src
+cat >src/Main.java <<EOF
+/*
+ * 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.
+ */
+
+EOF
+
+for i in {0..8192}; do echo "class Level1Class$i { }" >>src/Main.java; done
+for i in {0..1024}; do echo "class Level2Class$i extends Level1Class0 { }" >>src/Main.java; done
+
+cat >>src/Main.java <<EOF
+class Level3Class0 extends Level2Class0 { }
+class Level4Class0 extends Level3Class0 { }
+class Level5Class0 extends Level4Class0 { }
+class Level6Class0 extends Level5Class0 { }
+class Level7Class0 extends Level6Class0 { }
+class Level8Class0 extends Level7Class0 { }
+class Level9Class0 extends Level8Class0 { }
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ // 8193 classes at level 1 make sure we shall have an overflow if there are 13 or
+ // less bits for the level 1 character. 1025 classes at level 2 similarly guarantees
+ // an overflow if the number of bits for level 2 character is 10 or less. To test
+ // type checks also for the depth overflow, we provide a hierarchy 9 levels deep.
+
+ // Make sure the bitstrings are initialized.
+ for (int i = 0; i <= 8192; ++i) {
+ Class.forName("Level1Class" + i).newInstance();
+ }
+ for (int i = 0; i <= 1024; ++i) {
+ Class.forName("Level2Class" + i).newInstance();
+ }
+
+ // Note: Using a different class for tests so that verification of Main.main() does
+ // not try to resolve classes used by the tests. This guarantees uninitialized type
+ // check bitstrings when we enter Main.main() and start initializing them above.
+ Helper.testInstanceOf();
+ Helper.testCheckCast();
+ }
+}
+
+class Helper {
+ public static void testInstanceOf() throws Exception {
+ for (int i = 1; i <= 9; ++i) {
+ Object o = createInstance("Level" + i + "Class0");
+ assertTrue(o instanceof Level1Class0);
+ if (o instanceof Level2Class0) {
+ assertFalse(i < 2);
+ } else {
+ assertTrue(i < 2);
+ }
+ if (o instanceof Level3Class0) {
+ assertFalse(i < 3);
+ } else {
+ assertTrue(i < 3);
+ }
+ if (o instanceof Level4Class0) {
+ assertFalse(i < 4);
+ } else {
+ assertTrue(i < 4);
+ }
+ if (o instanceof Level5Class0) {
+ assertFalse(i < 5);
+ } else {
+ assertTrue(i < 5);
+ }
+ if (o instanceof Level6Class0) {
+ assertFalse(i < 6);
+ } else {
+ assertTrue(i < 6);
+ }
+ if (o instanceof Level7Class0) {
+ assertFalse(i < 7);
+ } else {
+ assertTrue(i < 7);
+ }
+ if (o instanceof Level8Class0) {
+ assertFalse(i < 8);
+ } else {
+ assertTrue(i < 8);
+ }
+ if (o instanceof Level9Class0) {
+ assertFalse(i < 9);
+ } else {
+ assertTrue(i < 9);
+ }
+ }
+
+ assertTrue(createInstance("Level1Class8192") instanceof Level1Class8192);
+ assertFalse(createInstance("Level1Class8192") instanceof Level1Class0);
+ assertTrue(createInstance("Level2Class1024") instanceof Level2Class1024);
+ assertTrue(createInstance("Level2Class1024") instanceof Level1Class0);
+ assertFalse(createInstance("Level2Class1024") instanceof Level2Class0);
+ }
+
+ public static void testCheckCast() throws Exception {
+ for (int i = 1; i <= 9; ++i) {
+ Object o = createInstance("Level" + i + "Class0");
+ Level1Class0 l1c0 = (Level1Class0) o;
+ try {
+ Level2Class0 l2c0 = (Level2Class0) o;
+ assertFalse(i < 2);
+ } catch (ClassCastException cce) {
+ assertTrue(i < 2);
+ }
+ try {
+ Level3Class0 l3c0 = (Level3Class0) o;
+ assertFalse(i < 3);
+ } catch (ClassCastException cce) {
+ assertTrue(i < 3);
+ }
+ try {
+ Level4Class0 l4c0 = (Level4Class0) o;
+ assertFalse(i < 4);
+ } catch (ClassCastException cce) {
+ assertTrue(i < 4);
+ }
+ try {
+ Level5Class0 l5c0 = (Level5Class0) o;
+ assertFalse(i < 5);
+ } catch (ClassCastException cce) {
+ assertTrue(i < 5);
+ }
+ try {
+ Level6Class0 l6c0 = (Level6Class0) o;
+ assertFalse(i < 6);
+ } catch (ClassCastException cce) {
+ assertTrue(i < 6);
+ }
+ try {
+ Level7Class0 l7c0 = (Level7Class0) o;
+ assertFalse(i < 7);
+ } catch (ClassCastException cce) {
+ assertTrue(i < 7);
+ }
+ try {
+ Level8Class0 l8c0 = (Level8Class0) o;
+ assertFalse(i < 8);
+ } catch (ClassCastException cce) {
+ assertTrue(i < 8);
+ }
+ try {
+ Level9Class0 l9c0 = (Level9Class0) o;
+ assertFalse(i < 9);
+ } catch (ClassCastException cce) {
+ assertTrue(i < 9);
+ }
+ }
+
+ Level1Class8192 l1c8192 = (Level1Class8192) createInstance("Level1Class8192");
+ try {
+ Level1Class0 l1c0 = (Level1Class0) createInstance("Level1Class8192");
+ throw new AssertionError("Unexpected");
+ } catch (ClassCastException expected) {}
+ Level2Class1024 l2c1024 = (Level2Class1024) createInstance("Level2Class1024");
+ Level1Class0 l1c0 = (Level1Class0) createInstance("Level2Class1024");
+ try {
+ Level2Class0 l2c0 = (Level2Class0) createInstance("Level2Class1024");
+ throw new AssertionError("Unexpected");
+ } catch (ClassCastException expected) {}
+ }
+
+ public static Object createInstance(String className) throws Exception {
+ return Class.forName(className).newInstance();
+ }
+
+ public static void assertTrue(boolean value) throws Exception {
+ if (!value) {
+ throw new AssertionError();
+ }
+ }
+
+ public static void assertFalse(boolean value) throws Exception {
+ if (value) {
+ throw new AssertionError();
+ }
+ }
+}
+EOF
diff --git a/test/670-bitstring-type-check/run b/test/670-bitstring-type-check/run
deleted file mode 100644
index a189dc5..0000000
--- a/test/670-bitstring-type-check/run
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2008 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.
-
-# This test can take 7-11 mins, so raise the default 10 min timeout.
-export ART_TIME_OUT_MULTIPLIER=2
-
-exec ${RUN} "$@"
diff --git a/test/670-bitstring-type-check/run.py b/test/670-bitstring-type-check/run.py
new file mode 100644
index 0000000..5bfe969
--- /dev/null
+++ b/test/670-bitstring-type-check/run.py
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # This test can take 7-11 mins, so raise the default 10 min timeout.
+ ctx.env.ART_TIME_OUT_MULTIPLIER = 2
+
+ ctx.default_run(args)
diff --git a/test/674-HelloWorld-Dm/run b/test/674-HelloWorld-Dm/run
deleted file mode 100644
index b8a61c5..0000000
--- a/test/674-HelloWorld-Dm/run
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-${RUN} --dex2oat-dm "${@}"
-return_status1=$?
-
-${RUN} --runtime-dm "${@}"
-return_status2=$?
-
-(exit ${return_status1}) && (exit ${return_status2})
diff --git a/test/674-HelloWorld-Dm/run.py b/test/674-HelloWorld-Dm/run.py
new file mode 100644
index 0000000..9945fec
--- /dev/null
+++ b/test/674-HelloWorld-Dm/run.py
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, dex2oat_dm=True)
+
+ ctx.default_run(args, runtime_dm=True)
diff --git a/test/674-hiddenapi/build b/test/674-hiddenapi/build
deleted file mode 100644
index 330a6de..0000000
--- a/test/674-hiddenapi/build
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-set -e
-
-# Build the jars twice. First with applying hiddenapi, creating a boot jar, then
-# a second time without to create a normal jar. We need to do this because we
-# want to load the jar once as an app module and once as a member of the boot
-# class path. The DexFileVerifier would fail on the former as it does not allow
-# hidden API access flags in dex files. DexFileVerifier is not invoked on boot
-# class path dex files, so the boot jar loads fine in the latter case.
-
-export USE_HIDDENAPI=true
-./default-build "$@"
-
-# Move the jar file into the resource folder to be bundled with the test.
-mkdir res
-mv ${TEST_NAME}.jar res/boot.jar
-
-# Clear all intermediate files otherwise default-build would either skip
-# compilation or fail rebuilding.
-rm -rf classes*
-
-export USE_HIDDENAPI=false
-./default-build "$@"
diff --git a/test/674-hiddenapi/build.py b/test/674-hiddenapi/build.py
new file mode 100644
index 0000000..c3ebb50
--- /dev/null
+++ b/test/674-hiddenapi/build.py
@@ -0,0 +1,39 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+# Build the jars twice. First with applying hiddenapi, creating a boot jar, then
+# a second time without to create a normal jar. We need to do this because we
+# want to load the jar once as an app module and once as a member of the boot
+# class path. The DexFileVerifier would fail on the former as it does not allow
+# hidden API access flags in dex files. DexFileVerifier is not invoked on boot
+# class path dex files, so the boot jar loads fine in the latter case.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build(use_hiddenapi=True)
+
+ # Move the jar file into the resource folder to be bundled with the test.
+ os.mkdir(ctx.test_dir / "res")
+ os.rename(ctx.test_dir / "674-hiddenapi.jar", ctx.test_dir / "res/boot.jar")
+
+ # Clear all intermediate files otherwise default-build would either skip
+ # compilation or fail rebuilding.
+ ctx.bash("rm -rf classes*")
+
+ ctx.default_build(use_hiddenapi=False)
diff --git a/test/674-hiddenapi/check b/test/674-hiddenapi/check
deleted file mode 100644
index c8afc22..0000000
--- a/test/674-hiddenapi/check
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Remove pid and date from the log messages.
-grep -v JNI_OnLoad "$2" \
- | grep -v JNI_OnUnload \
- > "$2.tmp"
-grep -vE '^dalvikvm(32|64) E [^]]+]' "$4" \
- > "$4.tmp"
-
-./default-check "$1" "$2.tmp" "$3" "$4.tmp"
diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc
index ebe9d10..5fa2532 100644
--- a/test/674-hiddenapi/hiddenapi.cc
+++ b/test/674-hiddenapi/hiddenapi.cc
@@ -15,13 +15,10 @@
*/
#include "base/sdk_version.h"
-#include "class_linker.h"
#include "dex/art_dex_file_loader.h"
#include "hidden_api.h"
#include "jni.h"
#include "runtime.h"
-#include "scoped_thread_state_change-inl.h"
-#include "thread.h"
#include "ti-agent/scoped_utf_chars.h"
namespace art {
@@ -77,10 +74,7 @@
Java_Main_setDexDomain(env, klass, int_index, is_core_platform);
- ScopedObjectAccess soa(Thread::Current());
- for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files[index]) {
- Runtime::Current()->GetClassLinker()->AppendToBootClassPath(Thread::Current(), dex_file.get());
- }
+ Runtime::Current()->AppendToBootClassPath(path, path, opened_dex_files[index]);
return int_index;
}
diff --git a/test/674-hiddenapi/run b/test/674-hiddenapi/run
deleted file mode 100755
index 0ab4763..0000000
--- a/test/674-hiddenapi/run
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Make verification soft fail so that we can re-verify boot classpath
-# methods at runtime.
-#
-# N.B. Compilation of secondary dexes can prevent hidden API checks, e.g. if
-# a blocklisted field get is inlined.
-exec ${RUN} $@ --verify-soft-fail --no-secondary-compilation
diff --git a/test/674-hiddenapi/run.py b/test/674-hiddenapi/run.py
new file mode 100644
index 0000000..1e364fa
--- /dev/null
+++ b/test/674-hiddenapi/run.py
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Make verification soft fail so that we can re-verify boot classpath
+ # methods at runtime.
+ #
+ # N.B. Compilation of secondary dexes can prevent hidden API checks, e.g. if
+ # a blocklisted field get is inlined.
+ ctx.default_run(args, verify_soft_fail=True, secondary_compilation=False)
+
+ ctx.run(fr"sed -i -E '/(JNI_OnLoad|JNI_OnUnload)/d' '{args.stdout_file}'")
+ ctx.run(fr"sed -i -E '/^dalvikvm(32|64) E [^]]+]/d' '{args.stderr_file}'")
diff --git a/test/674-hotness-compiled/run b/test/674-hotness-compiled/run
deleted file mode 100755
index 85e8e3b..0000000
--- a/test/674-hotness-compiled/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-${RUN} "$@" -Xcompiler-option --count-hotness-in-compiled-code
diff --git a/test/674-hotness-compiled/run.py b/test/674-hotness-compiled/run.py
new file mode 100644
index 0000000..aca5b13
--- /dev/null
+++ b/test/674-hotness-compiled/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, Xcompiler_option=["--count-hotness-in-compiled-code"])
diff --git a/test/674-vdex-uncompress/build b/test/674-vdex-uncompress/build
deleted file mode 100755
index 7b1804d..0000000
--- a/test/674-vdex-uncompress/build
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Uncompress and align the dex files so that dex2oat will not copy the dex
-# code to the .vdex file.
-./default-build "$@" --zip-compression-method store --zip-align 4
diff --git a/test/674-vdex-uncompress/build.py b/test/674-vdex-uncompress/build.py
new file mode 100644
index 0000000..cbe1f62
--- /dev/null
+++ b/test/674-vdex-uncompress/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 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.
+
+
+# Uncompress and align the dex files so that dex2oat will not copy the dex
+# code to the .vdex file.
+def build(ctx):
+ ctx.default_build(zip_compression_method="store", zip_align_bytes="4")
diff --git a/test/674-vdex-uncompress/run b/test/674-vdex-uncompress/run
deleted file mode 100644
index edf699f..0000000
--- a/test/674-vdex-uncompress/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-exec ${RUN} -Xcompiler-option --compiler-filter=verify --vdex "${@}"
diff --git a/test/674-vdex-uncompress/run.py b/test/674-vdex-uncompress/run.py
new file mode 100644
index 0000000..7684cbd
--- /dev/null
+++ b/test/674-vdex-uncompress/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args, Xcompiler_option=["--compiler-filter=verify"], vdex=True)
diff --git a/test/676-proxy-jit-at-first-use/run b/test/676-proxy-jit-at-first-use/run
deleted file mode 100644
index 16c9f76..0000000
--- a/test/676-proxy-jit-at-first-use/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Enable "jit at first use" (-Xjitthreshold:0).
-# Ensure this test is not subject to unexpected code collection.
-${RUN} "${@}" --runtime-option -Xjitthreshold:0 --runtime-option -Xjitinitialsize:32M
diff --git a/test/676-proxy-jit-at-first-use/run.py b/test/676-proxy-jit-at-first-use/run.py
new file mode 100644
index 0000000..4687094
--- /dev/null
+++ b/test/676-proxy-jit-at-first-use/run.py
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Enable "jit at first use" (-Xjitthreshold:0).
+ # Ensure this test is not subject to unexpected code collection.
+ ctx.default_run(
+ args, runtime_option=["-Xjitthreshold:0", "-Xjitinitialsize:32M"])
diff --git a/test/676-resolve-field-type/build.py b/test/676-resolve-field-type/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/676-resolve-field-type/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/676-resolve-field-type/test-metadata.json b/test/676-resolve-field-type/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/676-resolve-field-type/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/677-fsi/build b/test/677-fsi/build
deleted file mode 100755
index b90b408..0000000
--- a/test/677-fsi/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-./default-build "$@" --zip-compression-method store --zip-align 4
diff --git a/test/677-fsi/build.py b/test/677-fsi/build.py
new file mode 100644
index 0000000..69afbf3
--- /dev/null
+++ b/test/677-fsi/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(zip_compression_method="store", zip_align_bytes=4)
diff --git a/test/677-fsi/check b/test/677-fsi/check
deleted file mode 100644
index 8c7f18b..0000000
--- a/test/677-fsi/check
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Only keep the lines we're interested in.
-sed -s '/Hello World/!d' "$2" > "$2.tmp"
-sed -s '/^.*: oat file has dex code, but APK has uncompressed dex code/!d' "$4" > "$4.tmp"
-
-# Remove part of message containing filename.
-sed -s 's/^.*: //' "$4.tmp" > "$4.tmp2"
-
-diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4.tmp2" >/dev/null
diff --git a/test/677-fsi/run b/test/677-fsi/run
deleted file mode 100644
index 30d925e..0000000
--- a/test/677-fsi/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Redirect logger to stderr, as the test relies on error
-# messages being printed there.
-exec ${RUN} $@ -Xcompiler-option --copy-dex-files=always --runtime-option -Xonly-use-system-oat-files --runtime-option -Xuse-stderr-logger
diff --git a/test/677-fsi/run.py b/test/677-fsi/run.py
new file mode 100644
index 0000000..357692f
--- /dev/null
+++ b/test/677-fsi/run.py
@@ -0,0 +1,33 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Redirect logger to stderr, as the test relies on error
+ # messages being printed there.
+ ctx.default_run(
+ args,
+ Xcompiler_option=["--copy-dex-files=always"],
+ runtime_option=["-Xonly-use-system-oat-files", "-Xuse-stderr-logger"])
+
+ # Only keep the lines we're interested in.
+ ctx.run(fr"sed -i '/Hello World/!d' '{args.stdout_file}'")
+ ctx.run(
+ fr"sed -i '/^.*: oat file has dex code, but APK has uncompressed dex code/!d' '{args.stderr_file}'"
+ )
+
+ # Remove part of message containing filename.
+ ctx.run(fr"sed -i 's/^.*: //' '{args.stderr_file}'")
diff --git a/test/677-fsi2/run b/test/677-fsi2/run
deleted file mode 100644
index 651f082..0000000
--- a/test/677-fsi2/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-${RUN} $@ --runtime-option -Xonly-use-system-oat-files
diff --git a/test/677-fsi2/run.py b/test/677-fsi2/run.py
new file mode 100644
index 0000000..0bbabb9
--- /dev/null
+++ b/test/677-fsi2/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, runtime_option=["-Xonly-use-system-oat-files"])
diff --git a/test/678-quickening/run b/test/678-quickening/run
deleted file mode 100644
index 0cc87f3..0000000
--- a/test/678-quickening/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.i
-
-# Run without an app image to prevent the class NotLoaded to be loaded at startup.
-exec ${RUN} "${@}" --no-app-image
diff --git a/test/678-quickening/run.py b/test/678-quickening/run.py
new file mode 100644
index 0000000..ea7a2bb
--- /dev/null
+++ b/test/678-quickening/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.i
+
+
+def run(ctx, args):
+ # Run without an app image to prevent the class NotLoaded to be loaded at startup.
+ ctx.default_run(args, app_image=False)
diff --git a/test/679-locks/run b/test/679-locks/run
deleted file mode 100644
index 0cc87f3..0000000
--- a/test/679-locks/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.i
-
-# Run without an app image to prevent the class NotLoaded to be loaded at startup.
-exec ${RUN} "${@}" --no-app-image
diff --git a/test/679-locks/run.py b/test/679-locks/run.py
new file mode 100644
index 0000000..ea7a2bb
--- /dev/null
+++ b/test/679-locks/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.i
+
+
+def run(ctx, args):
+ # Run without an app image to prevent the class NotLoaded to be loaded at startup.
+ ctx.default_run(args, app_image=False)
diff --git a/test/683-clinit-inline-static-invoke/src-multidex/MyCalendarUtils.java b/test/683-clinit-inline-static-invoke/src-multidex/MyCalendarUtils.java
new file mode 100644
index 0000000..fc79e32
--- /dev/null
+++ b/test/683-clinit-inline-static-invoke/src-multidex/MyCalendarUtils.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+import sun.util.calendar.CalendarUtils;
+
+public abstract class MyCalendarUtils extends CalendarUtils {
+ // Reference to MyCalendarUtils.isJulianLeapYear() shall resolve to
+ // CalendarUtils.isJulianLeapYear() which should be easily inlined.
+}
diff --git a/test/683-clinit-inline-static-invoke/src-multidex/MyModifier.java b/test/683-clinit-inline-static-invoke/src-multidex/MyModifier.java
deleted file mode 100644
index bc01940..0000000
--- a/test/683-clinit-inline-static-invoke/src-multidex/MyModifier.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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.
- */
-
-import java.lang.reflect.Modifier;
-
-public abstract class MyModifier extends Modifier {
- // Reference to MyModifier.classModifiers() shall resolve to
- // Modifier.classModifiers() which should be easily inlined.
-}
diff --git a/test/683-clinit-inline-static-invoke/src/Main.java b/test/683-clinit-inline-static-invoke/src/Main.java
index 6f15e27..d222539 100644
--- a/test/683-clinit-inline-static-invoke/src/Main.java
+++ b/test/683-clinit-inline-static-invoke/src/Main.java
@@ -26,6 +26,6 @@
// TypeId in the current DexFile, we erroneously provided the type index from the
// declaring DexFile and that caused a crash. This was fixed by changing the
// ClinitCheck entrypoint to take the Class reference from LoadClass.
- int placeholder = MyModifier.classModifiers();
+ boolean placeholder = MyCalendarUtils.isJulianLeapYear(-43);
}
}
diff --git a/test/688-shared-library/check b/test/688-shared-library/check
deleted file mode 100644
index 8501835..0000000
--- a/test/688-shared-library/check
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Finalizers of DexFile will complain not being able to close
-# the main dex file, as it's still open. That's OK to ignore.
-# Oat file manager will also complain about duplicate dex files. Ignore.
-sed -e '/^E\/System/d' "$4" | sed -e '/.*oat_file_manager.*/d' > "$4.tmp"
-
-diff --strip-trailing-cr -q "$1" "$2" >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4.tmp" >/dev/null
diff --git a/test/688-shared-library/run b/test/688-shared-library/run
deleted file mode 100644
index fa6ab58..0000000
--- a/test/688-shared-library/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# App images are incompatible with what the test is doing: loading one
-# dex file multiple times.
-exec ${RUN} "${@}" --no-app-image
diff --git a/test/688-shared-library/run.py b/test/688-shared-library/run.py
new file mode 100644
index 0000000..f71e350
--- /dev/null
+++ b/test/688-shared-library/run.py
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # App images are incompatible with what the test is doing: loading one
+ # dex file multiple times.
+ ctx.default_run(args, app_image=False)
+
+ # Finalizers of DexFile will complain not being able to close
+ # the main dex file, as it's still open. That's OK to ignore.
+ # Oat file manager will also complain about duplicate dex files. Ignore.
+ ctx.run(
+ fr"sed -i -e '/^E\/System/d' -e '/.*oat_file_manager.*/d' '{args.stderr_file}'"
+ )
diff --git a/test/689-zygote-jit-deopt/build.py b/test/689-zygote-jit-deopt/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/689-zygote-jit-deopt/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/689-zygote-jit-deopt/run b/test/689-zygote-jit-deopt/run
deleted file mode 100644
index 7b4b7eb..0000000
--- a/test/689-zygote-jit-deopt/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-./default-run "$@" --zygote
diff --git a/test/689-zygote-jit-deopt/run.py b/test/689-zygote-jit-deopt/run.py
new file mode 100644
index 0000000..3c4f0fe
--- /dev/null
+++ b/test/689-zygote-jit-deopt/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, zygote=True)
diff --git a/test/689-zygote-jit-deopt/test-metadata.json b/test/689-zygote-jit-deopt/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/689-zygote-jit-deopt/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/690-hiddenapi-same-name-methods/build b/test/690-hiddenapi-same-name-methods/build
deleted file mode 100644
index c364b3b..0000000
--- a/test/690-hiddenapi-same-name-methods/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-USE_HIDDENAPI=true ./default-build "$@"
diff --git a/test/690-hiddenapi-same-name-methods/build.py b/test/690-hiddenapi-same-name-methods/build.py
new file mode 100644
index 0000000..942bb00
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(use_hiddenapi=True)
diff --git a/test/691-hiddenapi-proxy/build b/test/691-hiddenapi-proxy/build
deleted file mode 100644
index c364b3b..0000000
--- a/test/691-hiddenapi-proxy/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-USE_HIDDENAPI=true ./default-build "$@"
diff --git a/test/691-hiddenapi-proxy/build.py b/test/691-hiddenapi-proxy/build.py
new file mode 100644
index 0000000..942bb00
--- /dev/null
+++ b/test/691-hiddenapi-proxy/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(use_hiddenapi=True)
diff --git a/test/692-vdex-inmem-loader/build.py b/test/692-vdex-inmem-loader/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/692-vdex-inmem-loader/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/692-vdex-inmem-loader/test-metadata.json b/test/692-vdex-inmem-loader/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/692-vdex-inmem-loader/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/692-vdex-inmem-loader/vdex_inmem_loader.cc b/test/692-vdex-inmem-loader/vdex_inmem_loader.cc
index 1c7b6e8..a478f26 100644
--- a/test/692-vdex-inmem-loader/vdex_inmem_loader.cc
+++ b/test/692-vdex-inmem-loader/vdex_inmem_loader.cc
@@ -45,7 +45,7 @@
std::vector<const DexFile*> dex_files;
VisitClassLoaderDexFiles(
- soa,
+ soa.Self(),
h_loader,
[&](const DexFile* dex_file) {
dex_files.push_back(dex_file);
@@ -84,7 +84,7 @@
std::vector<const DexFile*> dex_files;
VisitClassLoaderDexFiles(
- soa,
+ soa.Self(),
h_loader,
[&](const DexFile* dex_file) {
dex_files.push_back(dex_file);
@@ -116,7 +116,7 @@
bool all_backed_by_oat = false;
VisitClassLoaderDexFiles(
- soa,
+ soa.Self(),
h_loader,
[&](const DexFile* dex_file) {
bool is_backed_by_oat = (dex_file->GetOatDexFile() != nullptr);
@@ -141,7 +141,7 @@
std::vector<const DexFile*> dex_files;
VisitClassLoaderDexFiles(
- soa,
+ soa.Self(),
h_loader,
[&](const DexFile* dex_file) {
dex_files.push_back(dex_file);
diff --git a/test/692-vdex-secondary-loader/build.py b/test/692-vdex-secondary-loader/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/692-vdex-secondary-loader/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/692-vdex-secondary-loader/run b/test/692-vdex-secondary-loader/run
deleted file mode 100644
index 35b55d6..0000000
--- a/test/692-vdex-secondary-loader/run
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2021 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.
-
-# Disable dex2oat of secondary dex files.
-${RUN} "$@" --no-secondary-compilation
-return_status1=$?
-
-# Set low RAM to hit the Madvise code which used to crash
-${RUN} "$@" --runtime-option -XX:LowMemoryMode --no-secondary-compilation
-return_status2=$?
-
-# Make sure we don't silently ignore an early failure.
-(exit $return_status1) && (exit $return_status2)
diff --git a/test/692-vdex-secondary-loader/run.py b/test/692-vdex-secondary-loader/run.py
new file mode 100644
index 0000000..7c9de5c
--- /dev/null
+++ b/test/692-vdex-secondary-loader/run.py
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # Disable dex2oat of secondary dex files.
+ ctx.default_run(args, secondary_compilation=False)
+
+ # Set low RAM to hit the Madvise code which used to crash
+ ctx.default_run(
+ args, runtime_option=["-XX:LowMemoryMode"], secondary_compilation=False)
diff --git a/test/692-vdex-secondary-loader/test-metadata.json b/test/692-vdex-secondary-loader/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/692-vdex-secondary-loader/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/693-vdex-inmem-loader-evict/build.py b/test/693-vdex-inmem-loader-evict/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/693-vdex-inmem-loader-evict/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/693-vdex-inmem-loader-evict/test-metadata.json b/test/693-vdex-inmem-loader-evict/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/693-vdex-inmem-loader-evict/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/695-simplify-throws/src/Main.java b/test/695-simplify-throws/src/Main.java
index 2799624..9a95d80 100644
--- a/test/695-simplify-throws/src/Main.java
+++ b/test/695-simplify-throws/src/Main.java
@@ -15,31 +15,31 @@
*/
public class Main {
- public static boolean alwaysThrows() {
- throw new Error("");
- }
-
- public static void test() {
- alwaysThrows();
- while (condition) {
- int a = 2;
- while (otherCondition) {
- a = 3;
- }
- staticField = a;
+ public static boolean alwaysThrows() {
+ throw new Error("");
}
- }
- public static void main(String[] args) throws Exception {
- try {
- test();
- throw new Exception("Unexpected exception");
- } catch (Error e) {
- // Expected.
+ public static void test() {
+ alwaysThrows();
+ while (condition) {
+ int a = 2;
+ while (otherCondition) {
+ a = 3;
+ }
+ staticField = a;
+ }
}
- }
- static boolean condition = false;
- static boolean otherCondition = false;
- static int staticField = 1;
+ public static void main(String[] args) throws Exception {
+ try {
+ test();
+ throw new Exception("Unexpected exception");
+ } catch (Error e) {
+ // Expected.
+ }
+ }
+
+ static boolean condition = false;
+ static boolean otherCondition = false;
+ static int staticField = 1;
}
diff --git a/test/696-loop/src/Main.java b/test/696-loop/src/Main.java
index b92166a..9763a8e 100644
--- a/test/696-loop/src/Main.java
+++ b/test/696-loop/src/Main.java
@@ -15,24 +15,24 @@
*/
public class Main {
- static int[] sA = new int[12];
- static int a = 1;
+ static int[] sA = new int[12];
+ static int a = 1;
- static void doIt(int n) {
- for (int i = 0; i < 2; i++) {
- n+=a;
+ static void doIt(int n) {
+ for (int i = 0; i < 2; i++) {
+ n += a;
+ }
+ for (int i = 0; i < n; i++) {
+ sA[i] += 1;
+ }
}
- for (int i = 0; i < n; i++) {
- sA[i] += 1;
- }
- }
- public static void main(String[] args) {
- doIt(10);
- for (int i = 0; i < sA.length; i++) {
- if (sA[i] != 1) {
- throw new Error("Expected 1, got " + sA[i]);
- }
+ public static void main(String[] args) {
+ doIt(10);
+ for (int i = 0; i < sA.length; i++) {
+ if (sA[i] != 1) {
+ throw new Error("Expected 1, got " + sA[i]);
+ }
+ }
}
- }
}
diff --git a/test/697-checker-string-append/src/Main.java b/test/697-checker-string-append/src/Main.java
index c63c328..e35986a 100644
--- a/test/697-checker-string-append/src/Main.java
+++ b/test/697-checker-string-append/src/Main.java
@@ -18,6 +18,9 @@
public static void main(String[] args) {
testAppendStringAndLong();
testAppendStringAndInt();
+ testAppendStringAndFloat();
+ testAppendStringAndDouble();
+ testAppendDoubleAndFloat();
testAppendStringAndString();
testMiscelaneous();
testNoArgs();
@@ -186,6 +189,159 @@
}
}
+ private static final String APPEND_FLOAT_PREFIX = "Float/";
+ private static final String[] APPEND_FLOAT_TEST_CASES = {
+ // We're testing only exact values here, i.e. values that do not require rounding.
+ "Float/1.0",
+ "Float/9.0",
+ "Float/10.0",
+ "Float/99.0",
+ "Float/100.0",
+ "Float/999.0",
+ "Float/1000.0",
+ "Float/9999.0",
+ "Float/10000.0",
+ "Float/99999.0",
+ "Float/100000.0",
+ "Float/999999.0",
+ "Float/1000000.0",
+ "Float/9999999.0",
+ "Float/1.0E7",
+ "Float/1.0E10",
+ "Float/-1.0",
+ "Float/-9.0",
+ "Float/-10.0",
+ "Float/-99.0",
+ "Float/-100.0",
+ "Float/-999.0",
+ "Float/-1000.0",
+ "Float/-9999.0",
+ "Float/-10000.0",
+ "Float/-99999.0",
+ "Float/-100000.0",
+ "Float/-999999.0",
+ "Float/-1000000.0",
+ "Float/-9999999.0",
+ "Float/-1.0E7",
+ "Float/-1.0E10",
+ "Float/0.25",
+ "Float/1.625",
+ "Float/9.3125",
+ "Float/-0.25",
+ "Float/-1.625",
+ "Float/-9.3125",
+ };
+
+ /// CHECK-START: java.lang.String Main.$noinline$appendStringAndFloat(java.lang.String, float) instruction_simplifier (before)
+ /// CHECK-NOT: StringBuilderAppend
+
+ /// CHECK-START: java.lang.String Main.$noinline$appendStringAndFloat(java.lang.String, float) instruction_simplifier (after)
+ /// CHECK: StringBuilderAppend
+ public static String $noinline$appendStringAndFloat(String s, float f) {
+ return new StringBuilder().append(s).append(f).toString();
+ }
+
+ public static void testAppendStringAndFloat() {
+ for (String expected : APPEND_FLOAT_TEST_CASES) {
+ float f = Float.valueOf(expected.substring(APPEND_FLOAT_PREFIX.length()));
+ String result = $noinline$appendStringAndFloat(APPEND_FLOAT_PREFIX, f);
+ assertEquals(expected, result);
+ }
+ // Special values.
+ assertEquals("Float/NaN", $noinline$appendStringAndFloat(APPEND_FLOAT_PREFIX, Float.NaN));
+ assertEquals("Float/Infinity",
+ $noinline$appendStringAndFloat(APPEND_FLOAT_PREFIX, Float.POSITIVE_INFINITY));
+ assertEquals("Float/-Infinity",
+ $noinline$appendStringAndFloat(APPEND_FLOAT_PREFIX, Float.NEGATIVE_INFINITY));
+ }
+
+ private static final String APPEND_DOUBLE_PREFIX = "Double/";
+ private static final String[] APPEND_DOUBLE_TEST_CASES = {
+ // We're testing only exact values here, i.e. values that do not require rounding.
+ "Double/1.0",
+ "Double/9.0",
+ "Double/10.0",
+ "Double/99.0",
+ "Double/100.0",
+ "Double/999.0",
+ "Double/1000.0",
+ "Double/9999.0",
+ "Double/10000.0",
+ "Double/99999.0",
+ "Double/100000.0",
+ "Double/999999.0",
+ "Double/1000000.0",
+ "Double/9999999.0",
+ "Double/1.0E7",
+ "Double/1.0E24",
+ "Double/-1.0",
+ "Double/-9.0",
+ "Double/-10.0",
+ "Double/-99.0",
+ "Double/-100.0",
+ "Double/-999.0",
+ "Double/-1000.0",
+ "Double/-9999.0",
+ "Double/-10000.0",
+ "Double/-99999.0",
+ "Double/-100000.0",
+ "Double/-999999.0",
+ "Double/-1000000.0",
+ "Double/-9999999.0",
+ "Double/-1.0E7",
+ "Double/-1.0E24",
+ "Double/0.25",
+ "Double/1.625",
+ "Double/9.3125",
+ "Double/-0.25",
+ "Double/-1.625",
+ "Double/-9.3125",
+ };
+
+ /// CHECK-START: java.lang.String Main.$noinline$appendStringAndDouble(java.lang.String, double) instruction_simplifier (before)
+ /// CHECK-NOT: StringBuilderAppend
+
+ /// CHECK-START: java.lang.String Main.$noinline$appendStringAndDouble(java.lang.String, double) instruction_simplifier (after)
+ /// CHECK: StringBuilderAppend
+ public static String $noinline$appendStringAndDouble(String s, double d) {
+ return new StringBuilder().append(s).append(d).toString();
+ }
+
+ public static void testAppendStringAndDouble() {
+ for (String expected : APPEND_DOUBLE_TEST_CASES) {
+ double f = Double.valueOf(expected.substring(APPEND_DOUBLE_PREFIX.length()));
+ String result = $noinline$appendStringAndDouble(APPEND_DOUBLE_PREFIX, f);
+ assertEquals(expected, result);
+ }
+ // Special values.
+ assertEquals(
+ "Double/NaN",
+ $noinline$appendStringAndDouble(APPEND_DOUBLE_PREFIX, Double.NaN));
+ assertEquals(
+ "Double/Infinity",
+ $noinline$appendStringAndDouble(APPEND_DOUBLE_PREFIX, Double.POSITIVE_INFINITY));
+ assertEquals(
+ "Double/-Infinity",
+ $noinline$appendStringAndDouble(APPEND_DOUBLE_PREFIX, Double.NEGATIVE_INFINITY));
+ }
+
+ /// CHECK-START: java.lang.String Main.$noinline$appendDoubleAndFloat(double, float) instruction_simplifier (before)
+ /// CHECK-NOT: StringBuilderAppend
+
+ /// CHECK-START: java.lang.String Main.$noinline$appendDoubleAndFloat(double, float) instruction_simplifier (after)
+ /// CHECK: StringBuilderAppend
+ public static String $noinline$appendDoubleAndFloat(double d, float f) {
+ return new StringBuilder().append(d).append(f).toString();
+ }
+
+ public static void testAppendDoubleAndFloat() {
+ assertEquals("1.50.325", $noinline$appendDoubleAndFloat(1.5, 0.325f));
+ assertEquals("1.5E170.3125", $noinline$appendDoubleAndFloat(1.5E17, 0.3125f));
+ assertEquals("1.0E8NaN", $noinline$appendDoubleAndFloat(1.0E8, Float.NaN));
+ assertEquals("Infinity0.5", $noinline$appendDoubleAndFloat(Double.POSITIVE_INFINITY, 0.5f));
+ assertEquals("2.5-Infinity", $noinline$appendDoubleAndFloat(2.5, Float.NEGATIVE_INFINITY));
+ }
+
public static String $noinline$appendStringAndString(String s1, String s2) {
return new StringBuilder().append(s1).append(s2).toString();
}
diff --git a/test/698-selects/src/Main.java b/test/698-selects/src/Main.java
index 1fadb86..514770a 100644
--- a/test/698-selects/src/Main.java
+++ b/test/698-selects/src/Main.java
@@ -15,28 +15,28 @@
*/
public class Main {
- public static int mZenMode = 0;
+ public static int mZenMode = 0;
- public static int $noinline$foo(int internal, boolean check1, boolean check2) {
- int result = internal;
- if (check1) {
- // This block is to ensure `result` is a phi in the return block. Without this block
- // the compiler could just generate one block with selects.
- if (check2) {
- mZenMode = 42;
- }
- result = (internal == 1) ? 1 : 0;
+ public static int $noinline$foo(int internal, boolean check1, boolean check2) {
+ int result = internal;
+ if (check1) {
+ // This block is to ensure `result` is a phi in the return block. Without this block
+ // the compiler could just generate one block with selects.
+ if (check2) {
+ mZenMode = 42;
+ }
+ result = (internal == 1) ? 1 : 0;
+ }
+ // The optimization bug was to make the incorrect assumption that:
+ // phi = (internal, (internal == 1))
+ // meant `internal` was a boolean.
+ return result;
}
- // The optimization bug was to make the incorrect assumption that:
- // phi = (internal, (internal == 1))
- // meant `internal` was a boolean.
- return result;
- }
- public static void main(String[] args) {
- int result = $noinline$foo(2, true, true);
- if (result != 0) {
- throw new Error("Expected 0, got " + result);
+ public static void main(String[] args) {
+ int result = $noinline$foo(2, true, true);
+ if (result != 0) {
+ throw new Error("Expected 0, got " + result);
+ }
}
- }
}
diff --git a/test/701-easy-div-rem/build b/test/701-easy-div-rem/build
deleted file mode 100644
index 6d114b6..0000000
--- a/test/701-easy-div-rem/build
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Stop if something fails.
-set -e
-
-# Write out the source file.
-mkdir src
-python3 ./genMain.py
-
-./default-build "$@"
diff --git a/test/701-easy-div-rem/build.py b/test/701-easy-div-rem/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/701-easy-div-rem/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/701-easy-div-rem/generate-sources b/test/701-easy-div-rem/generate-sources
new file mode 100755
index 0000000..efe4b05
--- /dev/null
+++ b/test/701-easy-div-rem/generate-sources
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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.
+
+# Stop if something fails.
+set -e
+
+# Write out the source file.
+mkdir src
+python3 ./genMain.py
diff --git a/test/702-LargeBranchOffset/build b/test/702-LargeBranchOffset/build
deleted file mode 100644
index 2505b0a..0000000
--- a/test/702-LargeBranchOffset/build
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Stop if something fails.
-set -e
-
-# Write out the source file.
-mkdir -p src
-./generate
-
-./default-build "$@"
diff --git a/test/702-LargeBranchOffset/build.py b/test/702-LargeBranchOffset/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/702-LargeBranchOffset/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/702-LargeBranchOffset/generate-sources b/test/702-LargeBranchOffset/generate-sources
new file mode 100755
index 0000000..b01afc4
--- /dev/null
+++ b/test/702-LargeBranchOffset/generate-sources
@@ -0,0 +1,22 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 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.
+
+# Stop if something fails.
+set -e
+
+# Write out the source file.
+mkdir -p src
+./generate
diff --git a/test/706-checker-scheduler/src/Main.java b/test/706-checker-scheduler/src/Main.java
index 1b8377d..255599d 100644
--- a/test/706-checker-scheduler/src/Main.java
+++ b/test/706-checker-scheduler/src/Main.java
@@ -66,23 +66,23 @@
}
/// CHECK-START-ARM: void Main.arrayAccessVariable(int) scheduler (before)
- /// CHECK: <<Param:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Param:i\d+>> ParameterValue
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
/// CHECK-DAG: <<Const3:i\d+>> IntConstant -1
- /// CHECK: <<Add1:i\d+>> Add [<<Param>>,<<Const1>>]
- /// CHECK: <<Add2:i\d+>> Add [<<Param>>,<<Const2>>]
- /// CHECK: <<Add3:i\d+>> Add [<<Param>>,<<Const3>>]
- /// CHECK: <<Array:i\d+>> IntermediateAddress
- /// CHECK: <<ArrayGet1:i\d+>> ArrayGet [<<Array>>,<<Add1>>]
- /// CHECK: <<AddArray1:i\d+>> Add [<<ArrayGet1>>,<<Const1>>]
- /// CHECK: <<ArraySet1:v\d+>> ArraySet [<<Array>>,<<Add1>>,<<AddArray1>>]
- /// CHECK: <<ArrayGet2:i\d+>> ArrayGet [<<Array>>,<<Add2>>]
- /// CHECK: <<AddArray2:i\d+>> Add [<<ArrayGet2>>,<<Const1>>]
- /// CHECK: <<ArraySet2:v\d+>> ArraySet [<<Array>>,<<Add2>>,<<AddArray2>>]
- /// CHECK: <<ArrayGet3:i\d+>> ArrayGet [<<Array>>,<<Add3>>]
- /// CHECK: <<AddArray3:i\d+>> Add [<<ArrayGet3>>,<<Const1>>]
- /// CHECK: <<ArraySet3:v\d+>> ArraySet [<<Array>>,<<Add3>>,<<AddArray3>>]
+ /// CHECK-DAG: <<Add1:i\d+>> Add [<<Param>>,<<Const1>>]
+ /// CHECK-DAG: <<Add2:i\d+>> Add [<<Param>>,<<Const2>>]
+ /// CHECK-DAG: <<Add3:i\d+>> Add [<<Param>>,<<Const3>>]
+ /// CHECK-DAG: <<Array:i\d+>> IntermediateAddress
+ /// CHECK-DAG: <<ArrayGet1:i\d+>> ArrayGet [<<Array>>,<<Add1>>]
+ /// CHECK-DAG: <<ArrayGet2:i\d+>> ArrayGet [<<Array>>,<<Add2>>]
+ /// CHECK-DAG: <<ArrayGet3:i\d+>> ArrayGet [<<Array>>,<<Add3>>]
+ /// CHECK-DAG: <<AddArray1:i\d+>> Add [<<ArrayGet1>>,<<Const2>>]
+ /// CHECK-DAG: {{v\d+}} ArraySet [<<Array>>,<<Add1>>,<<AddArray1>>]
+ /// CHECK-DAG: <<AddArray2:i\d+>> Add [<<ArrayGet2>>,<<Const2>>]
+ /// CHECK-DAG: {{v\d+}} ArraySet [<<Array>>,<<Add2>>,<<AddArray2>>]
+ /// CHECK-DAG: <<AddArray3:i\d+>> Add [<<ArrayGet3>>,<<Const2>>]
+ /// CHECK-DAG: {{v\d+}} ArraySet [<<Array>>,<<Add3>>,<<AddArray3>>]
/// CHECK-START-ARM: void Main.arrayAccessVariable(int) scheduler (after)
/// CHECK: <<Param:i\d+>> ParameterValue
@@ -104,23 +104,23 @@
/// CHECK: ArraySet
/// CHECK-START-ARM64: void Main.arrayAccessVariable(int) scheduler (before)
- /// CHECK: <<Param:i\d+>> ParameterValue
+ /// CHECK-DAG: <<Param:i\d+>> ParameterValue
/// CHECK-DAG: <<Const1:i\d+>> IntConstant 1
/// CHECK-DAG: <<Const2:i\d+>> IntConstant 2
/// CHECK-DAG: <<Const3:i\d+>> IntConstant -1
- /// CHECK: <<Add1:i\d+>> Add [<<Param>>,<<Const1>>]
- /// CHECK: <<Add2:i\d+>> Add [<<Param>>,<<Const2>>]
- /// CHECK: <<Add3:i\d+>> Add [<<Param>>,<<Const3>>]
- /// CHECK: <<Array:i\d+>> IntermediateAddress
- /// CHECK: <<ArrayGet1:i\d+>> ArrayGet [<<Array>>,<<Add1>>]
- /// CHECK: <<AddArray1:i\d+>> Add [<<ArrayGet1>>,<<Const1>>]
- /// CHECK: <<ArraySet1:v\d+>> ArraySet [<<Array>>,<<Add1>>,<<AddArray1>>]
- /// CHECK: <<ArrayGet2:i\d+>> ArrayGet [<<Array>>,<<Add2>>]
- /// CHECK: <<AddArray2:i\d+>> Add [<<ArrayGet2>>,<<Const1>>]
- /// CHECK: <<ArraySet2:v\d+>> ArraySet [<<Array>>,<<Add2>>,<<AddArray2>>]
- /// CHECK: <<ArrayGet3:i\d+>> ArrayGet [<<Array>>,<<Add3>>]
- /// CHECK: <<AddArray3:i\d+>> Add [<<ArrayGet3>>,<<Const1>>]
- /// CHECK: <<ArraySet3:v\d+>> ArraySet [<<Array>>,<<Add3>>,<<AddArray3>>]
+ /// CHECK-DAG: <<Add1:i\d+>> Add [<<Param>>,<<Const1>>]
+ /// CHECK-DAG: <<Add2:i\d+>> Add [<<Param>>,<<Const2>>]
+ /// CHECK-DAG: <<Add3:i\d+>> Add [<<Param>>,<<Const3>>]
+ /// CHECK-DAG: <<Array:i\d+>> IntermediateAddress
+ /// CHECK-DAG: <<ArrayGet1:i\d+>> ArrayGet [<<Array>>,<<Add1>>]
+ /// CHECK-DAG: <<ArrayGet2:i\d+>> ArrayGet [<<Array>>,<<Add2>>]
+ /// CHECK-DAG: <<ArrayGet3:i\d+>> ArrayGet [<<Array>>,<<Add3>>]
+ /// CHECK-DAG: <<AddArray1:i\d+>> Add [<<ArrayGet1>>,<<Const2>>]
+ /// CHECK-DAG: {{v\d+}} ArraySet [<<Array>>,<<Add1>>,<<AddArray1>>]
+ /// CHECK-DAG: <<AddArray2:i\d+>> Add [<<ArrayGet2>>,<<Const2>>]
+ /// CHECK-DAG: {{v\d+}} ArraySet [<<Array>>,<<Add2>>,<<AddArray2>>]
+ /// CHECK-DAG: <<AddArray3:i\d+>> Add [<<ArrayGet3>>,<<Const2>>]
+ /// CHECK-DAG: ArraySet [<<Array>>,<<Add3>>,<<AddArray3>>]
/// CHECK-START-ARM64: void Main.arrayAccessVariable(int) scheduler (after)
/// CHECK: <<Param:i\d+>> ParameterValue
@@ -131,9 +131,9 @@
/// CHECK: <<Add2:i\d+>> Add [<<Param>>,<<Const2>>]
/// CHECK: <<Add3:i\d+>> Add [<<Param>>,<<Const3>>]
/// CHECK: <<Array:i\d+>> IntermediateAddress
- /// CHECK: ArrayGet [<<Array>>,{{i\d+}}]
- /// CHECK: ArrayGet [<<Array>>,{{i\d+}}]
- /// CHECK: ArrayGet [<<Array>>,{{i\d+}}]
+ /// CHECK: ArrayGet [<<Array>>,<<Add3>>]
+ /// CHECK: ArrayGet [<<Array>>,<<Add2>>]
+ /// CHECK: ArrayGet [<<Array>>,<<Add1>>]
/// CHECK: Add
/// CHECK: Add
/// CHECK: Add
diff --git a/test/707-checker-invalid-profile/check b/test/707-checker-invalid-profile/check
deleted file mode 100755
index 58d3a52..0000000
--- a/test/707-checker-invalid-profile/check
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# When profile verification fails, dex2oat logs an error. The following
-# command strips out the error message.
-grep -v -f $1 $2 > $1
-grep -v -f $3 $4 > $3
-
-./default-check "$@"
diff --git a/test/707-checker-invalid-profile/run b/test/707-checker-invalid-profile/run
deleted file mode 100644
index 146e180..0000000
--- a/test/707-checker-invalid-profile/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile
diff --git a/test/707-checker-invalid-profile/run.py b/test/707-checker-invalid-profile/run.py
new file mode 100644
index 0000000..1616f1b
--- /dev/null
+++ b/test/707-checker-invalid-profile/run.py
@@ -0,0 +1,29 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args, profile=True, Xcompiler_option=["--compiler-filter=speed-profile"])
+
+ # When profile verification fails, dex2oat logs an error. The following
+ # command strips out the error message.
+ ctx.run(
+ fr"grep -v -f expected-stdout.txt '{args.stdout_file}' > expected-stdout.txt",
+ check=False)
+ ctx.run(
+ fr"grep -v -f expected-stderr.txt '{args.stderr_file}' > expected-stderr.txt",
+ check=False)
diff --git a/test/710-varhandle-creation/build b/test/710-varhandle-creation/build
deleted file mode 100644
index ca1e557..0000000
--- a/test/710-varhandle-creation/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Stop if something fails.
-set -e
-
-./default-build "$@" --experimental var-handles
diff --git a/test/710-varhandle-creation/build.py b/test/710-varhandle-creation/build.py
new file mode 100644
index 0000000..7ccbe96
--- /dev/null
+++ b/test/710-varhandle-creation/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(api_level="var-handles")
diff --git a/test/710-varhandle-creation/run b/test/710-varhandle-creation/run
deleted file mode 100644
index 46b1a83..0000000
--- a/test/710-varhandle-creation/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2021 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.
-
-# Currently app images aren't unloaded when dex files are unloaded.
-exec ${RUN} $@ --no-secondary-app-image
diff --git a/test/710-varhandle-creation/run.py b/test/710-varhandle-creation/run.py
new file mode 100644
index 0000000..930869a
--- /dev/null
+++ b/test/710-varhandle-creation/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # Currently app images aren't unloaded when dex files are unloaded.
+ ctx.default_run(args, secondary_app_image=False)
diff --git a/test/710-varhandle-creation/src/Main.java b/test/710-varhandle-creation/src/Main.java
index c4fe2ff..6ddc58a 100644
--- a/test/710-varhandle-creation/src/Main.java
+++ b/test/710-varhandle-creation/src/Main.java
@@ -271,6 +271,7 @@
private static void checkInstantiatedVarHandles() {
System.out.print("checkInstantiatedVarHandles...");
+ // clang-format off
System.out.print("vz...");
checkNotNull(vz);
checkVarType(vz, boolean.class);
@@ -2052,6 +2053,7 @@
System.out.print("vbbo...");
checkNull(vbbo);
+ // clang-format on
System.out.println("PASS");
}
@@ -2090,6 +2092,7 @@
if (VarHandle.AccessMode.values().length != expectedLength) {
fail("VarHandle.AccessMode.value().length != " + expectedLength);
}
+ // clang-format off
checkAccessMode(VarHandle.AccessMode.GET, "GET", "get", 0);
checkAccessMode(VarHandle.AccessMode.SET, "SET", "set", 1);
checkAccessMode(VarHandle.AccessMode.GET_VOLATILE, "GET_VOLATILE", "getVolatile", 2);
@@ -2121,6 +2124,7 @@
checkAccessMode(VarHandle.AccessMode.GET_AND_BITWISE_XOR, "GET_AND_BITWISE_XOR", "getAndBitwiseXor", 28);
checkAccessMode(VarHandle.AccessMode.GET_AND_BITWISE_XOR_RELEASE, "GET_AND_BITWISE_XOR_RELEASE", "getAndBitwiseXorRelease", 29);
checkAccessMode(VarHandle.AccessMode.GET_AND_BITWISE_XOR_ACQUIRE, "GET_AND_BITWISE_XOR_ACQUIRE", "getAndBitwiseXorAcquire", 30);
+ // clang-format on
System.out.println("PASS");
}
@@ -2456,4 +2460,3 @@
checkStaticFieldVarHandleGc();
}
}
-
diff --git a/test/710-varhandle-creation/test-metadata.json b/test/710-varhandle-creation/test-metadata.json
new file mode 100644
index 0000000..ed29691
--- /dev/null
+++ b/test/710-varhandle-creation/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "experimental": "var-handles"
+ }
+}
diff --git a/test/712-varhandle-invocations/build b/test/712-varhandle-invocations/build
deleted file mode 100755
index 9a6e96e..0000000
--- a/test/712-varhandle-invocations/build
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Make us exit on a failure
-set -e
-
-# Set variables for source directories. Using src-art so we use
-# VarHandles in the bootclasspath and can compile with the Java 8
-# compiler.
-MANUAL_SRC=src
-GENERATED_SRC=src2
-
-# Build the Java files
-mkdir -p src2
-
-# Collate list of manual test classes
-MANUAL_TESTS=$(cd "${MANUAL_SRC}" && find . -name 'Var*Tests.java' | sed -e 's@.*\(Var.*Tests\).*@\1@g' | sort)
-
-# Generate tests and Main that covers both the generated tests and manual tests
-python3 ./util-src/generate_java.py "${GENERATED_SRC}" ${MANUAL_TESTS}
-
-./default-build "$@" --experimental var-handles
diff --git a/test/712-varhandle-invocations/build.py b/test/712-varhandle-invocations/build.py
new file mode 100644
index 0000000..511d1c1
--- /dev/null
+++ b/test/712-varhandle-invocations/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build(api_level="var-handles")
diff --git a/test/712-varhandle-invocations/generate-sources b/test/712-varhandle-invocations/generate-sources
new file mode 100755
index 0000000..d025b26
--- /dev/null
+++ b/test/712-varhandle-invocations/generate-sources
@@ -0,0 +1,33 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+# Make us exit on a failure
+set -e
+
+# Set variables for source directories. Using src-art so we use
+# VarHandles in the bootclasspath and can compile with the Java 8
+# compiler.
+MANUAL_SRC=src
+GENERATED_SRC=src2
+
+# Build the Java files
+mkdir -p src2
+
+# Collate list of manual test classes
+MANUAL_TESTS=$(cd "${MANUAL_SRC}" && find . -name 'Var*Tests.java' | sed -e 's@.*\(Var.*Tests\).*@\1@g' | sort)
+
+# Generate tests and Main that covers both the generated tests and manual tests
+python3 ./util-src/generate_java.py "${GENERATED_SRC}" ${MANUAL_TESTS}
diff --git a/test/712-varhandle-invocations/src/VarHandleTypeConversionTests.java b/test/712-varhandle-invocations/src/VarHandleTypeConversionTests.java
index 73e3044..51c3b95 100644
--- a/test/712-varhandle-invocations/src/VarHandleTypeConversionTests.java
+++ b/test/712-varhandle-invocations/src/VarHandleTypeConversionTests.java
@@ -54,6 +54,52 @@
}
}
+ public static class ReferenceReturnTypeTest extends VarHandleUnitTest {
+ private Object o;
+ private static final VarHandle vh;
+
+ static {
+ try {
+ Class<?> cls = VoidReturnTypeTest.class;
+ vh = MethodHandles.lookup().findVarHandle(cls, "i", Object.class);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ protected void doTest() {
+ vh.set(this, null);
+ try {
+ int i = (int) vh.get(this);
+ failUnreachable();
+ } catch (NullPointerException cce) {
+ }
+
+ vh.set(this, new Object());
+ try {
+ int i = (int) vh.get(this);
+ failUnreachable();
+ } catch (ClassCastException cce) {
+ }
+
+ vh.set(this, Integer.valueOf(42));
+ {
+ int i = (int) vh.get(this);
+ }
+
+ vh.set(this, new ReferenceReturnTypeTest());
+ try {
+ int i = (int) vh.get(this);
+ } catch (ClassCastException cce) {
+ }
+ }
+
+ public static void main(String[] args) {
+ new ReferenceReturnTypeTest().run();
+ }
+ }
+
//
// Tests that a null reference as a boxed primitive type argument
// throws a NullPointerException. These vary the VarHandle type
diff --git a/test/713-varhandle-invokers/build b/test/713-varhandle-invokers/build
deleted file mode 100755
index 09d376b..0000000
--- a/test/713-varhandle-invokers/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Make us exit on a failure
-set -e
-
-./default-build "$@" --experimental var-handles
diff --git a/test/713-varhandle-invokers/build.py b/test/713-varhandle-invokers/build.py
new file mode 100644
index 0000000..7ccbe96
--- /dev/null
+++ b/test/713-varhandle-invokers/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(api_level="var-handles")
diff --git a/test/713-varhandle-invokers/test-metadata.json b/test/713-varhandle-invokers/test-metadata.json
new file mode 100644
index 0000000..ed29691
--- /dev/null
+++ b/test/713-varhandle-invokers/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "experimental": "var-handles"
+ }
+}
diff --git a/test/714-invoke-custom-lambda-metafactory/build b/test/714-invoke-custom-lambda-metafactory/build
deleted file mode 100644
index b5002ba..0000000
--- a/test/714-invoke-custom-lambda-metafactory/build
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Make us exit on a failure
-set -e
-
-# Opt-out from desugaring to ensure offending lambda is in the DEX.
-export USE_DESUGAR=false
-./default-build "$@" --experimental method-handles
diff --git a/test/714-invoke-custom-lambda-metafactory/build.py b/test/714-invoke-custom-lambda-metafactory/build.py
new file mode 100644
index 0000000..c1b1b60
--- /dev/null
+++ b/test/714-invoke-custom-lambda-metafactory/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(use_desugar=False, api_level="method-handles")
diff --git a/test/714-invoke-custom-lambda-metafactory/expected-stdout.txt b/test/714-invoke-custom-lambda-metafactory/expected-stdout.txt
index a9bf5a0..e69de29 100644
--- a/test/714-invoke-custom-lambda-metafactory/expected-stdout.txt
+++ b/test/714-invoke-custom-lambda-metafactory/expected-stdout.txt
@@ -1 +0,0 @@
-exit status: 1
diff --git a/test/714-invoke-custom-lambda-metafactory/run b/test/714-invoke-custom-lambda-metafactory/run
deleted file mode 100755
index 7a0d0d0..0000000
--- a/test/714-invoke-custom-lambda-metafactory/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/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.
-
-# Squash the exit status and put it in expected
-./default-run "$@"
-echo "exit status:" $?
diff --git a/test/714-invoke-custom-lambda-metafactory/run.py b/test/714-invoke-custom-lambda-metafactory/run.py
new file mode 100644
index 0000000..80a8a33
--- /dev/null
+++ b/test/714-invoke-custom-lambda-metafactory/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, expected_exit_code=1)
diff --git a/test/715-clinit-implicit-parameter-annotations/build b/test/715-clinit-implicit-parameter-annotations/build
deleted file mode 100644
index 2b5f92c..0000000
--- a/test/715-clinit-implicit-parameter-annotations/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Make us exit on a failure
-set -e
-
-./default-build "$@" --experimental parameter-annotations
diff --git a/test/715-clinit-implicit-parameter-annotations/build.py b/test/715-clinit-implicit-parameter-annotations/build.py
new file mode 100644
index 0000000..98d6c5d
--- /dev/null
+++ b/test/715-clinit-implicit-parameter-annotations/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(api_level="parameter-annotations")
diff --git a/test/716-jli-jit-samples/build b/test/716-jli-jit-samples/build
deleted file mode 100755
index 730a8a1..0000000
--- a/test/716-jli-jit-samples/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental var-handles
diff --git a/test/716-jli-jit-samples/build.py b/test/716-jli-jit-samples/build.py
new file mode 100644
index 0000000..7ccbe96
--- /dev/null
+++ b/test/716-jli-jit-samples/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(api_level="var-handles")
diff --git a/test/716-jli-jit-samples/test-metadata.json b/test/716-jli-jit-samples/test-metadata.json
new file mode 100644
index 0000000..ed29691
--- /dev/null
+++ b/test/716-jli-jit-samples/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "experimental": "var-handles"
+ }
+}
diff --git a/test/719-varhandle-concurrency/build b/test/719-varhandle-concurrency/build
deleted file mode 100755
index 98a9967..0000000
--- a/test/719-varhandle-concurrency/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2022 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.
-
-# Make us exit on a failure
-set -e
-
-./default-build "$@" --experimental var-handles
diff --git a/test/719-varhandle-concurrency/build.py b/test/719-varhandle-concurrency/build.py
new file mode 100644
index 0000000..7ccbe96
--- /dev/null
+++ b/test/719-varhandle-concurrency/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(api_level="var-handles")
diff --git a/test/719-varhandle-concurrency/src/Main.java b/test/719-varhandle-concurrency/src/Main.java
index 1c2135b..47f523f 100644
--- a/test/719-varhandle-concurrency/src/Main.java
+++ b/test/719-varhandle-concurrency/src/Main.java
@@ -33,196 +33,196 @@
* were not run at all (skipped by all threads).
*/
public class Main {
- private static final VarHandle QA;
- static {
- QA = MethodHandles.arrayElementVarHandle(TestTask[].class);
- }
-
- private static final int TASK_COUNT = 10000;
- private static final int THREAD_COUNT = 100;
- /* Each test may need several retries before a concurrent failure is seen. In the past, for a
- * known bug, between 5 and 10 retries were sufficient. Use RETRIES to configure how many
- * iterations to retry for each test scenario. However, to avoid the test running for too long,
- * for example with gcstress, set a cap duration in MAX_RETRIES_DURATION. With this at least one
- * iteration would run, but there could be fewer retries if each of them takes too long. */
- private static final int RETRIES = 50;
- private static final Duration MAX_RETRIES_DURATION = Duration.ofMinutes(1);
-
- public static void main(String[] args) throws Throwable {
- testConcurrentProcessing(new CompareAndExchangeRunnerFactory(), "compareAndExchange");
- testConcurrentProcessing(new CompareAndSetRunnerFactory(), "compareAndSet");
- testConcurrentProcessing(new WeakCompareAndSetRunnerFactory(), "weakCompareAndSet");
- }
-
- private static void testConcurrentProcessing(RunnerFactory factory,
- String testName) throws Throwable {
- final Duration startTs = Duration.ofNanos(System.nanoTime());
- final Duration endTs = startTs.plus(MAX_RETRIES_DURATION);
- for (int i = 0; i < RETRIES; ++i) {
- concurrentProcessingTestIteration(factory, i, testName);
- Duration now = Duration.ofNanos(System.nanoTime());
- if (0 < now.compareTo(endTs)) {
- break;
- }
+ private static final VarHandle QA;
+ static {
+ QA = MethodHandles.arrayElementVarHandle(TestTask[].class);
}
- }
- private static void concurrentProcessingTestIteration(RunnerFactory factory,
- int iteration, String testName) throws Throwable {
- final TestTask[] tasks = new TestTask[TASK_COUNT];
- final AtomicInteger result = new AtomicInteger();
+ private static final int TASK_COUNT = 10000;
+ private static final int THREAD_COUNT = 20;
+ /* Each test may need several retries before a concurrent failure is seen. In the past, for a
+ * known bug, between 5 and 10 retries were sufficient. Use RETRIES to configure how many
+ * iterations to retry for each test scenario. However, to avoid the test running for too long,
+ * for example with gcstress, set a cap duration in MAX_RETRIES_DURATION. With this at least one
+ * iteration would run, but there could be fewer retries if each of them takes too long. */
+ private static final int RETRIES = 50;
+ // b/235431387: timeout reduced from 1 minute
+ private static final Duration MAX_RETRIES_DURATION = Duration.ofSeconds(15);
- for (int i = 0; i < TASK_COUNT; ++i) {
- tasks[i] = new TestTask(Integer.valueOf(i+1), result::addAndGet);
- }
-
- Thread[] threads = new Thread[THREAD_COUNT];
- for (int i = 0; i < THREAD_COUNT; ++i) {
- threads[i] = factory.createRunner(tasks);
- }
-
- for (int i = 0; i < THREAD_COUNT; ++i) {
- threads[i].start();
- }
-
- for (int i = 0; i < THREAD_COUNT; ++i) {
- threads[i].join();
- }
-
- check(result.get(), TASK_COUNT * (TASK_COUNT + 1) / 2,
- testName + " test result not as expected", iteration);
- }
-
- /**
- * Processes the task queue until there are no tasks left.
- *
- * The actual task-grabbing mechanism is implemented in subclasses through grabTask(). This allows
- * testing various mechanisms, like compareAndSet() and compareAndExchange().
- */
- private static abstract class TaskRunner extends Thread {
-
- protected final TestTask[] tasks;
-
- TaskRunner(TestTask[] tasks) {
- this.tasks = tasks;
- }
-
- @Override
- public void run() {
- int i = 0;
- while (i < TASK_COUNT) {
- TestTask t = (TestTask) QA.get(tasks, i);
- if (t == null) {
- ++i;
- continue;
- }
- if (!grabTask(t, i)) {
- continue;
- }
- ++i;
- VarHandle.releaseFence();
- t.exec();
- }
- }
-
- /**
- * Grabs the next task from the queue in an atomic way.
- *
- * Once a task is retrieved successfully, the queue should no longer hold a reference to it.
- * This would be done, for example, by swapping the task with a null value.
- *
- * @param t The task to get from the queue
- * @param i The index where the task is found
- *
- * @return {@code true} if the task has been retrieved and is not available to any other
- * threads. Otherwise {@code false}. If {@code false} is returned, then either the task was no
- * longer present on the queue due to another thread grabbing it, or, in case of spurious
- * failure, the task is still available and no other thread managed to grab it.
- */
- protected abstract boolean grabTask(TestTask t, int i);
- }
-
- private static class TaskRunnerWithCompareAndExchange extends TaskRunner {
-
- TaskRunnerWithCompareAndExchange(TestTask[] tasks) {
- super(tasks);
- }
-
- @Override
- protected boolean grabTask(TestTask t, int i) {
- return (t == QA.compareAndExchange(tasks, i, t, null));
- }
- }
-
- private static class TaskRunnerWithCompareAndSet extends TaskRunner {
-
- TaskRunnerWithCompareAndSet(TestTask[] tasks) {
- super(tasks);
- }
-
- @Override
- protected boolean grabTask(TestTask t, int i) {
- return QA.compareAndSet(tasks, i, t, null);
- }
- }
-
- private static class TaskRunnerWithWeakCompareAndSet extends TaskRunner {
-
- TaskRunnerWithWeakCompareAndSet(TestTask[] tasks) {
- super(tasks);
- }
-
- @Override
- protected boolean grabTask(TestTask t, int i) {
- return QA.weakCompareAndSet(tasks, i, t, null);
- }
- }
-
-
- private interface RunnerFactory {
- Thread createRunner(TestTask[] tasks);
- }
-
- private static class CompareAndExchangeRunnerFactory implements RunnerFactory {
- @Override
- public Thread createRunner(TestTask[] tasks) {
- return new TaskRunnerWithCompareAndExchange(tasks);
- }
- }
-
- private static class CompareAndSetRunnerFactory implements RunnerFactory {
- @Override
- public Thread createRunner(TestTask[] tasks) {
- return new TaskRunnerWithCompareAndSet(tasks);
- }
- }
-
- private static class WeakCompareAndSetRunnerFactory implements RunnerFactory {
- @Override
- public Thread createRunner(TestTask[] tasks) {
- return new TaskRunnerWithWeakCompareAndSet(tasks);
- }
- }
-
- private static class TestTask {
- private final Integer ord;
- private final Consumer<Integer> action;
-
- TestTask(Integer ord, Consumer<Integer> action) {
- this.ord = ord;
- this.action = action;
- }
-
- public void exec() {
- action.accept(ord);
- }
- }
-
- private static void check(int actual, int expected, String msg, int iteration) {
- if (actual != expected) {
- System.err.println(String.format("[iteration %d] %s : %d != %d",
- iteration, msg, actual, expected));
- System.exit(1);
+ public static void main(String[] args) throws Throwable {
+ testConcurrentProcessing(new CompareAndExchangeRunnerFactory(), "compareAndExchange");
+ testConcurrentProcessing(new CompareAndSetRunnerFactory(), "compareAndSet");
+ testConcurrentProcessing(new WeakCompareAndSetRunnerFactory(), "weakCompareAndSet");
}
- }
+
+ private static void testConcurrentProcessing(RunnerFactory factory, String testName)
+ throws Throwable {
+ final Duration startTs = Duration.ofNanos(System.nanoTime());
+ final Duration endTs = startTs.plus(MAX_RETRIES_DURATION);
+ for (int i = 0; i < RETRIES; ++i) {
+ concurrentProcessingTestIteration(factory, i, testName);
+ Duration now = Duration.ofNanos(System.nanoTime());
+ if (0 < now.compareTo(endTs)) {
+ break;
+ }
+ }
+ }
+
+ private static void concurrentProcessingTestIteration(
+ RunnerFactory factory, int iteration, String testName) throws Throwable {
+ final TestTask[] tasks = new TestTask[TASK_COUNT];
+ final AtomicInteger result = new AtomicInteger();
+
+ for (int i = 0; i < TASK_COUNT; ++i) {
+ tasks[i] = new TestTask(Integer.valueOf(i + 1), result::addAndGet);
+ }
+
+ Thread[] threads = new Thread[THREAD_COUNT];
+ for (int i = 0; i < THREAD_COUNT; ++i) {
+ threads[i] = factory.createRunner(tasks);
+ }
+
+ for (int i = 0; i < THREAD_COUNT; ++i) {
+ threads[i].start();
+ }
+
+ for (int i = 0; i < THREAD_COUNT; ++i) {
+ threads[i].join();
+ }
+
+ check(result.get(),
+ TASK_COUNT * (TASK_COUNT + 1) / 2,
+ testName + " test result not as expected",
+ iteration);
+ }
+
+ /**
+ * Processes the task queue until there are no tasks left.
+ *
+ * The actual task-grabbing mechanism is implemented in subclasses through grabTask().
+ * This allows testing various mechanisms, like compareAndSet() and compareAndExchange().
+ */
+ private static abstract class TaskRunner extends Thread {
+
+ protected final TestTask[] tasks;
+
+ TaskRunner(TestTask[] tasks) {
+ this.tasks = tasks;
+ }
+
+ @Override
+ public void run() {
+ int i = 0;
+ while (i < TASK_COUNT) {
+ TestTask t = (TestTask) QA.get(tasks, i);
+ if (t == null) {
+ ++i;
+ continue;
+ }
+ if (!grabTask(t, i)) {
+ continue;
+ }
+ ++i;
+ VarHandle.releaseFence();
+ t.exec();
+ }
+ }
+
+ /**
+ * Grabs the next task from the queue in an atomic way.
+ *
+ * Once a task is retrieved successfully, the queue should no longer hold a reference to it.
+ * This would be done, for example, by swapping the task with a null value.
+ *
+ * @param t The task to get from the queue
+ * @param i The index where the task is found
+ *
+ * @return {@code true} if the task has been retrieved and is not available to any other
+ * threads. Otherwise {@code false}. If {@code false} is returned, then either the task was
+ * no longer present on the queue due to another thread grabbing it, or, in case of spurious
+ * failure, the task is still available and no other thread managed to grab it.
+ */
+ protected abstract boolean grabTask(TestTask t, int i);
+ }
+
+ private static class TaskRunnerWithCompareAndExchange extends TaskRunner {
+ TaskRunnerWithCompareAndExchange(TestTask[] tasks) {
+ super(tasks);
+ }
+
+ @Override
+ protected boolean grabTask(TestTask t, int i) {
+ return (t == QA.compareAndExchange(tasks, i, t, null));
+ }
+ }
+
+ private static class TaskRunnerWithCompareAndSet extends TaskRunner {
+ TaskRunnerWithCompareAndSet(TestTask[] tasks) {
+ super(tasks);
+ }
+
+ @Override
+ protected boolean grabTask(TestTask t, int i) {
+ return QA.compareAndSet(tasks, i, t, null);
+ }
+ }
+
+ private static class TaskRunnerWithWeakCompareAndSet extends TaskRunner {
+ TaskRunnerWithWeakCompareAndSet(TestTask[] tasks) {
+ super(tasks);
+ }
+
+ @Override
+ protected boolean grabTask(TestTask t, int i) {
+ return QA.weakCompareAndSet(tasks, i, t, null);
+ }
+ }
+
+
+ private interface RunnerFactory {
+ Thread createRunner(TestTask[] tasks);
+ }
+
+ private static class CompareAndExchangeRunnerFactory implements RunnerFactory {
+ @Override
+ public Thread createRunner(TestTask[] tasks) {
+ return new TaskRunnerWithCompareAndExchange(tasks);
+ }
+ }
+
+ private static class CompareAndSetRunnerFactory implements RunnerFactory {
+ @Override
+ public Thread createRunner(TestTask[] tasks) {
+ return new TaskRunnerWithCompareAndSet(tasks);
+ }
+ }
+
+ private static class WeakCompareAndSetRunnerFactory implements RunnerFactory {
+ @Override
+ public Thread createRunner(TestTask[] tasks) {
+ return new TaskRunnerWithWeakCompareAndSet(tasks);
+ }
+ }
+
+ private static class TestTask {
+ private final Integer ord;
+ private final Consumer<Integer> action;
+
+ TestTask(Integer ord, Consumer<Integer> action) {
+ this.ord = ord;
+ this.action = action;
+ }
+
+ public void exec() {
+ action.accept(ord);
+ }
+ }
+
+ private static void check(int actual, int expected, String msg, int iteration) {
+ if (actual != expected) {
+ System.err.println(String.format(
+ "[iteration %d] %s : %d != %d", iteration, msg, actual, expected));
+ System.exit(1);
+ }
+ }
}
diff --git a/test/719-varhandle-concurrency/test-metadata.json b/test/719-varhandle-concurrency/test-metadata.json
new file mode 100644
index 0000000..ed29691
--- /dev/null
+++ b/test/719-varhandle-concurrency/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "experimental": "var-handles"
+ }
+}
diff --git a/test/727-checker-unresolved-class/run b/test/727-checker-unresolved-class/run
deleted file mode 100644
index 1c9dd11..0000000
--- a/test/727-checker-unresolved-class/run
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2020 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.
-
-if [[ "$TEST_RUNTIME" == "jvm" ]]; then
- exec ${RUN} $@
-else
- # Append graphs for checker tests (we run dex2oat twice) with
- # --dump-cfg-append.
- # Make some classes unresolved for AOT compilation with
- # --updatable-bcp-packages-file.
- exec ${RUN} $@ \
- -Xcompiler-option --dump-cfg-append \
- -Xcompiler-option --updatable-bcp-packages-file="$DEX_LOCATION/res/updateable.txt"
-fi
diff --git a/test/727-checker-unresolved-class/run.py b/test/727-checker-unresolved-class/run.py
new file mode 100644
index 0000000..986897b
--- /dev/null
+++ b/test/727-checker-unresolved-class/run.py
@@ -0,0 +1,31 @@
+#!/bin/bash
+#
+# Copyright (C) 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ if args.jvm:
+ ctx.default_run(args)
+ else:
+ # Append graphs for checker tests (we run dex2oat twice) with
+ # --dump-cfg-append.
+ # Make some classes unresolved for AOT compilation with
+ # --updatable-bcp-packages-file.
+ ctx.default_run(
+ args,
+ Xcompiler_option=[
+ "--dump-cfg-append",
+ f"--updatable-bcp-packages-file={ctx.env.DEX_LOCATION}/res/updateable.txt"
+ ])
diff --git a/test/728-imt-conflict-zygote/run b/test/728-imt-conflict-zygote/run
deleted file mode 100644
index 8fdff6d..0000000
--- a/test/728-imt-conflict-zygote/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2020 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 "$@" --zygote
diff --git a/test/728-imt-conflict-zygote/run.py b/test/728-imt-conflict-zygote/run.py
new file mode 100644
index 0000000..d0b6e49
--- /dev/null
+++ b/test/728-imt-conflict-zygote/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# Copyright 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args, zygote=True)
diff --git a/test/729-checker-polymorphic-intrinsic/run b/test/729-checker-polymorphic-intrinsic/run
deleted file mode 100644
index 5fa72ed..0000000
--- a/test/729-checker-polymorphic-intrinsic/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2021 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.
-
-exec ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile
diff --git a/test/729-checker-polymorphic-intrinsic/run.py b/test/729-checker-polymorphic-intrinsic/run.py
new file mode 100644
index 0000000..43aa25b
--- /dev/null
+++ b/test/729-checker-polymorphic-intrinsic/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args, profile=True, Xcompiler_option=["--compiler-filter=speed-profile"])
diff --git a/test/800-smali/run b/test/800-smali/run
deleted file mode 100644
index c8ce0bc..0000000
--- a/test/800-smali/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2021 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.
-
-# Target 31 to have the verifier behavior the test expects.
-./default-run "$@" --runtime-option -Xtarget-sdk-version:31
diff --git a/test/800-smali/run.py b/test/800-smali/run.py
new file mode 100644
index 0000000..f20dbc0
--- /dev/null
+++ b/test/800-smali/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # Target 31 to have the verifier behavior the test expects.
+ ctx.default_run(args, runtime_option=["-Xtarget-sdk-version:31"])
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index 90476b3..06d24d8 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -21,7 +21,7 @@
import java.util.List;
/**
- * Smali excercise.
+ * Smali exercise.
*/
public class Main {
diff --git a/test/804-class-extends-itself/build.py b/test/804-class-extends-itself/build.py
new file mode 100644
index 0000000..2cd378a
--- /dev/null
+++ b/test/804-class-extends-itself/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build()
diff --git a/test/804-class-extends-itself/build b/test/804-class-extends-itself/generate-sources
old mode 100644
new mode 100755
similarity index 100%
rename from test/804-class-extends-itself/build
rename to test/804-class-extends-itself/generate-sources
diff --git a/test/807-method-handle-and-mr/build b/test/807-method-handle-and-mr/build
deleted file mode 100755
index 12a8e18..0000000
--- a/test/807-method-handle-and-mr/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/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.
-
-# Exit on failure.
-set -e
-
-./default-build "$@" --experimental method-handles
diff --git a/test/807-method-handle-and-mr/build.py b/test/807-method-handle-and-mr/build.py
new file mode 100644
index 0000000..027dd53
--- /dev/null
+++ b/test/807-method-handle-and-mr/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(api_level="method-handles")
diff --git a/test/811-checker-invoke-super-secondary/build.py b/test/811-checker-invoke-super-secondary/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/811-checker-invoke-super-secondary/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/811-checker-invoke-super-secondary/test-metadata.json b/test/811-checker-invoke-super-secondary/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/811-checker-invoke-super-secondary/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/815-invokeinterface-default/src/Main.java b/test/815-invokeinterface-default/src/Main.java
index db3612b..97321c8 100644
--- a/test/815-invokeinterface-default/src/Main.java
+++ b/test/815-invokeinterface-default/src/Main.java
@@ -18,174 +18,174 @@
// An interface with enough methods to trigger a conflict.
interface Itf {
- public void method0a();
- public void method0b();
- public void method0c();
- public void method0d();
- public void method0e();
- public void method0f();
- public void method0g();
- public void method0h();
- public void method0i();
- public void method0j();
- public void method0k();
- public void method0l();
- public void method0m();
- public void method0n();
- public void method0o();
- public void method0p();
- public void method0q();
- public void method0r();
- public void method0s();
- public void method0t();
- public void method0u();
- public void method0v();
- public void method0w();
- public void method0x();
- public void method0y();
- public void method0z();
- public void method1a();
- public void method1b();
- public void method1c();
- public void method1d();
- public void method1e();
- public void method1f();
- public void method1g();
- public void method1h();
- public void method1i();
- public void method1j();
- public void method1k();
- public void method1l();
- public void method1m();
- public void method1n();
- public void method1o();
- public void method1p();
- public void method1q();
- public void method1r();
- public void method1s();
- public void method1t();
- public void method1u();
- public void method1v();
- public void method1w();
- public void method1x();
- public void method1y();
- public void method1z();
- public void method2a();
- public void method2b();
- public void method2c();
- public void method2d();
- public void method2e();
- public void method2f();
- public void method2g();
- public void method2h();
- public void method2i();
- public void method2j();
- public void method2k();
- public void method2l();
- public void method2m();
- public void method2n();
- public void method2o();
- public void method2p();
- public void method2q();
- public void method2r();
- public void method2s();
- public void method2t();
- public void method2u();
- public void method2v();
- public void method2w();
- public void method2x();
- public void method2y();
- public void method2z();
+ public void method0a();
+ public void method0b();
+ public void method0c();
+ public void method0d();
+ public void method0e();
+ public void method0f();
+ public void method0g();
+ public void method0h();
+ public void method0i();
+ public void method0j();
+ public void method0k();
+ public void method0l();
+ public void method0m();
+ public void method0n();
+ public void method0o();
+ public void method0p();
+ public void method0q();
+ public void method0r();
+ public void method0s();
+ public void method0t();
+ public void method0u();
+ public void method0v();
+ public void method0w();
+ public void method0x();
+ public void method0y();
+ public void method0z();
+ public void method1a();
+ public void method1b();
+ public void method1c();
+ public void method1d();
+ public void method1e();
+ public void method1f();
+ public void method1g();
+ public void method1h();
+ public void method1i();
+ public void method1j();
+ public void method1k();
+ public void method1l();
+ public void method1m();
+ public void method1n();
+ public void method1o();
+ public void method1p();
+ public void method1q();
+ public void method1r();
+ public void method1s();
+ public void method1t();
+ public void method1u();
+ public void method1v();
+ public void method1w();
+ public void method1x();
+ public void method1y();
+ public void method1z();
+ public void method2a();
+ public void method2b();
+ public void method2c();
+ public void method2d();
+ public void method2e();
+ public void method2f();
+ public void method2g();
+ public void method2h();
+ public void method2i();
+ public void method2j();
+ public void method2k();
+ public void method2l();
+ public void method2m();
+ public void method2n();
+ public void method2o();
+ public void method2p();
+ public void method2q();
+ public void method2r();
+ public void method2s();
+ public void method2t();
+ public void method2u();
+ public void method2v();
+ public void method2w();
+ public void method2x();
+ public void method2y();
+ public void method2z();
- public default void $noinline$defaultRecursiveMethod(boolean callRecursive) {
- if (callRecursive) {
- $noinline$defaultRecursiveMethod(false);
+ public default void $noinline$defaultRecursiveMethod(boolean callRecursive) {
+ if (callRecursive) {
+ $noinline$defaultRecursiveMethod(false);
+ }
}
- }
}
public class Main implements Itf {
- public static void main(String[] args) throws Exception {
- Main main = new Main();
- main.$noinline$defaultRecursiveMethod(true);
- }
+ public static void main(String[] args) throws Exception {
+ Main main = new Main();
+ main.$noinline$defaultRecursiveMethod(true);
+ }
- public void method0a() {}
- public void method0b() {}
- public void method0c() {}
- public void method0d() {}
- public void method0e() {}
- public void method0f() {}
- public void method0g() {}
- public void method0h() {}
- public void method0i() {}
- public void method0j() {}
- public void method0k() {}
- public void method0l() {}
- public void method0m() {}
- public void method0n() {}
- public void method0o() {}
- public void method0p() {}
- public void method0q() {}
- public void method0r() {}
- public void method0s() {}
- public void method0t() {}
- public void method0u() {}
- public void method0v() {}
- public void method0w() {}
- public void method0x() {}
- public void method0y() {}
- public void method0z() {}
- public void method1a() {}
- public void method1b() {}
- public void method1c() {}
- public void method1d() {}
- public void method1e() {}
- public void method1f() {}
- public void method1g() {}
- public void method1h() {}
- public void method1i() {}
- public void method1j() {}
- public void method1k() {}
- public void method1l() {}
- public void method1m() {}
- public void method1n() {}
- public void method1o() {}
- public void method1p() {}
- public void method1q() {}
- public void method1r() {}
- public void method1s() {}
- public void method1t() {}
- public void method1u() {}
- public void method1v() {}
- public void method1w() {}
- public void method1x() {}
- public void method1y() {}
- public void method1z() {}
- public void method2a() {}
- public void method2b() {}
- public void method2c() {}
- public void method2d() {}
- public void method2e() {}
- public void method2f() {}
- public void method2g() {}
- public void method2h() {}
- public void method2i() {}
- public void method2j() {}
- public void method2k() {}
- public void method2l() {}
- public void method2m() {}
- public void method2n() {}
- public void method2o() {}
- public void method2p() {}
- public void method2q() {}
- public void method2r() {}
- public void method2s() {}
- public void method2t() {}
- public void method2u() {}
- public void method2v() {}
- public void method2w() {}
- public void method2x() {}
- public void method2y() {}
- public void method2z() {}
+ public void method0a() {}
+ public void method0b() {}
+ public void method0c() {}
+ public void method0d() {}
+ public void method0e() {}
+ public void method0f() {}
+ public void method0g() {}
+ public void method0h() {}
+ public void method0i() {}
+ public void method0j() {}
+ public void method0k() {}
+ public void method0l() {}
+ public void method0m() {}
+ public void method0n() {}
+ public void method0o() {}
+ public void method0p() {}
+ public void method0q() {}
+ public void method0r() {}
+ public void method0s() {}
+ public void method0t() {}
+ public void method0u() {}
+ public void method0v() {}
+ public void method0w() {}
+ public void method0x() {}
+ public void method0y() {}
+ public void method0z() {}
+ public void method1a() {}
+ public void method1b() {}
+ public void method1c() {}
+ public void method1d() {}
+ public void method1e() {}
+ public void method1f() {}
+ public void method1g() {}
+ public void method1h() {}
+ public void method1i() {}
+ public void method1j() {}
+ public void method1k() {}
+ public void method1l() {}
+ public void method1m() {}
+ public void method1n() {}
+ public void method1o() {}
+ public void method1p() {}
+ public void method1q() {}
+ public void method1r() {}
+ public void method1s() {}
+ public void method1t() {}
+ public void method1u() {}
+ public void method1v() {}
+ public void method1w() {}
+ public void method1x() {}
+ public void method1y() {}
+ public void method1z() {}
+ public void method2a() {}
+ public void method2b() {}
+ public void method2c() {}
+ public void method2d() {}
+ public void method2e() {}
+ public void method2f() {}
+ public void method2g() {}
+ public void method2h() {}
+ public void method2i() {}
+ public void method2j() {}
+ public void method2k() {}
+ public void method2l() {}
+ public void method2m() {}
+ public void method2n() {}
+ public void method2o() {}
+ public void method2p() {}
+ public void method2q() {}
+ public void method2r() {}
+ public void method2s() {}
+ public void method2t() {}
+ public void method2u() {}
+ public void method2v() {}
+ public void method2w() {}
+ public void method2x() {}
+ public void method2y() {}
+ public void method2z() {}
}
diff --git a/test/816-illegal-new-array/src/Main.java b/test/816-illegal-new-array/src/Main.java
index 2d48c12..7a960bb 100644
--- a/test/816-illegal-new-array/src/Main.java
+++ b/test/816-illegal-new-array/src/Main.java
@@ -19,37 +19,37 @@
public class Main {
- public static void main(String[] args) throws Exception {
- Class<?> c = Class.forName("TestCase");
- Method m = c.getMethod("filledNewArray");
- try {
- m.invoke(null);
- throw new Error("Expected IllegalAccessError");
- } catch (InvocationTargetException e) {
- if (!(e.getCause() instanceof IllegalAccessError)) {
- throw new Error("Expected IllegalAccessError, got " + e.getCause());
- }
- }
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("TestCase");
+ Method m = c.getMethod("filledNewArray");
+ try {
+ m.invoke(null);
+ throw new Error("Expected IllegalAccessError");
+ } catch (InvocationTargetException e) {
+ if (!(e.getCause() instanceof IllegalAccessError)) {
+ throw new Error("Expected IllegalAccessError, got " + e.getCause());
+ }
+ }
- m = c.getMethod("filledNewArrayRange");
- try {
- m.invoke(null);
- throw new Error("Expected IllegalAccessError");
- } catch (InvocationTargetException e) {
- if (!(e.getCause() instanceof IllegalAccessError)) {
- throw new Error("Expected IllegalAccessError, got " + e.getCause());
- }
- }
+ m = c.getMethod("filledNewArrayRange");
+ try {
+ m.invoke(null);
+ throw new Error("Expected IllegalAccessError");
+ } catch (InvocationTargetException e) {
+ if (!(e.getCause() instanceof IllegalAccessError)) {
+ throw new Error("Expected IllegalAccessError, got " + e.getCause());
+ }
+ }
- m = c.getMethod("newArray");
- try {
- m.invoke(null);
- throw new Error("Expected IllegalAccessError");
- } catch (InvocationTargetException e) {
- if (!(e.getCause() instanceof IllegalAccessError)) {
- throw new Error("Expected IllegalAccessError, got " + e.getCause());
- }
+ m = c.getMethod("newArray");
+ try {
+ m.invoke(null);
+ throw new Error("Expected IllegalAccessError");
+ } catch (InvocationTargetException e) {
+ if (!(e.getCause() instanceof IllegalAccessError)) {
+ throw new Error("Expected IllegalAccessError, got " + e.getCause());
+ }
+ }
}
- }
}
diff --git a/test/817-hiddenapi/build b/test/817-hiddenapi/build
deleted file mode 100644
index 330a6de..0000000
--- a/test/817-hiddenapi/build
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-set -e
-
-# Build the jars twice. First with applying hiddenapi, creating a boot jar, then
-# a second time without to create a normal jar. We need to do this because we
-# want to load the jar once as an app module and once as a member of the boot
-# class path. The DexFileVerifier would fail on the former as it does not allow
-# hidden API access flags in dex files. DexFileVerifier is not invoked on boot
-# class path dex files, so the boot jar loads fine in the latter case.
-
-export USE_HIDDENAPI=true
-./default-build "$@"
-
-# Move the jar file into the resource folder to be bundled with the test.
-mkdir res
-mv ${TEST_NAME}.jar res/boot.jar
-
-# Clear all intermediate files otherwise default-build would either skip
-# compilation or fail rebuilding.
-rm -rf classes*
-
-export USE_HIDDENAPI=false
-./default-build "$@"
diff --git a/test/817-hiddenapi/build.py b/test/817-hiddenapi/build.py
new file mode 100644
index 0000000..37b8cd5
--- /dev/null
+++ b/test/817-hiddenapi/build.py
@@ -0,0 +1,40 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+
+ # Build the jars twice. First with applying hiddenapi, creating a boot jar, then
+ # a second time without to create a normal jar. We need to do this because we
+ # want to load the jar once as an app module and once as a member of the boot
+ # class path. The DexFileVerifier would fail on the former as it does not allow
+ # hidden API access flags in dex files. DexFileVerifier is not invoked on boot
+ # class path dex files, so the boot jar loads fine in the latter case.
+
+ ctx.default_build(use_hiddenapi=True)
+
+ # Move the jar file into the resource folder to be bundled with the test.
+ os.mkdir(ctx.test_dir / "res")
+ os.rename(ctx.test_dir / "817-hiddenapi.jar", ctx.test_dir / "res/boot.jar")
+
+ # Clear all intermediate files otherwise default-build would either skip
+ # compilation or fail rebuilding.
+ ctx.bash("rm -rf classes*")
+
+ ctx.default_build(use_hiddenapi=False)
diff --git a/test/817-hiddenapi/check b/test/817-hiddenapi/check
deleted file mode 100755
index 8c21ab4..0000000
--- a/test/817-hiddenapi/check
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2021 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# On gcstress configurations, an extra "JNI_OnUnload called" line may
-# be emitted. If so, remove it.
-sed -e '${/^JNI_OnUnload called$/d;}' "$2" > "$2.tmp"
-
-./default-check "$1" "$2.tmp" "$3" "$4"
diff --git a/test/817-hiddenapi/run.py b/test/817-hiddenapi/run.py
new file mode 100644
index 0000000..0ebb768
--- /dev/null
+++ b/test/817-hiddenapi/run.py
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # On gcstress configurations, an extra "JNI_OnUnload called" line may
+ # be emitted. If so, remove it.
+ ctx.run(fr"sed -i '/^JNI_OnUnload called$/d' '{args.stdout_file}'")
diff --git a/test/818-clinit-nterp/run b/test/818-clinit-nterp/run
deleted file mode 100644
index 52d2b5f..0000000
--- a/test/818-clinit-nterp/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-exec ${RUN} $@ --profile
diff --git a/test/818-clinit-nterp/run.py b/test/818-clinit-nterp/run.py
new file mode 100644
index 0000000..0f3a0ea
--- /dev/null
+++ b/test/818-clinit-nterp/run.py
@@ -0,0 +1,19 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, profile=True)
diff --git a/test/818-clinit-nterp/src/Main.java b/test/818-clinit-nterp/src/Main.java
index 5342cec..5ebd384 100644
--- a/test/818-clinit-nterp/src/Main.java
+++ b/test/818-clinit-nterp/src/Main.java
@@ -15,20 +15,19 @@
*/
public class Main {
- public static void main(String[] args) {
- Clinit.run();
- if (!clinitDidRun) {
- throw new Error("Expected Clinit.<clinit> to have run");
+ public static void main(String[] args) {
+ Clinit.run();
+ if (!clinitDidRun) {
+ throw new Error("Expected Clinit.<clinit> to have run");
+ }
}
- }
- static boolean clinitDidRun = false;
+ static boolean clinitDidRun = false;
}
class Clinit {
- public static void run() {
- }
+ public static void run() {}
- static {
- Main.clinitDidRun = true;
- }
+ static {
+ Main.clinitDidRun = true;
+ }
}
diff --git a/test/819-verification-runtime/run b/test/819-verification-runtime/run
deleted file mode 100755
index c8ce0bc..0000000
--- a/test/819-verification-runtime/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2021 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.
-
-# Target 31 to have the verifier behavior the test expects.
-./default-run "$@" --runtime-option -Xtarget-sdk-version:31
diff --git a/test/819-verification-runtime/run.py b/test/819-verification-runtime/run.py
new file mode 100644
index 0000000..f20dbc0
--- /dev/null
+++ b/test/819-verification-runtime/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # Target 31 to have the verifier behavior the test expects.
+ ctx.default_run(args, runtime_option=["-Xtarget-sdk-version:31"])
diff --git a/test/820-vdex-multidex/run b/test/820-vdex-multidex/run
deleted file mode 100644
index 3f6dc3c..0000000
--- a/test/820-vdex-multidex/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2021 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.
-
-exec ${RUN} -Xcompiler-option --compiler-filter=verify --vdex "${@}"
diff --git a/test/820-vdex-multidex/run.py b/test/820-vdex-multidex/run.py
new file mode 100644
index 0000000..f597e41
--- /dev/null
+++ b/test/820-vdex-multidex/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args, Xcompiler_option=["--compiler-filter=verify"], vdex=True)
diff --git a/test/821-madvise-willneed/run b/test/821-madvise-willneed/run
deleted file mode 100644
index 2c3917f..0000000
--- a/test/821-madvise-willneed/run
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2021 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.i
-
-# Load and run HelloWorld after madvising odex, vdex, art files to 100MB size
-# limit
-exec ${RUN} "${@}" --runtime-option -XMadviseWillNeedVdexFileSize:104857600 \
- --runtime-option -XMadviseWillNeedOdexFileSize:104857600 \
- --runtime-option -XMadviseWillNeedArtFileSize:104857600
diff --git a/test/821-madvise-willneed/run.py b/test/821-madvise-willneed/run.py
new file mode 100644
index 0000000..06d3bac
--- /dev/null
+++ b/test/821-madvise-willneed/run.py
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# Copyright (C) 2021 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.i
+
+
+def run(ctx, args):
+ # Load and run HelloWorld after madvising odex, vdex, art files to 100MB size
+ # limit
+ ctx.default_run(
+ args,
+ runtime_option=[
+ "-XMadviseWillNeedVdexFileSize:104857600",
+ "-XMadviseWillNeedOdexFileSize:104857600",
+ "-XMadviseWillNeedArtFileSize:104857600"
+ ])
diff --git a/test/822-hiddenapi-future/build b/test/822-hiddenapi-future/build
deleted file mode 100644
index 02ce549..0000000
--- a/test/822-hiddenapi-future/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2021 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.
-
-USE_HIDDENAPI=true ./default-build "$@"
diff --git a/test/822-hiddenapi-future/build.py b/test/822-hiddenapi-future/build.py
new file mode 100644
index 0000000..942bb00
--- /dev/null
+++ b/test/822-hiddenapi-future/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(use_hiddenapi=True)
diff --git a/test/823-cha-inlining/Android.bp b/test/823-cha-inlining/Android.bp
new file mode 100644
index 0000000..f174b02
--- /dev/null
+++ b/test/823-cha-inlining/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `823-cha-inlining`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-823-cha-inlining-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-823-cha-inlining",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-823-cha-inlining-src"
+ ],
+ data: [
+ ":art-run-test-823-cha-inlining-expected-stdout",
+ ":art-run-test-823-cha-inlining-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-823-cha-inlining-expected-stdout",
+ out: ["art-run-test-823-cha-inlining-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-823-cha-inlining-expected-stderr",
+ out: ["art-run-test-823-cha-inlining-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/829-unresolved-enclosing/build b/test/829-unresolved-enclosing/build
deleted file mode 100644
index f378df1..0000000
--- a/test/829-unresolved-enclosing/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Stop if something fails.
-set -e
-
-./default-build "$@"
diff --git a/test/829-unresolved-enclosing/javac_post.sh b/test/829-unresolved-enclosing/javac_post.sh
new file mode 100755
index 0000000..93875d4
--- /dev/null
+++ b/test/829-unresolved-enclosing/javac_post.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+set -e # Stop on error - the caller script may not have this set.
+
+# Remove class available at compile time but not at run time.
+rm classes/MissingSuperClass.class
diff --git a/test/829-unresolved-enclosing/javac_wrapper.sh b/test/829-unresolved-enclosing/javac_wrapper.sh
deleted file mode 100755
index e3dc1e3..0000000
--- a/test/829-unresolved-enclosing/javac_wrapper.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-set -e # Stop on error - the caller script may not have this set.
-
-$JAVAC "$@"
-
-# Remove class available at compile time but not at run time.
-rm classes/MissingSuperClass.class
diff --git a/test/831-unverified-bcp/build.py b/test/831-unverified-bcp/build.py
new file mode 100644
index 0000000..7025b81
--- /dev/null
+++ b/test/831-unverified-bcp/build.py
@@ -0,0 +1,20 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ if ctx.jvm:
+ return # The test does not build on JVM
+ ctx.default_build()
diff --git a/test/831-unverified-bcp/test-metadata.json b/test/831-unverified-bcp/test-metadata.json
new file mode 100644
index 0000000..75f6c02
--- /dev/null
+++ b/test/831-unverified-bcp/test-metadata.json
@@ -0,0 +1,5 @@
+{
+ "build-param": {
+ "jvm-supported": "false"
+ }
+}
diff --git a/test/833-background-verification/run b/test/833-background-verification/run
deleted file mode 100755
index c455473..0000000
--- a/test/833-background-verification/run
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2021 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.
-
-# Target 31 to run the background verifier.
-# Disable dex2oat of secondary dex files to ensure we always run the background
-# verifier.
-./default-run "$@" --runtime-option -Xtarget-sdk-version:31 --no-secondary-compilation
diff --git a/test/833-background-verification/run.py b/test/833-background-verification/run.py
new file mode 100644
index 0000000..44d80ac
--- /dev/null
+++ b/test/833-background-verification/run.py
@@ -0,0 +1,25 @@
+#!/bin/bash
+#
+# Copyright 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # Target 31 to run the background verifier.
+ # Disable dex2oat of secondary dex files to ensure we always run the background
+ # verifier.
+ ctx.default_run(
+ args,
+ runtime_option=["-Xtarget-sdk-version:31"],
+ secondary_compilation=False)
diff --git a/test/837-deopt/Android.bp b/test/837-deopt/Android.bp
new file mode 100644
index 0000000..2e0e3aa
--- /dev/null
+++ b/test/837-deopt/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `837-deopt`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-837-deopt",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-837-deopt-expected-stdout",
+ ":art-run-test-837-deopt-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-837-deopt-expected-stdout",
+ out: ["art-run-test-837-deopt-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-837-deopt-expected-stderr",
+ out: ["art-run-test-837-deopt-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/837-deopt/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/837-deopt/expected-stderr.txt
diff --git a/test/837-deopt/expected-stdout.txt b/test/837-deopt/expected-stdout.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/837-deopt/expected-stdout.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/837-deopt/info.txt b/test/837-deopt/info.txt
new file mode 100644
index 0000000..5c95277
--- /dev/null
+++ b/test/837-deopt/info.txt
@@ -0,0 +1 @@
+Tests around deoptimization.
diff --git a/test/837-deopt/src/Main.java b/test/837-deopt/src/Main.java
new file mode 100644
index 0000000..8e3ad7c
--- /dev/null
+++ b/test/837-deopt/src/Main.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class Main {
+ int field = 42;
+
+ // Test that deoptimization preserves objects that are singletons.
+ public static int $noinline$foo(Main arg) {
+ Main m = new Main();
+ arg.returnValue();
+ return m.field;
+ }
+
+ // Test that doing OSR after deoptimization works.
+ public static int $noinline$foo2(Main arg, boolean osr) {
+ Main m = new Main();
+ arg.returnValue();
+ if (osr) {
+ while (!isInOsrCode("$noinline$foo2")) {}
+ }
+ return m.field;
+ }
+
+ public static void main(String[] args) throws Throwable {
+ System.loadLibrary(args[0]);
+ if (isDebuggable()) {
+ // We do not deoptimize with inline caches when the app is debuggable, so just don't run the
+ // test.
+ return;
+ }
+ test1();
+ test2();
+ }
+
+ public static void assertEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new Error("Expected " + expected + ", got " + actual);
+ }
+ }
+
+ public static void test1() {
+ ensureJitBaselineCompiled(Main.class, "$noinline$foo");
+ // Surround the call with GCs to increase chances we execute $noinline$foo
+ // while the GC isn't marking. This makes sure the inline cache is populated.
+ Runtime.getRuntime().gc();
+ assertEquals(42, $noinline$foo(new Main()));
+ Runtime.getRuntime().gc();
+
+ ensureJitCompiled(Main.class, "$noinline$foo");
+ assertEquals(42, $noinline$foo(new SubMain()));
+ }
+
+ public static void test2() {
+ ensureJitBaselineCompiled(Main.class, "$noinline$foo2");
+ // Surround the call with GCs to increase chances we execute $noinline$foo
+ // while the GC isn't marking. This makes sure the inline cache is populated.
+ Runtime.getRuntime().gc();
+ assertEquals(42, $noinline$foo2(new Main(), false));
+ Runtime.getRuntime().gc();
+
+ ensureJitCompiled(Main.class, "$noinline$foo2");
+ assertEquals(42, $noinline$foo2(new SubMain(), true));
+ }
+
+ public String returnValue() {
+ return "Main";
+ }
+
+ public static native void ensureJitCompiled(Class<?> cls, String methodName);
+ public static native void ensureJitBaselineCompiled(Class<?> cls, String methodName);
+ public static native boolean isInOsrCode(String methodName);
+ public static native boolean isDebuggable();
+}
+
+// Define a subclass with another implementation of returnValue to deoptimize $noinline$foo and
+// $noinline$foo2.
+class SubMain extends Main {
+ public String returnValue() {
+ return "SubMain";
+ }
+}
diff --git a/test/838-override/Android.bp b/test/838-override/Android.bp
new file mode 100644
index 0000000..2ac4e21
--- /dev/null
+++ b/test/838-override/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `838-override`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-838-override",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-838-override-expected-stdout",
+ ":art-run-test-838-override-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-838-override-expected-stdout",
+ out: ["art-run-test-838-override-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-838-override-expected-stderr",
+ out: ["art-run-test-838-override-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/838-override/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/838-override/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/838-override/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/838-override/expected-stdout.txt
diff --git a/test/838-override/info.txt b/test/838-override/info.txt
new file mode 100644
index 0000000..a33ddb2
--- /dev/null
+++ b/test/838-override/info.txt
@@ -0,0 +1 @@
+Tests for method overriding in the presence of package private methods.
diff --git a/test/838-override/src/Main.java b/test/838-override/src/Main.java
new file mode 100644
index 0000000..1e6aa49
--- /dev/null
+++ b/test/838-override/src/Main.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2022 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 assertEquals(Object expected, Object actual) {
+ if (expected != actual) {
+ throw new Error("Expected " + expected + ", got " + actual);
+ }
+ }
+
+ public static void main(String[] args) {
+ // Tescase 1: a class with:
+ // - a package-private pkg1 'foo'
+ // - a public pkg2 'foo'.
+ {
+ pkg2.PublicFoo obj = new pkg2.PublicFoo();
+ assertEquals(pkg1.Pkg1Foo.class, pkg1.Pkg1Foo.callFoo(obj));
+ assertEquals(pkg2.PublicFoo.class, pkg2.Pkg2Foo.callFoo(obj));
+ assertEquals(pkg2.PublicFoo.class, pkg3.Pkg3Foo.callFoo(obj));
+ assertEquals(pkg2.PublicFoo.class, obj.foo());
+ }
+ // Tescase 2: a class with:
+ // - a package-private pkg1 'foo'
+ // - a public pkg2 'foo'
+ // - a public pkg1 'foo.
+ {
+ pkg1.PublicFoo obj = new pkg1.PublicFoo();
+ assertEquals(pkg1.PublicFoo.class, pkg1.Pkg1Foo.callFoo(obj));
+ assertEquals(pkg1.PublicFoo.class, pkg2.Pkg2Foo.callFoo(obj));
+ assertEquals(pkg1.PublicFoo.class, pkg3.Pkg3Foo.callFoo(obj));
+ assertEquals(pkg1.PublicFoo.class, obj.foo());
+ }
+
+ // Tescase 3: a class with:
+ // - a package-private pkg1 'foo'
+ // - a package-private pkg2 'foo'
+ // - a public pkg3 'foo.
+ {
+ pkg3.PublicFoo obj = new pkg3.PublicFoo();
+ assertEquals(pkg1.Pkg1Foo.class, pkg1.Pkg1Foo.callFoo(obj));
+ assertEquals(pkg2.Pkg2Foo.class, pkg2.Pkg2Foo.callFoo(obj));
+ assertEquals(pkg3.PublicFoo.class, pkg3.Pkg3Foo.callFoo(obj));
+ assertEquals(pkg3.PublicFoo.class, obj.foo());
+ }
+
+ // Tescase 4: a class with:
+ // - a package-private pkg1 'foo'
+ // - a package-private pkg2 'foo'
+ // - a public pkg3 'foo.
+ // - a public pkg2 'foo'
+ {
+ pkg2.PublicFooInheritsPkg3 obj = new pkg2.PublicFooInheritsPkg3();
+ assertEquals(pkg1.Pkg1Foo.class, pkg1.Pkg1Foo.callFoo(obj));
+ assertEquals(pkg2.PublicFooInheritsPkg3.class, pkg2.Pkg2Foo.callFoo(obj));
+ assertEquals(pkg2.PublicFooInheritsPkg3.class, pkg3.Pkg3Foo.callFoo(obj));
+ assertEquals(pkg2.PublicFooInheritsPkg3.class, obj.foo());
+ }
+
+ // Tescase 5: a class with:
+ // - a package-private pkg1 'foo'
+ // - a package-private pkg2 'foo'
+ // - a public pkg3 'foo.
+ // - a public pkg2 'foo'
+ // - a public pkg1 'foo'
+ {
+ pkg1.PublicFooInheritsPkg2 obj = new pkg1.PublicFooInheritsPkg2();
+ assertEquals(pkg1.PublicFooInheritsPkg2.class, pkg1.Pkg1Foo.callFoo(obj));
+ assertEquals(pkg1.PublicFooInheritsPkg2.class, pkg2.Pkg2Foo.callFoo(obj));
+ assertEquals(pkg1.PublicFooInheritsPkg2.class, pkg3.Pkg3Foo.callFoo(obj));
+ assertEquals(pkg1.PublicFooInheritsPkg2.class, obj.foo());
+ }
+
+ // Tescase 6: a class with:
+ // - a package-private pkg1 'foo'
+ // - a package-private pkg2 'foo'
+ // - a public pkg1 'foo.
+ {
+ pkg1.LowerIndexImplementsFoo obj = new pkg1.LowerIndexImplementsFoo();
+ assertEquals(pkg1.LowerIndexPublicFoo.class, pkg1.Pkg1Foo.callFoo(obj));
+ assertEquals(pkg2.Pkg2Foo.class, pkg2.Pkg2Foo.callFoo(obj));
+ assertEquals(pkg2.Pkg2Foo.class, pkg3.Pkg3Foo.callFoo(obj));
+ assertEquals(pkg1.LowerIndexPublicFoo.class, obj.foo());
+ }
+ }
+}
diff --git a/test/838-override/src/pkg1/InterfaceFoo.java b/test/838-override/src/pkg1/InterfaceFoo.java
new file mode 100644
index 0000000..cd9e44b
--- /dev/null
+++ b/test/838-override/src/pkg1/InterfaceFoo.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 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 pkg1;
+
+public interface InterfaceFoo {
+ public Class<?> foo();
+}
diff --git a/test/838-override/src/pkg1/LowerIndexImplementsFoo.java b/test/838-override/src/pkg1/LowerIndexImplementsFoo.java
new file mode 100644
index 0000000..290f07b
--- /dev/null
+++ b/test/838-override/src/pkg1/LowerIndexImplementsFoo.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 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 pkg1;
+
+public class LowerIndexImplementsFoo extends LowerIndexPublicFoo implements InterfaceFoo {
+}
diff --git a/test/838-override/src/pkg1/LowerIndexPublicFoo.java b/test/838-override/src/pkg1/LowerIndexPublicFoo.java
new file mode 100644
index 0000000..87c0c72
--- /dev/null
+++ b/test/838-override/src/pkg1/LowerIndexPublicFoo.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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 pkg1;
+
+public class LowerIndexPublicFoo extends pkg2.Pkg2Foo {
+ public Class<?> foo() {
+ return LowerIndexPublicFoo.class;
+ }
+}
diff --git a/test/838-override/src/pkg1/Pkg1Foo.java b/test/838-override/src/pkg1/Pkg1Foo.java
new file mode 100644
index 0000000..0ffbc27
--- /dev/null
+++ b/test/838-override/src/pkg1/Pkg1Foo.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2022 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 pkg1;
+
+public class Pkg1Foo {
+
+ Class<?> foo() {
+ return Pkg1Foo.class;
+ }
+
+ public static Class<?> callFoo(Pkg1Foo obj) {
+ return obj.foo();
+ }
+}
diff --git a/test/838-override/src/pkg1/PublicFoo.java b/test/838-override/src/pkg1/PublicFoo.java
new file mode 100644
index 0000000..c43b282
--- /dev/null
+++ b/test/838-override/src/pkg1/PublicFoo.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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 pkg1;
+
+public class PublicFoo extends pkg2.PublicFoo {
+ public Class<?> foo() {
+ return PublicFoo.class;
+ }
+}
diff --git a/test/838-override/src/pkg1/PublicFooInheritsPkg2.java b/test/838-override/src/pkg1/PublicFooInheritsPkg2.java
new file mode 100644
index 0000000..a255f36
--- /dev/null
+++ b/test/838-override/src/pkg1/PublicFooInheritsPkg2.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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 pkg1;
+
+public class PublicFooInheritsPkg2 extends pkg2.PublicFooInheritsPkg3 {
+ public Class<?> foo() {
+ return PublicFooInheritsPkg2.class;
+ }
+}
diff --git a/test/838-override/src/pkg2/Pkg2Foo.java b/test/838-override/src/pkg2/Pkg2Foo.java
new file mode 100644
index 0000000..c521914
--- /dev/null
+++ b/test/838-override/src/pkg2/Pkg2Foo.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 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 pkg2;
+
+public class Pkg2Foo extends pkg1.Pkg1Foo{
+ Class<?> foo() {
+ return Pkg2Foo.class;
+ }
+
+ public static Class<?> callFoo(Pkg2Foo obj) {
+ return obj.foo();
+ }
+}
diff --git a/test/838-override/src/pkg2/PublicFoo.java b/test/838-override/src/pkg2/PublicFoo.java
new file mode 100644
index 0000000..5434234
--- /dev/null
+++ b/test/838-override/src/pkg2/PublicFoo.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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 pkg2;
+
+public class PublicFoo extends Pkg2Foo {
+ public Class<?> foo() {
+ return PublicFoo.class;
+ }
+}
diff --git a/test/838-override/src/pkg2/PublicFooInheritsPkg3.java b/test/838-override/src/pkg2/PublicFooInheritsPkg3.java
new file mode 100644
index 0000000..1a983a1
--- /dev/null
+++ b/test/838-override/src/pkg2/PublicFooInheritsPkg3.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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 pkg2;
+
+public class PublicFooInheritsPkg3 extends pkg3.PublicFoo {
+ public Class<?> foo() {
+ return PublicFooInheritsPkg3.class;
+ }
+}
diff --git a/test/838-override/src/pkg3/Pkg3Foo.java b/test/838-override/src/pkg3/Pkg3Foo.java
new file mode 100644
index 0000000..72a16aa
--- /dev/null
+++ b/test/838-override/src/pkg3/Pkg3Foo.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 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 pkg3;
+
+public class Pkg3Foo extends pkg2.Pkg2Foo {
+ Class<?> foo() {
+ return Pkg3Foo.class;
+ }
+
+ public static Class<?> callFoo(Pkg3Foo obj) {
+ return obj.foo();
+ }
+}
diff --git a/test/838-override/src/pkg3/PublicFoo.java b/test/838-override/src/pkg3/PublicFoo.java
new file mode 100644
index 0000000..9d37137
--- /dev/null
+++ b/test/838-override/src/pkg3/PublicFoo.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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 pkg3;
+
+public class PublicFoo extends Pkg3Foo {
+ public Class<?> foo() {
+ return PublicFoo.class;
+ }
+}
diff --git a/test/839-clinit-throw/Android.bp b/test/839-clinit-throw/Android.bp
new file mode 100644
index 0000000..dad79f0
--- /dev/null
+++ b/test/839-clinit-throw/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `839-clinit-throw`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-839-clinit-throw",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-839-clinit-throw-expected-stdout",
+ ":art-run-test-839-clinit-throw-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-839-clinit-throw-expected-stdout",
+ out: ["art-run-test-839-clinit-throw-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-839-clinit-throw-expected-stderr",
+ out: ["art-run-test-839-clinit-throw-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/839-clinit-throw/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/839-clinit-throw/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/839-clinit-throw/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/839-clinit-throw/expected-stdout.txt
diff --git a/test/839-clinit-throw/info.txt b/test/839-clinit-throw/info.txt
new file mode 100644
index 0000000..36cbb6f
--- /dev/null
+++ b/test/839-clinit-throw/info.txt
@@ -0,0 +1,2 @@
+Test that a static initializer throwing doesn't contain a static method of the
+same class in the stack trace.
diff --git a/test/839-clinit-throw/src/Main.java b/test/839-clinit-throw/src/Main.java
new file mode 100644
index 0000000..bb21de1
--- /dev/null
+++ b/test/839-clinit-throw/src/Main.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 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 {
+
+ static class NoPreloadHolder {
+ static Object o = null;
+
+ static {
+ o.toString();
+ }
+
+ static void $noinline$doCall() {
+ }
+ }
+
+ public static void main(String[] args) {
+ try {
+ NoPreloadHolder.$noinline$doCall();
+ throw new Error("Expected ExceptionInInitializerError");
+ } catch (ExceptionInInitializerError e) {
+ // expected
+ check(e, mainLine);
+ }
+ }
+
+ public static int mainLine = 32;
+
+ static void check(ExceptionInInitializerError ie, int mainLine) {
+ StackTraceElement[] trace = ie.getStackTrace();
+ assertEquals(trace.length, 1);
+ checkElement(trace[0], "Main", "main", "Main.java", mainLine);
+ }
+
+ static void checkElement(StackTraceElement element,
+ String declaringClass, String methodName,
+ String fileName, int lineNumber) {
+ assertEquals(declaringClass, element.getClassName());
+ assertEquals(methodName, element.getMethodName());
+ assertEquals(fileName, element.getFileName());
+ assertEquals(lineNumber, element.getLineNumber());
+ }
+
+ static void assertEquals(Object expected, Object actual) {
+ if (!expected.equals(actual)) {
+ String msg = "Expected \"" + expected + "\" but got \"" + actual + "\"";
+ throw new AssertionError(msg);
+ }
+ }
+
+ static void assertEquals(int expected, int actual) {
+ if (expected != actual) {
+ throw new AssertionError("Expected " + expected + " got " + actual);
+ }
+ }
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/840-resolution/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/840-resolution/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/840-resolution/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/840-resolution/expected-stdout.txt
diff --git a/test/840-resolution/info.txt b/test/840-resolution/info.txt
new file mode 100644
index 0000000..bd88f7d
--- /dev/null
+++ b/test/840-resolution/info.txt
@@ -0,0 +1,2 @@
+Various tests on interface method linking when not finding a public
+implementation.
diff --git a/test/840-resolution/jasmin/SubClass2.j b/test/840-resolution/jasmin/SubClass2.j
new file mode 100644
index 0000000..8a65e1f
--- /dev/null
+++ b/test/840-resolution/jasmin/SubClass2.j
@@ -0,0 +1,35 @@
+; Copyright (C) 2022 The Android Open Source Project
+;
+; Licensed under the Apache License, Version 2.0 (the "License");
+; you may not use this file except in compliance with the License.
+; You may obtain a copy of the License at
+;
+; http://www.apache.org/licenses/LICENSE-2.0
+;
+; Unless required by applicable law or agreed to in writing, software
+; distributed under the License is distributed on an "AS IS" BASIS,
+; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+; See the License for the specific language governing permissions and
+; limitations under the License.
+
+.class public SubClass2
+.super SuperClass
+.implements Interface
+
+.method public <init>()V
+ .limit stack 1
+ .limit locals 1
+ aload_0
+ invokespecial SuperClass/<init>()V
+ return
+.end method
+
+.method foo()Ljava/lang/Class;
+ .limit stack 1
+ .limit locals 1
+ ; jasmin does not support ldc with a class, so just return null for the
+ ; purpose of this test.
+ aconst_null
+ areturn
+.end method
+
diff --git a/test/840-resolution/src/Main.java b/test/840-resolution/src/Main.java
new file mode 100644
index 0000000..23cd31d
--- /dev/null
+++ b/test/840-resolution/src/Main.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2022 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 {
+
+ // Testcase 1: the superclass has a package private version in the same package.
+ static Interface s = new SubClass();
+
+ // Testcase 2: the class has a package private version.
+ static Interface s2;
+
+ // Testcase 3: the superclass has a package private version in a different package.
+ static Interface s3 = new SubClassFromPkg();
+
+ // Testcase 4: there is no implementation in the hierarchy.
+ static Interface s4 = new SubClassNoFoo();
+
+ // Testcase 5: there is a private method in the hierarchy.
+ static Interface s5 = new SubClassPrivateFoo();
+
+ // Testcase 6: there is a static method in the hierarchy.
+ static Interface s6 = new SubClassStaticFoo();
+
+ static {
+ try {
+ s2 = (Interface) Class.forName("SubClass2").newInstance();
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+
+ public static void assertEquals(Object expected, Object actual) {
+ if (expected != actual) {
+ throw new Error("Expected " + expected + ", got " + actual);
+ }
+ }
+
+ public static void assertTrue(boolean value) {
+ if (!value) {
+ throw new Error("");
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ assertEquals(SuperClass.class, ((SubClass) s).foo());
+ assertEquals(SuperClass.class, ((SuperClass) s).foo());
+
+ try {
+ s.foo();
+ throw new Error("Expected IllegalAccessError");
+ } catch (IllegalAccessError ie) {
+ // expected
+ }
+
+ assertEquals(null, ((SuperClass) s2).foo());
+ try {
+ s2.foo();
+ throw new Error("Expected IllegalAccessError");
+ } catch (IllegalAccessError ie) {
+ // expected
+ }
+
+ try {
+ ((pkg.PkgSuperClass) s3).foo();
+ throw new Error("Expected IllegalAccessError");
+ } catch (IllegalAccessError ie) {
+ // expected
+ }
+
+ try {
+ ((SubClassFromPkg) s3).foo();
+ throw new Error("Expected IllegalAccessError");
+ } catch (IllegalAccessError ie) {
+ // expected
+ }
+
+ try {
+ s3.foo();
+ throw new Error("Expected IllegalAccessError");
+ } catch (IllegalAccessError ie) {
+ // expected
+ }
+
+ try {
+ ((SuperClassNoFoo) s4).foo();
+ throw new Error("Expected NoSuchMethodError");
+ } catch (NoSuchMethodError e) {
+ // expected
+ }
+
+ try {
+ ((SubClassNoFoo) s4).foo();
+ throw new Error("Expected AbstractMethodError");
+ } catch (AbstractMethodError e) {
+ // expected
+ }
+
+ try {
+ s4.foo();
+ throw new Error("Expected AbstractMethodError");
+ } catch (AbstractMethodError e) {
+ // expected
+ }
+
+ try {
+ ((SuperClassPrivateFoo) s5).foo();
+ throw new Error("Expected IllegalAccessError");
+ } catch (IllegalAccessError e) {
+ // expected
+ }
+
+ try {
+ ((SubClassPrivateFoo) s5).foo();
+ throw new Error("Expected IllegalAccessError");
+ } catch (IllegalAccessError e) {
+ // expected
+ }
+
+ try {
+ s5.foo();
+ throw new Error("Expected AbstractMethodError on RI, IllegalAccessError on ART");
+ } catch (AbstractMethodError | IllegalAccessError e) {
+ // expected
+ }
+
+ try {
+ ((SuperClassStaticFoo) s6).foo();
+ throw new Error("Expected IncompatibleClassChangeError");
+ } catch (IncompatibleClassChangeError e) {
+ // expected
+ }
+
+ try {
+ ((SubClassStaticFoo) s6).foo();
+ throw new Error("Expected IncompatibleClassChangeError");
+ } catch (IncompatibleClassChangeError e) {
+ // expected
+ }
+
+ try {
+ s6.foo();
+ throw new Error("Expected AbstractMethodError");
+ } catch (AbstractMethodError e) {
+ // expected
+ }
+ }
+}
+
+interface Interface {
+ public Class<?> foo();
+}
+
+class SubClass extends SuperClass implements Interface {
+}
+
+class SubClassFromPkg extends pkg.PkgSuperClass implements Interface {
+}
+
+class SubClassNoFoo extends SuperClassNoFoo implements Interface {
+}
+
+class SubClassPrivateFoo extends SuperClassPrivateFoo implements Interface {
+}
+
+class SubClassStaticFoo extends SuperClassStaticFoo implements Interface {
+}
diff --git a/test/840-resolution/src/SuperClass.java b/test/840-resolution/src/SuperClass.java
new file mode 100644
index 0000000..ece0188
--- /dev/null
+++ b/test/840-resolution/src/SuperClass.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class SuperClass {
+ public Class<?> foo() {
+ return SuperClass.class;
+ }
+}
diff --git a/test/840-resolution/src/SuperClassNoFoo.java b/test/840-resolution/src/SuperClassNoFoo.java
new file mode 100644
index 0000000..747aaef
--- /dev/null
+++ b/test/840-resolution/src/SuperClassNoFoo.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 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 SuperClassNoFoo {
+ public Class<?> foo() {
+ throw new Error("Unreachable");
+ }
+}
diff --git a/test/840-resolution/src/SuperClassPrivateFoo.java b/test/840-resolution/src/SuperClassPrivateFoo.java
new file mode 100644
index 0000000..95af4f7
--- /dev/null
+++ b/test/840-resolution/src/SuperClassPrivateFoo.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class SuperClassPrivateFoo {
+ public Class<?> foo() {
+ return SuperClass.class;
+ }
+}
diff --git a/test/840-resolution/src/SuperClassStaticFoo.java b/test/840-resolution/src/SuperClassStaticFoo.java
new file mode 100644
index 0000000..490637f
--- /dev/null
+++ b/test/840-resolution/src/SuperClassStaticFoo.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 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 SuperClassStaticFoo {
+ public Class<?> foo() {
+ throw new Error("Unreachable");
+ }
+}
diff --git a/test/840-resolution/src/pkg/PkgSuperClass.java b/test/840-resolution/src/pkg/PkgSuperClass.java
new file mode 100644
index 0000000..397ae28
--- /dev/null
+++ b/test/840-resolution/src/pkg/PkgSuperClass.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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 pkg;
+
+public class PkgSuperClass {
+ public Class<?> foo() {
+ return PkgSuperClass.class;
+ }
+}
diff --git a/test/840-resolution/src2/SuperClass.java b/test/840-resolution/src2/SuperClass.java
new file mode 100644
index 0000000..fe40c0a
--- /dev/null
+++ b/test/840-resolution/src2/SuperClass.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class SuperClass {
+ Class<?> foo() {
+ return SuperClass.class;
+ }
+}
diff --git a/test/840-resolution/src2/SuperClassNoFoo.java b/test/840-resolution/src2/SuperClassNoFoo.java
new file mode 100644
index 0000000..c0e8c44
--- /dev/null
+++ b/test/840-resolution/src2/SuperClassNoFoo.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2022 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 SuperClassNoFoo {
+}
diff --git a/test/840-resolution/src2/SuperClassPrivateFoo.java b/test/840-resolution/src2/SuperClassPrivateFoo.java
new file mode 100644
index 0000000..f0c1c68
--- /dev/null
+++ b/test/840-resolution/src2/SuperClassPrivateFoo.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class SuperClassPrivateFoo {
+ private Class<?> foo() {
+ return SuperClass.class;
+ }
+}
diff --git a/test/840-resolution/src2/SuperClassStaticFoo.java b/test/840-resolution/src2/SuperClassStaticFoo.java
new file mode 100644
index 0000000..ecf8bc1
--- /dev/null
+++ b/test/840-resolution/src2/SuperClassStaticFoo.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 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 SuperClassStaticFoo {
+ public static Class<?> foo() {
+ return SuperClassStaticFoo.class;
+ }
+}
diff --git a/test/840-resolution/src2/pkg/PkgSuperClass.java b/test/840-resolution/src2/pkg/PkgSuperClass.java
new file mode 100644
index 0000000..2f333ee
--- /dev/null
+++ b/test/840-resolution/src2/pkg/PkgSuperClass.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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 pkg;
+
+public class PkgSuperClass {
+ Class<?> foo() {
+ return PkgSuperClass.class;
+ }
+}
diff --git a/test/841-defaults/Android.bp b/test/841-defaults/Android.bp
new file mode 100644
index 0000000..2d2fce6
--- /dev/null
+++ b/test/841-defaults/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `841-defaults`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-841-defaults",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-841-defaults-expected-stdout",
+ ":art-run-test-841-defaults-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-841-defaults-expected-stdout",
+ out: ["art-run-test-841-defaults-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-841-defaults-expected-stderr",
+ out: ["art-run-test-841-defaults-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/841-defaults/expected-stderr.txt
similarity index 100%
rename from test/089-many-methods/expected-stderr.txt
rename to test/841-defaults/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/841-defaults/expected-stdout.txt
similarity index 100%
rename from test/089-many-methods/expected-stdout.txt
rename to test/841-defaults/expected-stdout.txt
diff --git a/test/841-defaults/info.txt b/test/841-defaults/info.txt
new file mode 100644
index 0000000..85e265f
--- /dev/null
+++ b/test/841-defaults/info.txt
@@ -0,0 +1,2 @@
+Regression test for doing an invokeinterface on a default method whose
+dex method index is greater than the imt size.
diff --git a/test/841-defaults/src/Main.java b/test/841-defaults/src/Main.java
new file mode 100644
index 0000000..c07b516
--- /dev/null
+++ b/test/841-defaults/src/Main.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+interface Itf {
+ default int defaultMethod1() { return 1; }
+ default int defaultMethod2() { return 2; }
+ default int defaultMethod3() { return 3; }
+ default int defaultMethod4() { return 4; }
+ default int defaultMethod5() { return 5; }
+ default int defaultMethod6() { return 6; }
+ default int defaultMethod7() { return 7; }
+ default int defaultMethod8() { return 8; }
+ default int defaultMethod9() { return 9; }
+ default int defaultMethod10() { return 10; }
+ default int defaultMethod11() { return 11; }
+ default int defaultMethod12() { return 12; }
+ default int defaultMethod13() { return 13; }
+ default int defaultMethod14() { return 14; }
+ default int defaultMethod15() { return 15; }
+ default int defaultMethod16() { return 16; }
+ default int defaultMethod17() { return 17; }
+ default int defaultMethod18() { return 18; }
+ default int defaultMethod19() { return 19; }
+ default int defaultMethod20() { return 20; }
+ default int defaultMethod21() { return 21; }
+ default int defaultMethod22() { return 22; }
+ default int defaultMethod23() { return 23; }
+ default int defaultMethod24() { return 24; }
+ default int defaultMethod25() { return 25; }
+ default int defaultMethod26() { return 26; }
+ default int defaultMethod27() { return 27; }
+ default int defaultMethod28() { return 28; }
+ default int defaultMethod29() { return 29; }
+ default int defaultMethod30() { return 30; }
+ default int defaultMethod31() { return 31; }
+ default int defaultMethod32() { return 32; }
+ default int defaultMethod33() { return 33; }
+ default int defaultMethod34() { return 34; }
+ default int defaultMethod35() { return 35; }
+ default int defaultMethod36() { return 36; }
+ default int defaultMethod37() { return 37; }
+ default int defaultMethod38() { return 38; }
+ default int defaultMethod39() { return 39; }
+ default int defaultMethod40() { return 40; }
+ default int defaultMethod41() { return 41; }
+ default int defaultMethod42() { return 42; }
+ default int defaultMethod43() { return 43; }
+ default int defaultMethod44() { return 44; }
+ default int defaultMethod45() { return 45; }
+ default int defaultMethod46() { return 46; }
+ default int defaultMethod47() { return 47; }
+ default int defaultMethod48() { return 48; }
+ default int defaultMethod49() { return 49; }
+ default int defaultMethod50() { return 50; }
+ default int defaultMethod51() { return 51; }
+}
+
+public class Main implements Itf {
+ static Itf itf = new Main();
+ public static void assertEquals(int value, int expected) {
+ if (value != expected) {
+ throw new Error("Expected " + expected + ", got " + value);
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ assertEquals(itf.defaultMethod1(), 1);
+ assertEquals(itf.defaultMethod2(), 2);
+ assertEquals(itf.defaultMethod3(), 3);
+ assertEquals(itf.defaultMethod4(), 4);
+ assertEquals(itf.defaultMethod5(), 5);
+ assertEquals(itf.defaultMethod6(), 6);
+ assertEquals(itf.defaultMethod7(), 7);
+ assertEquals(itf.defaultMethod8(), 8);
+ assertEquals(itf.defaultMethod9(), 9);
+ assertEquals(itf.defaultMethod10(), 10);
+ assertEquals(itf.defaultMethod11(), 11);
+ assertEquals(itf.defaultMethod12(), 12);
+ assertEquals(itf.defaultMethod13(), 13);
+ assertEquals(itf.defaultMethod14(), 14);
+ assertEquals(itf.defaultMethod15(), 15);
+ assertEquals(itf.defaultMethod16(), 16);
+ assertEquals(itf.defaultMethod17(), 17);
+ assertEquals(itf.defaultMethod18(), 18);
+ assertEquals(itf.defaultMethod19(), 19);
+ assertEquals(itf.defaultMethod20(), 20);
+ assertEquals(itf.defaultMethod21(), 21);
+ assertEquals(itf.defaultMethod22(), 22);
+ assertEquals(itf.defaultMethod23(), 23);
+ assertEquals(itf.defaultMethod24(), 24);
+ assertEquals(itf.defaultMethod25(), 25);
+ assertEquals(itf.defaultMethod26(), 26);
+ assertEquals(itf.defaultMethod27(), 27);
+ assertEquals(itf.defaultMethod28(), 28);
+ assertEquals(itf.defaultMethod29(), 29);
+ assertEquals(itf.defaultMethod30(), 30);
+ assertEquals(itf.defaultMethod31(), 31);
+ assertEquals(itf.defaultMethod32(), 32);
+ assertEquals(itf.defaultMethod33(), 33);
+ assertEquals(itf.defaultMethod34(), 34);
+ assertEquals(itf.defaultMethod35(), 35);
+ assertEquals(itf.defaultMethod36(), 36);
+ assertEquals(itf.defaultMethod37(), 37);
+ assertEquals(itf.defaultMethod38(), 38);
+ assertEquals(itf.defaultMethod39(), 39);
+ assertEquals(itf.defaultMethod40(), 40);
+ assertEquals(itf.defaultMethod41(), 41);
+ assertEquals(itf.defaultMethod42(), 42);
+ assertEquals(itf.defaultMethod43(), 43);
+ assertEquals(itf.defaultMethod44(), 44);
+ assertEquals(itf.defaultMethod45(), 45);
+ assertEquals(itf.defaultMethod46(), 46);
+ assertEquals(itf.defaultMethod47(), 47);
+ assertEquals(itf.defaultMethod48(), 48);
+ assertEquals(itf.defaultMethod49(), 49);
+ assertEquals(itf.defaultMethod50(), 50);
+ assertEquals(itf.defaultMethod51(), 51);
+ }
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/842-vdex-hard-failure/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/842-vdex-hard-failure/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/842-vdex-hard-failure/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/842-vdex-hard-failure/expected-stdout.txt
diff --git a/test/842-vdex-hard-failure/info.txt b/test/842-vdex-hard-failure/info.txt
new file mode 100644
index 0000000..21412c7
--- /dev/null
+++ b/test/842-vdex-hard-failure/info.txt
@@ -0,0 +1,2 @@
+Regression test for vdex, where we were compiling methods that had hard
+failures.
diff --git a/test/842-vdex-hard-failure/run.py b/test/842-vdex-hard-failure/run.py
new file mode 100644
index 0000000..81ce172
--- /dev/null
+++ b/test/842-vdex-hard-failure/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ # This test is for testing vdex when calling FastVerify and doing compilation.
+ ctx.default_run(args, vdex=True, vdex_filter="speed")
diff --git a/test/842-vdex-hard-failure/smali/HardFail.smali b/test/842-vdex-hard-failure/smali/HardFail.smali
new file mode 100644
index 0000000..52aefe0
--- /dev/null
+++ b/test/842-vdex-hard-failure/smali/HardFail.smali
@@ -0,0 +1,26 @@
+# /*
+# * Copyright (C) 2022 The Android Open Source Project
+# *
+# * Licensed under the Apache License, Version 2.0 (the "License");
+# * you may not use this file except in compliance with the License.
+# * You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing, software
+# * distributed under the License is distributed on an "AS IS" BASIS,
+# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# * See the License for the specific language governing permissions and
+# * limitations under the License.
+# */
+
+.class public LHardFail;
+
+.super Ljava/lang/Object;
+
+.method public static foo()V
+ .locals 1
+ # smali requires at least one instruction
+ new-instance v0, Ljava/lang/Object;
+ # No return on purprose to hard fail the class and crash the compiler.
+.end method
diff --git a/test/842-vdex-hard-failure/src/Main.java b/test/842-vdex-hard-failure/src/Main.java
new file mode 100644
index 0000000..c6f1f68
--- /dev/null
+++ b/test/842-vdex-hard-failure/src/Main.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 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 ClassNotFoundException {
+ try {
+ Class.forName("HardFail");
+ throw new Error("Expected VerifyError");
+ } catch (VerifyError e) {
+ // expected
+ }
+ }
+}
diff --git a/test/843-default-interface/Android.bp b/test/843-default-interface/Android.bp
new file mode 100644
index 0000000..ff942e6
--- /dev/null
+++ b/test/843-default-interface/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `843-default-interface`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-843-default-interface-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-843-default-interface",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-843-default-interface-src"
+ ],
+ data: [
+ ":art-run-test-843-default-interface-expected-stdout",
+ ":art-run-test-843-default-interface-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-843-default-interface-expected-stdout",
+ out: ["art-run-test-843-default-interface-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-843-default-interface-expected-stderr",
+ out: ["art-run-test-843-default-interface-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/843-default-interface/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/843-default-interface/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/843-default-interface/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/843-default-interface/expected-stdout.txt
diff --git a/test/843-default-interface/info.txt b/test/843-default-interface/info.txt
new file mode 100644
index 0000000..3586e7f
--- /dev/null
+++ b/test/843-default-interface/info.txt
@@ -0,0 +1,2 @@
+Regression test for ArtMethod::CopyFrom, which used to wrongly override the
+imt_index_ of abstract methods with 0.
diff --git a/test/843-default-interface/src/Impl.java b/test/843-default-interface/src/Impl.java
new file mode 100644
index 0000000..8c9d76c
--- /dev/null
+++ b/test/843-default-interface/src/Impl.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 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 Impl implements SubItf {
+ public String foo() {
+ return "Impl";
+ }
+}
diff --git a/test/843-default-interface/src/Itf.java b/test/843-default-interface/src/Itf.java
new file mode 100644
index 0000000..0625429
--- /dev/null
+++ b/test/843-default-interface/src/Itf.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public interface Itf {
+ public default String bar() {
+ return "Itf";
+ }
+}
diff --git a/test/843-default-interface/src/Main.java b/test/843-default-interface/src/Main.java
new file mode 100644
index 0000000..8b2b2da
--- /dev/null
+++ b/test/843-default-interface/src/Main.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 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 {
+ static SubItf itf = new Impl();
+ public static void main(String[] args) throws Exception {
+ // Loop enough to trigger the native OOME.
+ for (int i = 0; i < 50000; ++i) {
+ // Because the imt index was overwritten to 0, this call ended up
+ // in the conflict trampoline which wrongly updated the 0th entry
+ // of the imt table. This lead to this call always calling the
+ // conflict trampoline.
+ itf.foo();
+ }
+ }
+}
diff --git a/test/843-default-interface/src/OtherItf.java b/test/843-default-interface/src/OtherItf.java
new file mode 100644
index 0000000..368631c
--- /dev/null
+++ b/test/843-default-interface/src/OtherItf.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public interface OtherItf {
+ // javac will complain when compiling SubItf if both superinterfaces Itf and OtherItf
+ // define a default method bar(), so we do not define bar() here.
+ // What will be loaded at runtime will actually be src2/OtherItf.
+ // public default String bar() {
+ // return "OtherItf";
+ // }
+}
diff --git a/test/843-default-interface/src/SubItf.java b/test/843-default-interface/src/SubItf.java
new file mode 100644
index 0000000..6983b18
--- /dev/null
+++ b/test/843-default-interface/src/SubItf.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+// The method bar will be a default conflict for this class. That used to make
+// the class linker re-allocate its ArtMethod array, and calling CopyFrom.
+// The bug was that CopyFrom was overwriting the imt index of interface methods,
+// and for this example `foo`.
+public interface SubItf extends Itf, OtherItf {
+ public String foo();
+}
diff --git a/test/843-default-interface/src2/OtherItf.java b/test/843-default-interface/src2/OtherItf.java
new file mode 100644
index 0000000..08028f4
--- /dev/null
+++ b/test/843-default-interface/src2/OtherItf.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public interface OtherItf {
+ public default String bar() {
+ return "OtherItf";
+ }
+}
diff --git a/test/844-exception/Android.bp b/test/844-exception/Android.bp
new file mode 100644
index 0000000..92ecef4
--- /dev/null
+++ b/test/844-exception/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `844-exception`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-844-exception",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-844-exception-expected-stdout",
+ ":art-run-test-844-exception-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-844-exception-expected-stdout",
+ out: ["art-run-test-844-exception-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-844-exception-expected-stderr",
+ out: ["art-run-test-844-exception-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/844-exception/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/844-exception/expected-stderr.txt
diff --git a/test/844-exception/expected-stdout.txt b/test/844-exception/expected-stdout.txt
new file mode 100644
index 0000000..7ddf8e3
--- /dev/null
+++ b/test/844-exception/expected-stdout.txt
@@ -0,0 +1,2 @@
+JNI_OnLoad called
+Caught exception
diff --git a/test/844-exception/info.txt b/test/844-exception/info.txt
new file mode 100644
index 0000000..a825fbb
--- /dev/null
+++ b/test/844-exception/info.txt
@@ -0,0 +1,2 @@
+Regression test for Thread::QuickDeliverException which used to expect a quick
+frame to be the caller.
diff --git a/test/844-exception/src/Main.java b/test/844-exception/src/Main.java
new file mode 100644
index 0000000..6237844
--- /dev/null
+++ b/test/844-exception/src/Main.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 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 {
+ static Main empty;
+
+ static class MyThread extends Thread {
+ public void run() {
+ // This will throw at `callMethodThatThrows` and trigger deoptimization checks which we used
+ // to crash on.
+ new Inner();
+ }
+ }
+
+ public static class Inner {
+ // Have a <clinit> method invoke another <clinit> method to ensure we execute in the
+ // interpreter.
+ static {
+ new Inner2();
+ }
+ }
+
+ public static class Inner2 {
+ static {
+ Main.callMethodThatThrows();
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ // Disables use of nterp.
+ Main.setAsyncExceptionsThrown();
+
+ // Execute the test in a different thread, to ensure we still
+ // return a 0 exit status.
+ Thread t = new MyThread();
+ t.setUncaughtExceptionHandler((th, e) -> {
+ System.out.println("Caught exception");
+ });
+ t.start();
+ t.join();
+ }
+
+ public static void callMethodThatThrows() {
+ // Ensures we get deoptimization requests.
+ Main.forceInterpreterOnThread();
+ throw new Error("");
+ }
+
+ public static native void forceInterpreterOnThread();
+ public static native void setAsyncExceptionsThrown();
+
+}
diff --git a/test/844-exception2/Android.bp b/test/844-exception2/Android.bp
new file mode 100644
index 0000000..50568b1
--- /dev/null
+++ b/test/844-exception2/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `844-exception2`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-844-exception2",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-844-exception2-expected-stdout",
+ ":art-run-test-844-exception2-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-844-exception2-expected-stdout",
+ out: ["art-run-test-844-exception2-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-844-exception2-expected-stderr",
+ out: ["art-run-test-844-exception2-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/844-exception2/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/844-exception2/expected-stderr.txt
diff --git a/test/844-exception2/expected-stdout.txt b/test/844-exception2/expected-stdout.txt
new file mode 100644
index 0000000..7ddf8e3
--- /dev/null
+++ b/test/844-exception2/expected-stdout.txt
@@ -0,0 +1,2 @@
+JNI_OnLoad called
+Caught exception
diff --git a/test/844-exception2/info.txt b/test/844-exception2/info.txt
new file mode 100644
index 0000000..641dc72
--- /dev/null
+++ b/test/844-exception2/info.txt
@@ -0,0 +1,2 @@
+Regression test for Thread::DeoptimizeWithDeoptimizationException which always
+expected to have a shadow frame to execute.
diff --git a/test/844-exception2/src/Main.java b/test/844-exception2/src/Main.java
new file mode 100644
index 0000000..400b337
--- /dev/null
+++ b/test/844-exception2/src/Main.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 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 class Inner {
+ // Use a <clinit> method to ensure we execute in the
+ // interpreter.
+ static {
+ Main.callMethodThatThrows();
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+ // Disables use of nterp.
+ Main.setAsyncExceptionsThrown();
+
+ Thread.currentThread().setUncaughtExceptionHandler((th, e) -> {
+ System.out.println("Caught exception");
+ // Exit the test gracefully.
+ System.exit(0);
+ });
+ // This will throw at `callMethodThatThrows` and trigger deoptimization checks which we used
+ // to crash on.
+ new Inner();
+ }
+
+ public static void callMethodThatThrows() {
+ // Ensures we get deoptimization requests.
+ Main.forceInterpreterOnThread();
+ throw new Error("");
+ }
+
+ public static native void forceInterpreterOnThread();
+ public static native void setAsyncExceptionsThrown();
+
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/845-data-image/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/845-data-image/expected-stderr.txt
diff --git a/test/845-data-image/expected-stdout.txt b/test/845-data-image/expected-stdout.txt
new file mode 100644
index 0000000..8db7853
--- /dev/null
+++ b/test/845-data-image/expected-stdout.txt
@@ -0,0 +1,2 @@
+JNI_OnLoad called
+JNI_OnLoad called
diff --git a/test/845-data-image/info.txt b/test/845-data-image/info.txt
new file mode 100644
index 0000000..dfcd0dd
--- /dev/null
+++ b/test/845-data-image/info.txt
@@ -0,0 +1 @@
+Test the generation of app image at runtime.
diff --git a/test/845-data-image/run.py b/test/845-data-image/run.py
new file mode 100644
index 0000000..37c990f
--- /dev/null
+++ b/test/845-data-image/run.py
@@ -0,0 +1,24 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import sys
+
+# We run the tests by disabling compilation with app image and forcing
+# relocation for better testing.
+# Run the test twice: one run for generating the image, a second run for using
+# the image.
+def run(ctx, args):
+ ctx.default_run(args, app_image=False, relocate=True)
+ # Pass another argument to let the test know it should now expect an image.
+ ctx.default_run(args, app_image=False, relocate=True, test_args=["--second-run"])
diff --git a/test/845-data-image/src-art/Main.java b/test/845-data-image/src-art/Main.java
new file mode 100644
index 0000000..c8d3e62
--- /dev/null
+++ b/test/845-data-image/src-art/Main.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import dalvik.system.DexFile;
+import dalvik.system.VMRuntime;
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.CyclicBarrier;
+
+public class Main {
+
+ static String myString = "MyString";
+
+ static class MyThread extends Thread {
+ CyclicBarrier barrier;
+
+ public MyThread(CyclicBarrier barrier) {
+ this.barrier = barrier;
+ }
+ public void run() {
+ try {
+ synchronized (Main.myString) {
+ barrier.await();
+ barrier.reset();
+ // Infinite wait.
+ barrier.await();
+ }
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+ }
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+
+ // Register the dex file so that the runtime can pick up which
+ // dex file to compile for the image.
+ File file = null;
+ try {
+ file = createTempFile();
+ String codePath = DEX_LOCATION + "/845-data-image.jar";
+ VMRuntime.registerAppInfo(
+ "test.app",
+ file.getPath(),
+ file.getPath(),
+ new String[] {codePath},
+ VMRuntime.CODE_PATH_TYPE_PRIMARY_APK);
+ } finally {
+ if (file != null) {
+ file.delete();
+ }
+ }
+
+ if (!hasOatFile() || !hasImage()) {
+ // We only generate an app image if there is at least a vdex file and a boot image.
+ return;
+ }
+
+ if (args.length == 2 && "--second-run".equals(args[1])) {
+ DexFile.OptimizationInfo info = VMRuntime.getBaseApkOptimizationInfo();
+ if (!info.isOptimized()) {
+ throw new Error("Expected image to be loaded");
+ }
+ }
+
+ // Test that we emit an empty lock word. If we are not, then this synchronized call here would
+ // block on a run with the runtime image.
+ synchronized (myString) {
+ }
+
+ // Create a thread that makes sure `myString` is locked while the main thread is generating
+ // the runtime image.
+ CyclicBarrier barrier = new CyclicBarrier(2);
+ Thread t = new MyThread(barrier);
+ t.setDaemon(true);
+ t.start();
+ barrier.await();
+
+ VMRuntime runtime = VMRuntime.getRuntime();
+ runtime.notifyStartupCompleted();
+
+ String filter = getCompilerFilter(Main.class);
+ if ("speed-profile".equals(filter) || "speed".equals(filter)) {
+ // We only generate an app image for filters that don't compile.
+ return;
+ }
+
+ // Wait for the file to be generated.
+ File image = new File(
+ DEX_LOCATION + "/845-data-image.art" + (runtime.is64Bit() ? "64" : "32"));
+ while (!image.exists()) {
+ Thread.yield();
+ }
+ }
+
+ private static native boolean hasOatFile();
+ private static native boolean hasImage();
+ private static native String getCompilerFilter(Class<?> cls);
+
+ private static final String TEMP_FILE_NAME_PREFIX = "temp";
+ private static final String TEMP_FILE_NAME_SUFFIX = "-file";
+ private static final String DEX_LOCATION = System.getenv("DEX_LOCATION");
+
+ private static File createTempFile() throws Exception {
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e) {
+ System.setProperty("java.io.tmpdir", "/data/local/tmp");
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e2) {
+ System.setProperty("java.io.tmpdir", "/sdcard");
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ }
+ }
+ }
+}
diff --git a/test/845-fast-verify/845-fast-verify.jar b/test/845-fast-verify/845-fast-verify.jar
new file mode 100644
index 0000000..d94777a
--- /dev/null
+++ b/test/845-fast-verify/845-fast-verify.jar
Binary files differ
diff --git a/test/845-fast-verify/build.py b/test/845-fast-verify/build.py
new file mode 100644
index 0000000..f304d95
--- /dev/null
+++ b/test/845-fast-verify/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ pass # Nothing to do.
diff --git a/test/845-fast-verify/classes.dm b/test/845-fast-verify/classes.dm
new file mode 100644
index 0000000..c2c8559
--- /dev/null
+++ b/test/845-fast-verify/classes.dm
Binary files differ
diff --git a/test/089-many-methods/expected-stderr.txt b/test/845-fast-verify/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/845-fast-verify/expected-stderr.txt
diff --git a/test/089-many-methods/expected-stdout.txt b/test/845-fast-verify/expected-stdout.txt
similarity index 100%
copy from test/089-many-methods/expected-stdout.txt
copy to test/845-fast-verify/expected-stdout.txt
diff --git a/test/845-fast-verify/info.txt b/test/845-fast-verify/info.txt
new file mode 100644
index 0000000..678ec3f
--- /dev/null
+++ b/test/845-fast-verify/info.txt
@@ -0,0 +1,46 @@
+Regression test for the combination of dex2oat using:
+- jar with multidex
+- vdex file where one dex file fails to fast verify (for example because of a
+ boot classpath change)
+- dex files being compiled individually
+
+We used to crash in CompilerDriver::FastVerify, assuming that only FastVerify
+can update the compiled_classes_ map. However, this isn't the case if one of the
+dex file ended up needing full verification.
+
+We need prebuilts of the .jar and .dm file as we rely on the bootclasspath to
+change which isn't expressable in a run-test. So we locally modified
+android.system.Int32Ref to inherit java.util.HashMap.
+
+The code that was used to generate the prebuilts is as follows:
+
+
+file Main.java in classes.dex:
+
+import java.util.HashMap;
+import android.system.Int32Ref;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ try {
+ FailVerification.foo();
+ throw new Exception("Expected error");
+ } catch (Error expected) {
+ }
+ }
+}
+
+class FailVerification extends Foo {
+
+ public static void foo() {
+ Int32Ref ref = new Int32Ref(42);
+ takeHashMap(ref);
+ }
+
+ public static void takeHashMap(HashMap m) {}
+}
+
+file Foo.java in classes2.dex:
+
+public class Foo {
+}
diff --git a/test/845-fast-verify/run.py b/test/845-fast-verify/run.py
new file mode 100644
index 0000000..f27ec80
--- /dev/null
+++ b/test/845-fast-verify/run.py
@@ -0,0 +1,25 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(
+ args,
+ # Disable app image to make sure we compile dex files individually.
+ app_image=False,
+ # Pass a .dm file to run FastVerify and ask to compile dex files
+ # individually in order to run the problematic code.
+ Xcompiler_option=[f"--dm-file={ctx.env.DEX_LOCATION}/classes.dm", "--compile-individually"])
diff --git a/test/089-many-methods/expected-stderr.txt b/test/846-multidex-data-image/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/846-multidex-data-image/expected-stderr.txt
diff --git a/test/846-multidex-data-image/expected-stdout.txt b/test/846-multidex-data-image/expected-stdout.txt
new file mode 100644
index 0000000..8db7853
--- /dev/null
+++ b/test/846-multidex-data-image/expected-stdout.txt
@@ -0,0 +1,2 @@
+JNI_OnLoad called
+JNI_OnLoad called
diff --git a/test/846-multidex-data-image/info.txt b/test/846-multidex-data-image/info.txt
new file mode 100644
index 0000000..3b83d2c
--- /dev/null
+++ b/test/846-multidex-data-image/info.txt
@@ -0,0 +1,2 @@
+Test the generation of app image at runtime, when the primary APK contains
+multiple dex files.
diff --git a/test/846-multidex-data-image/run.py b/test/846-multidex-data-image/run.py
new file mode 100644
index 0000000..3976af2
--- /dev/null
+++ b/test/846-multidex-data-image/run.py
@@ -0,0 +1,23 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import sys
+
+# We run the tests by disabling compilation with app image.
+# Run the test twice: one run for generating the image, a second run for using
+# the image.
+def run(ctx, args):
+ ctx.default_run(args, app_image=False)
+ # Pass another argument to let the test know it should now expect an image.
+ ctx.default_run(args, app_image=False, test_args=["--second-run"])
diff --git a/test/846-multidex-data-image/src-art/Main.java b/test/846-multidex-data-image/src-art/Main.java
new file mode 100644
index 0000000..1169c53
--- /dev/null
+++ b/test/846-multidex-data-image/src-art/Main.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import dalvik.system.DexFile;
+import dalvik.system.VMRuntime;
+import java.io.File;
+import java.io.IOException;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+
+ // Register the dex file so that the runtime can pick up which
+ // dex file to compile for the image.
+ File file = null;
+ try {
+ file = createTempFile();
+ String codePath = DEX_LOCATION + "/846-multidex-data-image.jar";
+ VMRuntime.registerAppInfo(
+ "test.app",
+ file.getPath(),
+ file.getPath(),
+ new String[] {codePath},
+ VMRuntime.CODE_PATH_TYPE_PRIMARY_APK);
+ } finally {
+ if (file != null) {
+ file.delete();
+ }
+ }
+
+ if (!hasOatFile() || !hasImage()) {
+ // We only generate an app image if there is at least a vdex file and a boot image.
+ return;
+ }
+
+ if (args.length == 2 && "--second-run".equals(args[1])) {
+ DexFile.OptimizationInfo info = VMRuntime.getBaseApkOptimizationInfo();
+ if (!info.isOptimized()) {
+ throw new Error("Expected image to be loaded");
+ }
+ }
+
+ VMRuntime runtime = VMRuntime.getRuntime();
+ runtime.notifyStartupCompleted();
+
+ String filter = getCompilerFilter(Main.class);
+ if ("speed-profile".equals(filter) || "speed".equals(filter)) {
+ // We only generate an app image for filters that don't compile.
+ return;
+ }
+
+ // Wait for the file to be generated.
+ File image = new File(
+ DEX_LOCATION + "/846-multidex-data-image.art" + (runtime.is64Bit() ? "64" : "32"));
+ while (!image.exists()) {
+ Thread.yield();
+ }
+
+ // Test that we can load a class from the other dex file. We do this after creating the image to
+ // check that the runtime can deal with a missing dex cache.
+ Class.forName("Foo");
+ }
+
+ private static native boolean hasOatFile();
+ private static native boolean hasImage();
+ private static native String getCompilerFilter(Class<?> cls);
+
+ private static final String TEMP_FILE_NAME_PREFIX = "temp";
+ private static final String TEMP_FILE_NAME_SUFFIX = "-file";
+ private static final String DEX_LOCATION = System.getenv("DEX_LOCATION");
+
+ private static File createTempFile() throws Exception {
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e) {
+ System.setProperty("java.io.tmpdir", "/data/local/tmp");
+ try {
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ } catch (IOException e2) {
+ System.setProperty("java.io.tmpdir", "/sdcard");
+ return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
+ }
+ }
+ }
+}
diff --git a/test/846-multidex-data-image/src-multidex/Foo.java b/test/846-multidex-data-image/src-multidex/Foo.java
new file mode 100644
index 0000000..4e36e88
--- /dev/null
+++ b/test/846-multidex-data-image/src-multidex/Foo.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2022 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 Foo {
+}
diff --git a/test/900-hello-plugin/run b/test/900-hello-plugin/run
deleted file mode 100755
index a19a38c..0000000
--- a/test/900-hello-plugin/run
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/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.
-
-plugin=libartagentd.so
-if [[ "$@" == *"-O"* ]]; then
- plugin=libartagent.so
-fi
-
-# Adjust the agent path when running on device.
-if [[ "$@" != *"--host"* ]]; then
- if [[ -z "$ANDROID_BUILD_TOP" ]]; then
- echo 'ANDROID_BUILD_TOP environment variable is empty; did you forget to run `lunch`?'
- exit 1
- fi
-
- bitness_flag=--32
- if [[ "$@" == *"--64"* ]]; then
- bitness_flag=--64
- fi
-
- # Path to native libraries installed on the device for testing purposes.
- test_native_lib_path=$("$ANDROID_BUILD_TOP/art/test/utils/get-device-test-native-lib-path" \
- "$bitness_flag")
-
- # The linker configuration used for dalvikvm(64) in the ART APEX requires us
- # to pass the full path to the agent to the runtime when running on device.
- plugin=${test_native_lib_path}/${plugin}
-fi
-
-./default-run "$@" --runtime-option -agentpath:${plugin}=test_900 \
- --runtime-option -agentpath:${plugin}=test_900_round_2 \
- --android-runtime-option -Xplugin:${plugin}
diff --git a/test/900-hello-plugin/run.py b/test/900-hello-plugin/run.py
new file mode 100644
index 0000000..607a0e5
--- /dev/null
+++ b/test/900-hello-plugin/run.py
@@ -0,0 +1,39 @@
+#!/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.
+
+
+def run(ctx, args):
+ plugin = "libartagent.so" if args.O else "libartagentd.so"
+
+ # Adjust the agent path when running on device.
+ if not args.host:
+ for i, opt in enumerate(args.runtime_option):
+ if opt.startswith("-Djava.library.path="):
+ libpath = opt.split("=")[-1]
+ assert libpath.startswith("/data/nativetest"), libpath
+
+ # The linker configuration used for dalvikvm(64) in the ART APEX requires us
+ # to pass the full path to the agent to the runtime when running on device.
+ plugin = f"{libpath}/{plugin}"
+ break
+
+ ctx.default_run(
+ args,
+ runtime_option=[
+ f"-agentpath:{plugin}=test_900",
+ f"-agentpath:{plugin}=test_900_round_2"
+ ],
+ android_runtime_option=[f"-Xplugin:{plugin}"])
diff --git a/test/901-hello-ti-agent/run b/test/901-hello-ti-agent/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/901-hello-ti-agent/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/901-hello-ti-agent/run.py b/test/901-hello-ti-agent/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/901-hello-ti-agent/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/902-hello-transformation/run b/test/902-hello-transformation/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/902-hello-transformation/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/902-hello-transformation/run.py b/test/902-hello-transformation/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/902-hello-transformation/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/903-hello-tagging/run b/test/903-hello-tagging/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/903-hello-tagging/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/903-hello-tagging/run.py b/test/903-hello-tagging/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/903-hello-tagging/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/904-object-allocation/run b/test/904-object-allocation/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/904-object-allocation/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/904-object-allocation/run.py b/test/904-object-allocation/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/904-object-allocation/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/905-object-free/expected-stdout.txt b/test/905-object-free/expected-stdout.txt
index dfcd7b6..50f11e0 100644
--- a/test/905-object-free/expected-stdout.txt
+++ b/test/905-object-free/expected-stdout.txt
@@ -10,4 +10,4 @@
---
[]
---
-Free counts 200000 200000
+Free counts as expected
diff --git a/test/905-object-free/run b/test/905-object-free/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/905-object-free/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/905-object-free/run.py b/test/905-object-free/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/905-object-free/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/905-object-free/src/art/Test905.java b/test/905-object-free/src/art/Test905.java
index 367da99..efd83c6 100644
--- a/test/905-object-free/src/art/Test905.java
+++ b/test/905-object-free/src/art/Test905.java
@@ -23,6 +23,8 @@
import java.util.function.BiConsumer;
public class Test905 {
+ private static final boolean DALVIK_RUN = "Dalvik".equals(System.getProperty("java.vm.name"));
+
// Taken from jdwp tests.
public static class MarkerObj {
public static int cnt = 0;
@@ -81,7 +83,13 @@
run(l);
enableFreeTracking(true);
- stress();
+ if (DALVIK_RUN) {
+ stress(400000);
+ } else {
+ // For JVM the JVMTI tag handling is not running as expected for the stress test
+ // (b/252990223).
+ stress(10000);
+ }
}
private static void run(ArrayList<Object> l) {
@@ -111,6 +119,8 @@
System.out.println("---");
}
+ private static int errors = 0;
+
private static void stressAllocate(int i, BiConsumer<Integer, Object> saver) {
Object obj = new Object();
Main.setTag(obj, i);
@@ -118,10 +128,10 @@
saver.accept(i, obj);
}
- private static void stress() {
+ private static void stress(int allocations) {
getCollectedTags(0);
getCollectedTags(1);
- final int num_obj = 400000;
+ final int num_obj = allocations;
final Object[] saved = new Object[num_obj/2];
// Allocate objects, Save every other one. We want to be sure that it's only the deleted objects
// that get their tags cleared and non-deleted objects correctly keep track of their tags.
@@ -139,10 +149,15 @@
Arrays.sort(freedTags1);
Arrays.sort(freedTags2);
// Make sure we freed all the ones we expect to and both envs agree on this.
- System.out.println("Free counts " + freedTags1.length + " " + freedTags2.length);
+ if (freedTags1.length == num_obj / 2 && freedTags2.length == num_obj / 2) {
+ System.out.println("Free counts as expected");
+ } else {
+ System.out.println("Free counts " + freedTags1.length + " " + freedTags2.length);
+ }
for (int i = 0; i < freedTags1.length; ++i) {
if (freedTags1[i] + 1 != freedTags2[i]) {
System.out.println("Mismatched tags " + (freedTags1[i] + 1) + " " + freedTags2[i]);
+ break;
}
}
// Make sure the saved-tags aren't present.
diff --git a/test/906-iterate-heap/iterate_heap.cc b/test/906-iterate-heap/iterate_heap.cc
index 521f9a6..f0a6624 100644
--- a/test/906-iterate-heap/iterate_heap.cc
+++ b/test/906-iterate-heap/iterate_heap.cc
@@ -198,7 +198,7 @@
void* user_data) {
FindStringCallbacks* p = reinterpret_cast<FindStringCallbacks*>(user_data);
if (*tag_ptr == p->tag_to_find) {
- size_t utf_byte_count = ti::CountUtf8Bytes(value, value_length);
+ size_t utf_byte_count = ti::CountModifiedUtf8BytesInUtf16(value, value_length);
std::unique_ptr<char[]> mod_utf(new char[utf_byte_count + 1]);
memset(mod_utf.get(), 0, utf_byte_count + 1);
ti::ConvertUtf16ToModifiedUtf8(mod_utf.get(), utf_byte_count, value, value_length);
diff --git a/test/906-iterate-heap/run b/test/906-iterate-heap/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/906-iterate-heap/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/906-iterate-heap/run.py b/test/906-iterate-heap/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/906-iterate-heap/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/907-get-loaded-classes/run b/test/907-get-loaded-classes/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/907-get-loaded-classes/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/907-get-loaded-classes/run.py b/test/907-get-loaded-classes/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/907-get-loaded-classes/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/908-gc-start-finish/run b/test/908-gc-start-finish/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/908-gc-start-finish/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/908-gc-start-finish/run.py b/test/908-gc-start-finish/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/908-gc-start-finish/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/909-attach-agent/expected-stdout.interpreter.txt b/test/909-attach-agent/expected-stdout.interpreter.txt
new file mode 100644
index 0000000..fef7cc9
--- /dev/null
+++ b/test/909-attach-agent/expected-stdout.interpreter.txt
@@ -0,0 +1,26 @@
+JNI_OnLoad called
+Hello, world!
+Attached Agent for test 909-attach-agent
+Created env for kArtTiVersion
+Attached Agent for test 909-attach-agent
+Created env for kArtTiVersion
+Goodbye!
+JNI_OnLoad called
+Hello, world!
+Attached Agent for test 909-attach-agent
+Created env for kArtTiVersion
+Attached Agent for test 909-attach-agent
+Created env for kArtTiVersion
+Goodbye!
+JNI_OnLoad called
+Hello, world!
+Attached Agent for test 909-attach-agent
+Created env for kArtTiVersion
+Attached Agent for test 909-attach-agent
+Created env for kArtTiVersion
+Goodbye!
+JNI_OnLoad called
+Hello, world!
+Can't attach agent, process is not debuggable.
+Can't attach agent, process is not debuggable.
+Goodbye!
diff --git a/test/909-attach-agent/interpreter-expected.patch b/test/909-attach-agent/interpreter-expected.patch
deleted file mode 100644
index 5035c6a..0000000
--- a/test/909-attach-agent/interpreter-expected.patch
+++ /dev/null
@@ -1,4 +0,0 @@
-19d18
-< version 0x30010000 is not valid!Unable to create env for JVMTI_VERSION_1_0
-22d20
-< version 0x30010000 is not valid!Unable to create env for JVMTI_VERSION_1_0
diff --git a/test/909-attach-agent/run b/test/909-attach-agent/run
deleted file mode 100755
index 71b1e1c..0000000
--- a/test/909-attach-agent/run
+++ /dev/null
@@ -1,88 +0,0 @@
-#!/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.
-
-plugin=libopenjdkjvmtid.so
-agent=libtiagentd.so
-if [[ "$@" == *"-O"* ]]; then
- agent=libtiagent.so
- plugin=libopenjdkjvmti.so
-fi
-
-if [[ "$@" == *"--interpreter"* ]]; then
- # On interpreter we are fully capable of providing the full jvmti api so we
- # have a slightly different expected output.
- # TODO We should really be changing this in the 'check' script.
- patch -s expected-stdout.txt <interpreter-expected.patch
-fi
-
-# Provide additional runtime options when running on device.
-extra_runtime_options=
-if [[ "$@" != *"--host"* ]]; then
- if [[ -z "$ANDROID_BUILD_TOP" ]]; then
- echo 'ANDROID_BUILD_TOP environment variable is empty; did you forget to run `lunch`?'
- exit 1
- fi
-
- bitness_flag=--32
- if [[ "$@" == *"--64"* ]]; then
- bitness_flag=--64
- fi
-
- # Path to native libraries installed on the device for testing purposes.
- test_native_lib_path=$("$ANDROID_BUILD_TOP/art/test/utils/get-device-test-native-lib-path" \
- "$bitness_flag")
-
- # The linker configuration used for dalvikvm(64) in the ART APEX requires us
- # to pass the full path to the agent to the runtime when running on device.
- agent=${test_native_lib_path}/${agent}
-
- # The above agent path is an absolute one; append the root directory to the
- # library path so that the agent can be found via the `java.library.path`
- # system property (see method `Main.find` in
- # test/909-attach-agent/src-art/Main.java).
- extra_runtime_options="--runtime-option -Djava.library.path=${test_native_lib_path}:/"
-fi
-
-export ANDROID_LOG_TAGS='*:f'
-./default-run "$@" --android-runtime-option -Xplugin:${plugin} \
- --android-runtime-option -Xcompiler-option \
- --android-runtime-option --debuggable \
- $extra_runtime_options \
- --args agent:${agent}=909-attach-agent
-return_status1=$?
-
-./default-run "$@" --android-runtime-option -Xcompiler-option \
- --android-runtime-option --debuggable \
- $extra_runtime_options \
- --args agent:${agent}=909-attach-agent
-return_status2=$?
-
-./default-run "$@" $extra_runtime_options \
- --args agent:${agent}=909-attach-agent \
- --external-log-tags
-return_status3=$?
-
-./default-run "$@" $extra_runtime_options \
- --args agent:${agent}=909-attach-agent \
- --args disallow-debugging \
- --external-log-tags
-return_status4=$?
-
-# Make sure we don't silently ignore an early failure.
-(exit $return_status1) && \
- (exit $return_status2) && \
- (exit $return_status3) && \
- (exit $return_status4)
diff --git a/test/909-attach-agent/run.py b/test/909-attach-agent/run.py
new file mode 100644
index 0000000..b5e2337
--- /dev/null
+++ b/test/909-attach-agent/run.py
@@ -0,0 +1,62 @@
+#!/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.
+
+
+def run(ctx, args):
+ agent = "libtiagent.so" if args.O else "libtiagentd.so"
+ plugin = "libopenjdkjvmti.so" if args.O else "libopenjdkjvmtid.so"
+
+ if args.interpreter:
+ # On interpreter we are fully capable of providing the full jvmti api so we
+ # have a slightly different expected output.
+ ctx.expected_stdout = ctx.expected_stdout.with_suffix(".interpreter.txt")
+
+ # Provide additional runtime options when running on device.
+ if not args.host:
+ for i, opt in enumerate(args.runtime_option):
+ if opt.startswith("-Djava.library.path="):
+ libpath = opt.split("=")[-1]
+ assert libpath.startswith("/data/nativetest"), libpath
+
+ # The linker configuration used for dalvikvm(64) in the ART APEX requires us
+ # to pass the full path to the agent to the runtime when running on device.
+ agent = f"{libpath}/{agent}"
+
+ # The above agent path is an absolute one; append the root directory to the
+ # library path so that the agent can be found via the `java.library.path`
+ # system property (see method `Main.find` in
+ # test/909-attach-agent/src-art/Main.java).
+ args.runtime_option[i] += ":/"
+ break
+
+ ctx.default_run(
+ args,
+ android_runtime_option=[
+ f"-Xplugin:{plugin}", "-Xcompiler-option", "--debuggable"
+ ],
+ test_args=[f"agent:{agent}=909-attach-agent"])
+
+ ctx.default_run(args, test_args=[f"agent:{agent}=909-attach-agent"])
+
+ ctx.default_run(
+ args,
+ test_args=[f"agent:{agent}=909-attach-agent"],
+ android_log_tags="*:f")
+
+ ctx.default_run(
+ args,
+ test_args=[f"agent:{agent}=909-attach-agent", "disallow-debugging"],
+ android_log_tags="*:f")
diff --git a/test/910-methods/run b/test/910-methods/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/910-methods/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/910-methods/run.py b/test/910-methods/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/910-methods/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/911-get-stack-trace/expected-cts-version.txt b/test/911-get-stack-trace/expected-cts-version.txt
index 728ea88..25f29aa 100644
--- a/test/911-get-stack-trace/expected-cts-version.txt
+++ b/test/911-get-stack-trace/expected-cts-version.txt
@@ -79,8 +79,8 @@
From top
---------
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -99,8 +99,8 @@
foo (IIILart/ControlData;)I 0 21
run ()V 4 28
---------
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -120,14 +120,14 @@
run ()V 4 28
---------
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
foo (IIILart/ControlData;)I 0 21
---------
- wait ()V 2 568
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -135,7 +135,7 @@
baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
bar (IIILart/ControlData;)J 0 26
---------
- wait ()V 2 568
+ wait ()V 2 524
From bottom
---------
run ()V 4 28
@@ -251,8 +251,8 @@
---------
ThreadListTraces Thread 0
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -261,8 +261,8 @@
---------
ThreadListTraces Thread 2
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -271,8 +271,8 @@
---------
ThreadListTraces Thread 4
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -281,8 +281,8 @@
---------
ThreadListTraces Thread 6
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -291,8 +291,8 @@
---------
ThreadListTraces Thread 8
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -308,8 +308,8 @@
---------
ThreadListTraces Thread 0
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -331,8 +331,8 @@
---------
ThreadListTraces Thread 2
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -354,8 +354,8 @@
---------
ThreadListTraces Thread 4
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -377,8 +377,8 @@
---------
ThreadListTraces Thread 6
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -400,8 +400,8 @@
---------
ThreadListTraces Thread 8
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
diff --git a/test/911-get-stack-trace/expected-stdout.txt b/test/911-get-stack-trace/expected-stdout.txt
index 19735d2..1109b3f 100644
--- a/test/911-get-stack-trace/expected-stdout.txt
+++ b/test/911-get-stack-trace/expected-stdout.txt
@@ -79,8 +79,8 @@
From top
---------
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -99,8 +99,8 @@
foo (IIILart/ControlData;)I 0 21
run ()V 4 28
---------
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -120,14 +120,14 @@
run ()V 4 28
---------
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
foo (IIILart/ControlData;)I 0 21
---------
- wait ()V 2 568
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -135,7 +135,7 @@
baz (IIILart/ControlData;)Ljava/lang/Object; 8 34
bar (IIILart/ControlData;)J 0 26
---------
- wait ()V 2 568
+ wait ()V 2 524
From bottom
---------
run ()V 4 28
@@ -276,8 +276,8 @@
---------
AllTraces Thread 0
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -286,8 +286,8 @@
---------
AllTraces Thread 1
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -296,8 +296,8 @@
---------
AllTraces Thread 2
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -306,8 +306,8 @@
---------
AllTraces Thread 3
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -316,8 +316,8 @@
---------
AllTraces Thread 4
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -326,8 +326,8 @@
---------
AllTraces Thread 5
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -336,8 +336,8 @@
---------
AllTraces Thread 6
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -346,8 +346,8 @@
---------
AllTraces Thread 7
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -356,8 +356,8 @@
---------
AllTraces Thread 8
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -366,8 +366,8 @@
---------
AllTraces Thread 9
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -401,8 +401,8 @@
---------
AllTraces Thread 0
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -424,8 +424,8 @@
---------
AllTraces Thread 1
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -447,8 +447,8 @@
---------
AllTraces Thread 2
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -470,8 +470,8 @@
---------
AllTraces Thread 3
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -493,8 +493,8 @@
---------
AllTraces Thread 4
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -516,8 +516,8 @@
---------
AllTraces Thread 5
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -539,8 +539,8 @@
---------
AllTraces Thread 6
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -562,8 +562,8 @@
---------
AllTraces Thread 7
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -585,8 +585,8 @@
---------
AllTraces Thread 8
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -608,8 +608,8 @@
---------
AllTraces Thread 9
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -685,8 +685,8 @@
---------
ThreadListTraces Thread 0
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -695,8 +695,8 @@
---------
ThreadListTraces Thread 2
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -705,8 +705,8 @@
---------
ThreadListTraces Thread 4
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -715,8 +715,8 @@
---------
ThreadListTraces Thread 6
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -725,8 +725,8 @@
---------
ThreadListTraces Thread 8
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -742,8 +742,8 @@
---------
ThreadListTraces Thread 0
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -765,8 +765,8 @@
---------
ThreadListTraces Thread 2
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -788,8 +788,8 @@
---------
ThreadListTraces Thread 4
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -811,8 +811,8 @@
---------
ThreadListTraces Thread 6
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
@@ -834,8 +834,8 @@
---------
ThreadListTraces Thread 8
wait (JI)V -1 -2
- wait (J)V 1 442
- wait ()V 2 568
+ wait (J)V 1 386
+ wait ()V 2 524
printOrWait (IILart/ControlData;)V 24 47
baz (IIILart/ControlData;)Ljava/lang/Object; 2 32
bar (IIILart/ControlData;)J 0 26
diff --git a/test/911-get-stack-trace/run b/test/911-get-stack-trace/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/911-get-stack-trace/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/911-get-stack-trace/run.py b/test/911-get-stack-trace/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/911-get-stack-trace/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/912-classes/expected-stdout.txt b/test/912-classes/expected-stdout.txt
index 5da9f8c..787efc5 100644
--- a/test/912-classes/expected-stdout.txt
+++ b/test/912-classes/expected-stdout.txt
@@ -23,7 +23,7 @@
[public static final int java.lang.Integer.BYTES, static final byte[] java.lang.Integer.DigitOnes, static final byte[] java.lang.Integer.DigitTens, public static final int java.lang.Integer.MAX_VALUE, public static final int java.lang.Integer.MIN_VALUE, public static final int java.lang.Integer.SIZE, private static final java.lang.String[] java.lang.Integer.SMALL_NEG_VALUES, private static final java.lang.String[] java.lang.Integer.SMALL_NONNEG_VALUES, public static final java.lang.Class java.lang.Integer.TYPE, static final char[] java.lang.Integer.digits, private static final long java.lang.Integer.serialVersionUID, static final int[] java.lang.Integer.sizeTable, private final int java.lang.Integer.value]
[]
[]
-[java.lang.Integer(), public java.lang.Integer(int), public java.lang.Integer(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.bitCount(int), public static int java.lang.Integer.compare(int,int), public static int java.lang.Integer.compareUnsigned(int,int), public static java.lang.Integer java.lang.Integer.decode(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.divideUnsigned(int,int), static void java.lang.Integer.formatUnsignedInt(int,int,byte[],int,int), static void java.lang.Integer.formatUnsignedInt(int,int,char[],int,int), static int java.lang.Integer.getChars(int,int,byte[]), static int java.lang.Integer.getChars(int,int,char[]), public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String), public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String,int), public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String,java.lang.Integer), public static int java.lang.Integer.hashCode(int), public static int java.lang.Integer.highestOneBit(int), public static int java.lang.Integer.lowestOneBit(int), public static int java.lang.Integer.max(int,int), public static int java.lang.Integer.min(int,int), public static int java.lang.Integer.numberOfLeadingZeros(int), public static int java.lang.Integer.numberOfTrailingZeros(int), public static int java.lang.Integer.parseInt(java.lang.CharSequence,int,int,int) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseInt(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseInt(java.lang.String,int) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseUnsignedInt(java.lang.CharSequence,int,int,int) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseUnsignedInt(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseUnsignedInt(java.lang.String,int) throws java.lang.NumberFormatException, public static int java.lang.Integer.remainderUnsigned(int,int), public static int java.lang.Integer.reverse(int), public static int java.lang.Integer.reverseBytes(int), public static int java.lang.Integer.rotateLeft(int,int), public static int java.lang.Integer.rotateRight(int,int), public static int java.lang.Integer.signum(int), static int java.lang.Integer.stringSize(int), public static int java.lang.Integer.sum(int,int), public static java.lang.String java.lang.Integer.toBinaryString(int), public static java.lang.String java.lang.Integer.toHexString(int), public static java.lang.String java.lang.Integer.toOctalString(int), public static java.lang.String java.lang.Integer.toString(int), public static java.lang.String java.lang.Integer.toString(int,int), public static long java.lang.Integer.toUnsignedLong(int), public static java.lang.String java.lang.Integer.toUnsignedString(int), public static java.lang.String java.lang.Integer.toUnsignedString(int,int), private static java.lang.String java.lang.Integer.toUnsignedString0(int,int), public static java.lang.Integer java.lang.Integer.valueOf(int), public static java.lang.Integer java.lang.Integer.valueOf(java.lang.String) throws java.lang.NumberFormatException, public static java.lang.Integer java.lang.Integer.valueOf(java.lang.String,int) throws java.lang.NumberFormatException, public byte java.lang.Integer.byteValue(), public int java.lang.Integer.compareTo(java.lang.Integer), public int java.lang.Integer.compareTo(java.lang.Object), public double java.lang.Integer.doubleValue(), public boolean java.lang.Integer.equals(java.lang.Object), public float java.lang.Integer.floatValue(), public int java.lang.Integer.hashCode(), public int java.lang.Integer.intValue(), public long java.lang.Integer.longValue(), public short java.lang.Integer.shortValue(), public java.lang.String java.lang.Integer.toString()]
+[java.lang.Integer(), public java.lang.Integer(int), public java.lang.Integer(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.bitCount(int), public static int java.lang.Integer.compare(int,int), public static int java.lang.Integer.compareUnsigned(int,int), public static java.lang.Integer java.lang.Integer.decode(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.divideUnsigned(int,int), private static void java.lang.Integer.formatUnsignedInt(int,int,byte[],int), static void java.lang.Integer.formatUnsignedInt(int,int,byte[],int,int), static void java.lang.Integer.formatUnsignedInt(int,int,char[],int,int), static int java.lang.Integer.getChars(int,int,byte[]), static int java.lang.Integer.getChars(int,int,char[]), public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String), public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String,int), public static java.lang.Integer java.lang.Integer.getInteger(java.lang.String,java.lang.Integer), public static int java.lang.Integer.hashCode(int), public static int java.lang.Integer.highestOneBit(int), public static int java.lang.Integer.lowestOneBit(int), public static int java.lang.Integer.max(int,int), public static int java.lang.Integer.min(int,int), public static int java.lang.Integer.numberOfLeadingZeros(int), public static int java.lang.Integer.numberOfTrailingZeros(int), public static int java.lang.Integer.parseInt(java.lang.CharSequence,int,int,int) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseInt(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseInt(java.lang.String,int) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseUnsignedInt(java.lang.CharSequence,int,int,int) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseUnsignedInt(java.lang.String) throws java.lang.NumberFormatException, public static int java.lang.Integer.parseUnsignedInt(java.lang.String,int) throws java.lang.NumberFormatException, public static int java.lang.Integer.remainderUnsigned(int,int), public static int java.lang.Integer.reverse(int), public static int java.lang.Integer.reverseBytes(int), public static int java.lang.Integer.rotateLeft(int,int), public static int java.lang.Integer.rotateRight(int,int), public static int java.lang.Integer.signum(int), static int java.lang.Integer.stringSize(int), public static int java.lang.Integer.sum(int,int), public static java.lang.String java.lang.Integer.toBinaryString(int), public static java.lang.String java.lang.Integer.toHexString(int), public static java.lang.String java.lang.Integer.toOctalString(int), public static java.lang.String java.lang.Integer.toString(int), public static java.lang.String java.lang.Integer.toString(int,int), public static long java.lang.Integer.toUnsignedLong(int), public static java.lang.String java.lang.Integer.toUnsignedString(int), public static java.lang.String java.lang.Integer.toUnsignedString(int,int), private static java.lang.String java.lang.Integer.toUnsignedString0(int,int), public static java.lang.Integer java.lang.Integer.valueOf(int), public static java.lang.Integer java.lang.Integer.valueOf(java.lang.String) throws java.lang.NumberFormatException, public static java.lang.Integer java.lang.Integer.valueOf(java.lang.String,int) throws java.lang.NumberFormatException, public byte java.lang.Integer.byteValue(), public int java.lang.Integer.compareTo(java.lang.Integer), public int java.lang.Integer.compareTo(java.lang.Object), public double java.lang.Integer.doubleValue(), public boolean java.lang.Integer.equals(java.lang.Object), public float java.lang.Integer.floatValue(), public int java.lang.Integer.hashCode(), public int java.lang.Integer.intValue(), public long java.lang.Integer.longValue(), public short java.lang.Integer.shortValue(), public java.lang.String java.lang.Integer.toString()]
[]
[]
int 100000
diff --git a/test/912-classes/run b/test/912-classes/run
deleted file mode 100755
index f24db40..0000000
--- a/test/912-classes/run
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/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.
-
-# This test checks which classes are initiated by a classloader. App images preload classes.
-# In certain configurations, the app images may be valid even in a new classloader. Turn off
-# app images to avoid the issue.
-
-./default-run "$@" --jvmti \
- --no-app-image
diff --git a/test/912-classes/run.py b/test/912-classes/run.py
new file mode 100644
index 0000000..2f3b9fa
--- /dev/null
+++ b/test/912-classes/run.py
@@ -0,0 +1,23 @@
+#!/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.
+
+
+def run(ctx, args):
+ # This test checks which classes are initiated by a classloader. App images preload classes.
+ # In certain configurations, the app images may be valid even in a new classloader. Turn off
+ # app images to avoid the issue.
+
+ ctx.default_run(args, jvmti=True, app_image=False)
diff --git a/test/913-heaps/expected-stdout.txt b/test/913-heaps/expected-stdout.txt
index 9684503..e7a372a 100644
--- a/test/913-heaps/expected-stdout.txt
+++ b/test/913-heaps/expected-stdout.txt
@@ -1,6 +1,5 @@
---
true true
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=120, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=120, length=-1]
root@root --(thread)--> 3000@0 [size=120, length=-1]
@@ -44,7 +43,6 @@
---
root@root --(jni-global)--> 1@1000 [size=16, length=-1]
root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=120, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1]
@@ -97,7 +95,6 @@
---
3@1001 --(class)--> 1001@0 [size=123456780016, length=-1]
---
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=120, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=120, length=-1]
root@root --(thread)--> 3000@0 [size=120, length=-1]
@@ -109,7 +106,6 @@
---
root@root --(jni-global)--> 1@1000 [size=16, length=-1]
root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=120, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1]
@@ -195,9 +191,7 @@
---
---
---- untagged objects
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=120, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestNonRoot,vreg=8,location= 31])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=4,method=runFollowReferences,vreg=3,location= 164])--> 1000@0 [size=123456780050, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=120, length=-1]
root@root --(thread)--> 3000@0 [size=120, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123456780050, length=-1]
@@ -240,12 +234,10 @@
---
root@root --(jni-global)--> 1@1000 [size=16, length=-1]
root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 1@1000 [size=16, length=-1]
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=120, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=10,location= 8])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=1,method=doFollowReferencesTestImpl,vreg=5,location= 8])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=13,location= 20])--> 1@1000 [size=16, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=2,method=doFollowReferencesTestRoot,vreg=4,location= 20])--> 1@1000 [size=16, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=4,method=runFollowReferences,vreg=3,location= 164])--> 1000@0 [size=123456780055, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=120, length=-1]
root@root --(thread)--> 3000@0 [size=120, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123456780055, length=-1]
@@ -287,8 +279,6 @@
6@1000 --(class)--> 1000@0 [size=123456780055, length=-1]
---
---- tagged classes
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=120, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=4,method=runFollowReferences,vreg=3,location= 181])--> 1000@0 [size=123456780060, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=120, length=-1]
root@root --(thread)--> 3000@0 [size=120, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123456780060, length=-1]
@@ -315,8 +305,6 @@
5@1002 --(field@8)--> 500@0 [size=20, length=2]
6@1000 --(class)--> 1000@0 [size=123456780060, length=-1]
---
-root@root --(jni-local[id=1,tag=3000,depth=0,method=followReferences])--> 3000@0 [size=120, length=-1]
-root@root --(stack-local[id=1,tag=3000,depth=4,method=runFollowReferences,vreg=3,location= 181])--> 1000@0 [size=123456780065, length=-1]
root@root --(stack-local[id=1,tag=3000,depth=5,method=run,vreg=2,location= 0])--> 3000@0 [size=120, length=-1]
root@root --(thread)--> 3000@0 [size=120, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123456780065, length=-1]
diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc
index 28a737d..311b029 100644
--- a/test/913-heaps/heaps.cc
+++ b/test/913-heaps/heaps.cc
@@ -191,6 +191,12 @@
return 0;
}
+ // Ignore system classes, which may come from the JIT compiling a method
+ // in these classes.
+ if (reference_kind == JVMTI_HEAP_REFERENCE_SYSTEM_CLASS) {
+ return 0;
+ }
+
// Only check tagged objects.
if (tag == 0) {
return JVMTI_VISIT_OBJECTS;
@@ -586,7 +592,7 @@
void* user_data) {
FindStringCallbacks* p = reinterpret_cast<FindStringCallbacks*>(user_data);
if (*tag_ptr != 0) {
- size_t utf_byte_count = ti::CountUtf8Bytes(value, value_length);
+ size_t utf_byte_count = ti::CountModifiedUtf8BytesInUtf16(value, value_length);
std::unique_ptr<char[]> mod_utf(new char[utf_byte_count + 1]);
memset(mod_utf.get(), 0, utf_byte_count + 1);
ti::ConvertUtf16ToModifiedUtf8(mod_utf.get(), utf_byte_count, value, value_length);
diff --git a/test/913-heaps/run b/test/913-heaps/run
deleted file mode 100755
index dd35526..0000000
--- a/test/913-heaps/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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 -Xcompiler-option -g
diff --git a/test/913-heaps/run.py b/test/913-heaps/run.py
new file mode 100644
index 0000000..d5731ca
--- /dev/null
+++ b/test/913-heaps/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, Xcompiler_option=["-g"])
diff --git a/test/914-hello-obsolescence/run b/test/914-hello-obsolescence/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/914-hello-obsolescence/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/914-hello-obsolescence/run.py b/test/914-hello-obsolescence/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/914-hello-obsolescence/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/915-obsolete-2/run b/test/915-obsolete-2/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/915-obsolete-2/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/915-obsolete-2/run.py b/test/915-obsolete-2/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/915-obsolete-2/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/915-obsolete-2/src/Main.java b/test/915-obsolete-2/src/Main.java
index be51234..58c4ab9 100644
--- a/test/915-obsolete-2/src/Main.java
+++ b/test/915-obsolete-2/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test915.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test915.run();
+ }
}
diff --git a/test/915-obsolete-2/src/art/Test915.java b/test/915-obsolete-2/src/art/Test915.java
index 63c7f34..2cb3c8f 100644
--- a/test/915-obsolete-2/src/art/Test915.java
+++ b/test/915-obsolete-2/src/art/Test915.java
@@ -19,105 +19,104 @@
import java.util.Base64;
public class Test915 {
+ static class Transform {
+ private void Start() {
+ System.out.println("hello - private");
+ }
- static class Transform {
- private void Start() {
- System.out.println("hello - private");
+ private void Finish() {
+ System.out.println("goodbye - private");
+ }
+
+ public void sayHi(Runnable r) {
+ System.out.println("Pre Start private method call");
+ Start();
+ System.out.println("Post Start private method call");
+ r.run();
+ System.out.println("Pre Finish private method call");
+ Finish();
+ System.out.println("Post Finish private method call");
+ }
}
- private void Finish() {
- System.out.println("goodbye - private");
+ // static class Transform {
+ // private void Start() {
+ // System.out.println("Hello - private - Transformed");
+ // }
+ //
+ // private void Finish() {
+ // System.out.println("Goodbye - private - Transformed");
+ // }
+ //
+ // public void sayHi(Runnable r) {
+ // System.out.println("Pre Start private method call - Transformed");
+ // Start();
+ // System.out.println("Post Start private method call - Transformed");
+ // r.run();
+ // System.out.println("Pre Finish private method call - Transformed");
+ // Finish();
+ // System.out.println("Post Finish private method call - Transformed");
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQANgoADgAZCQAaABsIABwKAB0AHggAHwgAIAoADQAhCAAiCwAjACQIACUKAA0AJggA" +
+ "JwcAKQcALAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAAVTdGFydAEA" +
+ "BkZpbmlzaAEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7KVYBAApTb3VyY2VGaWxlAQAM" +
+ "VGVzdDkxNS5qYXZhDAAPABAHAC0MAC4ALwEAHUhlbGxvIC0gcHJpdmF0ZSAtIFRyYW5zZm9ybWVk" +
+ "BwAwDAAxADIBAB9Hb29kYnllIC0gcHJpdmF0ZSAtIFRyYW5zZm9ybWVkAQArUHJlIFN0YXJ0IHBy" +
+ "aXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAwAEwAQAQAsUG9zdCBTdGFydCBwcml2YXRl" +
+ "IG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQHADMMADQAEAEALFByZSBGaW5pc2ggcHJpdmF0ZSBt" +
+ "ZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkDAAUABABAC1Qb3N0IEZpbmlzaCBwcml2YXRlIG1ldGhv" +
+ "ZCBjYWxsIC0gVHJhbnNmb3JtZWQHADUBABVhcnQvVGVzdDkxNSRUcmFuc2Zvcm0BAAlUcmFuc2Zv" +
+ "cm0BAAxJbm5lckNsYXNzZXMBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEA" +
+ "A291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmlu" +
+ "dGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuAQAL" +
+ "YXJ0L1Rlc3Q5MTUAIAANAA4AAAAAAAQAAAAPABAAAQARAAAAHQABAAEAAAAFKrcAAbEAAAABABIA" +
+ "AAAGAAEAAAAFAAIAEwAQAAEAEQAAACUAAgABAAAACbIAAhIDtgAEsQAAAAEAEgAAAAoAAgAAAAcA" +
+ "CAAIAAIAFAAQAAEAEQAAACUAAgABAAAACbIAAhIFtgAEsQAAAAEAEgAAAAoAAgAAAAoACAALAAEA" +
+ "FQAWAAEAEQAAAGMAAgACAAAAL7IAAhIGtgAEKrcAB7IAAhIItgAEK7kACQEAsgACEgq2AAQqtwAL" +
+ "sgACEgy2AASxAAAAAQASAAAAIgAIAAAADQAIAA4ADAAPABQAEAAaABEAIgASACYAEwAuABQAAgAX" +
+ "AAAAAgAYACsAAAAKAAEADQAoACoACA==");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQAQ+GYcAAAAAAAAAAAAAAAAAAAAAAAAAADUBQAAcAAAAHhWNBIAAAAAAAAAABAFAAAd" +
+ "AAAAcAAAAAoAAADkAAAAAwAAAAwBAAABAAAAMAEAAAcAAAA4AQAAAQAAAHABAABEBAAAkAEAAJAB" +
+ "AACYAQAAoAEAAMEBAADgAQAA+QEAAAgCAAAsAgAATAIAAGMCAAB3AgAAjQIAAKECAAC1AgAA5AIA" +
+ "ABIDAABAAwAAbQMAAHQDAACCAwAAjQMAAJADAACUAwAAoQMAAKcDAACsAwAAtQMAALoDAADBAwAA" +
+ "BAAAAAUAAAAGAAAABwAAAAgAAAAJAAAACgAAAAsAAAAMAAAAFAAAABQAAAAJAAAAAAAAABUAAAAJ" +
+ "AAAA0AMAABUAAAAJAAAAyAMAAAgABAAYAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAARAAAAAAABABsA" +
+ "AAAEAAIAGQAAAAUAAAAAAAAABgAAABoAAAAAAAAAAAAAAAUAAAAAAAAAEgAAAAAFAADMBAAAAAAA" +
+ "AAY8aW5pdD4ABkZpbmlzaAAfR29vZGJ5ZSAtIHByaXZhdGUgLSBUcmFuc2Zvcm1lZAAdSGVsbG8g" +
+ "LSBwcml2YXRlIC0gVHJhbnNmb3JtZWQAF0xhcnQvVGVzdDkxNSRUcmFuc2Zvcm07AA1MYXJ0L1Rl" +
+ "c3Q5MTU7ACJMZGFsdmlrL2Fubm90YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90" +
+ "YXRpb24vSW5uZXJDbGFzczsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEvbGFuZy9PYmpl" +
+ "Y3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJMamF2YS9sYW5n" +
+ "L1N5c3RlbTsALVBvc3QgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAAs" +
+ "UG9zdCBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQALFByZSBGaW5pc2gg" +
+ "cHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkACtQcmUgU3RhcnQgcHJpdmF0ZSBtZXRo" +
+ "b2QgY2FsbCAtIFRyYW5zZm9ybWVkAAVTdGFydAAMVGVzdDkxNS5qYXZhAAlUcmFuc2Zvcm0AAVYA" +
+ "AlZMAAthY2Nlc3NGbGFncwAEbmFtZQADb3V0AAdwcmludGxuAANydW4ABXNheUhpAAV2YWx1ZQAB" +
+ "AAAABwAAAAEAAAAGAAAABQAHDgAKAAcOAQgPAAcABw4BCA8ADQEABw4BCA8BAw8BCA8BAw8BCA8B" +
+ "Aw8BCA8AAQABAAEAAADYAwAABAAAAHAQBQAAAA4AAwABAAIAAADdAwAACQAAAGIAAAAbAQIAAABu" +
+ "IAQAEAAOAAAAAwABAAIAAADlAwAACQAAAGIAAAAbAQMAAABuIAQAEAAOAAAABAACAAIAAADtAwAA" +
+ "KgAAAGIAAAAbARAAAABuIAQAEABwEAIAAgBiAAAAGwEOAAAAbiAEABAAchAGAAMAYgAAABsBDwAA" +
+ "AG4gBAAQAHAQAQACAGIAAAAbAQ0AAABuIAQAEAAOAAAAAwEAgIAEiAgBAqAIAQLECAMB6AgAAAIC" +
+ "ARwYAQIDAhYECBcXEwACAAAA5AQAAOoEAAD0BAAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAA" +
+ "AAEAAAAdAAAAcAAAAAIAAAAKAAAA5AAAAAMAAAADAAAADAEAAAQAAAABAAAAMAEAAAUAAAAHAAAA" +
+ "OAEAAAYAAAABAAAAcAEAAAIgAAAdAAAAkAEAAAEQAAACAAAAyAMAAAMgAAAEAAAA2AMAAAEgAAAE" +
+ "AAAACAQAAAAgAAABAAAAzAQAAAQgAAACAAAA5AQAAAMQAAABAAAA9AQAAAYgAAABAAAAAAUAAAAQ" +
+ "AAABAAAAEAUAAA==");
+
+ public static void run() {
+ Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+ doTest(new Transform());
}
- public void sayHi(Runnable r) {
- System.out.println("Pre Start private method call");
- Start();
- System.out.println("Post Start private method call");
- r.run();
- System.out.println("Pre Finish private method call");
- Finish();
- System.out.println("Post Finish private method call");
+ public static void doTest(Transform t) {
+ t.sayHi(() -> { System.out.println("Not doing anything here"); });
+ t.sayHi(() -> {
+ System.out.println("transforming calling function");
+ Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ });
+ t.sayHi(() -> { System.out.println("Not doing anything here"); });
}
- }
-
- // static class Transform {
- // private void Start() {
- // System.out.println("Hello - private - Transformed");
- // }
- //
- // private void Finish() {
- // System.out.println("Goodbye - private - Transformed");
- // }
- //
- // public void sayHi(Runnable r) {
- // System.out.println("Pre Start private method call - Transformed");
- // Start();
- // System.out.println("Post Start private method call - Transformed");
- // r.run();
- // System.out.println("Pre Finish private method call - Transformed");
- // Finish();
- // System.out.println("Post Finish private method call - Transformed");
- // }
- // }
- private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
- "yv66vgAAADQANgoADgAZCQAaABsIABwKAB0AHggAHwgAIAoADQAhCAAiCwAjACQIACUKAA0AJggA" +
- "JwcAKQcALAEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAAVTdGFydAEA" +
- "BkZpbmlzaAEABXNheUhpAQAXKExqYXZhL2xhbmcvUnVubmFibGU7KVYBAApTb3VyY2VGaWxlAQAM" +
- "VGVzdDkxNS5qYXZhDAAPABAHAC0MAC4ALwEAHUhlbGxvIC0gcHJpdmF0ZSAtIFRyYW5zZm9ybWVk" +
- "BwAwDAAxADIBAB9Hb29kYnllIC0gcHJpdmF0ZSAtIFRyYW5zZm9ybWVkAQArUHJlIFN0YXJ0IHBy" +
- "aXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAwAEwAQAQAsUG9zdCBTdGFydCBwcml2YXRl" +
- "IG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQHADMMADQAEAEALFByZSBGaW5pc2ggcHJpdmF0ZSBt" +
- "ZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkDAAUABABAC1Qb3N0IEZpbmlzaCBwcml2YXRlIG1ldGhv" +
- "ZCBjYWxsIC0gVHJhbnNmb3JtZWQHADUBABVhcnQvVGVzdDkxNSRUcmFuc2Zvcm0BAAlUcmFuc2Zv" +
- "cm0BAAxJbm5lckNsYXNzZXMBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEA" +
- "A291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmlu" +
- "dGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuAQAL" +
- "YXJ0L1Rlc3Q5MTUAIAANAA4AAAAAAAQAAAAPABAAAQARAAAAHQABAAEAAAAFKrcAAbEAAAABABIA" +
- "AAAGAAEAAAAFAAIAEwAQAAEAEQAAACUAAgABAAAACbIAAhIDtgAEsQAAAAEAEgAAAAoAAgAAAAcA" +
- "CAAIAAIAFAAQAAEAEQAAACUAAgABAAAACbIAAhIFtgAEsQAAAAEAEgAAAAoAAgAAAAoACAALAAEA" +
- "FQAWAAEAEQAAAGMAAgACAAAAL7IAAhIGtgAEKrcAB7IAAhIItgAEK7kACQEAsgACEgq2AAQqtwAL" +
- "sgACEgy2AASxAAAAAQASAAAAIgAIAAAADQAIAA4ADAAPABQAEAAaABEAIgASACYAEwAuABQAAgAX" +
- "AAAAAgAYACsAAAAKAAEADQAoACoACA==");
- private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
- "ZGV4CjAzNQAQ+GYcAAAAAAAAAAAAAAAAAAAAAAAAAADUBQAAcAAAAHhWNBIAAAAAAAAAABAFAAAd" +
- "AAAAcAAAAAoAAADkAAAAAwAAAAwBAAABAAAAMAEAAAcAAAA4AQAAAQAAAHABAABEBAAAkAEAAJAB" +
- "AACYAQAAoAEAAMEBAADgAQAA+QEAAAgCAAAsAgAATAIAAGMCAAB3AgAAjQIAAKECAAC1AgAA5AIA" +
- "ABIDAABAAwAAbQMAAHQDAACCAwAAjQMAAJADAACUAwAAoQMAAKcDAACsAwAAtQMAALoDAADBAwAA" +
- "BAAAAAUAAAAGAAAABwAAAAgAAAAJAAAACgAAAAsAAAAMAAAAFAAAABQAAAAJAAAAAAAAABUAAAAJ" +
- "AAAA0AMAABUAAAAJAAAAyAMAAAgABAAYAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAARAAAAAAABABsA" +
- "AAAEAAIAGQAAAAUAAAAAAAAABgAAABoAAAAAAAAAAAAAAAUAAAAAAAAAEgAAAAAFAADMBAAAAAAA" +
- "AAY8aW5pdD4ABkZpbmlzaAAfR29vZGJ5ZSAtIHByaXZhdGUgLSBUcmFuc2Zvcm1lZAAdSGVsbG8g" +
- "LSBwcml2YXRlIC0gVHJhbnNmb3JtZWQAF0xhcnQvVGVzdDkxNSRUcmFuc2Zvcm07AA1MYXJ0L1Rl" +
- "c3Q5MTU7ACJMZGFsdmlrL2Fubm90YXRpb24vRW5jbG9zaW5nQ2xhc3M7AB5MZGFsdmlrL2Fubm90" +
- "YXRpb24vSW5uZXJDbGFzczsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEvbGFuZy9PYmpl" +
- "Y3Q7ABRMamF2YS9sYW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABJMamF2YS9sYW5n" +
- "L1N5c3RlbTsALVBvc3QgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAAs" +
- "UG9zdCBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQALFByZSBGaW5pc2gg" +
- "cHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkACtQcmUgU3RhcnQgcHJpdmF0ZSBtZXRo" +
- "b2QgY2FsbCAtIFRyYW5zZm9ybWVkAAVTdGFydAAMVGVzdDkxNS5qYXZhAAlUcmFuc2Zvcm0AAVYA" +
- "AlZMAAthY2Nlc3NGbGFncwAEbmFtZQADb3V0AAdwcmludGxuAANydW4ABXNheUhpAAV2YWx1ZQAB" +
- "AAAABwAAAAEAAAAGAAAABQAHDgAKAAcOAQgPAAcABw4BCA8ADQEABw4BCA8BAw8BCA8BAw8BCA8B" +
- "Aw8BCA8AAQABAAEAAADYAwAABAAAAHAQBQAAAA4AAwABAAIAAADdAwAACQAAAGIAAAAbAQIAAABu" +
- "IAQAEAAOAAAAAwABAAIAAADlAwAACQAAAGIAAAAbAQMAAABuIAQAEAAOAAAABAACAAIAAADtAwAA" +
- "KgAAAGIAAAAbARAAAABuIAQAEABwEAIAAgBiAAAAGwEOAAAAbiAEABAAchAGAAMAYgAAABsBDwAA" +
- "AG4gBAAQAHAQAQACAGIAAAAbAQ0AAABuIAQAEAAOAAAAAwEAgIAEiAgBAqAIAQLECAMB6AgAAAIC" +
- "ARwYAQIDAhYECBcXEwACAAAA5AQAAOoEAAD0BAAAAAAAAAAAAAAAAAAAEAAAAAAAAAABAAAAAAAA" +
- "AAEAAAAdAAAAcAAAAAIAAAAKAAAA5AAAAAMAAAADAAAADAEAAAQAAAABAAAAMAEAAAUAAAAHAAAA" +
- "OAEAAAYAAAABAAAAcAEAAAIgAAAdAAAAkAEAAAEQAAACAAAAyAMAAAMgAAAEAAAA2AMAAAEgAAAE" +
- "AAAACAQAAAAgAAABAAAAzAQAAAQgAAACAAAA5AQAAAMQAAABAAAA9AQAAAYgAAABAAAAAAUAAAAQ" +
- "AAABAAAAEAUAAA==");
-
- public static void run() {
- Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
- doTest(new Transform());
- }
-
- public static void doTest(Transform t) {
- t.sayHi(() -> { System.out.println("Not doing anything here"); });
- t.sayHi(() -> {
- System.out.println("transforming calling function");
- Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
- });
- t.sayHi(() -> { System.out.println("Not doing anything here"); });
- }
}
diff --git a/test/916-obsolete-jit/run b/test/916-obsolete-jit/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/916-obsolete-jit/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/916-obsolete-jit/run.py b/test/916-obsolete-jit/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/916-obsolete-jit/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/916-obsolete-jit/src/Main.java b/test/916-obsolete-jit/src/Main.java
index d7b32ba..f893c8e 100644
--- a/test/916-obsolete-jit/src/Main.java
+++ b/test/916-obsolete-jit/src/Main.java
@@ -23,151 +23,151 @@
public class Main {
- // import java.util.function.Consumer;
- //
- // class Transform {
- // private void Start(Consumer<String> reporter) {
- // reporter.accept("Hello - private - Transformed");
- // }
- //
- // private void Finish(Consumer<String> reporter) {
- // reporter.accept("Goodbye - private - Transformed");
- // }
- //
- // public void sayHi(Runnable r, Consumer<String> reporter) {
- // reporter.accept("pre Start private method call - Transformed");
- // Start(reporter);
- // reporter.accept("post Start private method call - Transformed");
- // r.run();
- // reporter.accept("pre Finish private method call - Transformed");
- // Finish(reporter);
- // reporter.accept("post Finish private method call - Transformed");
- // }
- // }
- private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
- "yv66vgAAADQAMAoADQAcCAAdCwAeAB8IACAIACEKAAwAIggAIwsAJAAlCAAmCgAMACcIACgHACkH" +
- "ACoBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAFU3RhcnQBACAoTGph" +
- "dmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcjspVgEACVNpZ25hdHVyZQEANChMamF2YS91dGlsL2Z1" +
- "bmN0aW9uL0NvbnN1bWVyPExqYXZhL2xhbmcvU3RyaW5nOz47KVYBAAZGaW5pc2gBAAVzYXlIaQEA" +
- "NChMamF2YS9sYW5nL1J1bm5hYmxlO0xqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI7KVYBAEgo" +
- "TGphdmEvbGFuZy9SdW5uYWJsZTtMamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyPExqYXZhL2xh" +
- "bmcvU3RyaW5nOz47KVYBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAA4ADwEAHUhlbGxv" +
- "IC0gcHJpdmF0ZSAtIFRyYW5zZm9ybWVkBwArDAAsAC0BAB9Hb29kYnllIC0gcHJpdmF0ZSAtIFRy" +
- "YW5zZm9ybWVkAQArcHJlIFN0YXJ0IHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAwA" +
- "EgATAQAscG9zdCBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQHAC4MAC8A" +
- "DwEALHByZSBGaW5pc2ggcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkDAAWABMBAC1w" +
- "b3N0IEZpbmlzaCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQBAAlUcmFuc2Zvcm0B" +
- "ABBqYXZhL2xhbmcvT2JqZWN0AQAbamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyAQAGYWNjZXB0" +
- "AQAVKExqYXZhL2xhbmcvT2JqZWN0OylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuACAADAAN" +
- "AAAAAAAEAAAADgAPAAEAEAAAAB0AAQABAAAABSq3AAGxAAAAAQARAAAABgABAAAAEwACABIAEwAC" +
- "ABAAAAAlAAIAAgAAAAkrEgK5AAMCALEAAAABABEAAAAKAAIAAAAVAAgAFgAUAAAAAgAVAAIAFgAT" +
- "AAIAEAAAACUAAgACAAAACSsSBLkAAwIAsQAAAAEAEQAAAAoAAgAAABkACAAaABQAAAACABUAAQAX" +
- "ABgAAgAQAAAAZQACAAMAAAAxLBIFuQADAgAqLLcABiwSB7kAAwIAK7kACAEALBIJuQADAgAqLLcA" +
- "CiwSC7kAAwIAsQAAAAEAEQAAACIACAAAAB0ACAAeAA0AHwAVACAAGwAhACMAIgAoACMAMAAkABQA" +
- "AAACABkAAQAaAAAAAgAb");
- private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
- "ZGV4CjAzNQBc8wr9PcHqnOR61m+0kimXTSddVMToJPuYBQAAcAAAAHhWNBIAAAAAAAAAAOAEAAAc" +
- "AAAAcAAAAAYAAADgAAAABAAAAPgAAAAAAAAAAAAAAAcAAAAoAQAAAQAAAGABAAAYBAAAgAEAAHoC" +
- "AAB9AgAAgAIAAIgCAACOAgAAlgIAALcCAADWAgAA4wIAAAIDAAAWAwAALAMAAEADAABeAwAAfQMA" +
- "AIQDAACUAwAAlwMAAJsDAACgAwAAqAMAALwDAADrAwAAGQQAAEcEAAB0BAAAeQQAAIAEAAAHAAAA" +
- "CAAAAAkAAAAKAAAADQAAABAAAAAQAAAABQAAAAAAAAARAAAABQAAAGQCAAASAAAABQAAAGwCAAAR" +
- "AAAABQAAAHQCAAAAAAAAAgAAAAAAAwAEAAAAAAADAA4AAAAAAAIAGgAAAAIAAAACAAAAAwAAABkA" +
- "AAAEAAEAEwAAAAAAAAAAAAAAAgAAAAAAAAAPAAAAPAIAAMoEAAAAAAAAAQAAAKgEAAABAAAAuAQA" +
- "AAEAAQABAAAAhwQAAAQAAABwEAQAAAAOAAMAAgACAAAAjAQAAAcAAAAbAAUAAAByIAYAAgAOAAAA" +
- "AwACAAIAAACTBAAABwAAABsABgAAAHIgBgACAA4AAAAEAAMAAgAAAJoEAAAiAAAAGwAYAAAAciAG" +
- "AAMAcCACADEAGwAWAAAAciAGAAMAchAFAAIAGwAXAAAAciAGAAMAcCABADEAGwAVAAAAciAGAAMA" +
- "DgAAAAAAAAAAAAMAAAAAAAAAAQAAAIABAAACAAAAgAEAAAMAAACIAQAAAQAAAAIAAAACAAAAAwAE" +
- "AAEAAAAEAAEoAAE8AAY8aW5pdD4ABD47KVYABkZpbmlzaAAfR29vZGJ5ZSAtIHByaXZhdGUgLSBU" +
- "cmFuc2Zvcm1lZAAdSGVsbG8gLSBwcml2YXRlIC0gVHJhbnNmb3JtZWQAC0xUcmFuc2Zvcm07AB1M" +
- "ZGFsdmlrL2Fubm90YXRpb24vU2lnbmF0dXJlOwASTGphdmEvbGFuZy9PYmplY3Q7ABRMamF2YS9s" +
- "YW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABxMamF2YS91dGlsL2Z1bmN0aW9uL0Nv" +
- "bnN1bWVyAB1MamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyOwAFU3RhcnQADlRyYW5zZm9ybS5q" +
- "YXZhAAFWAAJWTAADVkxMAAZhY2NlcHQAEmVtaXR0ZXI6IGphY2stNC4xOQAtcG9zdCBGaW5pc2gg" +
- "cHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkACxwb3N0IFN0YXJ0IHByaXZhdGUgbWV0" +
- "aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAAscHJlIEZpbmlzaCBwcml2YXRlIG1ldGhvZCBjYWxsIC0g" +
- "VHJhbnNmb3JtZWQAK3ByZSBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQA" +
- "A3J1bgAFc2F5SGkABXZhbHVlABMABw4AGQEABw5pABUBAAcOaQAdAgAABw5pPGk8aTxpAAIBARsc" +
- "BRcAFwwXARcLFwMCAQEbHAYXABcKFwwXARcLFwMAAAMBAICABJADAQKoAwECyAMDAegDDwAAAAAA" +
- "AAABAAAAAAAAAAEAAAAcAAAAcAAAAAIAAAAGAAAA4AAAAAMAAAAEAAAA+AAAAAUAAAAHAAAAKAEA" +
- "AAYAAAABAAAAYAEAAAMQAAACAAAAgAEAAAEgAAAEAAAAkAEAAAYgAAABAAAAPAIAAAEQAAADAAAA" +
- "ZAIAAAIgAAAcAAAAegIAAAMgAAAEAAAAhwQAAAQgAAACAAAAqAQAAAAgAAABAAAAygQAAAAQAAAB" +
- "AAAA4AQAAA==");
+ // import java.util.function.Consumer;
+ //
+ // class Transform {
+ // private void Start(Consumer<String> reporter) {
+ // reporter.accept("Hello - private - Transformed");
+ // }
+ //
+ // private void Finish(Consumer<String> reporter) {
+ // reporter.accept("Goodbye - private - Transformed");
+ // }
+ //
+ // public void sayHi(Runnable r, Consumer<String> reporter) {
+ // reporter.accept("pre Start private method call - Transformed");
+ // Start(reporter);
+ // reporter.accept("post Start private method call - Transformed");
+ // r.run();
+ // reporter.accept("pre Finish private method call - Transformed");
+ // Finish(reporter);
+ // reporter.accept("post Finish private method call - Transformed");
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAMAoADQAcCAAdCwAeAB8IACAIACEKAAwAIggAIwsAJAAlCAAmCgAMACcIACgHACkH" +
+ "ACoBAAY8aW5pdD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAFU3RhcnQBACAoTGph" +
+ "dmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcjspVgEACVNpZ25hdHVyZQEANChMamF2YS91dGlsL2Z1" +
+ "bmN0aW9uL0NvbnN1bWVyPExqYXZhL2xhbmcvU3RyaW5nOz47KVYBAAZGaW5pc2gBAAVzYXlIaQEA" +
+ "NChMamF2YS9sYW5nL1J1bm5hYmxlO0xqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI7KVYBAEgo" +
+ "TGphdmEvbGFuZy9SdW5uYWJsZTtMamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyPExqYXZhL2xh" +
+ "bmcvU3RyaW5nOz47KVYBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAA4ADwEAHUhlbGxv" +
+ "IC0gcHJpdmF0ZSAtIFRyYW5zZm9ybWVkBwArDAAsAC0BAB9Hb29kYnllIC0gcHJpdmF0ZSAtIFRy" +
+ "YW5zZm9ybWVkAQArcHJlIFN0YXJ0IHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAwA" +
+ "EgATAQAscG9zdCBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQHAC4MAC8A" +
+ "DwEALHByZSBGaW5pc2ggcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkDAAWABMBAC1w" +
+ "b3N0IEZpbmlzaCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQBAAlUcmFuc2Zvcm0B" +
+ "ABBqYXZhL2xhbmcvT2JqZWN0AQAbamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyAQAGYWNjZXB0" +
+ "AQAVKExqYXZhL2xhbmcvT2JqZWN0OylWAQASamF2YS9sYW5nL1J1bm5hYmxlAQADcnVuACAADAAN" +
+ "AAAAAAAEAAAADgAPAAEAEAAAAB0AAQABAAAABSq3AAGxAAAAAQARAAAABgABAAAAEwACABIAEwAC" +
+ "ABAAAAAlAAIAAgAAAAkrEgK5AAMCALEAAAABABEAAAAKAAIAAAAVAAgAFgAUAAAAAgAVAAIAFgAT" +
+ "AAIAEAAAACUAAgACAAAACSsSBLkAAwIAsQAAAAEAEQAAAAoAAgAAABkACAAaABQAAAACABUAAQAX" +
+ "ABgAAgAQAAAAZQACAAMAAAAxLBIFuQADAgAqLLcABiwSB7kAAwIAK7kACAEALBIJuQADAgAqLLcA" +
+ "CiwSC7kAAwIAsQAAAAEAEQAAACIACAAAAB0ACAAeAA0AHwAVACAAGwAhACMAIgAoACMAMAAkABQA" +
+ "AAACABkAAQAaAAAAAgAb");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQBc8wr9PcHqnOR61m+0kimXTSddVMToJPuYBQAAcAAAAHhWNBIAAAAAAAAAAOAEAAAc" +
+ "AAAAcAAAAAYAAADgAAAABAAAAPgAAAAAAAAAAAAAAAcAAAAoAQAAAQAAAGABAAAYBAAAgAEAAHoC" +
+ "AAB9AgAAgAIAAIgCAACOAgAAlgIAALcCAADWAgAA4wIAAAIDAAAWAwAALAMAAEADAABeAwAAfQMA" +
+ "AIQDAACUAwAAlwMAAJsDAACgAwAAqAMAALwDAADrAwAAGQQAAEcEAAB0BAAAeQQAAIAEAAAHAAAA" +
+ "CAAAAAkAAAAKAAAADQAAABAAAAAQAAAABQAAAAAAAAARAAAABQAAAGQCAAASAAAABQAAAGwCAAAR" +
+ "AAAABQAAAHQCAAAAAAAAAgAAAAAAAwAEAAAAAAADAA4AAAAAAAIAGgAAAAIAAAACAAAAAwAAABkA" +
+ "AAAEAAEAEwAAAAAAAAAAAAAAAgAAAAAAAAAPAAAAPAIAAMoEAAAAAAAAAQAAAKgEAAABAAAAuAQA" +
+ "AAEAAQABAAAAhwQAAAQAAABwEAQAAAAOAAMAAgACAAAAjAQAAAcAAAAbAAUAAAByIAYAAgAOAAAA" +
+ "AwACAAIAAACTBAAABwAAABsABgAAAHIgBgACAA4AAAAEAAMAAgAAAJoEAAAiAAAAGwAYAAAAciAG" +
+ "AAMAcCACADEAGwAWAAAAciAGAAMAchAFAAIAGwAXAAAAciAGAAMAcCABADEAGwAVAAAAciAGAAMA" +
+ "DgAAAAAAAAAAAAMAAAAAAAAAAQAAAIABAAACAAAAgAEAAAMAAACIAQAAAQAAAAIAAAACAAAAAwAE" +
+ "AAEAAAAEAAEoAAE8AAY8aW5pdD4ABD47KVYABkZpbmlzaAAfR29vZGJ5ZSAtIHByaXZhdGUgLSBU" +
+ "cmFuc2Zvcm1lZAAdSGVsbG8gLSBwcml2YXRlIC0gVHJhbnNmb3JtZWQAC0xUcmFuc2Zvcm07AB1M" +
+ "ZGFsdmlrL2Fubm90YXRpb24vU2lnbmF0dXJlOwASTGphdmEvbGFuZy9PYmplY3Q7ABRMamF2YS9s" +
+ "YW5nL1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABxMamF2YS91dGlsL2Z1bmN0aW9uL0Nv" +
+ "bnN1bWVyAB1MamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyOwAFU3RhcnQADlRyYW5zZm9ybS5q" +
+ "YXZhAAFWAAJWTAADVkxMAAZhY2NlcHQAEmVtaXR0ZXI6IGphY2stNC4xOQAtcG9zdCBGaW5pc2gg" +
+ "cHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkACxwb3N0IFN0YXJ0IHByaXZhdGUgbWV0" +
+ "aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAAscHJlIEZpbmlzaCBwcml2YXRlIG1ldGhvZCBjYWxsIC0g" +
+ "VHJhbnNmb3JtZWQAK3ByZSBTdGFydCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQA" +
+ "A3J1bgAFc2F5SGkABXZhbHVlABMABw4AGQEABw5pABUBAAcOaQAdAgAABw5pPGk8aTxpAAIBARsc" +
+ "BRcAFwwXARcLFwMCAQEbHAYXABcKFwwXARcLFwMAAAMBAICABJADAQKoAwECyAMDAegDDwAAAAAA" +
+ "AAABAAAAAAAAAAEAAAAcAAAAcAAAAAIAAAAGAAAA4AAAAAMAAAAEAAAA+AAAAAUAAAAHAAAAKAEA" +
+ "AAYAAAABAAAAYAEAAAMQAAACAAAAgAEAAAEgAAAEAAAAkAEAAAYgAAABAAAAPAIAAAEQAAADAAAA" +
+ "ZAIAAAIgAAAcAAAAegIAAAMgAAAEAAAAhwQAAAQgAAACAAAAqAQAAAAgAAABAAAAygQAAAAQAAAB" +
+ "AAAA4AQAAA==");
- // A class that we can use to keep track of the output of this test.
- private static class TestWatcher implements Consumer<String> {
- private StringBuilder sb;
- public TestWatcher() {
- sb = new StringBuilder();
+ // A class that we can use to keep track of the output of this test.
+ private static class TestWatcher implements Consumer<String> {
+ private StringBuilder sb;
+ public TestWatcher() {
+ sb = new StringBuilder();
+ }
+
+ @Override
+ public void accept(String s) {
+ sb.append(s);
+ sb.append('\n');
+ }
+
+ public String getOutput() {
+ return sb.toString();
+ }
+
+ public void clear() {
+ sb = new StringBuilder();
+ }
}
- @Override
- public void accept(String s) {
- sb.append(s);
- sb.append('\n');
+ public static void main(String[] args) {
+ doTest(new Transform(), new TestWatcher());
}
- public String getOutput() {
- return sb.toString();
+ private static boolean interpreting = true;
+ private static boolean retry = false;
+
+ public static void doTest(Transform t, TestWatcher w) {
+ // Get the methods that need to be optimized.
+ Method say_hi_method;
+ // Figure out if we can even JIT at all.
+ final boolean has_jit = hasJit();
+ try {
+ say_hi_method = Transform.class.getDeclaredMethod(
+ "sayHi", Runnable.class, Consumer.class);
+ } catch (Exception e) {
+ System.out.println("Unable to find methods!");
+ e.printStackTrace(System.out);
+ return;
+ }
+ // Makes sure the stack is the way we want it for the test and does the redefinition.
+ // It will set the retry boolean to true if the stack does not have a JIT-compiled
+ // sayHi entry. This can only happen if the method gets GC'd.
+ Runnable do_redefinition = () -> {
+ if (has_jit && Main.isInterpretedFunction(say_hi_method, true)) {
+ // Try again. We are not running the right jitted methods/cannot redefine them now.
+ retry = true;
+ } else {
+ // Actually do the redefinition. The stack looks good.
+ retry = false;
+ w.accept("transforming calling function");
+ Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ }
+ };
+ // This just prints something out to show we are running the Runnable.
+ Runnable say_nothing = () -> { w.accept("Not doing anything here"); };
+ do {
+ // Run ensureJitCompiled here since it might get GCd
+ ensureJitCompiled(Transform.class, "sayHi");
+ // Clear output.
+ w.clear();
+ // Try and redefine.
+ t.sayHi(say_nothing, w);
+ t.sayHi(do_redefinition, w);
+ t.sayHi(say_nothing, w);
+ } while (retry);
+ // Print output of last run.
+ System.out.print(w.getOutput());
}
- public void clear() {
- sb = new StringBuilder();
- }
- }
+ private static native boolean hasJit();
- public static void main(String[] args) {
- doTest(new Transform(), new TestWatcher());
- }
+ private static native boolean isInterpretedFunction(Method m, boolean require_deoptimizable);
- private static boolean interpreting = true;
- private static boolean retry = false;
-
- public static void doTest(Transform t, TestWatcher w) {
- // Get the methods that need to be optimized.
- Method say_hi_method;
- // Figure out if we can even JIT at all.
- final boolean has_jit = hasJit();
- try {
- say_hi_method = Transform.class.getDeclaredMethod(
- "sayHi", Runnable.class, Consumer.class);
- } catch (Exception e) {
- System.out.println("Unable to find methods!");
- e.printStackTrace(System.out);
- return;
- }
- // Makes sure the stack is the way we want it for the test and does the redefinition. It will
- // set the retry boolean to true if the stack does not have a JIT-compiled sayHi entry. This can
- // only happen if the method gets GC'd.
- Runnable do_redefinition = () -> {
- if (has_jit && Main.isInterpretedFunction(say_hi_method, true)) {
- // Try again. We are not running the right jitted methods/cannot redefine them now.
- retry = true;
- } else {
- // Actually do the redefinition. The stack looks good.
- retry = false;
- w.accept("transforming calling function");
- Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
- }
- };
- // This just prints something out to show we are running the Runnable.
- Runnable say_nothing = () -> { w.accept("Not doing anything here"); };
- do {
- // Run ensureJitCompiled here since it might get GCd
- ensureJitCompiled(Transform.class, "sayHi");
- // Clear output.
- w.clear();
- // Try and redefine.
- t.sayHi(say_nothing, w);
- t.sayHi(do_redefinition, w);
- t.sayHi(say_nothing, w);
- } while (retry);
- // Print output of last run.
- System.out.print(w.getOutput());
- }
-
- private static native boolean hasJit();
-
- private static native boolean isInterpretedFunction(Method m, boolean require_deoptimizable);
-
- private static native void ensureJitCompiled(Class c, String name);
+ private static native void ensureJitCompiled(Class c, String name);
}
diff --git a/test/916-obsolete-jit/src/Transform.java b/test/916-obsolete-jit/src/Transform.java
index 9c9adbc..5dbc04e 100644
--- a/test/916-obsolete-jit/src/Transform.java
+++ b/test/916-obsolete-jit/src/Transform.java
@@ -17,21 +17,21 @@
import java.util.function.Consumer;
class Transform {
- private void Start(Consumer<String> reporter) {
- reporter.accept("hello - private");
- }
+ private void Start(Consumer<String> reporter) {
+ reporter.accept("hello - private");
+ }
- private void Finish(Consumer<String> reporter) {
- reporter.accept("goodbye - private");
- }
+ private void Finish(Consumer<String> reporter) {
+ reporter.accept("goodbye - private");
+ }
- public void sayHi(Runnable r, Consumer<String> reporter) {
- reporter.accept("Pre Start private method call");
- Start(reporter);
- reporter.accept("Post Start private method call");
- r.run();
- reporter.accept("Pre Finish private method call");
- Finish(reporter);
- reporter.accept("Post Finish private method call");
- }
+ public void sayHi(Runnable r, Consumer<String> reporter) {
+ reporter.accept("Pre Start private method call");
+ Start(reporter);
+ reporter.accept("Post Start private method call");
+ r.run();
+ reporter.accept("Pre Finish private method call");
+ Finish(reporter);
+ reporter.accept("Post Finish private method call");
+ }
}
diff --git a/test/917-fields-transformation/run b/test/917-fields-transformation/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/917-fields-transformation/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/917-fields-transformation/run.py b/test/917-fields-transformation/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/917-fields-transformation/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/917-fields-transformation/src/Main.java b/test/917-fields-transformation/src/Main.java
index 289b89f..e277deb 100644
--- a/test/917-fields-transformation/src/Main.java
+++ b/test/917-fields-transformation/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test917.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test917.run();
+ }
}
diff --git a/test/917-fields-transformation/src/art/Test917.java b/test/917-fields-transformation/src/art/Test917.java
index 245e92e..ba9a06b 100644
--- a/test/917-fields-transformation/src/art/Test917.java
+++ b/test/917-fields-transformation/src/art/Test917.java
@@ -18,80 +18,78 @@
import java.util.Base64;
public class Test917 {
+ static class Transform {
+ public String take1;
+ public String take2;
- static class Transform {
- public String take1;
- public String take2;
+ public Transform(String take1, String take2) {
+ this.take1 = take1;
+ this.take2 = take2;
+ }
- public Transform(String take1, String take2) {
- this.take1 = take1;
- this.take2 = take2;
+ public String getResult() {
+ return take1;
+ }
}
- public String getResult() {
- return take1;
+
+ // base64 encoded class/dex file for
+ // static class Transform {
+ // public String take1;
+ // public String take2;
+ //
+ // public Transform(String a, String b) {
+ // take1 = a;
+ // take2 = b;
+ // }
+ //
+ // public String getResult() {
+ // return take2;
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAGwoABQARCQAEABIJAAQAEwcAFQcAGAEABXRha2UxAQASTGphdmEvbGFuZy9TdHJp" +
+ "bmc7AQAFdGFrZTIBAAY8aW5pdD4BACcoTGphdmEvbGFuZy9TdHJpbmc7TGphdmEvbGFuZy9TdHJp" +
+ "bmc7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAJZ2V0UmVzdWx0AQAUKClMamF2YS9sYW5n" +
+ "L1N0cmluZzsBAApTb3VyY2VGaWxlAQAMVGVzdDkxNy5qYXZhDAAJABkMAAYABwwACAAHBwAaAQAV" +
+ "YXJ0L1Rlc3Q5MTckVHJhbnNmb3JtAQAJVHJhbnNmb3JtAQAMSW5uZXJDbGFzc2VzAQAQamF2YS9s" +
+ "YW5nL09iamVjdAEAAygpVgEAC2FydC9UZXN0OTE3ACAABAAFAAAAAgABAAYABwAAAAEACAAHAAAA" +
+ "AgABAAkACgABAAsAAAAzAAIAAwAAAA8qtwABKiu1AAIqLLUAA7EAAAABAAwAAAASAAQAAAAJAAQA" +
+ "CgAJAAsADgAMAAEADQAOAAEACwAAAB0AAQABAAAABSq0AAOwAAAAAQAMAAAABgABAAAADwACAA8A" +
+ "AAACABAAFwAAAAoAAQAEABQAFgAI");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQBdcPySAAAAAAAAAAAAAAAAAAAAAAAAAACQAwAAcAAAAHhWNBIAAAAAAAAAAMwCAAAS" +
+ "AAAAcAAAAAcAAAC4AAAAAwAAANQAAAACAAAA+AAAAAMAAAAIAQAAAQAAACABAABQAgAAQAEAAEAB" +
+ "AABIAQAASwEAAGQBAABzAQAAlwEAALcBAADLAQAA3wEAAO0BAAD4AQAA+wEAAAACAAANAgAAGAIA" +
+ "AB4CAAAlAgAALAIAAAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAoAAAABAAAABQAAAAAAAAAKAAAA" +
+ "BgAAAAAAAAALAAAABgAAADQCAAAAAAUADwAAAAAABQAQAAAAAAACAAAAAAAAAAAADQAAAAQAAQAA" +
+ "AAAAAAAAAAAAAAAEAAAAAAAAAAgAAAC8AgAAjAIAAAAAAAAGPGluaXQ+AAFMABdMYXJ0L1Rlc3Q5" +
+ "MTckVHJhbnNmb3JtOwANTGFydC9UZXN0OTE3OwAiTGRhbHZpay9hbm5vdGF0aW9uL0VuY2xvc2lu" +
+ "Z0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABJMamF2YS9sYW5nL09iamVj" +
+ "dDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAMVGVzdDkxNy5qYXZhAAlUcmFuc2Zvcm0AAVYAA1ZMTAAL" +
+ "YWNjZXNzRmxhZ3MACWdldFJlc3VsdAAEbmFtZQAFdGFrZTEABXRha2UyAAV2YWx1ZQAAAgAAAAUA" +
+ "BQAJAgAABw4BAw8BAg8BAg8ADwAHDgAAAAADAAMAAQAAADwCAAAIAAAAcBACAAAAWwEAAFsCAQAO" +
+ "AAIAAQAAAAAATAIAAAMAAABUEAEAEQAAAAACAQEAAQEBAIGABNQEAQH0BAAAAgIBERgBAgMCDAQI" +
+ "DhcJAAIAAACgAgAApgIAALACAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAEAAAAAAAAAAQAAABIAAABw" +
+ "AAAAAgAAAAcAAAC4AAAAAwAAAAMAAADUAAAABAAAAAIAAAD4AAAABQAAAAMAAAAIAQAABgAAAAEA" +
+ "AAAgAQAAAiAAABIAAABAAQAAARAAAAEAAAA0AgAAAyAAAAIAAAA8AgAAASAAAAIAAABUAgAAACAA" +
+ "AAEAAACMAgAABCAAAAIAAACgAgAAAxAAAAEAAACwAgAABiAAAAEAAAC8AgAAABAAAAEAAADMAgAA");
+
+ public static void run() {
+ Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+ doTest(new Transform("Hello", "Goodbye"), new Transform("start", "end"));
}
- }
-
- // base64 encoded class/dex file for
- // static class Transform {
- // public String take1;
- // public String take2;
- //
- // public Transform(String a, String b) {
- // take1 = a;
- // take2 = b;
- // }
- //
- // public String getResult() {
- // return take2;
- // }
- // }
- private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
- "yv66vgAAADQAGwoABQARCQAEABIJAAQAEwcAFQcAGAEABXRha2UxAQASTGphdmEvbGFuZy9TdHJp" +
- "bmc7AQAFdGFrZTIBAAY8aW5pdD4BACcoTGphdmEvbGFuZy9TdHJpbmc7TGphdmEvbGFuZy9TdHJp" +
- "bmc7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAJZ2V0UmVzdWx0AQAUKClMamF2YS9sYW5n" +
- "L1N0cmluZzsBAApTb3VyY2VGaWxlAQAMVGVzdDkxNy5qYXZhDAAJABkMAAYABwwACAAHBwAaAQAV" +
- "YXJ0L1Rlc3Q5MTckVHJhbnNmb3JtAQAJVHJhbnNmb3JtAQAMSW5uZXJDbGFzc2VzAQAQamF2YS9s" +
- "YW5nL09iamVjdAEAAygpVgEAC2FydC9UZXN0OTE3ACAABAAFAAAAAgABAAYABwAAAAEACAAHAAAA" +
- "AgABAAkACgABAAsAAAAzAAIAAwAAAA8qtwABKiu1AAIqLLUAA7EAAAABAAwAAAASAAQAAAAJAAQA" +
- "CgAJAAsADgAMAAEADQAOAAEACwAAAB0AAQABAAAABSq0AAOwAAAAAQAMAAAABgABAAAADwACAA8A" +
- "AAACABAAFwAAAAoAAQAEABQAFgAI");
- private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
- "ZGV4CjAzNQBdcPySAAAAAAAAAAAAAAAAAAAAAAAAAACQAwAAcAAAAHhWNBIAAAAAAAAAAMwCAAAS" +
- "AAAAcAAAAAcAAAC4AAAAAwAAANQAAAACAAAA+AAAAAMAAAAIAQAAAQAAACABAABQAgAAQAEAAEAB" +
- "AABIAQAASwEAAGQBAABzAQAAlwEAALcBAADLAQAA3wEAAO0BAAD4AQAA+wEAAAACAAANAgAAGAIA" +
- "AB4CAAAlAgAALAIAAAIAAAADAAAABAAAAAUAAAAGAAAABwAAAAoAAAABAAAABQAAAAAAAAAKAAAA" +
- "BgAAAAAAAAALAAAABgAAADQCAAAAAAUADwAAAAAABQAQAAAAAAACAAAAAAAAAAAADQAAAAQAAQAA" +
- "AAAAAAAAAAAAAAAEAAAAAAAAAAgAAAC8AgAAjAIAAAAAAAAGPGluaXQ+AAFMABdMYXJ0L1Rlc3Q5" +
- "MTckVHJhbnNmb3JtOwANTGFydC9UZXN0OTE3OwAiTGRhbHZpay9hbm5vdGF0aW9uL0VuY2xvc2lu" +
- "Z0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ABJMamF2YS9sYW5nL09iamVj" +
- "dDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAMVGVzdDkxNy5qYXZhAAlUcmFuc2Zvcm0AAVYAA1ZMTAAL" +
- "YWNjZXNzRmxhZ3MACWdldFJlc3VsdAAEbmFtZQAFdGFrZTEABXRha2UyAAV2YWx1ZQAAAgAAAAUA" +
- "BQAJAgAABw4BAw8BAg8BAg8ADwAHDgAAAAADAAMAAQAAADwCAAAIAAAAcBACAAAAWwEAAFsCAQAO" +
- "AAIAAQAAAAAATAIAAAMAAABUEAEAEQAAAAACAQEAAQEBAIGABNQEAQH0BAAAAgIBERgBAgMCDAQI" +
- "DhcJAAIAAACgAgAApgIAALACAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAEAAAAAAAAAAQAAABIAAABw" +
- "AAAAAgAAAAcAAAC4AAAAAwAAAAMAAADUAAAABAAAAAIAAAD4AAAABQAAAAMAAAAIAQAABgAAAAEA" +
- "AAAgAQAAAiAAABIAAABAAQAAARAAAAEAAAA0AgAAAyAAAAIAAAA8AgAAASAAAAIAAABUAgAAACAA" +
- "AAEAAACMAgAABCAAAAIAAACgAgAAAxAAAAEAAACwAgAABiAAAAEAAAC8AgAAABAAAAEAAADMAgAA");
-
- public static void run() {
- Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
- doTest(new Transform("Hello", "Goodbye"),
- new Transform("start", "end"));
- }
-
- private static void printTransform(Transform t) {
- System.out.println("Result is " + t.getResult());
- System.out.println("take1 is " + t.take1);
- System.out.println("take2 is " + t.take2);
- }
- public static void doTest(Transform t1, Transform t2) {
- printTransform(t1);
- printTransform(t2);
- Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
- printTransform(t1);
- printTransform(t2);
- }
+ private static void printTransform(Transform t) {
+ System.out.println("Result is " + t.getResult());
+ System.out.println("take1 is " + t.take1);
+ System.out.println("take2 is " + t.take2);
+ }
+ public static void doTest(Transform t1, Transform t2) {
+ printTransform(t1);
+ printTransform(t2);
+ Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ printTransform(t1);
+ printTransform(t2);
+ }
}
diff --git a/test/918-fields/run b/test/918-fields/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/918-fields/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/918-fields/run.py b/test/918-fields/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/918-fields/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/918-fields/src/Main.java b/test/918-fields/src/Main.java
index e0ed65c..e9f9bc9 100644
--- a/test/918-fields/src/Main.java
+++ b/test/918-fields/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test918.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test918.run();
+ }
}
diff --git a/test/918-fields/src/art/Test918.java b/test/918-fields/src/art/Test918.java
index 5328b0b..966c093 100644
--- a/test/918-fields/src/art/Test918.java
+++ b/test/918-fields/src/art/Test918.java
@@ -21,58 +21,57 @@
import java.util.Arrays;
public class Test918 {
- public static void run() throws Exception {
- doTest();
- }
-
- public static void doTest() throws Exception {
- testField(Math.class, "PI");
- testField(InterruptedIOException.class, "bytesTransferred");
- testField(Foo.class, "this$0");
- testField(Bar.class, "VAL");
- testField(Generics.class, "generics");
- testField(Generics.class, "privateValue");
- }
-
- private static void testField(Class<?> base, String fieldName)
- throws Exception {
- Field f = base.getDeclaredField(fieldName);
- String[] result = getFieldName(f);
- System.out.println(Arrays.toString(result));
-
- Class<?> declClass = getFieldDeclaringClass(f);
- if (base != declClass) {
- throw new RuntimeException("Declaring class not equal: " + base + " vs " + declClass);
+ public static void run() throws Exception {
+ doTest();
}
- System.out.println(declClass);
- int modifiers = getFieldModifiers(f);
- if (modifiers != f.getModifiers()) {
- throw new RuntimeException("Modifiers not equal: " + f.getModifiers() + " vs " + modifiers);
+ public static void doTest() throws Exception {
+ testField(Math.class, "PI");
+ testField(InterruptedIOException.class, "bytesTransferred");
+ testField(Foo.class, "this$0");
+ testField(Bar.class, "VAL");
+ testField(Generics.class, "generics");
+ testField(Generics.class, "privateValue");
}
- System.out.println(modifiers);
- boolean synth = isFieldSynthetic(f);
- if (synth != f.isSynthetic()) {
- throw new RuntimeException("Synthetic not equal: " + f.isSynthetic() + " vs " + synth);
+ private static void testField(Class<?> base, String fieldName) throws Exception {
+ Field f = base.getDeclaredField(fieldName);
+ String[] result = getFieldName(f);
+ System.out.println(Arrays.toString(result));
+
+ Class<?> declClass = getFieldDeclaringClass(f);
+ if (base != declClass) {
+ throw new RuntimeException("Declaring class not equal: " + base + " vs " + declClass);
+ }
+ System.out.println(declClass);
+
+ int modifiers = getFieldModifiers(f);
+ if (modifiers != f.getModifiers()) {
+ throw new RuntimeException(
+ "Modifiers not equal: " + f.getModifiers() + " vs " + modifiers);
+ }
+ System.out.println(modifiers);
+
+ boolean synth = isFieldSynthetic(f);
+ if (synth != f.isSynthetic()) {
+ throw new RuntimeException("Synthetic not equal: " + f.isSynthetic() + " vs " + synth);
+ }
+ System.out.println(synth);
}
- System.out.println(synth);
- }
- private static native String[] getFieldName(Field f);
- private static native Class<?> getFieldDeclaringClass(Field f);
- private static native int getFieldModifiers(Field f);
- private static native boolean isFieldSynthetic(Field f);
+ private static native String[] getFieldName(Field f);
+ private static native Class<?> getFieldDeclaringClass(Field f);
+ private static native int getFieldModifiers(Field f);
+ private static native boolean isFieldSynthetic(Field f);
- private class Foo {
- }
+ private class Foo {}
- private static interface Bar {
- public static int VAL = 1;
- }
+ private static interface Bar {
+ public static int VAL = 1;
+ }
- private static class Generics<T> {
- T generics;
- private int privateValue = 42;
- }
+ private static class Generics<T> {
+ T generics;
+ private int privateValue = 42;
+ }
}
diff --git a/test/919-obsolete-fields/run b/test/919-obsolete-fields/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/919-obsolete-fields/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/919-obsolete-fields/run.py b/test/919-obsolete-fields/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/919-obsolete-fields/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/919-obsolete-fields/src/Main.java b/test/919-obsolete-fields/src/Main.java
index 10eadb2..bb08324 100644
--- a/test/919-obsolete-fields/src/Main.java
+++ b/test/919-obsolete-fields/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test919.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test919.run();
+ }
}
diff --git a/test/919-obsolete-fields/src/art/Test919.java b/test/919-obsolete-fields/src/art/Test919.java
index 11971ef..0ae5305 100644
--- a/test/919-obsolete-fields/src/art/Test919.java
+++ b/test/919-obsolete-fields/src/art/Test919.java
@@ -20,154 +20,153 @@
import java.util.Base64;
public class Test919 {
+ static class Transform {
+ private Consumer<String> reporter;
+ public Transform(Consumer<String> reporter) {
+ this.reporter = reporter;
+ }
- static class Transform {
- private Consumer<String> reporter;
- public Transform(Consumer<String> reporter) {
- this.reporter = reporter;
+ private void Start() {
+ reporter.accept("hello - private");
+ }
+
+ private void Finish() {
+ reporter.accept("goodbye - private");
+ }
+
+ public void sayHi(Runnable r) {
+ reporter.accept("Pre Start private method call");
+ Start();
+ reporter.accept("Post Start private method call");
+ r.run();
+ reporter.accept("Pre Finish private method call");
+ Finish();
+ reporter.accept("Post Finish private method call");
+ }
}
- private void Start() {
- reporter.accept("hello - private");
+
+ // What follows is the base64 encoded representation of the following class:
+ //
+ // import java.util.function.Consumer;
+ //
+ // static class Transform {
+ // private Consumer<String> reporter;
+ // public Transform(Consumer<String> reporter) {
+ // this.reporter = reporter;
+ // }
+ //
+ // private void Start() {
+ // reporter.accept("Hello - private - Transformed");
+ // }
+ //
+ // private void Finish() {
+ // reporter.accept("Goodbye - private - Transformed");
+ // }
+ //
+ // public void sayHi(Runnable r) {
+ // reporter.accept("pre Start private method call - Transformed");
+ // Start();
+ // reporter.accept("post Start private method call - Transformed");
+ // r.run();
+ // reporter.accept("pre Finish private method call - Transformed");
+ // Finish();
+ // reporter.accept("post Finish private method call - Transformed");
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAOAoADgAfCQANACAIACELACIAIwgAJAgAJQoADQAmCAAnCwAoACkIACoKAA0AKwgA" +
+ "LAcALgcAMQEACHJlcG9ydGVyAQAdTGphdmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcjsBAAlTaWdu" +
+ "YXR1cmUBADFMamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyPExqYXZhL2xhbmcvU3RyaW5nOz47" +
+ "AQAGPGluaXQ+AQAgKExqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI7KVYBAARDb2RlAQAPTGlu" +
+ "ZU51bWJlclRhYmxlAQA0KExqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI8TGphdmEvbGFuZy9T" +
+ "dHJpbmc7PjspVgEABVN0YXJ0AQADKClWAQAGRmluaXNoAQAFc2F5SGkBABcoTGphdmEvbGFuZy9S" +
+ "dW5uYWJsZTspVgEAClNvdXJjZUZpbGUBAAxUZXN0OTE5LmphdmEMABMAGQwADwAQAQAdSGVsbG8g" +
+ "LSBwcml2YXRlIC0gVHJhbnNmb3JtZWQHADIMADMANAEAH0dvb2RieWUgLSBwcml2YXRlIC0gVHJh" +
+ "bnNmb3JtZWQBACtwcmUgU3RhcnQgcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkDAAY" +
+ "ABkBACxwb3N0IFN0YXJ0IHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAcANQwANgAZ" +
+ "AQAscHJlIEZpbmlzaCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQMABoAGQEALXBv" +
+ "c3QgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAcANwEAFWFydC9UZXN0" +
+ "OTE5JFRyYW5zZm9ybQEACVRyYW5zZm9ybQEADElubmVyQ2xhc3NlcwEAEGphdmEvbGFuZy9PYmpl" +
+ "Y3QBABtqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXIBAAZhY2NlcHQBABUoTGphdmEvbGFuZy9P" +
+ "YmplY3Q7KVYBABJqYXZhL2xhbmcvUnVubmFibGUBAANydW4BAAthcnQvVGVzdDkxOQAgAA0ADgAA" +
+ "AAEAAgAPABAAAQARAAAAAgASAAQAAQATABQAAgAVAAAAKgACAAIAAAAKKrcAASortQACsQAAAAEA" +
+ "FgAAAA4AAwAAAAgABAAJAAkACgARAAAAAgAXAAIAGAAZAAEAFQAAACgAAgABAAAADCq0AAISA7kA" +
+ "BAIAsQAAAAEAFgAAAAoAAgAAAA0ACwAOAAIAGgAZAAEAFQAAACgAAgABAAAADCq0AAISBbkABAIA" +
+ "sQAAAAEAFgAAAAoAAgAAABEACwASAAEAGwAcAAEAFQAAAG8AAgACAAAAOyq0AAISBrkABAIAKrcA" +
+ "Byq0AAISCLkABAIAK7kACQEAKrQAAhIKuQAEAgAqtwALKrQAAhIMuQAEAgCxAAAAAQAWAAAAIgAI" +
+ "AAAAFQALABYADwAXABoAGAAgABkAKwAaAC8AGwA6ABwAAgAdAAAAAgAeADAAAAAKAAEADQAtAC8A" +
+ "CA==");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQBeEZYBAAAAAAAAAAAAAAAAAAAAAAAAAACMBgAAcAAAAHhWNBIAAAAAAAAAAMgFAAAi" +
+ "AAAAcAAAAAkAAAD4AAAABAAAABwBAAABAAAATAEAAAcAAABUAQAAAQAAAIwBAADgBAAArAEAAKwB" +
+ "AACvAQAAsgEAALoBAAC+AQAAxAEAAMwBAADtAQAADAIAACUCAAA0AgAAWAIAAHgCAACXAgAAqwIA" +
+ "AMECAADVAgAA8wIAABIDAAAZAwAAJwMAADIDAAA1AwAAOQMAAEEDAABOAwAAVAMAAIMDAACxAwAA" +
+ "3wMAAAwEAAAWBAAAGwQAACIEAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAAOAAAAEQAAABUAAAAV" +
+ "AAAACAAAAAAAAAAWAAAACAAAADQEAAAWAAAACAAAADwEAAAWAAAACAAAACwEAAAAAAcAHgAAAAAA" +
+ "AwACAAAAAAAAAAUAAAAAAAAAEgAAAAAAAgAgAAAABQAAAAIAAAAGAAAAHwAAAAcAAQAXAAAAAAAA" +
+ "AAAAAAAFAAAAAAAAABMAAACoBQAARAUAAAAAAAABKAABPAAGPGluaXQ+AAI+OwAEPjspVgAGRmlu" +
+ "aXNoAB9Hb29kYnllIC0gcHJpdmF0ZSAtIFRyYW5zZm9ybWVkAB1IZWxsbyAtIHByaXZhdGUgLSBU" +
+ "cmFuc2Zvcm1lZAAXTGFydC9UZXN0OTE5JFRyYW5zZm9ybTsADUxhcnQvVGVzdDkxOTsAIkxkYWx2" +
+ "aWsvYW5ub3RhdGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNs" +
+ "YXNzOwAdTGRhbHZpay9hbm5vdGF0aW9uL1NpZ25hdHVyZTsAEkxqYXZhL2xhbmcvT2JqZWN0OwAU" +
+ "TGphdmEvbGFuZy9SdW5uYWJsZTsAEkxqYXZhL2xhbmcvU3RyaW5nOwAcTGphdmEvdXRpbC9mdW5j" +
+ "dGlvbi9Db25zdW1lcgAdTGphdmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcjsABVN0YXJ0AAxUZXN0" +
+ "OTE5LmphdmEACVRyYW5zZm9ybQABVgACVkwABmFjY2VwdAALYWNjZXNzRmxhZ3MABG5hbWUALXBv" +
+ "c3QgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAAscG9zdCBTdGFydCBw" +
+ "cml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQALHByZSBGaW5pc2ggcHJpdmF0ZSBtZXRo" +
+ "b2QgY2FsbCAtIFRyYW5zZm9ybWVkACtwcmUgU3RhcnQgcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRy" +
+ "YW5zZm9ybWVkAAhyZXBvcnRlcgADcnVuAAVzYXlIaQAFdmFsdWUAAAAAAQAAAAcAAAABAAAABQAA" +
+ "AAEAAAAGAAAACAEABw4BAw8BAg8AEQAHDgEIDwANAAcOAQgPABUBAAcOAQgPAQMPAQgPAQMPAQgP" +
+ "AQMPAQgPAAACAAIAAQAAAEQEAAAGAAAAcBAEAAAAWwEAAA4AAwABAAIAAABQBAAACQAAAFQgAAAb" +
+ "AQYAAAByIAYAEAAOAAAAAwABAAIAAABYBAAACQAAAFQgAAAbAQcAAAByIAYAEAAOAAAABAACAAIA" +
+ "AABgBAAAKgAAAFQgAAAbAR0AAAByIAYAEABwEAIAAgBUIAAAGwEbAAAAciAGABAAchAFAAMAVCAA" +
+ "ABsBHAAAAHIgBgAQAHAQAQACAFQgAAAbARoAAAByIAYAEAAOAAABAwEAAgCBgAT8CAECmAkBArwJ" +
+ "AwHgCQICASEYAQIDAhgECBkXFAIEASEcBBcQFwEXDxcDAgQBIRwFFwAXEBcBFw8XBAAAAAIAAABc" +
+ "BQAAYgUAAAEAAABrBQAAAQAAAHkFAACMBQAAAQAAAAEAAAAAAAAAAAAAAJgFAAAAAAAAoAUAABAA" +
+ "AAAAAAAAAQAAAAAAAAABAAAAIgAAAHAAAAACAAAACQAAAPgAAAADAAAABAAAABwBAAAEAAAAAQAA" +
+ "AEwBAAAFAAAABwAAAFQBAAAGAAAAAQAAAIwBAAACIAAAIgAAAKwBAAABEAAAAwAAACwEAAADIAAA" +
+ "BAAAAEQEAAABIAAABAAAAHwEAAAAIAAAAQAAAEQFAAAEIAAABAAAAFwFAAADEAAAAwAAAIwFAAAG" +
+ "IAAAAQAAAKgFAAAAEAAAAQAAAMgFAAA=");
+
+ // A class that we can use to keep track of the output of this test.
+ private static class TestWatcher implements Consumer<String> {
+ private StringBuilder sb;
+ public TestWatcher() {
+ sb = new StringBuilder();
+ }
+
+ @Override
+ public void accept(String s) {
+ sb.append(s);
+ sb.append('\n');
+ }
+
+ public String getOutput() {
+ return sb.toString();
+ }
}
- private void Finish() {
- reporter.accept("goodbye - private");
+ public static void run() {
+ Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
+ TestWatcher w = new TestWatcher();
+ doTest(new Transform(w), w);
}
- public void sayHi(Runnable r) {
- reporter.accept("Pre Start private method call");
- Start();
- reporter.accept("Post Start private method call");
- r.run();
- reporter.accept("Pre Finish private method call");
- Finish();
- reporter.accept("Post Finish private method call");
+ public static void doTest(Transform t, TestWatcher w) {
+ Runnable do_redefinition = () -> {
+ w.accept("transforming calling function");
+ Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ };
+ // This just prints something out to show we are running the Runnable.
+ Runnable say_nothing = () -> { w.accept("Not doing anything here"); };
+
+ // Try and redefine.
+ t.sayHi(say_nothing);
+ t.sayHi(do_redefinition);
+ t.sayHi(say_nothing);
+
+ // Print output of last run.
+ System.out.print(w.getOutput());
}
- }
-
-
- // What follows is the base64 encoded representation of the following class:
- //
- // import java.util.function.Consumer;
- //
- // static class Transform {
- // private Consumer<String> reporter;
- // public Transform(Consumer<String> reporter) {
- // this.reporter = reporter;
- // }
- //
- // private void Start() {
- // reporter.accept("Hello - private - Transformed");
- // }
- //
- // private void Finish() {
- // reporter.accept("Goodbye - private - Transformed");
- // }
- //
- // public void sayHi(Runnable r) {
- // reporter.accept("pre Start private method call - Transformed");
- // Start();
- // reporter.accept("post Start private method call - Transformed");
- // r.run();
- // reporter.accept("pre Finish private method call - Transformed");
- // Finish();
- // reporter.accept("post Finish private method call - Transformed");
- // }
- // }
- private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
- "yv66vgAAADQAOAoADgAfCQANACAIACELACIAIwgAJAgAJQoADQAmCAAnCwAoACkIACoKAA0AKwgA" +
- "LAcALgcAMQEACHJlcG9ydGVyAQAdTGphdmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcjsBAAlTaWdu" +
- "YXR1cmUBADFMamF2YS91dGlsL2Z1bmN0aW9uL0NvbnN1bWVyPExqYXZhL2xhbmcvU3RyaW5nOz47" +
- "AQAGPGluaXQ+AQAgKExqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI7KVYBAARDb2RlAQAPTGlu" +
- "ZU51bWJlclRhYmxlAQA0KExqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXI8TGphdmEvbGFuZy9T" +
- "dHJpbmc7PjspVgEABVN0YXJ0AQADKClWAQAGRmluaXNoAQAFc2F5SGkBABcoTGphdmEvbGFuZy9S" +
- "dW5uYWJsZTspVgEAClNvdXJjZUZpbGUBAAxUZXN0OTE5LmphdmEMABMAGQwADwAQAQAdSGVsbG8g" +
- "LSBwcml2YXRlIC0gVHJhbnNmb3JtZWQHADIMADMANAEAH0dvb2RieWUgLSBwcml2YXRlIC0gVHJh" +
- "bnNmb3JtZWQBACtwcmUgU3RhcnQgcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRyYW5zZm9ybWVkDAAY" +
- "ABkBACxwb3N0IFN0YXJ0IHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAcANQwANgAZ" +
- "AQAscHJlIEZpbmlzaCBwcml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQMABoAGQEALXBv" +
- "c3QgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAcANwEAFWFydC9UZXN0" +
- "OTE5JFRyYW5zZm9ybQEACVRyYW5zZm9ybQEADElubmVyQ2xhc3NlcwEAEGphdmEvbGFuZy9PYmpl" +
- "Y3QBABtqYXZhL3V0aWwvZnVuY3Rpb24vQ29uc3VtZXIBAAZhY2NlcHQBABUoTGphdmEvbGFuZy9P" +
- "YmplY3Q7KVYBABJqYXZhL2xhbmcvUnVubmFibGUBAANydW4BAAthcnQvVGVzdDkxOQAgAA0ADgAA" +
- "AAEAAgAPABAAAQARAAAAAgASAAQAAQATABQAAgAVAAAAKgACAAIAAAAKKrcAASortQACsQAAAAEA" +
- "FgAAAA4AAwAAAAgABAAJAAkACgARAAAAAgAXAAIAGAAZAAEAFQAAACgAAgABAAAADCq0AAISA7kA" +
- "BAIAsQAAAAEAFgAAAAoAAgAAAA0ACwAOAAIAGgAZAAEAFQAAACgAAgABAAAADCq0AAISBbkABAIA" +
- "sQAAAAEAFgAAAAoAAgAAABEACwASAAEAGwAcAAEAFQAAAG8AAgACAAAAOyq0AAISBrkABAIAKrcA" +
- "Byq0AAISCLkABAIAK7kACQEAKrQAAhIKuQAEAgAqtwALKrQAAhIMuQAEAgCxAAAAAQAWAAAAIgAI" +
- "AAAAFQALABYADwAXABoAGAAgABkAKwAaAC8AGwA6ABwAAgAdAAAAAgAeADAAAAAKAAEADQAtAC8A" +
- "CA==");
- private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
- "ZGV4CjAzNQBeEZYBAAAAAAAAAAAAAAAAAAAAAAAAAACMBgAAcAAAAHhWNBIAAAAAAAAAAMgFAAAi" +
- "AAAAcAAAAAkAAAD4AAAABAAAABwBAAABAAAATAEAAAcAAABUAQAAAQAAAIwBAADgBAAArAEAAKwB" +
- "AACvAQAAsgEAALoBAAC+AQAAxAEAAMwBAADtAQAADAIAACUCAAA0AgAAWAIAAHgCAACXAgAAqwIA" +
- "AMECAADVAgAA8wIAABIDAAAZAwAAJwMAADIDAAA1AwAAOQMAAEEDAABOAwAAVAMAAIMDAACxAwAA" +
- "3wMAAAwEAAAWBAAAGwQAACIEAAAIAAAACQAAAAoAAAALAAAADAAAAA0AAAAOAAAAEQAAABUAAAAV" +
- "AAAACAAAAAAAAAAWAAAACAAAADQEAAAWAAAACAAAADwEAAAWAAAACAAAACwEAAAAAAcAHgAAAAAA" +
- "AwACAAAAAAAAAAUAAAAAAAAAEgAAAAAAAgAgAAAABQAAAAIAAAAGAAAAHwAAAAcAAQAXAAAAAAAA" +
- "AAAAAAAFAAAAAAAAABMAAACoBQAARAUAAAAAAAABKAABPAAGPGluaXQ+AAI+OwAEPjspVgAGRmlu" +
- "aXNoAB9Hb29kYnllIC0gcHJpdmF0ZSAtIFRyYW5zZm9ybWVkAB1IZWxsbyAtIHByaXZhdGUgLSBU" +
- "cmFuc2Zvcm1lZAAXTGFydC9UZXN0OTE5JFRyYW5zZm9ybTsADUxhcnQvVGVzdDkxOTsAIkxkYWx2" +
- "aWsvYW5ub3RhdGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNs" +
- "YXNzOwAdTGRhbHZpay9hbm5vdGF0aW9uL1NpZ25hdHVyZTsAEkxqYXZhL2xhbmcvT2JqZWN0OwAU" +
- "TGphdmEvbGFuZy9SdW5uYWJsZTsAEkxqYXZhL2xhbmcvU3RyaW5nOwAcTGphdmEvdXRpbC9mdW5j" +
- "dGlvbi9Db25zdW1lcgAdTGphdmEvdXRpbC9mdW5jdGlvbi9Db25zdW1lcjsABVN0YXJ0AAxUZXN0" +
- "OTE5LmphdmEACVRyYW5zZm9ybQABVgACVkwABmFjY2VwdAALYWNjZXNzRmxhZ3MABG5hbWUALXBv" +
- "c3QgRmluaXNoIHByaXZhdGUgbWV0aG9kIGNhbGwgLSBUcmFuc2Zvcm1lZAAscG9zdCBTdGFydCBw" +
- "cml2YXRlIG1ldGhvZCBjYWxsIC0gVHJhbnNmb3JtZWQALHByZSBGaW5pc2ggcHJpdmF0ZSBtZXRo" +
- "b2QgY2FsbCAtIFRyYW5zZm9ybWVkACtwcmUgU3RhcnQgcHJpdmF0ZSBtZXRob2QgY2FsbCAtIFRy" +
- "YW5zZm9ybWVkAAhyZXBvcnRlcgADcnVuAAVzYXlIaQAFdmFsdWUAAAAAAQAAAAcAAAABAAAABQAA" +
- "AAEAAAAGAAAACAEABw4BAw8BAg8AEQAHDgEIDwANAAcOAQgPABUBAAcOAQgPAQMPAQgPAQMPAQgP" +
- "AQMPAQgPAAACAAIAAQAAAEQEAAAGAAAAcBAEAAAAWwEAAA4AAwABAAIAAABQBAAACQAAAFQgAAAb" +
- "AQYAAAByIAYAEAAOAAAAAwABAAIAAABYBAAACQAAAFQgAAAbAQcAAAByIAYAEAAOAAAABAACAAIA" +
- "AABgBAAAKgAAAFQgAAAbAR0AAAByIAYAEABwEAIAAgBUIAAAGwEbAAAAciAGABAAchAFAAMAVCAA" +
- "ABsBHAAAAHIgBgAQAHAQAQACAFQgAAAbARoAAAByIAYAEAAOAAABAwEAAgCBgAT8CAECmAkBArwJ" +
- "AwHgCQICASEYAQIDAhgECBkXFAIEASEcBBcQFwEXDxcDAgQBIRwFFwAXEBcBFw8XBAAAAAIAAABc" +
- "BQAAYgUAAAEAAABrBQAAAQAAAHkFAACMBQAAAQAAAAEAAAAAAAAAAAAAAJgFAAAAAAAAoAUAABAA" +
- "AAAAAAAAAQAAAAAAAAABAAAAIgAAAHAAAAACAAAACQAAAPgAAAADAAAABAAAABwBAAAEAAAAAQAA" +
- "AEwBAAAFAAAABwAAAFQBAAAGAAAAAQAAAIwBAAACIAAAIgAAAKwBAAABEAAAAwAAACwEAAADIAAA" +
- "BAAAAEQEAAABIAAABAAAAHwEAAAAIAAAAQAAAEQFAAAEIAAABAAAAFwFAAADEAAAAwAAAIwFAAAG" +
- "IAAAAQAAAKgFAAAAEAAAAQAAAMgFAAA=");
-
- // A class that we can use to keep track of the output of this test.
- private static class TestWatcher implements Consumer<String> {
- private StringBuilder sb;
- public TestWatcher() {
- sb = new StringBuilder();
- }
-
- @Override
- public void accept(String s) {
- sb.append(s);
- sb.append('\n');
- }
-
- public String getOutput() {
- return sb.toString();
- }
- }
-
- public static void run() {
- Redefinition.setTestConfiguration(Redefinition.Config.COMMON_REDEFINE);
- TestWatcher w = new TestWatcher();
- doTest(new Transform(w), w);
- }
-
- public static void doTest(Transform t, TestWatcher w) {
- Runnable do_redefinition = () -> {
- w.accept("transforming calling function");
- Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
- };
- // This just prints something out to show we are running the Runnable.
- Runnable say_nothing = () -> { w.accept("Not doing anything here"); };
-
- // Try and redefine.
- t.sayHi(say_nothing);
- t.sayHi(do_redefinition);
- t.sayHi(say_nothing);
-
- // Print output of last run.
- System.out.print(w.getOutput());
- }
}
diff --git a/test/920-objects/run b/test/920-objects/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/920-objects/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/920-objects/run.py b/test/920-objects/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/920-objects/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/921-hello-failure/run b/test/921-hello-failure/run
deleted file mode 100755
index 8be0ed4..0000000
--- a/test/921-hello-failure/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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/921-hello-failure/run.py b/test/921-hello-failure/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/921-hello-failure/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/922-properties/run b/test/922-properties/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/922-properties/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/922-properties/run.py b/test/922-properties/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/922-properties/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/923-monitors/run b/test/923-monitors/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/923-monitors/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/923-monitors/run.py b/test/923-monitors/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/923-monitors/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/924-threads/run b/test/924-threads/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/924-threads/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/924-threads/run.py b/test/924-threads/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/924-threads/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/925-threadgroups/run b/test/925-threadgroups/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/925-threadgroups/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/925-threadgroups/run.py b/test/925-threadgroups/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/925-threadgroups/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/926-multi-obsolescence/run b/test/926-multi-obsolescence/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/926-multi-obsolescence/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/926-multi-obsolescence/run.py b/test/926-multi-obsolescence/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/926-multi-obsolescence/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/927-timers/run b/test/927-timers/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/927-timers/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/927-timers/run.py b/test/927-timers/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/927-timers/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/928-jni-table/run b/test/928-jni-table/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/928-jni-table/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/928-jni-table/run.py b/test/928-jni-table/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/928-jni-table/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/929-search/run b/test/929-search/run
deleted file mode 100755
index fb6b1b8..0000000
--- a/test/929-search/run
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/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.
-
-# This test checks whether dex files can be injected into parent classloaders. App images preload
-# classes, which will make the injection moot. Turn off app images to avoid the issue.
-# Pass the correct `--secondary-class-loader-context` for the "-ex" jar.
-
-./default-run "$@" --jvmti \
- --no-app-image \
- --secondary-class-loader-context "PCL[$DEX_LOCATION/$TEST_NAME.jar]"
diff --git a/test/929-search/run.py b/test/929-search/run.py
new file mode 100644
index 0000000..012e279
--- /dev/null
+++ b/test/929-search/run.py
@@ -0,0 +1,25 @@
+#!/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.
+
+
+def run(ctx, args):
+ # This test checks whether dex files can be injected into parent classloaders. App images preload
+ # classes, which will make the injection moot. Turn off app images to avoid the issue.
+ # Pass the correct `--secondary-class-loader-context` for the "-ex" jar.
+
+ pcl = f"PCL[{ctx.env.DEX_LOCATION}/{ctx.env.TEST_NAME}.jar]"
+ ctx.default_run(
+ args, jvmti=True, app_image=False, secondary_class_loader_context=pcl)
diff --git a/test/930-hello-retransform/run b/test/930-hello-retransform/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/930-hello-retransform/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/930-hello-retransform/run.py b/test/930-hello-retransform/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/930-hello-retransform/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/931-agent-thread/run b/test/931-agent-thread/run
deleted file mode 100755
index 67923a7..0000000
--- a/test/931-agent-thread/run
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/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.
-
-# This test checks whether dex files can be injected into parent classloaders. App images preload
-# classes, which will make the injection moot. Turn off app images to avoid the issue.
-
-./default-run "$@" --jvmti \
- --no-app-image
diff --git a/test/931-agent-thread/run.py b/test/931-agent-thread/run.py
new file mode 100644
index 0000000..4ac9127
--- /dev/null
+++ b/test/931-agent-thread/run.py
@@ -0,0 +1,22 @@
+#!/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.
+
+
+def run(ctx, args):
+ # This test checks whether dex files can be injected into parent classloaders. App images preload
+ # classes, which will make the injection moot. Turn off app images to avoid the issue.
+
+ ctx.default_run(args, jvmti=True, app_image=False)
diff --git a/test/932-transform-saves/run b/test/932-transform-saves/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/932-transform-saves/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/932-transform-saves/run.py b/test/932-transform-saves/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/932-transform-saves/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/933-misc-events/run b/test/933-misc-events/run
deleted file mode 100755
index 67923a7..0000000
--- a/test/933-misc-events/run
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/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.
-
-# This test checks whether dex files can be injected into parent classloaders. App images preload
-# classes, which will make the injection moot. Turn off app images to avoid the issue.
-
-./default-run "$@" --jvmti \
- --no-app-image
diff --git a/test/933-misc-events/run.py b/test/933-misc-events/run.py
new file mode 100644
index 0000000..5a2efa9
--- /dev/null
+++ b/test/933-misc-events/run.py
@@ -0,0 +1,21 @@
+#!/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.
+
+
+def run(ctx, args):
+ # This test checks whether dex files can be injected into parent classloaders. App images preload
+ # classes, which will make the injection moot. Turn off app images to avoid the issue.
+ ctx.default_run(args, jvmti=True, app_image=False)
diff --git a/test/934-load-transform/run b/test/934-load-transform/run
deleted file mode 100755
index adb1a1c..0000000
--- a/test/934-load-transform/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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 --no-app-image
diff --git a/test/934-load-transform/run.py b/test/934-load-transform/run.py
new file mode 100644
index 0000000..23b8dc3
--- /dev/null
+++ b/test/934-load-transform/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, app_image=False)
diff --git a/test/935-non-retransformable/run b/test/935-non-retransformable/run
deleted file mode 100755
index adb1a1c..0000000
--- a/test/935-non-retransformable/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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 --no-app-image
diff --git a/test/935-non-retransformable/run.py b/test/935-non-retransformable/run.py
new file mode 100644
index 0000000..23b8dc3
--- /dev/null
+++ b/test/935-non-retransformable/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, app_image=False)
diff --git a/test/936-search-onload/run b/test/936-search-onload/run
deleted file mode 100755
index fb6b1b8..0000000
--- a/test/936-search-onload/run
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/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.
-
-# This test checks whether dex files can be injected into parent classloaders. App images preload
-# classes, which will make the injection moot. Turn off app images to avoid the issue.
-# Pass the correct `--secondary-class-loader-context` for the "-ex" jar.
-
-./default-run "$@" --jvmti \
- --no-app-image \
- --secondary-class-loader-context "PCL[$DEX_LOCATION/$TEST_NAME.jar]"
diff --git a/test/936-search-onload/run.py b/test/936-search-onload/run.py
new file mode 100644
index 0000000..012e279
--- /dev/null
+++ b/test/936-search-onload/run.py
@@ -0,0 +1,25 @@
+#!/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.
+
+
+def run(ctx, args):
+ # This test checks whether dex files can be injected into parent classloaders. App images preload
+ # classes, which will make the injection moot. Turn off app images to avoid the issue.
+ # Pass the correct `--secondary-class-loader-context` for the "-ex" jar.
+
+ pcl = f"PCL[{ctx.env.DEX_LOCATION}/{ctx.env.TEST_NAME}.jar]"
+ ctx.default_run(
+ args, jvmti=True, app_image=False, secondary_class_loader_context=pcl)
diff --git a/test/937-hello-retransform-package/run b/test/937-hello-retransform-package/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/937-hello-retransform-package/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/937-hello-retransform-package/run.py b/test/937-hello-retransform-package/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/937-hello-retransform-package/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/938-load-transform-bcp/run b/test/938-load-transform-bcp/run
deleted file mode 100755
index adb1a1c..0000000
--- a/test/938-load-transform-bcp/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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 --no-app-image
diff --git a/test/938-load-transform-bcp/run.py b/test/938-load-transform-bcp/run.py
new file mode 100644
index 0000000..23b8dc3
--- /dev/null
+++ b/test/938-load-transform-bcp/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True, app_image=False)
diff --git a/test/939-hello-transformation-bcp/run b/test/939-hello-transformation-bcp/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/939-hello-transformation-bcp/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/939-hello-transformation-bcp/run.py b/test/939-hello-transformation-bcp/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/939-hello-transformation-bcp/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/940-recursive-obsolete/run b/test/940-recursive-obsolete/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/940-recursive-obsolete/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/940-recursive-obsolete/run.py b/test/940-recursive-obsolete/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/940-recursive-obsolete/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/941-recursive-obsolete-jit/run b/test/941-recursive-obsolete-jit/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/941-recursive-obsolete-jit/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/941-recursive-obsolete-jit/run.py b/test/941-recursive-obsolete-jit/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/941-recursive-obsolete-jit/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/942-private-recursive/run b/test/942-private-recursive/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/942-private-recursive/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/942-private-recursive/run.py b/test/942-private-recursive/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/942-private-recursive/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/943-private-recursive-jit/run b/test/943-private-recursive-jit/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/943-private-recursive-jit/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/943-private-recursive-jit/run.py b/test/943-private-recursive-jit/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/943-private-recursive-jit/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/944-transform-classloaders/run b/test/944-transform-classloaders/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/944-transform-classloaders/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/944-transform-classloaders/run.py b/test/944-transform-classloaders/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/944-transform-classloaders/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/945-obsolete-native/run b/test/945-obsolete-native/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/945-obsolete-native/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/945-obsolete-native/run.py b/test/945-obsolete-native/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/945-obsolete-native/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/946-obsolete-throw/run b/test/946-obsolete-throw/run
deleted file mode 100755
index e92b873..0000000
--- a/test/946-obsolete-throw/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/946-obsolete-throw/run.py b/test/946-obsolete-throw/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/946-obsolete-throw/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/947-reflect-method/run b/test/947-reflect-method/run
deleted file mode 100755
index e92b873..0000000
--- a/test/947-reflect-method/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/947-reflect-method/run.py b/test/947-reflect-method/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/947-reflect-method/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/948-change-annotations/build b/test/948-change-annotations/build
deleted file mode 100755
index 898e2e5..0000000
--- a/test/948-change-annotations/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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-build "$@" --experimental agents
diff --git a/test/948-change-annotations/build.py b/test/948-change-annotations/build.py
new file mode 100644
index 0000000..1613d3a
--- /dev/null
+++ b/test/948-change-annotations/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(api_level="agents")
diff --git a/test/948-change-annotations/run b/test/948-change-annotations/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/948-change-annotations/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/948-change-annotations/run.py b/test/948-change-annotations/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/948-change-annotations/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/949-in-memory-transform/run b/test/949-in-memory-transform/run
deleted file mode 100755
index e92b873..0000000
--- a/test/949-in-memory-transform/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/949-in-memory-transform/run.py b/test/949-in-memory-transform/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/949-in-memory-transform/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/950-redefine-intrinsic/run b/test/950-redefine-intrinsic/run
deleted file mode 100755
index e92b873..0000000
--- a/test/950-redefine-intrinsic/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/950-redefine-intrinsic/run.py b/test/950-redefine-intrinsic/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/950-redefine-intrinsic/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/951-threaded-obsolete/run b/test/951-threaded-obsolete/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/951-threaded-obsolete/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/951-threaded-obsolete/run.py b/test/951-threaded-obsolete/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/951-threaded-obsolete/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/952-invoke-custom/build b/test/952-invoke-custom/build
deleted file mode 100755
index e835517..0000000
--- a/test/952-invoke-custom/build
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# Stop on failure.
-set -e
-
-export ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-9.2.jar"
-
-# Build the transformer to apply to compiled classes.
-mkdir classes
-${JAVAC:-javac} ${JAVAC_ARGS} -cp "${ASM_JAR}" -d classes $(find util-src -name '*.java')
-${SOONG_ZIP} --jar -o transformer.jar -C classes -D classes
-rm -rf classes
-
-# Use API level 28 for invoke-custom bytecode support.
-USE_DESUGAR=false ./default-build "$@" --api-level 28
diff --git a/test/952-invoke-custom/build.py b/test/952-invoke-custom/build.py
new file mode 100644
index 0000000..166d3a8
--- /dev/null
+++ b/test/952-invoke-custom/build.py
@@ -0,0 +1,21 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build(use_desugar=False,
+ api_level=28,
+ javac_classpath=[ctx.test_dir / "transformer.jar"])
diff --git a/test/952-invoke-custom/generate-sources b/test/952-invoke-custom/generate-sources
new file mode 100755
index 0000000..4244f8c
--- /dev/null
+++ b/test/952-invoke-custom/generate-sources
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+# Stop on failure.
+set -e
+
+export ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-9.2.jar"
+
+# Build the transformer to apply to compiled classes.
+mkdir classes
+${JAVAC:-javac} ${JAVAC_ARGS} -cp "${ASM_JAR}" -d classes $(find util-src -name '*.java')
+${SOONG_ZIP} --jar -o transformer.jar -C classes -D classes
+rm -rf classes
diff --git a/test/952-invoke-custom/javac_post.sh b/test/952-invoke-custom/javac_post.sh
new file mode 100755
index 0000000..be5d8cf
--- /dev/null
+++ b/test/952-invoke-custom/javac_post.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+set -e # Stop on error - the caller script may not have this set.
+
+export ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-9.2.jar"
+
+# Move original classes to intermediate location.
+mv classes intermediate-classes
+mkdir classes
+
+# Transform intermediate classes.
+transformer_args="-cp ${ASM_JAR}:transformer.jar transformer.IndyTransformer"
+for class in intermediate-classes/*.class ; do
+ transformed_class=classes/$(basename ${class})
+ ${JAVA:-java} ${transformer_args} $PWD/${class} ${transformed_class}
+done
diff --git a/test/952-invoke-custom/javac_wrapper.sh b/test/952-invoke-custom/javac_wrapper.sh
deleted file mode 100755
index 8659030..0000000
--- a/test/952-invoke-custom/javac_wrapper.sh
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-set -e # Stop on error - the caller script may not have this set.
-
-# Update arguments to add transformer and ASM to the compiler classpath.
-classpath="./transformer.jar:$ASM_JAR"
-args=(-cp $classpath)
-while [ $# -ne 0 ] ; do
- case $1 in
- -cp|-classpath|--class-path)
- shift
- shift
- ;;
- *)
- args+=("$1")
- shift
- ;;
- esac
-done
-
-# Compile.
-$JAVAC "${args[@]}"
-
-# Move original classes to intermediate location.
-mv classes intermediate-classes
-mkdir classes
-
-# Transform intermediate classes.
-transformer_args="-cp ${ASM_JAR}:transformer.jar transformer.IndyTransformer"
-for class in intermediate-classes/*.class ; do
- transformed_class=classes/$(basename ${class})
- ${JAVA:-java} ${transformer_args} $PWD/${class} ${transformed_class}
-done
diff --git a/test/952-invoke-custom/util-src/transformer/IndyTransformer.java b/test/952-invoke-custom/util-src/transformer/IndyTransformer.java
index 6401c54..ee04ff2 100644
--- a/test/952-invoke-custom/util-src/transformer/IndyTransformer.java
+++ b/test/952-invoke-custom/util-src/transformer/IndyTransformer.java
@@ -189,7 +189,7 @@
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
try (InputStream is = Files.newInputStream(inputClassPath)) {
ClassReader cr = new ClassReader(is);
- cr.accept(new BootstrapBuilder(Opcodes.ASM7, cw, callsiteMap), 0);
+ cr.accept(new BootstrapBuilder(Opcodes.ASM9, cw, callsiteMap), 0);
}
try (OutputStream os = Files.newOutputStream(outputClassPath)) {
os.write(cw.toByteArray());
diff --git a/test/953-invoke-polymorphic-compiler/build b/test/953-invoke-polymorphic-compiler/build
deleted file mode 100755
index 2b0b2c1..0000000
--- a/test/953-invoke-polymorphic-compiler/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/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.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental method-handles
diff --git a/test/953-invoke-polymorphic-compiler/build.py b/test/953-invoke-polymorphic-compiler/build.py
new file mode 100644
index 0000000..027dd53
--- /dev/null
+++ b/test/953-invoke-polymorphic-compiler/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(api_level="method-handles")
diff --git a/test/954-invoke-polymorphic-verifier/build b/test/954-invoke-polymorphic-verifier/build
deleted file mode 100755
index 2b0b2c1..0000000
--- a/test/954-invoke-polymorphic-verifier/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/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.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental method-handles
diff --git a/test/954-invoke-polymorphic-verifier/build.py b/test/954-invoke-polymorphic-verifier/build.py
new file mode 100644
index 0000000..027dd53
--- /dev/null
+++ b/test/954-invoke-polymorphic-verifier/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(api_level="method-handles")
diff --git a/test/954-invoke-polymorphic-verifier/check b/test/954-invoke-polymorphic-verifier/check
deleted file mode 100755
index 3f9e6dc..0000000
--- a/test/954-invoke-polymorphic-verifier/check
+++ /dev/null
@@ -1,26 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# Strip out temporary file path information and indices from output.
-sed -e "s/ [(]declaration of.*//" -e "s/\[0x[0-9A-F]*\] //g" "$2" > "$2.tmp"
-diff --strip-trailing-cr -q "$1" "$2.tmp" >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4" >/dev/null
diff --git a/test/954-invoke-polymorphic-verifier/run.py b/test/954-invoke-polymorphic-verifier/run.py
new file mode 100644
index 0000000..bdb6301
--- /dev/null
+++ b/test/954-invoke-polymorphic-verifier/run.py
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Copyright 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def run(ctx, args):
+ ctx.default_run(args)
+
+ # Strip out temporary file path information and indices from output.
+ ctx.run(
+ fr"sed -i -e 's/ [(]declaration of.*//' -e 's/\[0x[0-9A-F]*\] //g' '{args.stdout_file}'"
+ )
diff --git a/test/955-methodhandles-smali/build b/test/955-methodhandles-smali/build
deleted file mode 100755
index 2b0b2c1..0000000
--- a/test/955-methodhandles-smali/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/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.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental method-handles
diff --git a/test/955-methodhandles-smali/build.py b/test/955-methodhandles-smali/build.py
new file mode 100644
index 0000000..027dd53
--- /dev/null
+++ b/test/955-methodhandles-smali/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(api_level="method-handles")
diff --git a/test/956-methodhandles/build b/test/956-methodhandles/build
deleted file mode 100755
index 2b0b2c1..0000000
--- a/test/956-methodhandles/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/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.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental method-handles
diff --git a/test/956-methodhandles/build.py b/test/956-methodhandles/build.py
new file mode 100644
index 0000000..027dd53
--- /dev/null
+++ b/test/956-methodhandles/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(api_level="method-handles")
diff --git a/test/957-methodhandle-transforms/build b/test/957-methodhandle-transforms/build
deleted file mode 100755
index 2b0b2c1..0000000
--- a/test/957-methodhandle-transforms/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/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.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental method-handles
diff --git a/test/957-methodhandle-transforms/build.py b/test/957-methodhandle-transforms/build.py
new file mode 100644
index 0000000..027dd53
--- /dev/null
+++ b/test/957-methodhandle-transforms/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(api_level="method-handles")
diff --git a/test/957-methodhandle-transforms/src/Main.java b/test/957-methodhandle-transforms/src/Main.java
index 2b16485..3ee270d 100644
--- a/test/957-methodhandle-transforms/src/Main.java
+++ b/test/957-methodhandle-transforms/src/Main.java
@@ -1557,7 +1557,7 @@
try {
adapter = MethodHandles.collectArguments(target, 3, filter);
fail();
- } catch (IndexOutOfBoundsException expected) {
+ } catch (IndexOutOfBoundsException | IllegalArgumentException expected) {
}
// Mismatch in filter return type.
diff --git a/test/958-methodhandle-stackframe/build b/test/958-methodhandle-stackframe/build
deleted file mode 100755
index 2b0b2c1..0000000
--- a/test/958-methodhandle-stackframe/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/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.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental method-handles
diff --git a/test/958-methodhandle-stackframe/build.py b/test/958-methodhandle-stackframe/build.py
new file mode 100644
index 0000000..027dd53
--- /dev/null
+++ b/test/958-methodhandle-stackframe/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(api_level="method-handles")
diff --git a/test/959-invoke-polymorphic-accessors/build b/test/959-invoke-polymorphic-accessors/build
deleted file mode 100644
index 2b0b2c1..0000000
--- a/test/959-invoke-polymorphic-accessors/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/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.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental method-handles
diff --git a/test/959-invoke-polymorphic-accessors/build.py b/test/959-invoke-polymorphic-accessors/build.py
new file mode 100644
index 0000000..027dd53
--- /dev/null
+++ b/test/959-invoke-polymorphic-accessors/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(api_level="method-handles")
diff --git a/test/959-invoke-polymorphic-accessors/src/Main.java b/test/959-invoke-polymorphic-accessors/src/Main.java
index 03fd285..73b7746 100644
--- a/test/959-invoke-polymorphic-accessors/src/Main.java
+++ b/test/959-invoke-polymorphic-accessors/src/Main.java
@@ -20,6 +20,8 @@
public class Main {
+ private static final boolean DALVIK_RUN = "Dalvik".equals(System.getProperty("java.vm.name"));
+
public static class ValueHolder {
public boolean m_z = false;
public byte m_b = 0;
@@ -793,6 +795,10 @@
Long z = (Long) h0.invoke(valueHolder);
fail();
} catch (WrongMethodTypeException expected) {}
+ try {
+ int x = (int) h0.invokeExact((ValueHolder) null);
+ fail();
+ } catch (NullPointerException expected) {}
}
/*package*/ static Number getDoubleAsNumber() {
@@ -822,6 +828,10 @@
h0.invoke(valueHolder, (Float) null);
fail();
} catch (NullPointerException expected) {}
+ try {
+ h0.invoke((ValueHolder) null, Float.valueOf(1.0f));
+ fail();
+ } catch (NullPointerException expected) {}
// Test that type conversion checks work on small field types.
short temp = (short) s0.invoke(valueHolder, new Byte((byte) 45));
@@ -951,8 +961,10 @@
MethodHandles.lookup().unreflectSetter(f).invokeExact(v, 'A');
assertEquals('A', (char) MethodHandles.lookup().unreflectGetter(f).invokeExact(v));
}
- {
+ if (DALVIK_RUN) {
// public static final field test
+ // for JVM it is not possible to get the unreflected setter for a static final
+ // field, see b/242985782
Field f = ValueHolder.class.getDeclaredField("s_fi");
try {
MethodHandles.lookup().unreflectSetter(f);
@@ -1003,8 +1015,10 @@
fail();
} catch (IllegalAccessException expected) {}
}
- {
+ if (DALVIK_RUN) {
// private static final field test
+ // for JVM it is not possible to get the unreflected setter for a static final
+ // field, see b/242985782
Field f = ValueHolder.class.getDeclaredField("s_fz"); // private static final field
try {
MethodHandles.lookup().unreflectSetter(f);
diff --git a/test/960-default-smali/build b/test/960-default-smali/build
deleted file mode 100755
index 44d6bd2..0000000
--- a/test/960-default-smali/build
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2015 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.
-
-# make us exit on a failure
-set -e
-
-# Generate the Main.java file or fail
-${ANDROID_BUILD_TOP}/art/test/utils/python/generate_java_main.py ./src
-
-./default-build "$@" --experimental default-methods
diff --git a/test/960-default-smali/build.py b/test/960-default-smali/build.py
new file mode 100644
index 0000000..4c6b53c
--- /dev/null
+++ b/test/960-default-smali/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build(api_level="default-methods")
diff --git a/test/960-default-smali/generate-sources b/test/960-default-smali/generate-sources
new file mode 100755
index 0000000..1fffc71
--- /dev/null
+++ b/test/960-default-smali/generate-sources
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+
+# make us exit on a failure
+set -e
+
+# Generate the Main.java file or fail
+${ANDROID_BUILD_TOP}/art/test/utils/python/generate_java_main.py ./src
diff --git a/test/961-default-iface-resolution-gen/build b/test/961-default-iface-resolution-gen/build
deleted file mode 100755
index b9b36d0..0000000
--- a/test/961-default-iface-resolution-gen/build
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2015 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.
-
-# make us exit on a failure
-set -e
-
-mkdir -p ./src
-
-# Generate the smali files and expected-stdout.txt or fail
-./util-src/generate_java.py ./src ./expected-stdout.txt
-
-./default-build "$@" --experimental default-methods
diff --git a/test/961-default-iface-resolution-gen/build.py b/test/961-default-iface-resolution-gen/build.py
new file mode 100644
index 0000000..4c6b53c
--- /dev/null
+++ b/test/961-default-iface-resolution-gen/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build(api_level="default-methods")
diff --git a/test/961-default-iface-resolution-gen/expected-stdout.txt b/test/961-default-iface-resolution-gen/expected-stdout.txt
index 1ddd65d..c146358 100644
--- a/test/961-default-iface-resolution-gen/expected-stdout.txt
+++ b/test/961-default-iface-resolution-gen/expected-stdout.txt
@@ -1 +1 @@
-This file is generated by util-src/generate_smali.py do not directly modify!
+[DO_NOT_UPDATE] This file is generated by util-src/generate_smali.py do not directly modify!
diff --git a/test/961-default-iface-resolution-gen/generate-sources b/test/961-default-iface-resolution-gen/generate-sources
new file mode 100755
index 0000000..4d12e5f
--- /dev/null
+++ b/test/961-default-iface-resolution-gen/generate-sources
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+
+# make us exit on a failure
+set -e
+
+mkdir -p ./src
+
+# Generate the smali files and expected-stdout.txt or fail
+./util-src/generate_java.py ./src ./expected-stdout.txt
diff --git a/test/961-default-iface-resolution-gen/run b/test/961-default-iface-resolution-gen/run
deleted file mode 100755
index fdcd2a8..0000000
--- a/test/961-default-iface-resolution-gen/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-# Run with a 2 minute default dex2oat timeout and a 2.5 minute hard dex2oat timeout.
-./default-run "$@" --dex2oat-timeout 120 --dex2oat-rt-timeout 180
diff --git a/test/961-default-iface-resolution-gen/run.py b/test/961-default-iface-resolution-gen/run.py
new file mode 100644
index 0000000..7de21e3
--- /dev/null
+++ b/test/961-default-iface-resolution-gen/run.py
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# 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.
+
+
+def run(ctx, args):
+ # Run with a 2 minute default dex2oat timeout and a 2.5 minute hard dex2oat timeout.
+ ctx.default_run(args, dex2oat_timeout=120, dex2oat_rt_timeout=180)
diff --git a/test/962-iface-static/build b/test/962-iface-static/build
deleted file mode 100644
index 82f4931..0000000
--- a/test/962-iface-static/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/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.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental default-methods
diff --git a/test/962-iface-static/build.py b/test/962-iface-static/build.py
new file mode 100644
index 0000000..3e0ecd5
--- /dev/null
+++ b/test/962-iface-static/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(api_level="default-methods")
diff --git a/test/964-default-iface-init-gen/build b/test/964-default-iface-init-gen/build
deleted file mode 100755
index b9b36d0..0000000
--- a/test/964-default-iface-init-gen/build
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2015 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.
-
-# make us exit on a failure
-set -e
-
-mkdir -p ./src
-
-# Generate the smali files and expected-stdout.txt or fail
-./util-src/generate_java.py ./src ./expected-stdout.txt
-
-./default-build "$@" --experimental default-methods
diff --git a/test/964-default-iface-init-gen/build.py b/test/964-default-iface-init-gen/build.py
new file mode 100644
index 0000000..4c6b53c
--- /dev/null
+++ b/test/964-default-iface-init-gen/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build(api_level="default-methods")
diff --git a/test/964-default-iface-init-gen/expected-stdout.txt b/test/964-default-iface-init-gen/expected-stdout.txt
index 1ddd65d..c146358 100644
--- a/test/964-default-iface-init-gen/expected-stdout.txt
+++ b/test/964-default-iface-init-gen/expected-stdout.txt
@@ -1 +1 @@
-This file is generated by util-src/generate_smali.py do not directly modify!
+[DO_NOT_UPDATE] This file is generated by util-src/generate_smali.py do not directly modify!
diff --git a/test/964-default-iface-init-gen/generate-sources b/test/964-default-iface-init-gen/generate-sources
new file mode 100755
index 0000000..4d12e5f
--- /dev/null
+++ b/test/964-default-iface-init-gen/generate-sources
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+
+# make us exit on a failure
+set -e
+
+mkdir -p ./src
+
+# Generate the smali files and expected-stdout.txt or fail
+./util-src/generate_java.py ./src ./expected-stdout.txt
diff --git a/test/965-default-verify/Android.bp b/test/965-default-verify/Android.bp
new file mode 100644
index 0000000..680c3a2
--- /dev/null
+++ b/test/965-default-verify/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `965-default-verify`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-965-default-verify-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-965-default-verify",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-965-default-verify-src"
+ ],
+ data: [
+ ":art-run-test-965-default-verify-expected-stdout",
+ ":art-run-test-965-default-verify-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-965-default-verify-expected-stdout",
+ out: ["art-run-test-965-default-verify-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-965-default-verify-expected-stderr",
+ out: ["art-run-test-965-default-verify-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/966-default-conflict/Android.bp b/test/966-default-conflict/Android.bp
new file mode 100644
index 0000000..19217fc
--- /dev/null
+++ b/test/966-default-conflict/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `966-default-conflict`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-966-default-conflict-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-966-default-conflict",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-966-default-conflict-src"
+ ],
+ data: [
+ ":art-run-test-966-default-conflict-expected-stdout",
+ ":art-run-test-966-default-conflict-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-966-default-conflict-expected-stdout",
+ out: ["art-run-test-966-default-conflict-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-966-default-conflict-expected-stderr",
+ out: ["art-run-test-966-default-conflict-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/967-default-ame/Android.bp b/test/967-default-ame/Android.bp
new file mode 100644
index 0000000..0268bf5
--- /dev/null
+++ b/test/967-default-ame/Android.bp
@@ -0,0 +1,50 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `967-default-ame`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Library with src/ sources for the test.
+java_library {
+ name: "art-run-test-967-default-ame-src",
+ defaults: ["art-run-test-defaults"],
+ srcs: ["src/**/*.java"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-967-default-ame",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-template",
+ srcs: ["src2/**/*.java"],
+ static_libs: [
+ "art-run-test-967-default-ame-src"
+ ],
+ data: [
+ ":art-run-test-967-default-ame-expected-stdout",
+ ":art-run-test-967-default-ame-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-967-default-ame-expected-stdout",
+ out: ["art-run-test-967-default-ame-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-967-default-ame-expected-stderr",
+ out: ["art-run-test-967-default-ame-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/968-default-partial-compile-gen/build b/test/968-default-partial-compile-gen/build
deleted file mode 100755
index 04532b0..0000000
--- a/test/968-default-partial-compile-gen/build
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2015 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.
-
-# make us exit on a failure
-set -e
-
-if [[ $@ == *"--jvm"* ]]; then
- # Build the Java files if we are running a --jvm test
- mkdir -p classes
- mkdir -p src
- echo "${JAVAC} ${JAVAC_ARGS} \$@" >> ./javac_exec.sh
- # This will use java_exec.sh to execute the javac compiler. It will place the
- # compiled class files in ./classes and the expected values in expected-stdout.txt
- #
- # After this the src directory will contain the final versions of all files.
- ./util-src/generate_java.py ./javac_exec.sh ./src ./classes ./expected-stdout.txt ./build_log
-else
- mkdir -p ./smali
- # Generate the smali files and expected-stdout.txt or fail
- ./util-src/generate_smali.py ./smali ./expected-stdout.txt
- # Use the default build script
- ./default-build "$@" --experimental default-methods
-fi
diff --git a/test/968-default-partial-compile-gen/build.py b/test/968-default-partial-compile-gen/build.py
new file mode 100644
index 0000000..435be54
--- /dev/null
+++ b/test/968-default-partial-compile-gen/build.py
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+
+def build(ctx):
+ ctx.bash("./generate-sources --" + ctx.mode)
+ if ctx.jvm:
+ return
+ ctx.default_build(api_level="default-methods")
diff --git a/test/968-default-partial-compile-gen/expected-stdout.txt b/test/968-default-partial-compile-gen/expected-stdout.txt
index 1ddd65d..c146358 100644
--- a/test/968-default-partial-compile-gen/expected-stdout.txt
+++ b/test/968-default-partial-compile-gen/expected-stdout.txt
@@ -1 +1 @@
-This file is generated by util-src/generate_smali.py do not directly modify!
+[DO_NOT_UPDATE] This file is generated by util-src/generate_smali.py do not directly modify!
diff --git a/test/968-default-partial-compile-gen/generate-sources b/test/968-default-partial-compile-gen/generate-sources
new file mode 100755
index 0000000..83aad65
--- /dev/null
+++ b/test/968-default-partial-compile-gen/generate-sources
@@ -0,0 +1,34 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+
+# make us exit on a failure
+set -e
+
+if [[ $@ == *"--jvm"* ]]; then
+ # Build the Java files if we are running a --jvm test
+ mkdir -p classes
+ mkdir -p src
+ echo "${JAVAC} ${JAVAC_ARGS} \$@" >> ./javac_exec.sh
+ # This will use java_exec.sh to execute the javac compiler. It will place the
+ # compiled class files in ./classes and the expected values in expected-stdout.txt
+ #
+ # After this the src directory will contain the final versions of all files.
+ ./util-src/generate_java.py ./javac_exec.sh ./src ./classes ./expected-stdout.txt ./build_log
+else
+ mkdir -p ./smali
+ # Generate the smali files and expected-stdout.txt or fail
+ ./util-src/generate_smali.py ./smali ./expected-stdout.txt
+fi
diff --git a/test/968-default-partial-compile-gen/util-src/generate_java.py b/test/968-default-partial-compile-gen/util-src/generate_java.py
index a4a4a4d..6027df9 100755
--- a/test/968-default-partial-compile-gen/util-src/generate_java.py
+++ b/test/968-default-partial-compile-gen/util-src/generate_java.py
@@ -69,7 +69,6 @@
files = list(map(str, files))
cmd = ['sh', '-a', '-e', '--', str(self.javac)] + args + sorted(files)
subprocess.check_call(cmd)
- print("Compiled {} files".format(len(files)))
def execute(self):
"""
@@ -104,7 +103,6 @@
self.compile_files("-d {outdir} -cp {outdir}".format(outdir = self.classes_dir), files)
# Remove these from the set of interfaces to be compiled.
ifaces -= tops
- print("Finished compiling all files.")
return
def main(argv):
@@ -125,7 +123,6 @@
with expected_txt.open('w') as out:
print(mainclass.get_expected(), file=out)
- print("Wrote expected output")
Compiler(all_files, javac_exec, temp_dir, classes_dir).execute()
diff --git a/test/969-iface-super/build b/test/969-iface-super/build
deleted file mode 100755
index 44d6bd2..0000000
--- a/test/969-iface-super/build
+++ /dev/null
@@ -1,23 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2015 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.
-
-# make us exit on a failure
-set -e
-
-# Generate the Main.java file or fail
-${ANDROID_BUILD_TOP}/art/test/utils/python/generate_java_main.py ./src
-
-./default-build "$@" --experimental default-methods
diff --git a/test/969-iface-super/build.py b/test/969-iface-super/build.py
new file mode 100644
index 0000000..4c6b53c
--- /dev/null
+++ b/test/969-iface-super/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build(api_level="default-methods")
diff --git a/test/969-iface-super/generate-sources b/test/969-iface-super/generate-sources
new file mode 100755
index 0000000..1fffc71
--- /dev/null
+++ b/test/969-iface-super/generate-sources
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+
+# make us exit on a failure
+set -e
+
+# Generate the Main.java file or fail
+${ANDROID_BUILD_TOP}/art/test/utils/python/generate_java_main.py ./src
diff --git a/test/970-iface-super-resolution-gen/build b/test/970-iface-super-resolution-gen/build
deleted file mode 100755
index 6eecd71..0000000
--- a/test/970-iface-super-resolution-gen/build
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2015 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.
-
-# make us exit on a failure
-set -e
-
-# Should we compile with Java source code. By default we will use Smali.
-USES_JAVA_SOURCE="false"
-if [[ $@ == *"--jvm"* ]]; then
- # Build the Java files
- mkdir -p src
- mkdir -p src2
- ./util-src/generate_java.py ./src2 ./src ./expected-stdout.txt
-else
- # Generate the smali files and expected-stdout.txt or fail
- mkdir -p smali
- ./util-src/generate_smali.py ./smali ./expected-stdout.txt
-fi
-
-./default-build "$@" --experimental default-methods
diff --git a/test/970-iface-super-resolution-gen/build.py b/test/970-iface-super-resolution-gen/build.py
new file mode 100644
index 0000000..114e452
--- /dev/null
+++ b/test/970-iface-super-resolution-gen/build.py
@@ -0,0 +1,21 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+
+def build(ctx):
+ ctx.bash("./generate-sources --" + ctx.mode)
+ ctx.default_build(api_level="default-methods")
diff --git a/test/970-iface-super-resolution-gen/expected-stdout.txt b/test/970-iface-super-resolution-gen/expected-stdout.txt
index 1ddd65d..c146358 100644
--- a/test/970-iface-super-resolution-gen/expected-stdout.txt
+++ b/test/970-iface-super-resolution-gen/expected-stdout.txt
@@ -1 +1 @@
-This file is generated by util-src/generate_smali.py do not directly modify!
+[DO_NOT_UPDATE] This file is generated by util-src/generate_smali.py do not directly modify!
diff --git a/test/970-iface-super-resolution-gen/generate-sources b/test/970-iface-super-resolution-gen/generate-sources
new file mode 100755
index 0000000..175497f
--- /dev/null
+++ b/test/970-iface-super-resolution-gen/generate-sources
@@ -0,0 +1,31 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+
+# make us exit on a failure
+set -e
+
+# Should we compile with Java source code. By default we will use Smali.
+USES_JAVA_SOURCE="false"
+if [[ $@ == *"--jvm"* ]]; then
+ # Build the Java files
+ mkdir -p src
+ mkdir -p src2
+ ./util-src/generate_java.py ./src2 ./src ./expected-stdout.txt
+else
+ # Generate the smali files and expected-stdout.txt or fail
+ mkdir -p smali
+ ./util-src/generate_smali.py ./smali ./expected-stdout.txt
+fi
diff --git a/test/971-iface-super/build b/test/971-iface-super/build
deleted file mode 100755
index 04532b0..0000000
--- a/test/971-iface-super/build
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2015 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.
-
-# make us exit on a failure
-set -e
-
-if [[ $@ == *"--jvm"* ]]; then
- # Build the Java files if we are running a --jvm test
- mkdir -p classes
- mkdir -p src
- echo "${JAVAC} ${JAVAC_ARGS} \$@" >> ./javac_exec.sh
- # This will use java_exec.sh to execute the javac compiler. It will place the
- # compiled class files in ./classes and the expected values in expected-stdout.txt
- #
- # After this the src directory will contain the final versions of all files.
- ./util-src/generate_java.py ./javac_exec.sh ./src ./classes ./expected-stdout.txt ./build_log
-else
- mkdir -p ./smali
- # Generate the smali files and expected-stdout.txt or fail
- ./util-src/generate_smali.py ./smali ./expected-stdout.txt
- # Use the default build script
- ./default-build "$@" --experimental default-methods
-fi
diff --git a/test/971-iface-super/build.py b/test/971-iface-super/build.py
new file mode 100644
index 0000000..435be54
--- /dev/null
+++ b/test/971-iface-super/build.py
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+
+
+def build(ctx):
+ ctx.bash("./generate-sources --" + ctx.mode)
+ if ctx.jvm:
+ return
+ ctx.default_build(api_level="default-methods")
diff --git a/test/971-iface-super/expected-stdout.txt b/test/971-iface-super/expected-stdout.txt
index 1ddd65d..c146358 100644
--- a/test/971-iface-super/expected-stdout.txt
+++ b/test/971-iface-super/expected-stdout.txt
@@ -1 +1 @@
-This file is generated by util-src/generate_smali.py do not directly modify!
+[DO_NOT_UPDATE] This file is generated by util-src/generate_smali.py do not directly modify!
diff --git a/test/971-iface-super/generate-sources b/test/971-iface-super/generate-sources
new file mode 100755
index 0000000..83aad65
--- /dev/null
+++ b/test/971-iface-super/generate-sources
@@ -0,0 +1,34 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+
+# make us exit on a failure
+set -e
+
+if [[ $@ == *"--jvm"* ]]; then
+ # Build the Java files if we are running a --jvm test
+ mkdir -p classes
+ mkdir -p src
+ echo "${JAVAC} ${JAVAC_ARGS} \$@" >> ./javac_exec.sh
+ # This will use java_exec.sh to execute the javac compiler. It will place the
+ # compiled class files in ./classes and the expected values in expected-stdout.txt
+ #
+ # After this the src directory will contain the final versions of all files.
+ ./util-src/generate_java.py ./javac_exec.sh ./src ./classes ./expected-stdout.txt ./build_log
+else
+ mkdir -p ./smali
+ # Generate the smali files and expected-stdout.txt or fail
+ ./util-src/generate_smali.py ./smali ./expected-stdout.txt
+fi
diff --git a/test/971-iface-super/util-src/generate_java.py b/test/971-iface-super/util-src/generate_java.py
index dafe7f5..c043a4a 100755
--- a/test/971-iface-super/util-src/generate_java.py
+++ b/test/971-iface-super/util-src/generate_java.py
@@ -69,7 +69,6 @@
files = list(map(str, files))
cmd = ['sh', '-a', '-e', '--', str(self.javac)] + args + sorted(files)
subprocess.check_call(cmd)
- print("Compiled {} files".format(len(files)))
def execute(self):
"""
@@ -108,7 +107,6 @@
self.compile_files("-d {outdir} -cp {outdir}".format(outdir = self.classes_dir), files)
# Remove these from the set of interfaces to be compiled.
ifaces -= tops
- print("Finished compiling all files.")
return
def main(argv):
@@ -129,7 +127,6 @@
with expected_txt.open('w') as out:
print(mainclass.get_expected(), file=out)
- print("Wrote expected output")
Compiler(all_files, javac_exec, temp_dir, classes_dir).execute()
diff --git a/test/975-iface-private/build b/test/975-iface-private/build
deleted file mode 100755
index 14230c2..0000000
--- a/test/975-iface-private/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2015 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.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental default-methods
diff --git a/test/975-iface-private/build.py b/test/975-iface-private/build.py
new file mode 100644
index 0000000..3e0ecd5
--- /dev/null
+++ b/test/975-iface-private/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(api_level="default-methods")
diff --git a/test/978-virtual-interface/build b/test/978-virtual-interface/build
deleted file mode 100755
index 14230c2..0000000
--- a/test/978-virtual-interface/build
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2015 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.
-
-# make us exit on a failure
-set -e
-
-./default-build "$@" --experimental default-methods
diff --git a/test/978-virtual-interface/build.py b/test/978-virtual-interface/build.py
new file mode 100644
index 0000000..3e0ecd5
--- /dev/null
+++ b/test/978-virtual-interface/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(api_level="default-methods")
diff --git a/test/979-const-method-handle/build b/test/979-const-method-handle/build
deleted file mode 100755
index fa6a0ea..0000000
--- a/test/979-const-method-handle/build
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-# make us exit on a failure
-set -e
-
-export ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-9.2.jar"
-
-# Build the transformer to apply to compiled classes.
-mkdir classes
-${JAVAC:-javac} ${JAVAC_ARGS} -cp "${ASM_JAR}" -d classes $(find util-src -name '*.java')
-${SOONG_ZIP} --jar -o transformer.jar -C classes -D classes
-rm -rf classes
-
-# Use API level 28 for DEX file support constant method handles.
-./default-build "$@" --api-level 28
diff --git a/test/979-const-method-handle/build.py b/test/979-const-method-handle/build.py
new file mode 100644
index 0000000..4eb0ccb
--- /dev/null
+++ b/test/979-const-method-handle/build.py
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ ctx.default_build(api_level=28)
diff --git a/test/979-const-method-handle/generate-sources b/test/979-const-method-handle/generate-sources
new file mode 100755
index 0000000..0cd94d1
--- /dev/null
+++ b/test/979-const-method-handle/generate-sources
@@ -0,0 +1,29 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+# make us exit on a failure
+set -e
+
+export ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-9.2.jar"
+
+# Build the transformer to apply to compiled classes.
+mkdir classes
+${JAVAC:-javac} ${JAVAC_ARGS} -cp "${ASM_JAR}" -d classes $(find util-src -name '*.java')
+${SOONG_ZIP} --jar -o transformer.jar -C classes -D classes
+rm -rf classes
+
+# Add annotation src files to our compiler inputs.
+cp -r util-src/annotations src/
diff --git a/test/979-const-method-handle/javac_post.sh b/test/979-const-method-handle/javac_post.sh
new file mode 100755
index 0000000..1be31d5
--- /dev/null
+++ b/test/979-const-method-handle/javac_post.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+set -e
+
+export ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-9.2.jar"
+
+# Move original classes to intermediate location.
+mv classes intermediate-classes
+mkdir classes
+
+# Transform intermediate classes.
+transformer_args="-cp ${ASM_JAR}:$PWD/transformer.jar transformer.ConstantTransformer"
+for class in intermediate-classes/*.class ; do
+ transformed_class=classes/$(basename ${class})
+ ${JAVA:-java} ${transformer_args} ${class} ${transformed_class}
+done
+
+# Remove class which we want missing at runtime.
+rm classes/MissingType.class
diff --git a/test/979-const-method-handle/javac_wrapper.sh b/test/979-const-method-handle/javac_wrapper.sh
deleted file mode 100755
index 77b6bc3..0000000
--- a/test/979-const-method-handle/javac_wrapper.sh
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-set -e
-
-# Add annotation src files to our compiler inputs.
-asrcs=util-src/annotations/*.java
-
-# Compile.
-$JAVAC "$@" $asrcs
-
-# Move original classes to intermediate location.
-mv classes intermediate-classes
-mkdir classes
-
-# Transform intermediate classes.
-transformer_args="-cp ${ASM_JAR}:$PWD/transformer.jar transformer.ConstantTransformer"
-for class in intermediate-classes/*.class ; do
- transformed_class=classes/$(basename ${class})
- ${JAVA:-java} ${transformer_args} ${class} ${transformed_class}
-done
diff --git a/test/979-const-method-handle/src/Main.java b/test/979-const-method-handle/src/Main.java
index 5368a22..72d529b 100644
--- a/test/979-const-method-handle/src/Main.java
+++ b/test/979-const-method-handle/src/Main.java
@@ -72,6 +72,14 @@
return null;
}
+ @ConstantMethodType(
+ returnType = void.class,
+ parameterTypes = {MissingType.class})
+ private static MethodType missingType() {
+ unreachable();
+ return null;
+ }
+
private static void repeatConstMethodType0(MethodType expected) {
System.out.print("repeatConstMethodType0(");
System.out.print(expected);
@@ -189,6 +197,16 @@
return null;
}
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.STATIC_GET,
+ owner = "PrivateMember",
+ fieldOrMethodName = "privateField",
+ descriptor = "I")
+ private static MethodHandle getPrivateField() {
+ unreachable();
+ return null;
+ }
+
private static void repeatConstMethodHandle() throws Throwable {
System.out.println("repeatConstMethodHandle()");
String[] values = {"A", "B", "C"};
@@ -243,5 +261,37 @@
System.out.println("Stack: capacity was " + stack.capacity());
stackTrim().invokeExact(stack);
System.out.println("Stack: capacity is " + stack.capacity());
+
+ // We used to not report in the compiler that loading a ConstMethodHandle/ConstMethodType
+ // can throw, which meant we were not catching the exception in the situation where we
+ // inline the loading.
+ try {
+ $inline$getPrivateField();
+ System.out.println("Expected IllegalAccessError");
+ } catch (IllegalAccessError e) {
+ // expected
+ }
+
+ try {
+ $inline$missingType();
+ System.out.println("Expected NoClassDefFoundError");
+ } catch (NoClassDefFoundError e) {
+ // expected
+ }
}
+
+ public static void $inline$getPrivateField() {
+ getPrivateField();
+ }
+
+ public static void $inline$missingType() {
+ missingType();
+ }
+}
+
+class PrivateMember {
+ private static int privateField;
+}
+
+class MissingType {
}
diff --git a/test/980-redefine-object/check b/test/980-redefine-object/check
deleted file mode 100755
index 80a3cd7..0000000
--- a/test/980-redefine-object/check
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-# The number of paused background threads (and therefore InterruptedExceptions)
-# can change so we will just delete their lines from the log.
-
-cat "$2" \
- | sed "/Object allocated of type 'java\.lang\.InterruptedException'/d" \
- | sed "/Object allocated of type 'java\.lang\.Long'/d" \
- | diff --strip-trailing-cr -q "$1" - >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4" >/dev/null
diff --git a/test/980-redefine-object/run b/test/980-redefine-object/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/980-redefine-object/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/980-redefine-object/run.py b/test/980-redefine-object/run.py
new file mode 100644
index 0000000..c45595d
--- /dev/null
+++ b/test/980-redefine-object/run.py
@@ -0,0 +1,25 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
+
+ # The number of paused background threads (and therefore InterruptedExceptions)
+ # can change so we will just delete their lines from the log.
+ ctx.run(
+ fr"""sed -i -E "/Object allocated of type 'java\.lang\.(InterruptedException|Long)'/d" '{args.stdout_file}'"""
+ )
diff --git a/test/981-dedup-original-dex/run b/test/981-dedup-original-dex/run
deleted file mode 100755
index e92b873..0000000
--- a/test/981-dedup-original-dex/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/981-dedup-original-dex/run.py b/test/981-dedup-original-dex/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/981-dedup-original-dex/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/982-ok-no-retransform/run b/test/982-ok-no-retransform/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/982-ok-no-retransform/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/982-ok-no-retransform/run.py b/test/982-ok-no-retransform/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/982-ok-no-retransform/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/983-source-transform-verify/run b/test/983-source-transform-verify/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/983-source-transform-verify/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/983-source-transform-verify/run.py b/test/983-source-transform-verify/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/983-source-transform-verify/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/984-obsolete-invoke/run b/test/984-obsolete-invoke/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/984-obsolete-invoke/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/984-obsolete-invoke/run.py b/test/984-obsolete-invoke/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/984-obsolete-invoke/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/985-re-obsolete/run b/test/985-re-obsolete/run
deleted file mode 100755
index e92b873..0000000
--- a/test/985-re-obsolete/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/985-re-obsolete/run.py b/test/985-re-obsolete/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/985-re-obsolete/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/986-native-method-bind/run b/test/986-native-method-bind/run
deleted file mode 100755
index e92b873..0000000
--- a/test/986-native-method-bind/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/986-native-method-bind/run.py b/test/986-native-method-bind/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/986-native-method-bind/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/987-agent-bind/run b/test/987-agent-bind/run
deleted file mode 100755
index e92b873..0000000
--- a/test/987-agent-bind/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/987-agent-bind/run.py b/test/987-agent-bind/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/987-agent-bind/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/988-method-trace/expected-stdout.txt b/test/988-method-trace/expected-stdout.txt
index 59077b9..9460503 100644
--- a/test/988-method-trace/expected-stdout.txt
+++ b/test/988-method-trace/expected-stdout.txt
@@ -13,13 +13,14 @@
..=> public java.lang.Object()
..<= public java.lang.Object() -> <null: null>
.<= public art.Test988$FibResult(java.lang.String,int,int) -> <null: null>
-.=> public boolean java.util.ArrayList.add(java.lang.Object)
-..=> private void java.util.ArrayList.ensureCapacityInternal(int)
-...=> private void java.util.ArrayList.ensureExplicitCapacity(int)
-...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
-..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
+.=> static void art.Test988.addToResults(art.Test988$Printable)
+..=> public void java.util.ArrayList.ensureCapacity(int)
+..<= public void java.util.ArrayList.ensureCapacity(int) -> <null: null>
+..=> public boolean java.util.ArrayList.add(java.lang.Object)
fibonacci(30)=832040
-.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+...<= private void java.util.ArrayList.add(java.lang.Object,java.lang.Object[],int) -> <null: null>
+..<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+.<= static void art.Test988.addToResults(art.Test988$Printable) -> <null: null>
<= public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator) -> <null: null>
=> art.Test988$RecurOp()
.=> public java.lang.Object()
@@ -62,13 +63,14 @@
..=> public java.lang.Object()
..<= public java.lang.Object() -> <null: null>
.<= public art.Test988$FibResult(java.lang.String,int,int) -> <null: null>
-.=> public boolean java.util.ArrayList.add(java.lang.Object)
-..=> private void java.util.ArrayList.ensureCapacityInternal(int)
-...=> private void java.util.ArrayList.ensureExplicitCapacity(int)
-...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
-..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
+.=> static void art.Test988.addToResults(art.Test988$Printable)
+..=> public void java.util.ArrayList.ensureCapacity(int)
+..<= public void java.util.ArrayList.ensureCapacity(int) -> <null: null>
+..=> public boolean java.util.ArrayList.add(java.lang.Object)
fibonacci(5)=5
-.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+...<= private void java.util.ArrayList.add(java.lang.Object,java.lang.Object[],int) -> <null: null>
+..<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+.<= static void art.Test988.addToResults(art.Test988$Printable) -> <null: null>
<= public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator) -> <null: null>
=> art.Test988$NativeOp()
.=> public java.lang.Object()
@@ -83,13 +85,14 @@
..=> public java.lang.Object()
..<= public java.lang.Object() -> <null: null>
.<= public art.Test988$FibResult(java.lang.String,int,int) -> <null: null>
-.=> public boolean java.util.ArrayList.add(java.lang.Object)
-..=> private void java.util.ArrayList.ensureCapacityInternal(int)
-...=> private void java.util.ArrayList.ensureExplicitCapacity(int)
-...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
-..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
+.=> static void art.Test988.addToResults(art.Test988$Printable)
+..=> public void java.util.ArrayList.ensureCapacity(int)
+..<= public void java.util.ArrayList.ensureCapacity(int) -> <null: null>
+..=> public boolean java.util.ArrayList.add(java.lang.Object)
fibonacci(5)=5
-.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+...<= private void java.util.ArrayList.add(java.lang.Object,java.lang.Object[],int) -> <null: null>
+..<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+.<= static void art.Test988.addToResults(art.Test988$Printable) -> <null: null>
<= public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator) -> <null: null>
=> art.Test988$IterOp()
.=> public java.lang.Object()
@@ -110,32 +113,42 @@
.....<= public int java.lang.String.length() -> <class java.lang.Integer: 14>
.....=> private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int)
.....<= private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int) -> <null: null>
-.....=> public void java.lang.String.getChars(int,int,char[],int)
-......=> public int java.lang.String.length()
-......<= public int java.lang.String.length() -> <class java.lang.Integer: 14>
-......=> static void java.lang.String.checkBoundsBeginEnd(int,int,int)
-......<= static void java.lang.String.checkBoundsBeginEnd(int,int,int) -> <null: null>
-......=> native void java.lang.String.getCharsNoCheck(int,int,char[],int)
-......<= native void java.lang.String.getCharsNoCheck(int,int,char[],int) -> <null: null>
-.....<= public void java.lang.String.getChars(int,int,char[],int) -> <null: null>
+.....=> private final void java.lang.AbstractStringBuilder.putStringAt(int,java.lang.String)
+......=> final byte java.lang.AbstractStringBuilder.getCoder()
+......<= final byte java.lang.AbstractStringBuilder.getCoder() -> <class java.lang.Byte: 0>
+......=> byte java.lang.String.coder()
+......<= byte java.lang.String.coder() -> <class java.lang.Byte: 0>
+......=> void java.lang.String.getBytes(byte[],int,byte)
+.......=> byte java.lang.String.coder()
+.......<= byte java.lang.String.coder() -> <class java.lang.Byte: 0>
+.......=> public int java.lang.String.length()
+.......<= public int java.lang.String.length() -> <class java.lang.Integer: 14>
+.......=> static void java.lang.String.checkBoundsOffCount(int,int,int)
+.......<= static void java.lang.String.checkBoundsOffCount(int,int,int) -> <null: null>
+.......=> private void java.lang.String.fillBytesLatin1(byte[],int)
+.......<= private void java.lang.String.fillBytesLatin1(byte[],int) -> <null: null>
+......<= void java.lang.String.getBytes(byte[],int,byte) -> <null: null>
+.....<= private final void java.lang.AbstractStringBuilder.putStringAt(int,java.lang.String) -> <null: null>
....<= public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...<= public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...=> public java.lang.StringBuilder java.lang.StringBuilder.append(int)
....=> public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(int)
.....=> static int java.lang.Integer.stringSize(int)
-.....<= static int java.lang.Integer.stringSize(int) -> <class java.lang.Integer: 2>
+.....<= static int java.lang.Integer.stringSize(int) -> <class java.lang.Integer: 3>
.....=> private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int)
......=> private int java.lang.AbstractStringBuilder.newCapacity(int)
......<= private int java.lang.AbstractStringBuilder.newCapacity(int) -> <class java.lang.Integer: 34>
-......=> public static char[] java.util.Arrays.copyOf(char[],int)
+......=> public static byte[] java.util.Arrays.copyOf(byte[],int)
.......=> public static int java.lang.Math.min(int,int)
.......<= public static int java.lang.Math.min(int,int) -> <class java.lang.Integer: 16>
.......=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
.......<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> <null: null>
-......<= public static char[] java.util.Arrays.copyOf(char[],int) -> <class [C: [B, a, d, , a, r, g, u, m, e, n, t, :, , -, 1, 9, , <, , 0, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>]>
+......<= public static byte[] java.util.Arrays.copyOf(byte[],int) -> <class [B: [66, 97, 100, 32, 97, 114, 103, 117, 109, 101, 110, 116, 58, 32, 45, 49, 57, 32, 60, 32, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]>
.....<= private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int) -> <null: null>
-.....=> static int java.lang.Integer.getChars(int,int,char[])
-.....<= static int java.lang.Integer.getChars(int,int,char[]) -> <class java.lang.Integer: 14>
+.....=> final boolean java.lang.AbstractStringBuilder.isLatin1()
+.....<= final boolean java.lang.AbstractStringBuilder.isLatin1() -> <class java.lang.Boolean: true>
+.....=> static int java.lang.Integer.getChars(int,int,byte[])
+.....<= static int java.lang.Integer.getChars(int,int,byte[]) -> <class java.lang.Integer: 14>
....<= public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(int) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...<= public java.lang.StringBuilder java.lang.StringBuilder.append(int) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...=> public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String)
@@ -144,21 +157,31 @@
.....<= public int java.lang.String.length() -> <class java.lang.Integer: 4>
.....=> private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int)
.....<= private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int) -> <null: null>
-.....=> public void java.lang.String.getChars(int,int,char[],int)
-......=> public int java.lang.String.length()
-......<= public int java.lang.String.length() -> <class java.lang.Integer: 4>
-......=> static void java.lang.String.checkBoundsBeginEnd(int,int,int)
-......<= static void java.lang.String.checkBoundsBeginEnd(int,int,int) -> <null: null>
-......=> native void java.lang.String.getCharsNoCheck(int,int,char[],int)
-......<= native void java.lang.String.getCharsNoCheck(int,int,char[],int) -> <null: null>
-.....<= public void java.lang.String.getChars(int,int,char[],int) -> <null: null>
+.....=> private final void java.lang.AbstractStringBuilder.putStringAt(int,java.lang.String)
+......=> final byte java.lang.AbstractStringBuilder.getCoder()
+......<= final byte java.lang.AbstractStringBuilder.getCoder() -> <class java.lang.Byte: 0>
+......=> byte java.lang.String.coder()
+......<= byte java.lang.String.coder() -> <class java.lang.Byte: 0>
+......=> void java.lang.String.getBytes(byte[],int,byte)
+.......=> byte java.lang.String.coder()
+.......<= byte java.lang.String.coder() -> <class java.lang.Byte: 0>
+.......=> public int java.lang.String.length()
+.......<= public int java.lang.String.length() -> <class java.lang.Integer: 4>
+.......=> static void java.lang.String.checkBoundsOffCount(int,int,int)
+.......<= static void java.lang.String.checkBoundsOffCount(int,int,int) -> <null: null>
+.......=> private void java.lang.String.fillBytesLatin1(byte[],int)
+.......<= private void java.lang.String.fillBytesLatin1(byte[],int) -> <null: null>
+......<= void java.lang.String.getBytes(byte[],int,byte) -> <null: null>
+.....<= private final void java.lang.AbstractStringBuilder.putStringAt(int,java.lang.String) -> <null: null>
....<= public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...<= public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...=> public java.lang.String java.lang.StringBuilder.toString()
-....=> public static java.lang.String java.lang.StringFactory.newStringFromChars(char[],int,int)
-.....=> static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[])
-.....<= static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -> <class java.lang.String: Bad argument: -19 < 0>
-....<= public static java.lang.String java.lang.StringFactory.newStringFromChars(char[],int,int) -> <class java.lang.String: Bad argument: -19 < 0>
+....=> final boolean java.lang.AbstractStringBuilder.isLatin1()
+....<= final boolean java.lang.AbstractStringBuilder.isLatin1() -> <class java.lang.Boolean: true>
+....=> public static java.lang.String java.lang.StringLatin1.newString(byte[],int,int)
+.....=> public static java.lang.String java.lang.StringFactory.newStringFromBytes(byte[],int,int,int)
+.....<= public static java.lang.String java.lang.StringFactory.newStringFromBytes(byte[],int,int,int) -> <class java.lang.String: Bad argument: -19 < 0>
+....<= public static java.lang.String java.lang.StringLatin1.newString(byte[],int,int) -> <class java.lang.String: Bad argument: -19 < 0>
...<= public java.lang.String java.lang.StringBuilder.toString() -> <class java.lang.String: Bad argument: -19 < 0>
...=> public java.lang.Error(java.lang.String)
....=> public java.lang.Throwable(java.lang.String)
@@ -170,10 +193,10 @@
......=> private static java.lang.Object java.lang.Throwable.nativeFillInStackTrace()
......<= private static java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
.....<= public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace() -> <class java.lang.Error: java.lang.Error: Bad argument: -19 < 0
- art.Test988.iter_fibonacci(Test988.java:269)
- art.Test988$IterOp.applyAsInt(Test988.java:264)
- art.Test988.doFibTest(Test988.java:402)
- art.Test988.run(Test988.java:358)
+ art.Test988.iter_fibonacci(Test988.java:280)
+ art.Test988$IterOp.applyAsInt(Test988.java:275)
+ art.Test988.doFibTest(Test988.java:413)
+ art.Test988.run(Test988.java:369)
<additional hidden frames>
>
....<= public java.lang.Throwable(java.lang.String) -> <null: null>
@@ -184,19 +207,20 @@
..=> public java.lang.Object()
..<= public java.lang.Object() -> <null: null>
.<= public art.Test988$FibThrow(java.lang.String,int,java.lang.Throwable) -> <null: null>
-.=> public boolean java.util.ArrayList.add(java.lang.Object)
-..=> private void java.util.ArrayList.ensureCapacityInternal(int)
-...=> private void java.util.ArrayList.ensureExplicitCapacity(int)
-...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
-..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
+.=> static void art.Test988.addToResults(art.Test988$Printable)
+..=> public void java.util.ArrayList.ensureCapacity(int)
+..<= public void java.util.ArrayList.ensureCapacity(int) -> <null: null>
+..=> public boolean java.util.ArrayList.add(java.lang.Object)
fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
- art.Test988.iter_fibonacci(Test988.java:269)
- art.Test988$IterOp.applyAsInt(Test988.java:264)
- art.Test988.doFibTest(Test988.java:402)
- art.Test988.run(Test988.java:358)
+ art.Test988.iter_fibonacci(Test988.java:280)
+ art.Test988$IterOp.applyAsInt(Test988.java:275)
+ art.Test988.doFibTest(Test988.java:413)
+ art.Test988.run(Test988.java:369)
<additional hidden frames>
-.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+...<= private void java.util.ArrayList.add(java.lang.Object,java.lang.Object[],int) -> <null: null>
+..<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+.<= static void art.Test988.addToResults(art.Test988$Printable) -> <null: null>
<= public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator) -> <null: null>
=> art.Test988$RecurOp()
.=> public java.lang.Object()
@@ -217,32 +241,42 @@
.....<= public int java.lang.String.length() -> <class java.lang.Integer: 14>
.....=> private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int)
.....<= private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int) -> <null: null>
-.....=> public void java.lang.String.getChars(int,int,char[],int)
-......=> public int java.lang.String.length()
-......<= public int java.lang.String.length() -> <class java.lang.Integer: 14>
-......=> static void java.lang.String.checkBoundsBeginEnd(int,int,int)
-......<= static void java.lang.String.checkBoundsBeginEnd(int,int,int) -> <null: null>
-......=> native void java.lang.String.getCharsNoCheck(int,int,char[],int)
-......<= native void java.lang.String.getCharsNoCheck(int,int,char[],int) -> <null: null>
-.....<= public void java.lang.String.getChars(int,int,char[],int) -> <null: null>
+.....=> private final void java.lang.AbstractStringBuilder.putStringAt(int,java.lang.String)
+......=> final byte java.lang.AbstractStringBuilder.getCoder()
+......<= final byte java.lang.AbstractStringBuilder.getCoder() -> <class java.lang.Byte: 0>
+......=> byte java.lang.String.coder()
+......<= byte java.lang.String.coder() -> <class java.lang.Byte: 0>
+......=> void java.lang.String.getBytes(byte[],int,byte)
+.......=> byte java.lang.String.coder()
+.......<= byte java.lang.String.coder() -> <class java.lang.Byte: 0>
+.......=> public int java.lang.String.length()
+.......<= public int java.lang.String.length() -> <class java.lang.Integer: 14>
+.......=> static void java.lang.String.checkBoundsOffCount(int,int,int)
+.......<= static void java.lang.String.checkBoundsOffCount(int,int,int) -> <null: null>
+.......=> private void java.lang.String.fillBytesLatin1(byte[],int)
+.......<= private void java.lang.String.fillBytesLatin1(byte[],int) -> <null: null>
+......<= void java.lang.String.getBytes(byte[],int,byte) -> <null: null>
+.....<= private final void java.lang.AbstractStringBuilder.putStringAt(int,java.lang.String) -> <null: null>
....<= public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...<= public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...=> public java.lang.StringBuilder java.lang.StringBuilder.append(int)
....=> public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(int)
.....=> static int java.lang.Integer.stringSize(int)
-.....<= static int java.lang.Integer.stringSize(int) -> <class java.lang.Integer: 2>
+.....<= static int java.lang.Integer.stringSize(int) -> <class java.lang.Integer: 3>
.....=> private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int)
......=> private int java.lang.AbstractStringBuilder.newCapacity(int)
......<= private int java.lang.AbstractStringBuilder.newCapacity(int) -> <class java.lang.Integer: 34>
-......=> public static char[] java.util.Arrays.copyOf(char[],int)
+......=> public static byte[] java.util.Arrays.copyOf(byte[],int)
.......=> public static int java.lang.Math.min(int,int)
.......<= public static int java.lang.Math.min(int,int) -> <class java.lang.Integer: 16>
.......=> public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int)
.......<= public static void java.lang.System.arraycopy(java.lang.Object,int,java.lang.Object,int,int) -> <null: null>
-......<= public static char[] java.util.Arrays.copyOf(char[],int) -> <class [C: [B, a, d, , a, r, g, u, m, e, n, t, :, , -, 1, 9, , <, , 0, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>, <control-0000>]>
+......<= public static byte[] java.util.Arrays.copyOf(byte[],int) -> <class [B: [66, 97, 100, 32, 97, 114, 103, 117, 109, 101, 110, 116, 58, 32, 45, 49, 57, 32, 60, 32, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]>
.....<= private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int) -> <null: null>
-.....=> static int java.lang.Integer.getChars(int,int,char[])
-.....<= static int java.lang.Integer.getChars(int,int,char[]) -> <class java.lang.Integer: 14>
+.....=> final boolean java.lang.AbstractStringBuilder.isLatin1()
+.....<= final boolean java.lang.AbstractStringBuilder.isLatin1() -> <class java.lang.Boolean: true>
+.....=> static int java.lang.Integer.getChars(int,int,byte[])
+.....<= static int java.lang.Integer.getChars(int,int,byte[]) -> <class java.lang.Integer: 14>
....<= public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(int) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...<= public java.lang.StringBuilder java.lang.StringBuilder.append(int) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...=> public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String)
@@ -251,21 +285,31 @@
.....<= public int java.lang.String.length() -> <class java.lang.Integer: 4>
.....=> private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int)
.....<= private void java.lang.AbstractStringBuilder.ensureCapacityInternal(int) -> <null: null>
-.....=> public void java.lang.String.getChars(int,int,char[],int)
-......=> public int java.lang.String.length()
-......<= public int java.lang.String.length() -> <class java.lang.Integer: 4>
-......=> static void java.lang.String.checkBoundsBeginEnd(int,int,int)
-......<= static void java.lang.String.checkBoundsBeginEnd(int,int,int) -> <null: null>
-......=> native void java.lang.String.getCharsNoCheck(int,int,char[],int)
-......<= native void java.lang.String.getCharsNoCheck(int,int,char[],int) -> <null: null>
-.....<= public void java.lang.String.getChars(int,int,char[],int) -> <null: null>
+.....=> private final void java.lang.AbstractStringBuilder.putStringAt(int,java.lang.String)
+......=> final byte java.lang.AbstractStringBuilder.getCoder()
+......<= final byte java.lang.AbstractStringBuilder.getCoder() -> <class java.lang.Byte: 0>
+......=> byte java.lang.String.coder()
+......<= byte java.lang.String.coder() -> <class java.lang.Byte: 0>
+......=> void java.lang.String.getBytes(byte[],int,byte)
+.......=> byte java.lang.String.coder()
+.......<= byte java.lang.String.coder() -> <class java.lang.Byte: 0>
+.......=> public int java.lang.String.length()
+.......<= public int java.lang.String.length() -> <class java.lang.Integer: 4>
+.......=> static void java.lang.String.checkBoundsOffCount(int,int,int)
+.......<= static void java.lang.String.checkBoundsOffCount(int,int,int) -> <null: null>
+.......=> private void java.lang.String.fillBytesLatin1(byte[],int)
+.......<= private void java.lang.String.fillBytesLatin1(byte[],int) -> <null: null>
+......<= void java.lang.String.getBytes(byte[],int,byte) -> <null: null>
+.....<= private final void java.lang.AbstractStringBuilder.putStringAt(int,java.lang.String) -> <null: null>
....<= public java.lang.AbstractStringBuilder java.lang.AbstractStringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...<= public java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String) -> <class java.lang.StringBuilder: Bad argument: -19 < 0>
...=> public java.lang.String java.lang.StringBuilder.toString()
-....=> public static java.lang.String java.lang.StringFactory.newStringFromChars(char[],int,int)
-.....=> static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[])
-.....<= static java.lang.String java.lang.StringFactory.newStringFromChars(int,int,char[]) -> <class java.lang.String: Bad argument: -19 < 0>
-....<= public static java.lang.String java.lang.StringFactory.newStringFromChars(char[],int,int) -> <class java.lang.String: Bad argument: -19 < 0>
+....=> final boolean java.lang.AbstractStringBuilder.isLatin1()
+....<= final boolean java.lang.AbstractStringBuilder.isLatin1() -> <class java.lang.Boolean: true>
+....=> public static java.lang.String java.lang.StringLatin1.newString(byte[],int,int)
+.....=> public static java.lang.String java.lang.StringFactory.newStringFromBytes(byte[],int,int,int)
+.....<= public static java.lang.String java.lang.StringFactory.newStringFromBytes(byte[],int,int,int) -> <class java.lang.String: Bad argument: -19 < 0>
+....<= public static java.lang.String java.lang.StringLatin1.newString(byte[],int,int) -> <class java.lang.String: Bad argument: -19 < 0>
...<= public java.lang.String java.lang.StringBuilder.toString() -> <class java.lang.String: Bad argument: -19 < 0>
...=> public java.lang.Error(java.lang.String)
....=> public java.lang.Throwable(java.lang.String)
@@ -277,10 +321,10 @@
......=> private static java.lang.Object java.lang.Throwable.nativeFillInStackTrace()
......<= private static java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
.....<= public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace() -> <class java.lang.Error: java.lang.Error: Bad argument: -19 < 0
- art.Test988.fibonacci(Test988.java:291)
- art.Test988$RecurOp.applyAsInt(Test988.java:286)
- art.Test988.doFibTest(Test988.java:402)
- art.Test988.run(Test988.java:359)
+ art.Test988.fibonacci(Test988.java:302)
+ art.Test988$RecurOp.applyAsInt(Test988.java:297)
+ art.Test988.doFibTest(Test988.java:413)
+ art.Test988.run(Test988.java:370)
<additional hidden frames>
>
....<= public java.lang.Throwable(java.lang.String) -> <null: null>
@@ -291,19 +335,20 @@
..=> public java.lang.Object()
..<= public java.lang.Object() -> <null: null>
.<= public art.Test988$FibThrow(java.lang.String,int,java.lang.Throwable) -> <null: null>
-.=> public boolean java.util.ArrayList.add(java.lang.Object)
-..=> private void java.util.ArrayList.ensureCapacityInternal(int)
-...=> private void java.util.ArrayList.ensureExplicitCapacity(int)
-...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
-..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
+.=> static void art.Test988.addToResults(art.Test988$Printable)
+..=> public void java.util.ArrayList.ensureCapacity(int)
+..<= public void java.util.ArrayList.ensureCapacity(int) -> <null: null>
+..=> public boolean java.util.ArrayList.add(java.lang.Object)
fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
- art.Test988.fibonacci(Test988.java:291)
- art.Test988$RecurOp.applyAsInt(Test988.java:286)
- art.Test988.doFibTest(Test988.java:402)
- art.Test988.run(Test988.java:359)
+ art.Test988.fibonacci(Test988.java:302)
+ art.Test988$RecurOp.applyAsInt(Test988.java:297)
+ art.Test988.doFibTest(Test988.java:413)
+ art.Test988.run(Test988.java:370)
<additional hidden frames>
-.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+...<= private void java.util.ArrayList.add(java.lang.Object,java.lang.Object[],int) -> <null: null>
+..<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+.<= static void art.Test988.addToResults(art.Test988$Printable) -> <null: null>
<= public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator) -> <null: null>
=> art.Test988$NativeOp()
.=> public java.lang.Object()
@@ -323,9 +368,9 @@
......<= private static java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
.....<= public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace() -> <class java.lang.Error: java.lang.Error: bad argument
art.Test988.nativeFibonacci(Native Method)
- art.Test988$NativeOp.applyAsInt(Test988.java:301)
- art.Test988.doFibTest(Test988.java:402)
- art.Test988.run(Test988.java:360)
+ art.Test988$NativeOp.applyAsInt(Test988.java:312)
+ art.Test988.doFibTest(Test988.java:413)
+ art.Test988.run(Test988.java:371)
<additional hidden frames>
>
....<= public java.lang.Throwable(java.lang.String) -> <null: null>
@@ -336,19 +381,20 @@
..=> public java.lang.Object()
..<= public java.lang.Object() -> <null: null>
.<= public art.Test988$FibThrow(java.lang.String,int,java.lang.Throwable) -> <null: null>
-.=> public boolean java.util.ArrayList.add(java.lang.Object)
-..=> private void java.util.ArrayList.ensureCapacityInternal(int)
-...=> private void java.util.ArrayList.ensureExplicitCapacity(int)
-...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
-..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
+.=> static void art.Test988.addToResults(art.Test988$Printable)
+..=> public void java.util.ArrayList.ensureCapacity(int)
+..<= public void java.util.ArrayList.ensureCapacity(int) -> <null: null>
+..=> public boolean java.util.ArrayList.add(java.lang.Object)
fibonacci(-19) -> java.lang.Error: bad argument
art.Test988.nativeFibonacci(Native Method)
- art.Test988$NativeOp.applyAsInt(Test988.java:301)
- art.Test988.doFibTest(Test988.java:402)
- art.Test988.run(Test988.java:360)
+ art.Test988$NativeOp.applyAsInt(Test988.java:312)
+ art.Test988.doFibTest(Test988.java:413)
+ art.Test988.run(Test988.java:371)
<additional hidden frames>
-.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+...<= private void java.util.ArrayList.add(java.lang.Object,java.lang.Object[],int) -> <null: null>
+..<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
+.<= static void art.Test988.addToResults(art.Test988$Printable) -> <null: null>
<= public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator) -> <null: null>
=> public final void <non-deterministic-type 0>.run()
.=> private static java.lang.Object java.lang.reflect.Proxy.invoke(java.lang.reflect.Proxy,java.lang.reflect.Method,java.lang.Object[]) throws java.lang.Throwable
diff --git a/test/988-method-trace/run b/test/988-method-trace/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/988-method-trace/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/988-method-trace/run.py b/test/988-method-trace/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/988-method-trace/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/988-method-trace/src/art/Test988.java b/test/988-method-trace/src/art/Test988.java
index 9c8ce4a..227ce62 100644
--- a/test/988-method-trace/src/art/Test988.java
+++ b/test/988-method-trace/src/art/Test988.java
@@ -253,11 +253,22 @@
}
}
- private static List<Printable> results = new ArrayList<>();
+ private static ArrayList<Printable> results = new ArrayList<>();
+ private static int results_index = 0;
// Starts with => enableMethodTracing
// .=> enableTracing
private static int cnt = 2;
+ static void addToResults(Printable obj) {
+ // Reserve space for the current object. If any other method entry callbacks are called they
+ // will reserve more space. Without this we may get into strange problems where ArrayList::add
+ // cecks there is enough space (which involves a couple of method calls) which then use up the
+ // space and by the time we actually add this record there is no capacity left.
+ results_index++;
+ results.ensureCapacity(results_index + 1);
+ results.add(obj);
+ }
+
// Iterative version
static final class IterOp implements IntUnaryOperator {
public int applyAsInt(int x) {
@@ -319,7 +330,7 @@
if ((cnt - 1) > METHOD_TRACING_IGNORE_DEPTH && sMethodTracingIgnore) {
return;
}
- results.add(new MethodEntry(m, cnt - 1));
+ addToResults(new MethodEntry(m, cnt - 1));
}
public static void notifyMethodExit(Executable m, boolean exception, Object result) {
@@ -330,9 +341,9 @@
}
if (exception) {
- results.add(new MethodThrownThrough(m, cnt));
+ addToResults(new MethodThrownThrough(m, cnt));
} else {
- results.add(new MethodReturn(m, result, cnt));
+ addToResults(new MethodReturn(m, result, cnt));
}
}
@@ -400,9 +411,9 @@
public static void doFibTest(int x, IntUnaryOperator op) {
try {
int y = op.applyAsInt(x);
- results.add(new FibResult("fibonacci(%d)=%d\n", x, y));
+ addToResults(new FibResult("fibonacci(%d)=%d\n", x, y));
} catch (Throwable t) {
- results.add(new FibThrow("fibonacci(%d) -> %s\n", x, t));
+ addToResults(new FibThrow("fibonacci(%d) -> %s\n", x, t));
}
}
diff --git a/test/989-method-trace-throw/run b/test/989-method-trace-throw/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/989-method-trace-throw/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/989-method-trace-throw/run.py b/test/989-method-trace-throw/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/989-method-trace-throw/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/990-field-trace/run b/test/990-field-trace/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/990-field-trace/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/990-field-trace/run.py b/test/990-field-trace/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/990-field-trace/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/991-field-trace-2/run b/test/991-field-trace-2/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/991-field-trace-2/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/991-field-trace-2/run.py b/test/991-field-trace-2/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/991-field-trace-2/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/992-source-data/run b/test/992-source-data/run
deleted file mode 100755
index e92b873..0000000
--- a/test/992-source-data/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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.
-
-./default-run "$@" --jvmti
diff --git a/test/992-source-data/run.py b/test/992-source-data/run.py
new file mode 100644
index 0000000..b596886
--- /dev/null
+++ b/test/992-source-data/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/993-breakpoints-non-debuggable/Android.bp b/test/993-breakpoints-non-debuggable/Android.bp
new file mode 100644
index 0000000..53a9f28
--- /dev/null
+++ b/test/993-breakpoints-non-debuggable/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `993-breakpoints-non-debuggable`.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+ name: "art-run-test-993-breakpoints-non-debuggable",
+ defaults: ["art-run-test-defaults"],
+ test_config_template: ":art-run-test-target-no-test-suite-tag-template",
+ srcs: ["src/**/*.java"],
+ data: [
+ ":art-run-test-993-breakpoints-non-debuggable-expected-stdout",
+ ":art-run-test-993-breakpoints-non-debuggable-expected-stderr",
+ ],
+}
+
+// Test's expected standard output.
+genrule {
+ name: "art-run-test-993-breakpoints-non-debuggable-expected-stdout",
+ out: ["art-run-test-993-breakpoints-non-debuggable-expected-stdout.txt"],
+ srcs: ["expected-stdout.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+ name: "art-run-test-993-breakpoints-non-debuggable-expected-stderr",
+ out: ["art-run-test-993-breakpoints-non-debuggable-expected-stderr.txt"],
+ srcs: ["expected-stderr.txt"],
+ cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/089-many-methods/expected-stderr.txt b/test/993-breakpoints-non-debuggable/expected-stderr.txt
similarity index 100%
copy from test/089-many-methods/expected-stderr.txt
copy to test/993-breakpoints-non-debuggable/expected-stderr.txt
diff --git a/test/993-breakpoints-non-debuggable/expected-stdout.txt b/test/993-breakpoints-non-debuggable/expected-stdout.txt
new file mode 100644
index 0000000..a4bd24b
--- /dev/null
+++ b/test/993-breakpoints-non-debuggable/expected-stdout.txt
@@ -0,0 +1,715 @@
+JNI_OnLoad called
+Running static invoke
+ Breaking on []
+ Native invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Reflective invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Invoking "Test993::breakpoint"
+ Breaking on [public static void art.Test993.breakpoint() @ 41]
+ Native invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993.breakpoint() @ line=41
+ Reflective invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993.breakpoint() @ line=41
+ Invoking "Test993::breakpoint"
+ Breakpoint: public static void art.Test993.breakpoint() @ line=41
+Running private static invoke
+ Breaking on []
+ Native invoking: private static void art.Test993.privateBreakpoint() args: [this: null]
+ Invoking "Test993::privateBreakpoint"
+ Breaking on [private static void art.Test993.privateBreakpoint() @ 45]
+ Native invoking: private static void art.Test993.privateBreakpoint() args: [this: null]
+ Breakpoint: private static void art.Test993.privateBreakpoint() @ line=45
+ Invoking "Test993::privateBreakpoint"
+ Breakpoint: private static void art.Test993.privateBreakpoint() @ line=45
+Running interface static invoke
+ Breaking on []
+ Native invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Reflective invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Invoking "Breakable::iBreakpoint"
+ Breaking on [public static void art.Test993$Breakable.iBreakpoint() @ 51]
+ Native invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993$Breakable.iBreakpoint() @ line=51
+ Reflective invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993$Breakable.iBreakpoint() @ line=51
+ Invoking "Breakable::iBreakpoint"
+ Breakpoint: public static void art.Test993$Breakable.iBreakpoint() @ line=51
+Running TestClass1 invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Invoking "((Breakable)new TestClass1()).breakit()"
+ Invoking "new TestClass1().breakit()"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass1()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass1().breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running TestClass1ext invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Invoking "new TestClass1ext().breakit()"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass1ext().breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass1ext.breakit() @ 74]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Invoking "new TestClass1ext().breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass1ext.breakit() @ 74]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass1ext().breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running TestClass2 invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Invoking "new TestClass2().breakit()"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Invoking "new TestClass2().breakit()"
+ Breaking on [public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2().breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2().breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+Running TestClass2ext invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Invoking "new TestClass2ext().breakit())"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Invoking "new TestClass2ext().breakit())"
+ Breaking on [public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breaking on [public void art.Test993$TestClass2.breakit() @ 83, public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2.breakit() @ 83, public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+Running TestClass3 invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Invoking "new TestClass3().breakit())"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3().breakit())"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "new TestClass3().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running TestClass3ext invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Invoking "new TestClass3ext().breakit())"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breaking on [public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass3.breakit() @ 99, public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3.breakit() @ 99, public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running private instance invoke
+ Breaking on []
+ Native invoking: private void art.Test993$TestClass4.privateMethod() args: [this: TestClass4]
+ Invoking "new TestClass4().callPrivateMethod()"
+ Breaking on [private void art.Test993$TestClass4.privateMethod() @ 118]
+ Native invoking: private void art.Test993$TestClass4.privateMethod() args: [this: TestClass4]
+ Breakpoint: private void art.Test993$TestClass4.privateMethod() @ line=118
+ Invoking "new TestClass4().callPrivateMethod()"
+ Breakpoint: private void art.Test993$TestClass4.privateMethod() @ line=118
+Running Vector constructor
+ Breaking on []
+ Native constructor: public java.util.Vector(), type: class java.util.Vector
+ Created: []
+ Reflective constructor: public java.util.Vector()
+ Created: []
+ Constructing: new Vector()
+ Created: []
+ Breaking on [public java.util.Vector() @ <NON-DETERMINISTIC>]
+ Native constructor: public java.util.Vector(), type: class java.util.Vector
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Reflective constructor: public java.util.Vector()
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Constructing: new Vector()
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+Running Stack constructor
+ Breaking on []
+ Native constructor: public java.util.Stack(), type: class java.util.Stack
+ Created: []
+ Reflective constructor: public java.util.Stack()
+ Created: []
+ Constructing: new Stack()
+ Created: []
+ Breaking on [public java.util.Stack() @ <NON-DETERMINISTIC>]
+ Native constructor: public java.util.Stack(), type: class java.util.Stack
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Reflective constructor: public java.util.Stack()
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Constructing: new Stack()
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Breaking on [public java.util.Vector() @ <NON-DETERMINISTIC>]
+ Native constructor: public java.util.Stack(), type: class java.util.Stack
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Reflective constructor: public java.util.Stack()
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Constructing: new Stack()
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Breaking on [public java.util.Stack() @ <NON-DETERMINISTIC>, public java.util.Vector() @ <NON-DETERMINISTIC>]
+ Native constructor: public java.util.Stack(), type: class java.util.Stack
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Reflective constructor: public java.util.Stack()
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Constructing: new Stack()
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+Running bcp static invoke
+ Breaking on []
+ Native invoking: public static java.util.Optional java.util.Optional.empty() args: [this: null]
+ Reflective invoking: public static java.util.Optional java.util.Optional.empty() args: [this: null]
+ Invoking "Optional::empty"
+ Breaking on [public static java.util.Optional java.util.Optional.empty() @ <NON-DETERMINISTIC>]
+ Native invoking: public static java.util.Optional java.util.Optional.empty() args: [this: null]
+ Breakpoint: public static java.util.Optional java.util.Optional.empty() @ line=<NON-DETERMINISTIC>
+ Reflective invoking: public static java.util.Optional java.util.Optional.empty() args: [this: null]
+ Breakpoint: public static java.util.Optional java.util.Optional.empty() @ line=<NON-DETERMINISTIC>
+ Invoking "Optional::empty"
+ Breakpoint: public static java.util.Optional java.util.Optional.empty() @ line=<NON-DETERMINISTIC>
+Running bcp private static invoke
+ Breaking on []
+ Native invoking: private static long java.util.Random.seedUniquifier() args: [this: null]
+ Invoking "Random::seedUniquifier"
+ Breaking on [private static long java.util.Random.seedUniquifier() @ <NON-DETERMINISTIC>]
+ Native invoking: private static long java.util.Random.seedUniquifier() args: [this: null]
+ Breakpoint: private static long java.util.Random.seedUniquifier() @ line=<NON-DETERMINISTIC>
+ Invoking "Random::seedUniquifier"
+ Breakpoint: private static long java.util.Random.seedUniquifier() @ line=<NON-DETERMINISTIC>
+Running bcp private invoke
+ Breaking on []
+ Native invoking: private java.math.BigDecimal java.time.Duration.toBigDecimalSeconds() args: [this: PT336H]
+ Invoking "Duration::toBigDecimalSeconds"
+ Breaking on [private java.math.BigDecimal java.time.Duration.toBigDecimalSeconds() @ <NON-DETERMINISTIC>]
+ Native invoking: private java.math.BigDecimal java.time.Duration.toBigDecimalSeconds() args: [this: PT336H]
+ Breakpoint: private java.math.BigDecimal java.time.Duration.toBigDecimalSeconds() @ line=<NON-DETERMINISTIC>
+ Invoking "Duration::toBigDecimalSeconds"
+ Breakpoint: private java.math.BigDecimal java.time.Duration.toBigDecimalSeconds() @ line=<NON-DETERMINISTIC>
+Running bcp invoke
+ Breaking on []
+ Native invoking: public boolean java.util.Optional.isPresent() args: [this: Optional[test]]
+ Reflective invoking: public boolean java.util.Optional.isPresent() args: [this: Optional[test2]]
+ Invoking "Optional::isPresent"
+ Breaking on [public boolean java.util.Optional.isPresent() @ <NON-DETERMINISTIC>]
+ Native invoking: public boolean java.util.Optional.isPresent() args: [this: Optional[test]]
+ Breakpoint: public boolean java.util.Optional.isPresent() @ line=<NON-DETERMINISTIC>
+ Reflective invoking: public boolean java.util.Optional.isPresent() args: [this: Optional[test2]]
+ Breakpoint: public boolean java.util.Optional.isPresent() @ line=<NON-DETERMINISTIC>
+ Invoking "Optional::isPresent"
+ Breakpoint: public boolean java.util.Optional.isPresent() @ line=<NON-DETERMINISTIC>
+Running TestClass1 constructor
+ Breaking on []
+ Native constructor: public art.Test993$TestClass1(), type: class art.Test993$TestClass1
+ Created: TestClass1
+ Reflective constructor: public art.Test993$TestClass1()
+ Created: TestClass1
+ Constructing: new TestClass1()
+ Created: TestClass1
+ Breaking on [public art.Test993$TestClass1() @ 62]
+ Native constructor: public art.Test993$TestClass1(), type: class art.Test993$TestClass1
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1
+ Reflective constructor: public art.Test993$TestClass1()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1
+ Constructing: new TestClass1()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1
+Running TestClass1ext constructor
+ Breaking on []
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Created: TestClass1Ext
+ Breaking on [public art.Test993$TestClass1() @ 62]
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Breaking on [public art.Test993$TestClass1ext() @ 70]
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Created: TestClass1Ext
+ Breaking on [public art.Test993$TestClass1() @ 62, public art.Test993$TestClass1ext() @ 70]
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
diff --git a/test/993-breakpoints-non-debuggable/expected_cts.txt b/test/993-breakpoints-non-debuggable/expected_cts.txt
new file mode 100644
index 0000000..6c4e881
--- /dev/null
+++ b/test/993-breakpoints-non-debuggable/expected_cts.txt
@@ -0,0 +1,696 @@
+Running static invoke
+ Breaking on []
+ Native invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Reflective invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Invoking "Test993::breakpoint"
+ Breaking on [public static void art.Test993.breakpoint() @ 41]
+ Native invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993.breakpoint() @ line=41
+ Reflective invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993.breakpoint() @ line=41
+ Invoking "Test993::breakpoint"
+ Breakpoint: public static void art.Test993.breakpoint() @ line=41
+Running private static invoke
+ Breaking on []
+ Native invoking: private static void art.Test993.privateBreakpoint() args: [this: null]
+ Invoking "Test993::privateBreakpoint"
+ Breaking on [private static void art.Test993.privateBreakpoint() @ 45]
+ Native invoking: private static void art.Test993.privateBreakpoint() args: [this: null]
+ Breakpoint: private static void art.Test993.privateBreakpoint() @ line=45
+ Invoking "Test993::privateBreakpoint"
+ Breakpoint: private static void art.Test993.privateBreakpoint() @ line=45
+Running interface static invoke
+ Breaking on []
+ Native invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Reflective invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Invoking "Breakable::iBreakpoint"
+ Breaking on [public static void art.Test993$Breakable.iBreakpoint() @ 51]
+ Native invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993$Breakable.iBreakpoint() @ line=51
+ Reflective invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993$Breakable.iBreakpoint() @ line=51
+ Invoking "Breakable::iBreakpoint"
+ Breakpoint: public static void art.Test993$Breakable.iBreakpoint() @ line=51
+Running TestClass1 invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Invoking "((Breakable)new TestClass1()).breakit()"
+ Invoking "new TestClass1().breakit()"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass1()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass1().breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running TestClass1ext invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Invoking "new TestClass1ext().breakit()"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass1ext().breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass1ext.breakit() @ 74]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Invoking "new TestClass1ext().breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass1ext.breakit() @ 74]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass1ext().breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running TestClass2 invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Invoking "new TestClass2().breakit()"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Invoking "new TestClass2().breakit()"
+ Breaking on [public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2().breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2().breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+Running TestClass2ext invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Invoking "new TestClass2ext().breakit())"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Invoking "new TestClass2ext().breakit())"
+ Breaking on [public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breaking on [public void art.Test993$TestClass2.breakit() @ 83, public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2.breakit() @ 83, public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+Running TestClass3 invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Invoking "new TestClass3().breakit())"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3().breakit())"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "new TestClass3().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running TestClass3ext invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Invoking "new TestClass3ext().breakit())"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breaking on [public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass3.breakit() @ 99, public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3.breakit() @ 99, public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running private instance invoke
+ Breaking on []
+ Native invoking: private void art.Test993$TestClass4.privateMethod() args: [this: TestClass4]
+ Invoking "new TestClass4().callPrivateMethod()"
+ Breaking on [private void art.Test993$TestClass4.privateMethod() @ 118]
+ Native invoking: private void art.Test993$TestClass4.privateMethod() args: [this: TestClass4]
+ Breakpoint: private void art.Test993$TestClass4.privateMethod() @ line=118
+ Invoking "new TestClass4().callPrivateMethod()"
+ Breakpoint: private void art.Test993$TestClass4.privateMethod() @ line=118
+Running Vector constructor
+ Breaking on []
+ Native constructor: public java.util.Vector(), type: class java.util.Vector
+ Created: []
+ Reflective constructor: public java.util.Vector()
+ Created: []
+ Constructing: new Vector()
+ Created: []
+ Breaking on [public java.util.Vector() @ <NON-DETERMINISTIC>]
+ Native constructor: public java.util.Vector(), type: class java.util.Vector
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Reflective constructor: public java.util.Vector()
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Constructing: new Vector()
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+Running Stack constructor
+ Breaking on []
+ Native constructor: public java.util.Stack(), type: class java.util.Stack
+ Created: []
+ Reflective constructor: public java.util.Stack()
+ Created: []
+ Constructing: new Stack()
+ Created: []
+ Breaking on [public java.util.Stack() @ <NON-DETERMINISTIC>]
+ Native constructor: public java.util.Stack(), type: class java.util.Stack
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Reflective constructor: public java.util.Stack()
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Constructing: new Stack()
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Breaking on [public java.util.Vector() @ <NON-DETERMINISTIC>]
+ Native constructor: public java.util.Stack(), type: class java.util.Stack
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Reflective constructor: public java.util.Stack()
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Constructing: new Stack()
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Breaking on [public java.util.Stack() @ <NON-DETERMINISTIC>, public java.util.Vector() @ <NON-DETERMINISTIC>]
+ Native constructor: public java.util.Stack(), type: class java.util.Stack
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Reflective constructor: public java.util.Stack()
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+ Constructing: new Stack()
+ Breakpoint: public java.util.Stack() @ line=<NON-DETERMINISTIC>
+ Breakpoint: public java.util.Vector() @ line=<NON-DETERMINISTIC>
+ Created: []
+Running bcp static invoke
+ Breaking on []
+ Native invoking: public static java.util.Optional java.util.Optional.empty() args: [this: null]
+ Reflective invoking: public static java.util.Optional java.util.Optional.empty() args: [this: null]
+ Invoking "Optional::empty"
+ Breaking on [public static java.util.Optional java.util.Optional.empty() @ <NON-DETERMINISTIC>]
+ Native invoking: public static java.util.Optional java.util.Optional.empty() args: [this: null]
+ Breakpoint: public static java.util.Optional java.util.Optional.empty() @ line=<NON-DETERMINISTIC>
+ Reflective invoking: public static java.util.Optional java.util.Optional.empty() args: [this: null]
+ Breakpoint: public static java.util.Optional java.util.Optional.empty() @ line=<NON-DETERMINISTIC>
+ Invoking "Optional::empty"
+ Breakpoint: public static java.util.Optional java.util.Optional.empty() @ line=<NON-DETERMINISTIC>
+Running bcp invoke
+ Breaking on []
+ Native invoking: public boolean java.util.Optional.isPresent() args: [this: Optional[test]]
+ Reflective invoking: public boolean java.util.Optional.isPresent() args: [this: Optional[test2]]
+ Invoking "Optional::isPresent"
+ Breaking on [public boolean java.util.Optional.isPresent() @ <NON-DETERMINISTIC>]
+ Native invoking: public boolean java.util.Optional.isPresent() args: [this: Optional[test]]
+ Breakpoint: public boolean java.util.Optional.isPresent() @ line=<NON-DETERMINISTIC>
+ Reflective invoking: public boolean java.util.Optional.isPresent() args: [this: Optional[test2]]
+ Breakpoint: public boolean java.util.Optional.isPresent() @ line=<NON-DETERMINISTIC>
+ Invoking "Optional::isPresent"
+ Breakpoint: public boolean java.util.Optional.isPresent() @ line=<NON-DETERMINISTIC>
+Running TestClass1 constructor
+ Breaking on []
+ Native constructor: public art.Test993$TestClass1(), type: class art.Test993$TestClass1
+ Created: TestClass1
+ Reflective constructor: public art.Test993$TestClass1()
+ Created: TestClass1
+ Constructing: new TestClass1()
+ Created: TestClass1
+ Breaking on [public art.Test993$TestClass1() @ 62]
+ Native constructor: public art.Test993$TestClass1(), type: class art.Test993$TestClass1
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1
+ Reflective constructor: public art.Test993$TestClass1()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1
+ Constructing: new TestClass1()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1
+Running TestClass1ext constructor
+ Breaking on []
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Created: TestClass1Ext
+ Breaking on [public art.Test993$TestClass1() @ 62]
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Breaking on [public art.Test993$TestClass1ext() @ 70]
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Created: TestClass1Ext
+ Breaking on [public art.Test993$TestClass1() @ 62, public art.Test993$TestClass1ext() @ 70]
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
diff --git a/test/993-breakpoints-non-debuggable/info.txt b/test/993-breakpoints-non-debuggable/info.txt
new file mode 100644
index 0000000..b5eb546
--- /dev/null
+++ b/test/993-breakpoints-non-debuggable/info.txt
@@ -0,0 +1,7 @@
+Test basic JVMTI breakpoint functionality.
+
+This test places a breakpoint on the first instruction of a number of functions
+that are entered in every way possible for the given class of method.
+
+It also tests that breakpoints don't interfere with each other by having
+multiple breakpoints be set at once.
diff --git a/test/993-breakpoints-non-debuggable/native_attach_agent.cc b/test/993-breakpoints-non-debuggable/native_attach_agent.cc
new file mode 100644
index 0000000..71d30eb
--- /dev/null
+++ b/test/993-breakpoints-non-debuggable/native_attach_agent.cc
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2013 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 <sstream>
+
+#include "jni.h"
+#include "runtime.h"
+
+namespace art {
+namespace Test993BreakpointsNonDebuggable {
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test993AttachAgent_setupJvmti(JNIEnv* env, jclass) {
+ Runtime* runtime = Runtime::Current();
+ std::ostringstream oss;
+ oss << (kIsDebugBuild ? "libtiagentd.so" : "libtiagent.so") << "=993-non-debuggable,art";
+ LOG(INFO) << "agent " << oss.str();
+ runtime->AttachAgent(env, oss.str(), nullptr);
+}
+
+} // namespace Test993BreakpointsNonDebuggable
+} // namespace art
diff --git a/test/993-breakpoints-non-debuggable/onload.cc b/test/993-breakpoints-non-debuggable/onload.cc
new file mode 100644
index 0000000..dbbcadc
--- /dev/null
+++ b/test/993-breakpoints-non-debuggable/onload.cc
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2013 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 "jvmti.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
+
+namespace art {
+namespace Test993BreakpointsNonDebuggable {
+
+static constexpr jint kArtTiVersion = JVMTI_VERSION_1_2 | 0x40000000;
+
+static const jvmtiCapabilities limited_caps = {
+ .can_tag_objects = 1,
+ .can_generate_field_modification_events = 1,
+ .can_generate_field_access_events = 1,
+ .can_get_bytecodes = 1,
+ .can_get_synthetic_attribute = 1,
+ .can_get_owned_monitor_info = 0,
+ .can_get_current_contended_monitor = 1,
+ .can_get_monitor_info = 1,
+ .can_pop_frame = 0,
+ .can_redefine_classes = 0,
+ .can_signal_thread = 1,
+ .can_get_source_file_name = 1,
+ .can_get_line_numbers = 1,
+ .can_get_source_debug_extension = 1,
+ .can_access_local_variables = 0,
+ .can_maintain_original_method_order = 1,
+ .can_generate_single_step_events = 1,
+ .can_generate_exception_events = 0,
+ .can_generate_frame_pop_events = 0,
+ .can_generate_breakpoint_events = 1,
+ .can_suspend = 1,
+ .can_redefine_any_class = 0,
+ .can_get_current_thread_cpu_time = 0,
+ .can_get_thread_cpu_time = 0,
+ .can_generate_method_entry_events = 1,
+ .can_generate_method_exit_events = 1,
+ .can_generate_all_class_hook_events = 0,
+ .can_generate_compiled_method_load_events = 0,
+ .can_generate_monitor_events = 0,
+ .can_generate_vm_object_alloc_events = 1,
+ .can_generate_native_method_bind_events = 1,
+ .can_generate_garbage_collection_events = 1,
+ .can_generate_object_free_events = 1,
+ .can_force_early_return = 0,
+ .can_get_owned_monitor_stack_depth_info = 0,
+ .can_get_constant_pool = 0,
+ .can_set_native_method_prefix = 0,
+ .can_retransform_classes = 0,
+ .can_retransform_any_class = 0,
+ .can_generate_resource_exhaustion_heap_events = 0,
+ .can_generate_resource_exhaustion_threads_events = 0,
+};
+
+jint OnLoad(JavaVM* vm, char* options ATTRIBUTE_UNUSED, void* reserved ATTRIBUTE_UNUSED) {
+ if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), kArtTiVersion) != 0) {
+ printf("Unable to get jvmti env!\n");
+ return 1;
+ }
+
+ CheckJvmtiError(jvmti_env, jvmti_env->AddCapabilities(&limited_caps));
+ return 0;
+}
+
+} // namespace Test993BreakpointsNonDebuggable
+} // namespace art
diff --git a/test/993-breakpoints-non-debuggable/onload.h b/test/993-breakpoints-non-debuggable/onload.h
new file mode 100644
index 0000000..99ce0d2b
--- /dev/null
+++ b/test/993-breakpoints-non-debuggable/onload.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_TEST_993_BREAKPOINTS_NON_DEBUGGABLE_ONLOAD_H_
+#define ART_TEST_993_BREAKPOINTS_NON_DEBUGGABLE_ONLOAD_H_
+
+#include "jni.h"
+
+namespace art {
+namespace Test993BreakpointsNonDebuggable {
+
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
+
+} // namespace Test993BreakpointsNonDebuggable
+} // namespace art
+
+#endif // ART_TEST_993_BREAKPOINTS_NON_DEBUGGABLE_ONLOAD_H_
diff --git a/test/993-breakpoints-non-debuggable/src/Main.java b/test/993-breakpoints-non-debuggable/src/Main.java
new file mode 100644
index 0000000..32fca46
--- /dev/null
+++ b/test/993-breakpoints-non-debuggable/src/Main.java
@@ -0,0 +1,23 @@
+/*
+ * 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 {
+ System.loadLibrary(args[0]);
+ art.Test993AttachAgent.loadAgent();
+ art.Test993.run(true);
+ }
+}
diff --git a/test/993-breakpoints-non-debuggable/src/art/Breakpoint.java b/test/993-breakpoints-non-debuggable/src/art/Breakpoint.java
new file mode 120000
index 0000000..3673916
--- /dev/null
+++ b/test/993-breakpoints-non-debuggable/src/art/Breakpoint.java
@@ -0,0 +1 @@
+../../../jvmti-common/Breakpoint.java
\ No newline at end of file
diff --git a/test/993-breakpoints-non-debuggable/src/art/Test993.java b/test/993-breakpoints-non-debuggable/src/art/Test993.java
new file mode 120000
index 0000000..7466288
--- /dev/null
+++ b/test/993-breakpoints-non-debuggable/src/art/Test993.java
@@ -0,0 +1 @@
+../../../993-breakpoints/src/art/Test993.java
\ No newline at end of file
diff --git a/test/993-breakpoints-non-debuggable/src/art/Test993AttachAgent.java b/test/993-breakpoints-non-debuggable/src/art/Test993AttachAgent.java
new file mode 100644
index 0000000..ac82c84
--- /dev/null
+++ b/test/993-breakpoints-non-debuggable/src/art/Test993AttachAgent.java
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+package art;
+
+public class Test993AttachAgent {
+ public static native void setupJvmti();
+
+ public static void loadAgent() throws Exception {
+ setupJvmti();
+ }
+}
diff --git a/test/993-breakpoints/run b/test/993-breakpoints/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/993-breakpoints/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/993-breakpoints/run.py b/test/993-breakpoints/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/993-breakpoints/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/994-breakpoint-line/run b/test/994-breakpoint-line/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/994-breakpoint-line/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/994-breakpoint-line/run.py b/test/994-breakpoint-line/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/994-breakpoint-line/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/995-breakpoints-throw/run b/test/995-breakpoints-throw/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/995-breakpoints-throw/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/995-breakpoints-throw/run.py b/test/995-breakpoints-throw/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/995-breakpoints-throw/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/995-breakpoints-throw/src/Main.java b/test/995-breakpoints-throw/src/Main.java
index 6f80b43..e92cd9b 100644
--- a/test/995-breakpoints-throw/src/Main.java
+++ b/test/995-breakpoints-throw/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test995.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test995.run();
+ }
}
diff --git a/test/995-breakpoints-throw/src/art/Test995.java b/test/995-breakpoints-throw/src/art/Test995.java
index a4023fb..5fb5e08 100644
--- a/test/995-breakpoints-throw/src/art/Test995.java
+++ b/test/995-breakpoints-throw/src/art/Test995.java
@@ -21,116 +21,119 @@
import java.lang.reflect.Method;
public class Test995 {
- public static final Breakpoint.Manager MANAGER = new Breakpoint.Manager();
- public static BreakpointHandler HANDLER = null;
+ public static final Breakpoint.Manager MANAGER = new Breakpoint.Manager();
+ public static BreakpointHandler HANDLER = null;
- public static void doNothing() { }
+ public static void doNothing() { }
- public static interface BreakpointHandler {
- public void breakpointReached(Executable e, long loc);
- }
-
- public static void breakpoint() {
- return;
- }
-
- public static void breakpointCatchLate() {
- doNothing();
- try {
- doNothing();
- } catch (Throwable t) {
- System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
+ public static interface BreakpointHandler {
+ public void breakpointReached(Executable e, long loc);
}
- }
- public static void breakpointCatch() {
- try {
- doNothing();
- } catch (Throwable t) {
- System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
+ public static void breakpoint() {
+ return;
}
- }
- public static void notifyBreakpointReached(Thread thr, Executable e, long loc) {
- System.out.println("\tBreakpoint: " + e + " @ line=" + Breakpoint.locationToLine(e, loc));
- HANDLER.breakpointReached(e, loc);
- }
-
-
- public static BreakpointHandler makeHandler(String name, BreakpointHandler h) {
- return new BreakpointHandler() {
- public String toString() {
- return name;
- }
- public void breakpointReached(Executable e, long loc) {
- h.breakpointReached(e, loc);
- }
- };
- }
-
- public static Runnable makeTest(String name, Runnable test) {
- return new Runnable() {
- public String toString() { return name; }
- public void run() { test.run(); }
- };
- }
-
- public static void run() throws Exception {
- // Set up breakpoints
- Breakpoint.stopBreakpointWatch(Thread.currentThread());
- Breakpoint.startBreakpointWatch(
- Test995.class,
- Test995.class.getDeclaredMethod(
- "notifyBreakpointReached", Thread.class, Executable.class, Long.TYPE),
- Thread.currentThread());
-
- Method breakpoint_method = Test995.class.getDeclaredMethod("breakpoint");
- Method breakpoint_catch_method = Test995.class.getDeclaredMethod("breakpointCatch");
- Method breakpoint_catch_late_method = Test995.class.getDeclaredMethod("breakpointCatchLate");
- MANAGER.setBreakpoint(breakpoint_method, Breakpoint.getStartLocation(breakpoint_method));
- MANAGER.setBreakpoint(
- breakpoint_catch_method, Breakpoint.getStartLocation(breakpoint_catch_method));
- MANAGER.setBreakpoint(
- breakpoint_catch_late_method, Breakpoint.getStartLocation(breakpoint_catch_late_method));
-
- BreakpointHandler[] handlers = new BreakpointHandler[] {
- makeHandler("do nothing", (e, l) -> {}),
- makeHandler("throw", (e, l) -> { throw new Error("throwing error!"); }),
- };
-
- Runnable[] tests = new Runnable[] {
- makeTest("call Test995::breakpoint", Test995::breakpoint),
- makeTest("call Test995::breakpointCatch", Test995::breakpointCatch),
- makeTest("call Test995::breakpointCatchLate", Test995::breakpointCatchLate),
- makeTest("catch subroutine Test995::breakpoint",
- () -> {
- try {
- breakpoint();
- } catch (Throwable t) {
- System.out.printf("Caught %s:\"%s\"\n", t.getClass().getName(), t.getMessage());
- }
- }),
- };
-
- for (BreakpointHandler handler : handlers) {
- for (Runnable test : tests) {
+ public static void breakpointCatchLate() {
+ doNothing();
try {
- HANDLER = handler;
- System.out.printf("Test \"%s\": Running breakpoint with handler \"%s\"\n",
- test, handler);
- test.run();
- System.out.printf("Test \"%s\": No error caught with handler \"%s\"\n",
- test, handler);
- } catch (Throwable e) {
- System.out.printf("Test \"%s\": Caught error %s:\"%s\" with handler \"%s\"\n",
- test, e.getClass().getName(), e.getMessage(), handler);
+ doNothing();
+ } catch (Throwable t) {
+ System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
}
- System.out.printf("Test \"%s\": Finished running with handler \"%s\"\n", test, handler);
- HANDLER = null;
- }
}
- MANAGER.clearAllBreakpoints();
- Breakpoint.stopBreakpointWatch(Thread.currentThread());
- }
+ public static void breakpointCatch() {
+ try {
+ doNothing();
+ } catch (Throwable t) {
+ System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
+ }
+ }
+
+ public static void notifyBreakpointReached(Thread thr, Executable e, long loc) {
+ System.out.println("\tBreakpoint: " + e + " @ line=" + Breakpoint.locationToLine(e, loc));
+ HANDLER.breakpointReached(e, loc);
+ }
+
+
+ public static BreakpointHandler makeHandler(String name, BreakpointHandler h) {
+ return new BreakpointHandler() {
+ public String toString() {
+ return name;
+ }
+ public void breakpointReached(Executable e, long loc) {
+ h.breakpointReached(e, loc);
+ }
+ };
+ }
+
+ public static Runnable makeTest(String name, Runnable test) {
+ return new Runnable() {
+ public String toString() { return name; }
+ public void run() { test.run(); }
+ };
+ }
+
+ public static void run() throws Exception {
+ // Set up breakpoints
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ Breakpoint.startBreakpointWatch(
+ Test995.class,
+ Test995.class.getDeclaredMethod(
+ "notifyBreakpointReached", Thread.class, Executable.class, Long.TYPE),
+ Thread.currentThread());
+
+ Method breakpoint_method = Test995.class.getDeclaredMethod("breakpoint");
+ Method breakpoint_catch_method = Test995.class.getDeclaredMethod("breakpointCatch");
+ Method breakpoint_catch_late_method =
+ Test995.class.getDeclaredMethod("breakpointCatchLate");
+ MANAGER.setBreakpoint(breakpoint_method, Breakpoint.getStartLocation(breakpoint_method));
+ MANAGER.setBreakpoint(
+ breakpoint_catch_method, Breakpoint.getStartLocation(breakpoint_catch_method));
+ MANAGER.setBreakpoint(breakpoint_catch_late_method,
+ Breakpoint.getStartLocation(breakpoint_catch_late_method));
+
+ BreakpointHandler[] handlers = new BreakpointHandler[] {
+ makeHandler("do nothing", (e, l) -> {}),
+ makeHandler("throw", (e, l) -> { throw new Error("throwing error!"); }),
+ };
+
+ Runnable[] tests = new Runnable[] {
+ makeTest("call Test995::breakpoint", Test995::breakpoint),
+ makeTest("call Test995::breakpointCatch", Test995::breakpointCatch),
+ makeTest("call Test995::breakpointCatchLate", Test995::breakpointCatchLate),
+ makeTest("catch subroutine Test995::breakpoint",
+ () -> {
+ try {
+ breakpoint();
+ } catch (Throwable t) {
+ System.out.printf("Caught %s:\"%s\"\n", t.getClass().getName(),
+ t.getMessage());
+ }
+ }),
+ };
+
+ for (BreakpointHandler handler : handlers) {
+ for (Runnable test : tests) {
+ try {
+ HANDLER = handler;
+ System.out.printf(
+ "Test \"%s\": Running breakpoint with handler \"%s\"\n", test, handler);
+ test.run();
+ System.out.printf(
+ "Test \"%s\": No error caught with handler \"%s\"\n", test, handler);
+ } catch (Throwable e) {
+ System.out.printf("Test \"%s\": Caught error %s:\"%s\" with handler \"%s\"\n",
+ test, e.getClass().getName(), e.getMessage(), handler);
+ }
+ System.out.printf(
+ "Test \"%s\": Finished running with handler \"%s\"\n", test, handler);
+ HANDLER = null;
+ }
+ }
+
+ MANAGER.clearAllBreakpoints();
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ }
}
diff --git a/test/996-breakpoint-obsolete/run b/test/996-breakpoint-obsolete/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/996-breakpoint-obsolete/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/996-breakpoint-obsolete/run.py b/test/996-breakpoint-obsolete/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/996-breakpoint-obsolete/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/996-breakpoint-obsolete/src/Main.java b/test/996-breakpoint-obsolete/src/Main.java
index 1b9b0a9..521d7fa 100644
--- a/test/996-breakpoint-obsolete/src/Main.java
+++ b/test/996-breakpoint-obsolete/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test996.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test996.run();
+ }
}
diff --git a/test/996-breakpoint-obsolete/src/art/Test996.java b/test/996-breakpoint-obsolete/src/art/Test996.java
index f3166c3..29dfff2 100644
--- a/test/996-breakpoint-obsolete/src/art/Test996.java
+++ b/test/996-breakpoint-obsolete/src/art/Test996.java
@@ -21,132 +21,133 @@
import java.util.Base64;
public class Test996 {
- // The line we are going to break on. This should be the println in the Transform class. We set a
- // breakpoint here after we have redefined the class.
- public static final int TRANSFORM_BREAKPOINT_REDEFINED_LINE = 40;
+ // The line we are going to break on. This should be the println in the Transform class.
+ // We set a breakpoint here after we have redefined the class.
+ public static final int TRANSFORM_BREAKPOINT_REDEFINED_LINE = 40;
- // The line we initially set a breakpoint on. This should be the doNothing call. This should be
- // cleared by the redefinition and should only be caught on the initial run.
- public static final int TRANSFORM_BREAKPOINT_INITIAL_LINE = 42;
+ // The line we initially set a breakpoint on. This should be the doNothing call. This should be
+ // cleared by the redefinition and should only be caught on the initial run.
+ public static final int TRANSFORM_BREAKPOINT_INITIAL_LINE = 42;
- // A function that doesn't do anything. Used for giving places to break on in a function.
- public static void doNothing() {}
+ // A function that doesn't do anything. Used for giving places to break on in a function.
+ public static void doNothing() {}
- public static final class Transform {
- public void run(Runnable r) {
- r.run();
- // Make sure we don't change anything above this line to keep all the breakpoint stuff
- // working. We will be putting a breakpoint before this line in the runnable.
- System.out.println("Should be after first breakpoint.");
- // This is set as a breakpoint prior to redefinition. It should not be hit.
- doNothing();
+ public static final class Transform {
+ public void run(Runnable r) {
+ r.run();
+ // Make sure we don't change anything above this line to keep all the breakpoint stuff
+ // working. We will be putting a breakpoint before this line in the runnable.
+ System.out.println("Should be after first breakpoint.");
+ // This is set as a breakpoint prior to redefinition. It should not be hit.
+ doNothing();
+ }
}
- }
- /* ******************************************************************************************** */
- // Try to keep all edits to this file below the above line. If edits need to be made above this
- // line be sure to update the TRANSFORM_BREAKPOINT_REDEFINED_LINE and
- // TRANSFORM_BREAKPOINT_INITIAL_LINE to their appropriate values.
+ /* ****************************************************************************************** */
+ // Try to keep all edits to this file below the above line. If edits need to be made above this
+ // line be sure to update the TRANSFORM_BREAKPOINT_REDEFINED_LINE and
+ // TRANSFORM_BREAKPOINT_INITIAL_LINE to their appropriate values.
- public static final int TRANSFORM_BREAKPOINT_POST_REDEFINITION_LINE = 8;
+ public static final int TRANSFORM_BREAKPOINT_POST_REDEFINITION_LINE = 8;
- // The base64 encoding of the following class. The redefined 'run' method should have the same
- // instructions as the original. This means that the locations of each line should stay the same
- // and the set of valid locations will not change. We use this to ensure that breakpoints are
- // removed from the redefined method.
- // public static final class Transform {
- // public void run(Runnable r) {
- // r.run();
- // System.out.println("Doing nothing transformed");
- // doNothing(); // try to catch non-removed breakpoints
- // }
- // }
- private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
- "yv66vgAAADQAKAoACAARCwASABMJABQAFQgAFgoAFwAYCgAZABoHABsHAB4BAAY8aW5pdD4BAAMo" +
- "KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQADcnVuAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
- "KVYBAApTb3VyY2VGaWxlAQAMVGVzdDk5Ni5qYXZhDAAJAAoHAB8MAA0ACgcAIAwAIQAiAQAZRG9p" +
- "bmcgbm90aGluZyB0cmFuc2Zvcm1lZAcAIwwAJAAlBwAmDAAnAAoBABVhcnQvVGVzdDk5NiRUcmFu" +
- "c2Zvcm0BAAlUcmFuc2Zvcm0BAAxJbm5lckNsYXNzZXMBABBqYXZhL2xhbmcvT2JqZWN0AQASamF2" +
- "YS9sYW5nL1J1bm5hYmxlAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50" +
- "U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3Ry" +
- "aW5nOylWAQALYXJ0L1Rlc3Q5OTYBAAlkb05vdGhpbmcAMQAHAAgAAAAAAAIAAQAJAAoAAQALAAAA" +
- "HQABAAEAAAAFKrcAAbEAAAABAAwAAAAGAAEAAAAEAAEADQAOAAEACwAAADYAAgACAAAAEiu5AAIB" +
- "ALIAAxIEtgAFuAAGsQAAAAEADAAAABIABAAAAAYABgAHAA4ACAARAAkAAgAPAAAAAgAQAB0AAAAK" +
- "AAEABwAZABwAGQ==");
- private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
- "ZGV4CjAzNQBzn3TiKGAiM0fubj25v816W0k+niqj+SQcBAAAcAAAAHhWNBIAAAAAAAAAAFgDAAAW" +
- "AAAAcAAAAAoAAADIAAAAAwAAAPAAAAABAAAAFAEAAAYAAAAcAQAAAQAAAEwBAACwAgAAbAEAANoB" +
- "AADiAQAA/QEAABYCAAAlAgAASQIAAGkCAACAAgAAlAIAAKoCAAC+AgAA0gIAAOACAADrAgAA7gIA" +
- "APICAAD/AgAACgMAABADAAAVAwAAHgMAACMDAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAA" +
- "CQAAAAoAAAANAAAADQAAAAkAAAAAAAAADgAAAAkAAADMAQAADgAAAAkAAADUAQAACAAEABIAAAAA" +
- "AAAAAAAAAAAAAQAUAAAAAQAAABAAAAAEAAIAEwAAAAUAAAAAAAAABgAAABQAAAAAAAAAEQAAAAUA" +
- "AAAAAAAACwAAALwBAABHAwAAAAAAAAIAAAA4AwAAPgMAAAEAAQABAAAAKgMAAAQAAABwEAQAAAAO" +
- "AAQAAgACAAAALwMAAA4AAAByEAUAAwBiAAAAGgEBAG4gAwAQAHEAAgAAAA4AbAEAAAAAAAAAAAAA" +
- "AAAAAAEAAAAGAAAAAQAAAAcABjxpbml0PgAZRG9pbmcgbm90aGluZyB0cmFuc2Zvcm1lZAAXTGFy" +
- "dC9UZXN0OTk2JFRyYW5zZm9ybTsADUxhcnQvVGVzdDk5NjsAIkxkYWx2aWsvYW5ub3RhdGlvbi9F" +
- "bmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAVTGphdmEvaW8v" +
- "UHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAFExqYXZhL2xhbmcvUnVubmFibGU7ABJM" +
- "amF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVtOwAMVGVzdDk5Ni5qYXZhAAlUcmFu" +
- "c2Zvcm0AAVYAAlZMAAthY2Nlc3NGbGFncwAJZG9Ob3RoaW5nAARuYW1lAANvdXQAB3ByaW50bG4A" +
- "A3J1bgAFdmFsdWUABAAHDgAGAQAHDjx4PAACAgEVGAECAwIPBBkRFwwAAAEBAIGABPgCAQGQAwAA" +
- "ABAAAAAAAAAAAQAAAAAAAAABAAAAFgAAAHAAAAACAAAACgAAAMgAAAADAAAAAwAAAPAAAAAEAAAA" +
- "AQAAABQBAAAFAAAABgAAABwBAAAGAAAAAQAAAEwBAAADEAAAAQAAAGwBAAABIAAAAgAAAHgBAAAG" +
- "IAAAAQAAALwBAAABEAAAAgAAAMwBAAACIAAAFgAAANoBAAADIAAAAgAAACoDAAAEIAAAAgAAADgD" +
- "AAAAIAAAAQAAAEcDAAAAEAAAAQAAAFgDAAA=");
+ // The base64 encoding of the following class. The redefined 'run' method should have the same
+ // instructions as the original. This means that the locations of each line should stay the same
+ // and the set of valid locations will not change. We use this to ensure that breakpoints are
+ // removed from the redefined method.
+ // public static final class Transform {
+ // public void run(Runnable r) {
+ // r.run();
+ // System.out.println("Doing nothing transformed");
+ // doNothing(); // try to catch non-removed breakpoints
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAKAoACAARCwASABMJABQAFQgAFgoAFwAYCgAZABoHABsHAB4BAAY8aW5pdD4BAAMo" +
+ "KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQADcnVuAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
+ "KVYBAApTb3VyY2VGaWxlAQAMVGVzdDk5Ni5qYXZhDAAJAAoHAB8MAA0ACgcAIAwAIQAiAQAZRG9p" +
+ "bmcgbm90aGluZyB0cmFuc2Zvcm1lZAcAIwwAJAAlBwAmDAAnAAoBABVhcnQvVGVzdDk5NiRUcmFu" +
+ "c2Zvcm0BAAlUcmFuc2Zvcm0BAAxJbm5lckNsYXNzZXMBABBqYXZhL2xhbmcvT2JqZWN0AQASamF2" +
+ "YS9sYW5nL1J1bm5hYmxlAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50" +
+ "U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3Ry" +
+ "aW5nOylWAQALYXJ0L1Rlc3Q5OTYBAAlkb05vdGhpbmcAMQAHAAgAAAAAAAIAAQAJAAoAAQALAAAA" +
+ "HQABAAEAAAAFKrcAAbEAAAABAAwAAAAGAAEAAAAEAAEADQAOAAEACwAAADYAAgACAAAAEiu5AAIB" +
+ "ALIAAxIEtgAFuAAGsQAAAAEADAAAABIABAAAAAYABgAHAA4ACAARAAkAAgAPAAAAAgAQAB0AAAAK" +
+ "AAEABwAZABwAGQ==");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQBzn3TiKGAiM0fubj25v816W0k+niqj+SQcBAAAcAAAAHhWNBIAAAAAAAAAAFgDAAAW" +
+ "AAAAcAAAAAoAAADIAAAAAwAAAPAAAAABAAAAFAEAAAYAAAAcAQAAAQAAAEwBAACwAgAAbAEAANoB" +
+ "AADiAQAA/QEAABYCAAAlAgAASQIAAGkCAACAAgAAlAIAAKoCAAC+AgAA0gIAAOACAADrAgAA7gIA" +
+ "APICAAD/AgAACgMAABADAAAVAwAAHgMAACMDAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAA" +
+ "CQAAAAoAAAANAAAADQAAAAkAAAAAAAAADgAAAAkAAADMAQAADgAAAAkAAADUAQAACAAEABIAAAAA" +
+ "AAAAAAAAAAAAAQAUAAAAAQAAABAAAAAEAAIAEwAAAAUAAAAAAAAABgAAABQAAAAAAAAAEQAAAAUA" +
+ "AAAAAAAACwAAALwBAABHAwAAAAAAAAIAAAA4AwAAPgMAAAEAAQABAAAAKgMAAAQAAABwEAQAAAAO" +
+ "AAQAAgACAAAALwMAAA4AAAByEAUAAwBiAAAAGgEBAG4gAwAQAHEAAgAAAA4AbAEAAAAAAAAAAAAA" +
+ "AAAAAAEAAAAGAAAAAQAAAAcABjxpbml0PgAZRG9pbmcgbm90aGluZyB0cmFuc2Zvcm1lZAAXTGFy" +
+ "dC9UZXN0OTk2JFRyYW5zZm9ybTsADUxhcnQvVGVzdDk5NjsAIkxkYWx2aWsvYW5ub3RhdGlvbi9F" +
+ "bmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAVTGphdmEvaW8v" +
+ "UHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAFExqYXZhL2xhbmcvUnVubmFibGU7ABJM" +
+ "amF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVtOwAMVGVzdDk5Ni5qYXZhAAlUcmFu" +
+ "c2Zvcm0AAVYAAlZMAAthY2Nlc3NGbGFncwAJZG9Ob3RoaW5nAARuYW1lAANvdXQAB3ByaW50bG4A" +
+ "A3J1bgAFdmFsdWUABAAHDgAGAQAHDjx4PAACAgEVGAECAwIPBBkRFwwAAAEBAIGABPgCAQGQAwAA" +
+ "ABAAAAAAAAAAAQAAAAAAAAABAAAAFgAAAHAAAAACAAAACgAAAMgAAAADAAAAAwAAAPAAAAAEAAAA" +
+ "AQAAABQBAAAFAAAABgAAABwBAAAGAAAAAQAAAEwBAAADEAAAAQAAAGwBAAABIAAAAgAAAHgBAAAG" +
+ "IAAAAQAAALwBAAABEAAAAgAAAMwBAAACIAAAFgAAANoBAAADIAAAAgAAACoDAAAEIAAAAgAAADgD" +
+ "AAAAIAAAAQAAAEcDAAAAEAAAAQAAAFgDAAA=");
- public static void notifyBreakpointReached(Thread thr, Executable e, long loc) {
- int line = Breakpoint.locationToLine(e, loc);
- if (line == -1 && e.getName().equals("run") && e.getDeclaringClass().equals(Transform.class)) {
- // RI always reports line = -1 for obsolete methods. Just replace it with the real line for
- // consistency.
- line = TRANSFORM_BREAKPOINT_REDEFINED_LINE;
+ public static void notifyBreakpointReached(Thread thr, Executable e, long loc) {
+ int line = Breakpoint.locationToLine(e, loc);
+ if (line == -1 && e.getName().equals("run")
+ && e.getDeclaringClass().equals(Transform.class)) {
+ // RI always reports line = -1 for obsolete methods. Just replace it with the real line
+ // for consistency.
+ line = TRANSFORM_BREAKPOINT_REDEFINED_LINE;
+ }
+ System.out.println("Breakpoint reached: " + e + " @ line=" + line);
}
- System.out.println("Breakpoint reached: " + e + " @ line=" + line);
- }
- public static void run() throws Exception {
- // Set up breakpoints
- Breakpoint.stopBreakpointWatch(Thread.currentThread());
- Breakpoint.startBreakpointWatch(
- Test996.class,
- Test996.class.getDeclaredMethod(
- "notifyBreakpointReached", Thread.class, Executable.class, Long.TYPE),
- Thread.currentThread());
+ public static void run() throws Exception {
+ // Set up breakpoints
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ Breakpoint.startBreakpointWatch(
+ Test996.class,
+ Test996.class.getDeclaredMethod(
+ "notifyBreakpointReached", Thread.class, Executable.class, Long.TYPE),
+ Thread.currentThread());
- Transform t = new Transform();
- Method non_obsolete_run_method = Transform.class.getDeclaredMethod("run", Runnable.class);
- final long obsolete_breakpoint_location =
- Breakpoint.lineToLocation(non_obsolete_run_method, TRANSFORM_BREAKPOINT_REDEFINED_LINE);
+ Transform t = new Transform();
+ Method non_obsolete_run_method = Transform.class.getDeclaredMethod("run", Runnable.class);
+ final long obsolete_breakpoint_location = Breakpoint.lineToLocation(
+ non_obsolete_run_method, TRANSFORM_BREAKPOINT_REDEFINED_LINE);
- System.out.println("Initially setting breakpoint to line " + TRANSFORM_BREAKPOINT_INITIAL_LINE);
- long initial_breakpoint_location =
- Breakpoint.lineToLocation(non_obsolete_run_method, TRANSFORM_BREAKPOINT_INITIAL_LINE);
- Breakpoint.setBreakpoint(non_obsolete_run_method, initial_breakpoint_location);
+ System.out.println(
+ "Initially setting breakpoint to line " + TRANSFORM_BREAKPOINT_INITIAL_LINE);
+ long initial_breakpoint_location = Breakpoint.lineToLocation(
+ non_obsolete_run_method, TRANSFORM_BREAKPOINT_INITIAL_LINE);
+ Breakpoint.setBreakpoint(non_obsolete_run_method, initial_breakpoint_location);
- System.out.println("Running transform without redefinition.");
- t.run(() -> {});
+ System.out.println("Running transform without redefinition.");
+ t.run(() -> {});
- System.out.println("Running transform with redefinition.");
- t.run(() -> {
- System.out.println("Redefining calling function!");
- // This should clear the breakpoint set to TRANSFORM_BREAKPOINT_INITIAL_LINE
- Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
- System.out.println("Setting breakpoint on now obsolete method to line " +
- TRANSFORM_BREAKPOINT_REDEFINED_LINE);
- setBreakpointOnObsoleteMethod(obsolete_breakpoint_location);
- });
- System.out.println("Running transform post redefinition. Should not hit any breakpoints.");
- t.run(() -> {});
+ System.out.println("Running transform with redefinition.");
+ t.run(() -> {
+ System.out.println("Redefining calling function!");
+ // This should clear the breakpoint set to TRANSFORM_BREAKPOINT_INITIAL_LINE
+ Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ System.out.println("Setting breakpoint on now obsolete method to line " +
+ TRANSFORM_BREAKPOINT_REDEFINED_LINE);
+ setBreakpointOnObsoleteMethod(obsolete_breakpoint_location);
+ });
+ System.out.println("Running transform post redefinition. Should not hit any breakpoints.");
+ t.run(() -> {});
- System.out.println("Setting initial breakpoint on redefined method.");
- long final_breakpoint_location =
- Breakpoint.lineToLocation(non_obsolete_run_method,
- TRANSFORM_BREAKPOINT_POST_REDEFINITION_LINE);
- Breakpoint.setBreakpoint(non_obsolete_run_method, final_breakpoint_location);
- t.run(() -> {});
+ System.out.println("Setting initial breakpoint on redefined method.");
+ long final_breakpoint_location = Breakpoint.lineToLocation(
+ non_obsolete_run_method, TRANSFORM_BREAKPOINT_POST_REDEFINITION_LINE);
+ Breakpoint.setBreakpoint(non_obsolete_run_method, final_breakpoint_location);
+ t.run(() -> {});
- Breakpoint.stopBreakpointWatch(Thread.currentThread());
- }
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ }
- public static native void setBreakpointOnObsoleteMethod(long location);
+ public static native void setBreakpointOnObsoleteMethod(long location);
}
diff --git a/test/997-single-step/run b/test/997-single-step/run
deleted file mode 100755
index 51875a7..0000000
--- a/test/997-single-step/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/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
diff --git a/test/997-single-step/run.py b/test/997-single-step/run.py
new file mode 100644
index 0000000..ce3a55a
--- /dev/null
+++ b/test/997-single-step/run.py
@@ -0,0 +1,20 @@
+#!/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.
+
+
+def run(ctx, args):
+ # Ask for stack traces to be dumped to a file rather than to stdout.
+ ctx.default_run(args, jvmti=True)
diff --git a/test/997-single-step/src/Main.java b/test/997-single-step/src/Main.java
index 1927f04..a3e911d 100644
--- a/test/997-single-step/src/Main.java
+++ b/test/997-single-step/src/Main.java
@@ -15,7 +15,7 @@
*/
public class Main {
- public static void main(String[] args) throws Exception {
- art.Test997.run();
- }
+ public static void main(String[] args) throws Exception {
+ art.Test997.run();
+ }
}
diff --git a/test/997-single-step/src/art/Test997.java b/test/997-single-step/src/art/Test997.java
index a7a522d..88fbb87 100644
--- a/test/997-single-step/src/art/Test997.java
+++ b/test/997-single-step/src/art/Test997.java
@@ -21,62 +21,63 @@
import java.lang.reflect.Method;
public class Test997 {
- static final int NO_LAST_LINE_NUMBER = -1;
- static int LAST_LINE_NUMBER = NO_LAST_LINE_NUMBER;
- static Method DO_MULTIPATH_METHOD;
+ static final int NO_LAST_LINE_NUMBER = -1;
+ static int LAST_LINE_NUMBER = NO_LAST_LINE_NUMBER;
+ static Method DO_MULTIPATH_METHOD;
- static {
- try {
- DO_MULTIPATH_METHOD = Test997.class.getDeclaredMethod("doMultiPath", Boolean.TYPE);
- } catch (Exception e) {
- throw new Error("could not find method doMultiPath", e);
- }
- }
-
- // Function that acts simply to ensure there are multiple lines.
- public static void doNothing() {}
-
- // Method with multiple paths we can break on.
- public static void doMultiPath(boolean bit) {
- doNothing();
- if (bit) {
- doNothing();
- } else {
- doNothing();
- }
- doNothing();
- }
-
- public static void notifySingleStep(Thread thr, Executable e, long loc) {
- if (!e.equals(DO_MULTIPATH_METHOD)) {
- // Only report steps in doMultiPath
- return;
- }
- int cur_line = Breakpoint.locationToLine(e, loc);
- // Only report anything when the line number changes. This is so we can run this test against
- // both the RI and ART and also to prevent front-end compiler changes from affecting output.
- if (LAST_LINE_NUMBER == NO_LAST_LINE_NUMBER || LAST_LINE_NUMBER != cur_line) {
- LAST_LINE_NUMBER = cur_line;
- System.out.println("Single step: " + e + " @ line=" + cur_line);
- }
- }
-
- public static void resetTest() {
- LAST_LINE_NUMBER = NO_LAST_LINE_NUMBER;
- }
-
- public static void run() throws Exception {
- boolean[] values = new boolean[] { true, false };
- Trace.enableSingleStepTracing(Test997.class,
- Test997.class.getDeclaredMethod(
- "notifySingleStep", Thread.class, Executable.class, Long.TYPE),
- Thread.currentThread());
- for (boolean arg : values) {
- System.out.println("Stepping through doMultiPath(" + arg + ")");
- resetTest();
- doMultiPath(arg);
+ static {
+ try {
+ DO_MULTIPATH_METHOD = Test997.class.getDeclaredMethod("doMultiPath", Boolean.TYPE);
+ } catch (Exception e) {
+ throw new Error("could not find method doMultiPath", e);
+ }
}
- Trace.disableTracing(Thread.currentThread());
- }
+ // Function that acts simply to ensure there are multiple lines.
+ public static void doNothing() {}
+
+ // Method with multiple paths we can break on.
+ public static void doMultiPath(boolean bit) {
+ doNothing();
+ if (bit) {
+ doNothing();
+ } else {
+ doNothing();
+ }
+ doNothing();
+ }
+
+ public static void notifySingleStep(Thread thr, Executable e, long loc) {
+ if (!e.equals(DO_MULTIPATH_METHOD)) {
+ // Only report steps in doMultiPath
+ return;
+ }
+ int cur_line = Breakpoint.locationToLine(e, loc);
+ // Only report anything when the line number changes. This is so we can run this test
+ // against both the RI and ART and also to prevent front-end compiler changes from
+ // affecting output.
+ if (LAST_LINE_NUMBER == NO_LAST_LINE_NUMBER || LAST_LINE_NUMBER != cur_line) {
+ LAST_LINE_NUMBER = cur_line;
+ System.out.println("Single step: " + e + " @ line=" + cur_line);
+ }
+ }
+
+ public static void resetTest() {
+ LAST_LINE_NUMBER = NO_LAST_LINE_NUMBER;
+ }
+
+ public static void run() throws Exception {
+ boolean[] values = new boolean[] { true, false };
+ Trace.enableSingleStepTracing(Test997.class,
+ Test997.class.getDeclaredMethod(
+ "notifySingleStep", Thread.class, Executable.class, Long.TYPE),
+ Thread.currentThread());
+ for (boolean arg : values) {
+ System.out.println("Stepping through doMultiPath(" + arg + ")");
+ resetTest();
+ doMultiPath(arg);
+ }
+
+ Trace.disableTracing(Thread.currentThread());
+ }
}
diff --git a/test/998-redefine-use-after-free/run b/test/998-redefine-use-after-free/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/998-redefine-use-after-free/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/998-redefine-use-after-free/run.py b/test/998-redefine-use-after-free/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/998-redefine-use-after-free/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/998-redefine-use-after-free/src-ex/DexCacheSmash.java b/test/998-redefine-use-after-free/src-ex/DexCacheSmash.java
index 2193a63..349b73e 100644
--- a/test/998-redefine-use-after-free/src-ex/DexCacheSmash.java
+++ b/test/998-redefine-use-after-free/src-ex/DexCacheSmash.java
@@ -18,26 +18,24 @@
import java.util.Base64;
public class DexCacheSmash {
- static class Transform {
- public void foo() {}
- public void bar() {}
- public String getId() {
- return "TRANSFORM_INITIAL";
+ static class Transform {
+ public void foo() {}
+ public void bar() {}
+ public String getId() {
+ return "TRANSFORM_INITIAL";
+ }
}
- }
- static class Transform2 {
- public String getId() {
- return "TRANSFORM2_INITIAL";
+ static class Transform2 {
+ public String getId() {
+ return "TRANSFORM2_INITIAL";
+ }
}
- }
- /**
- * A base64 encoding of the dex/class file of the Transform class above.
- */
- static final Redefinition.CommonClassDefinition TRANSFORM_INITIAL =
- new Redefinition.CommonClassDefinition(Transform.class,
- Base64.getDecoder().decode(
+ /**
+ * A base64 encoding of the dex/class file of the Transform class above.
+ */
+ static final byte[] TRANSFORM_INITIAL_CLASS_FILE_BYTES = Base64.getDecoder().decode(
"yv66vgAAADQAFwoABAAPCAAQBwASBwAVAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1i" +
"ZXJUYWJsZQEAA2ZvbwEAA2JhcgEABWdldElkAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAApTb3Vy" +
"Y2VGaWxlAQASRGV4Q2FjaGVTbWFzaC5qYXZhDAAFAAYBABFUUkFOU0ZPUk1fSU5JVElBTAcAFgEA" +
@@ -46,8 +44,8 @@
"AAEAAAAFKrcAAbEAAAABAAgAAAAGAAEAAAATAAEACQAGAAEABwAAABkAAAABAAAAAbEAAAABAAgA" +
"AAAGAAEAAAAUAAEACgAGAAEABwAAABkAAAABAAAAAbEAAAABAAgAAAAGAAEAAAAVAAEACwAMAAEA" +
"BwAAABsAAQABAAAAAxICsAAAAAEACAAAAAYAAQAAABcAAgANAAAAAgAOABQAAAAKAAEAAwARABMA" +
- "CA=="),
- Base64.getDecoder().decode(
+ "CA==");
+ static final byte[] TRANSFORM_INITIAL_DEX_FILE_BYTES = Base64.getDecoder().decode(
"ZGV4CjAzNQDhg9CfghG1SRlLClguRuFYsqihr4F7NsGQAwAAcAAAAHhWNBIAAAAAAAAAAOQCAAAS" +
"AAAAcAAAAAcAAAC4AAAAAgAAANQAAAAAAAAAAAAAAAUAAADsAAAAAQAAABQBAABcAgAANAEAAKgB" +
"AACwAQAAxAEAAMcBAADiAQAA8wEAABcCAAA3AgAASwIAAF8CAAByAgAAfQIAAIACAACNAgAAkgIA" +
@@ -63,59 +61,60 @@
"AAcOABUABw4AFAAHDgAXAAcOAAICAREYAQIDAgwECBAXCgAAAQMAgIAEwAIBAdgCAQHsAgEBgAMO" +
"AAAAAAAAAAEAAAAAAAAAAQAAABIAAABwAAAAAgAAAAcAAAC4AAAAAwAAAAIAAADUAAAABQAAAAUA" +
"AADsAAAABgAAAAEAAAAUAQAAAxAAAAEAAAA0AQAAASAAAAQAAABAAQAABiAAAAEAAACYAQAAAiAA" +
- "ABIAAACoAQAAAyAAAAQAAACrAgAABCAAAAIAAAC/AgAAACAAAAEAAADOAgAAABAAAAEAAADkAgAA"));
+ "ABIAAACoAQAAAyAAAAQAAACrAgAABCAAAAIAAAC/AgAAACAAAAEAAADOAgAAABAAAAEAAADkAgAA");
+ static final Redefinition.CommonClassDefinition TRANSFORM_INITIAL =
+ new Redefinition.CommonClassDefinition(Transform.class,
+ TRANSFORM_INITIAL_CLASS_FILE_BYTES, TRANSFORM_INITIAL_DEX_FILE_BYTES);
- /**
- * A base64 encoding of the following (invalid) class.
- *
- * .class LDexCacheSmash$Transform2;
- * .super Ljava/lang/Object;
- * .source "DexCacheSmash.java"
- *
- * # annotations
- * .annotation system Ldalvik/annotation/EnclosingClass;
- * value = LDexCacheSmash;
- * .end annotation
- *
- * .annotation system Ldalvik/annotation/InnerClass;
- * accessFlags = 0x8
- * name = "Transform2"
- * .end annotation
- *
- *
- * # direct methods
- * .method constructor <init>()V
- * .registers 1
- *
- * .prologue
- * .line 26
- * invoke-direct {p0}, Ljava/lang/Object;-><init>()V
- *
- * return-void
- * .end method
- *
- *
- * # virtual methods
- * .method public getId()Ljava/lang/String;
- * .registers 2
- *
- * .prologue
- * .line 28
- * # NB Fails verification due to this function not returning a String.
- * return-void
- * .end method
- */
- static final Redefinition.CommonClassDefinition TRANSFORM2_INVALID =
- new Redefinition.CommonClassDefinition(Transform2.class,
- Base64.getDecoder().decode(
+ /**
+ * A base64 encoding of the following (invalid) class.
+ *
+ * .class LDexCacheSmash$Transform2;
+ * .super Ljava/lang/Object;
+ * .source "DexCacheSmash.java"
+ *
+ * # annotations
+ * .annotation system Ldalvik/annotation/EnclosingClass;
+ * value = LDexCacheSmash;
+ * .end annotation
+ *
+ * .annotation system Ldalvik/annotation/InnerClass;
+ * accessFlags = 0x8
+ * name = "Transform2"
+ * .end annotation
+ *
+ *
+ * # direct methods
+ * .method constructor <init>()V
+ * .registers 1
+ *
+ * .prologue
+ * .line 26
+ * invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ *
+ * return-void
+ * .end method
+ *
+ *
+ * # virtual methods
+ * .method public getId()Ljava/lang/String;
+ * .registers 2
+ *
+ * .prologue
+ * .line 28
+ * # NB Fails verification due to this function not returning a String.
+ * return-void
+ * .end method
+ */
+ static final byte[] TRANSFORM2_INVALID_CLASS_FILE_BYTES = Base64.getDecoder().decode(
"yv66vgAAADQAEwcAEgcAEQEABjxpbml0PgEAAygpVgEABENvZGUKAAIAEAEAD0xpbmVOdW1iZXJU" +
"YWJsZQEABWdldElkAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAApTb3VyY2VGaWxlAQASRGV4Q2Fj" +
"aGVTbWFzaC5qYXZhAQAMSW5uZXJDbGFzc2VzBwAPAQAKVHJhbnNmb3JtMgEADURleENhY2hlU21h" +
"c2gMAAMABAEAEGphdmEvbGFuZy9PYmplY3QBABhEZXhDYWNoZVNtYXNoJFRyYW5zZm9ybTIAIAAB" +
"AAIAAAAAAAIAAAADAAQAAQAFAAAAHQABAAEAAAAFKrcABrEAAAABAAcAAAAGAAEAAAAaAAEACAAJ" +
"AAEABQAAABkAAQABAAAAAbEAAAABAAcAAAAGAAEAAAAcAAIACgAAAAIACwAMAAAACgABAAEADQAO" +
- "AAg="),
- Base64.getDecoder().decode(
+ "AAg=");
+ static final byte[] TRANSFORM2_INVALID_DEX_FILE_BYTES = Base64.getDecoder().decode(
"ZGV4CjAzNQCFcegr6Ns+I7iEF4uLRkUX4yGrLhP6soEgAwAAcAAAAHhWNBIAAAAAAAAAAHQCAAAP" +
"AAAAcAAAAAcAAACsAAAAAgAAAMgAAAAAAAAAAAAAAAMAAADgAAAAAQAAAPgAAAAIAgAAGAEAABgB" +
"AAAgAQAANAEAADcBAABTAQAAZAEAAIgBAACoAQAAvAEAANABAADcAQAA3wEAAOwBAADzAQAA+QEA" +
@@ -130,26 +129,31 @@
"BA4AAAAAAAAAAQAAAAAAAAABAAAADwAAAHAAAAACAAAABwAAAKwAAAADAAAAAgAAAMgAAAAFAAAA" +
"AwAAAOAAAAAGAAAAAQAAAPgAAAACIAAADwAAABgBAAAEIAAAAgAAAAACAAADEAAAAgAAABACAAAG" +
"IAAAAQAAACACAAADIAAAAgAAADACAAABIAAAAgAAADwCAAAAIAAAAQAAAGYCAAAAEAAAAQAAAHQC" +
- "AAA="));
+ "AAA=");
+ static final Redefinition.CommonClassDefinition TRANSFORM2_INVALID =
+ new Redefinition.CommonClassDefinition(Transform2.class,
+ TRANSFORM2_INVALID_CLASS_FILE_BYTES, TRANSFORM2_INVALID_DEX_FILE_BYTES);
- public static void run() throws Exception {
- try {
- Redefinition.doMultiClassRedefinition(TRANSFORM2_INVALID);
- } catch (Exception e) {
- if (!e.getMessage().endsWith("JVMTI_ERROR_FAILS_VERIFICATION")) {
- throw new Error(
- "Unexpected error: Expected failure due to JVMTI_ERROR_FAILS_VERIFICATION", e);
- }
+ public static void run() throws Exception {
+ try {
+ Redefinition.doMultiClassRedefinition(TRANSFORM2_INVALID);
+ } catch (Exception e) {
+ if (!e.getMessage().endsWith("JVMTI_ERROR_FAILS_VERIFICATION")) {
+ throw new Error(
+ "Unexpected error: Expected failure due to JVMTI_ERROR_FAILS_VERIFICATION",
+ e);
+ }
+ }
+ // Doing this redefinition after a redefinition that failed due to FAILS_VERIFICATION could
+ // cause a use-after-free of the Transform2's DexCache by the redefinition code if it
+ // happens that the native pointer of the art::DexFile created for the Transform
+ // redefinition aliases the one created for Transform2's failed redefinition.
+ //
+ // Due to the order of checks performed by the redefinition code FAILS_VERIFICATION is the
+ // only failure mode that can cause Use-after-frees in this way.
+ //
+ // This should never throw any exceptions (except perhaps OOME in very strange
+ // circumstances).
+ Redefinition.doMultiClassRedefinition(TRANSFORM_INITIAL);
}
- // Doing this redefinition after a redefinition that failed due to FAILS_VERIFICATION could
- // cause a use-after-free of the Transform2's DexCache by the redefinition code if it happens
- // that the native pointer of the art::DexFile created for the Transform redefinition aliases
- // the one created for Transform2's failed redefinition.
- //
- // Due to the order of checks performed by the redefinition code FAILS_VERIFICATION is the only
- // failure mode that can cause Use-after-frees in this way.
- //
- // This should never throw any exceptions (except perhaps OOME in very strange circumstances).
- Redefinition.doMultiClassRedefinition(TRANSFORM_INITIAL);
- }
}
diff --git a/test/998-redefine-use-after-free/src-ex/art/Redefinition.java b/test/998-redefine-use-after-free/src-ex/art/Redefinition.java
index 56d2938..7b37afd 100644
--- a/test/998-redefine-use-after-free/src-ex/art/Redefinition.java
+++ b/test/998-redefine-use-after-free/src-ex/art/Redefinition.java
@@ -19,73 +19,69 @@
import java.util.ArrayList;
// Common Redefinition functions. Placed here for use by CTS
public class Redefinition {
- public static final class CommonClassDefinition {
- public final Class<?> target;
- public final byte[] class_file_bytes;
- public final byte[] dex_file_bytes;
+ public static final class CommonClassDefinition {
+ public final Class<?> target;
+ public final byte[] class_file_bytes;
+ public final byte[] dex_file_bytes;
- public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
- this.target = target;
- this.class_file_bytes = class_file_bytes;
- this.dex_file_bytes = dex_file_bytes;
+ public CommonClassDefinition(
+ Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+ this.target = target;
+ this.class_file_bytes = class_file_bytes;
+ this.dex_file_bytes = 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);
+ // 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);
- private final int val;
- private Config(int val) {
- this.val = val;
+ private final int val;
+ private Config(int val) {
+ this.val = val;
+ }
}
- }
- public static void setTestConfiguration(Config type) {
- nativeSetTestConfiguration(type.val);
- }
-
- private static native void nativeSetTestConfiguration(int type);
-
- // Transforms the class
- public static native void doCommonClassRedefinition(Class<?> target,
- byte[] classfile,
- byte[] dexfile);
-
- public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
- ArrayList<Class<?>> classes = new ArrayList<>();
- ArrayList<byte[]> class_files = new ArrayList<>();
- ArrayList<byte[]> dex_files = new ArrayList<>();
-
- for (CommonClassDefinition d : defs) {
- classes.add(d.target);
- class_files.add(d.class_file_bytes);
- dex_files.add(d.dex_file_bytes);
+ public static void setTestConfiguration(Config type) {
+ nativeSetTestConfiguration(type.val);
}
- doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
- class_files.toArray(new byte[0][]),
- dex_files.toArray(new byte[0][]));
- }
- public static void addMultiTransformationResults(CommonClassDefinition... defs) {
- for (CommonClassDefinition d : defs) {
- addCommonTransformationResult(d.target.getCanonicalName(),
- d.class_file_bytes,
- d.dex_file_bytes);
+ private static native void nativeSetTestConfiguration(int type);
+
+ // Transforms the class
+ public static native void doCommonClassRedefinition(
+ Class<?> target, byte[] classfile, byte[] dexfile);
+
+ public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+ ArrayList<Class<?>> classes = new ArrayList<>();
+ ArrayList<byte[]> class_files = new ArrayList<>();
+ ArrayList<byte[]> dex_files = new ArrayList<>();
+
+ for (CommonClassDefinition d : defs) {
+ classes.add(d.target);
+ class_files.add(d.class_file_bytes);
+ dex_files.add(d.dex_file_bytes);
+ }
+ doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+ class_files.toArray(new byte[0][]), dex_files.toArray(new byte[0][]));
}
- }
- public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
- byte[][] classfiles,
- byte[][] dexfiles);
- public static native void doCommonClassRetransformation(Class<?>... target);
- public static native void setPopRetransformations(boolean pop);
- public static native void popTransformationFor(String name);
- public static native void enableCommonRetransformation(boolean enable);
- public static native void addCommonTransformationResult(String target_name,
- byte[] class_bytes,
- byte[] dex_bytes);
+ public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+ for (CommonClassDefinition d : defs) {
+ addCommonTransformationResult(
+ d.target.getCanonicalName(), d.class_file_bytes, d.dex_file_bytes);
+ }
+ }
+
+ public static native void doCommonMultiClassRedefinition(
+ Class<?>[] targets, byte[][] classfiles, byte[][] dexfiles);
+ public static native void doCommonClassRetransformation(Class<?>... target);
+ public static native void setPopRetransformations(boolean pop);
+ public static native void popTransformationFor(String name);
+ public static native void enableCommonRetransformation(boolean enable);
+ public static native void addCommonTransformationResult(
+ String target_name, byte[] class_bytes, byte[] dex_bytes);
}
diff --git a/test/998-redefine-use-after-free/src/Main.java b/test/998-redefine-use-after-free/src/Main.java
index cd3babf..fcc759f 100644
--- a/test/998-redefine-use-after-free/src/Main.java
+++ b/test/998-redefine-use-after-free/src/Main.java
@@ -17,38 +17,39 @@
import java.lang.reflect.*;
public class Main {
- public static final String TEST_NAME = "998-redefine-use-after-free";
- public static final int REPS = 1000;
- public static final int STEP = 100;
+ public static final String TEST_NAME = "998-redefine-use-after-free";
+ public static final int REPS = 1000;
+ public static final int STEP = 100;
- public static void main(String[] args) throws Exception {
- for (int i = 0; i < REPS; i += STEP) {
- runSeveralTimes(STEP);
+ public static void main(String[] args) throws Exception {
+ for (int i = 0; i < REPS; i += STEP) {
+ runSeveralTimes(STEP);
+ }
}
- }
- public static ClassLoader getClassLoaderFor(String location) throws Exception {
- try {
- Class<?> class_loader_class = Class.forName("dalvik.system.PathClassLoader");
- Constructor<?> ctor = class_loader_class.getConstructor(String.class, ClassLoader.class);
- return (ClassLoader)ctor.newInstance(location + "/" + TEST_NAME + "-ex.jar",
- Main.class.getClassLoader());
- } catch (ClassNotFoundException e) {
- // Running on RI. Use URLClassLoader.
- return new java.net.URLClassLoader(
- new java.net.URL[] { new java.net.URL("file://" + location + "/classes-ex/") });
+ public static ClassLoader getClassLoaderFor(String location) throws Exception {
+ try {
+ Class<?> class_loader_class = Class.forName("dalvik.system.PathClassLoader");
+ Constructor<?> ctor =
+ class_loader_class.getConstructor(String.class, ClassLoader.class);
+ return (ClassLoader) ctor.newInstance(
+ location + "/" + TEST_NAME + "-ex.jar", Main.class.getClassLoader());
+ } catch (ClassNotFoundException e) {
+ // Running on RI. Use URLClassLoader.
+ return new java.net.URLClassLoader(
+ new java.net.URL[] { new java.net.URL("file://" + location + "/classes-ex/") });
+ }
}
- }
- // Run the redefinition several times on a single class-loader to try to trigger the
- // Use-after-free bug b/62237378
- public static void runSeveralTimes(int times) throws Exception {
- ClassLoader c = getClassLoaderFor(System.getenv("DEX_LOCATION"));
+ // Run the redefinition several times on a single class-loader to try to trigger the
+ // Use-after-free bug b/62237378
+ public static void runSeveralTimes(int times) throws Exception {
+ ClassLoader c = getClassLoaderFor(System.getenv("DEX_LOCATION"));
- Class<?> klass = (Class<?>)c.loadClass("DexCacheSmash");
- Method m = klass.getDeclaredMethod("run");
- for (int i = 0 ; i < times; i++) {
- m.invoke(null);
+ Class<?> klass = (Class<?>) c.loadClass("DexCacheSmash");
+ Method m = klass.getDeclaredMethod("run");
+ for (int i = 0; i < times; i++) {
+ m.invoke(null);
+ }
}
- }
}
diff --git a/test/999-redefine-hiddenapi/build b/test/999-redefine-hiddenapi/build
deleted file mode 100644
index f4b029f..0000000
--- a/test/999-redefine-hiddenapi/build
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright 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.
-
-USE_HIDDENAPI=true ./default-build "$@"
diff --git a/test/999-redefine-hiddenapi/build.py b/test/999-redefine-hiddenapi/build.py
new file mode 100644
index 0000000..942bb00
--- /dev/null
+++ b/test/999-redefine-hiddenapi/build.py
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.default_build(use_hiddenapi=True)
diff --git a/test/999-redefine-hiddenapi/run b/test/999-redefine-hiddenapi/run
deleted file mode 100755
index c6e62ae..0000000
--- a/test/999-redefine-hiddenapi/run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/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/999-redefine-hiddenapi/run.py b/test/999-redefine-hiddenapi/run.py
new file mode 100644
index 0000000..4796039
--- /dev/null
+++ b/test/999-redefine-hiddenapi/run.py
@@ -0,0 +1,19 @@
+#!/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.
+
+
+def run(ctx, args):
+ ctx.default_run(args, jvmti=True)
diff --git a/test/999-redefine-hiddenapi/src-ex/Test999.java b/test/999-redefine-hiddenapi/src-ex/Test999.java
index 97495c5..d82619f 100644
--- a/test/999-redefine-hiddenapi/src-ex/Test999.java
+++ b/test/999-redefine-hiddenapi/src-ex/Test999.java
@@ -17,9 +17,9 @@
package art;
public class Test999 {
- public void foo() {
- System.out.println("hello");
- }
+ public void foo() {
+ System.out.println("hello");
+ }
- public int bar = 42;
+ public int bar = 42;
}
diff --git a/test/999-redefine-hiddenapi/src-redefine/art/Test999.java b/test/999-redefine-hiddenapi/src-redefine/art/Test999.java
index c1b838c..3bdb2b6 100644
--- a/test/999-redefine-hiddenapi/src-redefine/art/Test999.java
+++ b/test/999-redefine-hiddenapi/src-redefine/art/Test999.java
@@ -17,9 +17,9 @@
package art;
public class Test999 {
- public void foo() {
- System.out.println("Goodbye");
- }
+ public void foo() {
+ System.out.println("Goodbye");
+ }
- public int bar = 64;
+ public int bar = 64;
}
diff --git a/test/999-redefine-hiddenapi/src/Main.java b/test/999-redefine-hiddenapi/src/Main.java
index 014ea16..70dc006 100644
--- a/test/999-redefine-hiddenapi/src/Main.java
+++ b/test/999-redefine-hiddenapi/src/Main.java
@@ -19,92 +19,92 @@
import java.util.Base64;
public class Main {
- public static void main(String[] args) throws ClassNotFoundException {
- System.loadLibrary(args[0]);
+ public static void main(String[] args) throws ClassNotFoundException {
+ System.loadLibrary(args[0]);
- // Run the initialization routine. This will enable hidden API checks in
- // the runtime, in case they are not enabled by default.
- init();
+ // Run the initialization routine. This will enable hidden API checks in
+ // the runtime, in case they are not enabled by default.
+ init();
- // Load the '-ex' APK and attach it to the boot class path.
- appendToBootClassLoader(DEX_EXTRA, /* isCorePlatform */ false);
+ // Load the '-ex' APK and attach it to the boot class path.
+ appendToBootClassLoader(DEX_EXTRA, /* isCorePlatform */ false);
- // Find the test class in boot class loader and verify that its members are hidden.
- Class<?> klass = Class.forName("art.Test999", true, BOOT_CLASS_LOADER);
- assertFieldIsHidden(klass, "before redefinition");
- assertMethodIsHidden(klass, "before redefinition");
+ // Find the test class in boot class loader and verify that its members are hidden.
+ Class<?> klass = Class.forName("art.Test999", true, BOOT_CLASS_LOADER);
+ assertFieldIsHidden(klass, "before redefinition");
+ assertMethodIsHidden(klass, "before redefinition");
- // Redefine the class using JVMTI. Use dex file without hiddenapi flags.
- art.Redefinition.setTestConfiguration(art.Redefinition.Config.COMMON_REDEFINE);
- art.Redefinition.doCommonClassRedefinition(klass, CLASS_BYTES, DEX_BYTES);
+ // Redefine the class using JVMTI. Use dex file without hiddenapi flags.
+ art.Redefinition.setTestConfiguration(art.Redefinition.Config.COMMON_REDEFINE);
+ art.Redefinition.doCommonClassRedefinition(klass, CLASS_BYTES, DEX_BYTES);
- // Verify that the class members are still hidden.
- assertFieldIsHidden(klass, "after first redefinition");
- assertMethodIsHidden(klass, "after first redefinition");
- }
-
- private static void assertMethodIsHidden(Class<?> klass, String msg) {
- try {
- klass.getDeclaredMethod("foo");
- // Unexpected. Should have thrown NoSuchMethodException.
- throw new RuntimeException("Method should not be accessible " + msg);
- } catch (NoSuchMethodException ex) {
+ // Verify that the class members are still hidden.
+ assertFieldIsHidden(klass, "after first redefinition");
+ assertMethodIsHidden(klass, "after first redefinition");
}
- }
- private static void assertFieldIsHidden(Class<?> klass, String msg) {
- try {
- klass.getDeclaredField("bar");
- // Unexpected. Should have thrown NoSuchFieldException.
- throw new RuntimeException("Field should not be accessible " + msg);
- } catch (NoSuchFieldException ex) {
+ private static void assertMethodIsHidden(Class<?> klass, String msg) {
+ try {
+ klass.getDeclaredMethod("foo");
+ // Unexpected. Should have thrown NoSuchMethodException.
+ throw new RuntimeException("Method should not be accessible " + msg);
+ } catch (NoSuchMethodException ex) {
+ }
}
- }
- private static final String DEX_EXTRA =
- new File(System.getenv("DEX_LOCATION"), "999-redefine-hiddenapi-ex.jar").getAbsolutePath();
+ private static void assertFieldIsHidden(Class<?> klass, String msg) {
+ try {
+ klass.getDeclaredField("bar");
+ // Unexpected. Should have thrown NoSuchFieldException.
+ throw new RuntimeException("Field should not be accessible " + msg);
+ } catch (NoSuchFieldException ex) {
+ }
+ }
- private static ClassLoader BOOT_CLASS_LOADER = Object.class.getClassLoader();
+ private static final String DEX_EXTRA = new File(
+ System.getenv("DEX_LOCATION"), "999-redefine-hiddenapi-ex.jar").getAbsolutePath();
- // Native functions. Note that these are implemented in 674-hiddenapi/hiddenapi.cc.
- private static native void appendToBootClassLoader(String dexPath, boolean isCorePlatform);
- private static native void init();
+ private static ClassLoader BOOT_CLASS_LOADER = Object.class.getClassLoader();
- /**
- * base64 encoded class/dex file for
- *
- * public class Test999 {
- * public void foo() {
- * System.out.println("Goodbye");
- * }
- *
- * public int bar = 64;
- * }
- */
- private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
- "yv66vgAAADUAIAoABwARCQAGABIJABMAFAgAFQoAFgAXBwAYBwAZAQADYmFyAQABSQEABjxpbml0" +
- "PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAANmb28BAApTb3VyY2VGaWxlAQAMVGVz" +
- "dDk5OS5qYXZhDAAKAAsMAAgACQcAGgwAGwAcAQAHR29vZGJ5ZQcAHQwAHgAfAQALYXJ0L1Rlc3Q5" +
- "OTkBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lv" +
- "L1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xh" +
- "bmcvU3RyaW5nOylWACEABgAHAAAAAQABAAgACQAAAAIAAQAKAAsAAQAMAAAAJwACAAEAAAALKrcA" +
- "ASoQQLUAArEAAAABAA0AAAAKAAIAAAATAAQAGAABAA4ACwABAAwAAAAlAAIAAQAAAAmyAAMSBLYA" +
- "BbEAAAABAA0AAAAKAAIAAAAVAAgAFgABAA8AAAACABA=");
- private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
- "ZGV4CjAzNQDlfmgFfKulToQpDF+P4dsgeOkgfzzH+5lgAwAAcAAAAHhWNBIAAAAAAAAAALQCAAAQ" +
- "AAAAcAAAAAcAAACwAAAAAgAAAMwAAAACAAAA5AAAAAQAAAD0AAAAAQAAABQBAAAsAgAANAEAAIYB" +
- "AACOAQAAlwEAAJoBAACpAQAAwAEAANQBAADoAQAA/AEAAAoCAAANAgAAEQIAABYCAAAbAgAAIAIA" +
- "ACkCAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAJAAAACQAAAAYAAAAAAAAACgAAAAYAAACAAQAA" +
- "AQAAAAsAAAAFAAIADQAAAAEAAAAAAAAAAQAAAAwAAAACAAEADgAAAAMAAAAAAAAAAQAAAAEAAAAD" +
- "AAAAAAAAAAgAAAAAAAAAoAIAAAAAAAACAAEAAQAAAHQBAAAIAAAAcBADAAEAEwBAAFkQAAAOAAMA" +
- "AQACAAAAeQEAAAgAAABiAAEAGgEBAG4gAgAQAA4AEwAOQAAVAA54AAAAAQAAAAQABjxpbml0PgAH" +
- "R29vZGJ5ZQABSQANTGFydC9UZXN0OTk5OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9s" +
- "YW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0" +
- "OTk5LmphdmEAAVYAAlZMAANiYXIAA2ZvbwADb3V0AAdwcmludGxuAHV+fkQ4eyJjb21waWxhdGlv" +
- "bi1tb2RlIjoiZGVidWciLCJtaW4tYXBpIjoxLCJzaGEtMSI6ImQyMmFiNGYxOWI3NTYxNDQ3NTI4" +
- "NTdjYTg2YjJjZWU0ZGQ5Y2ExNjYiLCJ2ZXJzaW9uIjoiMS40LjktZGV2In0AAAEBAQABAIGABLQC" +
- "AQHUAgAAAAAOAAAAAAAAAAEAAAAAAAAAAQAAABAAAABwAAAAAgAAAAcAAACwAAAAAwAAAAIAAADM" +
- "AAAABAAAAAIAAADkAAAABQAAAAQAAAD0AAAABgAAAAEAAAAUAQAAASAAAAIAAAA0AQAAAyAAAAIA" +
- "AAB0AQAAARAAAAEAAACAAQAAAiAAABAAAACGAQAAACAAAAEAAACgAgAAAxAAAAEAAACwAgAAABAA" +
- "AAEAAAC0AgAA");
+ // Native functions. Note that these are implemented in 674-hiddenapi/hiddenapi.cc.
+ private static native void appendToBootClassLoader(String dexPath, boolean isCorePlatform);
+ private static native void init();
+
+ /**
+ * base64 encoded class/dex file for
+ *
+ * public class Test999 {
+ * public void foo() {
+ * System.out.println("Goodbye");
+ * }
+ *
+ * public int bar = 64;
+ * }
+ */
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADUAIAoABwARCQAGABIJABMAFAgAFQoAFgAXBwAYBwAZAQADYmFyAQABSQEABjxpbml0" +
+ "PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAANmb28BAApTb3VyY2VGaWxlAQAMVGVz" +
+ "dDk5OS5qYXZhDAAKAAsMAAgACQcAGgwAGwAcAQAHR29vZGJ5ZQcAHQwAHgAfAQALYXJ0L1Rlc3Q5" +
+ "OTkBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lv" +
+ "L1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xh" +
+ "bmcvU3RyaW5nOylWACEABgAHAAAAAQABAAgACQAAAAIAAQAKAAsAAQAMAAAAJwACAAEAAAALKrcA" +
+ "ASoQQLUAArEAAAABAA0AAAAKAAIAAAATAAQAGAABAA4ACwABAAwAAAAlAAIAAQAAAAmyAAMSBLYA" +
+ "BbEAAAABAA0AAAAKAAIAAAAVAAgAFgABAA8AAAACABA=");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQDlfmgFfKulToQpDF+P4dsgeOkgfzzH+5lgAwAAcAAAAHhWNBIAAAAAAAAAALQCAAAQ" +
+ "AAAAcAAAAAcAAACwAAAAAgAAAMwAAAACAAAA5AAAAAQAAAD0AAAAAQAAABQBAAAsAgAANAEAAIYB" +
+ "AACOAQAAlwEAAJoBAACpAQAAwAEAANQBAADoAQAA/AEAAAoCAAANAgAAEQIAABYCAAAbAgAAIAIA" +
+ "ACkCAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAJAAAACQAAAAYAAAAAAAAACgAAAAYAAACAAQAA" +
+ "AQAAAAsAAAAFAAIADQAAAAEAAAAAAAAAAQAAAAwAAAACAAEADgAAAAMAAAAAAAAAAQAAAAEAAAAD" +
+ "AAAAAAAAAAgAAAAAAAAAoAIAAAAAAAACAAEAAQAAAHQBAAAIAAAAcBADAAEAEwBAAFkQAAAOAAMA" +
+ "AQACAAAAeQEAAAgAAABiAAEAGgEBAG4gAgAQAA4AEwAOQAAVAA54AAAAAQAAAAQABjxpbml0PgAH" +
+ "R29vZGJ5ZQABSQANTGFydC9UZXN0OTk5OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9s" +
+ "YW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AAxUZXN0" +
+ "OTk5LmphdmEAAVYAAlZMAANiYXIAA2ZvbwADb3V0AAdwcmludGxuAHV+fkQ4eyJjb21waWxhdGlv" +
+ "bi1tb2RlIjoiZGVidWciLCJtaW4tYXBpIjoxLCJzaGEtMSI6ImQyMmFiNGYxOWI3NTYxNDQ3NTI4" +
+ "NTdjYTg2YjJjZWU0ZGQ5Y2ExNjYiLCJ2ZXJzaW9uIjoiMS40LjktZGV2In0AAAEBAQABAIGABLQC" +
+ "AQHUAgAAAAAOAAAAAAAAAAEAAAAAAAAAAQAAABAAAABwAAAAAgAAAAcAAACwAAAAAwAAAAIAAADM" +
+ "AAAABAAAAAIAAADkAAAABQAAAAQAAAD0AAAABgAAAAEAAAAUAQAAASAAAAIAAAA0AQAAAyAAAAIA" +
+ "AAB0AQAAARAAAAEAAACAAQAAAiAAABAAAACGAQAAACAAAAEAAACgAgAAAxAAAAEAAACwAgAAABAA" +
+ "AAEAAAC0AgAA");
}
diff --git a/test/Android.bp b/test/Android.bp
index 8a1ca84..c5322cb 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -58,7 +58,11 @@
cflags: ["-Wno-frame-larger-than="],
},
host: {
- cflags: ["-Wno-frame-larger-than="],
+ cflags: [
+ "-Wno-frame-larger-than=",
+ "-fsanitize-address-use-after-return=never",
+ "-Wno-unused-command-line-argument",
+ ],
},
},
}
@@ -104,10 +108,10 @@
],
}
-// Variant of art_test_defaults that installs the library in a location which
-// will be added to the default namespace, and hence also the com_android_art
-// namespace. That allows the library to have shared_libs dependencies on all
-// ART internal libraries.
+// Variant of art_test_defaults for test libraries that installs them in a
+// location which will be added to the default namespace, and hence also the
+// com_android_art namespace. That allows them to have shared_libs
+// dependencies on all ART internal libraries.
//
// Currently this only works for run tests where run-test-jar sets
// LD_LIBRARY_PATH and NATIVELOADER_DEFAULT_NAMESPACE_LIBS.
@@ -170,6 +174,21 @@
// eventually.
host_supported: false,
test_config_template: ":art-gtests-target-standalone-template",
+
+ // Support multilib variants (using different suffix per sub-architecture),
+ // which is needed on build targets with secondary architectures, as the
+ // CTS/MTS/etc test suite packaging logic flattens all test artifacts into a
+ // single `testcases` directory. Also, there is CI testing that expects
+ // 64-bit multilib test suites to work for 32-bit devices (b/233550842).
+ compile_multilib: "both",
+ multilib: {
+ lib32: {
+ suffix: "32",
+ },
+ lib64: {
+ suffix: "64",
+ },
+ },
}
// Properties common to `art_gtest_defaults` and `art_standalone_gtest_defaults`.
@@ -205,6 +224,10 @@
shared_libs: [
"libziparchive",
],
+ cflags: [
+ "-fsanitize-address-use-after-return=never",
+ "-Wno-unused-command-line-argument",
+ ],
},
},
}
@@ -328,6 +351,12 @@
"libgtest_isolated",
],
target: {
+ host: {
+ cflags: [
+ "-fsanitize-address-use-after-return=never",
+ "-Wno-unused-command-line-argument",
+ ],
+ },
android32: {
cflags: ["-DART_TARGET_NATIVETEST_DIR=/data/nativetest/art"],
},
@@ -399,10 +428,15 @@
],
shared_libs: [
"libbase",
- "libbacktrace",
"liblog",
],
target: {
+ host: {
+ cflags: [
+ "-fsanitize-address-use-after-return=never",
+ "-Wno-unused-command-line-argument",
+ ],
+ },
darwin: {
enabled: false,
},
@@ -581,7 +615,6 @@
name: "libartagent-defaults",
defaults: ["art_test_internal_library_defaults"],
shared_libs: [
- "libbacktrace",
"libbase",
"liblog",
"libnativehelper",
@@ -671,6 +704,7 @@
"991-field-trace-2/field_trace.cc",
"992-source-data/source_file.cc",
"993-breakpoints/breakpoints.cc",
+ "993-breakpoints-non-debuggable/onload.cc",
"996-breakpoint-obsolete/obsolete_breakpoints.cc",
"1900-track-alloc/alloc.cc",
"1901-get-bytecodes/bytecodes.cc",
@@ -689,6 +723,7 @@
"1932-monitor-events-misc/monitor_misc.cc",
"1934-jvmti-signal-thread/signal_threads.cc",
"1939-proxy-frames/local_instance.cc",
+ "1940-ddms-ext/ddm_ext.cc",
"1941-dispose-stress/dispose_stress.cc",
"1942-suspend-raw-monitor-exit/native_suspend_monitor.cc",
"1943-suspend-raw-monitor-wait/native_suspend_monitor.cc",
@@ -736,7 +771,7 @@
"936-search-onload/search_onload.cc",
"980-redefine-object/redef_object.cc",
"983-source-transform-verify/source_transform_art.cc",
- "1940-ddms-ext/ddm_ext.cc",
+ "993-breakpoints-non-debuggable/native_attach_agent.cc",
// "1952-pop-frame-jit/pop_frame.cc",
"1959-redefine-object-instrument/fake_redef_object.cc",
"1960-obsolete-jit-multithread-native/native_say_hi.cc",
@@ -933,6 +968,7 @@
"800-smali/jni.cc",
"817-hiddenapi/test_native.cc",
"909-attach-agent/disallow_debugging.cc",
+ "993-breakpoints-non-debuggable/native_attach_agent.cc",
"1001-app-image-regions/app_image_regions.cc",
"1002-notify-startup/startup_interface.cc",
"1947-breakpoint-redefine-deopt/check_deopt.cc",
@@ -949,10 +985,10 @@
"common/stack_inspect.cc",
],
shared_libs: [
- "libbacktrace",
"libbase",
"liblog",
"libnativehelper",
+ "libunwindstack",
],
}
@@ -1037,6 +1073,7 @@
"992-source-data/src/art/Test992.java",
"992-source-data/src/art/Target2.java",
"993-breakpoints/src/art/Test993.java",
+ "993-breakpoints-non-debuggable/src/art/Test993AttachAgent.java",
"994-breakpoint-line/src/art/Test994.java",
"995-breakpoints-throw/src/art/Test995.java",
"996-breakpoint-obsolete/src/art/Test996.java",
@@ -1075,6 +1112,7 @@
"1936-thread-end-events/src/art/Test1936.java",
"1937-transform-soft-fail/src/art/Test1937.java",
"1939-proxy-frames/src/art/Test1939.java",
+ "1940-ddms-ext/src-art/art/Test1940.java",
"1941-dispose-stress/src/art/Test1941.java",
"1942-suspend-raw-monitor-exit/src/art/Test1942.java",
"1943-suspend-raw-monitor-wait/src/art/Test1943.java",
@@ -1121,6 +1159,8 @@
"2007-virtual-structural-finalizable/src-art/art/Test2007.java",
],
sdk_version: "core_platform",
+ // Make sure that this will be added to the sdk snapshot for S.
+ min_sdk_version: "S",
// Some ART run-tests contain constructs which break ErrorProne checks;
// disable `errorprone` builds.
errorprone: {
@@ -1182,6 +1222,7 @@
"992-source-data/expected-stdout.txt",
// Need to avoid using hidden-apis
"993-breakpoints/expected_cts.txt",
+ "993-breakpoints-non-debuggable/expected_cts.txt",
"994-breakpoint-line/expected-stdout.txt",
"995-breakpoints-throw/expected-stdout.txt",
"996-breakpoint-obsolete/expected-stdout.txt",
@@ -1220,6 +1261,7 @@
"1936-thread-end-events/expected-stdout.txt",
"1937-transform-soft-fail/expected-stdout.txt",
"1939-proxy-frames/expected-stdout.txt",
+ "1940-ddms-ext/expected-stdout.txt",
"1941-dispose-stress/expected-stdout.txt",
"1942-suspend-raw-monitor-exit/expected-stdout.txt",
"1943-suspend-raw-monitor-wait/expected-stdout.txt",
@@ -1284,6 +1326,8 @@
"expected_cts_outputs_gen",
],
sdk_version: "core_current",
+ // Make sure that this will be added to the sdk snapshot for S.
+ min_sdk_version: "S",
}
art_cc_test {
@@ -1349,38 +1393,12 @@
test_config_template: "csuite-app-compile-launch.xml",
}
-// Install run-test data in the output directory.
-prebuilt_etc_host {
- name: "art-run-test-host-data",
- defaults: ["art_module_source_build_prebuilt_defaults"],
- src: ":art-run-test-host-data-merged",
- sub_dir: "art",
- filename: "art-run-test-host-data.zip",
-}
-
-// Install run-test data in the output directory.
-prebuilt_etc_host {
- name: "art-run-test-jvm-data",
- defaults: ["art_module_source_build_prebuilt_defaults"],
- src: ":art-run-test-jvm-data-merged",
- sub_dir: "art",
- filename: "art-run-test-jvm-data.zip",
-}
-
-// Install run-test data in the output directory.
-prebuilt_etc_host {
- name: "art-run-test-target-data",
- defaults: ["art_module_source_build_prebuilt_defaults"],
- src: ":art-run-test-target-data-merged",
- sub_dir: "art",
- filename: "art-run-test-target-data.zip",
-}
-
filegroup {
name: "art-gtest-jars",
srcs: [
":art-gtest-jars-AbstractMethod",
":art-gtest-jars-AllFields",
+ ":art-gtest-jars-ArrayClassWithUnresolvedComponent",
":art-gtest-jars-DefaultMethods",
":art-gtest-jars-ErroneousA",
":art-gtest-jars-ErroneousB",
@@ -1427,6 +1445,7 @@
":art-gtest-jars-MainStripped",
":art-gtest-jars-MainUncompressedAligned",
":art-gtest-jars-MultiDexUncompressedAligned",
+ ":art-gtest-jars-SuperWithAccessChecks",
":art-gtest-jars-VerifierDeps",
":art-gtest-jars-VerifierDepsMulti",
":art-gtest-jars-VerifySoftFailDuringClinit",
@@ -1832,6 +1851,20 @@
}
genrule {
+ name: "art-gtest-jars-ArrayClassWithUnresolvedComponent",
+ defaults: ["art-gtest-jars-smali-defaults"],
+ srcs: ["ArrayClassWithUnresolvedComponent/*.smali"],
+ out: ["art-gtest-jars-ArrayClassWithUnresolvedComponent.dex"],
+}
+
+genrule {
+ name: "art-gtest-jars-SuperWithAccessChecks",
+ defaults: ["art-gtest-jars-smali-defaults"],
+ srcs: ["SuperWithAccessChecks/*.smali"],
+ out: ["art-gtest-jars-SuperWithAccessChecks.dex"],
+}
+
+genrule {
name: "art-gtest-jars-LinkageTest",
defaults: ["art-gtest-jars-smali-defaults"],
srcs: ["LinkageTest/*.smali"],
@@ -1853,11 +1886,7 @@
"art_module_source_build_genrule_defaults",
],
tool_files: [
- "run-test-build.py",
- "buildfailures.json",
- "etc/default-build",
- "etc/default-run",
- "etc/default-check",
+ "run_test_build.py",
":art-run-test-bootclasspath",
],
tools: [
diff --git a/test/Android.run-test.bp b/test/Android.run-test.bp
index 27666f8..b5fed42 100644
--- a/test/Android.run-test.bp
+++ b/test/Android.run-test.bp
@@ -2,3031 +2,6301 @@
// It is not necessary to regenerate it when tests are added/removed/modified.
java_genrule {
- name: "art-run-test-host-data-shard00",
+ name: "art-run-test-host-data-shard00-tmp",
out: ["art-run-test-host-data-shard00.zip"],
- srcs: ["*00-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 00 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?00-*/**/*", "??00-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard00",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard00-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard00.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard01",
+ name: "art-run-test-host-data-shard01-tmp",
out: ["art-run-test-host-data-shard01.zip"],
- srcs: ["*01-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 01 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?01-*/**/*", "??01-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard01",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard01-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard01.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard02",
+ name: "art-run-test-host-data-shard02-tmp",
out: ["art-run-test-host-data-shard02.zip"],
- srcs: ["*02-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 02 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?02-*/**/*", "??02-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard02",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard02-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard02.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard03",
+ name: "art-run-test-host-data-shard03-tmp",
out: ["art-run-test-host-data-shard03.zip"],
- srcs: ["*03-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 03 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?03-*/**/*", "??03-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard03",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard03-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard03.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard04",
+ name: "art-run-test-host-data-shard04-tmp",
out: ["art-run-test-host-data-shard04.zip"],
- srcs: ["*04-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 04 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?04-*/**/*", "??04-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard04",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard04-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard04.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard05",
+ name: "art-run-test-host-data-shard05-tmp",
out: ["art-run-test-host-data-shard05.zip"],
- srcs: ["*05-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 05 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?05-*/**/*", "??05-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard05",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard05-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard05.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard06",
+ name: "art-run-test-host-data-shard06-tmp",
out: ["art-run-test-host-data-shard06.zip"],
- srcs: ["*06-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 06 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?06-*/**/*", "??06-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard06",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard06-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard06.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard07",
+ name: "art-run-test-host-data-shard07-tmp",
out: ["art-run-test-host-data-shard07.zip"],
- srcs: ["*07-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 07 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?07-*/**/*", "??07-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard07",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard07-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard07.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard08",
+ name: "art-run-test-host-data-shard08-tmp",
out: ["art-run-test-host-data-shard08.zip"],
- srcs: ["*08-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 08 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?08-*/**/*", "??08-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard08",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard08-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard08.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard09",
+ name: "art-run-test-host-data-shard09-tmp",
out: ["art-run-test-host-data-shard09.zip"],
- srcs: ["*09-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 09 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?09-*/**/*", "??09-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard09",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard09-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard09.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard10",
+ name: "art-run-test-host-data-shard10-tmp",
out: ["art-run-test-host-data-shard10.zip"],
- srcs: ["*10-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 10 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?10-*/**/*", "??10-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard10",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard10-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard10.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard11",
+ name: "art-run-test-host-data-shard11-tmp",
out: ["art-run-test-host-data-shard11.zip"],
- srcs: ["*11-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 11 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?11-*/**/*", "??11-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard11",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard11-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard11.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard12",
+ name: "art-run-test-host-data-shard12-tmp",
out: ["art-run-test-host-data-shard12.zip"],
- srcs: ["*12-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 12 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?12-*/**/*", "??12-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard12",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard12-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard12.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard13",
+ name: "art-run-test-host-data-shard13-tmp",
out: ["art-run-test-host-data-shard13.zip"],
- srcs: ["*13-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 13 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?13-*/**/*", "??13-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard13",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard13-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard13.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard14",
+ name: "art-run-test-host-data-shard14-tmp",
out: ["art-run-test-host-data-shard14.zip"],
- srcs: ["*14-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 14 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?14-*/**/*", "??14-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard14",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard14-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard14.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard15",
+ name: "art-run-test-host-data-shard15-tmp",
out: ["art-run-test-host-data-shard15.zip"],
- srcs: ["*15-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 15 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?15-*/**/*", "??15-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard15",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard15-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard15.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard16",
+ name: "art-run-test-host-data-shard16-tmp",
out: ["art-run-test-host-data-shard16.zip"],
- srcs: ["*16-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 16 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?16-*/**/*", "??16-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard16",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard16-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard16.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard17",
+ name: "art-run-test-host-data-shard17-tmp",
out: ["art-run-test-host-data-shard17.zip"],
- srcs: ["*17-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 17 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?17-*/**/*", "??17-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard17",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard17-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard17.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard18",
+ name: "art-run-test-host-data-shard18-tmp",
out: ["art-run-test-host-data-shard18.zip"],
- srcs: ["*18-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 18 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?18-*/**/*", "??18-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard18",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard18-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard18.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard19",
+ name: "art-run-test-host-data-shard19-tmp",
out: ["art-run-test-host-data-shard19.zip"],
- srcs: ["*19-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 19 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?19-*/**/*", "??19-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard19",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard19-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard19.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard20",
+ name: "art-run-test-host-data-shard20-tmp",
out: ["art-run-test-host-data-shard20.zip"],
- srcs: ["*20-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 20 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?20-*/**/*", "??20-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard20",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard20-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard20.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard21",
+ name: "art-run-test-host-data-shard21-tmp",
out: ["art-run-test-host-data-shard21.zip"],
- srcs: ["*21-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 21 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?21-*/**/*", "??21-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard21",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard21-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard21.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard22",
+ name: "art-run-test-host-data-shard22-tmp",
out: ["art-run-test-host-data-shard22.zip"],
- srcs: ["*22-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 22 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?22-*/**/*", "??22-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard22",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard22-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard22.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard23",
+ name: "art-run-test-host-data-shard23-tmp",
out: ["art-run-test-host-data-shard23.zip"],
- srcs: ["*23-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 23 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?23-*/**/*", "??23-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard23",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard23-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard23.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard24",
+ name: "art-run-test-host-data-shard24-tmp",
out: ["art-run-test-host-data-shard24.zip"],
- srcs: ["*24-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 24 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?24-*/**/*", "??24-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard24",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard24-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard24.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard25",
+ name: "art-run-test-host-data-shard25-tmp",
out: ["art-run-test-host-data-shard25.zip"],
- srcs: ["*25-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 25 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?25-*/**/*", "??25-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard25",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard25-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard25.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard26",
+ name: "art-run-test-host-data-shard26-tmp",
out: ["art-run-test-host-data-shard26.zip"],
- srcs: ["*26-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 26 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?26-*/**/*", "??26-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard26",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard26-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard26.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard27",
+ name: "art-run-test-host-data-shard27-tmp",
out: ["art-run-test-host-data-shard27.zip"],
- srcs: ["*27-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 27 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?27-*/**/*", "??27-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard27",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard27-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard27.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard28",
+ name: "art-run-test-host-data-shard28-tmp",
out: ["art-run-test-host-data-shard28.zip"],
- srcs: ["*28-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 28 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?28-*/**/*", "??28-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard28",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard28-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard28.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard29",
+ name: "art-run-test-host-data-shard29-tmp",
out: ["art-run-test-host-data-shard29.zip"],
- srcs: ["*29-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 29 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?29-*/**/*", "??29-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard29",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard29-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard29.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard30",
+ name: "art-run-test-host-data-shard30-tmp",
out: ["art-run-test-host-data-shard30.zip"],
- srcs: ["*30-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 30 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?30-*/**/*", "??30-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard30",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard30-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard30.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard31",
+ name: "art-run-test-host-data-shard31-tmp",
out: ["art-run-test-host-data-shard31.zip"],
- srcs: ["*31-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 31 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?31-*/**/*", "??31-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard31",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard31-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard31.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard32",
+ name: "art-run-test-host-data-shard32-tmp",
out: ["art-run-test-host-data-shard32.zip"],
- srcs: ["*32-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 32 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?32-*/**/*", "??32-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard32",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard32-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard32.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard33",
+ name: "art-run-test-host-data-shard33-tmp",
out: ["art-run-test-host-data-shard33.zip"],
- srcs: ["*33-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 33 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?33-*/**/*", "??33-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard33",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard33-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard33.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard34",
+ name: "art-run-test-host-data-shard34-tmp",
out: ["art-run-test-host-data-shard34.zip"],
- srcs: ["*34-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 34 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?34-*/**/*", "??34-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard34",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard34-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard34.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard35",
+ name: "art-run-test-host-data-shard35-tmp",
out: ["art-run-test-host-data-shard35.zip"],
- srcs: ["*35-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 35 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?35-*/**/*", "??35-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard35",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard35-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard35.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard36",
+ name: "art-run-test-host-data-shard36-tmp",
out: ["art-run-test-host-data-shard36.zip"],
- srcs: ["*36-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 36 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?36-*/**/*", "??36-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard36",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard36-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard36.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard37",
+ name: "art-run-test-host-data-shard37-tmp",
out: ["art-run-test-host-data-shard37.zip"],
- srcs: ["*37-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 37 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?37-*/**/*", "??37-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard37",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard37-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard37.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard38",
+ name: "art-run-test-host-data-shard38-tmp",
out: ["art-run-test-host-data-shard38.zip"],
- srcs: ["*38-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 38 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?38-*/**/*", "??38-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard38",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard38-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard38.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard39",
+ name: "art-run-test-host-data-shard39-tmp",
out: ["art-run-test-host-data-shard39.zip"],
- srcs: ["*39-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 39 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?39-*/**/*", "??39-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard39",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard39-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard39.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard40",
+ name: "art-run-test-host-data-shard40-tmp",
out: ["art-run-test-host-data-shard40.zip"],
- srcs: ["*40-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 40 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?40-*/**/*", "??40-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard40",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard40-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard40.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard41",
+ name: "art-run-test-host-data-shard41-tmp",
out: ["art-run-test-host-data-shard41.zip"],
- srcs: ["*41-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 41 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?41-*/**/*", "??41-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard41",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard41-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard41.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard42",
+ name: "art-run-test-host-data-shard42-tmp",
out: ["art-run-test-host-data-shard42.zip"],
- srcs: ["*42-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 42 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?42-*/**/*", "??42-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard42",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard42-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard42.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard43",
+ name: "art-run-test-host-data-shard43-tmp",
out: ["art-run-test-host-data-shard43.zip"],
- srcs: ["*43-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 43 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?43-*/**/*", "??43-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard43",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard43-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard43.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard44",
+ name: "art-run-test-host-data-shard44-tmp",
out: ["art-run-test-host-data-shard44.zip"],
- srcs: ["*44-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 44 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?44-*/**/*", "??44-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard44",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard44-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard44.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard45",
+ name: "art-run-test-host-data-shard45-tmp",
out: ["art-run-test-host-data-shard45.zip"],
- srcs: ["*45-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 45 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?45-*/**/*", "??45-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard45",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard45-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard45.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard46",
+ name: "art-run-test-host-data-shard46-tmp",
out: ["art-run-test-host-data-shard46.zip"],
- srcs: ["*46-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 46 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?46-*/**/*", "??46-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard46",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard46-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard46.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard47",
+ name: "art-run-test-host-data-shard47-tmp",
out: ["art-run-test-host-data-shard47.zip"],
- srcs: ["*47-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 47 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?47-*/**/*", "??47-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard47",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard47-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard47.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard48",
+ name: "art-run-test-host-data-shard48-tmp",
out: ["art-run-test-host-data-shard48.zip"],
- srcs: ["*48-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 48 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?48-*/**/*", "??48-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard48",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard48-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard48.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard49",
+ name: "art-run-test-host-data-shard49-tmp",
out: ["art-run-test-host-data-shard49.zip"],
- srcs: ["*49-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 49 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?49-*/**/*", "??49-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard49",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard49-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard49.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard50",
+ name: "art-run-test-host-data-shard50-tmp",
out: ["art-run-test-host-data-shard50.zip"],
- srcs: ["*50-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 50 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?50-*/**/*", "??50-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard50",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard50-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard50.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard51",
+ name: "art-run-test-host-data-shard51-tmp",
out: ["art-run-test-host-data-shard51.zip"],
- srcs: ["*51-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 51 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?51-*/**/*", "??51-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard51",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard51-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard51.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard52",
+ name: "art-run-test-host-data-shard52-tmp",
out: ["art-run-test-host-data-shard52.zip"],
- srcs: ["*52-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 52 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?52-*/**/*", "??52-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard52",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard52-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard52.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard53",
+ name: "art-run-test-host-data-shard53-tmp",
out: ["art-run-test-host-data-shard53.zip"],
- srcs: ["*53-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 53 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?53-*/**/*", "??53-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard53",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard53-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard53.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard54",
+ name: "art-run-test-host-data-shard54-tmp",
out: ["art-run-test-host-data-shard54.zip"],
- srcs: ["*54-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 54 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?54-*/**/*", "??54-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard54",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard54-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard54.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard55",
+ name: "art-run-test-host-data-shard55-tmp",
out: ["art-run-test-host-data-shard55.zip"],
- srcs: ["*55-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 55 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?55-*/**/*", "??55-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard55",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard55-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard55.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard56",
+ name: "art-run-test-host-data-shard56-tmp",
out: ["art-run-test-host-data-shard56.zip"],
- srcs: ["*56-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 56 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?56-*/**/*", "??56-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard56",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard56-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard56.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard57",
+ name: "art-run-test-host-data-shard57-tmp",
out: ["art-run-test-host-data-shard57.zip"],
- srcs: ["*57-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 57 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?57-*/**/*", "??57-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard57",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard57-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard57.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard58",
+ name: "art-run-test-host-data-shard58-tmp",
out: ["art-run-test-host-data-shard58.zip"],
- srcs: ["*58-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 58 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?58-*/**/*", "??58-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard58",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard58-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard58.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard59",
+ name: "art-run-test-host-data-shard59-tmp",
out: ["art-run-test-host-data-shard59.zip"],
- srcs: ["*59-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 59 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?59-*/**/*", "??59-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard59",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard59-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard59.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard60",
+ name: "art-run-test-host-data-shard60-tmp",
out: ["art-run-test-host-data-shard60.zip"],
- srcs: ["*60-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 60 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?60-*/**/*", "??60-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard60",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard60-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard60.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard61",
+ name: "art-run-test-host-data-shard61-tmp",
out: ["art-run-test-host-data-shard61.zip"],
- srcs: ["*61-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 61 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?61-*/**/*", "??61-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard61",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard61-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard61.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard62",
+ name: "art-run-test-host-data-shard62-tmp",
out: ["art-run-test-host-data-shard62.zip"],
- srcs: ["*62-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 62 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?62-*/**/*", "??62-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard62",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard62-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard62.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard63",
+ name: "art-run-test-host-data-shard63-tmp",
out: ["art-run-test-host-data-shard63.zip"],
- srcs: ["*63-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 63 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?63-*/**/*", "??63-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard63",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard63-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard63.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard64",
+ name: "art-run-test-host-data-shard64-tmp",
out: ["art-run-test-host-data-shard64.zip"],
- srcs: ["*64-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 64 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?64-*/**/*", "??64-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard64",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard64-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard64.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard65",
+ name: "art-run-test-host-data-shard65-tmp",
out: ["art-run-test-host-data-shard65.zip"],
- srcs: ["*65-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 65 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?65-*/**/*", "??65-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard65",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard65-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard65.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard66",
+ name: "art-run-test-host-data-shard66-tmp",
out: ["art-run-test-host-data-shard66.zip"],
- srcs: ["*66-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 66 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?66-*/**/*", "??66-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard66",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard66-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard66.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard67",
+ name: "art-run-test-host-data-shard67-tmp",
out: ["art-run-test-host-data-shard67.zip"],
- srcs: ["*67-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 67 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?67-*/**/*", "??67-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard67",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard67-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard67.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard68",
+ name: "art-run-test-host-data-shard68-tmp",
out: ["art-run-test-host-data-shard68.zip"],
- srcs: ["*68-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 68 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?68-*/**/*", "??68-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard68",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard68-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard68.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard69",
+ name: "art-run-test-host-data-shard69-tmp",
out: ["art-run-test-host-data-shard69.zip"],
- srcs: ["*69-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 69 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?69-*/**/*", "??69-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard69",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard69-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard69.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard70",
+ name: "art-run-test-host-data-shard70-tmp",
out: ["art-run-test-host-data-shard70.zip"],
- srcs: ["*70-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 70 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?70-*/**/*", "??70-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard70",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard70-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard70.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard71",
+ name: "art-run-test-host-data-shard71-tmp",
out: ["art-run-test-host-data-shard71.zip"],
- srcs: ["*71-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 71 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?71-*/**/*", "??71-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard71",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard71-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard71.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard72",
+ name: "art-run-test-host-data-shard72-tmp",
out: ["art-run-test-host-data-shard72.zip"],
- srcs: ["*72-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 72 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?72-*/**/*", "??72-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard72",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard72-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard72.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard73",
+ name: "art-run-test-host-data-shard73-tmp",
out: ["art-run-test-host-data-shard73.zip"],
- srcs: ["*73-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 73 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?73-*/**/*", "??73-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard73",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard73-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard73.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard74",
+ name: "art-run-test-host-data-shard74-tmp",
out: ["art-run-test-host-data-shard74.zip"],
- srcs: ["*74-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 74 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?74-*/**/*", "??74-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard74",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard74-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard74.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard75",
+ name: "art-run-test-host-data-shard75-tmp",
out: ["art-run-test-host-data-shard75.zip"],
- srcs: ["*75-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 75 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?75-*/**/*", "??75-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard75",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard75-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard75.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard76",
+ name: "art-run-test-host-data-shard76-tmp",
out: ["art-run-test-host-data-shard76.zip"],
- srcs: ["*76-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 76 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?76-*/**/*", "??76-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard76",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard76-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard76.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard77",
+ name: "art-run-test-host-data-shard77-tmp",
out: ["art-run-test-host-data-shard77.zip"],
- srcs: ["*77-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 77 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?77-*/**/*", "??77-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard77",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard77-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard77.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard78",
+ name: "art-run-test-host-data-shard78-tmp",
out: ["art-run-test-host-data-shard78.zip"],
- srcs: ["*78-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 78 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?78-*/**/*", "??78-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard78",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard78-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard78.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard79",
+ name: "art-run-test-host-data-shard79-tmp",
out: ["art-run-test-host-data-shard79.zip"],
- srcs: ["*79-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 79 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?79-*/**/*", "??79-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard79",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard79-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard79.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard80",
+ name: "art-run-test-host-data-shard80-tmp",
out: ["art-run-test-host-data-shard80.zip"],
- srcs: ["*80-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 80 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?80-*/**/*", "??80-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard80",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard80-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard80.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard81",
+ name: "art-run-test-host-data-shard81-tmp",
out: ["art-run-test-host-data-shard81.zip"],
- srcs: ["*81-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 81 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?81-*/**/*", "??81-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard81",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard81-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard81.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard82",
+ name: "art-run-test-host-data-shard82-tmp",
out: ["art-run-test-host-data-shard82.zip"],
- srcs: ["*82-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 82 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?82-*/**/*", "??82-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard82",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard82-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard82.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard83",
+ name: "art-run-test-host-data-shard83-tmp",
out: ["art-run-test-host-data-shard83.zip"],
- srcs: ["*83-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 83 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?83-*/**/*", "??83-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard83",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard83-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard83.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard84",
+ name: "art-run-test-host-data-shard84-tmp",
out: ["art-run-test-host-data-shard84.zip"],
- srcs: ["*84-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 84 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?84-*/**/*", "??84-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard84",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard84-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard84.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard85",
+ name: "art-run-test-host-data-shard85-tmp",
out: ["art-run-test-host-data-shard85.zip"],
- srcs: ["*85-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 85 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?85-*/**/*", "??85-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard85",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard85-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard85.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard86",
+ name: "art-run-test-host-data-shard86-tmp",
out: ["art-run-test-host-data-shard86.zip"],
- srcs: ["*86-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 86 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?86-*/**/*", "??86-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard86",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard86-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard86.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard87",
+ name: "art-run-test-host-data-shard87-tmp",
out: ["art-run-test-host-data-shard87.zip"],
- srcs: ["*87-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 87 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?87-*/**/*", "??87-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard87",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard87-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard87.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard88",
+ name: "art-run-test-host-data-shard88-tmp",
out: ["art-run-test-host-data-shard88.zip"],
- srcs: ["*88-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 88 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?88-*/**/*", "??88-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard88",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard88-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard88.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard89",
+ name: "art-run-test-host-data-shard89-tmp",
out: ["art-run-test-host-data-shard89.zip"],
- srcs: ["*89-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 89 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?89-*/**/*", "??89-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard89",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard89-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard89.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard90",
+ name: "art-run-test-host-data-shard90-tmp",
out: ["art-run-test-host-data-shard90.zip"],
- srcs: ["*90-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 90 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?90-*/**/*", "??90-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard90",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard90-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard90.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard91",
+ name: "art-run-test-host-data-shard91-tmp",
out: ["art-run-test-host-data-shard91.zip"],
- srcs: ["*91-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 91 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?91-*/**/*", "??91-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard91",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard91-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard91.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard92",
+ name: "art-run-test-host-data-shard92-tmp",
out: ["art-run-test-host-data-shard92.zip"],
- srcs: ["*92-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 92 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?92-*/**/*", "??92-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard92",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard92-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard92.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard93",
+ name: "art-run-test-host-data-shard93-tmp",
out: ["art-run-test-host-data-shard93.zip"],
- srcs: ["*93-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 93 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?93-*/**/*", "??93-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard93",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard93-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard93.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard94",
+ name: "art-run-test-host-data-shard94-tmp",
out: ["art-run-test-host-data-shard94.zip"],
- srcs: ["*94-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 94 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?94-*/**/*", "??94-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard94",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard94-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard94.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard95",
+ name: "art-run-test-host-data-shard95-tmp",
out: ["art-run-test-host-data-shard95.zip"],
- srcs: ["*95-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 95 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?95-*/**/*", "??95-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard95",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard95-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard95.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard96",
+ name: "art-run-test-host-data-shard96-tmp",
out: ["art-run-test-host-data-shard96.zip"],
- srcs: ["*96-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 96 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?96-*/**/*", "??96-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard96",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard96-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard96.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard97",
+ name: "art-run-test-host-data-shard97-tmp",
out: ["art-run-test-host-data-shard97.zip"],
- srcs: ["*97-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 97 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?97-*/**/*", "??97-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard97",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard97-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard97.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard98",
+ name: "art-run-test-host-data-shard98-tmp",
out: ["art-run-test-host-data-shard98.zip"],
- srcs: ["*98-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 98 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?98-*/**/*", "??98-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard98",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard98-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard98.zip",
}
java_genrule {
- name: "art-run-test-host-data-shard99",
+ name: "art-run-test-host-data-shard99-tmp",
out: ["art-run-test-host-data-shard99.zip"],
- srcs: ["*99-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode host --shard 99 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?99-*/**/*", "??99-*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shard99",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shard99-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shard99.zip",
}
java_genrule {
- name: "art-run-test-host-data-merged",
- defaults: ["art-run-test-data-defaults"],
+ name: "art-run-test-host-data-shardHiddenApi-tmp",
+ out: ["art-run-test-host-data-shardHiddenApi.zip"],
+ srcs: ["???-*hiddenapi*/**/*", "????-*hiddenapi*/**/*"],
+ defaults: ["art-run-test-host-data-defaults"],
+ tools: ["hiddenapi"],
+ cmd: "$(location run_test_build.py) --out $(out) --mode host " +
+ "--bootclasspath $(location :art-run-test-bootclasspath) " +
+ "--d8 $(location d8) " +
+ "--hiddenapi $(location hiddenapi) " +
+ "--jasmin $(location jasmin) " +
+ "--smali $(location smali) " +
+ "--soong_zip $(location soong_zip) " +
+ "--zipalign $(location zipalign) " +
+ "$(in)",
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-shardHiddenApi",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-shardHiddenApi-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-host-data-shardHiddenApi.zip",
+}
+
+genrule_defaults {
+ name: "art-run-test-host-data-defaults",
+ defaults: [
+ // Enable only in source builds, where com.android.art.testing is
+ // available.
+ "art_module_source_build_genrule_defaults",
+ ],
+ tool_files: [
+ "run_test_build.py",
+ ":art-run-test-bootclasspath",
+ ],
+ tools: [
+ "d8",
+ "jasmin",
+ "smali",
+ "soong_zip",
+ "zipalign",
+ ],
+ cmd: "$(location run_test_build.py) --out $(out) --mode host " +
+ "--bootclasspath $(location :art-run-test-bootclasspath) " +
+ "--d8 $(location d8) " +
+ "--jasmin $(location jasmin) " +
+ "--smali $(location smali) " +
+ "--soong_zip $(location soong_zip) " +
+ "--zipalign $(location zipalign) " +
+ "$(in)",
+}
+
+java_genrule {
+ name: "art-run-test-host-data-merged-tmp",
+ defaults: ["art_module_source_build_genrule_defaults"],
out: ["art-run-test-host-data-merged.zip"],
srcs: [
- ":art-run-test-host-data-shard00",
- ":art-run-test-host-data-shard01",
- ":art-run-test-host-data-shard02",
- ":art-run-test-host-data-shard03",
- ":art-run-test-host-data-shard04",
- ":art-run-test-host-data-shard05",
- ":art-run-test-host-data-shard06",
- ":art-run-test-host-data-shard07",
- ":art-run-test-host-data-shard08",
- ":art-run-test-host-data-shard09",
- ":art-run-test-host-data-shard10",
- ":art-run-test-host-data-shard11",
- ":art-run-test-host-data-shard12",
- ":art-run-test-host-data-shard13",
- ":art-run-test-host-data-shard14",
- ":art-run-test-host-data-shard15",
- ":art-run-test-host-data-shard16",
- ":art-run-test-host-data-shard17",
- ":art-run-test-host-data-shard18",
- ":art-run-test-host-data-shard19",
- ":art-run-test-host-data-shard20",
- ":art-run-test-host-data-shard21",
- ":art-run-test-host-data-shard22",
- ":art-run-test-host-data-shard23",
- ":art-run-test-host-data-shard24",
- ":art-run-test-host-data-shard25",
- ":art-run-test-host-data-shard26",
- ":art-run-test-host-data-shard27",
- ":art-run-test-host-data-shard28",
- ":art-run-test-host-data-shard29",
- ":art-run-test-host-data-shard30",
- ":art-run-test-host-data-shard31",
- ":art-run-test-host-data-shard32",
- ":art-run-test-host-data-shard33",
- ":art-run-test-host-data-shard34",
- ":art-run-test-host-data-shard35",
- ":art-run-test-host-data-shard36",
- ":art-run-test-host-data-shard37",
- ":art-run-test-host-data-shard38",
- ":art-run-test-host-data-shard39",
- ":art-run-test-host-data-shard40",
- ":art-run-test-host-data-shard41",
- ":art-run-test-host-data-shard42",
- ":art-run-test-host-data-shard43",
- ":art-run-test-host-data-shard44",
- ":art-run-test-host-data-shard45",
- ":art-run-test-host-data-shard46",
- ":art-run-test-host-data-shard47",
- ":art-run-test-host-data-shard48",
- ":art-run-test-host-data-shard49",
- ":art-run-test-host-data-shard50",
- ":art-run-test-host-data-shard51",
- ":art-run-test-host-data-shard52",
- ":art-run-test-host-data-shard53",
- ":art-run-test-host-data-shard54",
- ":art-run-test-host-data-shard55",
- ":art-run-test-host-data-shard56",
- ":art-run-test-host-data-shard57",
- ":art-run-test-host-data-shard58",
- ":art-run-test-host-data-shard59",
- ":art-run-test-host-data-shard60",
- ":art-run-test-host-data-shard61",
- ":art-run-test-host-data-shard62",
- ":art-run-test-host-data-shard63",
- ":art-run-test-host-data-shard64",
- ":art-run-test-host-data-shard65",
- ":art-run-test-host-data-shard66",
- ":art-run-test-host-data-shard67",
- ":art-run-test-host-data-shard68",
- ":art-run-test-host-data-shard69",
- ":art-run-test-host-data-shard70",
- ":art-run-test-host-data-shard71",
- ":art-run-test-host-data-shard72",
- ":art-run-test-host-data-shard73",
- ":art-run-test-host-data-shard74",
- ":art-run-test-host-data-shard75",
- ":art-run-test-host-data-shard76",
- ":art-run-test-host-data-shard77",
- ":art-run-test-host-data-shard78",
- ":art-run-test-host-data-shard79",
- ":art-run-test-host-data-shard80",
- ":art-run-test-host-data-shard81",
- ":art-run-test-host-data-shard82",
- ":art-run-test-host-data-shard83",
- ":art-run-test-host-data-shard84",
- ":art-run-test-host-data-shard85",
- ":art-run-test-host-data-shard86",
- ":art-run-test-host-data-shard87",
- ":art-run-test-host-data-shard88",
- ":art-run-test-host-data-shard89",
- ":art-run-test-host-data-shard90",
- ":art-run-test-host-data-shard91",
- ":art-run-test-host-data-shard92",
- ":art-run-test-host-data-shard93",
- ":art-run-test-host-data-shard94",
- ":art-run-test-host-data-shard95",
- ":art-run-test-host-data-shard96",
- ":art-run-test-host-data-shard97",
- ":art-run-test-host-data-shard98",
- ":art-run-test-host-data-shard99",
+ ":art-run-test-host-data-shard00-tmp",
+ ":art-run-test-host-data-shard01-tmp",
+ ":art-run-test-host-data-shard02-tmp",
+ ":art-run-test-host-data-shard03-tmp",
+ ":art-run-test-host-data-shard04-tmp",
+ ":art-run-test-host-data-shard05-tmp",
+ ":art-run-test-host-data-shard06-tmp",
+ ":art-run-test-host-data-shard07-tmp",
+ ":art-run-test-host-data-shard08-tmp",
+ ":art-run-test-host-data-shard09-tmp",
+ ":art-run-test-host-data-shard10-tmp",
+ ":art-run-test-host-data-shard11-tmp",
+ ":art-run-test-host-data-shard12-tmp",
+ ":art-run-test-host-data-shard13-tmp",
+ ":art-run-test-host-data-shard14-tmp",
+ ":art-run-test-host-data-shard15-tmp",
+ ":art-run-test-host-data-shard16-tmp",
+ ":art-run-test-host-data-shard17-tmp",
+ ":art-run-test-host-data-shard18-tmp",
+ ":art-run-test-host-data-shard19-tmp",
+ ":art-run-test-host-data-shard20-tmp",
+ ":art-run-test-host-data-shard21-tmp",
+ ":art-run-test-host-data-shard22-tmp",
+ ":art-run-test-host-data-shard23-tmp",
+ ":art-run-test-host-data-shard24-tmp",
+ ":art-run-test-host-data-shard25-tmp",
+ ":art-run-test-host-data-shard26-tmp",
+ ":art-run-test-host-data-shard27-tmp",
+ ":art-run-test-host-data-shard28-tmp",
+ ":art-run-test-host-data-shard29-tmp",
+ ":art-run-test-host-data-shard30-tmp",
+ ":art-run-test-host-data-shard31-tmp",
+ ":art-run-test-host-data-shard32-tmp",
+ ":art-run-test-host-data-shard33-tmp",
+ ":art-run-test-host-data-shard34-tmp",
+ ":art-run-test-host-data-shard35-tmp",
+ ":art-run-test-host-data-shard36-tmp",
+ ":art-run-test-host-data-shard37-tmp",
+ ":art-run-test-host-data-shard38-tmp",
+ ":art-run-test-host-data-shard39-tmp",
+ ":art-run-test-host-data-shard40-tmp",
+ ":art-run-test-host-data-shard41-tmp",
+ ":art-run-test-host-data-shard42-tmp",
+ ":art-run-test-host-data-shard43-tmp",
+ ":art-run-test-host-data-shard44-tmp",
+ ":art-run-test-host-data-shard45-tmp",
+ ":art-run-test-host-data-shard46-tmp",
+ ":art-run-test-host-data-shard47-tmp",
+ ":art-run-test-host-data-shard48-tmp",
+ ":art-run-test-host-data-shard49-tmp",
+ ":art-run-test-host-data-shard50-tmp",
+ ":art-run-test-host-data-shard51-tmp",
+ ":art-run-test-host-data-shard52-tmp",
+ ":art-run-test-host-data-shard53-tmp",
+ ":art-run-test-host-data-shard54-tmp",
+ ":art-run-test-host-data-shard55-tmp",
+ ":art-run-test-host-data-shard56-tmp",
+ ":art-run-test-host-data-shard57-tmp",
+ ":art-run-test-host-data-shard58-tmp",
+ ":art-run-test-host-data-shard59-tmp",
+ ":art-run-test-host-data-shard60-tmp",
+ ":art-run-test-host-data-shard61-tmp",
+ ":art-run-test-host-data-shard62-tmp",
+ ":art-run-test-host-data-shard63-tmp",
+ ":art-run-test-host-data-shard64-tmp",
+ ":art-run-test-host-data-shard65-tmp",
+ ":art-run-test-host-data-shard66-tmp",
+ ":art-run-test-host-data-shard67-tmp",
+ ":art-run-test-host-data-shard68-tmp",
+ ":art-run-test-host-data-shard69-tmp",
+ ":art-run-test-host-data-shard70-tmp",
+ ":art-run-test-host-data-shard71-tmp",
+ ":art-run-test-host-data-shard72-tmp",
+ ":art-run-test-host-data-shard73-tmp",
+ ":art-run-test-host-data-shard74-tmp",
+ ":art-run-test-host-data-shard75-tmp",
+ ":art-run-test-host-data-shard76-tmp",
+ ":art-run-test-host-data-shard77-tmp",
+ ":art-run-test-host-data-shard78-tmp",
+ ":art-run-test-host-data-shard79-tmp",
+ ":art-run-test-host-data-shard80-tmp",
+ ":art-run-test-host-data-shard81-tmp",
+ ":art-run-test-host-data-shard82-tmp",
+ ":art-run-test-host-data-shard83-tmp",
+ ":art-run-test-host-data-shard84-tmp",
+ ":art-run-test-host-data-shard85-tmp",
+ ":art-run-test-host-data-shard86-tmp",
+ ":art-run-test-host-data-shard87-tmp",
+ ":art-run-test-host-data-shard88-tmp",
+ ":art-run-test-host-data-shard89-tmp",
+ ":art-run-test-host-data-shard90-tmp",
+ ":art-run-test-host-data-shard91-tmp",
+ ":art-run-test-host-data-shard92-tmp",
+ ":art-run-test-host-data-shard93-tmp",
+ ":art-run-test-host-data-shard94-tmp",
+ ":art-run-test-host-data-shard95-tmp",
+ ":art-run-test-host-data-shard96-tmp",
+ ":art-run-test-host-data-shard97-tmp",
+ ":art-run-test-host-data-shard98-tmp",
+ ":art-run-test-host-data-shard99-tmp",
+ ":art-run-test-host-data-shardHiddenApi-tmp",
],
tools: ["merge_zips"],
cmd: "$(location merge_zips) $(out) $(in)",
}
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-host-data-merged",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-merged-tmp",
+ required: [
+ "art-run-test-host-data-shard00",
+ "art-run-test-host-data-shard01",
+ "art-run-test-host-data-shard02",
+ "art-run-test-host-data-shard03",
+ "art-run-test-host-data-shard04",
+ "art-run-test-host-data-shard05",
+ "art-run-test-host-data-shard06",
+ "art-run-test-host-data-shard07",
+ "art-run-test-host-data-shard08",
+ "art-run-test-host-data-shard09",
+ "art-run-test-host-data-shard10",
+ "art-run-test-host-data-shard11",
+ "art-run-test-host-data-shard12",
+ "art-run-test-host-data-shard13",
+ "art-run-test-host-data-shard14",
+ "art-run-test-host-data-shard15",
+ "art-run-test-host-data-shard16",
+ "art-run-test-host-data-shard17",
+ "art-run-test-host-data-shard18",
+ "art-run-test-host-data-shard19",
+ "art-run-test-host-data-shard20",
+ "art-run-test-host-data-shard21",
+ "art-run-test-host-data-shard22",
+ "art-run-test-host-data-shard23",
+ "art-run-test-host-data-shard24",
+ "art-run-test-host-data-shard25",
+ "art-run-test-host-data-shard26",
+ "art-run-test-host-data-shard27",
+ "art-run-test-host-data-shard28",
+ "art-run-test-host-data-shard29",
+ "art-run-test-host-data-shard30",
+ "art-run-test-host-data-shard31",
+ "art-run-test-host-data-shard32",
+ "art-run-test-host-data-shard33",
+ "art-run-test-host-data-shard34",
+ "art-run-test-host-data-shard35",
+ "art-run-test-host-data-shard36",
+ "art-run-test-host-data-shard37",
+ "art-run-test-host-data-shard38",
+ "art-run-test-host-data-shard39",
+ "art-run-test-host-data-shard40",
+ "art-run-test-host-data-shard41",
+ "art-run-test-host-data-shard42",
+ "art-run-test-host-data-shard43",
+ "art-run-test-host-data-shard44",
+ "art-run-test-host-data-shard45",
+ "art-run-test-host-data-shard46",
+ "art-run-test-host-data-shard47",
+ "art-run-test-host-data-shard48",
+ "art-run-test-host-data-shard49",
+ "art-run-test-host-data-shard50",
+ "art-run-test-host-data-shard51",
+ "art-run-test-host-data-shard52",
+ "art-run-test-host-data-shard53",
+ "art-run-test-host-data-shard54",
+ "art-run-test-host-data-shard55",
+ "art-run-test-host-data-shard56",
+ "art-run-test-host-data-shard57",
+ "art-run-test-host-data-shard58",
+ "art-run-test-host-data-shard59",
+ "art-run-test-host-data-shard60",
+ "art-run-test-host-data-shard61",
+ "art-run-test-host-data-shard62",
+ "art-run-test-host-data-shard63",
+ "art-run-test-host-data-shard64",
+ "art-run-test-host-data-shard65",
+ "art-run-test-host-data-shard66",
+ "art-run-test-host-data-shard67",
+ "art-run-test-host-data-shard68",
+ "art-run-test-host-data-shard69",
+ "art-run-test-host-data-shard70",
+ "art-run-test-host-data-shard71",
+ "art-run-test-host-data-shard72",
+ "art-run-test-host-data-shard73",
+ "art-run-test-host-data-shard74",
+ "art-run-test-host-data-shard75",
+ "art-run-test-host-data-shard76",
+ "art-run-test-host-data-shard77",
+ "art-run-test-host-data-shard78",
+ "art-run-test-host-data-shard79",
+ "art-run-test-host-data-shard80",
+ "art-run-test-host-data-shard81",
+ "art-run-test-host-data-shard82",
+ "art-run-test-host-data-shard83",
+ "art-run-test-host-data-shard84",
+ "art-run-test-host-data-shard85",
+ "art-run-test-host-data-shard86",
+ "art-run-test-host-data-shard87",
+ "art-run-test-host-data-shard88",
+ "art-run-test-host-data-shard89",
+ "art-run-test-host-data-shard90",
+ "art-run-test-host-data-shard91",
+ "art-run-test-host-data-shard92",
+ "art-run-test-host-data-shard93",
+ "art-run-test-host-data-shard94",
+ "art-run-test-host-data-shard95",
+ "art-run-test-host-data-shard96",
+ "art-run-test-host-data-shard97",
+ "art-run-test-host-data-shard98",
+ "art-run-test-host-data-shard99",
+ "art-run-test-host-data-shardHiddenApi",
+ ],
+ sub_dir: "art",
+ filename: "art-run-test-host-data-merged.zip",
+}
+
+// Phony target used to build all shards
java_genrule {
- name: "art-run-test-target-data-shard00",
+ name: "art-run-test-host-data-tmp",
+ defaults: ["art-run-test-data-defaults"],
+ out: ["art-run-test-host-data.txt"],
+ srcs: [
+ ":art-run-test-host-data-shard00-tmp",
+ ":art-run-test-host-data-shard01-tmp",
+ ":art-run-test-host-data-shard02-tmp",
+ ":art-run-test-host-data-shard03-tmp",
+ ":art-run-test-host-data-shard04-tmp",
+ ":art-run-test-host-data-shard05-tmp",
+ ":art-run-test-host-data-shard06-tmp",
+ ":art-run-test-host-data-shard07-tmp",
+ ":art-run-test-host-data-shard08-tmp",
+ ":art-run-test-host-data-shard09-tmp",
+ ":art-run-test-host-data-shard10-tmp",
+ ":art-run-test-host-data-shard11-tmp",
+ ":art-run-test-host-data-shard12-tmp",
+ ":art-run-test-host-data-shard13-tmp",
+ ":art-run-test-host-data-shard14-tmp",
+ ":art-run-test-host-data-shard15-tmp",
+ ":art-run-test-host-data-shard16-tmp",
+ ":art-run-test-host-data-shard17-tmp",
+ ":art-run-test-host-data-shard18-tmp",
+ ":art-run-test-host-data-shard19-tmp",
+ ":art-run-test-host-data-shard20-tmp",
+ ":art-run-test-host-data-shard21-tmp",
+ ":art-run-test-host-data-shard22-tmp",
+ ":art-run-test-host-data-shard23-tmp",
+ ":art-run-test-host-data-shard24-tmp",
+ ":art-run-test-host-data-shard25-tmp",
+ ":art-run-test-host-data-shard26-tmp",
+ ":art-run-test-host-data-shard27-tmp",
+ ":art-run-test-host-data-shard28-tmp",
+ ":art-run-test-host-data-shard29-tmp",
+ ":art-run-test-host-data-shard30-tmp",
+ ":art-run-test-host-data-shard31-tmp",
+ ":art-run-test-host-data-shard32-tmp",
+ ":art-run-test-host-data-shard33-tmp",
+ ":art-run-test-host-data-shard34-tmp",
+ ":art-run-test-host-data-shard35-tmp",
+ ":art-run-test-host-data-shard36-tmp",
+ ":art-run-test-host-data-shard37-tmp",
+ ":art-run-test-host-data-shard38-tmp",
+ ":art-run-test-host-data-shard39-tmp",
+ ":art-run-test-host-data-shard40-tmp",
+ ":art-run-test-host-data-shard41-tmp",
+ ":art-run-test-host-data-shard42-tmp",
+ ":art-run-test-host-data-shard43-tmp",
+ ":art-run-test-host-data-shard44-tmp",
+ ":art-run-test-host-data-shard45-tmp",
+ ":art-run-test-host-data-shard46-tmp",
+ ":art-run-test-host-data-shard47-tmp",
+ ":art-run-test-host-data-shard48-tmp",
+ ":art-run-test-host-data-shard49-tmp",
+ ":art-run-test-host-data-shard50-tmp",
+ ":art-run-test-host-data-shard51-tmp",
+ ":art-run-test-host-data-shard52-tmp",
+ ":art-run-test-host-data-shard53-tmp",
+ ":art-run-test-host-data-shard54-tmp",
+ ":art-run-test-host-data-shard55-tmp",
+ ":art-run-test-host-data-shard56-tmp",
+ ":art-run-test-host-data-shard57-tmp",
+ ":art-run-test-host-data-shard58-tmp",
+ ":art-run-test-host-data-shard59-tmp",
+ ":art-run-test-host-data-shard60-tmp",
+ ":art-run-test-host-data-shard61-tmp",
+ ":art-run-test-host-data-shard62-tmp",
+ ":art-run-test-host-data-shard63-tmp",
+ ":art-run-test-host-data-shard64-tmp",
+ ":art-run-test-host-data-shard65-tmp",
+ ":art-run-test-host-data-shard66-tmp",
+ ":art-run-test-host-data-shard67-tmp",
+ ":art-run-test-host-data-shard68-tmp",
+ ":art-run-test-host-data-shard69-tmp",
+ ":art-run-test-host-data-shard70-tmp",
+ ":art-run-test-host-data-shard71-tmp",
+ ":art-run-test-host-data-shard72-tmp",
+ ":art-run-test-host-data-shard73-tmp",
+ ":art-run-test-host-data-shard74-tmp",
+ ":art-run-test-host-data-shard75-tmp",
+ ":art-run-test-host-data-shard76-tmp",
+ ":art-run-test-host-data-shard77-tmp",
+ ":art-run-test-host-data-shard78-tmp",
+ ":art-run-test-host-data-shard79-tmp",
+ ":art-run-test-host-data-shard80-tmp",
+ ":art-run-test-host-data-shard81-tmp",
+ ":art-run-test-host-data-shard82-tmp",
+ ":art-run-test-host-data-shard83-tmp",
+ ":art-run-test-host-data-shard84-tmp",
+ ":art-run-test-host-data-shard85-tmp",
+ ":art-run-test-host-data-shard86-tmp",
+ ":art-run-test-host-data-shard87-tmp",
+ ":art-run-test-host-data-shard88-tmp",
+ ":art-run-test-host-data-shard89-tmp",
+ ":art-run-test-host-data-shard90-tmp",
+ ":art-run-test-host-data-shard91-tmp",
+ ":art-run-test-host-data-shard92-tmp",
+ ":art-run-test-host-data-shard93-tmp",
+ ":art-run-test-host-data-shard94-tmp",
+ ":art-run-test-host-data-shard95-tmp",
+ ":art-run-test-host-data-shard96-tmp",
+ ":art-run-test-host-data-shard97-tmp",
+ ":art-run-test-host-data-shard98-tmp",
+ ":art-run-test-host-data-shard99-tmp",
+ ":art-run-test-host-data-shardHiddenApi-tmp",
+ ],
+ cmd: "echo $(in) > $(out)",
+}
+
+// Phony target used to install all shards
+prebuilt_etc_host {
+ name: "art-run-test-host-data",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-host-data-tmp",
+ required: [
+ "art-run-test-host-data-shard00",
+ "art-run-test-host-data-shard01",
+ "art-run-test-host-data-shard02",
+ "art-run-test-host-data-shard03",
+ "art-run-test-host-data-shard04",
+ "art-run-test-host-data-shard05",
+ "art-run-test-host-data-shard06",
+ "art-run-test-host-data-shard07",
+ "art-run-test-host-data-shard08",
+ "art-run-test-host-data-shard09",
+ "art-run-test-host-data-shard10",
+ "art-run-test-host-data-shard11",
+ "art-run-test-host-data-shard12",
+ "art-run-test-host-data-shard13",
+ "art-run-test-host-data-shard14",
+ "art-run-test-host-data-shard15",
+ "art-run-test-host-data-shard16",
+ "art-run-test-host-data-shard17",
+ "art-run-test-host-data-shard18",
+ "art-run-test-host-data-shard19",
+ "art-run-test-host-data-shard20",
+ "art-run-test-host-data-shard21",
+ "art-run-test-host-data-shard22",
+ "art-run-test-host-data-shard23",
+ "art-run-test-host-data-shard24",
+ "art-run-test-host-data-shard25",
+ "art-run-test-host-data-shard26",
+ "art-run-test-host-data-shard27",
+ "art-run-test-host-data-shard28",
+ "art-run-test-host-data-shard29",
+ "art-run-test-host-data-shard30",
+ "art-run-test-host-data-shard31",
+ "art-run-test-host-data-shard32",
+ "art-run-test-host-data-shard33",
+ "art-run-test-host-data-shard34",
+ "art-run-test-host-data-shard35",
+ "art-run-test-host-data-shard36",
+ "art-run-test-host-data-shard37",
+ "art-run-test-host-data-shard38",
+ "art-run-test-host-data-shard39",
+ "art-run-test-host-data-shard40",
+ "art-run-test-host-data-shard41",
+ "art-run-test-host-data-shard42",
+ "art-run-test-host-data-shard43",
+ "art-run-test-host-data-shard44",
+ "art-run-test-host-data-shard45",
+ "art-run-test-host-data-shard46",
+ "art-run-test-host-data-shard47",
+ "art-run-test-host-data-shard48",
+ "art-run-test-host-data-shard49",
+ "art-run-test-host-data-shard50",
+ "art-run-test-host-data-shard51",
+ "art-run-test-host-data-shard52",
+ "art-run-test-host-data-shard53",
+ "art-run-test-host-data-shard54",
+ "art-run-test-host-data-shard55",
+ "art-run-test-host-data-shard56",
+ "art-run-test-host-data-shard57",
+ "art-run-test-host-data-shard58",
+ "art-run-test-host-data-shard59",
+ "art-run-test-host-data-shard60",
+ "art-run-test-host-data-shard61",
+ "art-run-test-host-data-shard62",
+ "art-run-test-host-data-shard63",
+ "art-run-test-host-data-shard64",
+ "art-run-test-host-data-shard65",
+ "art-run-test-host-data-shard66",
+ "art-run-test-host-data-shard67",
+ "art-run-test-host-data-shard68",
+ "art-run-test-host-data-shard69",
+ "art-run-test-host-data-shard70",
+ "art-run-test-host-data-shard71",
+ "art-run-test-host-data-shard72",
+ "art-run-test-host-data-shard73",
+ "art-run-test-host-data-shard74",
+ "art-run-test-host-data-shard75",
+ "art-run-test-host-data-shard76",
+ "art-run-test-host-data-shard77",
+ "art-run-test-host-data-shard78",
+ "art-run-test-host-data-shard79",
+ "art-run-test-host-data-shard80",
+ "art-run-test-host-data-shard81",
+ "art-run-test-host-data-shard82",
+ "art-run-test-host-data-shard83",
+ "art-run-test-host-data-shard84",
+ "art-run-test-host-data-shard85",
+ "art-run-test-host-data-shard86",
+ "art-run-test-host-data-shard87",
+ "art-run-test-host-data-shard88",
+ "art-run-test-host-data-shard89",
+ "art-run-test-host-data-shard90",
+ "art-run-test-host-data-shard91",
+ "art-run-test-host-data-shard92",
+ "art-run-test-host-data-shard93",
+ "art-run-test-host-data-shard94",
+ "art-run-test-host-data-shard95",
+ "art-run-test-host-data-shard96",
+ "art-run-test-host-data-shard97",
+ "art-run-test-host-data-shard98",
+ "art-run-test-host-data-shard99",
+ "art-run-test-host-data-shardHiddenApi",
+ ],
+ sub_dir: "art",
+ filename: "art-run-test-host-data.txt",
+}
+
+java_genrule {
+ name: "art-run-test-target-data-shard00-tmp",
out: ["art-run-test-target-data-shard00.zip"],
- srcs: ["*00-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 00 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?00-*/**/*", "??00-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard00",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard00-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard00.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard01",
+ name: "art-run-test-target-data-shard01-tmp",
out: ["art-run-test-target-data-shard01.zip"],
- srcs: ["*01-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 01 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?01-*/**/*", "??01-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard01",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard01-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard01.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard02",
+ name: "art-run-test-target-data-shard02-tmp",
out: ["art-run-test-target-data-shard02.zip"],
- srcs: ["*02-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 02 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?02-*/**/*", "??02-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard02",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard02-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard02.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard03",
+ name: "art-run-test-target-data-shard03-tmp",
out: ["art-run-test-target-data-shard03.zip"],
- srcs: ["*03-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 03 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?03-*/**/*", "??03-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard03",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard03-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard03.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard04",
+ name: "art-run-test-target-data-shard04-tmp",
out: ["art-run-test-target-data-shard04.zip"],
- srcs: ["*04-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 04 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?04-*/**/*", "??04-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard04",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard04-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard04.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard05",
+ name: "art-run-test-target-data-shard05-tmp",
out: ["art-run-test-target-data-shard05.zip"],
- srcs: ["*05-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 05 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?05-*/**/*", "??05-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard05",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard05-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard05.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard06",
+ name: "art-run-test-target-data-shard06-tmp",
out: ["art-run-test-target-data-shard06.zip"],
- srcs: ["*06-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 06 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?06-*/**/*", "??06-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard06",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard06-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard06.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard07",
+ name: "art-run-test-target-data-shard07-tmp",
out: ["art-run-test-target-data-shard07.zip"],
- srcs: ["*07-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 07 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?07-*/**/*", "??07-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard07",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard07-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard07.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard08",
+ name: "art-run-test-target-data-shard08-tmp",
out: ["art-run-test-target-data-shard08.zip"],
- srcs: ["*08-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 08 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?08-*/**/*", "??08-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard08",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard08-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard08.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard09",
+ name: "art-run-test-target-data-shard09-tmp",
out: ["art-run-test-target-data-shard09.zip"],
- srcs: ["*09-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 09 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?09-*/**/*", "??09-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard09",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard09-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard09.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard10",
+ name: "art-run-test-target-data-shard10-tmp",
out: ["art-run-test-target-data-shard10.zip"],
- srcs: ["*10-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 10 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?10-*/**/*", "??10-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard10",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard10-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard10.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard11",
+ name: "art-run-test-target-data-shard11-tmp",
out: ["art-run-test-target-data-shard11.zip"],
- srcs: ["*11-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 11 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?11-*/**/*", "??11-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard11",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard11-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard11.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard12",
+ name: "art-run-test-target-data-shard12-tmp",
out: ["art-run-test-target-data-shard12.zip"],
- srcs: ["*12-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 12 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?12-*/**/*", "??12-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard12",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard12-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard12.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard13",
+ name: "art-run-test-target-data-shard13-tmp",
out: ["art-run-test-target-data-shard13.zip"],
- srcs: ["*13-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 13 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?13-*/**/*", "??13-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard13",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard13-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard13.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard14",
+ name: "art-run-test-target-data-shard14-tmp",
out: ["art-run-test-target-data-shard14.zip"],
- srcs: ["*14-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 14 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?14-*/**/*", "??14-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard14",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard14-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard14.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard15",
+ name: "art-run-test-target-data-shard15-tmp",
out: ["art-run-test-target-data-shard15.zip"],
- srcs: ["*15-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 15 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?15-*/**/*", "??15-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard15",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard15-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard15.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard16",
+ name: "art-run-test-target-data-shard16-tmp",
out: ["art-run-test-target-data-shard16.zip"],
- srcs: ["*16-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 16 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?16-*/**/*", "??16-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard16",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard16-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard16.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard17",
+ name: "art-run-test-target-data-shard17-tmp",
out: ["art-run-test-target-data-shard17.zip"],
- srcs: ["*17-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 17 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?17-*/**/*", "??17-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard17",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard17-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard17.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard18",
+ name: "art-run-test-target-data-shard18-tmp",
out: ["art-run-test-target-data-shard18.zip"],
- srcs: ["*18-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 18 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?18-*/**/*", "??18-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard18",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard18-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard18.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard19",
+ name: "art-run-test-target-data-shard19-tmp",
out: ["art-run-test-target-data-shard19.zip"],
- srcs: ["*19-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 19 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?19-*/**/*", "??19-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard19",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard19-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard19.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard20",
+ name: "art-run-test-target-data-shard20-tmp",
out: ["art-run-test-target-data-shard20.zip"],
- srcs: ["*20-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 20 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?20-*/**/*", "??20-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard20",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard20-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard20.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard21",
+ name: "art-run-test-target-data-shard21-tmp",
out: ["art-run-test-target-data-shard21.zip"],
- srcs: ["*21-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 21 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?21-*/**/*", "??21-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard21",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard21-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard21.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard22",
+ name: "art-run-test-target-data-shard22-tmp",
out: ["art-run-test-target-data-shard22.zip"],
- srcs: ["*22-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 22 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?22-*/**/*", "??22-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard22",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard22-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard22.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard23",
+ name: "art-run-test-target-data-shard23-tmp",
out: ["art-run-test-target-data-shard23.zip"],
- srcs: ["*23-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 23 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?23-*/**/*", "??23-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard23",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard23-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard23.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard24",
+ name: "art-run-test-target-data-shard24-tmp",
out: ["art-run-test-target-data-shard24.zip"],
- srcs: ["*24-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 24 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?24-*/**/*", "??24-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard24",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard24-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard24.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard25",
+ name: "art-run-test-target-data-shard25-tmp",
out: ["art-run-test-target-data-shard25.zip"],
- srcs: ["*25-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 25 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?25-*/**/*", "??25-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard25",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard25-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard25.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard26",
+ name: "art-run-test-target-data-shard26-tmp",
out: ["art-run-test-target-data-shard26.zip"],
- srcs: ["*26-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 26 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?26-*/**/*", "??26-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard26",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard26-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard26.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard27",
+ name: "art-run-test-target-data-shard27-tmp",
out: ["art-run-test-target-data-shard27.zip"],
- srcs: ["*27-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 27 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?27-*/**/*", "??27-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard27",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard27-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard27.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard28",
+ name: "art-run-test-target-data-shard28-tmp",
out: ["art-run-test-target-data-shard28.zip"],
- srcs: ["*28-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 28 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?28-*/**/*", "??28-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard28",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard28-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard28.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard29",
+ name: "art-run-test-target-data-shard29-tmp",
out: ["art-run-test-target-data-shard29.zip"],
- srcs: ["*29-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 29 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?29-*/**/*", "??29-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard29",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard29-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard29.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard30",
+ name: "art-run-test-target-data-shard30-tmp",
out: ["art-run-test-target-data-shard30.zip"],
- srcs: ["*30-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 30 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?30-*/**/*", "??30-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard30",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard30-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard30.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard31",
+ name: "art-run-test-target-data-shard31-tmp",
out: ["art-run-test-target-data-shard31.zip"],
- srcs: ["*31-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 31 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?31-*/**/*", "??31-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard31",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard31-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard31.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard32",
+ name: "art-run-test-target-data-shard32-tmp",
out: ["art-run-test-target-data-shard32.zip"],
- srcs: ["*32-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 32 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?32-*/**/*", "??32-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard32",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard32-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard32.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard33",
+ name: "art-run-test-target-data-shard33-tmp",
out: ["art-run-test-target-data-shard33.zip"],
- srcs: ["*33-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 33 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?33-*/**/*", "??33-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard33",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard33-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard33.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard34",
+ name: "art-run-test-target-data-shard34-tmp",
out: ["art-run-test-target-data-shard34.zip"],
- srcs: ["*34-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 34 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?34-*/**/*", "??34-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard34",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard34-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard34.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard35",
+ name: "art-run-test-target-data-shard35-tmp",
out: ["art-run-test-target-data-shard35.zip"],
- srcs: ["*35-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 35 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?35-*/**/*", "??35-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard35",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard35-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard35.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard36",
+ name: "art-run-test-target-data-shard36-tmp",
out: ["art-run-test-target-data-shard36.zip"],
- srcs: ["*36-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 36 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?36-*/**/*", "??36-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard36",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard36-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard36.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard37",
+ name: "art-run-test-target-data-shard37-tmp",
out: ["art-run-test-target-data-shard37.zip"],
- srcs: ["*37-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 37 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?37-*/**/*", "??37-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard37",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard37-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard37.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard38",
+ name: "art-run-test-target-data-shard38-tmp",
out: ["art-run-test-target-data-shard38.zip"],
- srcs: ["*38-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 38 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?38-*/**/*", "??38-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard38",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard38-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard38.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard39",
+ name: "art-run-test-target-data-shard39-tmp",
out: ["art-run-test-target-data-shard39.zip"],
- srcs: ["*39-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 39 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?39-*/**/*", "??39-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard39",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard39-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard39.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard40",
+ name: "art-run-test-target-data-shard40-tmp",
out: ["art-run-test-target-data-shard40.zip"],
- srcs: ["*40-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 40 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?40-*/**/*", "??40-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard40",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard40-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard40.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard41",
+ name: "art-run-test-target-data-shard41-tmp",
out: ["art-run-test-target-data-shard41.zip"],
- srcs: ["*41-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 41 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?41-*/**/*", "??41-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard41",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard41-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard41.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard42",
+ name: "art-run-test-target-data-shard42-tmp",
out: ["art-run-test-target-data-shard42.zip"],
- srcs: ["*42-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 42 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?42-*/**/*", "??42-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard42",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard42-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard42.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard43",
+ name: "art-run-test-target-data-shard43-tmp",
out: ["art-run-test-target-data-shard43.zip"],
- srcs: ["*43-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 43 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?43-*/**/*", "??43-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard43",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard43-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard43.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard44",
+ name: "art-run-test-target-data-shard44-tmp",
out: ["art-run-test-target-data-shard44.zip"],
- srcs: ["*44-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 44 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?44-*/**/*", "??44-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard44",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard44-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard44.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard45",
+ name: "art-run-test-target-data-shard45-tmp",
out: ["art-run-test-target-data-shard45.zip"],
- srcs: ["*45-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 45 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?45-*/**/*", "??45-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard45",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard45-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard45.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard46",
+ name: "art-run-test-target-data-shard46-tmp",
out: ["art-run-test-target-data-shard46.zip"],
- srcs: ["*46-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 46 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?46-*/**/*", "??46-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard46",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard46-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard46.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard47",
+ name: "art-run-test-target-data-shard47-tmp",
out: ["art-run-test-target-data-shard47.zip"],
- srcs: ["*47-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 47 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?47-*/**/*", "??47-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard47",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard47-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard47.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard48",
+ name: "art-run-test-target-data-shard48-tmp",
out: ["art-run-test-target-data-shard48.zip"],
- srcs: ["*48-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 48 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?48-*/**/*", "??48-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard48",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard48-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard48.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard49",
+ name: "art-run-test-target-data-shard49-tmp",
out: ["art-run-test-target-data-shard49.zip"],
- srcs: ["*49-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 49 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?49-*/**/*", "??49-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard49",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard49-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard49.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard50",
+ name: "art-run-test-target-data-shard50-tmp",
out: ["art-run-test-target-data-shard50.zip"],
- srcs: ["*50-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 50 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?50-*/**/*", "??50-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard50",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard50-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard50.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard51",
+ name: "art-run-test-target-data-shard51-tmp",
out: ["art-run-test-target-data-shard51.zip"],
- srcs: ["*51-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 51 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?51-*/**/*", "??51-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard51",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard51-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard51.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard52",
+ name: "art-run-test-target-data-shard52-tmp",
out: ["art-run-test-target-data-shard52.zip"],
- srcs: ["*52-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 52 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?52-*/**/*", "??52-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard52",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard52-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard52.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard53",
+ name: "art-run-test-target-data-shard53-tmp",
out: ["art-run-test-target-data-shard53.zip"],
- srcs: ["*53-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 53 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?53-*/**/*", "??53-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard53",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard53-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard53.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard54",
+ name: "art-run-test-target-data-shard54-tmp",
out: ["art-run-test-target-data-shard54.zip"],
- srcs: ["*54-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 54 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?54-*/**/*", "??54-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard54",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard54-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard54.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard55",
+ name: "art-run-test-target-data-shard55-tmp",
out: ["art-run-test-target-data-shard55.zip"],
- srcs: ["*55-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 55 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?55-*/**/*", "??55-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard55",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard55-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard55.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard56",
+ name: "art-run-test-target-data-shard56-tmp",
out: ["art-run-test-target-data-shard56.zip"],
- srcs: ["*56-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 56 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?56-*/**/*", "??56-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard56",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard56-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard56.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard57",
+ name: "art-run-test-target-data-shard57-tmp",
out: ["art-run-test-target-data-shard57.zip"],
- srcs: ["*57-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 57 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?57-*/**/*", "??57-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard57",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard57-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard57.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard58",
+ name: "art-run-test-target-data-shard58-tmp",
out: ["art-run-test-target-data-shard58.zip"],
- srcs: ["*58-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 58 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?58-*/**/*", "??58-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard58",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard58-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard58.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard59",
+ name: "art-run-test-target-data-shard59-tmp",
out: ["art-run-test-target-data-shard59.zip"],
- srcs: ["*59-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 59 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?59-*/**/*", "??59-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard59",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard59-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard59.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard60",
+ name: "art-run-test-target-data-shard60-tmp",
out: ["art-run-test-target-data-shard60.zip"],
- srcs: ["*60-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 60 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?60-*/**/*", "??60-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard60",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard60-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard60.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard61",
+ name: "art-run-test-target-data-shard61-tmp",
out: ["art-run-test-target-data-shard61.zip"],
- srcs: ["*61-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 61 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?61-*/**/*", "??61-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard61",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard61-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard61.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard62",
+ name: "art-run-test-target-data-shard62-tmp",
out: ["art-run-test-target-data-shard62.zip"],
- srcs: ["*62-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 62 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?62-*/**/*", "??62-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard62",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard62-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard62.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard63",
+ name: "art-run-test-target-data-shard63-tmp",
out: ["art-run-test-target-data-shard63.zip"],
- srcs: ["*63-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 63 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?63-*/**/*", "??63-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard63",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard63-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard63.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard64",
+ name: "art-run-test-target-data-shard64-tmp",
out: ["art-run-test-target-data-shard64.zip"],
- srcs: ["*64-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 64 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?64-*/**/*", "??64-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard64",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard64-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard64.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard65",
+ name: "art-run-test-target-data-shard65-tmp",
out: ["art-run-test-target-data-shard65.zip"],
- srcs: ["*65-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 65 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?65-*/**/*", "??65-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard65",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard65-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard65.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard66",
+ name: "art-run-test-target-data-shard66-tmp",
out: ["art-run-test-target-data-shard66.zip"],
- srcs: ["*66-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 66 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?66-*/**/*", "??66-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard66",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard66-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard66.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard67",
+ name: "art-run-test-target-data-shard67-tmp",
out: ["art-run-test-target-data-shard67.zip"],
- srcs: ["*67-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 67 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?67-*/**/*", "??67-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard67",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard67-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard67.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard68",
+ name: "art-run-test-target-data-shard68-tmp",
out: ["art-run-test-target-data-shard68.zip"],
- srcs: ["*68-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 68 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?68-*/**/*", "??68-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard68",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard68-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard68.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard69",
+ name: "art-run-test-target-data-shard69-tmp",
out: ["art-run-test-target-data-shard69.zip"],
- srcs: ["*69-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 69 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?69-*/**/*", "??69-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard69",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard69-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard69.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard70",
+ name: "art-run-test-target-data-shard70-tmp",
out: ["art-run-test-target-data-shard70.zip"],
- srcs: ["*70-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 70 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?70-*/**/*", "??70-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard70",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard70-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard70.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard71",
+ name: "art-run-test-target-data-shard71-tmp",
out: ["art-run-test-target-data-shard71.zip"],
- srcs: ["*71-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 71 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?71-*/**/*", "??71-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard71",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard71-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard71.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard72",
+ name: "art-run-test-target-data-shard72-tmp",
out: ["art-run-test-target-data-shard72.zip"],
- srcs: ["*72-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 72 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?72-*/**/*", "??72-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard72",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard72-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard72.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard73",
+ name: "art-run-test-target-data-shard73-tmp",
out: ["art-run-test-target-data-shard73.zip"],
- srcs: ["*73-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 73 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?73-*/**/*", "??73-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard73",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard73-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard73.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard74",
+ name: "art-run-test-target-data-shard74-tmp",
out: ["art-run-test-target-data-shard74.zip"],
- srcs: ["*74-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 74 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?74-*/**/*", "??74-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard74",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard74-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard74.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard75",
+ name: "art-run-test-target-data-shard75-tmp",
out: ["art-run-test-target-data-shard75.zip"],
- srcs: ["*75-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 75 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?75-*/**/*", "??75-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard75",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard75-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard75.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard76",
+ name: "art-run-test-target-data-shard76-tmp",
out: ["art-run-test-target-data-shard76.zip"],
- srcs: ["*76-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 76 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?76-*/**/*", "??76-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard76",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard76-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard76.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard77",
+ name: "art-run-test-target-data-shard77-tmp",
out: ["art-run-test-target-data-shard77.zip"],
- srcs: ["*77-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 77 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?77-*/**/*", "??77-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard77",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard77-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard77.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard78",
+ name: "art-run-test-target-data-shard78-tmp",
out: ["art-run-test-target-data-shard78.zip"],
- srcs: ["*78-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 78 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?78-*/**/*", "??78-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard78",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard78-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard78.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard79",
+ name: "art-run-test-target-data-shard79-tmp",
out: ["art-run-test-target-data-shard79.zip"],
- srcs: ["*79-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 79 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?79-*/**/*", "??79-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard79",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard79-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard79.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard80",
+ name: "art-run-test-target-data-shard80-tmp",
out: ["art-run-test-target-data-shard80.zip"],
- srcs: ["*80-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 80 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?80-*/**/*", "??80-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard80",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard80-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard80.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard81",
+ name: "art-run-test-target-data-shard81-tmp",
out: ["art-run-test-target-data-shard81.zip"],
- srcs: ["*81-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 81 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?81-*/**/*", "??81-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard81",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard81-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard81.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard82",
+ name: "art-run-test-target-data-shard82-tmp",
out: ["art-run-test-target-data-shard82.zip"],
- srcs: ["*82-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 82 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?82-*/**/*", "??82-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard82",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard82-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard82.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard83",
+ name: "art-run-test-target-data-shard83-tmp",
out: ["art-run-test-target-data-shard83.zip"],
- srcs: ["*83-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 83 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?83-*/**/*", "??83-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard83",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard83-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard83.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard84",
+ name: "art-run-test-target-data-shard84-tmp",
out: ["art-run-test-target-data-shard84.zip"],
- srcs: ["*84-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 84 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?84-*/**/*", "??84-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard84",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard84-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard84.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard85",
+ name: "art-run-test-target-data-shard85-tmp",
out: ["art-run-test-target-data-shard85.zip"],
- srcs: ["*85-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 85 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?85-*/**/*", "??85-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard85",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard85-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard85.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard86",
+ name: "art-run-test-target-data-shard86-tmp",
out: ["art-run-test-target-data-shard86.zip"],
- srcs: ["*86-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 86 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?86-*/**/*", "??86-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard86",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard86-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard86.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard87",
+ name: "art-run-test-target-data-shard87-tmp",
out: ["art-run-test-target-data-shard87.zip"],
- srcs: ["*87-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 87 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?87-*/**/*", "??87-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard87",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard87-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard87.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard88",
+ name: "art-run-test-target-data-shard88-tmp",
out: ["art-run-test-target-data-shard88.zip"],
- srcs: ["*88-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 88 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?88-*/**/*", "??88-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard88",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard88-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard88.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard89",
+ name: "art-run-test-target-data-shard89-tmp",
out: ["art-run-test-target-data-shard89.zip"],
- srcs: ["*89-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 89 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?89-*/**/*", "??89-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard89",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard89-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard89.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard90",
+ name: "art-run-test-target-data-shard90-tmp",
out: ["art-run-test-target-data-shard90.zip"],
- srcs: ["*90-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 90 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?90-*/**/*", "??90-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard90",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard90-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard90.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard91",
+ name: "art-run-test-target-data-shard91-tmp",
out: ["art-run-test-target-data-shard91.zip"],
- srcs: ["*91-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 91 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?91-*/**/*", "??91-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard91",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard91-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard91.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard92",
+ name: "art-run-test-target-data-shard92-tmp",
out: ["art-run-test-target-data-shard92.zip"],
- srcs: ["*92-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 92 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?92-*/**/*", "??92-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard92",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard92-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard92.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard93",
+ name: "art-run-test-target-data-shard93-tmp",
out: ["art-run-test-target-data-shard93.zip"],
- srcs: ["*93-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 93 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?93-*/**/*", "??93-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard93",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard93-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard93.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard94",
+ name: "art-run-test-target-data-shard94-tmp",
out: ["art-run-test-target-data-shard94.zip"],
- srcs: ["*94-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 94 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?94-*/**/*", "??94-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard94",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard94-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard94.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard95",
+ name: "art-run-test-target-data-shard95-tmp",
out: ["art-run-test-target-data-shard95.zip"],
- srcs: ["*95-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 95 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?95-*/**/*", "??95-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard95",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard95-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard95.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard96",
+ name: "art-run-test-target-data-shard96-tmp",
out: ["art-run-test-target-data-shard96.zip"],
- srcs: ["*96-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 96 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?96-*/**/*", "??96-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard96",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard96-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard96.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard97",
+ name: "art-run-test-target-data-shard97-tmp",
out: ["art-run-test-target-data-shard97.zip"],
- srcs: ["*97-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 97 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?97-*/**/*", "??97-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard97",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard97-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard97.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard98",
+ name: "art-run-test-target-data-shard98-tmp",
out: ["art-run-test-target-data-shard98.zip"],
- srcs: ["*98-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 98 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?98-*/**/*", "??98-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard98",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard98-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard98.zip",
}
java_genrule {
- name: "art-run-test-target-data-shard99",
+ name: "art-run-test-target-data-shard99-tmp",
out: ["art-run-test-target-data-shard99.zip"],
- srcs: ["*99-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode target --shard 99 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?99-*/**/*", "??99-*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shard99",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shard99-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shard99.zip",
}
java_genrule {
- name: "art-run-test-target-data-merged",
- defaults: ["art-run-test-data-defaults"],
+ name: "art-run-test-target-data-shardHiddenApi-tmp",
+ out: ["art-run-test-target-data-shardHiddenApi.zip"],
+ srcs: ["???-*hiddenapi*/**/*", "????-*hiddenapi*/**/*"],
+ defaults: ["art-run-test-target-data-defaults"],
+ tools: ["hiddenapi"],
+ cmd: "$(location run_test_build.py) --out $(out) --mode target " +
+ "--bootclasspath $(location :art-run-test-bootclasspath) " +
+ "--d8 $(location d8) " +
+ "--hiddenapi $(location hiddenapi) " +
+ "--jasmin $(location jasmin) " +
+ "--smali $(location smali) " +
+ "--soong_zip $(location soong_zip) " +
+ "--zipalign $(location zipalign) " +
+ "$(in)",
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-shardHiddenApi",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-shardHiddenApi-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-target-data-shardHiddenApi.zip",
+}
+
+genrule_defaults {
+ name: "art-run-test-target-data-defaults",
+ defaults: [
+ // Enable only in source builds, where com.android.art.testing is
+ // available.
+ "art_module_source_build_genrule_defaults",
+ ],
+ tool_files: [
+ "run_test_build.py",
+ ":art-run-test-bootclasspath",
+ ],
+ tools: [
+ "d8",
+ "jasmin",
+ "smali",
+ "soong_zip",
+ "zipalign",
+ ],
+ cmd: "$(location run_test_build.py) --out $(out) --mode target " +
+ "--bootclasspath $(location :art-run-test-bootclasspath) " +
+ "--d8 $(location d8) " +
+ "--jasmin $(location jasmin) " +
+ "--smali $(location smali) " +
+ "--soong_zip $(location soong_zip) " +
+ "--zipalign $(location zipalign) " +
+ "$(in)",
+}
+
+java_genrule {
+ name: "art-run-test-target-data-merged-tmp",
+ defaults: ["art_module_source_build_genrule_defaults"],
out: ["art-run-test-target-data-merged.zip"],
srcs: [
- ":art-run-test-target-data-shard00",
- ":art-run-test-target-data-shard01",
- ":art-run-test-target-data-shard02",
- ":art-run-test-target-data-shard03",
- ":art-run-test-target-data-shard04",
- ":art-run-test-target-data-shard05",
- ":art-run-test-target-data-shard06",
- ":art-run-test-target-data-shard07",
- ":art-run-test-target-data-shard08",
- ":art-run-test-target-data-shard09",
- ":art-run-test-target-data-shard10",
- ":art-run-test-target-data-shard11",
- ":art-run-test-target-data-shard12",
- ":art-run-test-target-data-shard13",
- ":art-run-test-target-data-shard14",
- ":art-run-test-target-data-shard15",
- ":art-run-test-target-data-shard16",
- ":art-run-test-target-data-shard17",
- ":art-run-test-target-data-shard18",
- ":art-run-test-target-data-shard19",
- ":art-run-test-target-data-shard20",
- ":art-run-test-target-data-shard21",
- ":art-run-test-target-data-shard22",
- ":art-run-test-target-data-shard23",
- ":art-run-test-target-data-shard24",
- ":art-run-test-target-data-shard25",
- ":art-run-test-target-data-shard26",
- ":art-run-test-target-data-shard27",
- ":art-run-test-target-data-shard28",
- ":art-run-test-target-data-shard29",
- ":art-run-test-target-data-shard30",
- ":art-run-test-target-data-shard31",
- ":art-run-test-target-data-shard32",
- ":art-run-test-target-data-shard33",
- ":art-run-test-target-data-shard34",
- ":art-run-test-target-data-shard35",
- ":art-run-test-target-data-shard36",
- ":art-run-test-target-data-shard37",
- ":art-run-test-target-data-shard38",
- ":art-run-test-target-data-shard39",
- ":art-run-test-target-data-shard40",
- ":art-run-test-target-data-shard41",
- ":art-run-test-target-data-shard42",
- ":art-run-test-target-data-shard43",
- ":art-run-test-target-data-shard44",
- ":art-run-test-target-data-shard45",
- ":art-run-test-target-data-shard46",
- ":art-run-test-target-data-shard47",
- ":art-run-test-target-data-shard48",
- ":art-run-test-target-data-shard49",
- ":art-run-test-target-data-shard50",
- ":art-run-test-target-data-shard51",
- ":art-run-test-target-data-shard52",
- ":art-run-test-target-data-shard53",
- ":art-run-test-target-data-shard54",
- ":art-run-test-target-data-shard55",
- ":art-run-test-target-data-shard56",
- ":art-run-test-target-data-shard57",
- ":art-run-test-target-data-shard58",
- ":art-run-test-target-data-shard59",
- ":art-run-test-target-data-shard60",
- ":art-run-test-target-data-shard61",
- ":art-run-test-target-data-shard62",
- ":art-run-test-target-data-shard63",
- ":art-run-test-target-data-shard64",
- ":art-run-test-target-data-shard65",
- ":art-run-test-target-data-shard66",
- ":art-run-test-target-data-shard67",
- ":art-run-test-target-data-shard68",
- ":art-run-test-target-data-shard69",
- ":art-run-test-target-data-shard70",
- ":art-run-test-target-data-shard71",
- ":art-run-test-target-data-shard72",
- ":art-run-test-target-data-shard73",
- ":art-run-test-target-data-shard74",
- ":art-run-test-target-data-shard75",
- ":art-run-test-target-data-shard76",
- ":art-run-test-target-data-shard77",
- ":art-run-test-target-data-shard78",
- ":art-run-test-target-data-shard79",
- ":art-run-test-target-data-shard80",
- ":art-run-test-target-data-shard81",
- ":art-run-test-target-data-shard82",
- ":art-run-test-target-data-shard83",
- ":art-run-test-target-data-shard84",
- ":art-run-test-target-data-shard85",
- ":art-run-test-target-data-shard86",
- ":art-run-test-target-data-shard87",
- ":art-run-test-target-data-shard88",
- ":art-run-test-target-data-shard89",
- ":art-run-test-target-data-shard90",
- ":art-run-test-target-data-shard91",
- ":art-run-test-target-data-shard92",
- ":art-run-test-target-data-shard93",
- ":art-run-test-target-data-shard94",
- ":art-run-test-target-data-shard95",
- ":art-run-test-target-data-shard96",
- ":art-run-test-target-data-shard97",
- ":art-run-test-target-data-shard98",
- ":art-run-test-target-data-shard99",
+ ":art-run-test-target-data-shard00-tmp",
+ ":art-run-test-target-data-shard01-tmp",
+ ":art-run-test-target-data-shard02-tmp",
+ ":art-run-test-target-data-shard03-tmp",
+ ":art-run-test-target-data-shard04-tmp",
+ ":art-run-test-target-data-shard05-tmp",
+ ":art-run-test-target-data-shard06-tmp",
+ ":art-run-test-target-data-shard07-tmp",
+ ":art-run-test-target-data-shard08-tmp",
+ ":art-run-test-target-data-shard09-tmp",
+ ":art-run-test-target-data-shard10-tmp",
+ ":art-run-test-target-data-shard11-tmp",
+ ":art-run-test-target-data-shard12-tmp",
+ ":art-run-test-target-data-shard13-tmp",
+ ":art-run-test-target-data-shard14-tmp",
+ ":art-run-test-target-data-shard15-tmp",
+ ":art-run-test-target-data-shard16-tmp",
+ ":art-run-test-target-data-shard17-tmp",
+ ":art-run-test-target-data-shard18-tmp",
+ ":art-run-test-target-data-shard19-tmp",
+ ":art-run-test-target-data-shard20-tmp",
+ ":art-run-test-target-data-shard21-tmp",
+ ":art-run-test-target-data-shard22-tmp",
+ ":art-run-test-target-data-shard23-tmp",
+ ":art-run-test-target-data-shard24-tmp",
+ ":art-run-test-target-data-shard25-tmp",
+ ":art-run-test-target-data-shard26-tmp",
+ ":art-run-test-target-data-shard27-tmp",
+ ":art-run-test-target-data-shard28-tmp",
+ ":art-run-test-target-data-shard29-tmp",
+ ":art-run-test-target-data-shard30-tmp",
+ ":art-run-test-target-data-shard31-tmp",
+ ":art-run-test-target-data-shard32-tmp",
+ ":art-run-test-target-data-shard33-tmp",
+ ":art-run-test-target-data-shard34-tmp",
+ ":art-run-test-target-data-shard35-tmp",
+ ":art-run-test-target-data-shard36-tmp",
+ ":art-run-test-target-data-shard37-tmp",
+ ":art-run-test-target-data-shard38-tmp",
+ ":art-run-test-target-data-shard39-tmp",
+ ":art-run-test-target-data-shard40-tmp",
+ ":art-run-test-target-data-shard41-tmp",
+ ":art-run-test-target-data-shard42-tmp",
+ ":art-run-test-target-data-shard43-tmp",
+ ":art-run-test-target-data-shard44-tmp",
+ ":art-run-test-target-data-shard45-tmp",
+ ":art-run-test-target-data-shard46-tmp",
+ ":art-run-test-target-data-shard47-tmp",
+ ":art-run-test-target-data-shard48-tmp",
+ ":art-run-test-target-data-shard49-tmp",
+ ":art-run-test-target-data-shard50-tmp",
+ ":art-run-test-target-data-shard51-tmp",
+ ":art-run-test-target-data-shard52-tmp",
+ ":art-run-test-target-data-shard53-tmp",
+ ":art-run-test-target-data-shard54-tmp",
+ ":art-run-test-target-data-shard55-tmp",
+ ":art-run-test-target-data-shard56-tmp",
+ ":art-run-test-target-data-shard57-tmp",
+ ":art-run-test-target-data-shard58-tmp",
+ ":art-run-test-target-data-shard59-tmp",
+ ":art-run-test-target-data-shard60-tmp",
+ ":art-run-test-target-data-shard61-tmp",
+ ":art-run-test-target-data-shard62-tmp",
+ ":art-run-test-target-data-shard63-tmp",
+ ":art-run-test-target-data-shard64-tmp",
+ ":art-run-test-target-data-shard65-tmp",
+ ":art-run-test-target-data-shard66-tmp",
+ ":art-run-test-target-data-shard67-tmp",
+ ":art-run-test-target-data-shard68-tmp",
+ ":art-run-test-target-data-shard69-tmp",
+ ":art-run-test-target-data-shard70-tmp",
+ ":art-run-test-target-data-shard71-tmp",
+ ":art-run-test-target-data-shard72-tmp",
+ ":art-run-test-target-data-shard73-tmp",
+ ":art-run-test-target-data-shard74-tmp",
+ ":art-run-test-target-data-shard75-tmp",
+ ":art-run-test-target-data-shard76-tmp",
+ ":art-run-test-target-data-shard77-tmp",
+ ":art-run-test-target-data-shard78-tmp",
+ ":art-run-test-target-data-shard79-tmp",
+ ":art-run-test-target-data-shard80-tmp",
+ ":art-run-test-target-data-shard81-tmp",
+ ":art-run-test-target-data-shard82-tmp",
+ ":art-run-test-target-data-shard83-tmp",
+ ":art-run-test-target-data-shard84-tmp",
+ ":art-run-test-target-data-shard85-tmp",
+ ":art-run-test-target-data-shard86-tmp",
+ ":art-run-test-target-data-shard87-tmp",
+ ":art-run-test-target-data-shard88-tmp",
+ ":art-run-test-target-data-shard89-tmp",
+ ":art-run-test-target-data-shard90-tmp",
+ ":art-run-test-target-data-shard91-tmp",
+ ":art-run-test-target-data-shard92-tmp",
+ ":art-run-test-target-data-shard93-tmp",
+ ":art-run-test-target-data-shard94-tmp",
+ ":art-run-test-target-data-shard95-tmp",
+ ":art-run-test-target-data-shard96-tmp",
+ ":art-run-test-target-data-shard97-tmp",
+ ":art-run-test-target-data-shard98-tmp",
+ ":art-run-test-target-data-shard99-tmp",
+ ":art-run-test-target-data-shardHiddenApi-tmp",
],
tools: ["merge_zips"],
cmd: "$(location merge_zips) $(out) $(in)",
}
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-target-data-merged",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-merged-tmp",
+ required: [
+ "art-run-test-target-data-shard00",
+ "art-run-test-target-data-shard01",
+ "art-run-test-target-data-shard02",
+ "art-run-test-target-data-shard03",
+ "art-run-test-target-data-shard04",
+ "art-run-test-target-data-shard05",
+ "art-run-test-target-data-shard06",
+ "art-run-test-target-data-shard07",
+ "art-run-test-target-data-shard08",
+ "art-run-test-target-data-shard09",
+ "art-run-test-target-data-shard10",
+ "art-run-test-target-data-shard11",
+ "art-run-test-target-data-shard12",
+ "art-run-test-target-data-shard13",
+ "art-run-test-target-data-shard14",
+ "art-run-test-target-data-shard15",
+ "art-run-test-target-data-shard16",
+ "art-run-test-target-data-shard17",
+ "art-run-test-target-data-shard18",
+ "art-run-test-target-data-shard19",
+ "art-run-test-target-data-shard20",
+ "art-run-test-target-data-shard21",
+ "art-run-test-target-data-shard22",
+ "art-run-test-target-data-shard23",
+ "art-run-test-target-data-shard24",
+ "art-run-test-target-data-shard25",
+ "art-run-test-target-data-shard26",
+ "art-run-test-target-data-shard27",
+ "art-run-test-target-data-shard28",
+ "art-run-test-target-data-shard29",
+ "art-run-test-target-data-shard30",
+ "art-run-test-target-data-shard31",
+ "art-run-test-target-data-shard32",
+ "art-run-test-target-data-shard33",
+ "art-run-test-target-data-shard34",
+ "art-run-test-target-data-shard35",
+ "art-run-test-target-data-shard36",
+ "art-run-test-target-data-shard37",
+ "art-run-test-target-data-shard38",
+ "art-run-test-target-data-shard39",
+ "art-run-test-target-data-shard40",
+ "art-run-test-target-data-shard41",
+ "art-run-test-target-data-shard42",
+ "art-run-test-target-data-shard43",
+ "art-run-test-target-data-shard44",
+ "art-run-test-target-data-shard45",
+ "art-run-test-target-data-shard46",
+ "art-run-test-target-data-shard47",
+ "art-run-test-target-data-shard48",
+ "art-run-test-target-data-shard49",
+ "art-run-test-target-data-shard50",
+ "art-run-test-target-data-shard51",
+ "art-run-test-target-data-shard52",
+ "art-run-test-target-data-shard53",
+ "art-run-test-target-data-shard54",
+ "art-run-test-target-data-shard55",
+ "art-run-test-target-data-shard56",
+ "art-run-test-target-data-shard57",
+ "art-run-test-target-data-shard58",
+ "art-run-test-target-data-shard59",
+ "art-run-test-target-data-shard60",
+ "art-run-test-target-data-shard61",
+ "art-run-test-target-data-shard62",
+ "art-run-test-target-data-shard63",
+ "art-run-test-target-data-shard64",
+ "art-run-test-target-data-shard65",
+ "art-run-test-target-data-shard66",
+ "art-run-test-target-data-shard67",
+ "art-run-test-target-data-shard68",
+ "art-run-test-target-data-shard69",
+ "art-run-test-target-data-shard70",
+ "art-run-test-target-data-shard71",
+ "art-run-test-target-data-shard72",
+ "art-run-test-target-data-shard73",
+ "art-run-test-target-data-shard74",
+ "art-run-test-target-data-shard75",
+ "art-run-test-target-data-shard76",
+ "art-run-test-target-data-shard77",
+ "art-run-test-target-data-shard78",
+ "art-run-test-target-data-shard79",
+ "art-run-test-target-data-shard80",
+ "art-run-test-target-data-shard81",
+ "art-run-test-target-data-shard82",
+ "art-run-test-target-data-shard83",
+ "art-run-test-target-data-shard84",
+ "art-run-test-target-data-shard85",
+ "art-run-test-target-data-shard86",
+ "art-run-test-target-data-shard87",
+ "art-run-test-target-data-shard88",
+ "art-run-test-target-data-shard89",
+ "art-run-test-target-data-shard90",
+ "art-run-test-target-data-shard91",
+ "art-run-test-target-data-shard92",
+ "art-run-test-target-data-shard93",
+ "art-run-test-target-data-shard94",
+ "art-run-test-target-data-shard95",
+ "art-run-test-target-data-shard96",
+ "art-run-test-target-data-shard97",
+ "art-run-test-target-data-shard98",
+ "art-run-test-target-data-shard99",
+ "art-run-test-target-data-shardHiddenApi",
+ ],
+ sub_dir: "art",
+ filename: "art-run-test-target-data-merged.zip",
+}
+
+// Phony target used to build all shards
java_genrule {
- name: "art-run-test-jvm-data-shard00",
+ name: "art-run-test-target-data-tmp",
+ defaults: ["art-run-test-data-defaults"],
+ out: ["art-run-test-target-data.txt"],
+ srcs: [
+ ":art-run-test-target-data-shard00-tmp",
+ ":art-run-test-target-data-shard01-tmp",
+ ":art-run-test-target-data-shard02-tmp",
+ ":art-run-test-target-data-shard03-tmp",
+ ":art-run-test-target-data-shard04-tmp",
+ ":art-run-test-target-data-shard05-tmp",
+ ":art-run-test-target-data-shard06-tmp",
+ ":art-run-test-target-data-shard07-tmp",
+ ":art-run-test-target-data-shard08-tmp",
+ ":art-run-test-target-data-shard09-tmp",
+ ":art-run-test-target-data-shard10-tmp",
+ ":art-run-test-target-data-shard11-tmp",
+ ":art-run-test-target-data-shard12-tmp",
+ ":art-run-test-target-data-shard13-tmp",
+ ":art-run-test-target-data-shard14-tmp",
+ ":art-run-test-target-data-shard15-tmp",
+ ":art-run-test-target-data-shard16-tmp",
+ ":art-run-test-target-data-shard17-tmp",
+ ":art-run-test-target-data-shard18-tmp",
+ ":art-run-test-target-data-shard19-tmp",
+ ":art-run-test-target-data-shard20-tmp",
+ ":art-run-test-target-data-shard21-tmp",
+ ":art-run-test-target-data-shard22-tmp",
+ ":art-run-test-target-data-shard23-tmp",
+ ":art-run-test-target-data-shard24-tmp",
+ ":art-run-test-target-data-shard25-tmp",
+ ":art-run-test-target-data-shard26-tmp",
+ ":art-run-test-target-data-shard27-tmp",
+ ":art-run-test-target-data-shard28-tmp",
+ ":art-run-test-target-data-shard29-tmp",
+ ":art-run-test-target-data-shard30-tmp",
+ ":art-run-test-target-data-shard31-tmp",
+ ":art-run-test-target-data-shard32-tmp",
+ ":art-run-test-target-data-shard33-tmp",
+ ":art-run-test-target-data-shard34-tmp",
+ ":art-run-test-target-data-shard35-tmp",
+ ":art-run-test-target-data-shard36-tmp",
+ ":art-run-test-target-data-shard37-tmp",
+ ":art-run-test-target-data-shard38-tmp",
+ ":art-run-test-target-data-shard39-tmp",
+ ":art-run-test-target-data-shard40-tmp",
+ ":art-run-test-target-data-shard41-tmp",
+ ":art-run-test-target-data-shard42-tmp",
+ ":art-run-test-target-data-shard43-tmp",
+ ":art-run-test-target-data-shard44-tmp",
+ ":art-run-test-target-data-shard45-tmp",
+ ":art-run-test-target-data-shard46-tmp",
+ ":art-run-test-target-data-shard47-tmp",
+ ":art-run-test-target-data-shard48-tmp",
+ ":art-run-test-target-data-shard49-tmp",
+ ":art-run-test-target-data-shard50-tmp",
+ ":art-run-test-target-data-shard51-tmp",
+ ":art-run-test-target-data-shard52-tmp",
+ ":art-run-test-target-data-shard53-tmp",
+ ":art-run-test-target-data-shard54-tmp",
+ ":art-run-test-target-data-shard55-tmp",
+ ":art-run-test-target-data-shard56-tmp",
+ ":art-run-test-target-data-shard57-tmp",
+ ":art-run-test-target-data-shard58-tmp",
+ ":art-run-test-target-data-shard59-tmp",
+ ":art-run-test-target-data-shard60-tmp",
+ ":art-run-test-target-data-shard61-tmp",
+ ":art-run-test-target-data-shard62-tmp",
+ ":art-run-test-target-data-shard63-tmp",
+ ":art-run-test-target-data-shard64-tmp",
+ ":art-run-test-target-data-shard65-tmp",
+ ":art-run-test-target-data-shard66-tmp",
+ ":art-run-test-target-data-shard67-tmp",
+ ":art-run-test-target-data-shard68-tmp",
+ ":art-run-test-target-data-shard69-tmp",
+ ":art-run-test-target-data-shard70-tmp",
+ ":art-run-test-target-data-shard71-tmp",
+ ":art-run-test-target-data-shard72-tmp",
+ ":art-run-test-target-data-shard73-tmp",
+ ":art-run-test-target-data-shard74-tmp",
+ ":art-run-test-target-data-shard75-tmp",
+ ":art-run-test-target-data-shard76-tmp",
+ ":art-run-test-target-data-shard77-tmp",
+ ":art-run-test-target-data-shard78-tmp",
+ ":art-run-test-target-data-shard79-tmp",
+ ":art-run-test-target-data-shard80-tmp",
+ ":art-run-test-target-data-shard81-tmp",
+ ":art-run-test-target-data-shard82-tmp",
+ ":art-run-test-target-data-shard83-tmp",
+ ":art-run-test-target-data-shard84-tmp",
+ ":art-run-test-target-data-shard85-tmp",
+ ":art-run-test-target-data-shard86-tmp",
+ ":art-run-test-target-data-shard87-tmp",
+ ":art-run-test-target-data-shard88-tmp",
+ ":art-run-test-target-data-shard89-tmp",
+ ":art-run-test-target-data-shard90-tmp",
+ ":art-run-test-target-data-shard91-tmp",
+ ":art-run-test-target-data-shard92-tmp",
+ ":art-run-test-target-data-shard93-tmp",
+ ":art-run-test-target-data-shard94-tmp",
+ ":art-run-test-target-data-shard95-tmp",
+ ":art-run-test-target-data-shard96-tmp",
+ ":art-run-test-target-data-shard97-tmp",
+ ":art-run-test-target-data-shard98-tmp",
+ ":art-run-test-target-data-shard99-tmp",
+ ":art-run-test-target-data-shardHiddenApi-tmp",
+ ],
+ cmd: "echo $(in) > $(out)",
+}
+
+// Phony target used to install all shards
+prebuilt_etc_host {
+ name: "art-run-test-target-data",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-target-data-tmp",
+ required: [
+ "art-run-test-target-data-shard00",
+ "art-run-test-target-data-shard01",
+ "art-run-test-target-data-shard02",
+ "art-run-test-target-data-shard03",
+ "art-run-test-target-data-shard04",
+ "art-run-test-target-data-shard05",
+ "art-run-test-target-data-shard06",
+ "art-run-test-target-data-shard07",
+ "art-run-test-target-data-shard08",
+ "art-run-test-target-data-shard09",
+ "art-run-test-target-data-shard10",
+ "art-run-test-target-data-shard11",
+ "art-run-test-target-data-shard12",
+ "art-run-test-target-data-shard13",
+ "art-run-test-target-data-shard14",
+ "art-run-test-target-data-shard15",
+ "art-run-test-target-data-shard16",
+ "art-run-test-target-data-shard17",
+ "art-run-test-target-data-shard18",
+ "art-run-test-target-data-shard19",
+ "art-run-test-target-data-shard20",
+ "art-run-test-target-data-shard21",
+ "art-run-test-target-data-shard22",
+ "art-run-test-target-data-shard23",
+ "art-run-test-target-data-shard24",
+ "art-run-test-target-data-shard25",
+ "art-run-test-target-data-shard26",
+ "art-run-test-target-data-shard27",
+ "art-run-test-target-data-shard28",
+ "art-run-test-target-data-shard29",
+ "art-run-test-target-data-shard30",
+ "art-run-test-target-data-shard31",
+ "art-run-test-target-data-shard32",
+ "art-run-test-target-data-shard33",
+ "art-run-test-target-data-shard34",
+ "art-run-test-target-data-shard35",
+ "art-run-test-target-data-shard36",
+ "art-run-test-target-data-shard37",
+ "art-run-test-target-data-shard38",
+ "art-run-test-target-data-shard39",
+ "art-run-test-target-data-shard40",
+ "art-run-test-target-data-shard41",
+ "art-run-test-target-data-shard42",
+ "art-run-test-target-data-shard43",
+ "art-run-test-target-data-shard44",
+ "art-run-test-target-data-shard45",
+ "art-run-test-target-data-shard46",
+ "art-run-test-target-data-shard47",
+ "art-run-test-target-data-shard48",
+ "art-run-test-target-data-shard49",
+ "art-run-test-target-data-shard50",
+ "art-run-test-target-data-shard51",
+ "art-run-test-target-data-shard52",
+ "art-run-test-target-data-shard53",
+ "art-run-test-target-data-shard54",
+ "art-run-test-target-data-shard55",
+ "art-run-test-target-data-shard56",
+ "art-run-test-target-data-shard57",
+ "art-run-test-target-data-shard58",
+ "art-run-test-target-data-shard59",
+ "art-run-test-target-data-shard60",
+ "art-run-test-target-data-shard61",
+ "art-run-test-target-data-shard62",
+ "art-run-test-target-data-shard63",
+ "art-run-test-target-data-shard64",
+ "art-run-test-target-data-shard65",
+ "art-run-test-target-data-shard66",
+ "art-run-test-target-data-shard67",
+ "art-run-test-target-data-shard68",
+ "art-run-test-target-data-shard69",
+ "art-run-test-target-data-shard70",
+ "art-run-test-target-data-shard71",
+ "art-run-test-target-data-shard72",
+ "art-run-test-target-data-shard73",
+ "art-run-test-target-data-shard74",
+ "art-run-test-target-data-shard75",
+ "art-run-test-target-data-shard76",
+ "art-run-test-target-data-shard77",
+ "art-run-test-target-data-shard78",
+ "art-run-test-target-data-shard79",
+ "art-run-test-target-data-shard80",
+ "art-run-test-target-data-shard81",
+ "art-run-test-target-data-shard82",
+ "art-run-test-target-data-shard83",
+ "art-run-test-target-data-shard84",
+ "art-run-test-target-data-shard85",
+ "art-run-test-target-data-shard86",
+ "art-run-test-target-data-shard87",
+ "art-run-test-target-data-shard88",
+ "art-run-test-target-data-shard89",
+ "art-run-test-target-data-shard90",
+ "art-run-test-target-data-shard91",
+ "art-run-test-target-data-shard92",
+ "art-run-test-target-data-shard93",
+ "art-run-test-target-data-shard94",
+ "art-run-test-target-data-shard95",
+ "art-run-test-target-data-shard96",
+ "art-run-test-target-data-shard97",
+ "art-run-test-target-data-shard98",
+ "art-run-test-target-data-shard99",
+ "art-run-test-target-data-shardHiddenApi",
+ ],
+ sub_dir: "art",
+ filename: "art-run-test-target-data.txt",
+}
+
+java_genrule {
+ name: "art-run-test-jvm-data-shard00-tmp",
out: ["art-run-test-jvm-data-shard00.zip"],
- srcs: ["*00-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 00 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?00-*/**/*", "??00-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard00",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard00-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard00.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard01",
+ name: "art-run-test-jvm-data-shard01-tmp",
out: ["art-run-test-jvm-data-shard01.zip"],
- srcs: ["*01-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 01 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?01-*/**/*", "??01-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard01",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard01-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard01.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard02",
+ name: "art-run-test-jvm-data-shard02-tmp",
out: ["art-run-test-jvm-data-shard02.zip"],
- srcs: ["*02-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 02 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?02-*/**/*", "??02-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard02",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard02-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard02.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard03",
+ name: "art-run-test-jvm-data-shard03-tmp",
out: ["art-run-test-jvm-data-shard03.zip"],
- srcs: ["*03-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 03 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?03-*/**/*", "??03-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard03",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard03-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard03.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard04",
+ name: "art-run-test-jvm-data-shard04-tmp",
out: ["art-run-test-jvm-data-shard04.zip"],
- srcs: ["*04-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 04 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?04-*/**/*", "??04-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard04",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard04-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard04.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard05",
+ name: "art-run-test-jvm-data-shard05-tmp",
out: ["art-run-test-jvm-data-shard05.zip"],
- srcs: ["*05-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 05 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?05-*/**/*", "??05-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard05",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard05-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard05.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard06",
+ name: "art-run-test-jvm-data-shard06-tmp",
out: ["art-run-test-jvm-data-shard06.zip"],
- srcs: ["*06-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 06 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?06-*/**/*", "??06-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard06",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard06-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard06.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard07",
+ name: "art-run-test-jvm-data-shard07-tmp",
out: ["art-run-test-jvm-data-shard07.zip"],
- srcs: ["*07-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 07 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?07-*/**/*", "??07-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard07",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard07-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard07.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard08",
+ name: "art-run-test-jvm-data-shard08-tmp",
out: ["art-run-test-jvm-data-shard08.zip"],
- srcs: ["*08-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 08 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?08-*/**/*", "??08-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard08",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard08-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard08.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard09",
+ name: "art-run-test-jvm-data-shard09-tmp",
out: ["art-run-test-jvm-data-shard09.zip"],
- srcs: ["*09-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 09 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?09-*/**/*", "??09-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard09",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard09-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard09.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard10",
+ name: "art-run-test-jvm-data-shard10-tmp",
out: ["art-run-test-jvm-data-shard10.zip"],
- srcs: ["*10-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 10 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?10-*/**/*", "??10-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard10",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard10-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard10.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard11",
+ name: "art-run-test-jvm-data-shard11-tmp",
out: ["art-run-test-jvm-data-shard11.zip"],
- srcs: ["*11-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 11 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?11-*/**/*", "??11-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard11",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard11-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard11.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard12",
+ name: "art-run-test-jvm-data-shard12-tmp",
out: ["art-run-test-jvm-data-shard12.zip"],
- srcs: ["*12-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 12 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?12-*/**/*", "??12-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard12",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard12-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard12.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard13",
+ name: "art-run-test-jvm-data-shard13-tmp",
out: ["art-run-test-jvm-data-shard13.zip"],
- srcs: ["*13-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 13 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?13-*/**/*", "??13-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard13",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard13-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard13.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard14",
+ name: "art-run-test-jvm-data-shard14-tmp",
out: ["art-run-test-jvm-data-shard14.zip"],
- srcs: ["*14-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 14 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?14-*/**/*", "??14-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard14",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard14-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard14.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard15",
+ name: "art-run-test-jvm-data-shard15-tmp",
out: ["art-run-test-jvm-data-shard15.zip"],
- srcs: ["*15-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 15 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?15-*/**/*", "??15-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard15",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard15-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard15.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard16",
+ name: "art-run-test-jvm-data-shard16-tmp",
out: ["art-run-test-jvm-data-shard16.zip"],
- srcs: ["*16-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 16 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?16-*/**/*", "??16-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard16",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard16-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard16.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard17",
+ name: "art-run-test-jvm-data-shard17-tmp",
out: ["art-run-test-jvm-data-shard17.zip"],
- srcs: ["*17-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 17 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?17-*/**/*", "??17-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard17",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard17-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard17.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard18",
+ name: "art-run-test-jvm-data-shard18-tmp",
out: ["art-run-test-jvm-data-shard18.zip"],
- srcs: ["*18-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 18 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?18-*/**/*", "??18-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard18",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard18-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard18.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard19",
+ name: "art-run-test-jvm-data-shard19-tmp",
out: ["art-run-test-jvm-data-shard19.zip"],
- srcs: ["*19-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 19 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?19-*/**/*", "??19-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard19",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard19-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard19.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard20",
+ name: "art-run-test-jvm-data-shard20-tmp",
out: ["art-run-test-jvm-data-shard20.zip"],
- srcs: ["*20-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 20 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?20-*/**/*", "??20-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard20",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard20-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard20.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard21",
+ name: "art-run-test-jvm-data-shard21-tmp",
out: ["art-run-test-jvm-data-shard21.zip"],
- srcs: ["*21-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 21 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?21-*/**/*", "??21-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard21",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard21-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard21.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard22",
+ name: "art-run-test-jvm-data-shard22-tmp",
out: ["art-run-test-jvm-data-shard22.zip"],
- srcs: ["*22-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 22 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?22-*/**/*", "??22-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard22",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard22-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard22.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard23",
+ name: "art-run-test-jvm-data-shard23-tmp",
out: ["art-run-test-jvm-data-shard23.zip"],
- srcs: ["*23-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 23 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?23-*/**/*", "??23-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard23",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard23-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard23.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard24",
+ name: "art-run-test-jvm-data-shard24-tmp",
out: ["art-run-test-jvm-data-shard24.zip"],
- srcs: ["*24-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 24 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?24-*/**/*", "??24-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard24",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard24-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard24.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard25",
+ name: "art-run-test-jvm-data-shard25-tmp",
out: ["art-run-test-jvm-data-shard25.zip"],
- srcs: ["*25-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 25 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?25-*/**/*", "??25-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard25",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard25-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard25.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard26",
+ name: "art-run-test-jvm-data-shard26-tmp",
out: ["art-run-test-jvm-data-shard26.zip"],
- srcs: ["*26-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 26 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?26-*/**/*", "??26-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard26",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard26-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard26.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard27",
+ name: "art-run-test-jvm-data-shard27-tmp",
out: ["art-run-test-jvm-data-shard27.zip"],
- srcs: ["*27-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 27 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?27-*/**/*", "??27-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard27",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard27-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard27.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard28",
+ name: "art-run-test-jvm-data-shard28-tmp",
out: ["art-run-test-jvm-data-shard28.zip"],
- srcs: ["*28-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 28 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?28-*/**/*", "??28-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard28",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard28-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard28.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard29",
+ name: "art-run-test-jvm-data-shard29-tmp",
out: ["art-run-test-jvm-data-shard29.zip"],
- srcs: ["*29-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 29 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?29-*/**/*", "??29-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard29",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard29-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard29.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard30",
+ name: "art-run-test-jvm-data-shard30-tmp",
out: ["art-run-test-jvm-data-shard30.zip"],
- srcs: ["*30-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 30 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?30-*/**/*", "??30-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard30",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard30-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard30.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard31",
+ name: "art-run-test-jvm-data-shard31-tmp",
out: ["art-run-test-jvm-data-shard31.zip"],
- srcs: ["*31-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 31 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?31-*/**/*", "??31-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard31",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard31-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard31.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard32",
+ name: "art-run-test-jvm-data-shard32-tmp",
out: ["art-run-test-jvm-data-shard32.zip"],
- srcs: ["*32-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 32 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?32-*/**/*", "??32-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard32",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard32-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard32.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard33",
+ name: "art-run-test-jvm-data-shard33-tmp",
out: ["art-run-test-jvm-data-shard33.zip"],
- srcs: ["*33-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 33 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?33-*/**/*", "??33-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard33",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard33-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard33.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard34",
+ name: "art-run-test-jvm-data-shard34-tmp",
out: ["art-run-test-jvm-data-shard34.zip"],
- srcs: ["*34-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 34 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?34-*/**/*", "??34-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard34",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard34-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard34.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard35",
+ name: "art-run-test-jvm-data-shard35-tmp",
out: ["art-run-test-jvm-data-shard35.zip"],
- srcs: ["*35-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 35 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?35-*/**/*", "??35-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard35",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard35-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard35.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard36",
+ name: "art-run-test-jvm-data-shard36-tmp",
out: ["art-run-test-jvm-data-shard36.zip"],
- srcs: ["*36-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 36 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?36-*/**/*", "??36-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard36",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard36-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard36.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard37",
+ name: "art-run-test-jvm-data-shard37-tmp",
out: ["art-run-test-jvm-data-shard37.zip"],
- srcs: ["*37-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 37 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?37-*/**/*", "??37-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard37",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard37-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard37.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard38",
+ name: "art-run-test-jvm-data-shard38-tmp",
out: ["art-run-test-jvm-data-shard38.zip"],
- srcs: ["*38-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 38 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?38-*/**/*", "??38-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard38",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard38-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard38.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard39",
+ name: "art-run-test-jvm-data-shard39-tmp",
out: ["art-run-test-jvm-data-shard39.zip"],
- srcs: ["*39-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 39 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?39-*/**/*", "??39-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard39",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard39-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard39.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard40",
+ name: "art-run-test-jvm-data-shard40-tmp",
out: ["art-run-test-jvm-data-shard40.zip"],
- srcs: ["*40-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 40 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?40-*/**/*", "??40-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard40",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard40-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard40.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard41",
+ name: "art-run-test-jvm-data-shard41-tmp",
out: ["art-run-test-jvm-data-shard41.zip"],
- srcs: ["*41-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 41 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?41-*/**/*", "??41-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard41",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard41-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard41.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard42",
+ name: "art-run-test-jvm-data-shard42-tmp",
out: ["art-run-test-jvm-data-shard42.zip"],
- srcs: ["*42-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 42 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?42-*/**/*", "??42-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard42",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard42-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard42.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard43",
+ name: "art-run-test-jvm-data-shard43-tmp",
out: ["art-run-test-jvm-data-shard43.zip"],
- srcs: ["*43-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 43 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?43-*/**/*", "??43-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard43",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard43-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard43.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard44",
+ name: "art-run-test-jvm-data-shard44-tmp",
out: ["art-run-test-jvm-data-shard44.zip"],
- srcs: ["*44-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 44 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?44-*/**/*", "??44-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard44",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard44-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard44.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard45",
+ name: "art-run-test-jvm-data-shard45-tmp",
out: ["art-run-test-jvm-data-shard45.zip"],
- srcs: ["*45-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 45 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?45-*/**/*", "??45-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard45",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard45-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard45.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard46",
+ name: "art-run-test-jvm-data-shard46-tmp",
out: ["art-run-test-jvm-data-shard46.zip"],
- srcs: ["*46-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 46 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?46-*/**/*", "??46-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard46",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard46-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard46.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard47",
+ name: "art-run-test-jvm-data-shard47-tmp",
out: ["art-run-test-jvm-data-shard47.zip"],
- srcs: ["*47-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 47 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?47-*/**/*", "??47-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard47",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard47-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard47.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard48",
+ name: "art-run-test-jvm-data-shard48-tmp",
out: ["art-run-test-jvm-data-shard48.zip"],
- srcs: ["*48-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 48 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?48-*/**/*", "??48-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard48",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard48-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard48.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard49",
+ name: "art-run-test-jvm-data-shard49-tmp",
out: ["art-run-test-jvm-data-shard49.zip"],
- srcs: ["*49-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 49 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?49-*/**/*", "??49-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard49",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard49-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard49.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard50",
+ name: "art-run-test-jvm-data-shard50-tmp",
out: ["art-run-test-jvm-data-shard50.zip"],
- srcs: ["*50-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 50 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?50-*/**/*", "??50-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard50",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard50-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard50.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard51",
+ name: "art-run-test-jvm-data-shard51-tmp",
out: ["art-run-test-jvm-data-shard51.zip"],
- srcs: ["*51-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 51 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?51-*/**/*", "??51-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard51",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard51-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard51.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard52",
+ name: "art-run-test-jvm-data-shard52-tmp",
out: ["art-run-test-jvm-data-shard52.zip"],
- srcs: ["*52-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 52 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?52-*/**/*", "??52-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard52",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard52-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard52.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard53",
+ name: "art-run-test-jvm-data-shard53-tmp",
out: ["art-run-test-jvm-data-shard53.zip"],
- srcs: ["*53-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 53 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?53-*/**/*", "??53-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard53",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard53-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard53.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard54",
+ name: "art-run-test-jvm-data-shard54-tmp",
out: ["art-run-test-jvm-data-shard54.zip"],
- srcs: ["*54-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 54 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?54-*/**/*", "??54-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard54",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard54-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard54.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard55",
+ name: "art-run-test-jvm-data-shard55-tmp",
out: ["art-run-test-jvm-data-shard55.zip"],
- srcs: ["*55-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 55 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?55-*/**/*", "??55-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard55",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard55-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard55.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard56",
+ name: "art-run-test-jvm-data-shard56-tmp",
out: ["art-run-test-jvm-data-shard56.zip"],
- srcs: ["*56-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 56 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?56-*/**/*", "??56-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard56",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard56-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard56.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard57",
+ name: "art-run-test-jvm-data-shard57-tmp",
out: ["art-run-test-jvm-data-shard57.zip"],
- srcs: ["*57-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 57 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?57-*/**/*", "??57-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard57",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard57-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard57.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard58",
+ name: "art-run-test-jvm-data-shard58-tmp",
out: ["art-run-test-jvm-data-shard58.zip"],
- srcs: ["*58-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 58 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?58-*/**/*", "??58-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard58",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard58-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard58.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard59",
+ name: "art-run-test-jvm-data-shard59-tmp",
out: ["art-run-test-jvm-data-shard59.zip"],
- srcs: ["*59-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 59 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?59-*/**/*", "??59-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard59",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard59-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard59.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard60",
+ name: "art-run-test-jvm-data-shard60-tmp",
out: ["art-run-test-jvm-data-shard60.zip"],
- srcs: ["*60-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 60 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?60-*/**/*", "??60-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard60",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard60-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard60.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard61",
+ name: "art-run-test-jvm-data-shard61-tmp",
out: ["art-run-test-jvm-data-shard61.zip"],
- srcs: ["*61-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 61 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?61-*/**/*", "??61-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard61",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard61-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard61.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard62",
+ name: "art-run-test-jvm-data-shard62-tmp",
out: ["art-run-test-jvm-data-shard62.zip"],
- srcs: ["*62-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 62 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?62-*/**/*", "??62-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard62",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard62-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard62.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard63",
+ name: "art-run-test-jvm-data-shard63-tmp",
out: ["art-run-test-jvm-data-shard63.zip"],
- srcs: ["*63-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 63 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?63-*/**/*", "??63-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard63",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard63-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard63.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard64",
+ name: "art-run-test-jvm-data-shard64-tmp",
out: ["art-run-test-jvm-data-shard64.zip"],
- srcs: ["*64-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 64 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?64-*/**/*", "??64-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard64",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard64-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard64.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard65",
+ name: "art-run-test-jvm-data-shard65-tmp",
out: ["art-run-test-jvm-data-shard65.zip"],
- srcs: ["*65-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 65 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?65-*/**/*", "??65-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard65",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard65-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard65.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard66",
+ name: "art-run-test-jvm-data-shard66-tmp",
out: ["art-run-test-jvm-data-shard66.zip"],
- srcs: ["*66-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 66 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?66-*/**/*", "??66-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard66",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard66-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard66.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard67",
+ name: "art-run-test-jvm-data-shard67-tmp",
out: ["art-run-test-jvm-data-shard67.zip"],
- srcs: ["*67-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 67 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?67-*/**/*", "??67-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard67",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard67-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard67.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard68",
+ name: "art-run-test-jvm-data-shard68-tmp",
out: ["art-run-test-jvm-data-shard68.zip"],
- srcs: ["*68-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 68 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?68-*/**/*", "??68-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard68",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard68-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard68.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard69",
+ name: "art-run-test-jvm-data-shard69-tmp",
out: ["art-run-test-jvm-data-shard69.zip"],
- srcs: ["*69-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 69 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?69-*/**/*", "??69-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard69",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard69-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard69.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard70",
+ name: "art-run-test-jvm-data-shard70-tmp",
out: ["art-run-test-jvm-data-shard70.zip"],
- srcs: ["*70-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 70 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?70-*/**/*", "??70-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard70",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard70-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard70.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard71",
+ name: "art-run-test-jvm-data-shard71-tmp",
out: ["art-run-test-jvm-data-shard71.zip"],
- srcs: ["*71-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 71 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?71-*/**/*", "??71-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard71",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard71-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard71.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard72",
+ name: "art-run-test-jvm-data-shard72-tmp",
out: ["art-run-test-jvm-data-shard72.zip"],
- srcs: ["*72-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 72 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?72-*/**/*", "??72-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard72",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard72-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard72.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard73",
+ name: "art-run-test-jvm-data-shard73-tmp",
out: ["art-run-test-jvm-data-shard73.zip"],
- srcs: ["*73-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 73 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?73-*/**/*", "??73-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard73",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard73-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard73.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard74",
+ name: "art-run-test-jvm-data-shard74-tmp",
out: ["art-run-test-jvm-data-shard74.zip"],
- srcs: ["*74-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 74 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?74-*/**/*", "??74-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard74",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard74-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard74.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard75",
+ name: "art-run-test-jvm-data-shard75-tmp",
out: ["art-run-test-jvm-data-shard75.zip"],
- srcs: ["*75-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 75 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?75-*/**/*", "??75-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard75",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard75-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard75.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard76",
+ name: "art-run-test-jvm-data-shard76-tmp",
out: ["art-run-test-jvm-data-shard76.zip"],
- srcs: ["*76-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 76 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?76-*/**/*", "??76-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard76",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard76-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard76.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard77",
+ name: "art-run-test-jvm-data-shard77-tmp",
out: ["art-run-test-jvm-data-shard77.zip"],
- srcs: ["*77-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 77 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?77-*/**/*", "??77-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard77",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard77-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard77.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard78",
+ name: "art-run-test-jvm-data-shard78-tmp",
out: ["art-run-test-jvm-data-shard78.zip"],
- srcs: ["*78-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 78 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?78-*/**/*", "??78-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard78",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard78-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard78.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard79",
+ name: "art-run-test-jvm-data-shard79-tmp",
out: ["art-run-test-jvm-data-shard79.zip"],
- srcs: ["*79-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 79 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?79-*/**/*", "??79-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard79",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard79-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard79.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard80",
+ name: "art-run-test-jvm-data-shard80-tmp",
out: ["art-run-test-jvm-data-shard80.zip"],
- srcs: ["*80-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 80 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?80-*/**/*", "??80-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard80",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard80-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard80.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard81",
+ name: "art-run-test-jvm-data-shard81-tmp",
out: ["art-run-test-jvm-data-shard81.zip"],
- srcs: ["*81-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 81 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?81-*/**/*", "??81-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard81",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard81-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard81.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard82",
+ name: "art-run-test-jvm-data-shard82-tmp",
out: ["art-run-test-jvm-data-shard82.zip"],
- srcs: ["*82-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 82 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?82-*/**/*", "??82-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard82",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard82-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard82.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard83",
+ name: "art-run-test-jvm-data-shard83-tmp",
out: ["art-run-test-jvm-data-shard83.zip"],
- srcs: ["*83-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 83 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?83-*/**/*", "??83-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard83",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard83-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard83.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard84",
+ name: "art-run-test-jvm-data-shard84-tmp",
out: ["art-run-test-jvm-data-shard84.zip"],
- srcs: ["*84-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 84 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?84-*/**/*", "??84-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard84",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard84-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard84.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard85",
+ name: "art-run-test-jvm-data-shard85-tmp",
out: ["art-run-test-jvm-data-shard85.zip"],
- srcs: ["*85-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 85 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?85-*/**/*", "??85-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard85",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard85-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard85.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard86",
+ name: "art-run-test-jvm-data-shard86-tmp",
out: ["art-run-test-jvm-data-shard86.zip"],
- srcs: ["*86-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 86 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?86-*/**/*", "??86-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard86",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard86-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard86.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard87",
+ name: "art-run-test-jvm-data-shard87-tmp",
out: ["art-run-test-jvm-data-shard87.zip"],
- srcs: ["*87-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 87 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?87-*/**/*", "??87-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard87",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard87-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard87.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard88",
+ name: "art-run-test-jvm-data-shard88-tmp",
out: ["art-run-test-jvm-data-shard88.zip"],
- srcs: ["*88-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 88 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?88-*/**/*", "??88-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard88",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard88-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard88.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard89",
+ name: "art-run-test-jvm-data-shard89-tmp",
out: ["art-run-test-jvm-data-shard89.zip"],
- srcs: ["*89-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 89 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?89-*/**/*", "??89-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard89",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard89-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard89.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard90",
+ name: "art-run-test-jvm-data-shard90-tmp",
out: ["art-run-test-jvm-data-shard90.zip"],
- srcs: ["*90-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 90 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?90-*/**/*", "??90-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard90",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard90-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard90.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard91",
+ name: "art-run-test-jvm-data-shard91-tmp",
out: ["art-run-test-jvm-data-shard91.zip"],
- srcs: ["*91-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 91 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?91-*/**/*", "??91-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard91",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard91-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard91.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard92",
+ name: "art-run-test-jvm-data-shard92-tmp",
out: ["art-run-test-jvm-data-shard92.zip"],
- srcs: ["*92-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 92 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?92-*/**/*", "??92-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard92",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard92-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard92.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard93",
+ name: "art-run-test-jvm-data-shard93-tmp",
out: ["art-run-test-jvm-data-shard93.zip"],
- srcs: ["*93-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 93 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?93-*/**/*", "??93-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard93",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard93-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard93.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard94",
+ name: "art-run-test-jvm-data-shard94-tmp",
out: ["art-run-test-jvm-data-shard94.zip"],
- srcs: ["*94-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 94 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?94-*/**/*", "??94-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard94",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard94-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard94.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard95",
+ name: "art-run-test-jvm-data-shard95-tmp",
out: ["art-run-test-jvm-data-shard95.zip"],
- srcs: ["*95-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 95 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?95-*/**/*", "??95-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard95",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard95-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard95.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard96",
+ name: "art-run-test-jvm-data-shard96-tmp",
out: ["art-run-test-jvm-data-shard96.zip"],
- srcs: ["*96-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 96 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?96-*/**/*", "??96-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard96",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard96-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard96.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard97",
+ name: "art-run-test-jvm-data-shard97-tmp",
out: ["art-run-test-jvm-data-shard97.zip"],
- srcs: ["*97-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 97 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?97-*/**/*", "??97-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard97",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard97-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard97.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard98",
+ name: "art-run-test-jvm-data-shard98-tmp",
out: ["art-run-test-jvm-data-shard98.zip"],
- srcs: ["*98-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 98 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?98-*/**/*", "??98-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard98",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard98-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard98.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-shard99",
+ name: "art-run-test-jvm-data-shard99-tmp",
out: ["art-run-test-jvm-data-shard99.zip"],
- srcs: ["*99-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode jvm --shard 99 " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?99-*/**/*", "??99-*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shard99",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shard99-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shard99.zip",
}
java_genrule {
- name: "art-run-test-jvm-data-merged",
- defaults: ["art-run-test-data-defaults"],
+ name: "art-run-test-jvm-data-shardHiddenApi-tmp",
+ out: ["art-run-test-jvm-data-shardHiddenApi.zip"],
+ srcs: ["???-*hiddenapi*/**/*", "????-*hiddenapi*/**/*"],
+ defaults: ["art-run-test-jvm-data-defaults"],
+ tools: ["hiddenapi"],
+ cmd: "$(location run_test_build.py) --out $(out) --mode jvm " +
+ "--bootclasspath $(location :art-run-test-bootclasspath) " +
+ "--d8 $(location d8) " +
+ "--hiddenapi $(location hiddenapi) " +
+ "--jasmin $(location jasmin) " +
+ "--smali $(location smali) " +
+ "--soong_zip $(location soong_zip) " +
+ "--zipalign $(location zipalign) " +
+ "$(in)",
+}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-shardHiddenApi",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-shardHiddenApi-tmp",
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-shardHiddenApi.zip",
+}
+
+genrule_defaults {
+ name: "art-run-test-jvm-data-defaults",
+ defaults: [
+ // Enable only in source builds, where com.android.art.testing is
+ // available.
+ "art_module_source_build_genrule_defaults",
+ ],
+ tool_files: [
+ "run_test_build.py",
+ ":art-run-test-bootclasspath",
+ ],
+ tools: [
+ "d8",
+ "jasmin",
+ "smali",
+ "soong_zip",
+ "zipalign",
+ ],
+ cmd: "$(location run_test_build.py) --out $(out) --mode jvm " +
+ "--bootclasspath $(location :art-run-test-bootclasspath) " +
+ "--d8 $(location d8) " +
+ "--jasmin $(location jasmin) " +
+ "--smali $(location smali) " +
+ "--soong_zip $(location soong_zip) " +
+ "--zipalign $(location zipalign) " +
+ "$(in)",
+}
+
+java_genrule {
+ name: "art-run-test-jvm-data-merged-tmp",
+ defaults: ["art_module_source_build_genrule_defaults"],
out: ["art-run-test-jvm-data-merged.zip"],
srcs: [
- ":art-run-test-jvm-data-shard00",
- ":art-run-test-jvm-data-shard01",
- ":art-run-test-jvm-data-shard02",
- ":art-run-test-jvm-data-shard03",
- ":art-run-test-jvm-data-shard04",
- ":art-run-test-jvm-data-shard05",
- ":art-run-test-jvm-data-shard06",
- ":art-run-test-jvm-data-shard07",
- ":art-run-test-jvm-data-shard08",
- ":art-run-test-jvm-data-shard09",
- ":art-run-test-jvm-data-shard10",
- ":art-run-test-jvm-data-shard11",
- ":art-run-test-jvm-data-shard12",
- ":art-run-test-jvm-data-shard13",
- ":art-run-test-jvm-data-shard14",
- ":art-run-test-jvm-data-shard15",
- ":art-run-test-jvm-data-shard16",
- ":art-run-test-jvm-data-shard17",
- ":art-run-test-jvm-data-shard18",
- ":art-run-test-jvm-data-shard19",
- ":art-run-test-jvm-data-shard20",
- ":art-run-test-jvm-data-shard21",
- ":art-run-test-jvm-data-shard22",
- ":art-run-test-jvm-data-shard23",
- ":art-run-test-jvm-data-shard24",
- ":art-run-test-jvm-data-shard25",
- ":art-run-test-jvm-data-shard26",
- ":art-run-test-jvm-data-shard27",
- ":art-run-test-jvm-data-shard28",
- ":art-run-test-jvm-data-shard29",
- ":art-run-test-jvm-data-shard30",
- ":art-run-test-jvm-data-shard31",
- ":art-run-test-jvm-data-shard32",
- ":art-run-test-jvm-data-shard33",
- ":art-run-test-jvm-data-shard34",
- ":art-run-test-jvm-data-shard35",
- ":art-run-test-jvm-data-shard36",
- ":art-run-test-jvm-data-shard37",
- ":art-run-test-jvm-data-shard38",
- ":art-run-test-jvm-data-shard39",
- ":art-run-test-jvm-data-shard40",
- ":art-run-test-jvm-data-shard41",
- ":art-run-test-jvm-data-shard42",
- ":art-run-test-jvm-data-shard43",
- ":art-run-test-jvm-data-shard44",
- ":art-run-test-jvm-data-shard45",
- ":art-run-test-jvm-data-shard46",
- ":art-run-test-jvm-data-shard47",
- ":art-run-test-jvm-data-shard48",
- ":art-run-test-jvm-data-shard49",
- ":art-run-test-jvm-data-shard50",
- ":art-run-test-jvm-data-shard51",
- ":art-run-test-jvm-data-shard52",
- ":art-run-test-jvm-data-shard53",
- ":art-run-test-jvm-data-shard54",
- ":art-run-test-jvm-data-shard55",
- ":art-run-test-jvm-data-shard56",
- ":art-run-test-jvm-data-shard57",
- ":art-run-test-jvm-data-shard58",
- ":art-run-test-jvm-data-shard59",
- ":art-run-test-jvm-data-shard60",
- ":art-run-test-jvm-data-shard61",
- ":art-run-test-jvm-data-shard62",
- ":art-run-test-jvm-data-shard63",
- ":art-run-test-jvm-data-shard64",
- ":art-run-test-jvm-data-shard65",
- ":art-run-test-jvm-data-shard66",
- ":art-run-test-jvm-data-shard67",
- ":art-run-test-jvm-data-shard68",
- ":art-run-test-jvm-data-shard69",
- ":art-run-test-jvm-data-shard70",
- ":art-run-test-jvm-data-shard71",
- ":art-run-test-jvm-data-shard72",
- ":art-run-test-jvm-data-shard73",
- ":art-run-test-jvm-data-shard74",
- ":art-run-test-jvm-data-shard75",
- ":art-run-test-jvm-data-shard76",
- ":art-run-test-jvm-data-shard77",
- ":art-run-test-jvm-data-shard78",
- ":art-run-test-jvm-data-shard79",
- ":art-run-test-jvm-data-shard80",
- ":art-run-test-jvm-data-shard81",
- ":art-run-test-jvm-data-shard82",
- ":art-run-test-jvm-data-shard83",
- ":art-run-test-jvm-data-shard84",
- ":art-run-test-jvm-data-shard85",
- ":art-run-test-jvm-data-shard86",
- ":art-run-test-jvm-data-shard87",
- ":art-run-test-jvm-data-shard88",
- ":art-run-test-jvm-data-shard89",
- ":art-run-test-jvm-data-shard90",
- ":art-run-test-jvm-data-shard91",
- ":art-run-test-jvm-data-shard92",
- ":art-run-test-jvm-data-shard93",
- ":art-run-test-jvm-data-shard94",
- ":art-run-test-jvm-data-shard95",
- ":art-run-test-jvm-data-shard96",
- ":art-run-test-jvm-data-shard97",
- ":art-run-test-jvm-data-shard98",
- ":art-run-test-jvm-data-shard99",
+ ":art-run-test-jvm-data-shard00-tmp",
+ ":art-run-test-jvm-data-shard01-tmp",
+ ":art-run-test-jvm-data-shard02-tmp",
+ ":art-run-test-jvm-data-shard03-tmp",
+ ":art-run-test-jvm-data-shard04-tmp",
+ ":art-run-test-jvm-data-shard05-tmp",
+ ":art-run-test-jvm-data-shard06-tmp",
+ ":art-run-test-jvm-data-shard07-tmp",
+ ":art-run-test-jvm-data-shard08-tmp",
+ ":art-run-test-jvm-data-shard09-tmp",
+ ":art-run-test-jvm-data-shard10-tmp",
+ ":art-run-test-jvm-data-shard11-tmp",
+ ":art-run-test-jvm-data-shard12-tmp",
+ ":art-run-test-jvm-data-shard13-tmp",
+ ":art-run-test-jvm-data-shard14-tmp",
+ ":art-run-test-jvm-data-shard15-tmp",
+ ":art-run-test-jvm-data-shard16-tmp",
+ ":art-run-test-jvm-data-shard17-tmp",
+ ":art-run-test-jvm-data-shard18-tmp",
+ ":art-run-test-jvm-data-shard19-tmp",
+ ":art-run-test-jvm-data-shard20-tmp",
+ ":art-run-test-jvm-data-shard21-tmp",
+ ":art-run-test-jvm-data-shard22-tmp",
+ ":art-run-test-jvm-data-shard23-tmp",
+ ":art-run-test-jvm-data-shard24-tmp",
+ ":art-run-test-jvm-data-shard25-tmp",
+ ":art-run-test-jvm-data-shard26-tmp",
+ ":art-run-test-jvm-data-shard27-tmp",
+ ":art-run-test-jvm-data-shard28-tmp",
+ ":art-run-test-jvm-data-shard29-tmp",
+ ":art-run-test-jvm-data-shard30-tmp",
+ ":art-run-test-jvm-data-shard31-tmp",
+ ":art-run-test-jvm-data-shard32-tmp",
+ ":art-run-test-jvm-data-shard33-tmp",
+ ":art-run-test-jvm-data-shard34-tmp",
+ ":art-run-test-jvm-data-shard35-tmp",
+ ":art-run-test-jvm-data-shard36-tmp",
+ ":art-run-test-jvm-data-shard37-tmp",
+ ":art-run-test-jvm-data-shard38-tmp",
+ ":art-run-test-jvm-data-shard39-tmp",
+ ":art-run-test-jvm-data-shard40-tmp",
+ ":art-run-test-jvm-data-shard41-tmp",
+ ":art-run-test-jvm-data-shard42-tmp",
+ ":art-run-test-jvm-data-shard43-tmp",
+ ":art-run-test-jvm-data-shard44-tmp",
+ ":art-run-test-jvm-data-shard45-tmp",
+ ":art-run-test-jvm-data-shard46-tmp",
+ ":art-run-test-jvm-data-shard47-tmp",
+ ":art-run-test-jvm-data-shard48-tmp",
+ ":art-run-test-jvm-data-shard49-tmp",
+ ":art-run-test-jvm-data-shard50-tmp",
+ ":art-run-test-jvm-data-shard51-tmp",
+ ":art-run-test-jvm-data-shard52-tmp",
+ ":art-run-test-jvm-data-shard53-tmp",
+ ":art-run-test-jvm-data-shard54-tmp",
+ ":art-run-test-jvm-data-shard55-tmp",
+ ":art-run-test-jvm-data-shard56-tmp",
+ ":art-run-test-jvm-data-shard57-tmp",
+ ":art-run-test-jvm-data-shard58-tmp",
+ ":art-run-test-jvm-data-shard59-tmp",
+ ":art-run-test-jvm-data-shard60-tmp",
+ ":art-run-test-jvm-data-shard61-tmp",
+ ":art-run-test-jvm-data-shard62-tmp",
+ ":art-run-test-jvm-data-shard63-tmp",
+ ":art-run-test-jvm-data-shard64-tmp",
+ ":art-run-test-jvm-data-shard65-tmp",
+ ":art-run-test-jvm-data-shard66-tmp",
+ ":art-run-test-jvm-data-shard67-tmp",
+ ":art-run-test-jvm-data-shard68-tmp",
+ ":art-run-test-jvm-data-shard69-tmp",
+ ":art-run-test-jvm-data-shard70-tmp",
+ ":art-run-test-jvm-data-shard71-tmp",
+ ":art-run-test-jvm-data-shard72-tmp",
+ ":art-run-test-jvm-data-shard73-tmp",
+ ":art-run-test-jvm-data-shard74-tmp",
+ ":art-run-test-jvm-data-shard75-tmp",
+ ":art-run-test-jvm-data-shard76-tmp",
+ ":art-run-test-jvm-data-shard77-tmp",
+ ":art-run-test-jvm-data-shard78-tmp",
+ ":art-run-test-jvm-data-shard79-tmp",
+ ":art-run-test-jvm-data-shard80-tmp",
+ ":art-run-test-jvm-data-shard81-tmp",
+ ":art-run-test-jvm-data-shard82-tmp",
+ ":art-run-test-jvm-data-shard83-tmp",
+ ":art-run-test-jvm-data-shard84-tmp",
+ ":art-run-test-jvm-data-shard85-tmp",
+ ":art-run-test-jvm-data-shard86-tmp",
+ ":art-run-test-jvm-data-shard87-tmp",
+ ":art-run-test-jvm-data-shard88-tmp",
+ ":art-run-test-jvm-data-shard89-tmp",
+ ":art-run-test-jvm-data-shard90-tmp",
+ ":art-run-test-jvm-data-shard91-tmp",
+ ":art-run-test-jvm-data-shard92-tmp",
+ ":art-run-test-jvm-data-shard93-tmp",
+ ":art-run-test-jvm-data-shard94-tmp",
+ ":art-run-test-jvm-data-shard95-tmp",
+ ":art-run-test-jvm-data-shard96-tmp",
+ ":art-run-test-jvm-data-shard97-tmp",
+ ":art-run-test-jvm-data-shard98-tmp",
+ ":art-run-test-jvm-data-shard99-tmp",
+ ":art-run-test-jvm-data-shardHiddenApi-tmp",
],
tools: ["merge_zips"],
cmd: "$(location merge_zips) $(out) $(in)",
}
+
+// Install in the output directory to make it accessible for tests.
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data-merged",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-merged-tmp",
+ required: [
+ "art-run-test-jvm-data-shard00",
+ "art-run-test-jvm-data-shard01",
+ "art-run-test-jvm-data-shard02",
+ "art-run-test-jvm-data-shard03",
+ "art-run-test-jvm-data-shard04",
+ "art-run-test-jvm-data-shard05",
+ "art-run-test-jvm-data-shard06",
+ "art-run-test-jvm-data-shard07",
+ "art-run-test-jvm-data-shard08",
+ "art-run-test-jvm-data-shard09",
+ "art-run-test-jvm-data-shard10",
+ "art-run-test-jvm-data-shard11",
+ "art-run-test-jvm-data-shard12",
+ "art-run-test-jvm-data-shard13",
+ "art-run-test-jvm-data-shard14",
+ "art-run-test-jvm-data-shard15",
+ "art-run-test-jvm-data-shard16",
+ "art-run-test-jvm-data-shard17",
+ "art-run-test-jvm-data-shard18",
+ "art-run-test-jvm-data-shard19",
+ "art-run-test-jvm-data-shard20",
+ "art-run-test-jvm-data-shard21",
+ "art-run-test-jvm-data-shard22",
+ "art-run-test-jvm-data-shard23",
+ "art-run-test-jvm-data-shard24",
+ "art-run-test-jvm-data-shard25",
+ "art-run-test-jvm-data-shard26",
+ "art-run-test-jvm-data-shard27",
+ "art-run-test-jvm-data-shard28",
+ "art-run-test-jvm-data-shard29",
+ "art-run-test-jvm-data-shard30",
+ "art-run-test-jvm-data-shard31",
+ "art-run-test-jvm-data-shard32",
+ "art-run-test-jvm-data-shard33",
+ "art-run-test-jvm-data-shard34",
+ "art-run-test-jvm-data-shard35",
+ "art-run-test-jvm-data-shard36",
+ "art-run-test-jvm-data-shard37",
+ "art-run-test-jvm-data-shard38",
+ "art-run-test-jvm-data-shard39",
+ "art-run-test-jvm-data-shard40",
+ "art-run-test-jvm-data-shard41",
+ "art-run-test-jvm-data-shard42",
+ "art-run-test-jvm-data-shard43",
+ "art-run-test-jvm-data-shard44",
+ "art-run-test-jvm-data-shard45",
+ "art-run-test-jvm-data-shard46",
+ "art-run-test-jvm-data-shard47",
+ "art-run-test-jvm-data-shard48",
+ "art-run-test-jvm-data-shard49",
+ "art-run-test-jvm-data-shard50",
+ "art-run-test-jvm-data-shard51",
+ "art-run-test-jvm-data-shard52",
+ "art-run-test-jvm-data-shard53",
+ "art-run-test-jvm-data-shard54",
+ "art-run-test-jvm-data-shard55",
+ "art-run-test-jvm-data-shard56",
+ "art-run-test-jvm-data-shard57",
+ "art-run-test-jvm-data-shard58",
+ "art-run-test-jvm-data-shard59",
+ "art-run-test-jvm-data-shard60",
+ "art-run-test-jvm-data-shard61",
+ "art-run-test-jvm-data-shard62",
+ "art-run-test-jvm-data-shard63",
+ "art-run-test-jvm-data-shard64",
+ "art-run-test-jvm-data-shard65",
+ "art-run-test-jvm-data-shard66",
+ "art-run-test-jvm-data-shard67",
+ "art-run-test-jvm-data-shard68",
+ "art-run-test-jvm-data-shard69",
+ "art-run-test-jvm-data-shard70",
+ "art-run-test-jvm-data-shard71",
+ "art-run-test-jvm-data-shard72",
+ "art-run-test-jvm-data-shard73",
+ "art-run-test-jvm-data-shard74",
+ "art-run-test-jvm-data-shard75",
+ "art-run-test-jvm-data-shard76",
+ "art-run-test-jvm-data-shard77",
+ "art-run-test-jvm-data-shard78",
+ "art-run-test-jvm-data-shard79",
+ "art-run-test-jvm-data-shard80",
+ "art-run-test-jvm-data-shard81",
+ "art-run-test-jvm-data-shard82",
+ "art-run-test-jvm-data-shard83",
+ "art-run-test-jvm-data-shard84",
+ "art-run-test-jvm-data-shard85",
+ "art-run-test-jvm-data-shard86",
+ "art-run-test-jvm-data-shard87",
+ "art-run-test-jvm-data-shard88",
+ "art-run-test-jvm-data-shard89",
+ "art-run-test-jvm-data-shard90",
+ "art-run-test-jvm-data-shard91",
+ "art-run-test-jvm-data-shard92",
+ "art-run-test-jvm-data-shard93",
+ "art-run-test-jvm-data-shard94",
+ "art-run-test-jvm-data-shard95",
+ "art-run-test-jvm-data-shard96",
+ "art-run-test-jvm-data-shard97",
+ "art-run-test-jvm-data-shard98",
+ "art-run-test-jvm-data-shard99",
+ "art-run-test-jvm-data-shardHiddenApi",
+ ],
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data-merged.zip",
+}
+
+// Phony target used to build all shards
+java_genrule {
+ name: "art-run-test-jvm-data-tmp",
+ defaults: ["art-run-test-data-defaults"],
+ out: ["art-run-test-jvm-data.txt"],
+ srcs: [
+ ":art-run-test-jvm-data-shard00-tmp",
+ ":art-run-test-jvm-data-shard01-tmp",
+ ":art-run-test-jvm-data-shard02-tmp",
+ ":art-run-test-jvm-data-shard03-tmp",
+ ":art-run-test-jvm-data-shard04-tmp",
+ ":art-run-test-jvm-data-shard05-tmp",
+ ":art-run-test-jvm-data-shard06-tmp",
+ ":art-run-test-jvm-data-shard07-tmp",
+ ":art-run-test-jvm-data-shard08-tmp",
+ ":art-run-test-jvm-data-shard09-tmp",
+ ":art-run-test-jvm-data-shard10-tmp",
+ ":art-run-test-jvm-data-shard11-tmp",
+ ":art-run-test-jvm-data-shard12-tmp",
+ ":art-run-test-jvm-data-shard13-tmp",
+ ":art-run-test-jvm-data-shard14-tmp",
+ ":art-run-test-jvm-data-shard15-tmp",
+ ":art-run-test-jvm-data-shard16-tmp",
+ ":art-run-test-jvm-data-shard17-tmp",
+ ":art-run-test-jvm-data-shard18-tmp",
+ ":art-run-test-jvm-data-shard19-tmp",
+ ":art-run-test-jvm-data-shard20-tmp",
+ ":art-run-test-jvm-data-shard21-tmp",
+ ":art-run-test-jvm-data-shard22-tmp",
+ ":art-run-test-jvm-data-shard23-tmp",
+ ":art-run-test-jvm-data-shard24-tmp",
+ ":art-run-test-jvm-data-shard25-tmp",
+ ":art-run-test-jvm-data-shard26-tmp",
+ ":art-run-test-jvm-data-shard27-tmp",
+ ":art-run-test-jvm-data-shard28-tmp",
+ ":art-run-test-jvm-data-shard29-tmp",
+ ":art-run-test-jvm-data-shard30-tmp",
+ ":art-run-test-jvm-data-shard31-tmp",
+ ":art-run-test-jvm-data-shard32-tmp",
+ ":art-run-test-jvm-data-shard33-tmp",
+ ":art-run-test-jvm-data-shard34-tmp",
+ ":art-run-test-jvm-data-shard35-tmp",
+ ":art-run-test-jvm-data-shard36-tmp",
+ ":art-run-test-jvm-data-shard37-tmp",
+ ":art-run-test-jvm-data-shard38-tmp",
+ ":art-run-test-jvm-data-shard39-tmp",
+ ":art-run-test-jvm-data-shard40-tmp",
+ ":art-run-test-jvm-data-shard41-tmp",
+ ":art-run-test-jvm-data-shard42-tmp",
+ ":art-run-test-jvm-data-shard43-tmp",
+ ":art-run-test-jvm-data-shard44-tmp",
+ ":art-run-test-jvm-data-shard45-tmp",
+ ":art-run-test-jvm-data-shard46-tmp",
+ ":art-run-test-jvm-data-shard47-tmp",
+ ":art-run-test-jvm-data-shard48-tmp",
+ ":art-run-test-jvm-data-shard49-tmp",
+ ":art-run-test-jvm-data-shard50-tmp",
+ ":art-run-test-jvm-data-shard51-tmp",
+ ":art-run-test-jvm-data-shard52-tmp",
+ ":art-run-test-jvm-data-shard53-tmp",
+ ":art-run-test-jvm-data-shard54-tmp",
+ ":art-run-test-jvm-data-shard55-tmp",
+ ":art-run-test-jvm-data-shard56-tmp",
+ ":art-run-test-jvm-data-shard57-tmp",
+ ":art-run-test-jvm-data-shard58-tmp",
+ ":art-run-test-jvm-data-shard59-tmp",
+ ":art-run-test-jvm-data-shard60-tmp",
+ ":art-run-test-jvm-data-shard61-tmp",
+ ":art-run-test-jvm-data-shard62-tmp",
+ ":art-run-test-jvm-data-shard63-tmp",
+ ":art-run-test-jvm-data-shard64-tmp",
+ ":art-run-test-jvm-data-shard65-tmp",
+ ":art-run-test-jvm-data-shard66-tmp",
+ ":art-run-test-jvm-data-shard67-tmp",
+ ":art-run-test-jvm-data-shard68-tmp",
+ ":art-run-test-jvm-data-shard69-tmp",
+ ":art-run-test-jvm-data-shard70-tmp",
+ ":art-run-test-jvm-data-shard71-tmp",
+ ":art-run-test-jvm-data-shard72-tmp",
+ ":art-run-test-jvm-data-shard73-tmp",
+ ":art-run-test-jvm-data-shard74-tmp",
+ ":art-run-test-jvm-data-shard75-tmp",
+ ":art-run-test-jvm-data-shard76-tmp",
+ ":art-run-test-jvm-data-shard77-tmp",
+ ":art-run-test-jvm-data-shard78-tmp",
+ ":art-run-test-jvm-data-shard79-tmp",
+ ":art-run-test-jvm-data-shard80-tmp",
+ ":art-run-test-jvm-data-shard81-tmp",
+ ":art-run-test-jvm-data-shard82-tmp",
+ ":art-run-test-jvm-data-shard83-tmp",
+ ":art-run-test-jvm-data-shard84-tmp",
+ ":art-run-test-jvm-data-shard85-tmp",
+ ":art-run-test-jvm-data-shard86-tmp",
+ ":art-run-test-jvm-data-shard87-tmp",
+ ":art-run-test-jvm-data-shard88-tmp",
+ ":art-run-test-jvm-data-shard89-tmp",
+ ":art-run-test-jvm-data-shard90-tmp",
+ ":art-run-test-jvm-data-shard91-tmp",
+ ":art-run-test-jvm-data-shard92-tmp",
+ ":art-run-test-jvm-data-shard93-tmp",
+ ":art-run-test-jvm-data-shard94-tmp",
+ ":art-run-test-jvm-data-shard95-tmp",
+ ":art-run-test-jvm-data-shard96-tmp",
+ ":art-run-test-jvm-data-shard97-tmp",
+ ":art-run-test-jvm-data-shard98-tmp",
+ ":art-run-test-jvm-data-shard99-tmp",
+ ":art-run-test-jvm-data-shardHiddenApi-tmp",
+ ],
+ cmd: "echo $(in) > $(out)",
+}
+
+// Phony target used to install all shards
+prebuilt_etc_host {
+ name: "art-run-test-jvm-data",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":art-run-test-jvm-data-tmp",
+ required: [
+ "art-run-test-jvm-data-shard00",
+ "art-run-test-jvm-data-shard01",
+ "art-run-test-jvm-data-shard02",
+ "art-run-test-jvm-data-shard03",
+ "art-run-test-jvm-data-shard04",
+ "art-run-test-jvm-data-shard05",
+ "art-run-test-jvm-data-shard06",
+ "art-run-test-jvm-data-shard07",
+ "art-run-test-jvm-data-shard08",
+ "art-run-test-jvm-data-shard09",
+ "art-run-test-jvm-data-shard10",
+ "art-run-test-jvm-data-shard11",
+ "art-run-test-jvm-data-shard12",
+ "art-run-test-jvm-data-shard13",
+ "art-run-test-jvm-data-shard14",
+ "art-run-test-jvm-data-shard15",
+ "art-run-test-jvm-data-shard16",
+ "art-run-test-jvm-data-shard17",
+ "art-run-test-jvm-data-shard18",
+ "art-run-test-jvm-data-shard19",
+ "art-run-test-jvm-data-shard20",
+ "art-run-test-jvm-data-shard21",
+ "art-run-test-jvm-data-shard22",
+ "art-run-test-jvm-data-shard23",
+ "art-run-test-jvm-data-shard24",
+ "art-run-test-jvm-data-shard25",
+ "art-run-test-jvm-data-shard26",
+ "art-run-test-jvm-data-shard27",
+ "art-run-test-jvm-data-shard28",
+ "art-run-test-jvm-data-shard29",
+ "art-run-test-jvm-data-shard30",
+ "art-run-test-jvm-data-shard31",
+ "art-run-test-jvm-data-shard32",
+ "art-run-test-jvm-data-shard33",
+ "art-run-test-jvm-data-shard34",
+ "art-run-test-jvm-data-shard35",
+ "art-run-test-jvm-data-shard36",
+ "art-run-test-jvm-data-shard37",
+ "art-run-test-jvm-data-shard38",
+ "art-run-test-jvm-data-shard39",
+ "art-run-test-jvm-data-shard40",
+ "art-run-test-jvm-data-shard41",
+ "art-run-test-jvm-data-shard42",
+ "art-run-test-jvm-data-shard43",
+ "art-run-test-jvm-data-shard44",
+ "art-run-test-jvm-data-shard45",
+ "art-run-test-jvm-data-shard46",
+ "art-run-test-jvm-data-shard47",
+ "art-run-test-jvm-data-shard48",
+ "art-run-test-jvm-data-shard49",
+ "art-run-test-jvm-data-shard50",
+ "art-run-test-jvm-data-shard51",
+ "art-run-test-jvm-data-shard52",
+ "art-run-test-jvm-data-shard53",
+ "art-run-test-jvm-data-shard54",
+ "art-run-test-jvm-data-shard55",
+ "art-run-test-jvm-data-shard56",
+ "art-run-test-jvm-data-shard57",
+ "art-run-test-jvm-data-shard58",
+ "art-run-test-jvm-data-shard59",
+ "art-run-test-jvm-data-shard60",
+ "art-run-test-jvm-data-shard61",
+ "art-run-test-jvm-data-shard62",
+ "art-run-test-jvm-data-shard63",
+ "art-run-test-jvm-data-shard64",
+ "art-run-test-jvm-data-shard65",
+ "art-run-test-jvm-data-shard66",
+ "art-run-test-jvm-data-shard67",
+ "art-run-test-jvm-data-shard68",
+ "art-run-test-jvm-data-shard69",
+ "art-run-test-jvm-data-shard70",
+ "art-run-test-jvm-data-shard71",
+ "art-run-test-jvm-data-shard72",
+ "art-run-test-jvm-data-shard73",
+ "art-run-test-jvm-data-shard74",
+ "art-run-test-jvm-data-shard75",
+ "art-run-test-jvm-data-shard76",
+ "art-run-test-jvm-data-shard77",
+ "art-run-test-jvm-data-shard78",
+ "art-run-test-jvm-data-shard79",
+ "art-run-test-jvm-data-shard80",
+ "art-run-test-jvm-data-shard81",
+ "art-run-test-jvm-data-shard82",
+ "art-run-test-jvm-data-shard83",
+ "art-run-test-jvm-data-shard84",
+ "art-run-test-jvm-data-shard85",
+ "art-run-test-jvm-data-shard86",
+ "art-run-test-jvm-data-shard87",
+ "art-run-test-jvm-data-shard88",
+ "art-run-test-jvm-data-shard89",
+ "art-run-test-jvm-data-shard90",
+ "art-run-test-jvm-data-shard91",
+ "art-run-test-jvm-data-shard92",
+ "art-run-test-jvm-data-shard93",
+ "art-run-test-jvm-data-shard94",
+ "art-run-test-jvm-data-shard95",
+ "art-run-test-jvm-data-shard96",
+ "art-run-test-jvm-data-shard97",
+ "art-run-test-jvm-data-shard98",
+ "art-run-test-jvm-data-shard99",
+ "art-run-test-jvm-data-shardHiddenApi",
+ ],
+ sub_dir: "art",
+ filename: "art-run-test-jvm-data.txt",
+}
diff --git a/test/Android.run-test.bp.py b/test/Android.run-test.bp.py
index a051a1d..d1e3027 100755
--- a/test/Android.run-test.bp.py
+++ b/test/Android.run-test.bp.py
@@ -35,27 +35,140 @@
names.append(name)
f.write(textwrap.dedent("""
java_genrule {{
- name: "{name}",
+ name: "{name}-tmp",
out: ["{name}.zip"],
- srcs: ["*{shard}-*/**/*"],
- defaults: ["art-run-test-data-defaults"],
- cmd: "$(location run-test-build.py) --out $(out) --mode {mode} --shard {shard} " +
- "--bootclasspath $(location :art-run-test-bootclasspath)",
+ srcs: ["?{shard}-*/**/*", "??{shard}-*/**/*"],
+ defaults: ["art-run-test-{mode}-data-defaults"],
+ }}
+
+ // Install in the output directory to make it accessible for tests.
+ prebuilt_etc_host {{
+ name: "{name}",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":{name}-tmp",
+ sub_dir: "art",
+ filename: "{name}.zip",
}}
""".format(name=name, mode=mode, shard=shard)))
- srcs = ("\n"+" "*8).join('":{}",'.format(n) for n in names)
+
+ # Build all hiddenapi tests in their own shard.
+ # This removes the dependency on hiddenapi from all other shards,
+ # which in turn removes dependency on ART C++ source code.
+ name = "art-run-test-{mode}-data-shardHiddenApi".format(mode=mode)
+ names.append(name)
f.write(textwrap.dedent("""
java_genrule {{
- name: "art-run-test-{mode}-data-merged",
- defaults: ["art-run-test-data-defaults"],
- out: ["art-run-test-{mode}-data-merged.zip"],
+ name: "{name}-tmp",
+ out: ["{name}.zip"],
+ srcs: ["???-*hiddenapi*/**/*", "????-*hiddenapi*/**/*"],
+ defaults: ["art-run-test-{mode}-data-defaults"],
+ tools: ["hiddenapi"],
+ cmd: "$(location run_test_build.py) --out $(out) --mode {mode} " +
+ "--bootclasspath $(location :art-run-test-bootclasspath) " +
+ "--d8 $(location d8) " +
+ "--hiddenapi $(location hiddenapi) " +
+ "--jasmin $(location jasmin) " +
+ "--smali $(location smali) " +
+ "--soong_zip $(location soong_zip) " +
+ "--zipalign $(location zipalign) " +
+ "$(in)",
+ }}
+
+ // Install in the output directory to make it accessible for tests.
+ prebuilt_etc_host {{
+ name: "{name}",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":{name}-tmp",
+ sub_dir: "art",
+ filename: "{name}.zip",
+ }}
+ """.format(name=name, mode=mode)))
+
+ f.write(textwrap.dedent("""
+ genrule_defaults {{
+ name: "art-run-test-{mode}-data-defaults",
+ defaults: [
+ // Enable only in source builds, where com.android.art.testing is
+ // available.
+ "art_module_source_build_genrule_defaults",
+ ],
+ tool_files: [
+ "run_test_build.py",
+ ":art-run-test-bootclasspath",
+ ],
+ tools: [
+ "d8",
+ "jasmin",
+ "smali",
+ "soong_zip",
+ "zipalign",
+ ],
+ cmd: "$(location run_test_build.py) --out $(out) --mode {mode} " +
+ "--bootclasspath $(location :art-run-test-bootclasspath) " +
+ "--d8 $(location d8) " +
+ "--jasmin $(location jasmin) " +
+ "--smali $(location smali) " +
+ "--soong_zip $(location soong_zip) " +
+ "--zipalign $(location zipalign) " +
+ "$(in)",
+ }}
+ """).format(mode=mode))
+
+ name = "art-run-test-{mode}-data-merged".format(mode=mode)
+ srcs = ("\n"+" "*8).join('":{}-tmp",'.format(n) for n in names)
+ deps = ("\n"+" "*8).join('"{}",'.format(n) for n in names)
+ f.write(textwrap.dedent("""
+ java_genrule {{
+ name: "{name}-tmp",
+ defaults: ["art_module_source_build_genrule_defaults"],
+ out: ["{name}.zip"],
srcs: [
{srcs}
],
tools: ["merge_zips"],
cmd: "$(location merge_zips) $(out) $(in)",
}}
- """).format(mode=mode, srcs=srcs))
+
+ // Install in the output directory to make it accessible for tests.
+ prebuilt_etc_host {{
+ name: "{name}",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":{name}-tmp",
+ required: [
+ {deps}
+ ],
+ sub_dir: "art",
+ filename: "{name}.zip",
+ }}
+ """).format(name=name, srcs=srcs, deps=deps))
+
+ name = "art-run-test-{mode}-data".format(mode=mode)
+ srcs = ("\n"+" "*8).join('":{}-tmp",'.format(n) for n in names)
+ deps = ("\n"+" "*8).join('"{}",'.format(n) for n in names)
+ f.write(textwrap.dedent("""
+ // Phony target used to build all shards
+ java_genrule {{
+ name: "{name}-tmp",
+ defaults: ["art-run-test-data-defaults"],
+ out: ["{name}.txt"],
+ srcs: [
+ {srcs}
+ ],
+ cmd: "echo $(in) > $(out)",
+ }}
+
+ // Phony target used to install all shards
+ prebuilt_etc_host {{
+ name: "{name}",
+ defaults: ["art_module_source_build_prebuilt_defaults"],
+ src: ":{name}-tmp",
+ required: [
+ {deps}
+ ],
+ sub_dir: "art",
+ filename: "{name}.txt",
+ }}
+ """).format(name=name, srcs=srcs, deps=deps))
if __name__ == "__main__":
main()
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index ea305ce..9dec0c5 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -48,14 +48,10 @@
# Also need signal_dumper.
ART_TEST_TARGET_RUN_TEST_DEPENDENCIES += signal_dumper-target
-ART_TEST_TARGET_RUN_TEST_DEPENDENCIES += art-run-test-target-data
-
# All tests require the host executables. The tests also depend on the core images, but on
# specific version depending on the compiler.
ART_TEST_HOST_RUN_TEST_DEPENDENCIES := \
$(ART_HOST_EXECUTABLES) \
- art-run-test-host-data \
- art-run-test-jvm-data \
$(HOST_OUT_EXECUTABLES)/hprof-conv \
$(HOST_OUT_EXECUTABLES)/signal_dumper \
$(ART_TEST_LIST_host_$(ART_HOST_ARCH)_libtiagent) \
@@ -104,6 +100,9 @@
.PHONY: test-art-host-run-test-dependencies
test-art-run-test-dependencies : test-art-host-run-test-dependencies
+test-art-jvm-run-test-dependencies : test-art-host-run-test-dependencies
+.PHONY: test-art-jvm-run-test-dependencies
+
test-art-target-run-test-dependencies :
.PHONY: test-art-target-run-test-dependencies
test-art-run-test-dependencies : test-art-target-run-test-dependencies
diff --git a/test/ArrayClassWithUnresolvedComponent/ClassWithMissingInterface.smali b/test/ArrayClassWithUnresolvedComponent/ClassWithMissingInterface.smali
new file mode 100644
index 0000000..f68d70c
--- /dev/null
+++ b/test/ArrayClassWithUnresolvedComponent/ClassWithMissingInterface.smali
@@ -0,0 +1,18 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LClassWithMissingInterface;
+
+.super Ljava/lang/Object;
+.implements LMissingInterface;
diff --git a/test/ArrayClassWithUnresolvedComponent/ClassWithMissingSuper.smali b/test/ArrayClassWithUnresolvedComponent/ClassWithMissingSuper.smali
new file mode 100644
index 0000000..134302c
--- /dev/null
+++ b/test/ArrayClassWithUnresolvedComponent/ClassWithMissingSuper.smali
@@ -0,0 +1,17 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LClassWithMissingSuper;
+
+.super LMissingClass;
diff --git a/test/ArrayClassWithUnresolvedComponent/ClassWithStatic.smali b/test/ArrayClassWithUnresolvedComponent/ClassWithStatic.smali
new file mode 100644
index 0000000..f35e5f0
--- /dev/null
+++ b/test/ArrayClassWithUnresolvedComponent/ClassWithStatic.smali
@@ -0,0 +1,28 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LClassWithStatic;
+
+.super Ljava/lang/Object;
+
+.method static constructor <clinit>()V
+ .registers 1
+ const-string v0, "[LClassWithMissingInterface;"
+ invoke-static {v0}, Ljava/lang/Class;->forName(Ljava/lang/String;)Ljava/lang/Class;
+ move-result-object v0
+ sput-object v0, LClassWithStatic;->field:Ljava/lang/Class;
+ return-void
+.end method
+
+.field public static field:Ljava/lang/Class;
diff --git a/test/ArrayClassWithUnresolvedComponent/ClassWithStaticConst.smali b/test/ArrayClassWithUnresolvedComponent/ClassWithStaticConst.smali
new file mode 100644
index 0000000..68aecfb
--- /dev/null
+++ b/test/ArrayClassWithUnresolvedComponent/ClassWithStaticConst.smali
@@ -0,0 +1,26 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LClassWithStaticConst;
+
+.super Ljava/lang/Object;
+
+.method static constructor <clinit>()V
+ .registers 1
+ const-class v0, [LClassWithMissingInterface;
+ sput-object v0, LClassWithStaticConst;->field:Ljava/lang/Class;
+ return-void
+.end method
+
+.field public static field:Ljava/lang/Class;
diff --git a/test/Dex2oatVdexTestDex/Dex2oatVdexTestDex.java b/test/Dex2oatVdexTestDex/Dex2oatVdexTestDex.java
index e4862bd..ef6b4ef 100644
--- a/test/Dex2oatVdexTestDex/Dex2oatVdexTestDex.java
+++ b/test/Dex2oatVdexTestDex/Dex2oatVdexTestDex.java
@@ -30,6 +30,7 @@
}
}
+@SuppressWarnings("LockOnBoxedPrimitive")
class AccessPublicMethodFromParent {
public void foo(Integer i) {
i.notify();
@@ -64,6 +65,7 @@
}
}
+@SuppressWarnings("LockOnBoxedPrimitive")
class AccessNonPublicMethodFromParent {
public void foo(Integer i) {
i.notifyAll();
diff --git a/test/README.chroot.md b/test/README.chroot.md
index 49b76ba..b97e56c 100644
--- a/test/README.chroot.md
+++ b/test/README.chroot.md
@@ -50,19 +50,61 @@
```
2. Set lunch target and ADB:
* With a minimal `aosp/master-art` tree:
- ```bash
- export SOONG_ALLOW_MISSING_DEPENDENCIES=true
- . ./build/envsetup.sh
- lunch armv8-eng # or arm_krait-eng for 32-bit ARM
- export PATH="$(pwd)/prebuilts/runtime:$PATH"
- export ADB="$ANDROID_BUILD_TOP/prebuilts/runtime/adb"
- ```
+ 1. Initialize the environment:
+ ```bash
+ export SOONG_ALLOW_MISSING_DEPENDENCIES=true
+ . ./build/envsetup.sh
+ ```
+ 2. Select a lunch target corresponding to the architecture you want to
+ build and test:
+ * For (32-bit) Arm:
+ ```bash
+ lunch arm_krait-eng
+ ```
+ * For (64-bit only) Arm64:
+ ```bash
+ lunch armv8-eng
+ ```
+ * For (32- and 64-bit) Arm64:
+ ```bash
+ lunch arm_v7_v8-eng
+ ```
+ * For (32-bit) Intel x86:
+ ```bash
+ lunch silvermont-eng
+ ```
+ 3. Set up the environment to use a pre-built ADB:
+ ```bash
+ export PATH="$(pwd)/prebuilts/runtime:$PATH"
+ export ADB="$ANDROID_BUILD_TOP/prebuilts/runtime/adb"
+ ```
* With a full Android (AOSP) `aosp/master` tree:
- ```bash
- . ./build/envsetup.sh
- lunch aosp_arm64-eng # or aosp_arm-eng for 32-bit ARM
- m adb
- ```
+ 1. Initialize the environment:
+ ```bash
+ . ./build/envsetup.sh
+ ```
+ 2. Select a lunch target corresponding to the architecture you want to
+ build and test:
+ * For (32-bit) Arm:
+ ```bash
+ lunch aosp_arm-eng
+ ```
+ * For (32- and 64-bit) Arm64:
+ ```bash
+ lunch aosp_arm64-eng
+ ```
+ * For (32-bit) Intel x86:
+ ```bash
+ lunch aosp_x86-eng
+ ```
+ * For (32- and 64-bit) Intel x86-64:
+ ```bash
+ lunch aosp_x86_64-eng
+ ```
+ 3. Build ADB:
+ ```bash
+ m adb
+ ```
3. Build ART and required dependencies:
```bash
art/tools/buildbot-build.sh --target
diff --git a/test/README.md b/test/README.md
index da9a1f1..dc9e9b7 100644
--- a/test/README.md
+++ b/test/README.md
@@ -201,6 +201,17 @@
## Running one gtest on the build host
```sh
+$ m test-art-host-gtest-art_runtime_tests
+```
+
+Note: Although this is a build command, it actually builds the test with
+dependencies and runs the test.
+
+If you want to run the test with more options, use the following commands
+instead. Note that you need to run the test with the command above at least once
+before you run the commands below.
+
+```sh
$ find out/host/ -type f -name art_runtime_tests # Find the path of the test.
$ out/host/linux-x86/nativetest/art_runtime_tests/art_runtime_tests
```
diff --git a/test/SuperWithAccessChecks/ImplementsClass.smali b/test/SuperWithAccessChecks/ImplementsClass.smali
new file mode 100644
index 0000000..e39877f
--- /dev/null
+++ b/test/SuperWithAccessChecks/ImplementsClass.smali
@@ -0,0 +1,18 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LImplementsClass;
+
+.super Ljava/lang/Object;
+.implements LItf;
diff --git a/test/SuperWithAccessChecks/Itf.smali b/test/SuperWithAccessChecks/Itf.smali
new file mode 100644
index 0000000..c91686e
--- /dev/null
+++ b/test/SuperWithAccessChecks/Itf.smali
@@ -0,0 +1,23 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public abstract interface LItf;
+
+.super Ljava/lang/Object;
+
+.method public foo()V
+ .registers 1
+ invoke-static {}, LMissingClass;->forName()V
+ return-void
+.end method
diff --git a/test/SuperWithAccessChecks/SubClass.smali b/test/SuperWithAccessChecks/SubClass.smali
new file mode 100644
index 0000000..21c8f1c
--- /dev/null
+++ b/test/SuperWithAccessChecks/SubClass.smali
@@ -0,0 +1,17 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LSubClass;
+
+.super LSuperClass;
diff --git a/test/SuperWithAccessChecks/SuperClass.smali b/test/SuperWithAccessChecks/SuperClass.smali
new file mode 100644
index 0000000..8e12521
--- /dev/null
+++ b/test/SuperWithAccessChecks/SuperClass.smali
@@ -0,0 +1,24 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LSuperClass;
+
+.super Ljava/lang/Object;
+
+.method static foo()V
+ .registers 0
+ invoke-static {}, LMissingClass;->forName()V
+ return-void
+.end method
+
diff --git a/test/art-gtests-target-chroot.xml b/test/art-gtests-target-chroot.xml
index 5fd76f8..88c45b7 100644
--- a/test/art-gtests-target-chroot.xml
+++ b/test/art-gtests-target-chroot.xml
@@ -37,7 +37,7 @@
<test class="com.android.tradefed.testtype.ArtGTest" >
<!-- TODO(b/147821328): These tests do not work since they need to write to /system -->
- <option name="exclude-filter" value="HiddenApiTest.DexDomain_System*:OatFileAssistantTest.SystemFrameworkDir" />
+ <option name="exclude-filter" value="HiddenApiTest.DexDomain_System*:OatFileAssistantBaseTest.SystemFrameworkDir" />
<option name="native-test-timeout" value="600000" />
<option name="native-test-device-path" value="/data/local/tmp/art-test-chroot/apex/com.android.art/bin/art" />
</test>
diff --git a/test/art-gtests-target-install-apex.xml b/test/art-gtests-target-install-apex.xml
index 240b441..b030f26 100644
--- a/test/art-gtests-target-install-apex.xml
+++ b/test/art-gtests-target-install-apex.xml
@@ -23,7 +23,7 @@
<test class="com.android.tradefed.testtype.GTest" >
<!-- TODO(b/147821328): These tests do not work since they need to write to /system -->
- <option name="exclude-filter" value="HiddenApiTest.DexDomain_System*:OatFileAssistantTest.SystemFrameworkDir" />
+ <option name="exclude-filter" value="HiddenApiTest.DexDomain_System*:OatFileAssistantBaseTest.SystemFrameworkDir" />
<option name="native-test-timeout" value="600000" /> <!-- 10 min -->
<option name="native-test-device-path" value="/apex/com.android.art/bin/art" />
</test>
diff --git a/test/art-gtests-target-standalone-template.xml b/test/art-gtests-target-standalone-template.xml
index cc2f76e..194c1e3 100644
--- a/test/art-gtests-target-standalone-template.xml
+++ b/test/art-gtests-target-standalone-template.xml
@@ -15,6 +15,8 @@
-->
<!-- Note: This test config file for {MODULE} is generated from a template. -->
<configuration description="Runs {MODULE}.">
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.art.apex" />
+
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="{MODULE}->/data/local/tmp/{MODULE}/{MODULE}" />
diff --git a/test/buildfailures.json b/test/buildfailures.json
deleted file mode 100644
index c926298..0000000
--- a/test/buildfailures.json
+++ /dev/null
@@ -1,31 +0,0 @@
-[
- {
- "description": ["The following tests don't build on RI."],
- "tests": [
- "004-UnsafeTest",
- "030-bad-finalizer",
- "122-npe",
- "160-read-barrier-stress",
- "178-app-image-native-method",
- "612-jit-dex-cache",
- "613-inlining-dex-cache",
- "674-hiddenapi",
- "676-resolve-field-type",
- "689-zygote-jit-deopt",
- "692-vdex-inmem-loader",
- "692-vdex-secondary-loader",
- "693-vdex-inmem-loader-evict",
- "811-checker-invoke-super-secondary",
- "817-hiddenapi",
- "831-unverified-bcp",
- "1336-short-finalizer-timeout",
- "1339-dead-reference-safe",
- "2005-pause-all-redefine-multithreaded",
- "2040-huge-native-alloc",
- "2041-bad-cleaner",
- "2235-JdkUnsafeTest",
- "2239-varhandle-perf"
- ],
- "variant": "jvm"
- }
-]
diff --git a/test/common/gtest_main.cc b/test/common/gtest_main.cc
index 95dadcf..e491ffc 100644
--- a/test/common/gtest_main.cc
+++ b/test/common/gtest_main.cc
@@ -38,12 +38,6 @@
extern "C" __attribute__((visibility("default"))) __attribute__((weak)) void ArtTestGlobalInit();
int main(int argc, char** argv, char** envp) {
- // Gtests can be very noisy. For example, an executable with multiple tests will trigger native
- // bridge warnings. The following line reduces the minimum log severity to ERROR and suppresses
- // everything else. In case you want to see all messages, comment out the line.
- const char* log_tag_override = getenv("ART_GTEST_OVERRIDE_LOG_TAGS");
- setenv("ANDROID_LOG_TAGS", log_tag_override == nullptr ? "*:e" : log_tag_override, 1);
-
art::Locks::Init();
art::InitLogging(argv, art::Runtime::Abort);
art::MemMap::Init();
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index 0b72612..46f3828 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -18,6 +18,7 @@
#include <android-base/logging.h>
#include <android-base/macros.h>
+#include <sys/resource.h>
#include "art_field.h"
#include "art_method-inl.h"
@@ -234,7 +235,7 @@
{
ScopedObjectAccess soa(self);
- if (Runtime::Current()->GetRuntimeCallbacks()->IsMethodBeingInspected(method)) {
+ if (Runtime::Current()->GetInstrumentation()->IsDeoptimized(method)) {
std::string msg(method->PrettyMethod());
msg += ": is not safe to jit!";
ThrowIllegalStateException(msg.c_str());
@@ -276,7 +277,20 @@
// this before checking if we will execute JIT code in case the request
// is for an 'optimized' compilation.
jit->CompileMethod(method, self, kind, /*prejit=*/ false);
- } while (!code_cache->ContainsPc(method->GetEntryPointFromQuickCompiledCode()));
+ const void* entry_point = method->GetEntryPointFromQuickCompiledCode();
+ if (code_cache->ContainsPc(entry_point)) {
+ // If we're running baseline or not requesting optimized, we're good to go.
+ if (jit->GetJitCompiler()->IsBaselineCompiler() || kind != CompilationKind::kOptimized) {
+ break;
+ }
+ // If we're requesting optimized, check that we did get the method
+ // compiled optimized.
+ OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromEntryPoint(entry_point);
+ if (!CodeInfo::IsBaseline(method_header->GetOptimizedCodeInfoPtr())) {
+ break;
+ }
+ }
+ } while (true);
}
extern "C" JNIEXPORT void JNICALL Java_Main_ensureMethodJitCompiled(JNIEnv*, jclass, jobject meth) {
@@ -438,4 +452,21 @@
return soa.Decode<mirror::Class>(c)->IsObsoleteObject();
}
+extern "C" JNIEXPORT void JNICALL Java_Main_forceInterpreterOnThread(JNIEnv* env,
+ jclass cls ATTRIBUTE_UNUSED) {
+ ScopedObjectAccess soa(env);
+ MutexLock thread_list_mu(soa.Self(), *Locks::thread_list_lock_);
+ soa.Self()->IncrementForceInterpreterCount();
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_setAsyncExceptionsThrown(JNIEnv* env ATTRIBUTE_UNUSED,
+ jclass cls ATTRIBUTE_UNUSED) {
+ Runtime::Current()->SetAsyncExceptionsThrown();
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_setRlimitNoFile(JNIEnv*, jclass, jint value) {
+ rlimit limit { static_cast<rlim_t>(value), static_cast<rlim_t>(value) };
+ setrlimit(RLIMIT_NOFILE, &limit);
+}
+
} // namespace art
diff --git a/test/common/stack_inspect.cc b/test/common/stack_inspect.cc
index 79c7a36..33f31e2 100644
--- a/test/common/stack_inspect.cc
+++ b/test/common/stack_inspect.cc
@@ -195,7 +195,8 @@
if (stack_visitor->GetMethod() == nullptr ||
stack_visitor->GetMethod()->IsNative() ||
(stack_visitor->GetCurrentShadowFrame() == nullptr &&
- !Runtime::Current()->IsAsyncDeoptimizeable(stack_visitor->GetCurrentQuickFramePc()))) {
+ !Runtime::Current()->IsAsyncDeoptimizeable(stack_visitor->GetOuterMethod(),
+ stack_visitor->GetCurrentQuickFramePc()))) {
return true;
}
result = soa.AddLocalReference<jobject>(stack_visitor->GetThisObject());
diff --git a/test/csuite-app-compile-launch.xml b/test/csuite-app-compile-launch.xml
index 890c242..4ff7098 100644
--- a/test/csuite-app-compile-launch.xml
+++ b/test/csuite-app-compile-launch.xml
@@ -20,6 +20,9 @@
</target_preparer>
<target_preparer class="com.android.compatibility.targetprep.CheckGmsPreparer"/>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <!-- We will fail if any command in RunCommandTargetPreparer fails.
+ This is useful to know if we failed to compile a package. -->
+ <option name="throw-if-cmd-fail" value="true"/>
<option name="run-command" value="cmd package compile -m speed {package}"/>
<option name="run-command" value="input keyevent KEYCODE_WAKEUP"/>
<option name="run-command" value="input keyevent KEYCODE_MENU"/>
diff --git a/test/default_run.py b/test/default_run.py
new file mode 100755
index 0000000..f457956
--- /dev/null
+++ b/test/default_run.py
@@ -0,0 +1,1183 @@
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import sys, os, shutil, shlex, re, subprocess, glob
+from argparse import ArgumentParser, BooleanOptionalAction, Namespace
+from os import path
+from os.path import isfile, isdir, basename
+from subprocess import check_output, DEVNULL, PIPE, STDOUT
+from tempfile import NamedTemporaryFile
+from testrunner import env
+from typing import List
+
+COLOR = (os.environ.get("LUCI_CONTEXT") == None) # Disable colors on LUCI.
+COLOR_BLUE = '\033[94m' if COLOR else ''
+COLOR_GREEN = '\033[92m' if COLOR else ''
+COLOR_NORMAL = '\033[0m' if COLOR else ''
+COLOR_RED = '\033[91m' if COLOR else ''
+
+def parse_args(argv):
+ argp, opt_bool = ArgumentParser(), BooleanOptionalAction
+ argp.add_argument("--64", dest="is64", action="store_true")
+ argp.add_argument("--O", action="store_true")
+ argp.add_argument("--Xcompiler-option", default=[], action="append")
+ argp.add_argument("--add-libdir-argument", action="store_true")
+ argp.add_argument("--android-art-root", default="/apex/com.android.art")
+ argp.add_argument("--android-i18n-root", default="/apex/com.android.i18n")
+ argp.add_argument("--android-log-tags", default="*:i")
+ argp.add_argument("--android-root", default="/system")
+ argp.add_argument("--android-runtime-option", default=[], action="append")
+ argp.add_argument("--android-tzdata-root", default="/apex/com.android.tzdata")
+ argp.add_argument("--app-image", default=True, action=opt_bool)
+ argp.add_argument("--baseline", action="store_true")
+ argp.add_argument("--bionic", action="store_true")
+ argp.add_argument("--boot", default="")
+ argp.add_argument("--chroot", default="")
+ argp.add_argument("--compact-dex-level")
+ argp.add_argument("--compiler-only-option", default=[], action="append")
+ argp.add_argument("--create-runner", action="store_true")
+ argp.add_argument("--diff-min-log-tag", default="E")
+ argp.add_argument("--debug", action="store_true")
+ argp.add_argument("--debug-agent")
+ argp.add_argument("--debug-wrap-agent", action="store_true")
+ argp.add_argument("--dex2oat-dm", action="store_true")
+ argp.add_argument(
+ "--dex2oat-rt-timeout", type=int,
+ default=360) # The *hard* timeout. 6 min.
+ argp.add_argument(
+ "--dex2oat-timeout", type=int, default=300) # The "soft" timeout. 5 min.
+ argp.add_argument("--dry-run", action="store_true")
+ argp.add_argument("--experimental", default=[], action="append")
+ argp.add_argument("--external-log-tags", action="store_true")
+ argp.add_argument("--gc-stress", action="store_true")
+ argp.add_argument("--gdb", action="store_true")
+ argp.add_argument("--gdb-arg", default=[], action="append")
+ argp.add_argument("--gdb-dex2oat", action="store_true")
+ argp.add_argument("--gdb-dex2oat-args", default=[], action="append")
+ argp.add_argument("--gdbserver", action="store_true")
+ argp.add_argument("--gdbserver-bin")
+ argp.add_argument("--gdbserver-port", default=":5039")
+ argp.add_argument("--host", action="store_true")
+ argp.add_argument("--image", default=True, action=opt_bool)
+ argp.add_argument("--instruction-set-features", default="")
+ argp.add_argument("--interpreter", action="store_true")
+ argp.add_argument("--invoke-with", default=[], action="append")
+ argp.add_argument("--jit", action="store_true")
+ argp.add_argument("--jvm", action="store_true")
+ argp.add_argument("--jvmti", action="store_true")
+ argp.add_argument("--jvmti-field-stress", action="store_true")
+ argp.add_argument("--jvmti-redefine-stress", action="store_true")
+ argp.add_argument("--jvmti-step-stress", action="store_true")
+ argp.add_argument("--jvmti-trace-stress", action="store_true")
+ argp.add_argument("--lib", default="")
+ argp.add_argument("--optimize", default=True, action=opt_bool)
+ argp.add_argument("--prebuild", default=True, action=opt_bool)
+ argp.add_argument("--profile", action="store_true")
+ argp.add_argument("--random-profile", action="store_true")
+ argp.add_argument("--relocate", default=False, action=opt_bool)
+ argp.add_argument("--runtime-dm", action="store_true")
+ argp.add_argument("--runtime-extracted-zipapex", default="")
+ argp.add_argument("--runtime-option", default=[], action="append")
+ argp.add_argument("--runtime-zipapex", default="")
+ argp.add_argument("--secondary", action="store_true")
+ argp.add_argument("--secondary-app-image", default=True, action=opt_bool)
+ argp.add_argument("--secondary-class-loader-context", default="")
+ argp.add_argument("--secondary-compilation", default=True, action=opt_bool)
+ argp.add_argument("--simpleperf", action="store_true")
+ argp.add_argument("--sync", action="store_true")
+ argp.add_argument("--testlib", default=[], action="append")
+ argp.add_argument("--timeout", default=0, type=int)
+ argp.add_argument("--vdex", action="store_true")
+ argp.add_argument("--vdex-arg", default=[], action="append")
+ argp.add_argument("--vdex-filter", default="")
+ argp.add_argument("--verify", default=True, action=opt_bool)
+ argp.add_argument("--verify-soft-fail", action="store_true")
+ argp.add_argument("--with-agent", default=[], action="append")
+ argp.add_argument("--zygote", action="store_true")
+ argp.add_argument("--test_args", default=[], action="append")
+ argp.add_argument("--stdout_file", default="")
+ argp.add_argument("--stderr_file", default="")
+ argp.add_argument("--main", default="Main")
+ argp.add_argument("--expected_exit_code", default=0)
+
+ # Python parser requires the format --key=--value, since without the equals symbol
+ # it looks like the required value has been omitted and there is just another flag.
+ # For example, '--args --foo --host --64' will become '--arg=--foo --host --64'
+ # because otherwise the --args is missing its value and --foo is unknown argument.
+ for i, arg in reversed(list(enumerate(argv))):
+ if arg in [
+ "--args", "--runtime-option", "--android-runtime-option",
+ "-Xcompiler-option", "--compiler-only-option"
+ ]:
+ argv[i] += "=" + argv.pop(i + 1)
+
+ # Accept single-dash arguments as if they were double-dash arguments.
+ # For exmpample, '-Xcompiler-option' becomes '--Xcompiler-option'
+ # became single-dash can be used only with single-letter arguments.
+ for i, arg in list(enumerate(argv)):
+ if arg.startswith("-") and not arg.startswith("--"):
+ argv[i] = "-" + arg
+ if arg == "--":
+ break
+
+ return argp.parse_args(argv)
+
+def get_target_arch(is64: bool) -> str:
+ # We may build for two arches. Get the one with the expected bitness.
+ arches = [a for a in [env.TARGET_ARCH, env.TARGET_2ND_ARCH] if a]
+ assert len(arches) > 0, "TARGET_ARCH/TARGET_2ND_ARCH not set"
+ if is64:
+ arches = [a for a in arches if a.endswith("64")]
+ assert len(arches) == 1, f"Can not find (unique) 64-bit arch in {arches}"
+ else:
+ arches = [a for a in arches if not a.endswith("64")]
+ assert len(arches) == 1, f"Can not find (unique) 32-bit arch in {arches}"
+ return arches[0]
+
+# Note: This must start with the CORE_IMG_JARS in Android.common_path.mk
+# because that's what we use for compiling the boot.art image.
+# It may contain additional modules from TEST_CORE_JARS.
+bpath_modules = ("core-oj core-libart okhttp bouncycastle apache-xml core-icu4j"
+ " conscrypt")
+
+
+# Helper function to construct paths for apex modules (for both -Xbootclasspath and
+# -Xbootclasspath-location).
+def get_apex_bootclasspath_impl(bpath_prefix: str):
+ bpath_separator = ""
+ bpath = ""
+ bpath_jar = ""
+ for bpath_module in bpath_modules.split(" "):
+ apex_module = "com.android.art"
+ if bpath_module == "conscrypt":
+ apex_module = "com.android.conscrypt"
+ if bpath_module == "core-icu4j":
+ apex_module = "com.android.i18n"
+ bpath_jar = f"/apex/{apex_module}/javalib/{bpath_module}.jar"
+ bpath += f"{bpath_separator}{bpath_prefix}{bpath_jar}"
+ bpath_separator = ":"
+ return bpath
+
+
+# Gets a -Xbootclasspath paths with the apex modules.
+def get_apex_bootclasspath(host: bool):
+ bpath_prefix = ""
+
+ if host:
+ bpath_prefix = os.environ["ANDROID_HOST_OUT"]
+
+ return get_apex_bootclasspath_impl(bpath_prefix)
+
+
+# Gets a -Xbootclasspath-location paths with the apex modules.
+def get_apex_bootclasspath_locations(host: bool):
+ bpath_location_prefix = ""
+
+ if host:
+ ANDROID_BUILD_TOP=os.environ["ANDROID_BUILD_TOP"]
+ ANDROID_HOST_OUT=os.environ["ANDROID_HOST_OUT"]
+ if ANDROID_HOST_OUT[0:len(ANDROID_BUILD_TOP)+1] == f"{ANDROID_BUILD_TOP}/":
+ bpath_location_prefix=ANDROID_HOST_OUT[len(ANDROID_BUILD_TOP)+1:]
+ else:
+ print(f"ANDROID_BUILD_TOP/ is not a prefix of ANDROID_HOST_OUT"\
+ "\nANDROID_BUILD_TOP={ANDROID_BUILD_TOP}"\
+ "\nANDROID_HOST_OUT={ANDROID_HOST_OUT}")
+ sys.exit(1)
+
+ return get_apex_bootclasspath_impl(bpath_location_prefix)
+
+
+def default_run(ctx, args, **kwargs):
+ # Clone the args so we can modify them without affecting args in the caller.
+ args = Namespace(**vars(args))
+
+ # Overwrite args based on the named parameters.
+ # E.g. the caller can do `default_run(args, jvmti=True)` to modify args.jvmti.
+ for name, new_value in kwargs.items():
+ old_value = getattr(args, name)
+ assert isinstance(new_value, old_value.__class__), name + " should have type " + str(old_value.__class__)
+ if isinstance(old_value, list):
+ setattr(args, name, old_value + new_value) # Lists get merged.
+ else:
+ setattr(args, name, new_value)
+
+ # Store copy of stdout&stderr of command in files so that we can diff them later.
+ # This may run under 'adb shell' so we are limited only to 'sh' shell feature set.
+ def tee(cmd: str):
+ # 'tee' works on stdout only, so we need to temporarily swap stdout and stderr.
+ cmd = f"({cmd} | tee -a {DEX_LOCATION}/{basename(args.stdout_file)}) 3>&1 1>&2 2>&3"
+ cmd = f"({cmd} | tee -a {DEX_LOCATION}/{basename(args.stderr_file)}) 3>&1 1>&2 2>&3"
+ return f"set -o pipefail; {cmd}" # Use exit code of first failure in piped command.
+
+ local_path = os.path.dirname(__file__)
+
+ ANDROID_BUILD_TOP = os.environ.get("ANDROID_BUILD_TOP")
+ ANDROID_DATA = os.environ.get("ANDROID_DATA")
+ ANDROID_HOST_OUT = os.environ["ANDROID_HOST_OUT"]
+ ANDROID_LOG_TAGS = os.environ.get("ANDROID_LOG_TAGS", "")
+ ART_TIME_OUT_MULTIPLIER = int(os.environ.get("ART_TIME_OUT_MULTIPLIER", 1))
+ DEX2OAT = os.environ.get("DEX2OAT", "")
+ DEX_LOCATION = os.environ["DEX_LOCATION"]
+ JAVA = os.environ.get("JAVA")
+ OUT_DIR = os.environ.get("OUT_DIR")
+ PATH = os.environ.get("PATH", "")
+ SANITIZE_HOST = os.environ.get("SANITIZE_HOST", "")
+ TEST_NAME = os.environ["TEST_NAME"]
+ USE_EXRACTED_ZIPAPEX = os.environ.get("USE_EXRACTED_ZIPAPEX", "")
+
+ assert ANDROID_BUILD_TOP, "Did you forget to run `lunch`?"
+
+ ANDROID_ROOT = args.android_root
+ ANDROID_ART_ROOT = args.android_art_root
+ ANDROID_I18N_ROOT = args.android_i18n_root
+ ANDROID_TZDATA_ROOT = args.android_tzdata_root
+ ARCHITECTURES_32 = "(arm|x86|none)"
+ ARCHITECTURES_64 = "(arm64|x86_64|none)"
+ ARCHITECTURES_PATTERN = ARCHITECTURES_32
+ GET_DEVICE_ISA_BITNESS_FLAG = "--32"
+ BOOT_IMAGE = args.boot
+ CHROOT = args.chroot
+ COMPILE_FLAGS = ""
+ DALVIKVM = "dalvikvm32"
+ DEBUGGER = "n"
+ WITH_AGENT = args.with_agent
+ DEBUGGER_AGENT = args.debug_agent
+ WRAP_DEBUGGER_AGENT = args.debug_wrap_agent
+ DEX2OAT_NDEBUG_BINARY = "dex2oat32"
+ DEX2OAT_DEBUG_BINARY = "dex2oatd32"
+ EXPERIMENTAL = args.experimental
+ FALSE_BIN = "false"
+ FLAGS = ""
+ ANDROID_FLAGS = ""
+ GDB = ""
+ GDB_ARGS = ""
+ GDB_DEX2OAT = ""
+ GDB_DEX2OAT_ARGS = ""
+ GDBSERVER_DEVICE = "gdbserver"
+ GDBSERVER_HOST = "gdbserver"
+ HAVE_IMAGE = args.image
+ HOST = args.host
+ BIONIC = args.bionic
+ CREATE_ANDROID_ROOT = False
+ USE_ZIPAPEX = (args.runtime_zipapex != "")
+ ZIPAPEX_LOC = args.runtime_zipapex
+ USE_EXTRACTED_ZIPAPEX = (args.runtime_extracted_zipapex != "")
+ EXTRACTED_ZIPAPEX_LOC = args.runtime_extracted_zipapex
+ INTERPRETER = args.interpreter
+ JIT = args.jit
+ INVOKE_WITH = " ".join(args.invoke_with)
+ USE_JVMTI = args.jvmti
+ IS_JVMTI_TEST = False
+ ADD_LIBDIR_ARGUMENTS = args.add_libdir_argument
+ SUFFIX64 = ""
+ ISA = "x86"
+ LIBRARY_DIRECTORY = "lib"
+ TEST_DIRECTORY = "nativetest"
+ MAIN = args.main
+ OPTIMIZE = args.optimize
+ PREBUILD = args.prebuild
+ RELOCATE = args.relocate
+ SECONDARY_DEX = ""
+ TIME_OUT = "timeout" # "n" (disabled), "timeout" (use timeout), "gdb" (use gdb)
+ TIMEOUT_DUMPER = "signal_dumper"
+ # Values in seconds.
+ TIME_OUT_EXTRA = 0
+ TIME_OUT_VALUE = args.timeout
+ USE_GDB = args.gdb
+ USE_GDBSERVER = args.gdbserver
+ GDBSERVER_PORT = args.gdbserver_port
+ USE_GDB_DEX2OAT = args.gdb_dex2oat
+ USE_JVM = args.jvm
+ VERIFY = "y" if args.verify else "n" # y=yes,n=no,s=softfail
+ ZYGOTE = ""
+ DEX_VERIFY = ""
+ INSTRUCTION_SET_FEATURES = args.instruction_set_features
+ ARGS = ""
+ VDEX_ARGS = ""
+ DRY_RUN = args.dry_run
+ TEST_VDEX = args.vdex
+ TEST_DEX2OAT_DM = args.dex2oat_dm
+ TEST_RUNTIME_DM = args.runtime_dm
+ TEST_IS_NDEBUG = args.O
+ APP_IMAGE = args.app_image
+ SECONDARY_APP_IMAGE = args.secondary_app_image
+ SECONDARY_CLASS_LOADER_CONTEXT = args.secondary_class_loader_context
+ SECONDARY_COMPILATION = args.secondary_compilation
+ JVMTI_STRESS = False
+ JVMTI_REDEFINE_STRESS = args.jvmti_redefine_stress
+ JVMTI_STEP_STRESS = args.jvmti_step_stress
+ JVMTI_FIELD_STRESS = args.jvmti_field_stress
+ JVMTI_TRACE_STRESS = args.jvmti_trace_stress
+ PROFILE = args.profile
+ RANDOM_PROFILE = args.random_profile
+ DEX2OAT_TIMEOUT = args.dex2oat_timeout
+ DEX2OAT_RT_TIMEOUT = args.dex2oat_rt_timeout
+ CREATE_RUNNER = args.create_runner
+ INT_OPTS = ""
+ SIMPLEPERF = args.simpleperf
+ DEBUGGER_OPTS = ""
+ JVM_VERIFY_ARG = ""
+ LIB = args.lib
+
+ # if True, run 'sync' before dalvikvm to make sure all files from
+ # build step (e.g. dex2oat) were finished writing.
+ SYNC_BEFORE_RUN = args.sync
+
+ # When running a debug build, we want to run with all checks.
+ ANDROID_FLAGS += " -XX:SlowDebug=true"
+ # The same for dex2oatd, both prebuild and runtime-driven.
+ ANDROID_FLAGS += (" -Xcompiler-option --runtime-arg -Xcompiler-option "
+ "-XX:SlowDebug=true")
+ COMPILER_FLAGS = " --runtime-arg -XX:SlowDebug=true"
+
+ # Let the compiler and runtime know that we are running tests.
+ COMPILE_FLAGS += " --compile-art-test"
+ ANDROID_FLAGS += " -Xcompiler-option --compile-art-test"
+
+ if USE_JVMTI:
+ IS_JVMTI_TEST = True
+ # Secondary images block some tested behavior.
+ SECONDARY_APP_IMAGE = False
+ if args.gc_stress:
+ # Give an extra 20 mins if we are gc-stress.
+ TIME_OUT_EXTRA += 1200
+ for arg in args.testlib:
+ ARGS += f" {arg}"
+ for arg in args.test_args:
+ ARGS += f" {arg}"
+ for arg in args.compiler_only_option:
+ COMPILE_FLAGS += f" {arg}"
+ for arg in args.Xcompiler_option:
+ FLAGS += f" -Xcompiler-option {arg}"
+ COMPILE_FLAGS += f" {arg}"
+ if args.secondary:
+ SECONDARY_DEX = f":{DEX_LOCATION}/{TEST_NAME}-ex.jar"
+ # Enable cfg-append to make sure we get the dump for both dex files.
+ # (otherwise the runtime compilation of the secondary dex will overwrite
+ # the dump of the first one).
+ FLAGS += " -Xcompiler-option --dump-cfg-append"
+ COMPILE_FLAGS += " --dump-cfg-append"
+ for arg in args.android_runtime_option:
+ ANDROID_FLAGS += f" {arg}"
+ for arg in args.runtime_option:
+ FLAGS += f" {arg}"
+ if arg == "-Xmethod-trace":
+ # Method tracing can slow some tests down a lot.
+ TIME_OUT_EXTRA += 1200
+ if args.compact_dex_level:
+ arg = args.compact_dex_level
+ COMPILE_FLAGS += f" --compact-dex-level={arg}"
+ if JVMTI_REDEFINE_STRESS:
+ # APP_IMAGE doesn't really work with jvmti redefine stress
+ SECONDARY_APP_IMAGE = False
+ JVMTI_STRESS = True
+ if JVMTI_REDEFINE_STRESS or JVMTI_STEP_STRESS or JVMTI_FIELD_STRESS or JVMTI_TRACE_STRESS:
+ USE_JVMTI = True
+ JVMTI_STRESS = True
+ if HOST:
+ ANDROID_ROOT = ANDROID_HOST_OUT
+ ANDROID_ART_ROOT = f"{ANDROID_HOST_OUT}/com.android.art"
+ ANDROID_I18N_ROOT = f"{ANDROID_HOST_OUT}/com.android.i18n"
+ ANDROID_TZDATA_ROOT = f"{ANDROID_HOST_OUT}/com.android.tzdata"
+ # On host, we default to using the symlink, as the PREFER_32BIT
+ # configuration is the only configuration building a 32bit version of
+ # dex2oat.
+ DEX2OAT_DEBUG_BINARY = "dex2oatd"
+ DEX2OAT_NDEBUG_BINARY = "dex2oat"
+ if BIONIC:
+ # We need to create an ANDROID_ROOT because currently we cannot create
+ # the frameworks/libcore with linux_bionic so we need to use the normal
+ # host ones which are in a different location.
+ CREATE_ANDROID_ROOT = True
+ if USE_ZIPAPEX:
+ # TODO (b/119942078): Currently apex does not support
+ # symlink_preferred_arch so we will not have a dex2oatd to execute and
+ # need to manually provide
+ # dex2oatd64.
+ DEX2OAT_DEBUG_BINARY = "dex2oatd64"
+ if WITH_AGENT:
+ USE_JVMTI = True
+ if DEBUGGER_AGENT:
+ DEBUGGER = "agent"
+ USE_JVMTI = True
+ TIME_OUT = "n"
+ if args.debug:
+ USE_JVMTI = True
+ DEBUGGER = "y"
+ TIME_OUT = "n"
+ if args.gdbserver_bin:
+ arg = args.gdbserver_bin
+ GDBSERVER_HOST = arg
+ GDBSERVER_DEVICE = arg
+ if args.gdbserver or args.gdb or USE_GDB_DEX2OAT:
+ TIME_OUT = "n"
+ for arg in args.gdb_arg:
+ GDB_ARGS += f" {arg}"
+ if args.gdb_dex2oat_args:
+ for arg in arg.split(";"):
+ GDB_DEX2OAT_ARGS += f"{arg} "
+ if args.zygote:
+ ZYGOTE = "-Xzygote"
+ print("Spawning from zygote")
+ if args.baseline:
+ FLAGS += " -Xcompiler-option --baseline"
+ COMPILE_FLAGS += " --baseline"
+ if args.verify_soft_fail:
+ VERIFY = "s"
+ if args.is64:
+ SUFFIX64 = "64"
+ ISA = "x86_64"
+ GDBSERVER_DEVICE = "gdbserver64"
+ DALVIKVM = "dalvikvm64"
+ LIBRARY_DIRECTORY = "lib64"
+ TEST_DIRECTORY = "nativetest64"
+ ARCHITECTURES_PATTERN = ARCHITECTURES_64
+ GET_DEVICE_ISA_BITNESS_FLAG = "--64"
+ DEX2OAT_NDEBUG_BINARY = "dex2oat64"
+ DEX2OAT_DEBUG_BINARY = "dex2oatd64"
+ if args.vdex_filter:
+ option = args.vdex_filter
+ VDEX_ARGS += f" --compiler-filter={option}"
+ if args.vdex_arg:
+ arg = args.vdex_arg
+ VDEX_ARGS += f" {arg}"
+
+# HACK: Force the use of `signal_dumper` on host.
+ if HOST:
+ TIME_OUT = "timeout"
+
+# If you change this, update the timeout in testrunner.py as well.
+ if not TIME_OUT_VALUE:
+ # 10 minutes is the default.
+ TIME_OUT_VALUE = 600
+
+ # For sanitized builds use a larger base.
+ # TODO: Consider sanitized target builds?
+ if SANITIZE_HOST != "":
+ TIME_OUT_VALUE = 1500 # 25 minutes.
+
+ TIME_OUT_VALUE += TIME_OUT_EXTRA
+
+# Escape hatch for slow hosts or devices. Accept an environment variable as a timeout factor.
+ if ART_TIME_OUT_MULTIPLIER:
+ TIME_OUT_VALUE *= ART_TIME_OUT_MULTIPLIER
+
+# The DEX_LOCATION with the chroot prefix, if any.
+ CHROOT_DEX_LOCATION = f"{CHROOT}{DEX_LOCATION}"
+
+ # If running on device, determine the ISA of the device.
+ if not HOST and not USE_JVM:
+ ISA = get_target_arch(args.is64)
+
+ if not USE_JVM:
+ FLAGS += f" {ANDROID_FLAGS}"
+ # we don't want to be trying to get adbconnections since the plugin might
+ # not have been built.
+ FLAGS += " -XjdwpProvider:none"
+ for feature in EXPERIMENTAL:
+ FLAGS += f" -Xexperimental:{feature} -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:{feature}"
+ COMPILE_FLAGS = f"{COMPILE_FLAGS} --runtime-arg -Xexperimental:{feature}"
+
+ if CREATE_ANDROID_ROOT:
+ ANDROID_ROOT = f"{DEX_LOCATION}/android-root"
+
+ if ZYGOTE == "":
+ if OPTIMIZE:
+ if VERIFY == "y":
+ DEX_OPTIMIZE = "-Xdexopt:verified"
+ else:
+ DEX_OPTIMIZE = "-Xdexopt:all"
+ else:
+ DEX_OPTIMIZE = "-Xdexopt:none"
+
+ if VERIFY == "y":
+ JVM_VERIFY_ARG = "-Xverify:all"
+ elif VERIFY == "s":
+ JVM_VERIFY_ARG = "Xverify:all"
+ DEX_VERIFY = "-Xverify:softfail"
+ else: # VERIFY == "n"
+ DEX_VERIFY = "-Xverify:none"
+ JVM_VERIFY_ARG = "-Xverify:none"
+
+ if DEBUGGER == "y":
+ # Use this instead for ddms and connect by running 'ddms':
+ # DEBUGGER_OPTS="-XjdwpOptions=server=y,suspend=y -XjdwpProvider:adbconnection"
+ # TODO: add a separate --ddms option?
+
+ PORT = 12345
+ print("Waiting for jdb to connect:")
+ if not HOST:
+ print(f" adb forward tcp:{PORT} tcp:{PORT}")
+ print(f" jdb -attach localhost:{PORT}")
+ if not USE_JVM:
+ # Use the default libjdwp agent. Use --debug-agent to use a custom one.
+ DEBUGGER_OPTS = f"-agentpath:libjdwp.so=transport=dt_socket,address={PORT},server=y,suspend=y -XjdwpProvider:internal"
+ else:
+ DEBUGGER_OPTS = f"-agentlib:jdwp=transport=dt_socket,address={PORT},server=y,suspend=y"
+ elif DEBUGGER == "agent":
+ PORT = 12345
+ # TODO Support ddms connection and support target.
+ assert HOST, "--debug-agent not supported yet for target!"
+ AGENTPATH = DEBUGGER_AGENT
+ if WRAP_DEBUGGER_AGENT:
+ WRAPPROPS = f"{ANDROID_ROOT}/{LIBRARY_DIRECTORY}/libwrapagentpropertiesd.so"
+ if TEST_IS_NDEBUG:
+ WRAPPROPS = f"{ANDROID_ROOT}/{LIBRARY_DIRECTORY}/libwrapagentproperties.so"
+ AGENTPATH = f"{WRAPPROPS}={ANDROID_BUILD_TOP}/art/tools/libjdwp-compat.props,{AGENTPATH}"
+ print(f"Connect to localhost:{PORT}")
+ DEBUGGER_OPTS = f"-agentpath:{AGENTPATH}=transport=dt_socket,address={PORT},server=y,suspend=y"
+
+ for agent in WITH_AGENT:
+ FLAGS += f" -agentpath:{agent}"
+
+ if USE_JVMTI:
+ if not USE_JVM:
+ plugin = "libopenjdkjvmtid.so"
+ if TEST_IS_NDEBUG:
+ plugin = "libopenjdkjvmti.so"
+ # We used to add flags here that made the runtime debuggable but that is not
+ # needed anymore since the plugin can do it for us now.
+ FLAGS += f" -Xplugin:{plugin}"
+
+ # For jvmti tests, set the threshold of compilation to 1, so we jit early to
+ # provide better test coverage for jvmti + jit. This means we won't run
+ # the default --jit configuration but it is not too important test scenario for
+ # jvmti tests. This is art specific flag, so don't use it with jvm.
+ FLAGS += " -Xjitthreshold:1"
+
+# Add the libdir to the argv passed to the main function.
+ if ADD_LIBDIR_ARGUMENTS:
+ if HOST:
+ ARGS += f" {ANDROID_HOST_OUT}/{TEST_DIRECTORY}/"
+ else:
+ ARGS += f" /data/{TEST_DIRECTORY}/art/{ISA}/"
+ if IS_JVMTI_TEST:
+ agent = "libtiagentd.so"
+ lib = "tiagentd"
+ if TEST_IS_NDEBUG:
+ agent = "libtiagent.so"
+ lib = "tiagent"
+
+ ARGS += f" {lib}"
+ if USE_JVM:
+ FLAGS += f" -agentpath:{ANDROID_HOST_OUT}/nativetest64/{agent}={TEST_NAME},jvm"
+ else:
+ FLAGS += f" -agentpath:{agent}={TEST_NAME},art"
+
+ if JVMTI_STRESS:
+ agent = "libtistressd.so"
+ if TEST_IS_NDEBUG:
+ agent = "libtistress.so"
+
+ # Just give it a default start so we can always add ',' to it.
+ agent_args = "jvmti-stress"
+ if JVMTI_REDEFINE_STRESS:
+ # We really cannot do this on RI so don't both passing it in that case.
+ if not USE_JVM:
+ agent_args = f"{agent_args},redefine"
+ if JVMTI_FIELD_STRESS:
+ agent_args = f"{agent_args},field"
+ if JVMTI_STEP_STRESS:
+ agent_args = f"{agent_args},step"
+ if JVMTI_TRACE_STRESS:
+ agent_args = f"{agent_args},trace"
+ # In the future add onto this;
+ if USE_JVM:
+ FLAGS += f" -agentpath:{ANDROID_HOST_OUT}/nativetest64/{agent}={agent_args}"
+ else:
+ FLAGS += f" -agentpath:{agent}={agent_args}"
+
+ if USE_JVM:
+ ctx.export(
+ ANDROID_I18N_ROOT = ANDROID_I18N_ROOT,
+ DEX_LOCATION = DEX_LOCATION,
+ JAVA_HOME = os.environ["JAVA_HOME"],
+ LANG = "en_US.UTF-8", # Needed to enable unicode and make the output is deterministic.
+ LD_LIBRARY_PATH = f"{ANDROID_HOST_OUT}/lib64",
+ )
+ # Some jvmti tests are flaky without -Xint on the RI.
+ if IS_JVMTI_TEST:
+ FLAGS += " -Xint"
+ # Xmx is necessary since we don't pass down the ART flags to JVM.
+ # We pass the classes2 path whether it's used (src-multidex) or not.
+ cmdline = f"{JAVA} {DEBUGGER_OPTS} {JVM_VERIFY_ARG} -Xmx256m -classpath classes:classes2 {FLAGS} {MAIN} {ARGS}"
+ ctx.run(tee(cmdline), expected_exit_code=args.expected_exit_code)
+ return
+
+ b_path = get_apex_bootclasspath(HOST)
+ b_path_locations = get_apex_bootclasspath_locations(HOST)
+
+ BCPEX = ""
+ if isfile(f"{TEST_NAME}-bcpex.jar"):
+ BCPEX = f":{DEX_LOCATION}/{TEST_NAME}-bcpex.jar"
+
+ # Pass down the bootclasspath
+ FLAGS += f" -Xbootclasspath:{b_path}{BCPEX}"
+ FLAGS += f" -Xbootclasspath-locations:{b_path_locations}{BCPEX}"
+ COMPILE_FLAGS += f" --runtime-arg -Xbootclasspath:{b_path}"
+ COMPILE_FLAGS += f" --runtime-arg -Xbootclasspath-locations:{b_path_locations}"
+
+ if not HAVE_IMAGE:
+ # Disable image dex2oat - this will forbid the runtime to patch or compile an image.
+ FLAGS += " -Xnoimage-dex2oat"
+
+ # We'll abuse a second flag here to test different behavior. If --relocate, use the
+ # existing image - relocation will fail as patching is disallowed. If --no-relocate,
+ # pass a non-existent image - compilation will fail as dex2oat is disallowed.
+ if not RELOCATE:
+ BOOT_IMAGE = "/system/non-existent/boot.art"
+ # App images cannot be generated without a boot image.
+ APP_IMAGE = False
+ DALVIKVM_BOOT_OPT = f"-Ximage:{BOOT_IMAGE}"
+
+ if USE_GDB_DEX2OAT:
+ assert HOST, "The --gdb-dex2oat option is not yet implemented for target."
+
+ assert not USE_GDBSERVER, "Not supported"
+ if USE_GDB:
+ if not HOST:
+ # We might not have any hostname resolution if we are using a chroot.
+ GDB = f"{GDBSERVER_DEVICE} --no-startup-with-shell 127.0.0.1{GDBSERVER_PORT}"
+ else:
+ GDB = "gdb"
+ GDB_ARGS += f" --args {DALVIKVM}"
+
+ if INTERPRETER:
+ INT_OPTS += " -Xint"
+
+ if JIT:
+ INT_OPTS += " -Xusejit:true"
+ else:
+ INT_OPTS += " -Xusejit:false"
+
+ if INTERPRETER or JIT:
+ if VERIFY == "y":
+ INT_OPTS += " -Xcompiler-option --compiler-filter=verify"
+ COMPILE_FLAGS += " --compiler-filter=verify"
+ elif VERIFY == "s":
+ INT_OPTS += " -Xcompiler-option --compiler-filter=extract"
+ COMPILE_FLAGS += " --compiler-filter=extract"
+ DEX_VERIFY = f"{DEX_VERIFY} -Xverify:softfail"
+ else: # VERIFY == "n"
+ INT_OPTS += " -Xcompiler-option --compiler-filter=assume-verified"
+ COMPILE_FLAGS += " --compiler-filter=assume-verified"
+ DEX_VERIFY = f"{DEX_VERIFY} -Xverify:none"
+
+ JNI_OPTS = "-Xjnigreflimit:512 -Xcheck:jni"
+
+ COMPILE_FLAGS += " --runtime-arg -Xnorelocate"
+ if RELOCATE:
+ FLAGS += " -Xrelocate"
+ else:
+ FLAGS += " -Xnorelocate"
+
+ if BIONIC:
+ # This is the location that soong drops linux_bionic builds. Despite being
+ # called linux_bionic-x86 the build is actually amd64 (x86_64) only.
+ assert path.exists(f"{OUT_DIR}/soong/host/linux_bionic-x86"), (
+ "linux_bionic-x86 target doesn't seem to have been built!")
+ # Set TIMEOUT_DUMPER manually so it works even with apex's
+ TIMEOUT_DUMPER = f"{OUT_DIR}/soong/host/linux_bionic-x86/bin/signal_dumper"
+
+ # Prevent test from silently falling back to interpreter in no-prebuild mode. This happens
+ # when DEX_LOCATION path is too long, because vdex/odex filename is constructed by taking
+ # full path to dex, stripping leading '/', appending '@classes.vdex' and changing every
+ # remaining '/' into '@'.
+ if HOST:
+ max_filename_size = int(check_output(f"getconf NAME_MAX {DEX_LOCATION}", shell=True))
+ else:
+ # There is no getconf on device, fallback to standard value.
+ # See NAME_MAX in kernel <linux/limits.h>
+ max_filename_size = 255
+ # Compute VDEX_NAME.
+ DEX_LOCATION_STRIPPED = DEX_LOCATION.lstrip("/")
+ VDEX_NAME = f"{DEX_LOCATION_STRIPPED}@{TEST_NAME}.jar@classes.vdex".replace(
+ "/", "@")
+ assert len(VDEX_NAME) <= max_filename_size, "Dex location path too long"
+
+ if HOST:
+ # On host, run binaries (`dex2oat(d)`, `dalvikvm`, `profman`) from the `bin`
+ # directory under the "Android Root" (usually `out/host/linux-x86`).
+ #
+ # TODO(b/130295968): Adjust this if/when ART host artifacts are installed
+ # under the ART root (usually `out/host/linux-x86/com.android.art`).
+ ANDROID_ART_BIN_DIR = f"{ANDROID_ROOT}/bin"
+ else:
+ # On target, run binaries (`dex2oat(d)`, `dalvikvm`, `profman`) from the ART
+ # APEX's `bin` directory. This means the linker will observe the ART APEX
+ # linker configuration file (`/apex/com.android.art/etc/ld.config.txt`) for
+ # these binaries.
+ ANDROID_ART_BIN_DIR = f"{ANDROID_ART_ROOT}/bin"
+
+ profman_cmdline = "true"
+ dex2oat_cmdline = "true"
+ vdex_cmdline = "true"
+ dm_cmdline = "true"
+ mkdir_locations = f"{DEX_LOCATION}/dalvik-cache/{ISA}"
+ strip_cmdline = "true"
+ sync_cmdline = "true"
+ linkroot_cmdline = "true"
+ linkroot_overlay_cmdline = "true"
+ setupapex_cmdline = "true"
+ installapex_cmdline = "true"
+ installapex_test_cmdline = "true"
+
+ def linkdirs(host_out: str, root: str):
+ dirs = list(filter(os.path.isdir, glob.glob(os.path.join(host_out, "*"))))
+ # Also create a link for the boot image.
+ dirs.append(f"{ANDROID_HOST_OUT}/apex/art_boot_images")
+ return " && ".join(f"ln -sf {dir} {root}" for dir in dirs)
+
+ if CREATE_ANDROID_ROOT:
+ mkdir_locations += f" {ANDROID_ROOT}"
+ linkroot_cmdline = linkdirs(ANDROID_HOST_OUT, ANDROID_ROOT)
+ if BIONIC:
+ # TODO Make this overlay more generic.
+ linkroot_overlay_cmdline = linkdirs(
+ f"{OUT_DIR}/soong/host/linux_bionic-x86", ANDROID_ROOT)
+ # Replace the boot image to a location expected by the runtime.
+ DALVIKVM_BOOT_OPT = f"-Ximage:{ANDROID_ROOT}/art_boot_images/javalib/boot.art"
+
+ if USE_ZIPAPEX:
+ # TODO Currently this only works for linux_bionic zipapexes because those are
+ # stripped and so small enough that the ulimit doesn't kill us.
+ mkdir_locations += f" {DEX_LOCATION}/zipapex"
+ setupapex_cmdline = f"unzip -o -u {ZIPAPEX_LOC} apex_payload.zip -d {DEX_LOCATION}"
+ installapex_cmdline = f"unzip -o -u {DEX_LOCATION}/apex_payload.zip -d {DEX_LOCATION}/zipapex"
+ ANDROID_ART_BIN_DIR = f"{DEX_LOCATION}/zipapex/bin"
+ elif USE_EXTRACTED_ZIPAPEX:
+ # Just symlink the zipapex binaries
+ ANDROID_ART_BIN_DIR = f"{DEX_LOCATION}/zipapex/bin"
+ # Force since some tests manually run this file twice.
+ # If the {RUN} is executed multiple times we don't need to recreate the link
+ installapex_cmdline = f"ln -sfTv {EXTRACTED_ZIPAPEX_LOC} {DEX_LOCATION}/zipapex"
+
+ # PROFILE takes precedence over RANDOM_PROFILE, since PROFILE tests require a
+ # specific profile to run properly.
+ if PROFILE or RANDOM_PROFILE:
+ profman_cmdline = f"{ANDROID_ART_BIN_DIR}/profman \
+ --apk={DEX_LOCATION}/{TEST_NAME}.jar \
+ --dex-location={DEX_LOCATION}/{TEST_NAME}.jar"
+
+ if isfile(f"{TEST_NAME}-ex.jar") and SECONDARY_COMPILATION:
+ profman_cmdline = f"{profman_cmdline} \
+ --apk={DEX_LOCATION}/{TEST_NAME}-ex.jar \
+ --dex-location={DEX_LOCATION}/{TEST_NAME}-ex.jar"
+
+ COMPILE_FLAGS = f"{COMPILE_FLAGS} --profile-file={DEX_LOCATION}/{TEST_NAME}.prof"
+ FLAGS = f"{FLAGS} -Xcompiler-option --profile-file={DEX_LOCATION}/{TEST_NAME}.prof"
+ if PROFILE:
+ profman_cmdline = f"{profman_cmdline} --create-profile-from={DEX_LOCATION}/profile \
+ --reference-profile-file={DEX_LOCATION}/{TEST_NAME}.prof"
+
+ else:
+ profman_cmdline = f"{profman_cmdline} --generate-test-profile={DEX_LOCATION}/{TEST_NAME}.prof \
+ --generate-test-profile-seed=0"
+
+ def get_prebuilt_lldb_path():
+ CLANG_BASE = "prebuilts/clang/host"
+ CLANG_VERSION = check_output(
+ f"{ANDROID_BUILD_TOP}/build/soong/scripts/get_clang_version.py"
+ ).strip()
+ uname = check_output("uname -s", shell=True).strip()
+ if uname == "Darwin":
+ PREBUILT_NAME = "darwin-x86"
+ elif uname == "Linux":
+ PREBUILT_NAME = "linux-x86"
+ else:
+ print(
+ "Unknown host $(uname -s). Unsupported for debugging dex2oat with LLDB.",
+ file=sys.stderr)
+ return
+ CLANG_PREBUILT_HOST_PATH = f"{ANDROID_BUILD_TOP}/{CLANG_BASE}/{PREBUILT_NAME}/{CLANG_VERSION}"
+ # If the clang prebuilt directory exists and the reported clang version
+ # string does not, then it is likely that the clang version reported by the
+ # get_clang_version.py script does not match the expected directory name.
+ if isdir(f"{ANDROID_BUILD_TOP}/{CLANG_BASE}/{PREBUILT_NAME}"):
+ assert isdir(CLANG_PREBUILT_HOST_PATH), (
+ "The prebuilt clang directory exists, but the specific "
+ "clang\nversion reported by get_clang_version.py does not exist in "
+ "that path.\nPlease make sure that the reported clang version "
+ "resides in the\nprebuilt clang directory!")
+
+ # The lldb-server binary is a dependency of lldb.
+ os.environ[
+ "LLDB_DEBUGSERVER_PATH"] = f"{CLANG_PREBUILT_HOST_PATH}/runtimes_ndk_cxx/x86_64/lldb-server"
+
+ # Set the current terminfo directory to TERMINFO so that LLDB can read the
+ # termcap database.
+ terminfo = re.search("/.*/terminfo/", check_output("infocmp"))
+ if terminfo:
+ os.environ["TERMINFO"] = terminfo[0]
+
+ return f"{CLANG_PREBUILT_HOST_PATH}/bin/lldb.sh"
+
+ def write_dex2oat_cmdlines(name: str):
+ nonlocal dex2oat_cmdline, dm_cmdline, vdex_cmdline
+
+ class_loader_context = ""
+ enable_app_image = False
+ if APP_IMAGE:
+ enable_app_image = True
+
+ # If the name ends in -ex then this is a secondary dex file
+ if name.endswith("-ex"):
+ # Lazily realize the default value in case DEX_LOCATION/TEST_NAME change
+ nonlocal SECONDARY_CLASS_LOADER_CONTEXT
+ if SECONDARY_CLASS_LOADER_CONTEXT == "":
+ if SECONDARY_DEX == "":
+ # Tests without `--secondary` load the "-ex" jar in a separate PathClassLoader
+ # that is a child of the main PathClassLoader. If the class loader is constructed
+ # in any other way, the test needs to specify the secondary CLC explicitly.
+ SECONDARY_CLASS_LOADER_CONTEXT = f"PCL[];PCL[{DEX_LOCATION}/{TEST_NAME}.jar]"
+ else:
+ # Tests with `--secondary` load the `-ex` jar a part of the main PathClassLoader.
+ SECONDARY_CLASS_LOADER_CONTEXT = f"PCL[{DEX_LOCATION}/{TEST_NAME}.jar]"
+ class_loader_context = f"'--class-loader-context={SECONDARY_CLASS_LOADER_CONTEXT}'"
+ enable_app_image = enable_app_image and SECONDARY_APP_IMAGE
+
+ app_image = ""
+ if enable_app_image:
+ app_image = f"--app-image-file={DEX_LOCATION}/oat/{ISA}/{name}.art --resolve-startup-const-strings=true"
+
+ nonlocal GDB_DEX2OAT, GDB_DEX2OAT_ARGS
+ if USE_GDB_DEX2OAT:
+ prebuilt_lldb_path = get_prebuilt_lldb_path()
+ GDB_DEX2OAT = f"{prebuilt_lldb_path} -f"
+ GDB_DEX2OAT_ARGS += " -- "
+
+ dex2oat_binary = DEX2OAT_DEBUG_BINARY
+ if TEST_IS_NDEBUG:
+ dex2oat_binary = DEX2OAT_NDEBUG_BINARY
+ dex2oat_cmdline = f"{INVOKE_WITH} {GDB_DEX2OAT} \
+ {ANDROID_ART_BIN_DIR}/{dex2oat_binary} \
+ {GDB_DEX2OAT_ARGS} \
+ {COMPILE_FLAGS} \
+ --boot-image={BOOT_IMAGE} \
+ --dex-file={DEX_LOCATION}/{name}.jar \
+ --oat-file={DEX_LOCATION}/oat/{ISA}/{name}.odex \
+ {app_image} \
+ --generate-mini-debug-info \
+ --instruction-set={ISA} \
+ {class_loader_context}"
+
+ if INSTRUCTION_SET_FEATURES != "":
+ dex2oat_cmdline += f" --instruction-set-features={INSTRUCTION_SET_FEATURES}"
+
+ # Add in a timeout. This is important for testing the compilation/verification time of
+ # pathological cases. We do not append a timeout when debugging dex2oat because we
+ # do not want it to exit while debugging.
+ # Note: as we don't know how decent targets are (e.g., emulator), only do this on the host for
+ # now. We should try to improve this.
+ # The current value is rather arbitrary. run-tests should compile quickly.
+ # Watchdog timeout is in milliseconds so add 3 '0's to the dex2oat timeout.
+ if HOST and not USE_GDB_DEX2OAT:
+ # Use SIGRTMIN+2 to try to dump threads.
+ # Use -k 1m to SIGKILL it a minute later if it hasn't ended.
+ dex2oat_cmdline = f"timeout -k {DEX2OAT_TIMEOUT}s -s SIGRTMIN+2 {DEX2OAT_RT_TIMEOUT}s {dex2oat_cmdline} --watchdog-timeout={DEX2OAT_TIMEOUT}000"
+ if PROFILE or RANDOM_PROFILE:
+ vdex_cmdline = f"{dex2oat_cmdline} {VDEX_ARGS} --input-vdex={DEX_LOCATION}/oat/{ISA}/{name}.vdex --output-vdex={DEX_LOCATION}/oat/{ISA}/{name}.vdex"
+ elif TEST_VDEX:
+ if VDEX_ARGS == "":
+ # If no arguments need to be passed, just delete the odex file so that the runtime only picks up the vdex file.
+ vdex_cmdline = f"rm {DEX_LOCATION}/oat/{ISA}/{name}.odex"
+ else:
+ vdex_cmdline = f"{dex2oat_cmdline} {VDEX_ARGS} --compact-dex-level=none --input-vdex={DEX_LOCATION}/oat/{ISA}/{name}.vdex"
+ elif TEST_DEX2OAT_DM:
+ vdex_cmdline = f"{dex2oat_cmdline} {VDEX_ARGS} --dump-timings --dm-file={DEX_LOCATION}/oat/{ISA}/{name}.dm"
+ dex2oat_cmdline = f"{dex2oat_cmdline} --copy-dex-files=false --output-vdex={DEX_LOCATION}/oat/{ISA}/primary.vdex"
+ dm_cmdline = f"zip -qj {DEX_LOCATION}/oat/{ISA}/{name}.dm {DEX_LOCATION}/oat/{ISA}/primary.vdex"
+ elif TEST_RUNTIME_DM:
+ dex2oat_cmdline = f"{dex2oat_cmdline} --copy-dex-files=false --output-vdex={DEX_LOCATION}/oat/{ISA}/primary.vdex"
+ dm_cmdline = f"zip -qj {DEX_LOCATION}/{name}.dm {DEX_LOCATION}/oat/{ISA}/primary.vdex"
+
+# Enable mini-debug-info for JIT (if JIT is used).
+
+ FLAGS += " -Xcompiler-option --generate-mini-debug-info"
+
+ if PREBUILD:
+ mkdir_locations += f" {DEX_LOCATION}/oat/{ISA}"
+
+ # "Primary".
+ write_dex2oat_cmdlines(TEST_NAME)
+ dex2oat_cmdline = re.sub(" +", " ", dex2oat_cmdline)
+ dm_cmdline = re.sub(" +", " ", dm_cmdline)
+ vdex_cmdline = re.sub(" +", " ", vdex_cmdline)
+
+ # Enable mini-debug-info for JIT (if JIT is used).
+ FLAGS += " -Xcompiler-option --generate-mini-debug-info"
+
+ if isfile(f"{TEST_NAME}-ex.jar") and SECONDARY_COMPILATION:
+ # "Secondary" for test coverage.
+
+ # Store primary values.
+ base_dex2oat_cmdline = dex2oat_cmdline
+ base_dm_cmdline = dm_cmdline
+ base_vdex_cmdline = vdex_cmdline
+
+ write_dex2oat_cmdlines(f"{TEST_NAME}-ex")
+ dex2oat_cmdline = re.sub(" +", " ", dex2oat_cmdline)
+ dm_cmdline = re.sub(" +", " ", dm_cmdline)
+ vdex_cmdline = re.sub(" +", " ", vdex_cmdline)
+
+ # Concatenate.
+ dex2oat_cmdline = f"{base_dex2oat_cmdline} && {dex2oat_cmdline}"
+ dm_cmdline = base_dm_cmdline # Only use primary dm.
+ vdex_cmdline = f"{base_vdex_cmdline} && {vdex_cmdline}"
+
+ if SYNC_BEFORE_RUN:
+ sync_cmdline = "sync"
+
+ DALVIKVM_ISA_FEATURES_ARGS = ""
+ if INSTRUCTION_SET_FEATURES != "":
+ DALVIKVM_ISA_FEATURES_ARGS = f"-Xcompiler-option --instruction-set-features={INSTRUCTION_SET_FEATURES}"
+
+# java.io.tmpdir can only be set at launch time.
+ TMP_DIR_OPTION = ""
+ if not HOST:
+ TMP_DIR_OPTION = "-Djava.io.tmpdir=/data/local/tmp"
+
+# The build servers have an ancient version of bash so we cannot use @Q.
+ QUOTED_DALVIKVM_BOOT_OPT = shlex.quote(DALVIKVM_BOOT_OPT)
+
+ DALVIKVM_CLASSPATH = f"{DEX_LOCATION}/{TEST_NAME}.jar"
+ if isfile(f"{TEST_NAME}-aotex.jar"):
+ DALVIKVM_CLASSPATH = f"{DALVIKVM_CLASSPATH}:{DEX_LOCATION}/{TEST_NAME}-aotex.jar"
+ DALVIKVM_CLASSPATH = f"{DALVIKVM_CLASSPATH}{SECONDARY_DEX}"
+
+ # We set DumpNativeStackOnSigQuit to false to avoid stressing libunwind.
+ # b/27185632
+ # b/24664297
+
+ dalvikvm_cmdline = f"{INVOKE_WITH} {GDB} {ANDROID_ART_BIN_DIR}/{DALVIKVM} \
+ {GDB_ARGS} \
+ {FLAGS} \
+ {DEX_VERIFY} \
+ -XXlib:{LIB} \
+ {DEX2OAT} \
+ {DALVIKVM_ISA_FEATURES_ARGS} \
+ {ZYGOTE} \
+ {JNI_OPTS} \
+ {INT_OPTS} \
+ {DEBUGGER_OPTS} \
+ {QUOTED_DALVIKVM_BOOT_OPT} \
+ {TMP_DIR_OPTION} \
+ -XX:DumpNativeStackOnSigQuit:false \
+ -cp {DALVIKVM_CLASSPATH} {MAIN} {ARGS}"
+
+ if SIMPLEPERF:
+ dalvikvm_cmdline = f"simpleperf record {dalvikvm_cmdline} && simpleperf report"
+
+ def sanitize_dex2oat_cmdline(cmdline: str) -> str:
+ args = []
+ for arg in cmdline.split(" "):
+ if arg == "--class-loader-context=&":
+ arg = "--class-loader-context=\&"
+ args.append(arg)
+ return " ".join(args)
+
+ # Remove whitespace.
+ dex2oat_cmdline = sanitize_dex2oat_cmdline(dex2oat_cmdline)
+ dalvikvm_cmdline = re.sub(" +", " ", dalvikvm_cmdline)
+ dm_cmdline = re.sub(" +", " ", dm_cmdline)
+ vdex_cmdline = sanitize_dex2oat_cmdline(vdex_cmdline)
+ profman_cmdline = re.sub(" +", " ", profman_cmdline)
+
+ # Use an empty ASAN_OPTIONS to enable defaults.
+ # Note: this is required as envsetup right now exports detect_leaks=0.
+ RUN_TEST_ASAN_OPTIONS = ""
+
+ # Multiple shutdown leaks. b/38341789
+ if RUN_TEST_ASAN_OPTIONS != "":
+ RUN_TEST_ASAN_OPTIONS = f"{RUN_TEST_ASAN_OPTIONS}:"
+ RUN_TEST_ASAN_OPTIONS = f"{RUN_TEST_ASAN_OPTIONS}detect_leaks=0"
+
+ assert not args.external_log_tags, "Deprecated: use --android-log-tags=*:v"
+
+ ANDROID_LOG_TAGS = args.android_log_tags
+
+ if not HOST:
+ # Populate LD_LIBRARY_PATH.
+ LD_LIBRARY_PATH = ""
+ if ANDROID_ROOT != "/system":
+ # Current default installation is dalvikvm 64bits and dex2oat 32bits,
+ # so we can only use LD_LIBRARY_PATH when testing on a local
+ # installation.
+ LD_LIBRARY_PATH = f"{ANDROID_ROOT}/{LIBRARY_DIRECTORY}"
+
+ # This adds libarttest(d).so to the default linker namespace when dalvikvm
+ # is run from /apex/com.android.art/bin. Since that namespace is essentially
+ # an alias for the com_android_art namespace, that gives libarttest(d).so
+ # full access to the internal ART libraries.
+ LD_LIBRARY_PATH = f"/data/{TEST_DIRECTORY}/com.android.art/lib{SUFFIX64}:{LD_LIBRARY_PATH}"
+ dlib = ("" if TEST_IS_NDEBUG else "d")
+ art_test_internal_libraries = [
+ f"libartagent{dlib}.so",
+ f"libarttest{dlib}.so",
+ f"libtiagent{dlib}.so",
+ f"libtistress{dlib}.so",
+ ]
+ NATIVELOADER_DEFAULT_NAMESPACE_LIBS = ":".join(art_test_internal_libraries)
+ dlib = ""
+ art_test_internal_libraries = []
+
+ # Needed to access the test's Odex files.
+ LD_LIBRARY_PATH = f"{DEX_LOCATION}/oat/{ISA}:{LD_LIBRARY_PATH}"
+ # Needed to access the test's native libraries (see e.g. 674-hiddenapi,
+ # which generates `libhiddenapitest_*.so` libraries in `{DEX_LOCATION}`).
+ LD_LIBRARY_PATH = f"{DEX_LOCATION}:{LD_LIBRARY_PATH}"
+
+ # Prepend directories to the path on device.
+ PREPEND_TARGET_PATH = ANDROID_ART_BIN_DIR
+ if ANDROID_ROOT != "/system":
+ PREPEND_TARGET_PATH = f"{PREPEND_TARGET_PATH}:{ANDROID_ROOT}/bin"
+
+ timeout_dumper_cmd = ""
+
+ if TIMEOUT_DUMPER:
+ # Use "-l" to dump to logcat. That is convenience for the build bot crash symbolization.
+ # Use exit code 124 for toybox timeout (b/141007616).
+ timeout_dumper_cmd = f"{TIMEOUT_DUMPER} -l -s 15 -e 124"
+
+ timeout_prefix = ""
+ if TIME_OUT == "timeout":
+ # Add timeout command if time out is desired.
+ #
+ # Note: We first send SIGTERM (the timeout default, signal 15) to the signal dumper, which
+ # will induce a full thread dump before killing the process. To ensure any issues in
+ # dumping do not lead to a deadlock, we also use the "-k" option to definitely kill the
+ # child.
+ # Note: Using "--foreground" to not propagate the signal to children, i.e., the runtime.
+ timeout_prefix = f"timeout --foreground -k 120s {TIME_OUT_VALUE}s {timeout_dumper_cmd}"
+
+ ctx.export(
+ ASAN_OPTIONS = RUN_TEST_ASAN_OPTIONS,
+ ANDROID_DATA = DEX_LOCATION,
+ DEX_LOCATION = DEX_LOCATION,
+ ANDROID_ROOT = ANDROID_ROOT,
+ ANDROID_I18N_ROOT = ANDROID_I18N_ROOT,
+ ANDROID_ART_ROOT = ANDROID_ART_ROOT,
+ ANDROID_TZDATA_ROOT = ANDROID_TZDATA_ROOT,
+ ANDROID_LOG_TAGS = ANDROID_LOG_TAGS,
+ LD_LIBRARY_PATH = LD_LIBRARY_PATH,
+ NATIVELOADER_DEFAULT_NAMESPACE_LIBS = NATIVELOADER_DEFAULT_NAMESPACE_LIBS,
+ PATH = f"{PREPEND_TARGET_PATH}:$PATH",
+ )
+
+ if USE_GDB or USE_GDBSERVER:
+ print(f"Forward {GDBSERVER_PORT} to local port and connect GDB")
+
+ ctx.run(f"rm -rf {DEX_LOCATION}/{{oat,dalvik-cache}}/ && mkdir -p {mkdir_locations}")
+ ctx.run(f"{profman_cmdline}")
+ ctx.run(f"{dex2oat_cmdline}", desc="Dex2oat")
+ ctx.run(f"{dm_cmdline}")
+ ctx.run(f"{vdex_cmdline}")
+ ctx.run(f"{strip_cmdline}")
+ ctx.run(f"{sync_cmdline}")
+ ctx.run(tee(f"{timeout_prefix} {dalvikvm_cmdline}"),
+ expected_exit_code=args.expected_exit_code, desc="DalvikVM")
+ else:
+ # Host run.
+ if USE_ZIPAPEX or USE_EXRACTED_ZIPAPEX:
+ # Put the zipapex files in front of the ld-library-path
+ LD_LIBRARY_PATH = f"{ANDROID_DATA}/zipapex/{LIBRARY_DIRECTORY}:{ANDROID_ROOT}/{TEST_DIRECTORY}"
+ else:
+ LD_LIBRARY_PATH = f"{ANDROID_ROOT}/{LIBRARY_DIRECTORY}:{ANDROID_ROOT}/{TEST_DIRECTORY}"
+
+ ctx.export(
+ ANDROID_PRINTF_LOG = "brief",
+ ASAN_OPTIONS = RUN_TEST_ASAN_OPTIONS,
+ ANDROID_DATA = DEX_LOCATION,
+ DEX_LOCATION = DEX_LOCATION,
+ ANDROID_ROOT = ANDROID_ROOT,
+ ANDROID_I18N_ROOT = ANDROID_I18N_ROOT,
+ ANDROID_ART_ROOT = ANDROID_ART_ROOT,
+ ANDROID_TZDATA_ROOT = ANDROID_TZDATA_ROOT,
+ ANDROID_LOG_TAGS = ANDROID_LOG_TAGS,
+ LD_LIBRARY_PATH = LD_LIBRARY_PATH,
+ PATH = f"{PATH}:{ANDROID_ART_BIN_DIR}",
+ # Temporarily disable address space layout randomization (ASLR).
+ # This is needed on the host so that the linker loads core.oat at the necessary address.
+ LD_USE_LOAD_BIAS = "1",
+ TERM = os.environ.get("TERM", ""), # Needed for GDB
+ )
+
+ cmdline = dalvikvm_cmdline
+
+ if TIME_OUT == "gdb":
+ if run("uname").stdout.strip() == "Darwin":
+ # Fall back to timeout on Mac.
+ TIME_OUT = "timeout"
+ elif ISA == "x86":
+ # prctl call may fail in 32-bit on an older (3.2) 64-bit Linux kernel. Fall back to timeout.
+ TIME_OUT = "timeout"
+ else:
+ # Check if gdb is available.
+ proc = run('gdb --eval-command="quit"', check=False, save_cmd=False)
+ if proc.returncode != 0:
+ # gdb isn't available. Fall back to timeout.
+ TIME_OUT = "timeout"
+
+ if TIME_OUT == "timeout":
+ # Add timeout command if time out is desired.
+ #
+ # Note: We first send SIGTERM (the timeout default, signal 15) to the signal dumper, which
+ # will induce a full thread dump before killing the process. To ensure any issues in
+ # dumping do not lead to a deadlock, we also use the "-k" option to definitely kill the
+ # child.
+ # Note: Using "--foreground" to not propagate the signal to children, i.e., the runtime.
+ cmdline = f"timeout --foreground -k 120s {TIME_OUT_VALUE}s {TIMEOUT_DUMPER} -s 15 {cmdline}"
+
+ os.chdir(ANDROID_BUILD_TOP)
+
+ # Make sure we delete any existing compiler artifacts.
+ # This enables tests to call the RUN script multiple times in a row
+ # without worrying about interference.
+ ctx.run(f"rm -rf {DEX_LOCATION}/{{oat,dalvik-cache}}/")
+
+ ctx.run(f"mkdir -p {mkdir_locations}")
+ ctx.run(setupapex_cmdline)
+ if USE_EXTRACTED_ZIPAPEX:
+ ctx.run(installapex_cmdline)
+ ctx.run(linkroot_cmdline)
+ ctx.run(linkroot_overlay_cmdline)
+ ctx.run(profman_cmdline)
+ ctx.run(dex2oat_cmdline, desc="Dex2oat")
+ ctx.run(dm_cmdline)
+ ctx.run(vdex_cmdline)
+ ctx.run(strip_cmdline)
+ ctx.run(sync_cmdline)
+
+ if DRY_RUN:
+ return
+
+ if USE_GDB:
+ # When running under gdb, we cannot do piping and grepping...
+ ctx.run(cmdline)
+ else:
+ ctx.run(tee(cmdline), expected_exit_code=args.expected_exit_code, desc="DalvikVM")
+
+ # Remove unwanted log messages from stderr before diffing with the expected output.
+ # NB: The unwanted log line can be interleaved in the middle of wanted stderr printf.
+ # In particular, unhandled exception is printed using several unterminated printfs.
+ ALL_LOG_TAGS = ["V", "D", "I", "W", "E", "F", "S"]
+ skip_tag_set = "|".join(ALL_LOG_TAGS[:ALL_LOG_TAGS.index(args.diff_min_log_tag.upper())])
+ skip_reg_exp = fr'[[:alnum:]]+ ({skip_tag_set}) #-# #:#:# [^\n]*\n'.replace('#', '[0-9]+')
+ ctx.run(fr"sed -i -z -E 's/{skip_reg_exp}//g' '{args.stderr_file}'")
+ if not HAVE_IMAGE:
+ message = "(Unable to open file|Could not create image space)"
+ ctx.run(fr"sed -i -E '/^dalvikvm(|32|64) E .* {message}/d' '{args.stderr_file}'")
+ if ANDROID_LOG_TAGS != "*:i" and "D" in skip_tag_set:
+ ctx.run(fr"sed -i -E '/^(Time zone|I18n) APEX ICU file found/d' '{args.stderr_file}'")
diff --git a/test/dexpreopt/Android.bp b/test/dexpreopt/Android.bp
index b39565b..f5ffd89 100644
--- a/test/dexpreopt/Android.bp
+++ b/test/dexpreopt/Android.bp
@@ -36,5 +36,5 @@
"libgmock",
"libprocinfo",
],
- test_config_template: "//art/test:art-gtests-target-standalone-root-template",
+ test_config_template: "art_standalone_dexpreopt_tests.xml",
}
diff --git a/test/dexpreopt/art_standalone_dexpreopt_tests.xml b/test/dexpreopt/art_standalone_dexpreopt_tests.xml
new file mode 100644
index 0000000..873b6a2
--- /dev/null
+++ b/test/dexpreopt/art_standalone_dexpreopt_tests.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<!-- Note: This test config file for {MODULE} is generated from a template. -->
+<configuration description="Runs {MODULE} as root.">
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="{MODULE}->/data/local/tmp/{MODULE}/{MODULE}" />
+ <option name="append-bitness" value="true" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp/{MODULE}" />
+ <option name="module-name" value="{MODULE}" />
+ <option name="ld-library-path-32" value="/apex/com.android.art/lib" />
+ <option name="ld-library-path-64" value="/apex/com.android.art/lib64" />
+ </test>
+
+ <!-- When this test is run in a Mainline context (e.g. with `mts-tradefed`), only enable it if
+ one of the Mainline modules below is present on the device used for testing. -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <!-- ART Mainline Module (internal version). -->
+ <option name="mainline-module-package-name" value="com.google.android.art" />
+ <!-- ART Mainline Module (external (AOSP) version). -->
+ <option name="mainline-module-package-name" value="com.android.art" />
+ </object>
+
+ <!-- Only run tests if the device under test is SDK version 31 (Android 12) or above. -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" />
+</configuration>
diff --git a/test/etc/apex-bootclasspath-utils.sh b/test/etc/apex-bootclasspath-utils.sh
deleted file mode 100755
index 5a0873d..0000000
--- a/test/etc/apex-bootclasspath-utils.sh
+++ /dev/null
@@ -1,86 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2022 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.
-
-# This file contains utils for constructing -Xbootclasspath and -Xbootclasspath-location
-# for dex2oat and dalvikvm from apex modules list.
-#
-# Those utils could be used outside of art/test/ to run ART in chroot setup.
-
-# Note: This must start with the CORE_IMG_JARS in Android.common_path.mk
-# because that's what we use for compiling the boot.art image.
-# It may contain additional modules from TEST_CORE_JARS.
-readonly bpath_modules="core-oj core-libart okhttp bouncycastle apache-xml core-icu4j conscrypt"
-
-# Helper function to construct paths for apex modules (for both -Xbootclasspath and
-# -Xbootclasspath-location).
-#
-# Arguments.
-# ${1}: path prefix.
-get_apex_bootclasspath_impl() {
- local -r bpath_prefix="$1"
- local bpath_separator=""
- local bpath=""
- local bpath_jar=""
- for bpath_module in ${bpath_modules}; do
- local apex_module="com.android.art"
- case "$bpath_module" in
- (conscrypt) apex_module="com.android.conscrypt";;
- (core-icu4j) apex_module="com.android.i18n";;
- (*) apex_module="com.android.art";;
- esac
- bpath_jar="/apex/${apex_module}/javalib/${bpath_module}.jar"
- bpath+="${bpath_separator}${bpath_prefix}${bpath_jar}"
- bpath_separator=":"
- done
- echo "${bpath}"
-}
-
-# Gets a -Xbootclasspath paths with the apex modules.
-#
-# Arguments.
-# ${1}: host (y|n).
-get_apex_bootclasspath() {
- local -r host="${1}"
- local bpath_prefix=""
-
- if [[ "${host}" == "y" ]]; then
- bpath_prefix="${ANDROID_HOST_OUT}"
- fi
-
- get_apex_bootclasspath_impl "${bpath_prefix}"
-}
-
-# Gets a -Xbootclasspath-location paths with the apex modules.
-#
-# Arguments.
-# ${1}: host (y|n).
-get_apex_bootclasspath_locations() {
- local -r host="${1}"
- local bpath_location_prefix=""
-
- if [[ "${host}" == "y" ]]; then
- if [[ "${ANDROID_HOST_OUT:0:${#ANDROID_BUILD_TOP}+1}" == "${ANDROID_BUILD_TOP}/" ]]; then
- bpath_location_prefix="${ANDROID_HOST_OUT:${#ANDROID_BUILD_TOP}+1}"
- else
- error_msg "ANDROID_BUILD_TOP/ is not a prefix of ANDROID_HOST_OUT"\
- "\nANDROID_BUILD_TOP=${ANDROID_BUILD_TOP}"\
- "\nANDROID_HOST_OUT=${ANDROID_HOST_OUT}"
- exit
- fi
- fi
-
- get_apex_bootclasspath_impl "${bpath_location_prefix}"
-}
diff --git a/test/etc/default-build b/test/etc/default-build
deleted file mode 100755
index 26820f2..0000000
--- a/test/etc/default-build
+++ /dev/null
@@ -1,430 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2021 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.
-
-"""This is the default build script for run-tests.
-
-It can be overwrite by specific run-tests if needed.
-It is used from soong build and not intended to be called directly.
-"""
-
-import argparse
-import functools
-import glob
-import os
-from os import path
-import shlex
-import shutil
-import subprocess
-import tempfile
-import zipfile
-
-if not os.sys.argv:
- print(
- 'Error: default-build should have the parameters from the "build" script forwarded to it'
- )
- print('Error: An example of how do it correctly is ./default-build "$@"')
- os.sys.exit(1)
-
-
-def parse_bool(text):
- return {"true": True, "false": False}[text.lower()]
-
-
-TEST_NAME = os.environ["TEST_NAME"]
-ART_TEST_RUN_TEST_BOOTCLASSPATH = os.environ["ART_TEST_RUN_TEST_BOOTCLASSPATH"]
-NEED_DEX = parse_bool(os.environ["NEED_DEX"])
-
-# Set default values for directories.
-HAS_SMALI = path.exists("smali")
-HAS_JASMIN = path.exists("jasmin")
-HAS_SRC = path.exists("src")
-HAS_SRC_ART = path.exists("src-art")
-HAS_SRC2 = path.exists("src2")
-HAS_SRC_MULTIDEX = path.exists("src-multidex")
-HAS_SMALI_MULTIDEX = path.exists("smali-multidex")
-HAS_JASMIN_MULTIDEX = path.exists("jasmin-multidex")
-HAS_SMALI_EX = path.exists("smali-ex")
-HAS_SRC_EX = path.exists("src-ex")
-HAS_SRC_EX2 = path.exists("src-ex2")
-HAS_SRC_AOTEX = path.exists("src-aotex")
-HAS_SRC_BCPEX = path.exists("src-bcpex")
-HAS_HIDDENAPI_SPEC = path.exists("hiddenapi-flags.csv")
-
-# USE_HIDDENAPI=false run-test... will disable hiddenapi.
-USE_HIDDENAPI = parse_bool(os.environ.get("USE_HIDDENAPI", "true"))
-
-# USE_DESUGAR=false run-test... will disable desugaring.
-USE_DESUGAR = parse_bool(os.environ.get("USE_DESUGAR", "true"))
-
-JAVAC_ARGS = shlex.split(os.environ.get("JAVAC_ARGS", ""))
-SMALI_ARGS = shlex.split(os.environ.get("SMALI_ARGS", ""))
-D8_FLAGS = shlex.split(os.environ.get("D8_FLAGS", ""))
-
-# Allow overriding ZIP_COMPRESSION_METHOD with e.g. 'store'
-ZIP_COMPRESSION_METHOD = "deflate"
-# Align every ZIP file made by calling $ZIPALIGN command?
-ZIP_ALIGN_BYTES = None
-
-DEV_MODE = False
-BUILD_MODE = "target"
-API_LEVEL = None
-DEFAULT_EXPERIMENT = "no-experiment"
-EXPERIMENTAL = DEFAULT_EXPERIMENT
-
-# Setup experimental API level mappings in a bash associative array.
-EXPERIMENTAL_API_LEVEL = {}
-EXPERIMENTAL_API_LEVEL[DEFAULT_EXPERIMENT] = "26"
-EXPERIMENTAL_API_LEVEL["default-methods"] = "24"
-EXPERIMENTAL_API_LEVEL["parameter-annotations"] = "25"
-EXPERIMENTAL_API_LEVEL["agents"] = "26"
-EXPERIMENTAL_API_LEVEL["method-handles"] = "26"
-EXPERIMENTAL_API_LEVEL["var-handles"] = "28"
-
-# Parse command line arguments.
-opt_bool = argparse.BooleanOptionalAction # Bool also accepts the --no- prefix.
-parser = argparse.ArgumentParser(description=__doc__)
-parser.add_argument("--src", dest="HAS_SRC", action=opt_bool)
-parser.add_argument("--src2", dest="HAS_SRC2", action=opt_bool)
-parser.add_argument("--src-multidex", dest="HAS_SRC_MULTIDEX", action=opt_bool)
-parser.add_argument(
- "--smali-multidex", dest="HAS_SMALI_MULTIDEX", action=opt_bool)
-parser.add_argument("--src-ex", dest="HAS_SRC_EX", action=opt_bool)
-parser.add_argument("--src-ex2", dest="HAS_SRC_EX2", action=opt_bool)
-parser.add_argument("--smali", dest="HAS_SMALI", action=opt_bool)
-parser.add_argument("--jasmin", dest="HAS_JASMIN", action=opt_bool)
-parser.add_argument("--api-level", dest="API_LEVEL", type=int)
-parser.add_argument(
- "--experimental", dest="EXPERIMENTAL", type=str)
-parser.add_argument(
- "--zip-compression-method", dest="ZIP_COMPRESSION_METHOD", type=str)
-parser.add_argument("--zip-align", dest="ZIP_ALIGN_BYTES", type=int)
-parser.add_argument(
- "--host", dest="BUILD_MODE", action="store_const", const="host")
-parser.add_argument(
- "--target", dest="BUILD_MODE", action="store_const", const="target")
-parser.add_argument(
- "--jvm", dest="BUILD_MODE", action="store_const", const="jvm")
-parser.add_argument("--dev", dest="DEV_MODE", action=opt_bool)
-# Update variables with command line arguments that were set.
-globals().update(
- {k: v for k, v in parser.parse_args().__dict__.items() if v is not None})
-
-if BUILD_MODE == "jvm":
- # No desugaring on jvm because it supports the latest functionality.
- USE_DESUGAR = False
- # Do not attempt to build src-art directories on jvm,
- # since it would fail without libcore.
- HAS_SRC_ART = False
-
-# Set API level for smali and d8.
-if not API_LEVEL:
- API_LEVEL = EXPERIMENTAL_API_LEVEL[EXPERIMENTAL]
-
-# Add API level arguments to smali and dx
-SMALI_ARGS.extend(["--api", str(API_LEVEL)])
-D8_FLAGS.extend(["--min-api", str(API_LEVEL)])
-
-
-def run(executable, args):
- cmd = shlex.split(executable) + args
- if executable.endswith(".sh"):
- cmd = ["/bin/bash"] + cmd
- if DEV_MODE:
- print("Run:", " ".join(cmd))
- p = subprocess.run(cmd, check=True)
- if p.returncode != 0:
- raise Exception("Failed command: " + " ".join(cmd))
-
-
-# Helper functions to execute tools.
-soong_zip = functools.partial(run, os.environ["SOONG_ZIP"])
-zipalign = functools.partial(run, os.environ["ZIPALIGN"])
-javac = functools.partial(run, os.environ["JAVAC"])
-jasmin = functools.partial(run, os.environ["JASMIN"])
-smali = functools.partial(run, os.environ["SMALI"])
-d8 = functools.partial(run, os.environ["D8"])
-hiddenapi = functools.partial(run, os.environ["HIDDENAPI"])
-
-# If wrapper script exists, use it instead of the default javac.
-if os.path.exists("javac_wrapper.sh"):
- javac = functools.partial(run, "javac_wrapper.sh")
-
-def find(root, name):
- return sorted(glob.glob(path.join(root, "**", name), recursive=True))
-
-
-def zip(zip_target, *files):
- zip_args = ["-o", zip_target]
- if ZIP_COMPRESSION_METHOD == "store":
- zip_args.extend(["-L", "0"])
- for f in files:
- zip_args.extend(["-f", f])
- soong_zip(zip_args)
-
- if ZIP_ALIGN_BYTES:
- # zipalign does not operate in-place, so write results to a temp file.
- with tempfile.TemporaryDirectory(dir=".") as tmp_dir:
- tmp_file = path.join(tmp_dir, "aligned.zip")
- zipalign(["-f", str(ZIP_ALIGN_BYTES), zip_target, tmp_file])
- # replace original zip target with our temp file.
- os.rename(tmp_file, zip_target)
-
-
-def make_jasmin(out_directory, jasmin_sources):
- os.makedirs(out_directory, exist_ok=True)
- jasmin(["-d", out_directory] + sorted(jasmin_sources))
-
-
-# Like regular javac but may include libcore on the bootclasspath.
-def javac_with_bootclasspath(args):
- flags = JAVAC_ARGS + ["-encoding", "utf8"]
- if BUILD_MODE != "jvm":
- flags.extend(["-bootclasspath", ART_TEST_RUN_TEST_BOOTCLASSPATH])
- javac(flags + args)
-
-
-# Make a "dex" file given a directory of classes. This will be
-# packaged in a jar file.
-def make_dex(name):
- d8_inputs = find(name, "*.class")
- d8_output = name + ".jar"
- dex_output = name + ".dex"
- if USE_DESUGAR:
- flags = ["--lib", ART_TEST_RUN_TEST_BOOTCLASSPATH]
- else:
- flags = ["--no-desugaring"]
- assert d8_inputs
- d8(D8_FLAGS + flags + ["--output", d8_output] + d8_inputs)
-
- # D8 outputs to JAR files today rather than DEX files as DX used
- # to. To compensate, we extract the DEX from d8's output to meet the
- # expectations of make_dex callers.
- with tempfile.TemporaryDirectory(dir=".") as tmp_dir:
- zipfile.ZipFile(d8_output, "r").extractall(tmp_dir)
- os.rename(path.join(tmp_dir, "classes.dex"), dex_output)
-
-
-# Merge all the dex files.
-# Skip non-existing files, but at least 1 file must exist.
-def make_dexmerge(*dex_files_to_merge):
- # Dex file that acts as the destination.
- dst_file = dex_files_to_merge[0]
-
- # Skip any non-existing files.
- dex_files_to_merge = list(filter(path.exists, dex_files_to_merge))
-
- # NB: We merge even if there is just single input.
- # It is useful to normalize non-deterministic smali output.
-
- with tempfile.TemporaryDirectory(dir=".") as tmp_dir:
- d8(["--min-api", API_LEVEL, "--output", tmp_dir] + dex_files_to_merge)
- assert not path.exists(path.join(tmp_dir, "classes2.dex"))
- for input_dex in dex_files_to_merge:
- os.remove(input_dex)
- os.rename(path.join(tmp_dir, "classes.dex"), dst_file)
-
-
-def make_hiddenapi(*dex_files):
- args = ["encode"]
- for dex_file in dex_files:
- args.extend(["--input-dex=" + dex_file, "--output-dex=" + dex_file])
- args.append("--api-flags=hiddenapi-flags.csv")
- args.append("--no-force-assign-all")
- hiddenapi(args)
-
-
-if path.exists("classes.dex"):
- zip(TEST_NAME + ".jar", "classes.dex")
- os.sys.exit(0)
-
-
-def has_multidex():
- return HAS_SRC_MULTIDEX or HAS_JASMIN_MULTIDEX or HAS_SMALI_MULTIDEX
-
-
-def add_to_cp_args(old_cp_args, path):
- if len(old_cp_args) == 0:
- return ["-cp", path]
- else:
- return ["-cp", old_cp_args[1] + ":" + path]
-
-
-src_tmp_all = []
-
-if HAS_JASMIN:
- make_jasmin("jasmin_classes", find("jasmin", "*.j"))
- src_tmp_all = add_to_cp_args(src_tmp_all, "jasmin_classes")
-
-if HAS_JASMIN_MULTIDEX:
- make_jasmin("jasmin_classes2", find("jasmin-multidex", "*.j"))
- src_tmp_all = add_to_cp_args(src_tmp_all, "jasmin_classes2")
-
-if HAS_SRC and (HAS_SRC_MULTIDEX or HAS_SRC_AOTEX or HAS_SRC_BCPEX or
- HAS_SRC_EX or HAS_SRC_ART or HAS_SRC2 or HAS_SRC_EX2):
- # To allow circular references, compile src/, src-multidex/, src-aotex/,
- # src-bcpex/, src-ex/ together and pass the output as class path argument.
- # Replacement sources in src-art/, src2/ and src-ex2/ can replace symbols
- # used by the other src-* sources we compile here but everything needed to
- # compile the other src-* sources should be present in src/ (and jasmin*/).
- os.makedirs("classes-tmp-all")
- javac_with_bootclasspath(["-implicit:none"] + src_tmp_all +
- ["-d", "classes-tmp-all"] +
- find("src", "*.java") +
- find("src-multidex", "*.java") +
- find("src-aotex", "*.java") +
- find("src-bcpex", "*.java") +
- find("src-ex", "*.java"))
- src_tmp_all = add_to_cp_args(src_tmp_all, "classes-tmp-all")
-
-if HAS_SRC_AOTEX:
- os.makedirs("classes-aotex")
- javac_with_bootclasspath(["-implicit:none"] + src_tmp_all +
- ["-d", "classes-aotex"] +
- find("src-aotex", "*.java"))
- if NEED_DEX:
- make_dex("classes-aotex")
- # rename it so it shows up as "classes.dex" in the zip file.
- os.rename("classes-aotex.dex", "classes.dex")
- zip(TEST_NAME + "-aotex.jar", "classes.dex")
-
-if HAS_SRC_BCPEX:
- os.makedirs("classes-bcpex")
- javac_with_bootclasspath(["-implicit:none"] + src_tmp_all +
- ["-d", "classes-bcpex"] +
- find("src-bcpex", "*.java"))
- if NEED_DEX:
- make_dex("classes-bcpex")
- # rename it so it shows up as "classes.dex" in the zip file.
- os.rename("classes-bcpex.dex", "classes.dex")
- zip(TEST_NAME + "-bcpex.jar", "classes.dex")
-
-if HAS_SRC:
- os.makedirs("classes", exist_ok=True)
- javac_with_bootclasspath(["-implicit:none"] + src_tmp_all +
- ["-d", "classes"] + find("src", "*.java"))
-
-if HAS_SRC_ART:
- os.makedirs("classes", exist_ok=True)
- javac_with_bootclasspath(["-implicit:none"] + src_tmp_all +
- ["-d", "classes"] + find("src-art", "*.java"))
-
-if HAS_SRC_MULTIDEX:
- os.makedirs("classes2")
- javac_with_bootclasspath(["-implicit:none"] + src_tmp_all +
- ["-d", "classes2"] +
- find("src-multidex", "*.java"))
- if NEED_DEX:
- make_dex("classes2")
-
-if HAS_SRC2:
- os.makedirs("classes", exist_ok=True)
- javac_with_bootclasspath(["-implicit:none"] + src_tmp_all +
- ["-d", "classes"] +
- find("src2", "*.java"))
-
-# If the classes directory is not-empty, package classes in a DEX file.
-# NB: some tests provide classes rather than java files.
-if find("classes", "*"):
- if NEED_DEX:
- make_dex("classes")
-
-if HAS_JASMIN:
- # Compile Jasmin classes as if they were part of the classes.dex file.
- if NEED_DEX:
- make_dex("jasmin_classes")
- make_dexmerge("classes.dex", "jasmin_classes.dex")
- else:
- # Move jasmin classes into classes directory so that they are picked up
- # with -cp classes.
- os.makedirs("classes", exist_ok=True)
- shutil.copytree("jasmin_classes", "classes", dirs_exist_ok=True)
-
-if HAS_SMALI and NEED_DEX:
- # Compile Smali classes
- smali(["-JXmx512m", "assemble"] + SMALI_ARGS +
- ["--output", "smali_classes.dex"] + find("smali", "*.smali"))
- assert path.exists("smali_classes.dex")
- # Merge smali files into classes.dex,
- # this takes priority over any jasmin files.
- make_dexmerge("classes.dex", "smali_classes.dex")
-
-# Compile Jasmin classes in jasmin-multidex as if they were part of
-# the classes2.jar
-if HAS_JASMIN_MULTIDEX:
- if NEED_DEX:
- make_dex("jasmin_classes2")
- make_dexmerge("classes2.dex", "jasmin_classes2.dex")
- else:
- # Move jasmin classes into classes2 directory so that
- # they are picked up with -cp classes2.
- os.makedirs("classes2", exist_ok=True)
- shutil.copytree("jasmin_classes2", "classes2", dirs_exist_ok=True)
- shutil.rmtree("jasmin_classes2")
-
-if HAS_SMALI_MULTIDEX and NEED_DEX:
- # Compile Smali classes
- smali(["-JXmx512m", "assemble"] + SMALI_ARGS +
- ["--output", "smali_classes2.dex"] + find("smali-multidex", "*.smali"))
-
- # Merge smali_classes2.dex into classes2.dex
- make_dexmerge("classes2.dex", "smali_classes2.dex")
-
-if HAS_SRC_EX:
- os.makedirs("classes-ex", exist_ok=True)
- javac_with_bootclasspath(["-implicit:none"] + src_tmp_all +
- ["-d", "classes-ex"] + find("src-ex", "*.java"))
-
-if HAS_SRC_EX2:
- os.makedirs("classes-ex", exist_ok=True)
- javac_with_bootclasspath(["-implicit:none"] + src_tmp_all +
- ["-d", "classes-ex"] + find("src-ex2", "*.java"))
-
-if path.exists("classes-ex") and NEED_DEX:
- make_dex("classes-ex")
-
-if HAS_SMALI_EX and NEED_DEX:
- # Compile Smali classes
- smali(["-JXmx512m", "assemble"] + SMALI_ARGS +
- ["--output", "smali_classes-ex.dex"] + find("smali-ex", "*.smali"))
- assert path.exists("smali_classes-ex.dex")
- # Merge smali files into classes-ex.dex.
- make_dexmerge("classes-ex.dex", "smali_classes-ex.dex")
-
-if path.exists("classes-ex.dex"):
- # Apply hiddenapi on the dex files if the test has API list file(s).
- if USE_HIDDENAPI and HAS_HIDDENAPI_SPEC:
- make_hiddenapi("classes-ex.dex")
-
- # quick shuffle so that the stored name is "classes.dex"
- os.rename("classes.dex", "classes-1.dex")
- os.rename("classes-ex.dex", "classes.dex")
- zip(TEST_NAME + "-ex.jar", "classes.dex")
- os.rename("classes.dex", "classes-ex.dex")
- os.rename("classes-1.dex", "classes.dex")
-
-# Apply hiddenapi on the dex files if the test has API list file(s).
-if NEED_DEX and USE_HIDDENAPI and HAS_HIDDENAPI_SPEC:
- if has_multidex():
- make_hiddenapi("classes.dex", "classes2.dex")
- else:
- make_hiddenapi("classes.dex")
-
-# Create a single dex jar with two dex files for multidex.
-if NEED_DEX:
- if path.exists("classes2.dex"):
- zip(TEST_NAME + ".jar", "classes.dex", "classes2.dex")
- else:
- zip(TEST_NAME + ".jar", "classes.dex")
diff --git a/test/etc/default-check b/test/etc/default-check
deleted file mode 100755
index f6f7bf4..0000000
--- a/test/etc/default-check
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2014 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.
-
-# Inputs:
-# $1: Test's expected standard output
-# $2: Test's actual standard output
-# $3: Test's expected standard error
-# $4: Test's actual standard error
-
-diff --strip-trailing-cr -q "$1" "$2" >/dev/null \
- && diff --strip-trailing-cr -q "$3" "$4" >/dev/null
diff --git a/test/etc/default-run b/test/etc/default-run
deleted file mode 100755
index ecbbbc7..0000000
--- a/test/etc/default-run
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2008 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.
-
-exec ${RUN} "$@"
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
deleted file mode 100755
index ef168ca..0000000
--- a/test/etc/run-test-jar
+++ /dev/null
@@ -1,1546 +0,0 @@
-#!/bin/bash
-#
-# Runner for an individual run-test.
-
-readonly local_path=$(dirname "$0")
-source "${local_path}/apex-bootclasspath-utils.sh"
-
-# Check how many colors the terminal can display.
-ncolors=$(tput colors 2>/dev/null)
-
-# Check that stdout is connected to a terminal and that we have at least 1 color.
-# This ensures that if the stdout is not connected to a terminal and instead
-# the stdout will be used for a log, it will not append the color characters.
-if [[ -t 1 && ${ncolors} && ${ncolors} -ge 1 ]]; then
- bold_red="$(tput bold)$(tput setaf 1)"
-fi
-
-readonly bold_red
-
-error_msg() {
- echo -e "${bold_red}ERROR: $@" 1>&2
-}
-
-if [[ -z "$ANDROID_BUILD_TOP" ]]; then
- error_msg 'ANDROID_BUILD_TOP environment variable is empty; did you forget to run `lunch`?'
- exit 1
-fi
-
-msg() {
- if [ "$QUIET" = "n" ]; then
- echo "$@"
- fi
-}
-
-ANDROID_ROOT="/system"
-ANDROID_ART_ROOT="/apex/com.android.art"
-ANDROID_I18N_ROOT="/apex/com.android.i18n"
-ANDROID_TZDATA_ROOT="/apex/com.android.tzdata"
-ARCHITECTURES_32="(arm|x86|none)"
-ARCHITECTURES_64="(arm64|x86_64|none)"
-ARCHITECTURES_PATTERN="${ARCHITECTURES_32}"
-GET_DEVICE_ISA_BITNESS_FLAG="--32"
-BOOT_IMAGE=""
-CHROOT=
-COMPILE_FLAGS=""
-DALVIKVM="dalvikvm32"
-DEBUGGER="n"
-WITH_AGENT=()
-DEBUGGER_AGENT=""
-WRAP_DEBUGGER_AGENT="n"
-DEV_MODE="n"
-DEX2OAT_NDEBUG_BINARY="dex2oat32"
-DEX2OAT_DEBUG_BINARY="dex2oatd32"
-EXPERIMENTAL=""
-FALSE_BIN="false"
-FLAGS=""
-ANDROID_FLAGS=""
-GDB=""
-GDB_ARGS=""
-GDB_DEX2OAT=""
-GDB_DEX2OAT_ARGS=""
-GDBSERVER_DEVICE="gdbserver"
-GDBSERVER_HOST="gdbserver"
-HAVE_IMAGE="y"
-HOST="n"
-BIONIC="n"
-CREATE_ANDROID_ROOT="n"
-USE_ZIPAPEX="n"
-ZIPAPEX_LOC=""
-USE_EXTRACTED_ZIPAPEX="n"
-EXTRACTED_ZIPAPEX_LOC=""
-INTERPRETER="n"
-JIT="n"
-INVOKE_WITH=""
-IS_JVMTI_TEST="n"
-ADD_LIBDIR_ARGUMENTS="n"
-SUFFIX64=""
-ISA=x86
-LIBRARY_DIRECTORY="lib"
-TEST_DIRECTORY="nativetest"
-MAIN=""
-OPTIMIZE="y"
-PREBUILD="y"
-QUIET="n"
-RELOCATE="n"
-SECONDARY_DEX=""
-TIME_OUT="n" # "n" (disabled), "timeout" (use timeout), "gdb" (use gdb)
-TIMEOUT_DUMPER=signal_dumper
-# Values in seconds.
-TIME_OUT_EXTRA=0
-TIME_OUT_VALUE=
-USE_GDB="n"
-USE_GDBSERVER="n"
-GDBSERVER_PORT=":5039"
-USE_GDB_DEX2OAT="n"
-USE_JVM="n"
-USE_JVMTI="n"
-VERIFY="y" # y=yes,n=no,s=softfail
-ZYGOTE=""
-DEX_VERIFY=""
-INSTRUCTION_SET_FEATURES=""
-ARGS=""
-VDEX_ARGS=""
-EXTERNAL_LOG_TAGS="n" # if y respect externally set ANDROID_LOG_TAGS.
-DRY_RUN="n" # if y prepare to run the test but don't run it.
-TEST_VDEX="n"
-TEST_DEX2OAT_DM="n"
-TEST_RUNTIME_DM="n"
-TEST_IS_NDEBUG="n"
-APP_IMAGE="y"
-SECONDARY_APP_IMAGE="y"
-SECONDARY_CLASS_LOADER_CONTEXT=""
-SECONDARY_COMPILATION="y"
-JVMTI_STRESS="n"
-JVMTI_STEP_STRESS="n"
-JVMTI_FIELD_STRESS="n"
-JVMTI_TRACE_STRESS="n"
-JVMTI_REDEFINE_STRESS="n"
-PROFILE="n"
-RANDOM_PROFILE="n"
-# The normal dex2oat timeout.
-DEX2OAT_TIMEOUT="300" # 5 mins
-# The *hard* timeout where we really start trying to kill the dex2oat.
-DEX2OAT_RT_TIMEOUT="360" # 6 mins
-CREATE_RUNNER="n"
-
-# if "y", run 'sync' before dalvikvm to make sure all files from
-# build step (e.g. dex2oat) were finished writing.
-SYNC_BEFORE_RUN="n"
-
-# When running a debug build, we want to run with all checks.
-ANDROID_FLAGS="${ANDROID_FLAGS} -XX:SlowDebug=true"
-# The same for dex2oatd, both prebuild and runtime-driven.
-ANDROID_FLAGS="${ANDROID_FLAGS} -Xcompiler-option --runtime-arg -Xcompiler-option -XX:SlowDebug=true"
-COMPILER_FLAGS="${COMPILER_FLAGS} --runtime-arg -XX:SlowDebug=true"
-
-# Let the compiler and runtime know that we are running tests.
-COMPILE_FLAGS="${COMPILE_FLAGS} --compile-art-test"
-ANDROID_FLAGS="${ANDROID_FLAGS} -Xcompiler-option --compile-art-test"
-
-while true; do
- if [ "x$1" = "x--quiet" ]; then
- QUIET="y"
- shift
- elif [ "x$1" = "x--dex2oat-rt-timeout" ]; then
- shift
- if [ "x$1" = "x" ]; then
- error_msg "$0 missing argument to --dex2oat-rt-timeout"
- exit 1
- fi
- DEX2OAT_RT_TIMEOUT="$1"
- shift
- elif [ "x$1" = "x--dex2oat-timeout" ]; then
- shift
- if [ "x$1" = "x" ]; then
- error_msg "$0 missing argument to --dex2oat-timeout"
- exit 1
- fi
- DEX2OAT_TIMEOUT="$1"
- shift
- elif [ "x$1" = "x--jvmti" ]; then
- USE_JVMTI="y"
- IS_JVMTI_TEST="y"
- # Secondary images block some tested behavior.
- SECONDARY_APP_IMAGE="n"
- shift
- elif [ "x$1" = "x--add-libdir-argument" ]; then
- ADD_LIBDIR_ARGUMENTS="y"
- shift
- elif [ "x$1" = "x-O" ]; then
- TEST_IS_NDEBUG="y"
- shift
- elif [ "x$1" = "x--lib" ]; then
- shift
- if [ "x$1" = "x" ]; then
- error_msg "$0 missing argument to --lib"
- exit 1
- fi
- LIB="$1"
- shift
- elif [ "x$1" = "x--gc-stress" ]; then
- # Give an extra 20 mins if we are gc-stress.
- TIME_OUT_EXTRA=$((${TIME_OUT_EXTRA} + 1200))
- shift
- elif [ "x$1" = "x--testlib" ]; then
- shift
- if [ "x$1" = "x" ]; then
- error_msg "$0 missing argument to --testlib"
- exit 1
- fi
- ARGS="${ARGS} $1"
- shift
- elif [ "x$1" = "x--args" ]; then
- shift
- if [ "x$1" = "x" ]; then
- error_msg "$0 missing argument to --args"
- exit 1
- fi
- ARGS="${ARGS} $1"
- shift
- elif [ "x$1" = "x--compiler-only-option" ]; then
- shift
- option="$1"
- COMPILE_FLAGS="${COMPILE_FLAGS} $option"
- shift
- elif [ "x$1" = "x-Xcompiler-option" ]; then
- shift
- option="$1"
- FLAGS="${FLAGS} -Xcompiler-option $option"
- COMPILE_FLAGS="${COMPILE_FLAGS} $option"
- shift
- elif [ "x$1" = "x--create-runner" ]; then
- CREATE_RUNNER="y"
- shift
- elif [ "x$1" = "x--android-runtime-option" ]; then
- shift
- option="$1"
- ANDROID_FLAGS="${ANDROID_FLAGS} $option"
- shift
- elif [ "x$1" = "x--runtime-option" ]; then
- shift
- option="$1"
- FLAGS="${FLAGS} $option"
- if [ "x$option" = "x-Xmethod-trace" ]; then
- # Method tracing can slow some tests down a lot.
- TIME_OUT_EXTRA=$((${TIME_OUT_EXTRA} + 1200))
- fi
- shift
- elif [ "x$1" = "x--boot" ]; then
- shift
- BOOT_IMAGE="$1"
- shift
- elif [ "x$1" = "x--relocate" ]; then
- RELOCATE="y"
- shift
- elif [ "x$1" = "x--no-relocate" ]; then
- RELOCATE="n"
- shift
- elif [ "x$1" = "x--prebuild" ]; then
- PREBUILD="y"
- shift
- elif [ "x$1" = "x--compact-dex-level" ]; then
- shift
- COMPILE_FLAGS="${COMPILE_FLAGS} --compact-dex-level=$1"
- shift
- elif [ "x$1" = "x--jvmti-redefine-stress" ]; then
- # APP_IMAGE doesn't really work with jvmti redefine stress
- USE_JVMTI="y"
- APP_IMAGE="n"
- SECONDARY_APP_IMAGE="n"
- JVMTI_STRESS="y"
- JVMTI_REDEFINE_STRESS="y"
- shift
- elif [ "x$1" = "x--jvmti-step-stress" ]; then
- USE_JVMTI="y"
- JVMTI_STRESS="y"
- JVMTI_STEP_STRESS="y"
- shift
- elif [ "x$1" = "x--jvmti-field-stress" ]; then
- USE_JVMTI="y"
- JVMTI_STRESS="y"
- JVMTI_FIELD_STRESS="y"
- shift
- elif [ "x$1" = "x--jvmti-trace-stress" ]; then
- USE_JVMTI="y"
- JVMTI_STRESS="y"
- JVMTI_TRACE_STRESS="y"
- shift
- elif [ "x$1" = "x--no-app-image" ]; then
- APP_IMAGE="n"
- shift
- elif [ "x$1" = "x--no-secondary-app-image" ]; then
- SECONDARY_APP_IMAGE="n"
- shift
- elif [ "x$1" = "x--secondary-class-loader-context" ]; then
- shift
- SECONDARY_CLASS_LOADER_CONTEXT="$1"
- shift
- elif [ "x$1" = "x--no-secondary-compilation" ]; then
- SECONDARY_COMPILATION="n"
- shift
- elif [ "x$1" = "x--host" ]; then
- HOST="y"
- ANDROID_ROOT="${ANDROID_HOST_OUT}"
- ANDROID_ART_ROOT="${ANDROID_HOST_OUT}/com.android.art"
- ANDROID_I18N_ROOT="${ANDROID_HOST_OUT}/com.android.i18n"
- ANDROID_TZDATA_ROOT="${ANDROID_HOST_OUT}/com.android.tzdata"
- # On host, we default to using the symlink, as the PREFER_32BIT
- # configuration is the only configuration building a 32bit version of
- # dex2oat.
- DEX2OAT_DEBUG_BINARY="dex2oatd"
- DEX2OAT_NDEBUG_BINARY="dex2oat"
- shift
- elif [ "x$1" = "x--bionic" ]; then
- BIONIC="y"
- # We need to create an ANDROID_ROOT because currently we cannot create
- # the frameworks/libcore with linux_bionic so we need to use the normal
- # host ones which are in a different location.
- CREATE_ANDROID_ROOT="y"
- shift
- elif [ "x$1" = "x--runtime-extracted-zipapex" ]; then
- shift
- USE_EXTRACTED_ZIPAPEX="y"
- EXTRACTED_ZIPAPEX_LOC="$1"
- shift
- elif [ "x$1" = "x--runtime-zipapex" ]; then
- shift
- USE_ZIPAPEX="y"
- ZIPAPEX_LOC="$1"
- # TODO (b/119942078): Currently apex does not support
- # symlink_preferred_arch so we will not have a dex2oatd to execute and
- # need to manually provide
- # dex2oatd64.
- DEX2OAT_DEBUG_BINARY="dex2oatd64"
- shift
- elif [ "x$1" = "x--no-prebuild" ]; then
- PREBUILD="n"
- shift
- elif [ "x$1" = "x--no-image" ]; then
- HAVE_IMAGE="n"
- shift
- elif [ "x$1" = "x--secondary" ]; then
- SECONDARY_DEX=":$DEX_LOCATION/$TEST_NAME-ex.jar"
- # Enable cfg-append to make sure we get the dump for both dex files.
- # (otherwise the runtime compilation of the secondary dex will overwrite
- # the dump of the first one).
- FLAGS="${FLAGS} -Xcompiler-option --dump-cfg-append"
- COMPILE_FLAGS="${COMPILE_FLAGS} --dump-cfg-append"
- shift
- elif [ "x$1" = "x--with-agent" ]; then
- shift
- USE_JVMTI="y"
- WITH_AGENT+=("$1")
- shift
- elif [ "x$1" = "x--debug-wrap-agent" ]; then
- WRAP_DEBUGGER_AGENT="y"
- shift
- elif [ "x$1" = "x--debug-agent" ]; then
- shift
- DEBUGGER="agent"
- USE_JVMTI="y"
- DEBUGGER_AGENT="$1"
- TIME_OUT="n"
- shift
- elif [ "x$1" = "x--debug" ]; then
- USE_JVMTI="y"
- DEBUGGER="y"
- TIME_OUT="n"
- shift
- elif [ "x$1" = "x--gdbserver-port" ]; then
- shift
- GDBSERVER_PORT=$1
- shift
- elif [ "x$1" = "x--gdbserver-bin" ]; then
- shift
- GDBSERVER_HOST=$1
- GDBSERVER_DEVICE=$1
- shift
- elif [ "x$1" = "x--gdbserver" ]; then
- USE_GDBSERVER="y"
- DEV_MODE="y"
- TIME_OUT="n"
- shift
- elif [ "x$1" = "x--gdb" ]; then
- USE_GDB="y"
- DEV_MODE="y"
- TIME_OUT="n"
- shift
- elif [ "x$1" = "x--gdb-arg" ]; then
- shift
- gdb_arg="$1"
- GDB_ARGS="${GDB_ARGS} $gdb_arg"
- shift
- elif [ "x$1" = "x--gdb-dex2oat" ]; then
- USE_GDB_DEX2OAT="y"
- DEV_MODE="y"
- TIME_OUT="n"
- shift
- elif [ "x$1" = "x--gdb-dex2oat-args" ]; then
- shift
- for arg in $(echo $1 | tr ";" " "); do
- GDB_DEX2OAT_ARGS+="$arg "
- done
- shift
- elif [ "x$1" = "x--zygote" ]; then
- ZYGOTE="-Xzygote"
- msg "Spawning from zygote"
- shift
- elif [ "x$1" = "x--dev" ]; then
- DEV_MODE="y"
- shift
- elif [ "x$1" = "x--interpreter" ]; then
- INTERPRETER="y"
- shift
- elif [ "x$1" = "x--jit" ]; then
- JIT="y"
- shift
- elif [ "x$1" = "x--baseline" ]; then
- FLAGS="${FLAGS} -Xcompiler-option --baseline"
- COMPILE_FLAGS="${COMPILE_FLAGS} --baseline"
- shift
- elif [ "x$1" = "x--jvm" ]; then
- USE_JVM="y"
- shift
- elif [ "x$1" = "x--invoke-with" ]; then
- shift
- if [ "x$1" = "x" ]; then
- error_msg "$0 missing argument to --invoke-with"
- exit 1
- fi
- if [ "x$INVOKE_WITH" = "x" ]; then
- INVOKE_WITH="$1"
- else
- INVOKE_WITH="$INVOKE_WITH $1"
- fi
- shift
- elif [ "x$1" = "x--no-verify" ]; then
- VERIFY="n"
- shift
- elif [ "x$1" = "x--verify-soft-fail" ]; then
- VERIFY="s"
- shift
- elif [ "x$1" = "x--no-optimize" ]; then
- OPTIMIZE="n"
- shift
- elif [ "x$1" = "x--chroot" ]; then
- shift
- CHROOT="$1"
- shift
- elif [ "x$1" = "x--simpleperf" ]; then
- SIMPLEPERF="yes"
- shift
- elif [ "x$1" = "x--android-root" ]; then
- shift
- ANDROID_ROOT="$1"
- shift
- elif [ "x$1" = "x--android-i18n-root" ]; then
- shift
- ANDROID_I18N_ROOT="$1"
- shift
- elif [ "x$1" = "x--android-art-root" ]; then
- shift
- ANDROID_ART_ROOT="$1"
- shift
- elif [ "x$1" = "x--android-tzdata-root" ]; then
- shift
- ANDROID_TZDATA_ROOT="$1"
- shift
- elif [ "x$1" = "x--instruction-set-features" ]; then
- shift
- INSTRUCTION_SET_FEATURES="$1"
- shift
- elif [ "x$1" = "x--timeout" ]; then
- shift
- TIME_OUT_VALUE="$1"
- shift
- elif [ "x$1" = "x--" ]; then
- shift
- break
- elif [ "x$1" = "x--64" ]; then
- SUFFIX64="64"
- ISA="x86_64"
- GDBSERVER_DEVICE="gdbserver64"
- DALVIKVM="dalvikvm64"
- LIBRARY_DIRECTORY="lib64"
- TEST_DIRECTORY="nativetest64"
- ARCHITECTURES_PATTERN="${ARCHITECTURES_64}"
- GET_DEVICE_ISA_BITNESS_FLAG="--64"
- DEX2OAT_NDEBUG_BINARY="dex2oat64"
- DEX2OAT_DEBUG_BINARY="dex2oatd64"
- shift
- elif [ "x$1" = "x--experimental" ]; then
- if [ "$#" -lt 2 ]; then
- error_msg "missing --experimental option"
- exit 1
- fi
- EXPERIMENTAL="$EXPERIMENTAL $2"
- shift 2
- elif [ "x$1" = "x--external-log-tags" ]; then
- EXTERNAL_LOG_TAGS="y"
- shift
- elif [ "x$1" = "x--dry-run" ]; then
- DRY_RUN="y"
- shift
- elif [ "x$1" = "x--vdex" ]; then
- TEST_VDEX="y"
- shift
- elif [ "x$1" = "x--dex2oat-dm" ]; then
- TEST_DEX2OAT_DM="y"
- shift
- elif [ "x$1" = "x--runtime-dm" ]; then
- TEST_RUNTIME_DM="y"
- shift
- elif [ "x$1" = "x--vdex-filter" ]; then
- shift
- option="$1"
- VDEX_ARGS="${VDEX_ARGS} --compiler-filter=$option"
- shift
- elif [ "x$1" = "x--vdex-arg" ]; then
- shift
- VDEX_ARGS="${VDEX_ARGS} $1"
- shift
- elif [ "x$1" = "x--sync" ]; then
- SYNC_BEFORE_RUN="y"
- shift
- elif [ "x$1" = "x--profile" ]; then
- PROFILE="y"
- shift
- elif [ "x$1" = "x--random-profile" ]; then
- RANDOM_PROFILE="y"
- shift
- elif expr "x$1" : "x--" >/dev/null 2>&1; then
- error_msg "unknown $0 option: $1"
- exit 1
- else
- break
- fi
-done
-
-# HACK: Force the use of `signal_dumper` on host.
-if [[ "$HOST" = "y" ]]; then
- TIME_OUT="timeout"
-fi
-
-# If you change this, update the timeout in testrunner.py as well.
-if [ -z "$TIME_OUT_VALUE" ] ; then
- # 10 minutes is the default.
- TIME_OUT_VALUE=600
-
- # For sanitized builds use a larger base.
- # TODO: Consider sanitized target builds?
- if [ "x$SANITIZE_HOST" != "x" ] ; then
- TIME_OUT_VALUE=1500 # 25 minutes.
- fi
-
- TIME_OUT_VALUE=$((${TIME_OUT_VALUE} + ${TIME_OUT_EXTRA}))
-fi
-
-# Escape hatch for slow hosts or devices. Accept an environment variable as a timeout factor.
-if [ ! -z "$ART_TIME_OUT_MULTIPLIER" ] ; then
- TIME_OUT_VALUE=$((${TIME_OUT_VALUE} * ${ART_TIME_OUT_MULTIPLIER}))
-fi
-
-# The DEX_LOCATION with the chroot prefix, if any.
-CHROOT_DEX_LOCATION="$CHROOT$DEX_LOCATION"
-
-# If running on device, determine the ISA of the device.
-if [ "$HOST" = "n" -a "$USE_JVM" = "n" ]; then
- ISA=$("$ANDROID_BUILD_TOP/art/test/utils/get-device-isa" "$GET_DEVICE_ISA_BITNESS_FLAG")
-fi
-
-if [ "$USE_JVM" = "n" ]; then
- FLAGS="${FLAGS} ${ANDROID_FLAGS}"
- # we don't want to be trying to get adbconnections since the plugin might
- # not have been built.
- FLAGS="${FLAGS} -XjdwpProvider:none"
- for feature in ${EXPERIMENTAL}; do
- FLAGS="${FLAGS} -Xexperimental:${feature} -Xcompiler-option --runtime-arg -Xcompiler-option -Xexperimental:${feature}"
- COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xexperimental:${feature}"
- done
-fi
-
-if [ "$CREATE_ANDROID_ROOT" = "y" ]; then
- ANDROID_ROOT=$DEX_LOCATION/android-root
-fi
-
-if [ "x$1" = "x" ] ; then
- MAIN="Main"
-else
- MAIN="$1"
- shift
-fi
-
-if [ "$ZYGOTE" = "" ]; then
- if [ "$OPTIMIZE" = "y" ]; then
- if [ "$VERIFY" = "y" ]; then
- DEX_OPTIMIZE="-Xdexopt:verified"
- else
- DEX_OPTIMIZE="-Xdexopt:all"
- fi
- msg "Performing optimizations"
- else
- DEX_OPTIMIZE="-Xdexopt:none"
- msg "Skipping optimizations"
- fi
-
- if [ "$VERIFY" = "y" ]; then
- JVM_VERIFY_ARG="-Xverify:all"
- msg "Performing verification"
- elif [ "$VERIFY" = "s" ]; then
- JVM_VERIFY_ARG="Xverify:all"
- DEX_VERIFY="-Xverify:softfail"
- msg "Forcing verification to be soft fail"
- else # VERIFY = "n"
- DEX_VERIFY="-Xverify:none"
- JVM_VERIFY_ARG="-Xverify:none"
- msg "Skipping verification"
- fi
-fi
-
-msg "------------------------------"
-
-if [ "$DEBUGGER" = "y" ]; then
- # Use this instead for ddms and connect by running 'ddms':
- # DEBUGGER_OPTS="-XjdwpOptions=server=y,suspend=y -XjdwpProvider:adbconnection"
- # TODO: add a separate --ddms option?
-
- PORT=12345
- msg "Waiting for jdb to connect:"
- if [ "$HOST" = "n" ]; then
- msg " adb forward tcp:$PORT tcp:$PORT"
- fi
- msg " jdb -attach localhost:$PORT"
- if [ "$USE_JVM" = "n" ]; then
- # Use the default libjdwp agent. Use --debug-agent to use a custom one.
- DEBUGGER_OPTS="-agentpath:libjdwp.so=transport=dt_socket,address=$PORT,server=y,suspend=y -XjdwpProvider:internal"
- else
- DEBUGGER_OPTS="-agentlib:jdwp=transport=dt_socket,address=$PORT,server=y,suspend=y"
- fi
-elif [ "$DEBUGGER" = "agent" ]; then
- PORT=12345
- # TODO Support ddms connection and support target.
- if [ "$HOST" = "n" ]; then
- error_msg "--debug-agent not supported yet for target!"
- exit 1
- fi
- AGENTPATH=${DEBUGGER_AGENT}
- if [ "$WRAP_DEBUGGER_AGENT" = "y" ]; then
- WRAPPROPS="${ANDROID_ROOT}/${LIBRARY_DIRECTORY}/libwrapagentpropertiesd.so"
- if [ "$TEST_IS_NDEBUG" = "y" ]; then
- WRAPPROPS="${ANDROID_ROOT}/${LIBRARY_DIRECTORY}/libwrapagentproperties.so"
- fi
- AGENTPATH="${WRAPPROPS}=${ANDROID_BUILD_TOP}/art/tools/libjdwp-compat.props,${AGENTPATH}"
- fi
- msg "Connect to localhost:$PORT"
- DEBUGGER_OPTS="-agentpath:${AGENTPATH}=transport=dt_socket,address=$PORT,server=y,suspend=y"
-fi
-
-for agent in "${WITH_AGENT[@]}"; do
- FLAGS="${FLAGS} -agentpath:${agent}"
-done
-
-if [ "$USE_JVMTI" = "y" ]; then
- if [ "$USE_JVM" = "n" ]; then
- plugin=libopenjdkjvmtid.so
- if [[ "$TEST_IS_NDEBUG" = "y" ]]; then
- plugin=libopenjdkjvmti.so
- fi
- # We used to add flags here that made the runtime debuggable but that is not
- # needed anymore since the plugin can do it for us now.
- FLAGS="${FLAGS} -Xplugin:${plugin}"
-
- # For jvmti tests, set the threshold of compilation to 1, so we jit early to
- # provide better test coverage for jvmti + jit. This means we won't run
- # the default --jit configuration but it is not too important test scenario for
- # jvmti tests. This is art specific flag, so don't use it with jvm.
- FLAGS="${FLAGS} -Xjitthreshold:1"
- fi
-fi
-
-# Add the libdir to the argv passed to the main function.
-if [ "$ADD_LIBDIR_ARGUMENTS" = "y" ]; then
- if [[ "$HOST" = "y" ]]; then
- ARGS="${ARGS} ${ANDROID_HOST_OUT}/${TEST_DIRECTORY}/"
- else
- ARGS="${ARGS} /data/${TEST_DIRECTORY}/art/${ISA}/"
- fi
-fi
-if [ "$IS_JVMTI_TEST" = "y" ]; then
- agent=libtiagentd.so
- lib=tiagentd
- if [[ "$TEST_IS_NDEBUG" = "y" ]]; then
- agent=libtiagent.so
- lib=tiagent
- fi
-
- ARGS="${ARGS} ${lib}"
- if [[ "$USE_JVM" = "y" ]]; then
- FLAGS="${FLAGS} -agentpath:${ANDROID_HOST_OUT}/nativetest64/${agent}=${TEST_NAME},jvm"
- else
- FLAGS="${FLAGS} -agentpath:${agent}=${TEST_NAME},art"
- fi
-fi
-
-if [[ "$JVMTI_STRESS" = "y" ]]; then
- agent=libtistressd.so
- if [[ "$TEST_IS_NDEBUG" = "y" ]]; then
- agent=libtistress.so
- fi
-
- # Just give it a default start so we can always add ',' to it.
- agent_args="jvmti-stress"
- if [[ "$JVMTI_REDEFINE_STRESS" = "y" ]]; then
- # We really cannot do this on RI so don't both passing it in that case.
- if [[ "$USE_JVM" = "n" ]]; then
- agent_args="${agent_args},redefine"
- fi
- fi
- if [[ "$JVMTI_FIELD_STRESS" = "y" ]]; then
- agent_args="${agent_args},field"
- fi
- if [[ "$JVMTI_STEP_STRESS" = "y" ]]; then
- agent_args="${agent_args},step"
- fi
- if [[ "$JVMTI_TRACE_STRESS" = "y" ]]; then
- agent_args="${agent_args},trace"
- fi
- # In the future add onto this;
- if [[ "$USE_JVM" = "y" ]]; then
- FLAGS="${FLAGS} -agentpath:${ANDROID_HOST_OUT}/nativetest64/${agent}=${agent_args}"
- else
- FLAGS="${FLAGS} -agentpath:${agent}=${agent_args}"
- fi
-fi
-
-if [ "$USE_JVM" = "y" ]; then
- export LD_LIBRARY_PATH=${ANDROID_HOST_OUT}/lib64
- # Some jvmti tests are flaky without -Xint on the RI.
- if [ "$IS_JVMTI_TEST" = "y" ]; then
- FLAGS="${FLAGS} -Xint"
- fi
- # Xmx is necessary since we don't pass down the ART flags to JVM.
- # We pass the classes2 path whether it's used (src-multidex) or not.
- cmdline="${JAVA} ${DEBUGGER_OPTS} ${JVM_VERIFY_ARG} -Xmx256m -classpath classes:classes2 ${FLAGS} $MAIN $@ ${ARGS}"
- if [ "$DEV_MODE" = "y" ]; then
- echo $cmdline
- fi
- if [ "$CREATE_RUNNER" = "y" ]; then
- echo "#!/bin/bash" > runit.sh
- echo "export LD_LIBRARY_PATH=\"$LD_LIBRARY_PATH\""
- echo $cmdline >> runit.sh
- chmod u+x runit.sh
- echo "Runnable test script written to $PWD/runit.sh"
- else
- $cmdline
- fi
- exit
-fi
-
-readonly b_path=$(get_apex_bootclasspath ${HOST})
-readonly b_path_locations=$(get_apex_bootclasspath_locations ${HOST})
-
-BCPEX=
-if [ -f "$TEST_NAME-bcpex.jar" ] ; then
- BCPEX=":$DEX_LOCATION/$TEST_NAME-bcpex.jar"
-fi
-
-# Pass down the bootclasspath
-FLAGS="${FLAGS} -Xbootclasspath:${b_path}${BCPEX}"
-FLAGS="${FLAGS} -Xbootclasspath-locations:${b_path_locations}${BCPEX}"
-COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xbootclasspath:${b_path}"
-COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xbootclasspath-locations:${b_path_locations}"
-
-if [ "$HAVE_IMAGE" = "n" ]; then
- # Disable image dex2oat - this will forbid the runtime to patch or compile an image.
- FLAGS="${FLAGS} -Xnoimage-dex2oat"
-
- # We'll abuse a second flag here to test different behavior. If --relocate, use the
- # existing image - relocation will fail as patching is disallowed. If --no-relocate,
- # pass a non-existent image - compilation will fail as dex2oat is disallowed.
- if [ "${RELOCATE}" = "n" ] ; then
- BOOT_IMAGE="/system/non-existent/boot.art"
- fi
- # App images cannot be generated without a boot image.
- APP_IMAGE="n"
-fi
-DALVIKVM_BOOT_OPT="-Ximage:${BOOT_IMAGE}"
-
-if [ "$USE_GDB_DEX2OAT" = "y" ]; then
- if [ "$HOST" = "n" ]; then
- echo "The --gdb-dex2oat option is not yet implemented for target." >&2
- exit 1
- fi
-fi
-
-if [ "$USE_GDB" = "y" ]; then
- if [ "$USE_GDBSERVER" = "y" ]; then
- error_msg "Cannot pass both --gdb and --gdbserver at the same time!"
- exit 1
- elif [ "$HOST" = "n" ]; then
- # We might not have any hostname resolution if we are using a chroot.
- GDB="$GDBSERVER_DEVICE --no-startup-with-shell 127.0.0.1$GDBSERVER_PORT"
- else
- if [ `uname` = "Darwin" ]; then
- GDB=lldb
- GDB_ARGS="$GDB_ARGS -- $DALVIKVM"
- DALVIKVM=
- else
- GDB=gdb
- GDB_ARGS="$GDB_ARGS --args $DALVIKVM"
- # Enable for Emacs "M-x gdb" support. TODO: allow extra gdb arguments on command line.
- # gdbargs="--annotate=3 $gdbargs"
- fi
- fi
-elif [ "$USE_GDBSERVER" = "y" ]; then
- if [ "$HOST" = "n" ]; then
- # We might not have any hostname resolution if we are using a chroot.
- GDB="$GDBSERVER_DEVICE --no-startup-with-shell 127.0.0.1$GDBSERVER_PORT"
- else
- GDB="$GDBSERVER_HOST $GDBSERVER_PORT"
- fi
-fi
-
-if [ "$INTERPRETER" = "y" ]; then
- INT_OPTS="${INT_OPTS} -Xint"
-fi
-
-if [ "$JIT" = "y" ]; then
- INT_OPTS="${INT_OPTS} -Xusejit:true"
-else
- INT_OPTS="${INT_OPTS} -Xusejit:false"
-fi
-
-if [ "$INTERPRETER" = "y" ] || [ "$JIT" = "y" ]; then
- if [ "$VERIFY" = "y" ] ; then
- INT_OPTS="${INT_OPTS} -Xcompiler-option --compiler-filter=verify"
- COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=verify"
- elif [ "$VERIFY" = "s" ]; then
- INT_OPTS="${INT_OPTS} -Xcompiler-option --compiler-filter=extract"
- COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=extract"
- DEX_VERIFY="${DEX_VERIFY} -Xverify:softfail"
- else # VERIFY = "n"
- INT_OPTS="${INT_OPTS} -Xcompiler-option --compiler-filter=assume-verified"
- COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=assume-verified"
- DEX_VERIFY="${DEX_VERIFY} -Xverify:none"
- fi
-fi
-
-JNI_OPTS="-Xjnigreflimit:512 -Xcheck:jni"
-
-COMPILE_FLAGS="${COMPILE_FLAGS} --runtime-arg -Xnorelocate"
-if [ "$RELOCATE" = "y" ]; then
- FLAGS="${FLAGS} -Xrelocate"
-else
- FLAGS="$FLAGS -Xnorelocate"
-fi
-
-if [ "$BIONIC" = "y" ]; then
- # This is the location that soong drops linux_bionic builds. Despite being
- # called linux_bionic-x86 the build is actually amd64 (x86_64) only.
- if [ ! -e "$OUT_DIR/soong/host/linux_bionic-x86" ]; then
- error_msg "linux_bionic-x86 target doesn't seem to have been built!"
- exit 1
- fi
- # Set TIMEOUT_DUMPER manually so it works even with apex's
- TIMEOUT_DUMPER=$OUT_DIR/soong/host/linux_bionic-x86/bin/signal_dumper
-fi
-
-# Prevent test from silently falling back to interpreter in no-prebuild mode. This happens
-# when DEX_LOCATION path is too long, because vdex/odex filename is constructed by taking
-# full path to dex, stripping leading '/', appending '@classes.vdex' and changing every
-# remaining '/' into '@'.
-if [ "$HOST" = "y" ]; then
- max_filename_size=$(getconf NAME_MAX $DEX_LOCATION)
-else
- # There is no getconf on device, fallback to standard value.
- # See NAME_MAX in kernel <linux/limits.h>
- max_filename_size=255
-fi
-# Compute VDEX_NAME.
-DEX_LOCATION_STRIPPED="${DEX_LOCATION#/}"
-VDEX_NAME="${DEX_LOCATION_STRIPPED//\//@}@$TEST_NAME.jar@classes.vdex"
-if [ ${#VDEX_NAME} -gt $max_filename_size ]; then
- echo "Dex location path too long:"
- error_msg "$VDEX_NAME is ${#VDEX_NAME} character long, and the limit is $max_filename_size."
- exit 1
-fi
-
-if [ "$HOST" = "y" ]; then
- # On host, run binaries (`dex2oat(d)`, `dalvikvm`, `profman`) from the `bin`
- # directory under the "Android Root" (usually `out/host/linux-x86`).
- #
- # TODO(b/130295968): Adjust this if/when ART host artifacts are installed
- # under the ART root (usually `out/host/linux-x86/com.android.art`).
- ANDROID_ART_BIN_DIR=$ANDROID_ROOT/bin
-else
- # On target, run binaries (`dex2oat(d)`, `dalvikvm`, `profman`) from the ART
- # APEX's `bin` directory. This means the linker will observe the ART APEX
- # linker configuration file (`/apex/com.android.art/etc/ld.config.txt`) for
- # these binaries.
- ANDROID_ART_BIN_DIR=$ANDROID_ART_ROOT/bin
-fi
-
-profman_cmdline="true"
-dex2oat_cmdline="true"
-vdex_cmdline="true"
-dm_cmdline="true"
-mkdir_locations="${DEX_LOCATION}/dalvik-cache/$ISA"
-strip_cmdline="true"
-sync_cmdline="true"
-linkroot_cmdline="true"
-linkroot_overlay_cmdline="true"
-setupapex_cmdline="true"
-installapex_cmdline="true"
-installapex_test_cmdline="true"
-
-linkdirs() {
- find "$1" -maxdepth 1 -mindepth 1 -type d | xargs -i ln -sf '{}' "$2"
- # Also create a link for the boot image.
- ln -sf $ANDROID_HOST_OUT/apex/art_boot_images "$2"
-}
-
-if [ "$CREATE_ANDROID_ROOT" = "y" ]; then
- mkdir_locations="${mkdir_locations} ${ANDROID_ROOT}"
- linkroot_cmdline="linkdirs ${ANDROID_HOST_OUT} ${ANDROID_ROOT}"
- if [ "${BIONIC}" = "y" ]; then
- # TODO Make this overlay more generic.
- linkroot_overlay_cmdline="linkdirs $OUT_DIR/soong/host/linux_bionic-x86 ${ANDROID_ROOT}"
- fi
- # Replace the boot image to a location expected by the runtime.
- DALVIKVM_BOOT_OPT="-Ximage:${ANDROID_ROOT}/art_boot_images/javalib/boot.art"
-fi
-
-if [ "$USE_ZIPAPEX" = "y" ]; then
- # TODO Currently this only works for linux_bionic zipapexes because those are
- # stripped and so small enough that the ulimit doesn't kill us.
- mkdir_locations="${mkdir_locations} $DEX_LOCATION/zipapex"
- zip_options="-qq"
- if [ "$DEV_MODE" = "y" ]; then
- zip_options=""
- fi
- setupapex_cmdline="unzip -o -u ${zip_options} ${ZIPAPEX_LOC} apex_payload.zip -d ${DEX_LOCATION}"
- installapex_cmdline="unzip -o -u ${zip_options} ${DEX_LOCATION}/apex_payload.zip -d ${DEX_LOCATION}/zipapex"
- ANDROID_ART_BIN_DIR=$DEX_LOCATION/zipapex/bin
-elif [ "$USE_EXTRACTED_ZIPAPEX" = "y" ]; then
- # Just symlink the zipapex binaries
- ANDROID_ART_BIN_DIR=$DEX_LOCATION/zipapex/bin
- # Force since some tests manually run this file twice.
- ln_options=""
- if [ "$DEV_MODE" = "y" ]; then
- ln_options="--verbose"
- fi
- # If the ${RUN} is executed multiple times we don't need to recreate the link
- installapex_test_cmdline="test -L ${DEX_LOCATION}/zipapex"
- installapex_cmdline="ln -s -f ${ln_options} ${EXTRACTED_ZIPAPEX_LOC} ${DEX_LOCATION}/zipapex"
-fi
-
-# PROFILE takes precedence over RANDOM_PROFILE, since PROFILE tests require a
-# specific profile to run properly.
-if [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then
- profman_cmdline="$ANDROID_ART_BIN_DIR/profman \
- --apk=$DEX_LOCATION/$TEST_NAME.jar \
- --dex-location=$DEX_LOCATION/$TEST_NAME.jar"
- if [ -f "$TEST_NAME-ex.jar" ] && [ "$SECONDARY_COMPILATION" = "y" ] ; then
- profman_cmdline="${profman_cmdline} \
- --apk=$DEX_LOCATION/$TEST_NAME-ex.jar \
- --dex-location=$DEX_LOCATION/$TEST_NAME-ex.jar"
- fi
- COMPILE_FLAGS="${COMPILE_FLAGS} --profile-file=$DEX_LOCATION/$TEST_NAME.prof"
- FLAGS="${FLAGS} -Xcompiler-option --profile-file=$DEX_LOCATION/$TEST_NAME.prof"
- if [ "$PROFILE" = "y" ]; then
- profman_cmdline="${profman_cmdline} --create-profile-from=$DEX_LOCATION/profile \
- --reference-profile-file=$DEX_LOCATION/$TEST_NAME.prof"
- else
- profman_cmdline="${profman_cmdline} --generate-test-profile=$DEX_LOCATION/$TEST_NAME.prof \
- --generate-test-profile-seed=0"
- fi
-fi
-
-function get_prebuilt_lldb_path {
- local CLANG_BASE="prebuilts/clang/host"
- local CLANG_VERSION="$("$ANDROID_BUILD_TOP/build/soong/scripts/get_clang_version.py")"
- case "$(uname -s)" in
- Darwin)
- local PREBUILT_NAME="darwin-x86"
- ;;
- Linux)
- local PREBUILT_NAME="linux-x86"
- ;;
- *)
- >&2 echo "Unknown host $(uname -s). Unsupported for debugging dex2oat with LLDB."
- return
- ;;
- esac
- local CLANG_PREBUILT_HOST_PATH="$ANDROID_BUILD_TOP/$CLANG_BASE/$PREBUILT_NAME/$CLANG_VERSION"
- # If the clang prebuilt directory exists and the reported clang version
- # string does not, then it is likely that the clang version reported by the
- # get_clang_version.py script does not match the expected directory name.
- if [ -d "${ANDROID_BUILD_TOP}/${CLANG_BASE}/${PREBUILT_NAME}" ] && \
- [ ! -d "${CLANG_PREBUILT_HOST_PATH}" ]; then
- error_msg "The prebuilt clang directory exists, but the specific clang"\
- "\nversion reported by get_clang_version.py does not exist in that path."\
- "\nPlease make sure that the reported clang version resides in the"\
- "\nprebuilt clang directory!"
- exit 1
- fi
-
- # The lldb-server binary is a dependency of lldb.
- export LLDB_DEBUGSERVER_PATH="${CLANG_PREBUILT_HOST_PATH}/runtimes_ndk_cxx/x86_64/lldb-server"
-
- # Set the current terminfo directory to TERMINFO so that LLDB can read the
- # termcap database.
- local terminfo_regexp_path='\/.*\/*terminfo\/'
- if [[ $(infocmp) =~ $terminfo_regexp_path ]] ; then
- export TERMINFO="${BASH_REMATCH[0]}"
- fi
-
- prebuilt_lldb_path="$CLANG_PREBUILT_HOST_PATH/bin/lldb.sh"
-}
-
-function write_dex2oat_cmdlines {
- local name="$1"
-
- local class_loader_context=""
- local enable_app_image=false
- if [ "$APP_IMAGE" = "y" ]; then
- enable_app_image=true
- fi
-
- # If the name ends in -ex then this is a secondary dex file
- if [ "${name:${#name}-3}" = "-ex" ]; then
- # Lazily realize the default value in case DEX_LOCATION/TEST_NAME change
- if [ "x$SECONDARY_CLASS_LOADER_CONTEXT" = "x" ]; then
- if [ "x$SECONDARY_DEX" = "x" ]; then
- # Tests without `--secondary` load the "-ex" jar in a separate PathClassLoader
- # that is a child of the main PathClassLoader. If the class loader is constructed
- # in any other way, the test needs to specify the secondary CLC explicitly.
- SECONDARY_CLASS_LOADER_CONTEXT="PCL[];PCL[$DEX_LOCATION/$TEST_NAME.jar]"
- else
- # Tests with `--secondary` load the `-ex` jar a part of the main PathClassLoader.
- SECONDARY_CLASS_LOADER_CONTEXT="PCL[$DEX_LOCATION/$TEST_NAME.jar]"
- fi
- fi
- class_loader_context="'--class-loader-context=$SECONDARY_CLASS_LOADER_CONTEXT'"
- $enable_app_image && [ "$SECONDARY_APP_IMAGE" = "y" ] || enable_app_image=false
- fi
-
- local app_image=""
- $enable_app_image && app_image="--app-image-file=$DEX_LOCATION/oat/$ISA/$name.art --resolve-startup-const-strings=true"
-
- if [ "$USE_GDB_DEX2OAT" = "y" ]; then
- get_prebuilt_lldb_path
- GDB_DEX2OAT="$prebuilt_lldb_path -f"
- GDB_DEX2OAT_ARGS+=" -- "
- fi
-
- local dex2oat_binary
- dex2oat_binary=${DEX2OAT_DEBUG_BINARY}
- if [[ "$TEST_IS_NDEBUG" = "y" ]]; then
- dex2oat_binary=${DEX2OAT_NDEBUG_BINARY}
- fi
- dex2oat_cmdline="$INVOKE_WITH $GDB_DEX2OAT \
- $ANDROID_ART_BIN_DIR/$dex2oat_binary \
- $GDB_DEX2OAT_ARGS \
- $COMPILE_FLAGS \
- --boot-image=${BOOT_IMAGE} \
- --dex-file=$DEX_LOCATION/$name.jar \
- --oat-file=$DEX_LOCATION/oat/$ISA/$name.odex \
- "$app_image" \
- --generate-mini-debug-info \
- --instruction-set=$ISA \
- $class_loader_context"
- if [ "x$INSTRUCTION_SET_FEATURES" != "x" ] ; then
- dex2oat_cmdline="${dex2oat_cmdline} --instruction-set-features=${INSTRUCTION_SET_FEATURES}"
- fi
-
- # Add in a timeout. This is important for testing the compilation/verification time of
- # pathological cases. We do not append a timeout when debugging dex2oat because we
- # do not want it to exit while debugging.
- # Note: as we don't know how decent targets are (e.g., emulator), only do this on the host for
- # now. We should try to improve this.
- # The current value is rather arbitrary. run-tests should compile quickly.
- # Watchdog timeout is in milliseconds so add 3 '0's to the dex2oat timeout.
- if [ "$HOST" != "n" ] && [ "$USE_GDB_DEX2OAT" != "y" ]; then
- # Use SIGRTMIN+2 to try to dump threads.
- # Use -k 1m to SIGKILL it a minute later if it hasn't ended.
- dex2oat_cmdline="timeout -k ${DEX2OAT_TIMEOUT}s -s SIGRTMIN+2 ${DEX2OAT_RT_TIMEOUT}s ${dex2oat_cmdline} --watchdog-timeout=${DEX2OAT_TIMEOUT}000"
- fi
- if [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then
- vdex_cmdline="${dex2oat_cmdline} ${VDEX_ARGS} --input-vdex=$DEX_LOCATION/oat/$ISA/$name.vdex --output-vdex=$DEX_LOCATION/oat/$ISA/$name.vdex"
- elif [ "$TEST_VDEX" = "y" ]; then
- if [ "$VDEX_ARGS" = "" ]; then
- # If no arguments need to be passed, just delete the odex file so that the runtime only picks up the vdex file.
- vdex_cmdline="rm $DEX_LOCATION/oat/$ISA/$name.odex"
- else
- vdex_cmdline="${dex2oat_cmdline} ${VDEX_ARGS} --input-vdex=$DEX_LOCATION/oat/$ISA/$name.vdex"
- fi
- elif [ "$TEST_DEX2OAT_DM" = "y" ]; then
- vdex_cmdline="${dex2oat_cmdline} ${VDEX_ARGS} --dump-timings --dm-file=$DEX_LOCATION/oat/$ISA/$name.dm"
- dex2oat_cmdline="${dex2oat_cmdline} --copy-dex-files=false --output-vdex=$DEX_LOCATION/oat/$ISA/primary.vdex"
- dm_cmdline="zip -qj $DEX_LOCATION/oat/$ISA/$name.dm $DEX_LOCATION/oat/$ISA/primary.vdex"
- elif [ "$TEST_RUNTIME_DM" = "y" ]; then
- dex2oat_cmdline="${dex2oat_cmdline} --copy-dex-files=false --output-vdex=$DEX_LOCATION/oat/$ISA/primary.vdex"
- dm_cmdline="zip -qj $DEX_LOCATION/$name.dm $DEX_LOCATION/oat/$ISA/primary.vdex"
- fi
-}
-
-# Enable mini-debug-info for JIT (if JIT is used).
-FLAGS="$FLAGS -Xcompiler-option --generate-mini-debug-info"
-
-if [ "$PREBUILD" = "y" ]; then
- mkdir_locations="${mkdir_locations} ${DEX_LOCATION}/oat/$ISA"
-
- # "Primary".
- write_dex2oat_cmdlines "$TEST_NAME"
- dex2oat_cmdline=$(echo $dex2oat_cmdline)
- dm_cmdline=$(echo $dm_cmdline)
- vdex_cmdline=$(echo $vdex_cmdline)
-
- # Enable mini-debug-info for JIT (if JIT is used).
- FLAGS="$FLAGS -Xcompiler-option --generate-mini-debug-info"
-
- if [ -f "$TEST_NAME-ex.jar" ] && [ "$SECONDARY_COMPILATION" = "y" ] ; then
- # "Secondary" for test coverage.
-
- # Store primary values.
- base_dex2oat_cmdline="$dex2oat_cmdline"
- base_dm_cmdline="$dm_cmdline"
- base_vdex_cmdline="$vdex_cmdline"
-
- write_dex2oat_cmdlines "$TEST_NAME-ex"
- dex2oat_cmdline=$(echo $dex2oat_cmdline)
- dm_cmdline=$(echo $dm_cmdline)
- vdex_cmdline=$(echo $vdex_cmdline)
-
- # Concatenate.
- dex2oat_cmdline="$base_dex2oat_cmdline && $dex2oat_cmdline"
- dm_cmdline="$base_dm_cmdline" # Only use primary dm.
- vdex_cmdline="$base_vdex_cmdline && $vdex_cmdline"
- fi
-fi
-
-if [ "$SYNC_BEFORE_RUN" = "y" ]; then
- sync_cmdline="sync"
-fi
-
-DALVIKVM_ISA_FEATURES_ARGS=""
-if [ "x$INSTRUCTION_SET_FEATURES" != "x" ] ; then
- DALVIKVM_ISA_FEATURES_ARGS="-Xcompiler-option --instruction-set-features=${INSTRUCTION_SET_FEATURES}"
-fi
-
-# java.io.tmpdir can only be set at launch time.
-TMP_DIR_OPTION=""
-if [ "$HOST" = "n" ]; then
- TMP_DIR_OPTION="-Djava.io.tmpdir=/data/local/tmp"
-fi
-
-# The build servers have an ancient version of bash so we cannot use @Q.
-if [ "$USE_GDBSERVER" == "y" ]; then
- printf -v QUOTED_DALVIKVM_BOOT_OPT "%q" "$DALVIKVM_BOOT_OPT"
-else
- QUOTED_DALVIKVM_BOOT_OPT="$DALVIKVM_BOOT_OPT"
-fi
-
-DALVIKVM_CLASSPATH=$DEX_LOCATION/$TEST_NAME.jar
-if [ -f "$TEST_NAME-aotex.jar" ] ; then
- DALVIKVM_CLASSPATH=$DALVIKVM_CLASSPATH:$DEX_LOCATION/$TEST_NAME-aotex.jar
-fi
-DALVIKVM_CLASSPATH=$DALVIKVM_CLASSPATH$SECONDARY_DEX
-
-# We set DumpNativeStackOnSigQuit to false to avoid stressing libunwind.
-# b/27185632
-# b/24664297
-
-dalvikvm_cmdline="$INVOKE_WITH $GDB $ANDROID_ART_BIN_DIR/$DALVIKVM \
- $GDB_ARGS \
- $FLAGS \
- $DEX_VERIFY \
- -XXlib:$LIB \
- $DEX2OAT \
- $DALVIKVM_ISA_FEATURES_ARGS \
- $ZYGOTE \
- $JNI_OPTS \
- $INT_OPTS \
- $DEBUGGER_OPTS \
- ${QUOTED_DALVIKVM_BOOT_OPT} \
- $TMP_DIR_OPTION \
- -XX:DumpNativeStackOnSigQuit:false \
- -cp $DALVIKVM_CLASSPATH $MAIN $ARGS"
-
-if [ "x$SIMPLEPERF" == xyes ]; then
- dalvikvm_cmdline="simpleperf record ${dalvikvm_cmdline} && simpleperf report"
-fi
-
-sanitize_dex2oat_cmdline() {
- local args=()
- for arg in "$@"; do
- if [ "$arg" = "--class-loader-context=&" ]; then
- arg="--class-loader-context=\&"
- fi
- args+=("$arg")
- done
- echo -n "${args[@]}"
-}
-
-# Remove whitespace.
-dex2oat_cmdline=$(sanitize_dex2oat_cmdline $(echo $dex2oat_cmdline))
-dalvikvm_cmdline=$(echo $dalvikvm_cmdline)
-dm_cmdline=$(echo $dm_cmdline)
-vdex_cmdline=$(sanitize_dex2oat_cmdline $(echo $vdex_cmdline))
-profman_cmdline=$(echo $profman_cmdline)
-
-# Use an empty ASAN_OPTIONS to enable defaults.
-# Note: this is required as envsetup right now exports detect_leaks=0.
-RUN_TEST_ASAN_OPTIONS=""
-
-# Multiple shutdown leaks. b/38341789
-if [ "x$RUN_TEST_ASAN_OPTIONS" != "x" ] ; then
- RUN_TEST_ASAN_OPTIONS="${RUN_TEST_ASAN_OPTIONS}:"
-fi
-RUN_TEST_ASAN_OPTIONS="${RUN_TEST_ASAN_OPTIONS}detect_leaks=0"
-
-# For running, we must turn off logging when dex2oat is missing. Otherwise we use
-# the same defaults as for prebuilt: everything when --dev, otherwise errors and above only.
-if [ "$EXTERNAL_LOG_TAGS" = "n" ]; then
- if [ "$DEV_MODE" = "y" ]; then
- export ANDROID_LOG_TAGS='*:d'
- elif [ "$HAVE_IMAGE" = "n" ]; then
- # All tests would log the error of missing image. Be silent here and only log fatal
- # events.
- export ANDROID_LOG_TAGS='*:s'
- else
- # We are interested in LOG(ERROR) output.
- export ANDROID_LOG_TAGS='*:e'
- fi
-fi
-
-if [ "$HOST" = "n" ]; then
- adb root > /dev/null
- adb wait-for-device
- if [ "$QUIET" = "n" ]; then
- adb shell rm -rf $CHROOT_DEX_LOCATION
- adb shell mkdir -p $CHROOT_DEX_LOCATION
- adb push $TEST_NAME.jar $CHROOT_DEX_LOCATION
- adb push $TEST_NAME-ex.jar $CHROOT_DEX_LOCATION
- adb push $TEST_NAME-aotex.jar $CHROOT_DEX_LOCATION
- adb push $TEST_NAME-bcpex.jar $CHROOT_DEX_LOCATION
- if [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then
- adb push profile $CHROOT_DEX_LOCATION
- fi
- # Copy resource folder
- if [ -d res ]; then
- adb push res $CHROOT_DEX_LOCATION
- fi
- else
- adb shell rm -rf $CHROOT_DEX_LOCATION >/dev/null 2>&1
- adb shell mkdir -p $CHROOT_DEX_LOCATION >/dev/null 2>&1
- adb push $TEST_NAME.jar $CHROOT_DEX_LOCATION >/dev/null 2>&1
- adb push $TEST_NAME-ex.jar $CHROOT_DEX_LOCATION >/dev/null 2>&1
- adb push $TEST_NAME-aotex.jar $CHROOT_DEX_LOCATION >/dev/null 2>&1
- adb push $TEST_NAME-bcpex.jar $CHROOT_DEX_LOCATION >/dev/null 2>&1
- if [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then
- adb push profile $CHROOT_DEX_LOCATION >/dev/null 2>&1
- fi
- # Copy resource folder
- if [ -d res ]; then
- adb push res $CHROOT_DEX_LOCATION >/dev/null 2>&1
- fi
- fi
-
- # Populate LD_LIBRARY_PATH.
- LD_LIBRARY_PATH=
- if [ "$ANDROID_ROOT" != "/system" ]; then
- # Current default installation is dalvikvm 64bits and dex2oat 32bits,
- # so we can only use LD_LIBRARY_PATH when testing on a local
- # installation.
- LD_LIBRARY_PATH="$ANDROID_ROOT/$LIBRARY_DIRECTORY"
- fi
-
- # This adds libarttest(d).so to the default linker namespace when dalvikvm
- # is run from /apex/com.android.art/bin. Since that namespace is essentially
- # an alias for the com_android_art namespace, that gives libarttest(d).so
- # full access to the internal ART libraries.
- LD_LIBRARY_PATH="/data/$TEST_DIRECTORY/com.android.art/lib${SUFFIX64}:$LD_LIBRARY_PATH"
- if [ "$TEST_IS_NDEBUG" = "y" ]; then dlib=""; else dlib="d"; fi
- art_test_internal_libraries=(
- libartagent${dlib}.so
- libarttest${dlib}.so
- libtiagent${dlib}.so
- libtistress${dlib}.so
- )
- art_test_internal_libraries="${art_test_internal_libraries[*]}"
- NATIVELOADER_DEFAULT_NAMESPACE_LIBS="${art_test_internal_libraries// /:}"
- dlib=
- art_test_internal_libraries=
-
- # Needed to access the test's Odex files.
- LD_LIBRARY_PATH="$DEX_LOCATION/oat/$ISA:$LD_LIBRARY_PATH"
- # Needed to access the test's native libraries (see e.g. 674-hiddenapi,
- # which generates `libhiddenapitest_*.so` libraries in `$DEX_LOCATION`).
- LD_LIBRARY_PATH="$DEX_LOCATION:$LD_LIBRARY_PATH"
-
- # Prepend directories to the path on device.
- PREPEND_TARGET_PATH=$ANDROID_ART_BIN_DIR
- if [ "$ANDROID_ROOT" != "/system" ]; then
- PREPEND_TARGET_PATH="$PREPEND_TARGET_PATH:$ANDROID_ROOT/bin"
- fi
-
- timeout_dumper_cmd=
-
- # Check whether signal_dumper is available.
- if [ "$TIMEOUT_DUMPER" = signal_dumper ] ; then
- # Chroot? Use as prefix for tests.
- TIMEOUT_DUMPER_PATH_PREFIX=
- if [ -n "$CHROOT" ]; then
- TIMEOUT_DUMPER_PATH_PREFIX="$CHROOT/"
- fi
-
- # Testing APEX?
- if adb shell "test -x ${TIMEOUT_DUMPER_PATH_PREFIX}/apex/com.android.art/bin/signal_dumper" ; then
- TIMEOUT_DUMPER="/apex/com.android.art/bin/signal_dumper"
- # Is it in /system/bin?
- elif adb shell "test -x ${TIMEOUT_DUMPER_PATH_PREFIX}/system/bin/signal_dumper" ; then
- TIMEOUT_DUMPER="/system/bin/signal_dumper"
- else
- TIMEOUT_DUMPER=
- fi
- else
- TIMEOUT_DUMPER=
- fi
-
- if [ ! -z "$TIMEOUT_DUMPER" ] ; then
- # Use "-l" to dump to logcat. That is convenience for the build bot crash symbolization.
- # Use exit code 124 for toybox timeout (b/141007616).
- timeout_dumper_cmd="${TIMEOUT_DUMPER} -l -s 15 -e 124"
- fi
-
- timeout_prefix=
- if [ "$TIME_OUT" = "timeout" ]; then
- # Add timeout command if time out is desired.
- #
- # Note: We first send SIGTERM (the timeout default, signal 15) to the signal dumper, which
- # will induce a full thread dump before killing the process. To ensure any issues in
- # dumping do not lead to a deadlock, we also use the "-k" option to definitely kill the
- # child.
- # Note: Using "--foreground" to not propagate the signal to children, i.e., the runtime.
- timeout_prefix="timeout --foreground -k 120s ${TIME_OUT_VALUE}s ${timeout_dumper_cmd} $cmdline"
- fi
-
- # Create a script with the command. The command can get longer than the longest
- # allowed adb command and there is no way to get the exit status from a adb shell
- # command. Dalvik cache is cleaned before running to make subsequent executions
- # of the script follow the same runtime path.
- cmdline="cd $DEX_LOCATION && \
- export ASAN_OPTIONS=$RUN_TEST_ASAN_OPTIONS && \
- export ANDROID_DATA=$DEX_LOCATION && \
- export DEX_LOCATION=$DEX_LOCATION && \
- export ANDROID_ROOT=$ANDROID_ROOT && \
- export ANDROID_I18N_ROOT=$ANDROID_I18N_ROOT && \
- export ANDROID_ART_ROOT=$ANDROID_ART_ROOT && \
- export ANDROID_TZDATA_ROOT=$ANDROID_TZDATA_ROOT && \
- export ANDROID_LOG_TAGS=$ANDROID_LOG_TAGS && \
- rm -rf ${DEX_LOCATION}/dalvik-cache/ && \
- mkdir -p ${mkdir_locations} && \
- export LD_LIBRARY_PATH=$LD_LIBRARY_PATH && \
- export NATIVELOADER_DEFAULT_NAMESPACE_LIBS=$NATIVELOADER_DEFAULT_NAMESPACE_LIBS && \
- export PATH=$PREPEND_TARGET_PATH:\$PATH && \
- $profman_cmdline && \
- $dex2oat_cmdline && \
- $dm_cmdline && \
- $vdex_cmdline && \
- $strip_cmdline && \
- $sync_cmdline && \
- $timeout_prefix $dalvikvm_cmdline"
-
- cmdfile=$(mktemp cmd-XXXX --suffix "-$TEST_NAME")
- echo "$cmdline" >> $cmdfile
-
- if [ "$DEV_MODE" = "y" ]; then
- echo $cmdline
- if [ "$USE_GDB" = "y" ] || [ "$USE_GDBSERVER" = "y" ]; then
- echo "Forward ${GDBSERVER_PORT} to local port and connect GDB"
- fi
- fi
-
- if [ "$QUIET" = "n" ]; then
- adb push $cmdfile $CHROOT_DEX_LOCATION/cmdline.sh
- else
- adb push $cmdfile $CHROOT_DEX_LOCATION/cmdline.sh >/dev/null 2>&1
- fi
-
- exit_status=0
- if [ "$DRY_RUN" != "y" ]; then
- if [ -n "$CHROOT" ]; then
- adb shell chroot "$CHROOT" sh $DEX_LOCATION/cmdline.sh
- else
- adb shell sh $DEX_LOCATION/cmdline.sh
- fi
- exit_status=$?
- fi
-
- rm -f $cmdfile
- exit $exit_status
-else
- # Host run.
- export ANDROID_PRINTF_LOG=brief
-
- export ANDROID_DATA="$DEX_LOCATION"
- export ANDROID_ROOT="${ANDROID_ROOT}"
- export ANDROID_I18N_ROOT="${ANDROID_I18N_ROOT}"
- export ANDROID_ART_ROOT="${ANDROID_ART_ROOT}"
- export ANDROID_TZDATA_ROOT="${ANDROID_TZDATA_ROOT}"
- if [ "$USE_ZIPAPEX" = "y" ] || [ "$USE_EXRACTED_ZIPAPEX" = "y" ]; then
- # Put the zipapex files in front of the ld-library-path
- export LD_LIBRARY_PATH="${ANDROID_DATA}/zipapex/${LIBRARY_DIRECTORY}:${ANDROID_ROOT}/${TEST_DIRECTORY}"
- export DYLD_LIBRARY_PATH="${ANDROID_DATA}/zipapex/${LIBRARY_DIRECTORY}:${ANDROID_ROOT}/${TEST_DIRECTORY}"
- else
- export LD_LIBRARY_PATH="${ANDROID_ROOT}/${LIBRARY_DIRECTORY}:${ANDROID_ROOT}/${TEST_DIRECTORY}"
- export DYLD_LIBRARY_PATH="${ANDROID_ROOT}/${LIBRARY_DIRECTORY}:${ANDROID_ROOT}/${TEST_DIRECTORY}"
- fi
- export PATH="$PATH:$ANDROID_ART_BIN_DIR"
-
- # Temporarily disable address space layout randomization (ASLR).
- # This is needed on the host so that the linker loads core.oat at the necessary address.
- export LD_USE_LOAD_BIAS=1
-
- cmdline="$dalvikvm_cmdline"
-
- if [ "$TIME_OUT" = "gdb" ]; then
- if [ `uname` = "Darwin" ]; then
- # Fall back to timeout on Mac.
- TIME_OUT="timeout"
- elif [ "$ISA" = "x86" ]; then
- # prctl call may fail in 32-bit on an older (3.2) 64-bit Linux kernel. Fall back to timeout.
- TIME_OUT="timeout"
- else
- # Check if gdb is available.
- gdb --eval-command="quit" > /dev/null 2>&1
- if [ $? != 0 ]; then
- # gdb isn't available. Fall back to timeout.
- TIME_OUT="timeout"
- fi
- fi
- fi
-
- if [ "$TIME_OUT" = "timeout" ]; then
- # Add timeout command if time out is desired.
- #
- # Note: We first send SIGTERM (the timeout default, signal 15) to the signal dumper, which
- # will induce a full thread dump before killing the process. To ensure any issues in
- # dumping do not lead to a deadlock, we also use the "-k" option to definitely kill the
- # child.
- # Note: Using "--foreground" to not propagate the signal to children, i.e., the runtime.
- cmdline="timeout --foreground -k 120s ${TIME_OUT_VALUE}s ${TIMEOUT_DUMPER} -s 15 $cmdline"
- fi
-
- if [ "$DEV_MODE" = "y" ]; then
- for var in ANDROID_PRINTF_LOG ANDROID_DATA ANDROID_ROOT ANDROID_I18N_ROOT ANDROID_TZDATA_ROOT ANDROID_ART_ROOT LD_LIBRARY_PATH DYLD_LIBRARY_PATH PATH LD_USE_LOAD_BIAS; do
- echo EXPORT $var=${!var}
- done
- echo "$(declare -f linkdirs)"
- echo "mkdir -p ${mkdir_locations} && $setupapex_cmdline && ( $installapex_test_cmdline || $installapex_cmdline ) && $linkroot_cmdline && $linkroot_overlay_cmdline && $profman_cmdline && $dex2oat_cmdline && $dm_cmdline && $vdex_cmdline && $strip_cmdline && $sync_cmdline && $cmdline"
- fi
-
- cd $ANDROID_BUILD_TOP
-
- # Make sure we delete any existing compiler artifacts.
- # This enables tests to call the RUN script multiple times in a row
- # without worrying about interference.
- rm -rf ${DEX_LOCATION}/oat
- rm -rf ${DEX_LOCATION}/dalvik-cache/
-
- export ASAN_OPTIONS=$RUN_TEST_ASAN_OPTIONS
-
- mkdir -p ${mkdir_locations} || exit 1
- $setupapex_cmdline || { echo "zipapex extraction failed." >&2 ; exit 2; }
- $installapex_test_cmdline || $installapex_cmdline || { echo "zipapex install failed. cmd was: ${installapex_test_cmdline} || ${installapex_cmdline}." >&2; find ${mkdir_locations} -type f >&2; exit 2; }
- $linkroot_cmdline || { echo "create symlink android-root failed." >&2 ; exit 2; }
- $linkroot_overlay_cmdline || { echo "overlay android-root failed." >&2 ; exit 2; }
- $profman_cmdline || { echo "Profman failed." >&2 ; exit 2; }
- eval "$dex2oat_cmdline" || { echo "Dex2oat failed." >&2 ; exit 2; }
- eval "$dm_cmdline" || { echo "Dex2oat failed." >&2 ; exit 2; }
- eval "$vdex_cmdline" || { echo "Dex2oat failed." >&2 ; exit 2; }
- $strip_cmdline || { echo "Strip failed." >&2 ; exit 3; }
- $sync_cmdline || { echo "Sync failed." >&2 ; exit 4; }
-
- if [ "$CREATE_RUNNER" = "y" ]; then
- echo "#!/bin/bash" > ${DEX_LOCATION}/runit.sh
- for var in ANDROID_PRINTF_LOG ANDROID_DATA ANDROID_ROOT ANDROID_I18N_ROOT ANDROID_TZDATA_ROOT ANDROID_ART_ROOT LD_LIBRARY_PATH DYLD_LIBRARY_PATH PATH LD_USE_LOAD_BIAS; do
- echo export $var="${!var}" >> ${DEX_LOCATION}/runit.sh
- done
- if [ "$DEV_MODE" = "y" ]; then
- echo $cmdline >> ${DEX_LOCATION}/runit.sh
- else
- echo 'STDERR=$(mktemp)' >> ${DEX_LOCATION}/runit.sh
- echo 'STDOUT=$(mktemp)' >> ${DEX_LOCATION}/runit.sh
- echo $cmdline '>${STDOUT} 2>${STDERR}' >> ${DEX_LOCATION}/runit.sh
- echo 'if diff ${STDOUT} $ANDROID_DATA/expected-stdout.txt; then' \
- >> ${DEX_LOCATION}/runit.sh
- echo ' rm -f ${STDOUT} ${STDERR}' >> ${DEX_LOCATION}/runit.sh
- echo ' exit 0' >> ${DEX_LOCATION}/runit.sh
- echo 'elif diff ${STDERR} $ANDROID_DATA/expected-stderr.txt; then' \
- >> ${DEX_LOCATION}/runit.sh
- echo ' rm -f ${STDOUT} ${STDERR}' >> ${DEX_LOCATION}/runit.sh
- echo ' exit 0' >> ${DEX_LOCATION}/runit.sh
- echo 'else' >> ${DEX_LOCATION}/runit.sh
- echo ' echo STDOUT:' >> ${DEX_LOCATION}/runit.sh
- echo ' cat ${STDOUT}' >> ${DEX_LOCATION}/runit.sh
- echo ' echo STDERR:' >> ${DEX_LOCATION}/runit.sh
- echo ' cat ${STDERR}' >> ${DEX_LOCATION}/runit.sh
- echo ' rm -f ${STDOUT} ${STDERR}' >> ${DEX_LOCATION}/runit.sh
- echo ' exit 1' >> ${DEX_LOCATION}/runit.sh
- echo 'fi' >> ${DEX_LOCATION}/runit.sh
- fi
- chmod u+x $DEX_LOCATION/runit.sh
- echo "Runnable test script written to ${DEX_LOCATION}/runit.sh"
- fi
- if [ "$DRY_RUN" = "y" ]; then
- exit 0
- fi
-
- if [ "$USE_GDB" = "y" ]; then
- # When running under gdb, we cannot do piping and grepping...
- $cmdline "$@"
- elif [ "$USE_GDBSERVER" = "y" ]; then
- echo "Connect to $GDBSERVER_PORT"
- # When running under gdb, we cannot do piping and grepping...
- $cmdline "$@"
- else
- if [ "$TIME_OUT" != "gdb" ]; then
- trap 'kill -INT -$pid' INT
- $cmdline "$@" & pid=$!
- wait $pid
- exit_value=$?
- # Add extra detail if time out is enabled.
- if [ $exit_value = 124 ] && [ "$TIME_OUT" = "timeout" ]; then
- echo -e "\e[91mTEST TIMED OUT!\e[0m" >&2
- fi
- exit $exit_value
- else
- # With a thread dump that uses gdb if a timeout.
- trap 'kill -INT -$pid' INT
- $cmdline "$@" & pid=$!
- # Spawn a watcher process.
- ( sleep $TIME_OUT_VALUE && \
- echo "##### Thread dump using gdb on test timeout" && \
- ( gdb -q -p $pid --eval-command="info thread" --eval-command="thread apply all bt" \
- --eval-command="call exit(124)" --eval-command=quit || \
- kill $pid )) 2> /dev/null & watcher=$!
- wait $pid
- test_exit_status=$?
- pkill -P $watcher 2> /dev/null # kill the sleep which will in turn end the watcher as well
- if [ $test_exit_status = 0 ]; then
- # The test finished normally.
- exit 0
- else
- # The test failed or timed out.
- if [ $test_exit_status = 124 ]; then
- # The test timed out.
- echo -e "\e[91mTEST TIMED OUT!\e[0m" >&2
- fi
- exit $test_exit_status
- fi
- fi
- fi
-fi
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 3e485f6..1855429 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -81,7 +81,7 @@
},
{
"tests" : "629-vdex-speed",
- "variant": "interp-ac | interpreter | jit",
+ "variant": "interp-ac | interpreter | no-prebuild | debuggable | trace | stream",
"description": "629 requires compilation."
},
{
@@ -339,12 +339,6 @@
"variant": "optimizing | regalloc_gc"
},
{
- "tests": "089-many-methods",
- "description": "The test tests a build failure",
- "env_vars": {"ART_TEST_BISECTION": "true"},
- "variant": "optimizing | regalloc_gc"
- },
- {
"tests": ["018-stack-overflow",
"116-nodex2oat",
"118-noimage-dex2oat",
@@ -383,16 +377,10 @@
"variant": "interp-ac"
},
{
- "tests": ["629-vdex-speed",
- "634-vdex-duplicate"],
- "description": ["Profile driven dexlayout does not work with vdex or dex verifier."],
- "variant": "speed-profile"
- },
- {
"test_patterns": ["616-cha.*"],
"description": ["cha tests rely on knowing the exact set of optimizations available. ",
"Debuggable runtimes change the set of optimizations."],
- "variant": "debuggable"
+ "variant": "debuggable | trace | stream"
},
{
"test_patterns": ["616-cha.*"],
@@ -550,6 +538,7 @@
"674-hiddenapi",
"690-hiddenapi-same-name-methods",
"804-class-extends-itself",
+ "842-vdex-hard-failure",
"921-hello-failure",
"999-redefine-hiddenapi"
],
@@ -712,10 +701,9 @@
"description": "Test disabled due to redefine-stress disabling intrinsics which changes the trace output slightly."
},
{
- "tests": ["137-cfi", "629-vdex-speed"],
- "description": [ "Tests require speed compilation which is no longer the default for",
- "no-prebuild or no-image configs."],
- "variant": "no-prebuild | no-image"
+ "tests": ["137-cfi"],
+ "description": [ "Tests require speed compilation which is no longer the default for no-prebuild"],
+ "variant": "no-prebuild"
},
{
"tests": ["059-finalizer-throw", "063-process-manager"],
@@ -760,20 +748,13 @@
"bug": "b/64683522"
},
{
- "tests": ["628-vdex",
- "629-vdex-speed",
- "634-vdex-duplicate"],
- "variant": "cdex-fast",
- "description": ["Tests that depend on input-vdex are not supported with compact dex"]
- },
- {
"tests": ["661-oat-writer-layout"],
"variant": "interp-ac | interpreter | jit | jit-on-first-use | no-prebuild | no-image | trace | redefine-stress | jvmti-stress",
"description": ["Test is designed to only check --optimizing"]
},
{
"tests": ["004-StackWalk"],
- "variant": "speed-profile | interp-ac | interpreter | jit | no-prebuild | no-image | trace | redefine-stress | jvmti-stress | debuggable",
+ "variant": "speed-profile | interp-ac | interpreter | jit | no-prebuild | no-image | trace | redefine-stress | jvmti-stress | debuggable | stream",
"description": ["Test is designed to only check --optimizing"]
},
{
@@ -1030,6 +1011,7 @@
"816-illegal-new-array",
"819-verification-runtime",
"823-cha-inlining",
+ "842-vdex-hard-failure",
"900-hello-plugin",
"901-hello-ti-agent",
"903-hello-tagging",
@@ -1073,6 +1055,7 @@
"988-method-trace",
"989-method-trace-throw",
"993-breakpoints",
+ "993-breakpoints-non-debuggable",
"1002-notify-startup",
"1003-metadata-section-strings",
"1336-short-finalizer-timeout",
@@ -1090,13 +1073,21 @@
"1946-list-descriptors",
"1947-breakpoint-redefine-deopt",
"2041-bad-cleaner",
- "2230-profile-save-hotness"
+ "2230-profile-save-hotness",
+ "2245-checker-smali-instance-of-comparison",
+ "2251-checker-irreducible-loop-do-not-inline"
],
"variant": "jvm",
"bug": "b/73888836",
"description": ["Failing on RI. Needs further investigating. Some of these use smali."]
},
{
+ "tests": ["2042-reference-processing",
+ "2043-reference-pauses"],
+ "variant": "jvm",
+ "description": ["Flakey behavior of RI."]
+ },
+ {
"tests": [
"1974-resize-array",
"1975-hello-structural-transformation",
@@ -1133,7 +1124,7 @@
"2006-virtual-structural-finalizing",
"2007-virtual-structural-finalizable"
],
- "env_vars": {"ART_USE_READ_BARRIER": "false"},
+ "env_vars": {"ART_USE_READ_BARRIER": "false", "ART_DEFAULT_GC_TYPE": "CMS"},
"description": ["Relies on the accuracy of the Heap::VisitObjects function which is broken",
" when READ_BARRIER==false (I.e. On CMS collector)."],
"bug": "b/147207934"
@@ -1168,6 +1159,12 @@
"831-unresolved-field",
"833-background-verification",
"836-32768classes",
+ "837-deopt",
+ "844-exception",
+ "844-exception2",
+ "845-fast-verify",
+ "845-data-image",
+ "846-multidex-data-image",
"999-redefine-hiddenapi",
"1000-non-moving-space-stress",
"1001-app-image-regions",
@@ -1218,7 +1215,9 @@
"2036-structural-subclass-shadow",
"2038-hiddenapi-jvmti-ext",
"2040-huge-native-alloc",
- "2238-checker-polymorphic-recursive-inlining"],
+ "2238-checker-polymorphic-recursive-inlining",
+ "2240-tracing-non-invokable-method",
+ "2246-trace-stream"],
"variant": "jvm",
"description": ["Doesn't run on RI."]
},
@@ -1261,7 +1260,7 @@
"tests": ["141-class-unload", "071-dexfile"],
"variant": "gcstress",
"bug": "b/111543628",
- "description" : ["Test seems to timeout when run with gcstress due to slower unwinding by libbacktrace"]
+ "description" : ["Test seems to timeout when run with gcstress due to slower unwinding by libunwindstack"]
},
{
"tests": ["708-jit-cache-churn"],
@@ -1273,7 +1272,7 @@
"tests": ["712-varhandle-invocations"],
"variant": "gcstress",
"bug": "b/111630237",
- "description": ["Test timing out under gcstress possibly due to slower unwinding by libbacktrace"]
+ "description": ["Test timing out under gcstress possibly due to slower unwinding by libunwindstack"]
},
{
"tests": ["1336-short-finalizer-timeout"],
@@ -1307,7 +1306,7 @@
},
{
"tests": ["1339-dead-reference-safe"],
- "variant": "debuggable",
+ "variant": "debuggable | trace | stream",
"description": [ "Fails to eliminate dead reference when debuggable." ]
},
{
@@ -1326,6 +1325,13 @@
"description": ["Test containing Checker assertions expecting Baker read barriers."]
},
{
+ "tests": ["2040-huge-native-alloc"],
+ "env_vars": {"ART_USE_READ_BARRIER": "false"},
+ "variant": "debug",
+ "bug": "b/242181443",
+ "description": ["Test fails due to delay delebrately added in the userfaultfd GC between marking and compaction."]
+ },
+ {
"tests": ["1004-checker-volatile-ref-load"],
"env_vars": {"ART_READ_BARRIER_TYPE": "TABLELOOKUP"},
"bug": "b/140507091",
@@ -1358,11 +1364,23 @@
"description": "Interpreting BigInteger.add() is too slow (timeouts)"
},
{
- "tests": ["2029-contended-monitors"],
- "variant": "interpreter | interp-ac | gcstress | trace",
+ "tests": ["2029-contended-monitors", "2043-reference-pauses"],
+ "variant": "interpreter | interp-ac | gcstress | trace | stream",
+ "description": ["Slow tests. Prone to timeouts."]
+ },
+ {
+ "tests": ["2042-reference-processing"],
+ "variant": "interpreter | interp-ac | gcstress | trace | debuggable | stream",
"description": ["Slow test. Prone to timeouts."]
},
{
+ "tests": ["2043-reference-pauses"],
+ "env_vars": {"ART_USE_READ_BARRIER": "false", "ART_DEFAULT_GC_TYPE": "CMS"},
+ "variant": "host",
+ "bug": "b/232459100",
+ "description": ["Fails intermittently for CMS."]
+ },
+ {
"tests": ["096-array-copy-concurrent-gc"],
"variant": "gcstress & debuggable & debug & host",
"bug": "b/149708943",
@@ -1420,7 +1438,7 @@
},
{
"tests": ["692-vdex-secondary-loader"],
- "env_vars": {"ART_USE_READ_BARRIER": "false"},
+ "env_vars": {"ART_USE_READ_BARRIER": "false", "ART_DEFAULT_GC_TYPE": "CMS"},
"description": ["Uses the low-ram flag which does not work with CMS"]
},
{
@@ -1497,5 +1515,39 @@
"variant": "target & ndebug & 64",
"bug": "b/224733324",
"description": ["segfault in VarHandle::GetMethodTypeMatchForAccessMode"]
+ },
+ {
+ "tests": ["2043-reference-pauses"],
+ "env_vars": {"ART_TEST_DEBUG_GC": "true"},
+ "description": ["Test timing out on debug gc."]
+ },
+ {
+ "tests": ["837-deopt"],
+ "description": ["Tests deoptimization and OSR, which never happens when tracing."],
+ "variant": "trace | stream | jit-on-first-use"
+ },
+ {
+ "tests": ["1912-get-set-local-primitive"],
+ "description": ["JVMTI error code used changed in JDK 17."],
+ "bug": "b/243356199",
+ "variant": "jvm"
+ },
+ {
+ "tests": ["715-clinit-implicit-parameter-annotations"],
+ "description": ["Change for Annotation.toString() in JDK 17."],
+ "bug": "b/243500721",
+ "variant": "jvm"
+ },
+ {
+ "tests": ["1907-suspend-list-self-twice"],
+ "description": ["Change of behavior when used with JDK 17."],
+ "bug": "b/243139124",
+ "variant": "jvm"
+ },
+ {
+ "tests": ["1921-suspend-native-recursive-monitor"],
+ "description": ["Change of behavior when used with JDK 17."],
+ "bug": "b/242985234",
+ "variant": "jvm"
}
]
diff --git a/test/odsign/test-src/com/android/tests/odsign/CompOsTestUtils.java b/test/odsign/test-src/com/android/tests/odsign/CompOsTestUtils.java
index 8c2c5b2..60d7642 100644
--- a/test/odsign/test-src/com/android/tests/odsign/CompOsTestUtils.java
+++ b/test/odsign/test-src/com/android/tests/odsign/CompOsTestUtils.java
@@ -16,7 +16,6 @@
package com.android.tests.odsign;
-import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.fail;
@@ -36,7 +35,8 @@
"/data/misc/apexdata/com.android.art/compos-pending";
/** Maximum time for a slow VM like cuttlefish to boot and finish odrefresh. */
- private static final int VM_ODREFRESH_MAX_SECONDS = 360;
+ // odrefresh overall timeout is currently 480s; add some generous padding for VM startup.
+ private static final int VM_ODREFRESH_MAX_SECONDS = 480 + 60;
/** Waiting time for the job to be scheduled after staging an APEX */
private static final int JOB_CREATION_MAX_SECONDS = 5;
@@ -74,8 +74,6 @@
// Sort by filename (second column) to make comparison easier.
// Filter out compos.info* (which will be deleted at boot) and cache-info.xml
// compos.info.signature since it's only generated by CompOS.
- // TODO(b/210473615): Remove irrelevant APEXes (i.e. those aren't contributing to the
- // classpaths, thus not in the VM) from cache-info.xml.
return assertCommandSucceeds("cd " + path + "; find -type f -exec sha256sum {} \\;"
+ "| grep -v cache-info.xml | grep -v compos.info"
+ "| sort -k2");
diff --git a/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java b/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java
index 731ea38..2bd24f9 100644
--- a/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java
+++ b/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java
@@ -27,6 +27,7 @@
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.testtype.junit4.BeforeClassWithInfo;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,10 +47,6 @@
private static final String CACHE_INFO_FILE =
OdsignTestUtils.ART_APEX_DALVIK_CACHE_DIRNAME + "/cache-info.xml";
private static final String ODREFRESH_BIN = "odrefresh";
- private static final String ODREFRESH_COMMAND =
- ODREFRESH_BIN + " --partial-compilation --no-refresh --compile";
- private static final String ODREFRESH_MINIMAL_COMMAND =
- ODREFRESH_BIN + " --partial-compilation --no-refresh --minimal --compile";
private static final String TAG = "OdrefreshHostTest";
private static final String ZYGOTE_ARTIFACTS_KEY = TAG + ":ZYGOTE_ARTIFACTS";
@@ -87,11 +84,27 @@
mTestUtils = new OdsignTestUtils(getTestInformation());
}
+ @After
+ public void tearDown() throws Exception {
+ Set<String> artifacts = new HashSet<>();
+ artifacts.addAll(getZygoteArtifacts());
+ artifacts.addAll(getSystemServerArtifacts());
+
+ for (String artifact : artifacts) {
+ if (!getDevice().doesFileExist(artifact)) {
+ // Things went wrong during the test. Run odrefresh to revert to a normal state.
+ mTestUtils.removeCompilationLogToAvoidBackoff();
+ runOdrefresh();
+ break;
+ }
+ }
+ }
+
@Test
public void verifyArtSamegradeUpdateTriggersCompilation() throws Exception {
simulateArtApexUpgrade();
long timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
assertArtifactsModifiedAfter(getZygoteArtifacts(), timeMs);
assertArtifactsModifiedAfter(getSystemServerArtifacts(), timeMs);
@@ -101,7 +114,7 @@
public void verifyOtherApexSamegradeUpdateTriggersCompilation() throws Exception {
simulateApexUpgrade();
long timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
assertArtifactsModifiedAfter(getSystemServerArtifacts(), timeMs);
@@ -111,7 +124,7 @@
public void verifyBootClasspathOtaTriggersCompilation() throws Exception {
simulateBootClasspathOta();
long timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
assertArtifactsModifiedAfter(getZygoteArtifacts(), timeMs);
assertArtifactsModifiedAfter(getSystemServerArtifacts(), timeMs);
@@ -121,7 +134,7 @@
public void verifySystemServerOtaTriggersCompilation() throws Exception {
simulateSystemServerOta();
long timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
assertArtifactsModifiedAfter(getSystemServerArtifacts(), timeMs);
@@ -137,7 +150,7 @@
mTestUtils.removeCompilationLogToAvoidBackoff();
long timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
assertArtifactsNotModifiedAfter(remainingArtifacts, timeMs);
assertArtifactsModifiedAfter(missingArtifacts, timeMs);
@@ -158,7 +171,7 @@
"device_config put runtime_native_boot enable_uffd_gc false");
long timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
// Artifacts should not be re-compiled.
assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
@@ -169,7 +182,7 @@
"device_config put runtime_native_boot enable_uffd_gc true");
timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
// Artifacts should be re-compiled.
assertArtifactsModifiedAfter(getZygoteArtifacts(), timeMs);
@@ -177,7 +190,7 @@
// Run odrefresh again with the flag unchanged.
timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
// Artifacts should not be re-compiled.
assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
@@ -188,7 +201,7 @@
"device_config put runtime_native_boot enable_uffd_gc false");
timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
// Artifacts should be re-compiled.
assertArtifactsModifiedAfter(getZygoteArtifacts(), timeMs);
@@ -201,11 +214,77 @@
}
@Test
+ public void verifySystemServerCompilerFilterOverrideChangeTriggersCompilation()
+ throws Exception {
+ try {
+ // Disable phenotype flag syncing. Potentially, we can set
+ // `set_sync_disabled_for_tests` to `until_reboot`, but setting it to
+ // `persistent` prevents unrelated system crashes/restarts from affecting the
+ // test. `set_sync_disabled_for_tests` is reset in the `finally` block anyway.
+ getDevice()
+ .executeShellV2Command("device_config set_sync_disabled_for_tests persistent");
+
+ // Simulate that the phenotype flag is set to the default value.
+ getDevice()
+ .executeShellV2Command(
+ "device_config put runtime_native_boot"
+ + " systemservercompilerfilter_override");
+
+ long timeMs = mTestUtils.getCurrentTimeMs();
+ runOdrefresh();
+
+ // Artifacts should not be re-compiled.
+ assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
+ assertArtifactsNotModifiedAfter(getSystemServerArtifacts(), timeMs);
+
+ // Simulate that the phenotype flag is set to "speed".
+ getDevice()
+ .executeShellV2Command(
+ "device_config put runtime_native_boot"
+ + " systemservercompilerfilter_override speed");
+
+ timeMs = mTestUtils.getCurrentTimeMs();
+ runOdrefresh();
+
+ // Artifacts should be re-compiled.
+ assertArtifactsModifiedAfter(getZygoteArtifacts(), timeMs);
+ assertArtifactsModifiedAfter(getSystemServerArtifacts(), timeMs);
+
+ // Run odrefresh again with the flag unchanged.
+ timeMs = mTestUtils.getCurrentTimeMs();
+ runOdrefresh();
+
+ // Artifacts should not be re-compiled.
+ assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
+ assertArtifactsNotModifiedAfter(getSystemServerArtifacts(), timeMs);
+
+ // Simulate that the phenotype flag is set to "verify".
+ getDevice()
+ .executeShellV2Command(
+ "device_config put runtime_native_boot"
+ + " systemservercompilerfilter_override verify");
+
+ timeMs = mTestUtils.getCurrentTimeMs();
+ runOdrefresh();
+
+ // Artifacts should be re-compiled.
+ assertArtifactsModifiedAfter(getZygoteArtifacts(), timeMs);
+ assertArtifactsModifiedAfter(getSystemServerArtifacts(), timeMs);
+ } finally {
+ getDevice().executeShellV2Command("device_config set_sync_disabled_for_tests none");
+ getDevice()
+ .executeShellV2Command(
+ "device_config delete runtime_native_boot"
+ + " systemservercompilerfilter_override");
+ }
+ }
+
+ @Test
public void verifySystemPropertyMismatchTriggersCompilation() throws Exception {
// Change a system property from empty to a value.
getDevice().setProperty("dalvik.vm.foo", "1");
long timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
// Artifacts should be re-compiled.
assertArtifactsModifiedAfter(getZygoteArtifacts(), timeMs);
@@ -213,7 +292,7 @@
// Run again with the same value.
timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
// Artifacts should not be re-compiled.
assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
@@ -222,7 +301,7 @@
// Change the system property to another value.
getDevice().setProperty("dalvik.vm.foo", "2");
timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
// Artifacts should be re-compiled.
assertArtifactsModifiedAfter(getZygoteArtifacts(), timeMs);
@@ -230,7 +309,7 @@
// Run again with the same value.
timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
// Artifacts should not be re-compiled.
assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
@@ -239,7 +318,7 @@
// Change the system property to empty.
getDevice().setProperty("dalvik.vm.foo", "");
timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
// Artifacts should be re-compiled.
assertArtifactsModifiedAfter(getZygoteArtifacts(), timeMs);
@@ -247,7 +326,7 @@
// Run again with the same value.
timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
// Artifacts should not be re-compiled.
assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
@@ -258,7 +337,7 @@
public void verifyNoCompilationWhenCacheIsGood() throws Exception {
mTestUtils.removeCompilationLogToAvoidBackoff();
long timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
assertArtifactsNotModifiedAfter(getSystemServerArtifacts(), timeMs);
@@ -267,8 +346,8 @@
@Test
public void verifyUnexpectedFilesAreCleanedUp() throws Exception {
String unexpected = OdsignTestUtils.ART_APEX_DALVIK_CACHE_DIRNAME + "/unexpected";
- getDevice().pushString(/*contents=*/"", unexpected);
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ getDevice().pushString("" /* contents */, unexpected);
+ runOdrefresh();
assertFalse(getDevice().doesFileExist(unexpected));
}
@@ -292,9 +371,7 @@
mTestUtils.removeCompilationLogToAvoidBackoff();
simulateApexUpgrade();
long timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(
- ODREFRESH_BIN + " --no-refresh --partial-compilation"
- + " --compilation-os-mode --compile");
+ runOdrefresh("--compilation-os-mode");
assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
assertArtifactsModifiedAfter(getSystemServerArtifacts(), timeMs);
@@ -307,7 +384,7 @@
// Simulate the odrefresh invocation on the next boot.
timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
// odrefresh should not re-compile anything.
assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
@@ -319,7 +396,7 @@
mTestUtils.removeCompilationLogToAvoidBackoff();
getDevice().executeShellV2Command(
"rm -rf " + OdsignTestUtils.ART_APEX_DALVIK_CACHE_DIRNAME);
- getDevice().executeShellV2Command(ODREFRESH_MINIMAL_COMMAND);
+ runOdrefresh("--minimal");
mTestUtils.restartZygote();
@@ -330,21 +407,14 @@
// Running the command again should not overwrite the minimal boot image.
mTestUtils.removeCompilationLogToAvoidBackoff();
long timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_MINIMAL_COMMAND);
-
- assertArtifactsNotModifiedAfter(minimalZygoteArtifacts, timeMs);
-
- // `odrefresh --check` should keep the minimal boot image.
- mTestUtils.removeCompilationLogToAvoidBackoff();
- timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_BIN + " --check");
+ runOdrefresh("--minimal");
assertArtifactsNotModifiedAfter(minimalZygoteArtifacts, timeMs);
// A normal odrefresh invocation should replace the minimal boot image with a full one.
mTestUtils.removeCompilationLogToAvoidBackoff();
timeMs = mTestUtils.getCurrentTimeMs();
- getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+ runOdrefresh();
for (String artifact : minimalZygoteArtifacts) {
assertFalse(
@@ -488,4 +558,14 @@
private Set<String> getSystemServerArtifacts() {
return getColonSeparatedSet(SYSTEM_SERVER_ARTIFACTS_KEY);
}
+
+ private void runOdrefresh() throws Exception {
+ runOdrefresh("" /* extraArgs */);
+ }
+
+ private void runOdrefresh(String extraArgs) throws Exception {
+ getDevice().executeShellV2Command(ODREFRESH_BIN + " --check");
+ getDevice().executeShellV2Command(
+ ODREFRESH_BIN + " --partial-compilation --no-refresh " + extraArgs + " --compile");
+ }
}
diff --git a/test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java b/test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java
index caf94a7..5951600 100644
--- a/test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java
+++ b/test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java
@@ -314,18 +314,14 @@
// We can't use the "-c '%.3Y'" flag when to get the timestamp because the Toybox's `stat`
// implementation truncates the timestamp to seconds, which is not accurate enough, so we
// use "-c '%%y'" and parse the time ourselves.
- String dateTimeStr = mTestInfo.getDevice()
- .executeShellCommand(String.format("stat -c '%%y' '%s'", filename))
- .trim();
+ String dateTimeStr = assertCommandSucceeds(String.format("stat -c '%%y' '%s'", filename));
return parseFormattedDateTime(dateTimeStr);
}
public long getCurrentTimeMs() throws Exception {
// We can't use getDevice().getDeviceDate() because it truncates the timestamp to seconds,
// which is not accurate enough.
- String dateTimeStr = mTestInfo.getDevice()
- .executeShellCommand("date +'%Y-%m-%d %H:%M:%S.%N %z'")
- .trim();
+ String dateTimeStr = assertCommandSucceeds("date +'%Y-%m-%d %H:%M:%S.%N %z'");
return parseFormattedDateTime(dateTimeStr);
}
diff --git a/test/run-test b/test/run-test
index dccc9f6..e283079 100755
--- a/test/run-test
+++ b/test/run-test
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env python3
#
# Copyright (C) 2007 The Android Open Source Project
#
@@ -14,1059 +14,1052 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Set up prog to be the path of this script, including following symlinks,
-# and set up progdir to be the fully-qualified pathname of its directory.
-prog="$0"
-args="$@"
-while [ -h "${prog}" ]; do
- newProg=`/bin/ls -ld "${prog}"`
- newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
- if expr "x${newProg}" : 'x/' >/dev/null; then
- prog="${newProg}"
- else
- progdir=`dirname "${prog}"`
- prog="${progdir}/${newProg}"
- fi
-done
-oldwd=`pwd`
-progdir=`dirname "${prog}"`
-cd "${progdir}"
-progdir=`pwd`
-prog="${progdir}"/`basename "${prog}"`
-test_dir="test-$$"
-if [ -z "$TMPDIR" ]; then
- tmp_dir="/tmp/$USER/${test_dir}"
-else
- tmp_dir="${TMPDIR}/${test_dir}"
-fi
-checker="${progdir}/../tools/checker/checker.py"
-export JAVA="java"
-export JAVAC="javac -g -Xlint:-options -source 1.8 -target 1.8"
-export RUN="${progdir}/etc/run-test-jar"
-export DEX_LOCATION=/data/run-test/${test_dir}
+import os, sys, glob, re, shutil, subprocess, shlex, resource, atexit
-# ANDROID_BUILD_TOP is not set in a build environment.
-if [ -z "$ANDROID_BUILD_TOP" ]; then
- export ANDROID_BUILD_TOP=$oldwd
-fi
+import default_run as default_run_module
-# OUT_DIR defaults to out, and may be relative to $ANDROID_BUILD_TOP.
-# Convert it to an absolute path, since we cd into the tmp_dir to run the tests.
-export OUT_DIR=${OUT_DIR:-out}
-if [[ "$OUT_DIR" != /* ]]; then
- export OUT_DIR=$ANDROID_BUILD_TOP/$OUT_DIR
-fi
+from default_run import get_target_arch
+from importlib.machinery import SourceFileLoader
+from inspect import currentframe, getframeinfo, FrameInfo
+from pathlib import Path
+from shutil import copyfile
+from testrunner import env
+from typing import Optional, Dict, List
+from zipfile import ZipFile
+
+COLOR = (os.environ.get("LUCI_CONTEXT") == None) # Disable colors on LUCI.
+COLOR_BLUE = '\033[94m' if COLOR else ''
+COLOR_GREEN = '\033[92m' if COLOR else ''
+COLOR_NORMAL = '\033[0m' if COLOR else ''
+COLOR_RED = '\033[91m' if COLOR else ''
+
+# Helper class which allows us to access the environment using syntax sugar.
+# E.g. `env.ANDROID_BUILD_TOP` instead of `os.environ["ANDROID_BUILD_TOP"]`.
+class Environment:
+
+ def __getattr__(self, name):
+ return os.environ.get(name)
+
+ def __setattr__(self, name, value):
+ os.environ[name] = str(value)
+
+
+# Context passed to individual tests to let them customize the behaviour.
+class RunTestContext:
+
+ def __init__(self, tmp_dir: Path, target: bool, chroot, dex_location, test_name) -> None:
+ self.env = Environment()
+ self.target = target
+ self.chroot = chroot
+ self.dex_location = dex_location
+ self.test_name = test_name
+
+ # Note: The expected path can be modified by the tests.
+ self.expected_stdout = tmp_dir / "expected-stdout.txt"
+ self.expected_stderr = tmp_dir / "expected-stderr.txt"
+
+ self.runner: List[str] = ["#!/bin/bash"]
+
+ def echo(self, text):
+ self.run(f"echo {text} > {test_stdout}")
+
+ def export(self, **env: str) -> None:
+ self.runner.append("")
+ for name, value in env.items():
+ self.runner.append(f"export {name}={value}")
+
+ # Add "runner" script command. It is not executed now.
+ # All "runner" commands are executed later via single bash call.
+ def run(self, cmd: str, check: bool=True, expected_exit_code: int=0, desc:str = None) -> None:
+ if cmd == "true":
+ return
+ cmd_esc = cmd.replace("'", r"'\''")
+ self.runner.append("")
+ self.runner.append(f"echo '{COLOR_BLUE}$$ {cmd_esc}{COLOR_NORMAL}'")
+ self.runner.append(cmd)
+
+ # Check the exit code.
+ if check:
+ caller = getframeinfo(currentframe().f_back) # type: ignore
+ source = "{}:{}".format(Path(caller.filename).name, caller.lineno)
+ msg = f"{self.test_name} FAILED: [{source}] "
+ msg += "{} returned exit code ${{exit_code}}.".format(desc or "Command")
+ if expected_exit_code:
+ msg += f" Expected {expected_exit_code}."
+ self.runner.append(
+ f"exit_code=$?; if [ $exit_code -ne {expected_exit_code} ]; then "
+ f"echo {COLOR_RED}{msg}{COLOR_NORMAL}; exit 100; "
+ f"fi; ")
+ else:
+ self.runner.append("true; # Ignore previous exit code")
+
+ # Execute the default runner (possibly with modified arguments).
+ def default_run(self, args, **kwargs):
+ default_run_module.default_run(self, args, **kwargs)
+
+
+# TODO: Replace with 'def main():' (which might change variables from globals to locals)
+if True:
+ progdir = os.path.dirname(__file__)
+ oldwd = os.getcwd()
+ os.chdir(progdir)
+ test_dir = "test-{}".format(os.getpid())
+ TMPDIR = os.environ.get("TMPDIR")
+ USER = os.environ.get("USER")
+ PYTHON3 = os.environ.get("PYTHON3")
+ if not TMPDIR:
+ tmp_dir = f"/tmp/{USER}/{test_dir}"
+ else:
+ tmp_dir = f"{TMPDIR}/{test_dir}"
+ checker = f"{progdir}/../tools/checker/checker.py"
+
+ def fail(message: str, caller:Optional[FrameInfo]=None):
+ caller = caller or getframeinfo(currentframe().f_back) # type: ignore
+ assert caller
+ source = "{}:{}".format(Path(caller.filename).name, caller.lineno)
+ print(f"{COLOR_RED}{TEST_NAME} FAILED: [{source}] {message}{COLOR_NORMAL}",
+ file=sys.stderr)
+ sys.exit(1)
+
+ def run(cmdline: str, check=True, fail_message=None) -> subprocess.CompletedProcess:
+ print(f"{COLOR_BLUE}$ {cmdline}{COLOR_NORMAL}", flush=True)
+ proc = subprocess.run([cmdline],
+ shell=True,
+ executable="/bin/bash",
+ stderr=subprocess.STDOUT)
+ if (check and proc.returncode != 0):
+ if fail_message:
+ # If we have custom fail message, exit without printing the full backtrace.
+ fail(fail_message, getframeinfo(currentframe().f_back)) # type: ignore
+ raise Exception(f"Command failed (exit code {proc.returncode})")
+ return proc
+
+ def export(env: str, value: str) -> None:
+ os.environ[env] = value
+ globals()[env] = value
+
+ def error(msg) -> None:
+ print(msg, file=sys.stderr, flush=True)
+
+ # ANDROID_BUILD_TOP is not set in a build environment.
+ ANDROID_BUILD_TOP = os.environ.get("ANDROID_BUILD_TOP")
+ if not ANDROID_BUILD_TOP:
+ export("ANDROID_BUILD_TOP", oldwd)
+
+ export("JAVA", "java")
+ export("JAVAC", "javac -g -Xlint:-options -source 1.8 -target 1.8")
+ export("PYTHON3",
+ f"{ANDROID_BUILD_TOP}/prebuilts/build-tools/path/linux-x86/python3")
+ export("RUN", f"{PYTHON3} {progdir}/etc/run-test-jar")
+ export("DEX_LOCATION", f"/data/run-test/{test_dir}")
+
+ # OUT_DIR defaults to out, and may be relative to ANDROID_BUILD_TOP.
+ # Convert it to an absolute path, since we cd into the tmp_dir to run the tests.
+ OUT_DIR = os.environ.get("OUT_DIR", "")
+ export("OUT_DIR", OUT_DIR or "out")
+ if not OUT_DIR.startswith("/"):
+ export("OUT_DIR", f"{ANDROID_BUILD_TOP}/{OUT_DIR}")
# ANDROID_HOST_OUT is not set in a build environment.
-if [ -z "$ANDROID_HOST_OUT" ]; then
- export ANDROID_HOST_OUT=${OUT_DIR}/host/linux-x86
-fi
+ ANDROID_HOST_OUT = os.environ.get("ANDROID_HOST_OUT")
+ if not ANDROID_HOST_OUT:
+ export("ANDROID_HOST_OUT", f"{OUT_DIR}/host/linux-x86")
-host_lib_root=${ANDROID_HOST_OUT}
-chroot=
-info="info.txt"
-run="run"
-expected_stdout="expected-stdout.txt"
-expected_stderr="expected-stderr.txt"
-check_cmd="check"
-test_stdout="test-stdout.txt"
-test_stderr="test-stderr.txt"
-cfg_output="graph.cfg"
-strace_output="strace-output.txt"
-lib="libartd.so"
-testlib="arttestd"
-run_args=(--quiet)
+ host_lib_root = ANDROID_HOST_OUT
+ chroot = ""
+ info = "info.txt"
+ run_cmd = "run"
+ test_stdout = "test-stdout.txt"
+ test_stderr = "test-stderr.txt"
+ cfg_output = "graph.cfg"
+ strace_output = "strace-output.txt"
+ lib = "libartd.so"
+ testlib = "arttestd"
+ run_args = []
+ run_checker = "no"
-quiet="no"
-debuggable="no"
-prebuild_mode="yes"
-target_mode="yes"
-dev_mode="no"
-create_runner="no"
-update_mode="no"
-debug_mode="no"
-relocate="no"
-runtime="art"
-usage="no"
-suffix64=""
-trace="false"
-trace_stream="false"
-basic_verify="false"
-gc_verify="false"
-gc_stress="false"
-jvmti_trace_stress="false"
-jvmti_field_stress="false"
-jvmti_step_stress="false"
-jvmti_redefine_stress="false"
-strace="false"
-always_clean="no"
-never_clean="no"
-have_image="yes"
-android_root="/system"
-bisection_search="no"
-timeout=""
-suspend_timeout="500000"
-run_optimizing="false"
-dump_cfg="false"
-dump_cfg_path=""
-# To cause tests to fail fast, limit the file sizes created by dx, dex2oat and
-# ART output to approximately 128MB. This should be more than sufficient
-# for any test while still catching cases of runaway output.
-# Set a hard limit to encourage ART developers to increase the ulimit here if
-# needed to support a test case rather than resetting the limit in the run
-# script for the particular test in question. Adjust this if needed for
-# particular configurations.
-file_ulimit=128000
+ quiet = "no"
+ debuggable = "no"
+ prebuild_mode = "yes"
+ target_mode = "yes"
+ dev_mode = "no"
+ create_runner = "no"
+ update_mode = "no"
+ debug_mode = "no"
+ relocate = "no"
+ runtime = "art"
+ usage = "no"
+ suffix64 = ""
+ trace = "false"
+ trace_stream = "false"
+ basic_verify = "false"
+ gc_verify = "false"
+ gc_stress = "false"
+ jvmti_trace_stress = "false"
+ jvmti_field_stress = "false"
+ jvmti_step_stress = "false"
+ jvmti_redefine_stress = "false"
+ strace = "false"
+ always_clean = "no"
+ never_clean = "no"
+ have_image = "yes"
+ android_root = "/system"
+ bisection_search = "no"
+ timeout = ""
+ suspend_timeout = "500000"
+ run_optimizing = "false"
+ dump_cfg = "false"
+ dump_cfg_path = ""
+ # To cause tests to fail fast, limit the file sizes created by dx, dex2oat and
+ # ART output to approximately 128MB. This should be more than sufficient
+ # for any test while still catching cases of runaway output.
+ # Set a hard limit to encourage ART developers to increase the ulimit here if
+ # needed to support a test case rather than resetting the limit in the run
+ # script for the particular test in question. Adjust this if needed for
+ # particular configurations.
+ file_ulimit = 128000
+ args = list(sys.argv)
+ arg = args[0]
-while true; do
- if [ "x$1" = "x--host" ]; then
- target_mode="no"
- DEX_LOCATION=$tmp_dir
- run_args+=(--host)
- shift
- elif [ "x$1" = "x--quiet" ]; then
- quiet="yes"
- shift
- elif [ "x$1" = "x--use-java-home" ]; then
- if [ -n "${JAVA_HOME}" ]; then
- export JAVA="${JAVA_HOME}/bin/java"
- export JAVAC="${JAVA_HOME}/bin/javac -g"
- else
- echo "Passed --use-java-home without JAVA_HOME variable set!"
- usage="yes"
- fi
- shift
- elif [ "x$1" = "x--jvm" ]; then
- target_mode="no"
- DEX_LOCATION="$tmp_dir"
- runtime="jvm"
- prebuild_mode="no"
- run_args+=(--jvm)
- shift
- elif [ "x$1" = "x-O" ]; then
- lib="libart.so"
- testlib="arttest"
- run_args+=(-O)
- shift
- elif [ "x$1" = "x--dalvik" ]; then
- lib="libdvm.so"
- runtime="dalvik"
- shift
- elif [ "x$1" = "x--no-image" ]; then
- have_image="no"
- shift
- elif [ "x$1" = "x--relocate" ]; then
- relocate="yes"
- shift
- elif [ "x$1" = "x--no-relocate" ]; then
- relocate="no"
- shift
- elif [ "x$1" = "x--prebuild" ]; then
- run_args+=(--prebuild)
- prebuild_mode="yes"
- shift;
- elif [ "x$1" = "x--compact-dex-level" ]; then
- option="$1"
- shift
- run_args+=("$option" "$1")
- shift;
- elif [ "x$1" = "x--strip-dex" ]; then
- run_args+=(--strip-dex)
- shift;
- elif [ "x$1" = "x--debuggable" ]; then
- run_args+=(-Xcompiler-option --debuggable)
- debuggable="yes"
- shift;
- elif [ "x$1" = "x--no-prebuild" ]; then
- run_args+=(--no-prebuild)
- prebuild_mode="no"
- shift;
- elif [ "x$1" = "x--gcverify" ]; then
- basic_verify="true"
- gc_verify="true"
- shift
- elif [ "x$1" = "x--gcstress" ]; then
- basic_verify="true"
- gc_stress="true"
- shift
- elif [ "x$1" = "x--jvmti-step-stress" ]; then
- jvmti_step_stress="true"
- shift
- elif [ "x$1" = "x--jvmti-redefine-stress" ]; then
- jvmti_redefine_stress="true"
- shift
- elif [ "x$1" = "x--jvmti-field-stress" ]; then
- jvmti_field_stress="true"
- shift
- elif [ "x$1" = "x--jvmti-trace-stress" ]; then
- jvmti_trace_stress="true"
- shift
- elif [ "x$1" = "x--suspend-timeout" ]; then
- shift
- suspend_timeout="$1"
- shift
- elif [ "x$1" = "x--image" ]; then
- shift
- image="$1"
- run_args+=(--image "$image")
- shift
- elif [ "x$1" = "x-Xcompiler-option" ]; then
- shift
- option="$1"
- run_args+=(-Xcompiler-option "$option")
- shift
- elif [ "x$1" = "x--runtime-option" ]; then
- shift
- option="$1"
- run_args+=(--runtime-option "$option")
- shift
- elif [ "x$1" = "x--gdb-arg" ]; then
- shift
- gdb_arg="$1"
- run_args+=(--gdb-arg "$gdb_arg")
- shift
- elif [ "x$1" = "x--gdb-dex2oat-args" ]; then
- shift
- gdb_dex2oat_args="$1"
- run_args+=(--gdb-dex2oat-args "$gdb_dex2oat_args")
- shift
- elif [ "x$1" = "x--debug" ]; then
- run_args+=(--debug)
- shift
- elif [ "x$1" = "x--debug-wrap-agent" ]; then
- run_args+=(--debug-wrap-agent)
- shift
- elif [ "x$1" = "x--with-agent" ]; then
- shift
- option="$1"
- run_args+=(--with-agent "$1")
- shift
- elif [ "x$1" = "x--debug-agent" ]; then
- shift
- option="$1"
- run_args+=(--debug-agent "$1")
- shift
- elif [ "x$1" = "x--dump-cfg" ]; then
- shift
- dump_cfg="true"
- dump_cfg_path="$1"
- shift
- elif [ "x$1" = "x--gdb" ]; then
- run_args+=(--gdb)
- dev_mode="yes"
- shift
- elif [ "x$1" = "x--gdb-dex2oat" ]; then
- run_args+=(--gdb-dex2oat)
- dev_mode="yes"
- shift
- elif [ "x$1" = "x--gdbserver-bin" ]; then
- shift
- run_args+=(--gdbserver-bin "$1")
- shift
- elif [ "x$1" = "x--gdbserver-port" ]; then
- shift
- run_args+=(--gdbserver-port "$1")
- shift
- elif [ "x$1" = "x--gdbserver" ]; then
- run_args+=(--gdbserver)
- dev_mode="yes"
- shift
- elif [ "x$1" = "x--strace" ]; then
- strace="yes"
- run_args+=(--invoke-with strace --invoke-with -o --invoke-with "$tmp_dir/$strace_output")
- timeout="${timeout:-1800}"
- shift
- elif [ "x$1" = "x--zygote" ]; then
- run_args+=(--zygote)
- shift
- elif [ "x$1" = "x--interpreter" ]; then
- run_args+=(--interpreter)
- shift
- elif [ "x$1" = "x--jit" ]; then
- run_args+=(--jit)
- shift
- elif [ "x$1" = "x--baseline" ]; then
- run_args+=(--baseline)
- shift
- elif [ "x$1" = "x--optimizing" ]; then
- run_optimizing="true"
- shift
- elif [ "x$1" = "x--no-verify" ]; then
- run_args+=(--no-verify)
- shift
- elif [ "x$1" = "x--verify-soft-fail" ]; then
- run_args+=(--verify-soft-fail)
- shift
- elif [ "x$1" = "x--no-optimize" ]; then
- run_args+=(--no-optimize)
- shift
- elif [ "x$1" = "x--no-precise" ]; then
- run_args+=(--no-precise)
- shift
- elif [ "x$1" = "x--external-log-tags" ]; then
- run_args+=(--external-log-tags)
- shift
- elif [ "x$1" = "x--invoke-with" ]; then
- shift
- what="$1"
- if [ "x$what" = "x" ]; then
- echo "$0 missing argument to --invoke-with" 1>&2
- usage="yes"
- break
- fi
- run_args+=(--invoke-with "${what}")
- shift
- elif [ "x$1" = "x--create-runner" ]; then
- run_args+=(--create-runner --dry-run)
- dev_mode="yes"
- never_clean="yes"
- create_runner="yes"
- shift
- elif [ "x$1" = "x--dev" ]; then
- run_args+=(--dev)
- dev_mode="yes"
- shift
- elif [ "x$1" = "x--temp-path" ]; then
- shift
- tmp_dir=$1
- if [ "x$tmp_dir" = "x" ]; then
- echo "$0 missing argument to --temp-path" 1>&2
- usage="yes"
- break
- fi
- shift
- elif [ "x$1" = "x--chroot" ]; then
- shift
- if [ "x$1" = "x" ]; then
- echo "$0 missing argument to --chroot" 1>&2
- usage="yes"
- break
- fi
- chroot="$1"
- run_args+=(--chroot "$1")
- shift
- elif [ "x$1" = "x--simpleperf" ]; then
- run_args+=(--simpleperf)
- shift
- elif [ "x$1" = "x--android-root" ]; then
- shift
- if [ "x$1" = "x" ]; then
- echo "$0 missing argument to --android-root" 1>&2
- usage="yes"
- break
- fi
- android_root="$1"
- run_args+=(--android-root "$1")
- shift
- elif [ "x$1" = "x--android-art-root" ]; then
- shift
- if [ "x$1" = "x" ]; then
- echo "$0 missing argument to --android-art-root" 1>&2
- usage="yes"
- break
- fi
- run_args+=(--android-art-root "$1")
- shift
- elif [ "x$1" = "x--android-tzdata-root" ]; then
- shift
- if [ "x$1" = "x" ]; then
- echo "$0 missing argument to --android-tzdata-root" 1>&2
- usage="yes"
- break
- fi
- run_args+=(--android-tzdata-root "$1")
- shift
- elif [ "x$1" = "x--update" ]; then
- update_mode="yes"
- shift
- elif [ "x$1" = "x--help" ]; then
- usage="yes"
- shift
- elif [ "x$1" = "x--64" ]; then
- run_args+=(--64)
- suffix64="64"
- shift
- elif [ "x$1" = "x--bionic" ]; then
- # soong linux_bionic builds are 64bit only.
- run_args+=(--bionic --host --64)
- suffix64="64"
- target_mode="no"
- DEX_LOCATION=$tmp_dir
- host_lib_root=$OUT_DIR/soong/host/linux_bionic-x86
- shift
- elif [ "x$1" = "x--runtime-extracted-zipapex" ]; then
- shift
- # TODO Should we allow the java.library.path to search the zipapex too?
- # Not needed at the moment and adding it will be complicated so for now
- # we'll ignore this.
- run_args+=(--host --runtime-extracted-zipapex "$1")
- target_mode="no"
- DEX_LOCATION=$tmp_dir
- shift
- elif [ "x$1" = "x--runtime-zipapex" ]; then
- shift
- # TODO Should we allow the java.library.path to search the zipapex too?
- # Not needed at the moment and adding it will be complicated so for now
- # we'll ignore this.
- run_args+=(--host --runtime-zipapex "$1")
- target_mode="no"
- DEX_LOCATION=$tmp_dir
- # apex_payload.zip is quite large we need a high enough ulimit to
- # extract it. 512mb should be good enough.
- file_ulimit=512000
- shift
- elif [ "x$1" = "x--timeout" ]; then
- shift
- if [ "x$1" = "x" ]; then
- echo "$0 missing argument to --timeout" 1>&2
- usage="yes"
- break
- fi
- timeout="$1"
- shift
- elif [ "x$1" = "x--trace" ]; then
- trace="true"
- shift
- elif [ "x$1" = "x--stream" ]; then
- trace_stream="true"
- shift
- elif [ "x$1" = "x--always-clean" ]; then
- always_clean="yes"
- shift
- elif [ "x$1" = "x--never-clean" ]; then
- never_clean="yes"
- shift
- elif [ "x$1" = "x--dex2oat-swap" ]; then
- run_args+=(--dex2oat-swap)
- shift
- elif [ "x$1" = "x--instruction-set-features" ]; then
- shift
- run_args+=(--instruction-set-features "$1")
- shift
- elif [ "x$1" = "x--bisection-search" ]; then
- bisection_search="yes"
- shift
- elif [ "x$1" = "x--vdex" ]; then
- run_args+=(--vdex)
- shift
- elif [ "x$1" = "x--dm" ]; then
- run_args+=(--dm)
- shift
- elif [ "x$1" = "x--vdex-filter" ]; then
- shift
- filter=$1
- run_args+=(--vdex-filter "$filter")
- shift
- elif [ "x$1" = "x--random-profile" ]; then
- run_args+=(--random-profile)
- shift
- elif [ "x$1" = "x--dex2oat-jobs" ]; then
- shift
- run_args+=(-Xcompiler-option "-j$1")
- shift
- elif expr "x$1" : "x--" >/dev/null 2>&1; then
- echo "unknown $0 option: $1" 1>&2
- usage="yes"
+ def shift():
+ global arg
+ args.pop(0)
+ arg = args[0] if args else ""
+
+ shift()
+
+ while True:
+ if arg == "--host":
+ target_mode = "no"
+ DEX_LOCATION = tmp_dir
+ run_args += ["--host"]
+ os.environ["RUN_MODE"] = "host"
+ shift()
+ elif arg == "--quiet":
+ quiet = "yes"
+ shift()
+ elif arg == "--use-java-home":
+ JAVA_HOME = os.environ.get("JAVA_HOME")
+ if JAVA_HOME:
+ export("JAVA", f"{JAVA_HOME}/bin/java")
+ export("JAVAC", f"{JAVA_HOME}/bin/javac -g")
+ else:
+ error("Passed --use-java-home without JAVA_HOME variable set!")
+ usage = "yes"
+ shift()
+ elif arg == "--jvm":
+ target_mode = "no"
+ DEX_LOCATION = tmp_dir
+ runtime = "jvm"
+ prebuild_mode = "no"
+ run_args += ["--jvm"]
+ shift()
+ elif arg == "-O":
+ lib = "libart.so"
+ testlib = "arttest"
+ run_args += ["-O"]
+ shift()
+ elif arg == "--dalvik":
+ lib = "libdvm.so"
+ runtime = "dalvik"
+ shift()
+ elif arg == "--no-image":
+ have_image = "no"
+ shift()
+ elif arg == "--relocate":
+ relocate = "yes"
+ shift()
+ elif arg == "--no-relocate":
+ relocate = "no"
+ shift()
+ elif arg == "--prebuild":
+ run_args += ["--prebuild"]
+ prebuild_mode = "yes"
+ shift()
+ elif arg == "--compact-dex-level":
+ option = arg
+ shift()
+ run_args += [f'"{option}" "{arg}"']
+ shift()
+ elif arg == "--strip-dex":
+ run_args += ["--strip-dex"]
+ shift()
+ elif arg == "--debuggable":
+ run_args += ["-Xcompiler-option --debuggable"]
+ debuggable = "yes"
+ shift()
+ elif arg == "--no-prebuild":
+ run_args += ["--no-prebuild"]
+ prebuild_mode = "no"
+ shift()
+ elif arg == "--gcverify":
+ basic_verify = "true"
+ gc_verify = "true"
+ shift()
+ elif arg == "--gcstress":
+ basic_verify = "true"
+ gc_stress = "true"
+ shift()
+ elif arg == "--jvmti-step-stress":
+ jvmti_step_stress = "true"
+ os.environ["JVMTI_STEP_STRESS"] = "true"
+ shift()
+ elif arg == "--jvmti-redefine-stress":
+ jvmti_redefine_stress = "true"
+ os.environ["JVMTI_REDEFINE_STRESS"] = "true"
+ shift()
+ elif arg == "--jvmti-field-stress":
+ jvmti_field_stress = "true"
+ os.environ["JVMTI_FIELD_STRESS"] = "true"
+ shift()
+ elif arg == "--jvmti-trace-stress":
+ jvmti_trace_stress = "true"
+ os.environ["JVMTI_TRACE_STRESS"] = "true"
+ shift()
+ elif arg == "--suspend-timeout":
+ shift()
+ suspend_timeout = arg
+ shift()
+ elif arg == "--image":
+ shift()
+ image = arg
+ run_args += [f'--image "{image}"']
+ shift()
+ elif arg == "-Xcompiler-option":
+ shift()
+ option = arg
+ run_args += [f'-Xcompiler-option "{option}"']
+ shift()
+ elif arg == "--runtime-option":
+ shift()
+ option = arg
+ run_args += [f'--runtime-option "{option}"']
+ shift()
+ elif arg == "--gdb-arg":
+ shift()
+ gdb_arg = arg
+ run_args += [f'--gdb-arg "{gdb_arg}"']
+ shift()
+ elif arg == "--gdb-dex2oat-args":
+ shift()
+ gdb_dex2oat_args = arg
+ run_args += ['--gdb-dex2oat-args "{gdb_dex2oat_args}"']
+ shift()
+ elif arg == "--debug":
+ run_args += ["--debug"]
+ shift()
+ elif arg == "--debug-wrap-agent":
+ run_args += ["--debug-wrap-agent"]
+ shift()
+ elif arg == "--with-agent":
+ shift()
+ option = arg
+ run_args += [f'--with-agent "{arg}"']
+ shift()
+ elif arg == "--debug-agent":
+ shift()
+ option = arg
+ run_args += [f'--debug-agent "{arg}"']
+ shift()
+ elif arg == "--dump-cfg":
+ shift()
+ dump_cfg = "true"
+ dump_cfg_path = arg
+ shift()
+ elif arg == "--gdb":
+ run_args += ["--gdb"]
+ dev_mode = "yes"
+ shift()
+ elif arg == "--gdb-dex2oat":
+ run_args += ["--gdb-dex2oat"]
+ dev_mode = "yes"
+ shift()
+ elif arg == "--gdbserver-bin":
+ shift()
+ run_args += [f'--gdbserver-bin "{arg}"']
+ shift()
+ elif arg == "--gdbserver-port":
+ shift()
+ run_args += [f'--gdbserver-port "{arg}"']
+ shift()
+ elif arg == "--gdbserver":
+ run_args += ["--gdbserver"]
+ dev_mode = "yes"
+ shift()
+ elif arg == "--strace":
+ strace = "yes"
+ run_args += [
+ f'--invoke-with=strace --invoke-with=-o --invoke-with="{tmp_dir}/{strace_output}"'
+ ]
+ timeout = timeout or "1800"
+ shift()
+ elif arg == "--zygote":
+ run_args += ["--zygote"]
+ shift()
+ elif arg == "--interpreter":
+ run_args += ["--interpreter"]
+ shift()
+ elif arg == "--jit":
+ run_args += ["--jit"]
+ shift()
+ elif arg == "--baseline":
+ run_args += ["--baseline"]
+ shift()
+ elif arg == "--optimizing":
+ run_optimizing = "true"
+ shift()
+ elif arg == "--no-verify":
+ run_args += ["--no-verify"]
+ shift()
+ elif arg == "--verify-soft-fail":
+ run_args += ["--verify-soft-fail"]
+ os.environ["VERIFY_SOFT_FAIL"] = "true"
+ shift()
+ elif arg == "--no-optimize":
+ run_args += ["--no-optimize"]
+ shift()
+ elif arg == "--no-precise":
+ run_args += ["--no-precise"]
+ shift()
+ elif arg.startswith("--android-log-tags"):
+ run_args += [arg]
+ shift()
+ elif arg == "--external-log-tags":
+ run_args += ["--external-log-tags"]
+ shift()
+ elif arg == "--invoke-with":
+ shift()
+ what = arg
+ if not arg:
+ error("missing argument to --invoke-with")
+ usage = "yes"
break
- else
+ run_args += [f'--invoke-with "{what}"']
+ shift()
+ elif arg == "--create-runner":
+ run_args += ["--create-runner --dry-run"]
+ dev_mode = "yes"
+ never_clean = "yes"
+ create_runner = "yes"
+ shift()
+ elif arg == "--dev":
+ dev_mode = "yes"
+ shift()
+ elif arg == "--temp-path":
+ shift()
+ if not arg:
+ error("missing argument to --temp-path")
+ usage = "yes"
break
- fi
-done
+ shift()
+ elif arg == "--chroot":
+ shift()
+ if not arg:
+ error("missing argument to --chroot")
+ usage = "yes"
+ break
+ chroot = arg
+ run_args += [f'--chroot "{arg}"']
+ shift()
+ elif arg == "--simpleperf":
+ run_args += ["--simpleperf"]
+ shift()
+ elif arg == "--android-root":
+ shift()
+ if not arg:
+ error("missing argument to --android-root")
+ usage = "yes"
+ break
+ android_root = arg
+ run_args += [f'--android-root "{arg}"']
+ shift()
+ elif arg == "--android-art-root":
+ shift()
+ if not arg:
+ error("missing argument to --android-art-root")
+ usage = "yes"
+ break
+ run_args += [f'--android-art-root "{arg}"']
+ shift()
+ elif arg == "--android-tzdata-root":
+ shift()
+ if not arg:
+ error("missing argument to --android-tzdata-root")
+ usage = "yes"
+ break
+ run_args += [f'--android-tzdata-root "{arg}"']
+ shift()
+ elif arg == "--update":
+ update_mode = "yes"
+ shift()
+ elif arg == "--help":
+ usage = "yes"
+ shift()
+ elif arg == "--64":
+ run_args += ["--64"]
+ suffix64 = "64"
+ shift()
+ elif arg == "--bionic":
+ # soong linux_bionic builds are 64bit only.
+ run_args += ["--bionic --host --64"]
+ suffix64 = "64"
+ target_mode = "no"
+ DEX_LOCATION = tmp_dir
+ host_lib_root = f"{OUT_DIR}/soong/host/linux_bionic-x86"
+ shift()
+ elif arg == "--runtime-extracted-zipapex":
+ shift()
+ # TODO Should we allow the java.library.path to search the zipapex too?
+ # Not needed at the moment and adding it will be complicated so for now
+ # we'll ignore this.
+ run_args += [f'--host --runtime-extracted-zipapex "{arg}"']
+ target_mode = "no"
+ DEX_LOCATION = tmp_dir
+ shift()
+ elif arg == "--runtime-zipapex":
+ shift()
+ # TODO Should we allow the java.library.path to search the zipapex too?
+ # Not needed at the moment and adding it will be complicated so for now
+ # we'll ignore this.
+ run_args += [f'--host --runtime-zipapex "{arg}"']
+ target_mode = "no"
+ DEX_LOCATION = tmp_dir
+ # apex_payload.zip is quite large we need a high enough ulimit to
+ # extract it. 512mb should be good enough.
+ file_ulimit = 512000
+ shift()
+ elif arg == "--timeout":
+ shift()
+ if not arg:
+ error("missing argument to --timeout")
+ usage = "yes"
+ break
+ timeout = arg
+ shift()
+ elif arg == "--trace":
+ trace = "true"
+ shift()
+ elif arg == "--stream":
+ trace_stream = "true"
+ shift()
+ elif arg == "--always-clean":
+ always_clean = "yes"
+ shift()
+ elif arg == "--never-clean":
+ never_clean = "yes"
+ shift()
+ elif arg == "--dex2oat-swap":
+ run_args += ["--dex2oat-swap"]
+ shift()
+ elif arg == "--instruction-set-features":
+ shift()
+ run_args += [f'--instruction-set-features "{arg}"']
+ shift()
+ elif arg == "--bisection-search":
+ bisection_search = "yes"
+ shift()
+ elif arg == "--vdex":
+ run_args += ["--vdex"]
+ shift()
+ elif arg == "--dm":
+ run_args += ["--dm"]
+ shift()
+ elif arg == "--vdex-filter":
+ shift()
+ filter = arg
+ run_args += ['--vdex-filter "{filter}"']
+ shift()
+ elif arg == "--random-profile":
+ run_args += ["--random-profile"]
+ shift()
+ elif arg == "--dex2oat-jobs":
+ shift()
+ run_args += [f'-Xcompiler-option "-j{arg}"']
+ shift()
+ elif arg.startswith("--"):
+ error(f"unknown option: {arg}")
+ usage = "yes"
+ break
+ else:
+ break
-if [ "$usage" = "no" -a "x$1" = "x" ]; then
- echo "missing test to run" 1>&2
- usage="yes"
-fi
+ export("DEX_LOCATION", DEX_LOCATION)
+
+ if usage == "no" and not arg:
+ error("missing test to run")
+ usage = "yes"
# The DEX_LOCATION with the chroot prefix, if any.
-chroot_dex_location="$chroot$DEX_LOCATION"
+ chroot_dex_location = f"{chroot}{DEX_LOCATION}"
-# Allocate file descriptor real_stderr and redirect it to the shell's error
-# output (fd 2).
-if [ ${BASH_VERSINFO[1]} -ge 4 ] && [ ${BASH_VERSINFO[2]} -ge 1 ]; then
- exec {real_stderr}>&2
-else
- # In bash before version 4.1 we need to do a manual search for free file
- # descriptors.
- FD=3
- while [ -e /dev/fd/$FD ]; do FD=$((FD + 1)); done
- real_stderr=$FD
- eval "exec ${real_stderr}>&2"
-fi
-if [ "$quiet" = "yes" ]; then
- # Force the default standard output and error to go to /dev/null so we will
- # not print them.
- exec 1>/dev/null
- exec 2>/dev/null
-fi
+ # tmp_dir may be relative, resolve.
+ os.chdir(oldwd)
+ tmp_dir = os.path.realpath(tmp_dir)
+ os.chdir(progdir)
+ if not tmp_dir:
+ error(f"Failed to resolve {tmp_dir}")
+ sys.exit(1)
+ os.makedirs(tmp_dir, exist_ok=True)
-function err_echo() {
- echo "$@" 1>&${real_stderr}
-}
+ # Add thread suspend timeout flag
+ if runtime != "jvm":
+ run_args += [
+ f'--runtime-option "-XX:ThreadSuspendTimeout={suspend_timeout}"'
+ ]
-# tmp_dir may be relative, resolve.
-#
-# Cannot use realpath, as it does not exist on Mac.
-# Cannot use a simple "cd", as the path might not be created yet.
-# Cannot use readlink -m, as it does not exist on Mac.
-# Fallback to nuclear option:
-noncanonical_tmp_dir=$tmp_dir
-tmp_dir="`cd $oldwd ; python3 -c "import os; import sys; sys.stdout.write(os.path.realpath('$tmp_dir'))"`"
-if [ -z $tmp_dir ] ; then
- err_echo "Failed to resolve $tmp_dir"
- exit 1
-fi
-mkdir -p $tmp_dir
-
-# Add thread suspend timeout flag
-if [ ! "$runtime" = "jvm" ]; then
- run_args+=(--runtime-option "-XX:ThreadSuspendTimeout=$suspend_timeout")
-fi
-
-if [ "$basic_verify" = "true" ]; then
- # Set HspaceCompactForOOMMinIntervalMs to zero to run hspace compaction for OOM more frequently in tests.
- run_args+=(--runtime-option -Xgc:preverify --runtime-option -Xgc:postverify --runtime-option -XX:HspaceCompactForOOMMinIntervalMs=0)
-fi
-if [ "$gc_verify" = "true" ]; then
- run_args+=(--runtime-option -Xgc:preverify_rosalloc --runtime-option -Xgc:postverify_rosalloc)
-fi
-if [ "$gc_stress" = "true" ]; then
- run_args+=(--gc-stress --runtime-option -Xgc:gcstress --runtime-option -Xms2m --runtime-option -Xmx16m)
-fi
-if [ "$jvmti_redefine_stress" = "true" ]; then
- run_args+=(--no-app-image --jvmti-redefine-stress)
-fi
-if [ "$jvmti_step_stress" = "true" ]; then
- run_args+=(--no-app-image --jvmti-step-stress)
-fi
-if [ "$jvmti_field_stress" = "true" ]; then
- run_args+=(--no-app-image --jvmti-field-stress)
-fi
-if [ "$jvmti_trace_stress" = "true" ]; then
- run_args+=(--no-app-image --jvmti-trace-stress)
-fi
-if [ "$trace" = "true" ]; then
- run_args+=(--runtime-option -Xmethod-trace --runtime-option -Xmethod-trace-file-size:2000000)
- if [ "$trace_stream" = "true" ]; then
- # Streaming mode uses the file size as the buffer size. So output gets really large. Drop
- # the ability to analyze the file and just write to /dev/null.
- run_args+=(--runtime-option -Xmethod-trace-file:/dev/null)
- # Enable streaming mode.
- run_args+=(--runtime-option -Xmethod-trace-stream)
- else
- run_args+=(--runtime-option "-Xmethod-trace-file:${DEX_LOCATION}/trace.bin")
- fi
-elif [ "$trace_stream" = "true" ]; then
- err_echo "Cannot use --stream without --trace."
- exit 1
-fi
-if [ -n "$timeout" ]; then
- run_args+=(--timeout "$timeout")
-fi
+ if basic_verify == "true":
+ # Set HspaceCompactForOOMMinIntervalMs to zero to run hspace compaction for OOM more frequently in tests.
+ run_args += [
+ "--runtime-option -Xgc:preverify --runtime-option -Xgc:postverify "
+ "--runtime-option -XX:HspaceCompactForOOMMinIntervalMs=0"
+ ]
+ if gc_verify == "true":
+ run_args += [
+ "--runtime-option -Xgc:preverify_rosalloc --runtime-option "
+ "-Xgc:postverify_rosalloc"
+ ]
+ if gc_stress == "true":
+ run_args += [
+ "--gc-stress --runtime-option -Xgc:gcstress --runtime-option -Xms2m "
+ "--runtime-option -Xmx16m"
+ ]
+ if jvmti_redefine_stress == "true":
+ run_args += ["--no-app-image --jvmti-redefine-stress"]
+ if jvmti_step_stress == "true":
+ run_args += ["--no-app-image --jvmti-step-stress"]
+ if jvmti_field_stress == "true":
+ run_args += ["--no-app-image --jvmti-field-stress"]
+ if jvmti_trace_stress == "true":
+ run_args += ["--no-app-image --jvmti-trace-stress"]
+ if trace == "true":
+ run_args += [
+ "--runtime-option -Xmethod-trace --runtime-option "
+ "-Xmethod-trace-file-size:2000000"
+ ]
+ if trace_stream == "true":
+ # Streaming mode uses the file size as the buffer size. So output gets really large. Drop
+ # the ability to analyze the file and just write to /dev/null.
+ run_args += ["--runtime-option -Xmethod-trace-file:/dev/null"]
+ # Enable streaming mode.
+ run_args += ["--runtime-option -Xmethod-trace-stream"]
+ else:
+ run_args += [
+ f'--runtime-option "-Xmethod-trace-file:{DEX_LOCATION}/trace.bin"'
+ ]
+ elif trace_stream == "true":
+ error("Cannot use --stream without --trace.")
+ sys.exit(1)
+ if timeout:
+ run_args += [f'--timeout "{timeout}"']
# Most interesting target architecture variables are Makefile variables, not environment variables.
-# Try to map the suffix64 flag and what we find in ${ANDROID_PRODUCT_OUT}/data/art-test to an architecture name.
-function guess_target_arch_name() {
- # Check whether this is a device with native bridge. Currently this is hardcoded
- # to x86 + arm.
- local guess_path=$chroot/system/framework/art_boot_images
- local x86_arm=`adb shell ls ${guess_path} | sort | grep -E '^(arm|x86)$'`
- # Collapse line-breaks into spaces
- x86_arm=$(echo $x86_arm)
- if [ "x$x86_arm" = "xarm x86" ] ; then
- err_echo "Native-bridge configuration detected."
- # We only support the main arch for tests.
- if [ "x${suffix64}" = "x64" ]; then
- target_arch_name=""
- else
- target_arch_name=x86
- fi
- else
- local grep32bit=`adb shell ls ${guess_path} | grep -E '^(arm|x86)$'`
- local grep64bit=`adb shell ls ${guess_path} | grep -E '^(arm64|x86_64)$'`
- if [ "x${suffix64}" = "x64" ]; then
- target_arch_name=${grep64bit}
- else
- target_arch_name=${grep32bit}
- fi
- fi
-}
+# Try to map the suffix64 flag and what we find in {ANDROID_PRODUCT_OUT}/data/art-test to an architecture name.
-function guess_host_arch_name() {
- if [ "x${suffix64}" = "x64" ]; then
- host_arch_name="x86_64"
- else
- host_arch_name="x86"
- fi
-}
+ def guess_target_arch_name():
+ return get_target_arch(suffix64 == "64")
-if [ "$target_mode" = "no" ]; then
- if [ "$runtime" = "jvm" ]; then
- if [ "$prebuild_mode" = "yes" ]; then
- err_echo "--prebuild with --jvm is unsupported"
- exit 1
- fi
- else
- # ART/Dalvik host mode.
- if [ -n "$chroot" ]; then
- err_echo "--chroot with --host is unsupported"
- exit 1
- fi
- fi
-fi
+ def guess_host_arch_name():
+ if suffix64 == "64":
+ return "x86_64"
+ else:
+ return "x86"
-if [ ! "$runtime" = "jvm" ]; then
- run_args+=(--lib "$lib")
-fi
+ if target_mode == "no":
+ if runtime == "jvm":
+ if prebuild_mode == "yes":
+ error("--prebuild with --jvm is unsupported")
+ sys.exit(1)
+ else:
+ # ART/Dalvik host mode.
+ if chroot:
+ error("--chroot with --host is unsupported")
+ sys.exit(1)
-if [ "$runtime" = "dalvik" ]; then
- if [ "$target_mode" = "no" ]; then
- framework="${ANDROID_PRODUCT_OUT}/system/framework"
- bpath="${framework}/core-icu4j.jar:${framework}/core-libart.jar:${framework}/core-oj.jar:${framework}/conscrypt.jar:${framework}/okhttp.jar:${framework}/bouncycastle.jar:${framework}/ext.jar"
- run_args+=(--boot --runtime-option "-Xbootclasspath:${bpath}")
- else
- true # defaults to using target BOOTCLASSPATH
- fi
-elif [ "$runtime" = "art" ]; then
- if [ "$target_mode" = "no" ]; then
- guess_host_arch_name
- run_args+=(--boot "${ANDROID_HOST_OUT}/apex/art_boot_images/javalib/boot.art")
- run_args+=(--runtime-option "-Djava.library.path=${host_lib_root}/lib${suffix64}:${host_lib_root}/nativetest${suffix64}")
- else
- guess_target_arch_name
- # Note that libarttest(d).so and other test libraries that depend on ART
- # internal libraries must not be in this path for JNI libraries - they
- # need to be loaded through LD_LIBRARY_PATH and
- # NATIVELOADER_DEFAULT_NAMESPACE_LIBS instead.
- run_args+=(--runtime-option "-Djava.library.path=/data/nativetest${suffix64}/art/${target_arch_name}")
- run_args+=(--boot "/system/framework/art_boot_images/boot.art")
- fi
- if [ "$relocate" = "yes" ]; then
- run_args+=(--relocate)
- else
- run_args+=(--no-relocate)
- fi
-elif [ "$runtime" = "jvm" ]; then
+ if runtime != "jvm":
+ run_args += [f'--lib "{lib}"']
+
+ ANDROID_PRODUCT_OUT = os.environ.get("ANDROID_PRODUCT_OUT")
+ if runtime == "dalvik":
+ if target_mode == "no":
+ framework = f"{ANDROID_PRODUCT_OUT}/system/framework"
+ bpath = f"{framework}/core-icu4j.jar:{framework}/core-libart.jar:{framework}/core-oj.jar:{framework}/conscrypt.jar:{framework}/okhttp.jar:{framework}/bouncycastle.jar:{framework}/ext.jar"
+ run_args += [f'--boot --runtime-option "-Xbootclasspath:{bpath}"']
+ else:
+ pass # defaults to using target BOOTCLASSPATH
+ elif runtime == "art":
+ if target_mode == "no":
+ host_arch_name = guess_host_arch_name()
+ run_args += [
+ f'--boot "{ANDROID_HOST_OUT}/apex/art_boot_images/javalib/boot.art"'
+ ]
+ run_args += [
+ f'--runtime-option "-Djava.library.path={host_lib_root}/lib{suffix64}:{host_lib_root}/nativetest{suffix64}"'
+ ]
+ else:
+ target_arch_name = guess_target_arch_name()
+ # Note that libarttest(d).so and other test libraries that depend on ART
+ # internal libraries must not be in this path for JNI libraries - they
+ # need to be loaded through LD_LIBRARY_PATH and
+ # NATIVELOADER_DEFAULT_NAMESPACE_LIBS instead.
+ run_args += [
+ f'--runtime-option "-Djava.library.path=/data/nativetest{suffix64}/art/{target_arch_name}"'
+ ]
+ run_args += ['--boot "/system/framework/art_boot_images/boot.art"']
+ if relocate == "yes":
+ run_args += ["--relocate"]
+ else:
+ run_args += ["--no-relocate"]
+ elif runtime == "jvm":
# TODO: Detect whether the host is 32-bit or 64-bit.
- run_args+=(--runtime-option "-Djava.library.path=${ANDROID_HOST_OUT}/lib64:${ANDROID_HOST_OUT}/nativetest64")
-fi
+ run_args += [
+ f'--runtime-option "-Djava.library.path={ANDROID_HOST_OUT}/lib64:{ANDROID_HOST_OUT}/nativetest64"'
+ ]
-if [ "$have_image" = "no" ]; then
- if [ "$runtime" != "art" ]; then
- err_echo "--no-image is only supported on the art runtime"
- exit 1
- fi
- run_args+=(--no-image)
-fi
+ if have_image == "no":
+ if runtime != "art":
+ error("--no-image is only supported on the art runtime")
+ sys.exit(1)
+ run_args += ["--no-image"]
-if [ "$create_runner" = "yes" -a "$target_mode" = "yes" ]; then
- err_echo "--create-runner does not function for non --host tests"
- usage="yes"
-fi
+ if create_runner == "yes" and target_mode == "yes":
+ error("--create-runner does not function for non --host tests")
+ usage = "yes"
-if [ "$dev_mode" = "yes" -a "$update_mode" = "yes" ]; then
- err_echo "--dev and --update are mutually exclusive"
- usage="yes"
-fi
+ if dev_mode == "yes" and update_mode == "yes":
+ error("--dev and --update are mutually exclusive")
+ usage = "yes"
-if [ "$dev_mode" = "yes" -a "$quiet" = "yes" ]; then
- err_echo "--dev and --quiet are mutually exclusive"
- usage="yes"
-fi
+ if dev_mode == "yes" and quiet == "yes":
+ error("--dev and --quiet are mutually exclusive")
+ usage = "yes"
-if [ "$bisection_search" = "yes" -a "$prebuild_mode" = "yes" ]; then
- err_echo "--bisection-search and --prebuild are mutually exclusive"
- usage="yes"
-fi
+ if bisection_search == "yes" and prebuild_mode == "yes":
+ error("--bisection-search and --prebuild are mutually exclusive")
+ usage = "yes"
# TODO: Chroot-based bisection search is not supported yet (see below); implement it.
-if [ "$bisection_search" = "yes" -a -n "$chroot" ]; then
- err_echo "--chroot with --bisection-search is unsupported"
- exit 1
-fi
+ if bisection_search == "yes" and chroot:
+ error("--chroot with --bisection-search is unsupported")
+ sys.exit(1)
-if [ "$usage" = "no" ]; then
- if [ "x$1" = "x" -o "x$1" = "x-" ]; then
- test_dir=`basename "$oldwd"`
- else
- test_dir="$1"
- fi
+ if usage == "no":
+ if not arg or arg == "-":
+ test_dir = os.path.basename(oldwd)
+ else:
+ test_dir = arg
- if [ '!' -d "$test_dir" ]; then
- td2=`echo ${test_dir}-*`
- if [ '!' -d "$td2" ]; then
- err_echo "${test_dir}: no such test directory"
- usage="yes"
- fi
- test_dir="$td2"
- fi
+ if not os.path.isdir(test_dir):
+ td2 = glob.glob(f"{test_dir}-*")
+ if len(td2) == 1 and os.path.isdir(td2[0]):
+ test_dir = td2[0]
+ else:
+ error(f"{test_dir}: no such test directory")
+ usage = "yes"
# Shift to get rid of the test name argument. The rest of the arguments
# will get passed to the test run.
- shift
-fi
+ shift()
-if [ "$usage" = "yes" ]; then
- prog=`basename $prog`
- (
- echo "usage:"
- echo " $prog --help Print this message."
- echo " $prog [options] [test-name] Run test normally."
- echo " $prog --dev [options] [test-name] Development mode" \
- "(dumps to stdout)."
- echo " $prog --create-runner [options] [test-name]"
- echo " Creates a runner script for use with other " \
- "tools (e.g. parallel_run.py)."
- echo " The script will only run the test portion, and " \
- "share oat and dex files."
- echo " $prog --update [options] [test-name] Update mode" \
- "(replaces expected-stdout.txt and expected-stderr.txt)."
- echo ' Omitting the test name or specifying "-" will use the' \
- "current directory."
- echo " Runtime Options:"
- echo " -O Run non-debug rather than debug build (off by default)."
- echo " -Xcompiler-option Pass an option to the compiler."
- echo " --runtime-option Pass an option to the runtime."
- echo " --compact-dex-level Specify a compact dex level to the compiler."
- echo " --debug Wait for the default debugger to attach."
- echo " --debug-agent <agent-path>"
- echo " Wait for the given debugger agent to attach. Currently"
- echo " only supported on host."
- echo " --debug-wrap-agent use libwrapagentproperties and tools/libjdwp-compat.props"
- echo " to load the debugger agent specified by --debug-agent."
- echo " --with-agent <agent> Run the test with the given agent loaded with -agentpath:"
- echo " --debuggable Whether to compile Java code for a debugger."
- echo " --gdb Run under gdb; incompatible with some tests."
- echo " --gdb-dex2oat Run dex2oat under the prebuilt lldb."
- echo " --gdbserver Start gdbserver (defaults to port :5039)."
- echo " --gdbserver-port <port>"
- echo " Start gdbserver with the given COMM (see man gdbserver)."
- echo " --gdbserver-bin <binary>"
- echo " Use the given binary as gdbserver."
- echo " --gdb-arg Pass an option to gdb or gdbserver."
- echo " --gdb-dex2oat-args Pass options separated by ';' to lldb for dex2oat."
- echo " --simpleperf Wraps the dalvikvm invocation in 'simpleperf record ..."
- echo " ... simpleperf report' and dumps stats to stdout."
- echo " --temp-path [path] Location where to execute the tests."
- echo " --interpreter Enable interpreter only mode (off by default)."
- echo " --jit Enable jit (off by default)."
- echo " --optimizing Enable optimizing compiler (default)."
- echo " --no-verify Turn off verification (on by default)."
- echo " --verify-soft-fail Force soft fail verification (off by default)."
- echo " Verification is enabled if neither --no-verify"
- echo " nor --verify-soft-fail is specified."
- echo " --no-optimize Turn off optimization (on by default)."
- echo " --no-precise Turn off precise GC (on by default)."
- echo " --zygote Spawn the process from the Zygote." \
- "If used, then the"
- echo " other runtime options are ignored."
- echo " --prebuild Run dex2oat on the files before starting test. (default)"
- echo " --no-prebuild Do not run dex2oat on the files before starting"
- echo " the test."
- echo " --strip-dex Strip the dex files before starting test."
- echo " --relocate Force the use of relocating in the test, making"
- echo " the image and oat files be relocated to a random"
- echo " address before running."
- echo " --no-relocate Force the use of no relocating in the test. (default)"
- echo " --image Run the test using a precompiled boot image. (default)"
- echo " --no-image Run the test without a precompiled boot image."
- echo " --host Use the host-mode virtual machine."
- echo " --invoke-with Pass --invoke-with option to runtime."
- echo " --dalvik Use Dalvik (off by default)."
- echo " --jvm Use a host-local RI virtual machine."
- echo " --use-java-home Use the JAVA_HOME environment variable"
- echo " to find the java compiler and runtime"
- echo " (if applicable) to run the test with."
- echo " --64 Run the test in 64-bit mode"
- echo " --bionic Use the (host, 64-bit only) linux_bionic libc runtime"
- echo " --runtime-zipapex [file]"
- echo " Use the given zipapex file to provide runtime binaries"
- echo " --runtime-extracted-zipapex [dir]"
- echo " Use the given extracted zipapex directory to provide"
- echo " runtime binaries"
- echo " --timeout n Test timeout in seconds"
- echo " --trace Run with method tracing"
- echo " --strace Run with syscall tracing from strace."
- echo " --stream Run method tracing in streaming mode (requires --trace)"
- echo " --gcstress Run with gc stress testing"
- echo " --gcverify Run with gc verification"
- echo " --jvmti-trace-stress Run with jvmti method tracing stress testing"
- echo " --jvmti-step-stress Run with jvmti single step stress testing"
- echo " --jvmti-redefine-stress"
- echo " Run with jvmti method redefinition stress testing"
- echo " --always-clean Delete the test files even if the test fails."
- echo " --never-clean Keep the test files even if the test succeeds."
- echo " --chroot [newroot] Run with root directory set to newroot."
- echo " --android-root [path] The path on target for the android root. (/system by default)."
- echo " --android-i18n-root [path]"
- echo " The path on target for the i18n module root."
- echo " (/apex/com.android.i18n by default)."
- echo " --android-art-root [path]"
- echo " The path on target for the ART module root."
- echo " (/apex/com.android.art by default)."
- echo " --android-tzdata-root [path]"
- echo " The path on target for the Android Time Zone Data root."
- echo " (/apex/com.android.tzdata by default)."
- echo " --dex2oat-swap Use a dex2oat swap file."
- echo " --instruction-set-features [string]"
- echo " Set instruction-set-features for compilation."
- echo " --quiet Don't print anything except failure messages"
- echo " --external-log-tags Use ANDROID_LOG_TAGS to set a custom logging level for"
- echo " a test run."
- echo " --bisection-search Perform bisection bug search."
- echo " --vdex Test using vdex as in input to dex2oat. Only works with --prebuild."
- echo " --suspend-timeout Change thread suspend timeout ms (default 500000)."
- echo " --dex2oat-jobs Number of dex2oat jobs."
- ) 1>&2 # Direct to stderr so usage is not printed if --quiet is set.
- exit 1
-fi
+ if usage == "yes":
+ prog = os.path.basename(__file__)
+ # pyformat: disable
+ help=(
+ "usage:\n"
+ f" $prog --help Print this message.\n"
+ f" $prog [options] [test-name] Run test normally.\n"
+ f" $prog --dev [options] [test-name] Development mode\n"
+ "(dumps to stdout).\n"
+ f" $prog --create-runner [options] [test-name]\n"
+ " Creates a runner script for use with other \n"
+ "tools (e.g. parallel_run.py).\n"
+ " The script will only run the test portion, and \n"
+ "share oat and dex files.\n"
+ f" $prog --update [options] [test-name] Update mode\n"
+ "(replaces expected-stdout.txt and expected-stderr.txt).\n"
+ ' Omitting the test name or specifying "-" will use the\n'
+ "current directory.\n"
+ " Runtime Options:\n"
+ " -O Run non-debug rather than debug build (off by default).\n"
+ " -Xcompiler-option Pass an option to the compiler.\n"
+ " --runtime-option Pass an option to the runtime.\n"
+ " --compact-dex-level Specify a compact dex level to the compiler.\n"
+ " --debug Wait for the default debugger to attach.\n"
+ " --debug-agent <agent-path>\n"
+ " Wait for the given debugger agent to attach. Currently\n"
+ " only supported on host.\n"
+ " --debug-wrap-agent use libwrapagentproperties and tools/libjdwp-compat.props\n"
+ " to load the debugger agent specified by --debug-agent.\n"
+ " --with-agent <agent> Run the test with the given agent loaded with -agentpath:\n"
+ " --debuggable Whether to compile Java code for a debugger.\n"
+ " --gdb Run under gdb; incompatible with some tests.\n"
+ " --gdb-dex2oat Run dex2oat under the prebuilt lldb.\n"
+ " --gdbserver Start gdbserver (defaults to port :5039).\n"
+ " --gdbserver-port <port>\n"
+ " Start gdbserver with the given COMM (see man gdbserver).\n"
+ " --gdbserver-bin <binary>\n"
+ " Use the given binary as gdbserver.\n"
+ " --gdb-arg Pass an option to gdb or gdbserver.\n"
+ " --gdb-dex2oat-args Pass options separated by ';' to lldb for dex2oat.\n"
+ " --simpleperf Wraps the dalvikvm invocation in 'simpleperf record ...\n"
+ " ... simpleperf report' and dumps stats to stdout.\n"
+ " --temp-path [path] Location where to execute the tests.\n"
+ " --interpreter Enable interpreter only mode (off by default).\n"
+ " --jit Enable jit (off by default).\n"
+ " --optimizing Enable optimizing compiler (default).\n"
+ " --no-verify Turn off verification (on by default).\n"
+ " --verify-soft-fail Force soft fail verification (off by default).\n"
+ " Verification is enabled if neither --no-verify\n"
+ " nor --verify-soft-fail is specified.\n"
+ " --no-optimize Turn off optimization (on by default).\n"
+ " --no-precise Turn off precise GC (on by default).\n"
+ " --zygote Spawn the process from the Zygote.\n"
+ "If used, then the\n"
+ " other runtime options are ignored.\n"
+ " --prebuild Run dex2oat on the files before starting test. (default)\n"
+ " --no-prebuild Do not run dex2oat on the files before starting\n"
+ " the test.\n"
+ " --strip-dex Strip the dex files before starting test.\n"
+ " --relocate Force the use of relocating in the test, making\n"
+ " the image and oat files be relocated to a random\n"
+ " address before running.\n"
+ " --no-relocate Force the use of no relocating in the test. (default)\n"
+ " --image Run the test using a precompiled boot image. (default)\n"
+ " --no-image Run the test without a precompiled boot image.\n"
+ " --host Use the host-mode virtual machine.\n"
+ " --invoke-with Pass --invoke-with option to runtime.\n"
+ " --dalvik Use Dalvik (off by default).\n"
+ " --jvm Use a host-local RI virtual machine.\n"
+ " --use-java-home Use the JAVA_HOME environment variable\n"
+ " to find the java compiler and runtime\n"
+ " (if applicable) to run the test with.\n"
+ " --64 Run the test in 64-bit mode\n"
+ " --bionic Use the (host, 64-bit only) linux_bionic libc runtime\n"
+ " --runtime-zipapex [file]\n"
+ " Use the given zipapex file to provide runtime binaries\n"
+ " --runtime-extracted-zipapex [dir]\n"
+ " Use the given extracted zipapex directory to provide\n"
+ " runtime binaries\n"
+ " --timeout n Test timeout in seconds\n"
+ " --trace Run with method tracing\n"
+ " --strace Run with syscall tracing from strace.\n"
+ " --stream Run method tracing in streaming mode (requires --trace)\n"
+ " --gcstress Run with gc stress testing\n"
+ " --gcverify Run with gc verification\n"
+ " --jvmti-trace-stress Run with jvmti method tracing stress testing\n"
+ " --jvmti-step-stress Run with jvmti single step stress testing\n"
+ " --jvmti-redefine-stress\n"
+ " Run with jvmti method redefinition stress testing\n"
+ " --always-clean Delete the test files even if the test fails.\n"
+ " --never-clean Keep the test files even if the test succeeds.\n"
+ " --chroot [newroot] Run with root directory set to newroot.\n"
+ " --android-root [path] The path on target for the android root. (/system by default).\n"
+ " --android-i18n-root [path]\n"
+ " The path on target for the i18n module root.\n"
+ " (/apex/com.android.i18n by default).\n"
+ " --android-art-root [path]\n"
+ " The path on target for the ART module root.\n"
+ " (/apex/com.android.art by default).\n"
+ " --android-tzdata-root [path]\n"
+ " The path on target for the Android Time Zone Data root.\n"
+ " (/apex/com.android.tzdata by default).\n"
+ " --dex2oat-swap Use a dex2oat swap file.\n"
+ " --instruction-set-features [string]\n"
+ " Set instruction-set-features for compilation.\n"
+ " --quiet Don't print anything except failure messages\n"
+ " --external-log-tags Use ANDROID_LOG_TAGS to set a custom logging level for\n"
+ " a test run.\n"
+ " --bisection-search Perform bisection bug search.\n"
+ " --vdex Test using vdex as in input to dex2oat. Only works with --prebuild.\n"
+ " --suspend-timeout Change thread suspend timeout ms (default 500000).\n"
+ " --dex2oat-jobs Number of dex2oat jobs.\n"
+ )
+ # pyformat: enable
+ error(help)
+ sys.exit(1)
-cd "$test_dir"
-test_dir=`pwd`
+ os.chdir(test_dir)
+ test_dir = os.getcwd()
-td_info="${test_dir}/${info}"
-td_expected_stdout="${test_dir}/${expected_stdout}"
-td_expected_stderr="${test_dir}/${expected_stderr}"
+ TEST_NAME = os.path.basename(test_dir)
+ export("TEST_NAME", TEST_NAME)
-for td_file in "$td_info" "$td_expected_stdout" "$td_expected_stderr"; do
- if [ ! -r "$td_file" ]; then
- err_echo "${test_dir}: missing file $td_file"
- exit 1
- fi
-done
+ # Tests named '<number>-checker-*' will also have their CFGs verified with
+ # Checker when compiled with Optimizing on host.
+ # Additionally, if the user specifies that the CFG must be dumped, it will
+ # run the checker for any type of test to generate the CFG.
+ if re.match("[0-9]+-checker-", TEST_NAME) or dump_cfg == "true":
+ if runtime == "art" and run_optimizing == "true":
+ # In no-prebuild or no-image mode, the compiler only quickens so disable the checker.
+ if prebuild_mode == "yes":
+ run_checker = "yes"
-export TEST_NAME=`basename ${test_dir}`
+ if target_mode == "no":
+ cfg_output_dir = tmp_dir
+ checker_args = f"--arch={host_arch_name.upper()}"
+ else:
+ cfg_output_dir = DEX_LOCATION
+ checker_args = f"--arch={target_arch_name.upper()}"
-# Tests named '<number>-checker-*' will also have their CFGs verified with
-# Checker when compiled with Optimizing on host.
-# Additionally, if the user specifies that the CFG must be dumped, it will
-# run the checker for any type of test to generate the CFG.
-if [[ "$TEST_NAME" =~ ^[0-9]+-checker- ]] || [ "$dump_cfg" = "true" ]; then
- if [ "$runtime" = "art" -a "$run_optimizing" = "true" ]; then
- # In no-prebuild or no-image mode, the compiler only quickens so disable the checker.
- if [ "$prebuild_mode" = "yes" ]; then
- run_checker="yes"
+ if debuggable == "yes":
+ checker_args += " --debuggable"
- if [ "$target_mode" = "no" ]; then
- cfg_output_dir="$tmp_dir"
- checker_args="--arch=${host_arch_name^^}"
- else
- cfg_output_dir="$DEX_LOCATION"
- checker_args="--arch=${target_arch_name^^}"
- fi
+ run_args += [
+ f'-Xcompiler-option "--dump-cfg={cfg_output_dir}/{cfg_output}" -Xcompiler-option -j1'
+ ]
+ checker_args = f"{checker_args} --print-cfg"
- if [ "$debuggable" = "yes" ]; then
- checker_args="$checker_args --debuggable"
- fi
+ run_args += [f'--testlib "{testlib}"']
- run_args+=(-Xcompiler-option "--dump-cfg=$cfg_output_dir/$cfg_output" -Xcompiler-option -j1)
- checker_args="$checker_args --print-cfg"
- fi
- fi
-fi
+ resource.setrlimit(resource.RLIMIT_FSIZE, (file_ulimit * 1024, resource.RLIM_INFINITY))
-run_args+=(--testlib "${testlib}")
+ # Extract run-test data from the zip file.
+ shutil.rmtree(tmp_dir)
+ os.makedirs(f"{tmp_dir}/.unzipped")
+ os.chdir(tmp_dir)
+ m = re.match("[0-9]*([0-9][0-9])-.*", TEST_NAME)
+ assert m, "Can not find test number in " + TEST_NAME
+ SHARD = "HiddenApi" if "hiddenapi" in TEST_NAME else m.group(1)
+ if target_mode == "yes":
+ zip_file = f"{ANDROID_HOST_OUT}/etc/art/art-run-test-target-data-shard{SHARD}.zip"
+ zip_entry = f"target/{TEST_NAME}/"
+ elif runtime == "jvm":
+ zip_file = f"{ANDROID_HOST_OUT}/etc/art/art-run-test-jvm-data-shard{SHARD}.zip"
+ zip_entry = f"jvm/{TEST_NAME}/"
+ else:
+ zip_file = f"{ANDROID_HOST_OUT}/etc/art/art-run-test-host-data-shard{SHARD}.zip"
+ zip_entry = f"host/{TEST_NAME}/"
+ zip = ZipFile(zip_file, "r")
+ zip_entries = [e for e in zip.namelist() if e.startswith(zip_entry)]
+ zip.extractall(Path(tmp_dir) / ".unzipped", members=zip_entries)
+ for entry in (Path(tmp_dir) / ".unzipped" / zip_entry).iterdir():
+ entry.rename(Path(tmp_dir) / entry.name)
-if ! ulimit -f ${file_ulimit}; then
- err_echo "ulimit file size setting failed"
-fi
+ def clean_up(passed: bool):
+ if always_clean == "yes" or (passed and never_clean == "no"):
+ os.chdir(oldwd)
+ shutil.rmtree(tmp_dir)
+ if target_mode == "yes":
+ run(f"adb shell rm -rf {chroot_dex_location}")
+ print(f"{TEST_NAME} files deleted from host" +
+ (" and from target" if target_mode == "yes" else ""))
+ else:
+ print(f"{TEST_NAME} files left in {tmp_dir} on host" +
+ (f" and in {chroot_dex_location} on target" if target_mode == "yes" else ""))
+ atexit.unregister(clean_up)
+ # TODO: Run this in global try-finally once the script is more refactored.
+ atexit.register(clean_up, passed=False)
-# Extract run-test data from the zip file.
-rm -rf "$tmp_dir"
-mkdir -p "$tmp_dir/.unzipped"
-cd "$tmp_dir"
-if [[ "$target_mode" == "yes" ]]; then
- zip_file="${ANDROID_HOST_OUT}/etc/art/art-run-test-target-data.zip"
- zip_entry="target/${TEST_NAME}"
-elif [[ $runtime == "jvm" ]]; then
- zip_file="${ANDROID_HOST_OUT}/etc/art/art-run-test-jvm-data.zip"
- zip_entry="jvm/${TEST_NAME}"
-else
- zip_file="${ANDROID_HOST_OUT}/etc/art/art-run-test-host-data.zip"
- zip_entry="host/${TEST_NAME}"
-fi
-unzip -q "${zip_file}" "${zip_entry}/*" -d "$tmp_dir/.unzipped"
-mv "$tmp_dir"/.unzipped/${zip_entry}/* "$tmp_dir"
+ ctx = RunTestContext(Path(tmp_dir), target_mode == "yes", chroot, DEX_LOCATION, TEST_NAME)
+ td_info = f"{test_dir}/{info}"
+ for td_file in [td_info, ctx.expected_stdout, ctx.expected_stderr]:
+ assert os.access(td_file, os.R_OK)
-good="no"
-good_run="yes"
-export TEST_RUNTIME="${runtime}"
-if [ "$dev_mode" = "yes" ]; then
- echo "${test_dir}: running..." 1>&2
- "./${run}" "${run_args[@]}" "$@"
- run_exit="$?"
+ joined_run_args = " ".join(run_args)
+ joined_args = " ".join(args)
- if [ "$run_exit" = "0" ]; then
- if [ "$run_checker" = "yes" ]; then
- if [ "$target_mode" = "yes" ]; then
- adb pull "$chroot/$cfg_output_dir/$cfg_output" &> /dev/null
- fi
- "$checker" $checker_args "$cfg_output" "$tmp_dir" 2>&1
- checker_exit="$?"
- if [ "$checker_exit" = "0" ]; then
- good="yes"
- fi
- err_echo "checker exit status: $checker_exit"
- else
- good="yes"
- fi
- fi
- echo "run exit status: $run_exit" 1>&2
-elif [ "$update_mode" = "yes" ]; then
- echo "${test_dir}: running..." 1>&2
- "./${run}" "${run_args[@]}" "$@" >"$test_stdout" 2>"$test_stderr"
- if [ "$run_checker" = "yes" ]; then
- if [ "$target_mode" = "yes" ]; then
- adb pull "$chroot/$cfg_output_dir/$cfg_output" &> /dev/null
- fi
- "$checker" -q $checker_args "$cfg_output" "$tmp_dir" >>"$test_stdout" 2>>"$test_stderr"
- fi
- sed -e 's/[[:cntrl:]]$//g' <"$test_stdout" >"${td_expected_stdout}"
- sed -e 's/[[:cntrl:]]$//g' <"$test_stderr" >"${td_expected_stderr}"
- good="yes"
-else
- echo "${test_dir}: running..." 1>&2
- "./${run}" "${run_args[@]}" "$@" >"$test_stdout" 2>"$test_stderr"
- run_exit="$?"
- if [ "$run_exit" != "0" ]; then
- err_echo "run exit status: $run_exit"
- good_run="no"
- elif [ "$run_checker" = "yes" ]; then
- if [ "$target_mode" = "yes" ]; then
- adb pull "$chroot/$cfg_output_dir/$cfg_output" &> /dev/null
- fi
- "$checker" -q $checker_args "$cfg_output" "$tmp_dir" >>"$test_stdout" 2>>"$test_stderr"
- checker_exit="$?"
- if [ "$checker_exit" != "0" ]; then
- err_echo "checker exit status: $checker_exit"
- good_run="no"
- else
- good_run="yes"
- fi
- else
- good_run="yes"
- fi
- ./$check_cmd "$expected_stdout" "$test_stdout" "$expected_stderr" "$test_stderr"
- if [ "$?" = "0" ]; then
- if [ "$good_run" = "yes" ]; then
- # test_stdout == expected_stdout && test_stderr == expected_stderr
- good="yes"
- echo "${test_dir}: succeeded!" 1>&2
- fi
- fi
-fi
+ # Create runner (bash script that executes the whole test)
+ def create_runner_script() -> Path:
+ parsed_args = default_run_module.parse_args(shlex.split(" ".join(run_args + args)))
+ parsed_args.stdout_file = os.path.join(DEX_LOCATION, test_stdout)
+ parsed_args.stderr_file = os.path.join(DEX_LOCATION, test_stderr)
-(
- if [ "$good" != "yes" -a "$update_mode" != "yes" ]; then
- echo "${test_dir}: FAILED!"
- echo ' '
- echo '#################### info'
- cat "${td_info}" | sed 's/^/# /g'
- echo '#################### stdout diffs'
- if [ "$run_checker" == "yes" ]; then
- # Checker failures dump the whole CFG, so we output the whole diff.
- diff --strip-trailing-cr -u "$expected_stdout" "$test_stdout"
- else
- diff --strip-trailing-cr -u "$expected_stdout" "$test_stdout" | tail -n 10000
- fi
- echo '####################'
- echo '#################### stderr diffs'
- diff --strip-trailing-cr -u "$expected_stderr" "$test_stderr" | tail -n 10000
- echo '####################'
- if [ "$strace" = "yes" ]; then
- echo '#################### strace output'
- tail -n 3000 "$tmp_dir/$strace_output"
- echo '####################'
- fi
- if [ "x$target_mode" = "xno" -a "x$SANITIZE_HOST" = "xaddress" ]; then
- # Run the stack script to symbolize any ASAN aborts on the host for SANITIZE_HOST. The
- # tools used by the given ABI work for both x86 and x86-64.
- echo "ABI: 'x86_64'" | cat - "$test_stdout" "$test_stderr" \
- | $ANDROID_BUILD_TOP/development/scripts/stack | tail -n 3000
- fi
- echo ' '
- fi
+ ctx.run(f"cd {DEX_LOCATION}")
+ if target_mode != "yes":
+ # Make "out" directory accessible from test directory.
+ ctx.run(f"ln -s -f -t {DEX_LOCATION} {ANDROID_BUILD_TOP}/out")
+ # Clear the stdout/stderr files (create empty files).
+ ctx.run(f"echo -n > {test_stdout} && echo -n > {test_stderr}")
-) 2>&${real_stderr} 1>&2
+ script = Path(tmp_dir) / "run.py"
+ if script.exists():
+ module = SourceFileLoader("run_" + TEST_NAME, str(script)).load_module()
+ module.run(ctx, parsed_args)
+ else:
+ default_run_module.default_run(ctx, parsed_args)
-# Copy the generated CFG to the specified path.
-if [ $dump_cfg = "true" ]; then
- if [ $run_optimizing != "true" ]; then
- err_echo "Can't dump the .cfg if the compiler type isn't set to \"optimizing\"."
- else
- if [ "$target_mode" = "yes" ]; then
- adb pull $chroot/$cfg_output_dir/$cfg_output $dump_cfg_path
- else
- cp $cfg_output_dir/$cfg_output $dump_cfg_path
- fi
- fi
-fi
+ runner = Path(tmp_dir) / "run.sh"
+ runner.write_text("\n".join(ctx.runner))
+ runner.chmod(0o777)
+ return runner
-# Attempt bisection only if the test failed.
-# TODO: Implement support for chroot-based bisection search.
-if [ "$bisection_search" = "yes" -a "$good" != "yes" ]; then
- # Bisecting works by skipping different optimization passes which breaks checker assertions.
- if [ "$run_checker" == "yes" ]; then
- echo "${test_dir}: not bisecting, checker test." 1>&2
- else
- # Increase file size limit, bisection search can generate large logfiles.
- echo "${test_dir}: bisecting..." 1>&2
- cwd=`pwd`
- maybe_device_mode=""
- raw_cmd=""
- if [ "$target_mode" = "yes" ]; then
- # Produce cmdline.sh in $chroot_dex_location. "$@" is passed as a runtime option
- # so that cmdline.sh forwards its arguments to dalvikvm. invoke-with is set
- # to exec in order to preserve pid when calling dalvikvm. This is required
- # for bisection search to correctly retrieve logs from device.
- "./${run}" "${run_args[@]}" --runtime-option '"$@"' --invoke-with exec --dry-run "$@" &> /dev/null
- adb shell chmod u+x "$chroot_dex_location/cmdline.sh"
- maybe_device_mode="--device"
- raw_cmd="$DEX_LOCATION/cmdline.sh"
- else
- raw_cmd="$cwd/${run} --external-log-tags "${run_args[@]}" $@"
- fi
- # TODO: Pass a `--chroot` option to the bisection_search.py script and use it there.
- $ANDROID_BUILD_TOP/art/tools/bisection_search/bisection_search.py \
- $maybe_device_mode \
- --raw-cmd="$raw_cmd" \
- --check-script="$cwd/check" \
- --expected-output="$cwd/expected-stdout.txt" \
- --logfile="$cwd/bisection_log.txt" \
- --timeout=${timeout:-300}
- fi
-fi
+ # Test might not execute anything but we still expect the output files to exist.
+ Path(test_stdout).touch()
+ Path(test_stderr).touch()
-# Clean up test files.
-if [ "$always_clean" = "yes" -o "$good" = "yes" ] && [ "$never_clean" = "no" ]; then
- cd "$oldwd"
- rm -rf "$tmp_dir"
- if [ "$target_mode" = "yes" -a "$build_exit" = "0" ]; then
- adb shell rm -rf $chroot_dex_location
- fi
- if [ "$good" = "yes" ]; then
- exit 0
- fi
-fi
+ export("TEST_RUNTIME", runtime)
+ print(f"{test_dir}: Create runner script...")
+ runner = create_runner_script()
-(
- if [ "$always_clean" = "yes" ]; then
- echo "${TEST_NAME} files deleted from host "
- if [ "$target_mode" == "yes" ]; then
- echo "and from target"
- fi
- else
- echo "${TEST_NAME} files left in ${tmp_dir} on host"
- if [ "$target_mode" == "yes" ]; then
- echo "and in ${chroot_dex_location} on target"
- fi
- fi
+ print(f"{test_dir}: Run...")
+ if target_mode == "yes":
+ # Prepare the on-device test directory
+ run("adb root")
+ run("adb wait-for-device")
+ run(f"adb shell 'rm -rf {chroot_dex_location} && mkdir -p {chroot_dex_location}'")
+ push_files = [Path(runner.name)]
+ push_files += list(Path(".").glob(f"{TEST_NAME}*.jar"))
+ push_files += list(Path(".").glob(f"expected-*.txt"))
+ push_files += [p for p in [Path("profile"), Path("res")] if p.exists()]
+ run("adb push {} {}".format(" ".join(map(str, push_files)), chroot_dex_location))
-) 2>&${real_stderr} 1>&2
+ chroot_prefix = f"chroot {chroot}" if chroot else ""
+ run(f"adb shell {chroot_prefix} sh {DEX_LOCATION}/run.sh",
+ fail_message=f"Runner {chroot_dex_location}/run.sh failed")
-if [ "$never_clean" = "yes" ] && [ "$good" = "yes" ]; then
- exit 0
-else
- exit 1
-fi
+ # Copy the on-device stdout/stderr to host.
+ pull_files = [test_stdout, test_stderr, "expected-stdout.txt", "expected-stderr.txt"]
+ run("adb pull {} .".format(" ".join(f"{chroot_dex_location}/{f}" for f in pull_files)))
+ else:
+ run(str(runner), fail_message=f"Runner {str(runner)} failed")
+
+ # NB: There is no exit code or return value.
+ # Failing tests just raise python exception.
+ os.chdir(tmp_dir)
+ if update_mode == "yes":
+ for src, dst in [(test_stdout, os.path.join(test_dir, ctx.expected_stdout.name)),
+ (test_stderr, os.path.join(test_dir, ctx.expected_stderr.name))]:
+ if "[DO_NOT_UPDATE]" not in open(dst).readline():
+ copyfile(src, dst)
+
+ print("#################### info")
+ run(f'cat "{td_info}" | sed "s/^/# /g"')
+ print("#################### stdout diff")
+ proc_out = run(f'diff --strip-trailing-cr -u '
+ f'"{ctx.expected_stdout}" "{test_stdout}"', check=False)
+ print("#################### stderr diff")
+ proc_err = run(f'diff --strip-trailing-cr -u '
+ f'"{ctx.expected_stderr}" "{test_stderr}"', check=False)
+ if strace == "yes":
+ print("#################### strace output (trimmed to 3000 lines)")
+ # Some tests do not run dalvikvm, in which case the trace does not exist.
+ run(f'tail -n 3000 "{tmp_dir}/{strace_output}"', check=False)
+ SANITIZE_HOST = os.environ.get("SANITIZE_HOST")
+ if target_mode == "no" and SANITIZE_HOST == "address":
+ # Run the stack script to symbolize any ASAN aborts on the host for SANITIZE_HOST. The
+ # tools used by the given ABI work for both x86 and x86-64.
+ print("#################### symbolizer (trimmed to 3000 lines)")
+ run(f'''echo "ABI: 'x86_64'" | cat - "{test_stdout}" "{test_stderr}"'''
+ f"""| {ANDROID_BUILD_TOP}/development/scripts/stack | tail -n 3000""")
+ print("####################", flush=True)
+ if proc_out.returncode != 0 or proc_err.returncode != 0:
+ kind = ((["stdout"] if proc_out.returncode != 0 else []) +
+ (["stderr"] if proc_err.returncode != 0 else []))
+ fail("{} did not match the expected file".format(" and ".join(kind)))
+
+ if run_checker == "yes":
+ if target_mode == "yes":
+ run(f'adb pull "{chroot}/{cfg_output_dir}/{cfg_output}"')
+ run(f'"{checker}" -q {checker_args} "{cfg_output}" "{tmp_dir}"',
+ fail_message="CFG checker failed")
+
+ # Copy the generated CFG to the specified path.
+ if dump_cfg == "true":
+ assert run_optimizing == "true", "The CFG can be dumped only in optimizing mode"
+ if target_mode == "yes":
+ run(f"adb pull {chroot}/{cfg_output_dir}/{cfg_output} {dump_cfg_path}")
+ else:
+ run(f"cp {cfg_output_dir}/{cfg_output} {dump_cfg_path}")
+
+ clean_up(passed=True)
+ print(f"{COLOR_GREEN}{test_dir}: PASSED{COLOR_NORMAL}")
diff --git a/test/run-test-build.py b/test/run-test-build.py
deleted file mode 100755
index f8eb283..0000000
--- a/test/run-test-build.py
+++ /dev/null
@@ -1,109 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2021 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.
-
-"""
-This scripts compiles Java files which are needed to execute run-tests.
-It is intended to be used only from soong genrule.
-"""
-
-import argparse, os, tempfile, shutil, subprocess, glob, textwrap, re, json, concurrent.futures
-
-ZIP = "prebuilts/build-tools/linux-x86/bin/soong_zip"
-BUILDFAILURES = json.loads(open(os.path.join("art", "test", "buildfailures.json"), "rt").read())
-
-def copy_sources(args, tmp, mode, srcdir):
- """Copy test files from Android tree into the build sandbox and return its path."""
-
- join = os.path.join
- test = os.path.basename(srcdir)
- dstdir = join(tmp, mode, test)
-
- # Don't build tests that are disabled since they might not compile (e.g. on jvm).
- def is_buildfailure(kf):
- return test in kf.get("tests", []) and mode == kf.get("variant") and not kf.get("env_vars")
- if any(is_buildfailure(kf) for kf in BUILDFAILURES):
- return None
-
- # Copy all source files to the temporary directory.
- shutil.copytree(srcdir, dstdir)
-
- # Copy the default scripts if the test does not have a custom ones.
- for name in ["build", "run", "check"]:
- src, dst = f"art/test/etc/default-{name}", join(dstdir, name)
- if os.path.exists(dst):
- shutil.copy2(src, dstdir) # Copy default script next to the custom script.
- else:
- shutil.copy2(src, dst) # Use just the default script.
- os.chmod(dst, 0o755)
-
- return dstdir
-
-def build_test(args, mode, dstdir):
- """Run the build script for single run-test"""
-
- join = os.path.join
- build_top = os.getcwd()
- java_home = os.environ.get("JAVA_HOME")
- tools_dir = os.path.abspath(join(os.path.dirname(__file__), "../../../out/bin"))
- env = {
- "PATH": os.environ.get("PATH"),
- "ANDROID_BUILD_TOP": build_top,
- "ART_TEST_RUN_TEST_BOOTCLASSPATH": join(build_top, args.bootclasspath),
- "TEST_NAME": os.path.basename(dstdir),
- "SOONG_ZIP": join(build_top, "prebuilts/build-tools/linux-x86/bin/soong_zip"),
- "ZIPALIGN": join(build_top, "prebuilts/build-tools/linux-x86/bin/zipalign"),
- "JAVA": join(java_home, "bin/java"),
- "JAVAC": join(java_home, "bin/javac"),
- "JAVAC_ARGS": "-g -Xlint:-options -source 1.8 -target 1.8",
- "D8": join(tools_dir, "d8"),
- "HIDDENAPI": join(tools_dir, "hiddenapi"),
- "JASMIN": join(tools_dir, "jasmin"),
- "SMALI": join(tools_dir, "smali"),
- "NEED_DEX": {"host": "true", "target": "true", "jvm": "false"}[mode],
- "USE_DESUGAR": "true",
- }
- proc = subprocess.run([join(dstdir, "build"), "--" + mode],
- cwd=dstdir,
- env=env,
- encoding=os.sys.stdout.encoding,
- stderr=subprocess.STDOUT,
- stdout=subprocess.PIPE)
- return proc.stdout, proc.returncode
-
-def main():
- parser = argparse.ArgumentParser(description=__doc__)
- parser.add_argument("--out", help="Path of the generated ZIP file with the build data")
- parser.add_argument('--mode', choices=['host', 'jvm', 'target'])
- parser.add_argument("--shard", help="Identifies subset of tests to build (00..99)")
- parser.add_argument("--bootclasspath", help="JAR files used for javac compilation")
- args = parser.parse_args()
-
- with tempfile.TemporaryDirectory(prefix=os.path.basename(__file__)) as tmp:
- srcdirs = sorted(glob.glob(os.path.join("art", "test", "*")))
- srcdirs = filter(lambda srcdir: re.match(".*/\d*{}-.*".format(args.shard), srcdir), srcdirs)
- dstdirs = [copy_sources(args, tmp, args.mode, srcdir) for srcdir in srcdirs]
- dstdirs = filter(lambda dstdir: dstdir, dstdirs) # Remove None (skipped tests).
- with concurrent.futures.ThreadPoolExecutor() as pool:
- for stdout, exitcode in pool.map(lambda dstdir: build_test(args, args.mode, dstdir), dstdirs):
- if stdout:
- print(stdout.strip())
- assert(exitcode == 0) # Build failed. Add test to buildfailures.json if this is expected.
-
- # Create the final zip file which contains the content of the temporary directory.
- proc = subprocess.run([ZIP, "-o", args.out, "-C", tmp, "-D", tmp], check=True)
-
-if __name__ == "__main__":
- main()
diff --git a/test/run_test_build.py b/test/run_test_build.py
new file mode 100755
index 0000000..75cd64f
--- /dev/null
+++ b/test/run_test_build.py
@@ -0,0 +1,522 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 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.
+
+"""
+This scripts compiles Java files which are needed to execute run-tests.
+It is intended to be used only from soong genrule.
+"""
+
+import argparse
+import functools
+import glob
+import os
+import pathlib
+import shlex
+import shutil
+import subprocess
+import sys
+import zipfile
+
+from argparse import ArgumentParser
+from fcntl import lockf, LOCK_EX, LOCK_NB
+from importlib.machinery import SourceFileLoader
+from concurrent.futures import ThreadPoolExecutor
+from os import environ, getcwd, chdir, cpu_count, chmod
+from os.path import relpath
+from pathlib import Path
+from pprint import pprint
+from re import match
+from shutil import copytree, rmtree
+from subprocess import run
+from tempfile import TemporaryDirectory, NamedTemporaryFile
+from typing import Dict, List, Union, Set, Optional
+
+USE_RBE = 100 # Percentage of tests that can use RBE (between 0 and 100)
+
+lock_file = None # Keep alive as long as this process is alive.
+
+RBE_D8_DISABLED_FOR = {
+ "952-invoke-custom", # b/228312861: RBE uses wrong inputs.
+ "979-const-method-handle", # b/228312861: RBE uses wrong inputs.
+}
+
+class BuildTestContext:
+ def __init__(self, args, android_build_top, test_dir):
+ self.android_build_top = android_build_top.absolute()
+ self.bootclasspath = args.bootclasspath.absolute()
+ self.test_name = test_dir.name
+ self.test_dir = test_dir.absolute()
+ self.mode = args.mode
+ self.jvm = (self.mode == "jvm")
+ self.host = (self.mode == "host")
+ self.target = (self.mode == "target")
+ assert self.jvm or self.host or self.target
+
+ self.java_home = Path(os.environ.get("JAVA_HOME")).absolute()
+ self.java_path = self.java_home / "bin/java"
+ self.javac_path = self.java_home / "bin/javac"
+ self.javac_args = "-g -Xlint:-options -source 1.8 -target 1.8"
+
+ # Helper functions to execute tools.
+ self.d8 = functools.partial(self.run, args.d8.absolute())
+ self.jasmin = functools.partial(self.run, args.jasmin.absolute())
+ self.javac = functools.partial(self.run, self.javac_path)
+ self.smali = functools.partial(self.run, args.smali.absolute())
+ self.soong_zip = functools.partial(self.run, args.soong_zip.absolute())
+ self.zipalign = functools.partial(self.run, args.zipalign.absolute())
+ if args.hiddenapi:
+ self.hiddenapi = functools.partial(self.run, args.hiddenapi.absolute())
+
+ # RBE wrapper for some of the tools.
+ if "RBE_server_address" in os.environ and USE_RBE > (hash(self.test_name) % 100):
+ self.rbe_exec_root = os.environ.get("RBE_exec_root")
+ self.rbe_rewrapper = self.android_build_top / "prebuilts/remoteexecution-client/live/rewrapper"
+ if self.test_name not in RBE_D8_DISABLED_FOR:
+ self.d8 = functools.partial(self.rbe_d8, args.d8.absolute())
+ self.javac = functools.partial(self.rbe_javac, self.javac_path)
+ self.smali = functools.partial(self.rbe_smali, args.smali.absolute())
+
+ # Minimal environment needed for bash commands that we execute.
+ self.bash_env = {
+ "ANDROID_BUILD_TOP": self.android_build_top,
+ "D8": args.d8.absolute(),
+ "JAVA": self.java_path,
+ "JAVAC": self.javac_path,
+ "JAVAC_ARGS": self.javac_args,
+ "JAVA_HOME": self.java_home,
+ "PATH": os.environ["PATH"],
+ "PYTHONDONTWRITEBYTECODE": "1",
+ "SMALI": args.smali.absolute(),
+ "SOONG_ZIP": args.soong_zip.absolute(),
+ "TEST_NAME": self.test_name,
+ }
+
+ def bash(self, cmd):
+ return subprocess.run(cmd,
+ shell=True,
+ cwd=self.test_dir,
+ env=self.bash_env,
+ check=True)
+
+ def run(self, executable: pathlib.Path, args: List[Union[pathlib.Path, str]]):
+ assert isinstance(executable, pathlib.Path), executable
+ cmd: List[Union[pathlib.Path, str]] = []
+ if executable.suffix == ".sh":
+ cmd += ["/bin/bash"]
+ cmd += [executable]
+ cmd += args
+ env = self.bash_env
+ env.update({k: v for k, v in os.environ.items() if k.startswith("RBE_")})
+ # Make paths relative as otherwise we could create too long command line.
+ for i, arg in enumerate(cmd):
+ if isinstance(arg, pathlib.Path):
+ assert arg.absolute(), arg
+ cmd[i] = relpath(arg, self.test_dir)
+ elif isinstance(arg, list):
+ assert all(p.absolute() for p in arg), arg
+ cmd[i] = ":".join(relpath(p, self.test_dir) for p in arg)
+ else:
+ assert isinstance(arg, str), arg
+ p = subprocess.run(cmd,
+ encoding=sys.stdout.encoding,
+ cwd=self.test_dir,
+ env=self.bash_env,
+ stderr=subprocess.STDOUT,
+ stdout=subprocess.PIPE)
+ if p.returncode != 0:
+ raise Exception("Command failed with exit code {}\n$ {}\n{}".format(
+ p.returncode, " ".join(map(str, cmd)), p.stdout))
+ return p
+
+ def rbe_wrap(self, args, inputs: Set[pathlib.Path]=None):
+ with NamedTemporaryFile(mode="w+t") as input_list:
+ inputs = inputs or set()
+ for i, arg in enumerate(args):
+ if isinstance(arg, pathlib.Path):
+ assert arg.absolute(), arg
+ inputs.add(arg)
+ elif isinstance(arg, list):
+ assert all(p.absolute() for p in arg), arg
+ inputs.update(arg)
+ input_list.writelines([relpath(i, self.rbe_exec_root)+"\n" for i in inputs])
+ input_list.flush()
+ return self.run(self.rbe_rewrapper, [
+ "--platform=" + os.environ["RBE_platform"],
+ "--input_list_paths=" + input_list.name,
+ ] + args)
+
+ def rbe_javac(self, javac_path:Path, args):
+ output = relpath(Path(args[args.index("-d") + 1]), self.rbe_exec_root)
+ return self.rbe_wrap(["--output_directories", output, javac_path] + args)
+
+ def rbe_d8(self, d8_path:Path, args):
+ inputs = set([d8_path.parent.parent / "framework/d8.jar"])
+ output = relpath(Path(args[args.index("--output") + 1]), self.rbe_exec_root)
+ return self.rbe_wrap([
+ "--output_files" if output.endswith(".jar") else "--output_directories", output,
+ "--toolchain_inputs=prebuilts/jdk/jdk17/linux-x86/bin/java",
+ d8_path] + args, inputs)
+
+ def rbe_smali(self, smali_path:Path, args):
+ inputs = set([smali_path.parent.parent / "framework/smali.jar"])
+ output = relpath(Path(args[args.index("--output") + 1]), self.rbe_exec_root)
+ return self.rbe_wrap([
+ "--output_files", output,
+ "--toolchain_inputs=prebuilts/jdk/jdk17/linux-x86/bin/java",
+ smali_path] + args, inputs)
+
+ def build(self) -> None:
+ script = self.test_dir / "build.py"
+ if script.exists():
+ module = SourceFileLoader("build_" + self.test_name,
+ str(script)).load_module()
+ module.build(self)
+ else:
+ self.default_build()
+
+ def default_build(
+ self,
+ use_desugar=True,
+ use_hiddenapi=True,
+ need_dex=None,
+ zip_compression_method="deflate",
+ zip_align_bytes=None,
+ api_level:Union[int, str]=26, # Can also be named alias (string).
+ javac_args=[],
+ javac_classpath: List[Path]=[],
+ d8_flags=[],
+ smali_args=[],
+ use_smali=True,
+ use_jasmin=True,
+ ):
+ javac_classpath = javac_classpath.copy() # Do not modify default value.
+
+ # Wrap "pathlib.Path" with our own version that ensures all paths are absolute.
+ # Plain filenames are assumed to be relative to self.test_dir and made absolute.
+ class Path(pathlib.Path):
+ def __new__(cls, filename: str):
+ path = pathlib.Path(filename)
+ return path if path.is_absolute() else (self.test_dir / path)
+
+ need_dex = (self.host or self.target) if need_dex is None else need_dex
+
+ if self.jvm:
+ # No desugaring on jvm because it supports the latest functionality.
+ use_desugar = False
+
+ # Set API level for smali and d8.
+ if isinstance(api_level, str):
+ API_LEVEL = {
+ "default-methods": 24,
+ "parameter-annotations": 25,
+ "agents": 26,
+ "method-handles": 26,
+ "var-handles": 28,
+ }
+ api_level = API_LEVEL[api_level]
+ assert isinstance(api_level, int), api_level
+
+ def zip(zip_target: Path, *files: Path):
+ zip_args = ["-o", zip_target, "-C", zip_target.parent]
+ if zip_compression_method == "store":
+ zip_args.extend(["-L", "0"])
+ for f in files:
+ zip_args.extend(["-f", f])
+ self.soong_zip(zip_args)
+
+ if zip_align_bytes:
+ # zipalign does not operate in-place, so write results to a temp file.
+ with TemporaryDirectory() as tmp_dir:
+ tmp_file = Path(tmp_dir) / "aligned.zip"
+ self.zipalign(["-f", str(zip_align_bytes), zip_target, tmp_file])
+ # replace original zip target with our temp file.
+ tmp_file.rename(zip_target)
+
+
+ def make_jasmin(dst_dir: Path, src_dir: Path) -> Optional[Path]:
+ if not use_jasmin or not src_dir.exists():
+ return None # No sources to compile.
+ dst_dir.mkdir()
+ self.jasmin(["-d", dst_dir] + sorted(src_dir.glob("**/*.j")))
+ return dst_dir
+
+ def make_smali(dst_dex: Path, src_dir: Path) -> Optional[Path]:
+ if not use_smali or not src_dir.exists():
+ return None # No sources to compile.
+ self.smali(["-JXmx512m", "assemble"] + smali_args + ["--api", str(api_level)] +
+ ["--output", dst_dex] + sorted(src_dir.glob("**/*.smali")))
+ return dst_dex
+
+ def make_java(dst_dir: Path, *src_dirs: Path) -> Optional[Path]:
+ if not any(src_dir.exists() for src_dir in src_dirs):
+ return None # No sources to compile.
+ dst_dir.mkdir(exist_ok=True)
+ args = self.javac_args.split(" ") + javac_args
+ args += ["-implicit:none", "-encoding", "utf8", "-d", dst_dir]
+ if not self.jvm:
+ args += ["-bootclasspath", self.bootclasspath]
+ if javac_classpath:
+ args += ["-classpath", javac_classpath]
+ for src_dir in src_dirs:
+ args += sorted(src_dir.glob("**/*.java"))
+ self.javac(args)
+ javac_post = Path("javac_post.sh")
+ if javac_post.exists():
+ self.run(javac_post, [dst_dir])
+ return dst_dir
+
+
+ # Make a "dex" file given a directory of classes. This will be
+ # packaged in a jar file.
+ def make_dex(src_dir: Path):
+ dst_jar = Path(src_dir.name + ".jar")
+ args = d8_flags + ["--min-api", str(api_level), "--output", dst_jar]
+ args += ["--lib", self.bootclasspath] if use_desugar else ["--no-desugaring"]
+ args += sorted(src_dir.glob("**/*.class"))
+ self.d8(args)
+
+ # D8 outputs to JAR files today rather than DEX files as DX used
+ # to. To compensate, we extract the DEX from d8's output to meet the
+ # expectations of make_dex callers.
+ dst_dex = Path(src_dir.name + ".dex")
+ with TemporaryDirectory() as tmp_dir:
+ zipfile.ZipFile(dst_jar, "r").extractall(tmp_dir)
+ (Path(tmp_dir) / "classes.dex").rename(dst_dex)
+
+ # Merge all the dex files.
+ # Skip non-existing files, but at least 1 file must exist.
+ def make_dexmerge(dst_dex: Path, *src_dexs: Path):
+ # Include destination. Skip any non-existing files.
+ srcs = [f for f in [dst_dex] + list(src_dexs) if f.exists()]
+
+ # NB: We merge even if there is just single input.
+ # It is useful to normalize non-deterministic smali output.
+ tmp_dir = self.test_dir / "dexmerge"
+ tmp_dir.mkdir()
+ self.d8(["--min-api", str(api_level), "--output", tmp_dir] + srcs)
+ assert not (tmp_dir / "classes2.dex").exists()
+ for src_file in srcs:
+ src_file.unlink()
+ (tmp_dir / "classes.dex").rename(dst_dex)
+ tmp_dir.rmdir()
+
+
+ def make_hiddenapi(*dex_files: Path):
+ if not use_hiddenapi or not Path("hiddenapi-flags.csv").exists():
+ return # Nothing to do.
+ args: List[Union[str, Path]] = ["encode"]
+ for dex_file in dex_files:
+ args.extend(["--input-dex=" + str(dex_file), "--output-dex=" + str(dex_file)])
+ args.append("--api-flags=hiddenapi-flags.csv")
+ args.append("--no-force-assign-all")
+ self.hiddenapi(args)
+
+
+ if Path("classes.dex").exists():
+ zip(Path(self.test_name + ".jar"), Path("classes.dex"))
+ return
+
+ if Path("classes.dm").exists():
+ zip(Path(self.test_name + ".jar"), Path("classes.dm"))
+ return
+
+ if make_jasmin(Path("jasmin_classes"), Path("jasmin")):
+ javac_classpath.append(Path("jasmin_classes"))
+
+ if make_jasmin(Path("jasmin_classes2"), Path("jasmin-multidex")):
+ javac_classpath.append(Path("jasmin_classes2"))
+
+ # To allow circular references, compile src/, src-multidex/, src-aotex/,
+ # src-bcpex/, src-ex/ together and pass the output as class path argument.
+ # Replacement sources in src-art/, src2/ and src-ex2/ can replace symbols
+ # used by the other src-* sources we compile here but everything needed to
+ # compile the other src-* sources should be present in src/ (and jasmin*/).
+ extra_srcs = ["src-multidex", "src-aotex", "src-bcpex", "src-ex"]
+ replacement_srcs = ["src2", "src-ex2"] + ([] if self.jvm else ["src-art"])
+ if (Path("src").exists() and
+ any(Path(p).exists() for p in extra_srcs + replacement_srcs)):
+ make_java(Path("classes-tmp-all"), Path("src"), *map(Path, extra_srcs))
+ javac_classpath.append(Path("classes-tmp-all"))
+
+ if make_java(Path("classes-aotex"), Path("src-aotex")) and need_dex:
+ make_dex(Path("classes-aotex"))
+ # rename it so it shows up as "classes.dex" in the zip file.
+ Path("classes-aotex.dex").rename(Path("classes.dex"))
+ zip(Path(self.test_name + "-aotex.jar"), Path("classes.dex"))
+
+ if make_java(Path("classes-bcpex"), Path("src-bcpex")) and need_dex:
+ make_dex(Path("classes-bcpex"))
+ # rename it so it shows up as "classes.dex" in the zip file.
+ Path("classes-bcpex.dex").rename(Path("classes.dex"))
+ zip(Path(self.test_name + "-bcpex.jar"), Path("classes.dex"))
+
+ make_java(Path("classes"), Path("src"))
+
+ if not self.jvm:
+ # Do not attempt to build src-art directories on jvm,
+ # since it would fail without libcore.
+ make_java(Path("classes"), Path("src-art"))
+
+ if make_java(Path("classes2"), Path("src-multidex")) and need_dex:
+ make_dex(Path("classes2"))
+
+ make_java(Path("classes"), Path("src2"))
+
+ # If the classes directory is not-empty, package classes in a DEX file.
+ # NB: some tests provide classes rather than java files.
+ if any(Path("classes").glob("*")) and need_dex:
+ make_dex(Path("classes"))
+
+ if Path("jasmin_classes").exists():
+ # Compile Jasmin classes as if they were part of the classes.dex file.
+ if need_dex:
+ make_dex(Path("jasmin_classes"))
+ make_dexmerge(Path("classes.dex"), Path("jasmin_classes.dex"))
+ else:
+ # Move jasmin classes into classes directory so that they are picked up
+ # with -cp classes.
+ Path("classes").mkdir(exist_ok=True)
+ copytree(Path("jasmin_classes"), Path("classes"), dirs_exist_ok=True)
+
+ if need_dex and make_smali(Path("smali_classes.dex"), Path("smali")):
+ # Merge smali files into classes.dex,
+ # this takes priority over any jasmin files.
+ make_dexmerge(Path("classes.dex"), Path("smali_classes.dex"))
+
+ # Compile Jasmin classes in jasmin-multidex as if they were part of
+ # the classes2.jar
+ if Path("jasmin-multidex").exists():
+ if need_dex:
+ make_dex(Path("jasmin_classes2"))
+ make_dexmerge(Path("classes2.dex"), Path("jasmin_classes2.dex"))
+ else:
+ # Move jasmin classes into classes2 directory so that
+ # they are picked up with -cp classes2.
+ Path("classes2").mkdir()
+ copytree(Path("jasmin_classes2"), Path("classes2"), dirs_exist_ok=True)
+ rmtree(Path("jasmin_classes2"))
+
+ if need_dex and make_smali(Path("smali_classes2.dex"), Path("smali-multidex")):
+ # Merge smali_classes2.dex into classes2.dex
+ make_dexmerge(Path("classes2.dex"), Path("smali_classes2.dex"))
+
+ make_java(Path("classes-ex"), Path("src-ex"))
+
+ make_java(Path("classes-ex"), Path("src-ex2"))
+
+ if Path("classes-ex").exists() and need_dex:
+ make_dex(Path("classes-ex"))
+
+ if need_dex and make_smali(Path("smali_classes-ex.dex"), Path("smali-ex")):
+ # Merge smali files into classes-ex.dex.
+ make_dexmerge(Path("classes-ex.dex"), Path("smali_classes-ex.dex"))
+
+ if Path("classes-ex.dex").exists():
+ # Apply hiddenapi on the dex files if the test has API list file(s).
+ make_hiddenapi(Path("classes-ex.dex"))
+
+ # quick shuffle so that the stored name is "classes.dex"
+ Path("classes.dex").rename(Path("classes-1.dex"))
+ Path("classes-ex.dex").rename(Path("classes.dex"))
+ zip(Path(self.test_name + "-ex.jar"), Path("classes.dex"))
+ Path("classes.dex").rename(Path("classes-ex.dex"))
+ Path("classes-1.dex").rename(Path("classes.dex"))
+
+ # Apply hiddenapi on the dex files if the test has API list file(s).
+ if need_dex:
+ if any(Path(".").glob("*-multidex")):
+ make_hiddenapi(Path("classes.dex"), Path("classes2.dex"))
+ else:
+ make_hiddenapi(Path("classes.dex"))
+
+ # Create a single dex jar with two dex files for multidex.
+ if need_dex:
+ if Path("classes2.dex").exists():
+ zip(Path(self.test_name + ".jar"), Path("classes.dex"), Path("classes2.dex"))
+ else:
+ zip(Path(self.test_name + ".jar"), Path("classes.dex"))
+
+
+# If we build just individual shard, we want to split the work among all the cores,
+# but if the build system builds all shards, we don't want to overload the machine.
+# We don't know which situation we are in, so as simple work-around, we use a lock
+# file to allow only one shard to use multiprocessing at the same time.
+def use_multiprocessing(mode: str) -> bool:
+ if "RBE_server_address" in os.environ:
+ return True
+ global lock_file
+ lock_path = Path(environ["TMPDIR"]) / ("art-test-run-test-build-py-" + mode)
+ lock_file = open(lock_path, "w")
+ try:
+ lockf(lock_file, LOCK_EX | LOCK_NB)
+ return True # We are the only instance of this script in the build system.
+ except BlockingIOError:
+ return False # Some other instance is already running.
+
+
+def main() -> None:
+ parser = ArgumentParser(description=__doc__)
+ parser.add_argument("--out", type=Path, help="Final zip file")
+ parser.add_argument("--mode", choices=["host", "jvm", "target"])
+ parser.add_argument("--bootclasspath", type=Path)
+ parser.add_argument("--d8", type=Path)
+ parser.add_argument("--hiddenapi", type=Path)
+ parser.add_argument("--jasmin", type=Path)
+ parser.add_argument("--smali", type=Path)
+ parser.add_argument("--soong_zip", type=Path)
+ parser.add_argument("--zipalign", type=Path)
+ parser.add_argument("srcs", nargs="+", type=Path)
+ args = parser.parse_args()
+
+ android_build_top = Path(getcwd()).absolute()
+ ziproot = args.out.absolute().parent / "zip"
+ srcdirs = set(s.parents[-4].absolute() for s in args.srcs)
+
+ # Special hidden-api shard: If the --hiddenapi flag is provided, build only
+ # hiddenapi tests. Otherwise exclude all hiddenapi tests from normal shards.
+ def filter_by_hiddenapi(srcdir: Path) -> bool:
+ return (args.hiddenapi != None) == ("hiddenapi" in srcdir.name)
+
+ # Initialize the test objects.
+ # We need to do this before we change the working directory below.
+ tests: List[BuildTestContext] = []
+ for srcdir in filter(filter_by_hiddenapi, srcdirs):
+ dstdir = ziproot / args.mode / srcdir.name
+ copytree(srcdir, dstdir)
+ tests.append(BuildTestContext(args, android_build_top, dstdir))
+
+ # We can not change the working directory per each thread since they all run in parallel.
+ # Create invalid read-only directory to catch accidental use of current working directory.
+ with TemporaryDirectory("-do-not-use-cwd") as invalid_tmpdir:
+ os.chdir(invalid_tmpdir)
+ os.chmod(invalid_tmpdir, 0)
+ with ThreadPoolExecutor(cpu_count() if use_multiprocessing(args.mode) else 1) as pool:
+ jobs = {}
+ for ctx in tests:
+ jobs[ctx.test_name] = pool.submit(ctx.build)
+ for test_name, job in jobs.items():
+ try:
+ job.result()
+ except Exception as e:
+ raise Exception("Failed to build " + test_name) from e
+
+ # Create the final zip file which contains the content of the temporary directory.
+ proc = run([android_build_top / args.soong_zip, "-o", android_build_top / args.out,
+ "-C", ziproot, "-D", ziproot], check=True)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/test/testrunner/device_config.py b/test/testrunner/device_config.py
index 1fad7d2..c56a05d 100644
--- a/test/testrunner/device_config.py
+++ b/test/testrunner/device_config.py
@@ -11,10 +11,11 @@
#
##########################################
# Fugu's don't have enough memory to support a 128m heap with normal concurrency.
+ # Also update timeout value as some tests can go beyond the default 600s.
'aosp_fugu' : {
- 'run-test-args': [ "--runtime-option", "-Xmx128m" ],
+ 'run-test-args': [ "--runtime-option", "-Xmx128m", "--timeout", "900" ],
},
'fugu' : {
- 'run-test-args': [ "--runtime-option", "-Xmx128m" ],
+ 'run-test-args': [ "--runtime-option", "-Xmx128m", "--timeout", "900" ],
},
}
diff --git a/test/testrunner/env.py b/test/testrunner/env.py
index 319e1a7..d5e0543 100644
--- a/test/testrunner/env.py
+++ b/test/testrunner/env.py
@@ -132,8 +132,8 @@
HOST_OUT_EXECUTABLES = os.path.join(ANDROID_BUILD_TOP,
_get_build_var("HOST_OUT_EXECUTABLES"))
-# Set up default values for $DX, $SMALI, etc to the $HOST_OUT_EXECUTABLES/$name path.
-for tool in ['dx', 'smali', 'jasmin', 'd8']:
+# Set up default values for $D8, $SMALI, etc to the $HOST_OUT_EXECUTABLES/$name path.
+for tool in ['smali', 'jasmin', 'd8']:
os.environ.setdefault(tool.upper(), HOST_OUT_EXECUTABLES + '/' + tool)
ANDROID_JAVA_TOOLCHAIN = os.path.join(ANDROID_BUILD_TOP,
diff --git a/test/testrunner/run_build_test_target.py b/test/testrunner/run_build_test_target.py
index 4191771..551e8da 100755
--- a/test/testrunner/run_build_test_target.py
+++ b/test/testrunner/run_build_test_target.py
@@ -29,12 +29,18 @@
import argparse
import os
import pathlib
+import re
import subprocess
import sys
from target_config import target_config
import env
+# Check that we are using reasonably recent version of python
+print("Using", sys.executable, sys.version, flush=True)
+version = tuple(map(int, re.match(r"(\d*)\.(\d*)", sys.version).groups()))
+assert (version >= (3, 9)), "Python version is too old"
+
parser = argparse.ArgumentParser()
parser.add_argument('-j', default='1', dest='n_threads')
# either -l/--list OR build-target is required (but not both).
@@ -81,7 +87,7 @@
if 'build' in target:
build_command = target.get('build').format(
ANDROID_BUILD_TOP = env.ANDROID_BUILD_TOP,
- MAKE_OPTIONS='DX= -j{threads}'.format(threads = n_threads))
+ MAKE_OPTIONS='D8= -j{threads}'.format(threads = n_threads))
sys.stdout.write(str(build_command) + '\n')
sys.stdout.flush()
if subprocess.call(build_command.split()):
@@ -90,7 +96,7 @@
# make runs soong/kati to build the target listed in the entry.
if 'make' in target:
build_command = 'build/soong/soong_ui.bash --make-mode'
- build_command += ' DX='
+ build_command += ' D8='
build_command += ' -j' + str(n_threads)
build_command += ' ' + target.get('make')
if env.DIST_DIR:
@@ -119,7 +125,8 @@
sys.exit(1)
if 'run-test' in target:
- run_test_command = [os.path.join(env.ANDROID_BUILD_TOP,
+ run_test_command = [sys.executable, # Use the same python as we are using now.
+ os.path.join(env.ANDROID_BUILD_TOP,
'art/test/testrunner/testrunner.py')]
test_flags = target.get('run-test', [])
out_dir = pathlib.PurePath(env.SOONG_OUT_DIR)
diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py
index 907f4ec..eaf33b7 100644
--- a/test/testrunner/target_config.py
+++ b/test/testrunner/target_config.py
@@ -138,7 +138,8 @@
}
},
'art-tracing' : {
- 'run-test' : ['--trace']
+ 'run-test' : ['--trace',
+ '--stream']
},
'art-interpreter-tracing' : {
'run-test' : ['--interpreter',
@@ -246,6 +247,20 @@
'ASAN_OPTIONS' : 'detect_leaks=0'
}
},
+ 'art-gtest-asan32': {
+ 'make' : 'test-art-host-gtest32',
+ 'env': {
+ 'SANITIZE_HOST' : 'address',
+ 'ASAN_OPTIONS' : 'detect_leaks=0'
+ }
+ },
+ 'art-gtest-asan64': {
+ 'make' : 'test-art-host-gtest64',
+ 'env': {
+ 'SANITIZE_HOST' : 'address',
+ 'ASAN_OPTIONS' : 'detect_leaks=0'
+ }
+ },
'art-asan': {
'run-test' : ['--interpreter',
'--interp-ac',
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index 935ce0c..80b98fe 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -581,7 +581,9 @@
temp_path = tempfile.mkdtemp(dir=env.ART_HOST_TEST_DIR)
options_test = '--temp-path {} '.format(temp_path) + options_test
- run_test_sh = env.ANDROID_BUILD_TOP + '/art/test/run-test'
+ # Run the run-test script using the prebuilt python.
+ python3_bin = env.ANDROID_BUILD_TOP + "/prebuilts/build-tools/path/linux-x86/python3"
+ run_test_sh = python3_bin + ' ' + env.ANDROID_BUILD_TOP + '/art/test/run-test'
command = ' '.join((run_test_sh, options_test, ' '.join(extra_arguments[target]), test))
return executor.submit(run_test, command, test, variant_set, test_name)
@@ -665,9 +667,12 @@
test_start_time = time.monotonic()
if verbose:
print_text("Starting %s at %s\n" % (test_name, test_start_time))
+ env = dict(os.environ)
+ env["FULL_TEST_NAME"] = test_name
if gdb or gdb_dex2oat:
proc = _popen(
args=command.split(),
+ env=env,
stderr=subprocess.STDOUT,
universal_newlines=True,
start_new_session=True
@@ -675,6 +680,7 @@
else:
proc = _popen(
args=command.split(),
+ env=env,
stderr=subprocess.STDOUT,
stdout = subprocess.PIPE,
universal_newlines=True,
@@ -1104,7 +1110,9 @@
global csv_result
parser = argparse.ArgumentParser(description="Runs all or a subset of the ART test suite.")
- parser.add_argument('-t', '--test', action='append', dest='tests', help='name(s) of the test(s)')
+ parser.add_argument('tests', action='extend', nargs="*", help='name(s) of the test(s)')
+ parser.add_argument('-t', '--test', action='append', dest='tests', help='name(s) of the test(s)'
+ ' (deprecated: use positional arguments at the end without any option instead)')
global_group = parser.add_argument_group('Global options',
'Options that affect all tests being run')
global_group.add_argument('-j', type=int, dest='n_thread', help="""Number of CPUs to use.
@@ -1225,26 +1233,31 @@
if options['run_all']:
run_all_configs = True
- return tests
+ return tests or RUN_TEST_SET
def main():
gather_test_info()
- user_requested_tests = parse_option()
+ tests = parse_option()
setup_test_env()
gather_disabled_test_info()
if build:
- build_targets = ''
- if 'host' in _user_input_variants['target']:
- build_targets += 'test-art-host-run-test-dependencies '
- if 'target' in _user_input_variants['target']:
- build_targets += 'test-art-target-run-test-dependencies '
- if 'jvm' in _user_input_variants['target']:
- build_targets += 'test-art-host-run-test-dependencies '
+ build_targets = []
+ # Build only the needed shards (depending on the selected tests).
+ shards = set(re.search("(\d\d)-", t).group(1) for t in tests)
+ if any("hiddenapi" in t for t in tests):
+ shards.add("HiddenApi") # Include special HiddenApi shard.
+ for mode in ['host', 'target', 'jvm']:
+ if mode in _user_input_variants['target']:
+ build_targets += ['test-art-{}-run-test-dependencies'.format(mode)]
+ if len(shards) >= 100:
+ build_targets += ["art-run-test-{}-data".format(mode)] # Build all.
+ else:
+ build_targets += ["art-run-test-{}-data-shard{}".format(mode, s) for s in shards]
build_command = env.ANDROID_BUILD_TOP + '/build/soong/soong_ui.bash --make-mode'
- build_command += ' DX='
+ build_command += ' D8='
if dist:
build_command += ' dist'
- build_command += ' ' + build_targets
+ build_command += ' ' + ' '.join(build_targets)
print_text('Build command: %s\n' % build_command)
if subprocess.call(build_command.split()):
# Debugging for b/62653020
@@ -1252,10 +1265,7 @@
shutil.copyfile(env.SOONG_OUT_DIR + '/build.ninja', env.DIST_DIR + '/soong.ninja')
sys.exit(1)
- if user_requested_tests:
- run_tests(user_requested_tests)
- else:
- run_tests(RUN_TEST_SET)
+ run_tests(tests)
print_analysis()
close_csv_file()
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index cc83ad3..ff8b3a8 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -29,6 +29,7 @@
#include "909-attach-agent/attach.h"
#include "936-search-onload/search_onload.h"
#include "1919-vminit-thread-start-timing/vminit.h"
+#include "993-breakpoints-non-debuggable/onload.h"
namespace art {
@@ -82,6 +83,7 @@
{ "939-hello-transformation-bcp", common_redefine::OnLoad, nullptr },
{ "941-recursive-obsolete-jit", common_redefine::OnLoad, nullptr },
{ "943-private-recursive-jit", common_redefine::OnLoad, nullptr },
+ { "993-non-debuggable", nullptr, Test993BreakpointsNonDebuggable::OnLoad },
{ "1919-vminit-thread-start-timing", Test1919VMInitThreadStart::OnLoad, nullptr },
{ "2031-zygote-compiled-frame-deopt", nullptr, MinimalOnLoad },
{ "2039-load-transform-larger", common_retransform::OnLoad, nullptr },
diff --git a/test/ti-agent/jni_helper.h b/test/ti-agent/jni_helper.h
index 0cbc634..f99e627 100644
--- a/test/ti-agent/jni_helper.h
+++ b/test/ti-agent/jni_helper.h
@@ -61,7 +61,7 @@
ScopedLocalRef<jclass> exc_class(env, env->FindClass("java/lang/NullPointerException"));
if (exc_class.get() == nullptr) {
- return -1;
+ return false;
}
return env->ThrowNew(exc_class.get(), msg) == JNI_OK;
diff --git a/test/ti-agent/scoped_utf_chars.h b/test/ti-agent/scoped_utf_chars.h
index 422caaf..ddf1bd5 100644
--- a/test/ti-agent/scoped_utf_chars.h
+++ b/test/ti-agent/scoped_utf_chars.h
@@ -38,7 +38,7 @@
}
}
- ScopedUtfChars(ScopedUtfChars&& rhs) :
+ ScopedUtfChars(ScopedUtfChars&& rhs) noexcept :
env_(rhs.env_), string_(rhs.string_), utf_chars_(rhs.utf_chars_) {
rhs.env_ = nullptr;
rhs.string_ = nullptr;
@@ -51,7 +51,7 @@
}
}
- ScopedUtfChars& operator=(ScopedUtfChars&& rhs) {
+ ScopedUtfChars& operator=(ScopedUtfChars&& rhs) noexcept {
if (this != &rhs) {
// Delete the currently owned UTF chars.
this->~ScopedUtfChars();
diff --git a/test/ti-agent/ti_macros.h b/test/ti-agent/ti_macros.h
index a871270..abd54e0 100644
--- a/test/ti-agent/ti_macros.h
+++ b/test/ti-agent/ti_macros.h
@@ -21,4 +21,10 @@
#define UNREACHABLE __builtin_unreachable
+#ifndef NDEBUG
+#define ALWAYS_INLINE
+#else
+#define ALWAYS_INLINE __attribute__ ((always_inline))
+#endif
+
#endif // ART_TEST_TI_AGENT_TI_MACROS_H_
diff --git a/test/ti-agent/ti_utf.h b/test/ti-agent/ti_utf.h
index 341e106..15fe22c 100644
--- a/test/ti-agent/ti_utf.h
+++ b/test/ti-agent/ti_utf.h
@@ -21,6 +21,7 @@
#include <string.h>
#include "android-base/logging.h"
+#include "ti_macros.h"
namespace art {
namespace ti {
@@ -104,6 +105,56 @@
return surrogate_pair;
}
+// Note: This is a copy of the code in `libdexfile`.
+template <bool kUseShortZero, bool kUse4ByteSequence, bool kReplaceBadSurrogates, typename Append>
+inline void ConvertUtf16ToUtf8(const uint16_t* utf16, size_t char_count, Append&& append) {
+ static_assert(kUse4ByteSequence || !kReplaceBadSurrogates);
+
+ // Use local helpers instead of macros from `libicu` to avoid the dependency on `libicu`.
+ auto is_lead = [](uint16_t ch) ALWAYS_INLINE { return (ch & 0xfc00u) == 0xd800u; };
+ auto is_trail = [](uint16_t ch) ALWAYS_INLINE { return (ch & 0xfc00u) == 0xdc00u; };
+ auto is_surrogate = [](uint16_t ch) ALWAYS_INLINE { return (ch & 0xf800u) == 0xd800u; };
+ auto is_surrogate_lead = [](uint16_t ch) ALWAYS_INLINE { return (ch & 0x0400u) == 0u; };
+ auto get_supplementary = [](uint16_t lead, uint16_t trail) ALWAYS_INLINE {
+ constexpr uint32_t offset = (0xd800u << 10) + 0xdc00u - 0x10000u;
+ return (static_cast<uint32_t>(lead) << 10) + static_cast<uint32_t>(trail) - offset;
+ };
+
+ for (size_t i = 0u; i < char_count; ++i) {
+ auto has_trail = [&]() { return i + 1u != char_count && is_trail(utf16[i + 1u]); };
+
+ uint16_t ch = utf16[i];
+ if (ch < 0x80u && (kUseShortZero || ch != 0u)) {
+ // One byte.
+ append(ch);
+ } else if (ch < 0x800u) {
+ // Two bytes.
+ append((ch >> 6) | 0xc0);
+ append((ch & 0x3f) | 0x80);
+ } else if (kReplaceBadSurrogates
+ ? is_surrogate(ch)
+ : kUse4ByteSequence && is_lead(ch) && has_trail()) {
+ if (kReplaceBadSurrogates && (!is_surrogate_lead(ch) || !has_trail())) {
+ append('?');
+ } else {
+ // We have a *valid* surrogate pair.
+ uint32_t code_point = get_supplementary(ch, utf16[i + 1u]);
+ ++i; // Consume the leading surrogate.
+ // Four bytes.
+ append((code_point >> 18) | 0xf0);
+ append(((code_point >> 12) & 0x3f) | 0x80);
+ append(((code_point >> 6) & 0x3f) | 0x80);
+ append((code_point & 0x3f) | 0x80);
+ }
+ } else {
+ // Three bytes.
+ append((ch >> 12) | 0xe0);
+ append(((ch >> 6) & 0x3f) | 0x80);
+ append((ch & 0x3f) | 0x80);
+ }
+ }
+}
+
inline void ConvertUtf16ToModifiedUtf8(char* utf8_out,
size_t byte_count,
const uint16_t* utf16_in,
@@ -118,75 +169,20 @@
}
// String contains non-ASCII characters.
- while (char_count--) {
- const uint16_t ch = *utf16_in++;
- if (ch > 0 && ch <= 0x7f) {
- *utf8_out++ = ch;
- } else {
- // Char_count == 0 here implies we've encountered an unpaired
- // surrogate and we have no choice but to encode it as 3-byte UTF
- // sequence. Note that unpaired surrogates can occur as a part of
- // "normal" operation.
- if ((ch >= 0xd800 && ch <= 0xdbff) && (char_count > 0)) {
- const uint16_t ch2 = *utf16_in;
-
- // Check if the other half of the pair is within the expected
- // range. If it isn't, we will have to emit both "halves" as
- // separate 3 byte sequences.
- if (ch2 >= 0xdc00 && ch2 <= 0xdfff) {
- utf16_in++;
- char_count--;
- const uint32_t code_point = (ch << 10) + ch2 - 0x035fdc00;
- *utf8_out++ = (code_point >> 18) | 0xf0;
- *utf8_out++ = ((code_point >> 12) & 0x3f) | 0x80;
- *utf8_out++ = ((code_point >> 6) & 0x3f) | 0x80;
- *utf8_out++ = (code_point & 0x3f) | 0x80;
- continue;
- }
- }
-
- if (ch > 0x07ff) {
- // Three byte encoding.
- *utf8_out++ = (ch >> 12) | 0xe0;
- *utf8_out++ = ((ch >> 6) & 0x3f) | 0x80;
- *utf8_out++ = (ch & 0x3f) | 0x80;
- } else /*(ch > 0x7f || ch == 0)*/ {
- // Two byte encoding.
- *utf8_out++ = (ch >> 6) | 0xc0;
- *utf8_out++ = (ch & 0x3f) | 0x80;
- }
- }
- }
+ // FIXME: We should not emit 4-byte sequences. Bug: 192935764
+ auto append = [&](char c) { *utf8_out++ = c; };
+ ConvertUtf16ToUtf8</*kUseShortZero=*/ false,
+ /*kUse4ByteSequence=*/ true,
+ /*kReplaceBadSurrogates=*/ false>(utf16_in, char_count, append);
}
-inline size_t CountUtf8Bytes(const uint16_t* chars, size_t char_count) {
+inline size_t CountModifiedUtf8BytesInUtf16(const uint16_t* chars, size_t char_count) {
+ // FIXME: We should not emit 4-byte sequences. Bug: 192935764
size_t result = 0;
- const uint16_t *end = chars + char_count;
- while (chars < end) {
- const uint16_t ch = *chars++;
- if (LIKELY(ch != 0 && ch < 0x80)) {
- result++;
- continue;
- }
- if (ch < 0x800) {
- result += 2;
- continue;
- }
- if (ch >= 0xd800 && ch < 0xdc00) {
- if (chars < end) {
- const uint16_t ch2 = *chars;
- // If we find a properly paired surrogate, we emit it as a 4 byte
- // UTF sequence. If we find an unpaired leading or trailing surrogate,
- // we emit it as a 3 byte sequence like would have done earlier.
- if (ch2 >= 0xdc00 && ch2 < 0xe000) {
- chars++;
- result += 4;
- continue;
- }
- }
- }
- result += 3;
- }
+ auto append = [&](char c ATTRIBUTE_UNUSED) { ++result; };
+ ConvertUtf16ToUtf8</*kUseShortZero=*/ false,
+ /*kUse4ByteSequence=*/ true,
+ /*kReplaceBadSurrogates=*/ false>(chars, char_count, append);
return result;
}
diff --git a/test/update-rollback/Android.bp b/test/update-rollback/Android.bp
index 3713328..ad14988 100644
--- a/test/update-rollback/Android.bp
+++ b/test/update-rollback/Android.bp
@@ -23,5 +23,9 @@
libs: ["tradefed"],
static_libs: ["cts-install-lib-host"],
data: [":test_broken_com.android.art"],
- test_suites: ["general-tests"],
+ // Add this test to `device-tests` rather than `general-tests` to ensure
+ // that the type of ART APEX -- public (`com.android.art`) or internal
+ // (`com.google.android.art`) -- used in the test matches the one bundled
+ // with the Android platform used in the device-under-test.
+ test_suites: ["device-tests"],
}
diff --git a/test/utils/get-device-isa b/test/utils/get-device-isa
deleted file mode 100755
index 5f9c2a4..0000000
--- a/test/utils/get-device-isa
+++ /dev/null
@@ -1,77 +0,0 @@
-#! /bin/bash
-#
-# 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() {
- cat >&2 <<EOF
-Determine and print the 32- or 64-bit architecture of a device.
-
-Usage:
- $0 --32 Select the 32-bit architecture
- $0 --64 Select the 64-bit architecture
-EOF
- exit 1
-}
-
-check_32bit() {
- if ! adb shell test -e /system/bin/linker; then
- echo >&2 "Device does not have 32-bit support"
- exit 1
- fi
-}
-
-if [[ $# -ne 1 ]]; then
- usage
-fi
-
-uname_m="$(adb shell uname -m)"
-
-case "$1" in
- (--32)
- case $uname_m in
- (armv*)
- echo arm
- ;;
- (i?86)
- echo x86
- ;;
- (aarch64)
- check_32bit
- echo arm
- ;;
- (x86_64)
- check_32bit
- echo x86
- ;;
- (*)
- echo >&2 "Unknown ISA: $uname_m"
- exit 1
- esac
- ;;
- (--64)
- case $uname_m in
- (aarch64)
- echo arm64
- ;;
- (x86_64)
- echo x86_64
- ;;
- (*)
- echo >&2 "Unknown ISA: $uname_m"
- exit 1
- esac
- ;;
- (*) usage;;
-esac
diff --git a/test/utils/get-device-test-native-lib-path b/test/utils/get-device-test-native-lib-path
deleted file mode 100755
index 21ea98c..0000000
--- a/test/utils/get-device-test-native-lib-path
+++ /dev/null
@@ -1,47 +0,0 @@
-#! /bin/bash
-#
-# 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() {
- cat >&2 <<EOF
-Determine the 32- or 64-bit architecture of a device and print the path to
-native libraries installed on the device for testing purposes.
-
-Usage:
- $0 --32 Select the 32-bit architecture
- $0 --64 Select the 64-bit architecture
-EOF
- exit 1
-}
-
-if [[ $# -ne 1 ]]; then
- usage
-fi
-
-case "$1" in
- (--32) TEST_DIRECTORY="nativetest";;
- (--64) TEST_DIRECTORY="nativetest64";;
- (*) usage;;
-esac
-
-if [[ -z "$ANDROID_BUILD_TOP" ]]; then
- echo 'ANDROID_BUILD_TOP environment variable is empty; did you forget to run `lunch`?'
- exit 1
-fi
-
-bitness_flag=$1
-ISA=$("$ANDROID_BUILD_TOP/art/test/utils/get-device-isa" "$bitness_flag")
-
-echo "/data/${TEST_DIRECTORY}/art/${ISA}"
diff --git a/test/utils/regen-test-files b/test/utils/regen-test-files
index b6c2fb6..5b72479 100755
--- a/test/utils/regen-test-files
+++ b/test/utils/regen-test-files
@@ -94,14 +94,12 @@
"1004-checker-volatile-ref-load",
"133-static-invoke-super",
"1338-gc-no-los",
- "151-OpenFileLimit",
"159-app-image-fields",
"160-read-barrier-stress",
"163-app-image-methods",
"165-lock-owner-proxy",
"168-vmstack-annotated",
"176-app-image-string",
- "2232-write-metrics-to-log",
"304-method-tracing",
"628-vdex",
"643-checker-bogus-ic",
@@ -197,17 +195,35 @@
# at Main.main(Main.java:20)
#
"821-many-args",
+ # 823-cha-inlining: Dependency on `libarttest`.
+ "823-cha-inlining",
# 826-infinite-loop: The test expects an argument passed to `Main.main` (the test library,
# usually `arttestd` or `arttest)`, but the ART run-test TradeFed test runner
# (`com.android.tradefed.testtype.ArtRunTest`) does not implement this yet.
"826-infinite-loop",
# 832-cha-recursive: Dependency on `libarttest`.
"832-cha-recursive",
+ # 837-deopt: Dependency on `libarttest`.
+ "837-deopt",
+ # 844-exception: Dependency on `libarttest`.
+ "844-exception",
+ # 844-exception2: Dependency on `libarttest`.
+ "844-exception2",
+ # 966-default-conflict: Dependency on `libarttest`.
+ "966-default-conflict",
+ # 993-breakpoints-non-debuggable: This test needs native code.
+ "993-breakpoints-non-debuggable",
+])
+
+known_failing_on_hwasan_tests = frozenset([
+ "BootImageProfileTest", # b/232012605
+ "CtsJdwpTestCases", # times out
])
# ART gtests that do not need root access to the device.
art_gtest_user_module_names = [
"art_libnativebridge_cts_tests",
+ "art_standalone_artd_tests",
"art_standalone_cmdline_tests",
"art_standalone_compiler_tests",
# Temporarily disable this test as it is failing with ART module prebuilts (see b/243510263).
@@ -224,7 +240,6 @@
"art_standalone_libprofile_tests",
"art_standalone_oatdump_tests",
"art_standalone_odrefresh_tests",
- "art_standalone_runtime_compiler_tests",
"art_standalone_runtime_tests",
"art_standalone_sigchain_tests",
"libnativeloader_test",
@@ -234,6 +249,7 @@
art_gtest_eng_only_module_names = [
"art_standalone_dexoptanalyzer_tests",
"art_standalone_profman_tests",
+ "libnativeloader_e2e_tests",
]
# All supported ART gtests.
@@ -241,57 +257,53 @@
# ART gtests supported in MTS that do not need root access to the device.
art_gtest_mts_user_module_names = copy.copy(art_gtest_user_module_names)
-# Temporarily disable `art_standalone_odrefresh_tests` in MTS,
-# as it is currently failing in Mainline testing
-# (b/206335809); a fix is in the works but may take some time
-# to land.
-#
-# TODO(b/206335809): Re-enable this test when the fix has landed.
-art_gtest_mts_user_module_names.remove("art_standalone_odrefresh_tests")
# ART gtests supported in Mainline presubmits.
art_gtests_mainline_presubmit_module_names = copy.copy(art_gtest_module_names)
-# Temporarily disable `art_standalone_odrefresh_tests` in Mainline
-# presubmits, as it is currently failing in Mainline testing
-# (b/206335809); a fix is in the works but may take some time to
-# land.
-#
-# TODO(b/206335809): Re-enable this test when the fix has landed.
-art_gtests_mainline_presubmit_module_names.remove("art_standalone_odrefresh_tests")
# Tests exhibiting a flaky behavior, currently exluded from MTS for
# the stake of stability / confidence (b/209958457).
-flaky_tests_excluded_from_mts = [
- ("CtsLibcoreFileIOTestCases" +
- " android.cts.FileChannelInterProcessLockTest#" + m) for m in [
+flaky_tests_excluded_from_mts = {
+ "CtsLibcoreFileIOTestCases": [
+ ("android.cts.FileChannelInterProcessLockTest#" + m) for m in [
"test_lockJJZ_Exclusive_asyncChannel",
"test_lockJJZ_Exclusive_syncChannel",
"test_lock_differentChannelTypes",
"test_lockJJZ_Shared_asyncChannel",
"test_lockJJZ_Shared_syncChannel",
+ ]
+ ],
+ "CtsLibcoreTestCases": [
+ ("com.android.org.conscrypt.javax.net.ssl.SSLSocketVersionCompatibilityTest#" + m + c)
+ for (m, c) in itertools.product(
+ [
+ "test_SSLSocket_interrupt_read_withoutAutoClose",
+ "test_SSLSocket_setSoWriteTimeout",
+ ],
+ [
+ "[0: TLSv1.2 client, TLSv1.2 server]",
+ "[1: TLSv1.2 client, TLSv1.3 server]",
+ "[2: TLSv1.3 client, TLSv1.2 server]",
+ "[3: TLSv1.3 client, TLSv1.3 server]",
+ ]
+ )
+ ] + [
+ ("libcore.dalvik.system.DelegateLastClassLoaderTest#" + m) for m in [
+ "testLookupOrderNodelegate_getResource",
+ "testLookupOrder_getResource",
+ ]
]
-] + [
- ("CtsLibcoreTestCases" +
- " com.android.org.conscrypt.javax.net.ssl.SSLSocketVersionCompatibilityTest#" + m + c)
- for (m, c) in itertools.product(
- [
- "test_SSLSocket_interrupt_read_withoutAutoClose",
- "test_SSLSocket_setSoWriteTimeout",
- ],
- [
- "[0: TLSv1.2 client, TLSv1.2 server]",
- "[1: TLSv1.2 client, TLSv1.3 server]",
- "[2: TLSv1.3 client, TLSv1.2 server]",
- "[3: TLSv1.3 client, TLSv1.3 server]",
- ]
- )
-] + [
- ("CtsLibcoreTestCases" +
- " libcore.dalvik.system.DelegateLastClassLoaderTest#" + m) for m in [
- "testLookupOrderNodelegate_getResource",
- "testLookupOrder_getResource",
- ]
-]
+}
+
+# Tests failing because of linking issues, currently exluded from MTS
+# and Mainline Presubmits to minimize noise in continuous runs while
+# we investigate.
+#
+# TODO(b/247108425): Address the linking issues and re-enable these
+# tests.
+failing_tests_excluded_from_mts_and_mainline_presubmits = {
+ "art_standalone_compiler_tests": ["JniCompilerTest*"],
+}
# Is `run_test` a Checker test (i.e. a test containing Checker
# assertions)?
@@ -317,37 +329,37 @@
for run_test in os.listdir(self.art_test_dir)
if re.match("^[0-9]{3,}-", run_test)])
- # Read build file (Bash script) and return a canonized version of it
- # (without comments, blank lines, "debugging" statements, etc.).
- def canonize_build_script(self, build_file):
+ # Return the metadata of a test, if any.
+ def get_test_metadata(self, run_test):
+ run_test_path = os.path.join(self.art_test_dir, run_test)
+ metadata_file = os.path.join(run_test_path, "test-metadata.json")
+ metadata = {}
+ if os.path.exists(metadata_file):
+ with open(metadata_file, "r") as f:
+ try:
+ metadata = json.load(f)
+ except json.decoder.JSONDecodeError:
+ logging.error(f"Unable to parse test metadata file `{metadata_file}`")
+ raise
+ return metadata
- def is_comment(line):
- return re.match("^\\s*#", line)
-
- def is_blank(line):
- return re.match("^\\s*$", line)
-
- # Is `line` a `set -e` statement?
- def is_set_e(line):
- return re.match("^\\s*set -e\\s*", line)
-
- # Should `line` be kept in the canonized build script?
- def keep_line(line):
- return not (is_comment(line) or is_blank(line) or is_set_e(line))
-
- with open(build_file, "r") as f:
- lines = f.readlines()
- return list(filter(keep_line, lines))
-
- # Can the build script in `build_file` be safely ignored?
- def can_ignore_build_script(self, build_file):
- build_script = self.canonize_build_script(build_file)
- if len(build_script) == 1:
- if build_script[0] == "./default-build \"$@\" --experimental var-handles\n":
- # Soong builds JARs with VarHandle support by default (i.e. by
- # using an API level greater or equal to 28), so we can ignore
- # build scripts that just request support for this feature.
- return True
+ # Can the build script of `run_test` be safely ignored?
+ def can_ignore_build_script(self, run_test):
+ # Check whether there are test metadata with build parameters
+ # enabling us to safely ignore the build script.
+ metadata = self.get_test_metadata(run_test)
+ build_param = metadata.get("build-param", {})
+ # Ignore build scripts that are just about preventing building for
+ # the JVM and/or using VarHandles (Soong builds JARs with
+ # VarHandle support by default (i.e. by using an API level greater
+ # or equal to 28), so we can ignore build scripts that just
+ # request support for this feature.)
+ experimental_var_handles = {"experimental": "var-handles"}
+ jvm_supported_false = {"jvm-supported": "false"}
+ if (build_param == experimental_var_handles or
+ build_param == jvm_supported_false or
+ build_param == experimental_var_handles | jvm_supported_false):
+ return True
return False
# Is building `run_test` supported?
@@ -357,8 +369,11 @@
# Skip tests with non-default build rules, unless these build
# rules can be safely ignored.
- if os.path.isfile(os.path.join(run_test_path, "build")):
- if not self.can_ignore_build_script(os.path.join(run_test_path, "build")):
+ if (os.path.isfile(os.path.join(run_test_path, "generate-sources")) or
+ os.path.isfile(os.path.join(run_test_path, "javac_post.sh"))):
+ return False
+ if os.path.isfile(os.path.join(run_test_path, "build.py")):
+ if not self.can_ignore_build_script(run_test):
return False
# Skip tests with sources outside the `src` directory.
for subdir in ["jasmin",
@@ -370,8 +385,7 @@
"src-bcpex",
"src-ex",
"src-ex2",
- "src-multidex",
- "src2"]:
+ "src-multidex"]:
if os.path.isdir(os.path.join(run_test_path, subdir)):
return False
# Skip tests that have both an `src` directory and an `src-art` directory.
@@ -391,19 +405,60 @@
# All other tests are considered buildable.
return True
- # Is (successfully) running `run_test` supported?
- # TODO(b/147812905): Add run-time support for more tests.
- def is_runnable(self, run_test):
- run_test_path = os.path.join(self.art_test_dir, run_test)
+ # Can the run script of `run_test` be safely ignored?
+ def can_ignore_run_script(self, run_test):
# Unconditionally consider some identified tests that have a
# (not-yet-handled) custom `run` script as runnable.
+ #
# TODO(rpl): Get rid of this exception mechanism by supporting
# these tests' `run` scripts properly.
if run_test in runnable_test_exceptions:
return True
- # Skip tests with a custom `run` script.
- if os.path.isfile(os.path.join(run_test_path, "run")):
- return False
+ # Check whether there are test metadata with run parameters
+ # enabling us to safely ignore the run script.
+ metadata = self.get_test_metadata(run_test)
+ run_param = metadata.get("run-param", {})
+ if run_param.get("default-run", ""):
+ return True
+ return False
+
+ def gen_libs_list_impl(self, library_type, libraries):
+ if len(libraries) == 0:
+ return ""
+ libraries_joined = """,
+ """.join(libraries)
+ return f"""
+ {library_type}: [
+ {libraries_joined}
+ ],"""
+
+ def gen_libs_list(self, libraries):
+ return self.gen_libs_list_impl("libs", libraries);
+
+ def gen_static_libs_list(self, libraries):
+ return self.gen_libs_list_impl("static_libs", libraries);
+
+ def gen_java_library_rule(self, name, src_dir, libraries):
+ return f"""\
+
+
+ // Library with {src_dir}/ sources for the test.
+ java_library {{
+ name: "{name}",
+ defaults: ["art-run-test-defaults"],{self.gen_libs_list(libraries)}
+ srcs: ["{src_dir}/**/*.java"],
+ }}"""
+
+ # Is (successfully) running `run_test` supported?
+ # TODO(b/147812905): Add run-time support for more tests.
+ def is_runnable(self, run_test):
+ run_test_path = os.path.join(self.art_test_dir, run_test)
+
+ # Skip tests with non-default run rules, unless these run rules
+ # can be safely ignored.
+ if os.path.isfile(os.path.join(run_test_path, "run.py")):
+ if not self.can_ignore_run_script(run_test):
+ return False
# Skip tests known to fail.
if run_test in known_failing_tests:
return False
@@ -431,11 +486,7 @@
bp_file = os.path.join(run_test_path, "Android.bp")
# Optional test metadata (JSON file).
- metadata_file = os.path.join(run_test_path, "test-metadata.json")
- metadata = {}
- if os.path.exists(metadata_file):
- with open(metadata_file, "r") as f:
- metadata = json.load(f)
+ metadata = self.get_test_metadata(run_test)
run_test_module_name = ART_RUN_TEST_MODULE_NAME_PREFIX + run_test
@@ -472,6 +523,16 @@
else:
source_dir = "src"
+ src_library_rules = []
+ test_libraries = []
+ if os.path.isdir(os.path.join(run_test_path, "src2")):
+ src_library_rules.append(self.gen_java_library_rule(
+ f"{run_test_module_name}-{source_dir}",
+ source_dir,
+ test_libraries))
+ test_libraries.append(f"\"{run_test_module_name}-src\"")
+ source_dir = "src2"
+
with open(bp_file, "w") as f:
logging.debug(f"Writing `{bp_file}`.")
f.write(textwrap.dedent(f"""\
@@ -486,14 +547,14 @@
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["art_license"],
- }}
+ }}{''.join(src_library_rules)}
// Test's Dex code.
java_test {{
name: "{run_test_module_name}",
defaults: ["art-run-test-defaults"],
test_config_template: ":{test_config_template}",
- srcs: ["{source_dir}/**/*.java"],
+ srcs: ["{source_dir}/**/*.java"],{self.gen_static_libs_list(test_libraries)}
data: [
":{run_test_module_name}-expected-stdout",
":{run_test_module_name}-expected-stderr",
@@ -523,26 +584,36 @@
run_test_module_names = [ART_RUN_TEST_MODULE_NAME_PREFIX + t for t in art_run_tests]
# Mainline presubmits.
+ mainline_presubmit_apex_suffix = "[com.google.android.art.apex]"
mainline_other_presubmit_tests = [
"ComposHostTestCases",
]
mainline_presubmit_tests = (mainline_other_presubmit_tests + run_test_module_names +
art_gtests_mainline_presubmit_module_names)
- mainline_presubmit_tests_with_apex = [t + "[com.google.android.art.apex]"
- for t
- in mainline_presubmit_tests]
- mainline_presubmit_tests_dict = [{"name": t} for t in mainline_presubmit_tests_with_apex]
+ mainline_presubmit_tests_dict = [
+ ({"name": t + mainline_presubmit_apex_suffix,
+ "options": [
+ {"exclude-filter": e}
+ for e in failing_tests_excluded_from_mts_and_mainline_presubmits[t]
+ ]}
+ if t in failing_tests_excluded_from_mts_and_mainline_presubmits
+ else {"name": t + mainline_presubmit_apex_suffix})
+ for t in mainline_presubmit_tests
+ ]
# Presubmits.
other_presubmit_tests = [
- "CtsJdwpTestCases",
- "BootImageProfileTest",
"ArtServiceTests",
+ "BootImageProfileTest",
"ComposHostTestCases",
+ "CtsJdwpTestCases",
+ "art-apex-update-rollback",
"art_standalone_dexpreopt_tests",
]
presubmit_tests = other_presubmit_tests + run_test_module_names + art_gtest_module_names
presubmit_tests_dict = [{"name": t} for t in presubmit_tests]
+ hwasan_presubmit_tests_dict = [{"name": t} for t in presubmit_tests
+ if t not in known_failing_on_hwasan_tests]
# Use an `OrderedDict` container to preserve the order in which items are inserted.
# Do not produce an entry for a test group if it is empty.
@@ -552,6 +623,7 @@
in [
("mainline-presubmit", mainline_presubmit_tests_dict),
("presubmit", presubmit_tests_dict),
+ ("hwasan-presubmit", hwasan_presubmit_tests_dict),
]
if test_group_dict
])
@@ -701,18 +773,25 @@
include.setAttribute("name", f"mts-art-tests-list-user-shard-{s:02}")
configuration.appendChild(include)
- # Excluded flaky tests.
- xml_comment = root.createComment(f" Excluded flaky tests (b/209958457). ")
- configuration.appendChild(xml_comment)
-
def append_test_exclusion(test):
option = root.createElement("option")
option.setAttribute("name", "compatibility:exclude-filter")
option.setAttribute("value", test)
configuration.appendChild(option)
- for t in flaky_tests_excluded_from_mts:
- append_test_exclusion(t)
+ # Excluded flaky tests.
+ xml_comment = root.createComment(" Excluded flaky tests (b/209958457). ")
+ configuration.appendChild(xml_comment)
+ for module in flaky_tests_excluded_from_mts:
+ for testcase in flaky_tests_excluded_from_mts[module]:
+ append_test_exclusion(f"{module} {testcase}")
+
+ # Excluded failing tests.
+ xml_comment = root.createComment(" Excluded failing tests (b/247108425). ")
+ configuration.appendChild(xml_comment)
+ for module in failing_tests_excluded_from_mts_and_mainline_presubmits:
+ for testcase in failing_tests_excluded_from_mts_and_mainline_presubmits[module]:
+ append_test_exclusion(f"{module} {testcase}")
xml_str = root.toprettyxml(indent = XML_INDENT, encoding = "utf-8")
@@ -738,18 +817,11 @@
mts_test_shards = []
- # ART test (gtest & run-test) shard(s).
- # TODO: Also handle the case of gtests requiring root access to the device
- # (`art_gtest_eng_only_module_names`).
+ # ART run-tests shard(s).
art_run_test_module_names = [ART_RUN_TEST_MODULE_NAME_PREFIX + t for t in art_run_tests]
art_run_test_shards = split_list(art_run_test_module_names, NUM_MTS_ART_RUN_TEST_SHARDS)
for i in range(len(art_run_test_shards)):
art_tests_shard_i_tests = art_run_test_shards[i]
- # Append ART gtests to the last ART run-test shard for now.
- # If needed, consider moving them to their own shard to increase
- # the parallelization of code coverage runs.
- if i + 1 == len(art_run_test_shards):
- art_tests_shard_i_tests.extend(art_gtest_mts_user_module_names)
art_tests_shard_i = self.create_mts_test_shard(
"ART run-tests", art_tests_shard_i_tests, i, 2020,
["TODO(rpl): Find a way to express this list in a more concise fashion."])
@@ -777,6 +849,15 @@
other_cts_libcore_tests_shard_num, 2021)
mts_test_shards.append(other_cts_libcore_tests_shard)
+ # ART gtests shard.
+ # TODO: Also handle the case of gtests requiring root access to the device
+ # (`art_gtest_eng_only_module_names`).
+ art_gtests_shard_num = len(mts_test_shards)
+ art_gtests_shard_tests = art_gtest_mts_user_module_names
+ art_gtests_shard = self.create_mts_test_shard(
+ "ART gtests", art_gtests_shard_tests, art_gtests_shard_num, 2022)
+ mts_test_shards.append(art_gtests_shard)
+
for s in mts_test_shards:
s.regen_test_plan_file()
s.regen_test_list_file()
diff --git a/tools/Android.bp b/tools/Android.bp
index b7f5c1b..7ffa672 100644
--- a/tools/Android.bp
+++ b/tools/Android.bp
@@ -97,23 +97,3 @@
},
},
}
-
-python_binary_host {
- name: "art-run-test-checker",
- srcs: [
- "checker/**/*.py",
- ],
- main: "checker/checker.py",
- version: {
- py2: {
- enabled: false,
- },
- py3: {
- enabled: true,
- },
- },
- test_suites: [
- "general-tests",
- "mts-art",
- ],
-}
diff --git a/tools/PresubmitJsonLinter.java b/tools/PresubmitJsonLinter.java
new file mode 100644
index 0000000..334d200
--- /dev/null
+++ b/tools/PresubmitJsonLinter.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import com.google.gson.stream.JsonReader;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+/**
+ * Pre upload hook that ensures art-buildbot expectation files (files under //art/tools ending with
+ * "_failures.txt", e.g. //art/tools/libcore_failures.txt) are well-formed json files.
+ *
+ * It makes basic validation of the keys but does not cover all the cases. Parser structure is
+ * based on external/vogar/src/vogar/ExpectationStore.java.
+ *
+ * Hook is set up in //art/PREUPLOAD.cfg See also //tools/repohooks/README.md
+ */
+class PresubmitJsonLinter {
+
+ private static final int FLAGS = Pattern.MULTILINE | Pattern.DOTALL;
+ private static final Set<String> RESULTS = new HashSet<>();
+
+ static {
+ RESULTS.addAll(List.of(
+ "UNSUPPORTED",
+ "COMPILE_FAILED",
+ "EXEC_FAILED",
+ "EXEC_TIMEOUT",
+ "ERROR",
+ "SUCCESS"
+ ));
+ }
+
+ public static void main(String[] args) {
+ for (String arg : args) {
+ info("Checking " + arg);
+ checkExpectationFile(arg);
+ }
+ }
+
+ private static void info(String message) {
+ System.err.println(message);
+ }
+
+ private static void error(String message) {
+ System.err.println(message);
+ System.exit(1);
+ }
+
+ private static void checkExpectationFile(String arg) {
+ JsonReader reader;
+ try {
+ reader = new JsonReader(new FileReader(arg));
+ } catch (FileNotFoundException e) {
+ error("File '" + arg + "' is not found");
+ return;
+ }
+ reader.setLenient(true);
+ try {
+ reader.beginArray();
+ while (reader.hasNext()) {
+ readExpectation(reader);
+ }
+ reader.endArray();
+ } catch (IOException e) {
+ error("Malformed json: " + reader);
+ }
+ }
+
+ private static void readExpectation(JsonReader reader) throws IOException {
+ Set<String> names = new LinkedHashSet<String>();
+ Set<String> tags = new LinkedHashSet<String>();
+ boolean readResult = false;
+ boolean readDescription = false;
+
+ reader.beginObject();
+ while (reader.hasNext()) {
+ String name = reader.nextName();
+ switch (name) {
+ case "result":
+ String result = reader.nextString();
+ if (!RESULTS.contains(result)) {
+ error("Invalid 'result' value: '" + result +
+ "'. Expected one of " + String.join(", ", RESULTS) +
+ ". " + reader);
+ }
+ readResult = true;
+ break;
+ case "substring": {
+ try {
+ Pattern.compile(
+ ".*" + Pattern.quote(reader.nextString()) + ".*", FLAGS);
+ } catch (PatternSyntaxException e) {
+ error("Malformed 'substring' value: " + reader);
+ }
+ }
+ case "pattern": {
+ try {
+ Pattern.compile(reader.nextString(), FLAGS);
+ } catch (PatternSyntaxException e) {
+ error("Malformed 'pattern' value: " + reader);
+ }
+ break;
+ }
+ case "failure":
+ names.add(reader.nextString());
+ break;
+ case "description":
+ reader.nextString();
+ readDescription = true;
+ break;
+ case "name":
+ names.add(reader.nextString());
+ break;
+ case "names":
+ readStrings(reader, names);
+ break;
+ case "tags":
+ readStrings(reader, tags);
+ break;
+ case "bug":
+ reader.nextLong();
+ break;
+ case "modes":
+ readModes(reader);
+ break;
+ case "modes_variants":
+ readModesAndVariants(reader);
+ break;
+ default:
+ error("Unknown key '" + name + "' in expectations file");
+ reader.skipValue();
+ break;
+ }
+ }
+ reader.endObject();
+
+ if (names.isEmpty()) {
+ error("Missing 'name' or 'failure' key in " + reader);
+ }
+ if (!readResult) {
+ error("Missing 'result' key in " + reader);
+ }
+ if (!readDescription) {
+ error("Missing 'description' key in " + reader);
+ }
+ }
+
+ private static void readStrings(JsonReader reader, Set<String> output) throws IOException {
+ reader.beginArray();
+ while (reader.hasNext()) {
+ output.add(reader.nextString());
+ }
+ reader.endArray();
+ }
+
+ private static void readModes(JsonReader reader) throws IOException {
+ reader.beginArray();
+ while (reader.hasNext()) {
+ reader.nextString();
+ }
+ reader.endArray();
+ }
+
+ /**
+ * Expected format: mode_variants: [["host", "X32"], ["host", "X64"]]
+ */
+ private static void readModesAndVariants(JsonReader reader) throws IOException {
+ reader.beginArray();
+ while (reader.hasNext()) {
+ reader.beginArray();
+ reader.nextString();
+ reader.nextString();
+ reader.endArray();
+ }
+ reader.endArray();
+ }
+}
\ No newline at end of file
diff --git a/tools/ahat/.clang-format b/tools/ahat/.clang-format
new file mode 120000
index 0000000..88ab38e
--- /dev/null
+++ b/tools/ahat/.clang-format
@@ -0,0 +1 @@
+../../.clang-format-java-2
\ No newline at end of file
diff --git a/tools/ahat/Android.bp b/tools/ahat/Android.bp
index affa2e0..5f6ba81 100644
--- a/tools/ahat/Android.bp
+++ b/tools/ahat/Android.bp
@@ -27,7 +27,7 @@
visibility: [
"//libcore/metrictests/memory/host",
],
- wrapper: "ahat",
+ wrapper: "ahat.sh",
srcs: ["src/main/**/*.java"],
manifest: "etc/ahat.mf",
java_resources: ["etc/style.css"],
diff --git a/tools/ahat/ahat b/tools/ahat/ahat.sh
similarity index 100%
rename from tools/ahat/ahat
rename to tools/ahat/ahat.sh
diff --git a/tools/ahat/etc/style.css b/tools/ahat/etc/style.css
index 47fae1d..83e5b20 100644
--- a/tools/ahat/etc/style.css
+++ b/tools/ahat/etc/style.css
@@ -14,6 +14,11 @@
* limitations under the License.
*/
+html {
+ /* Roboto has tabular numbers, use it if available, fallback to other sans. */
+ font-family: "Roboto", "Arial", "sans-serif";
+}
+
div.menu {
background-color: #eeffff;
}
@@ -39,3 +44,23 @@
padding-left: 8px;
padding-right: 8px;
}
+
+.sidebar {
+ position: fixed;
+ right: 0;
+ top: 48px;
+ padding-left: 12px;
+ padding-right: 24px;
+ font-size: small;
+ border-left: 4px solid #dcedc8;
+}
+
+.sidebar a {
+ text-decoration: none;
+ color: #4285f4;
+}
+
+.sidebar a:hover {
+ text-decoration: underline;
+ color: #073042;
+}
\ No newline at end of file
diff --git a/tools/ahat/src/main/com/android/ahat/DocString.java b/tools/ahat/src/main/com/android/ahat/DocString.java
index eda9b38..ca5dbf0 100644
--- a/tools/ahat/src/main/com/android/ahat/DocString.java
+++ b/tools/ahat/src/main/com/android/ahat/DocString.java
@@ -136,7 +136,7 @@
if (isPlaceHolder) {
string.append(DocString.removed("del"));
} else if (size != 0) {
- string.appendFormat("%,14d", size);
+ string.appendFormat("%,d", size);
}
return string;
}
@@ -162,13 +162,13 @@
public DocString appendDelta(boolean noCurrent, boolean noBaseline,
long current, long baseline) {
if (noCurrent) {
- append(removed(format("%+,14d", 0 - baseline)));
+ append(removed(format("%+,d", 0 - baseline)));
} else if (noBaseline) {
append(added("new"));
} else if (current > baseline) {
- append(added(format("%+,14d", current - baseline)));
+ append(added(format("%+,d", current - baseline)));
} else if (current < baseline) {
- append(removed(format("%+,14d", current - baseline)));
+ append(removed(format("%+,d", current - baseline)));
}
return this;
}
diff --git a/tools/ahat/src/main/com/android/ahat/HtmlDoc.java b/tools/ahat/src/main/com/android/ahat/HtmlDoc.java
index d5106dc..6c3ab2f 100644
--- a/tools/ahat/src/main/com/android/ahat/HtmlDoc.java
+++ b/tools/ahat/src/main/com/android/ahat/HtmlDoc.java
@@ -18,6 +18,7 @@
import java.io.PrintStream;
import java.net.URI;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -26,6 +27,7 @@
class HtmlDoc implements Doc {
private PrintStream ps;
private Column[] mCurrentTableColumns;
+ private List<String> mSections;
/**
* Create an HtmlDoc that writes to the given print stream.
@@ -34,6 +36,7 @@
*/
public HtmlDoc(PrintStream ps, DocString title, URI style) {
this.ps = ps;
+ mSections = new ArrayList<>();
ps.println("<!DOCTYPE html>");
ps.println("<html>");
@@ -59,9 +62,10 @@
@Override
public void section(String title) {
- ps.print("<h2>");
+ ps.format("<h2 id=\"%d\">", mSections.size());
ps.print(DocString.text(title).html());
ps.println(":</h2>");
+ mSections.add(title);
}
@Override
@@ -182,8 +186,17 @@
mCurrentTableColumns = null;
}
+ private void sidebar() {
+ ps.println("<div class=\"sidebar\">");
+ for (int i = 0; i < mSections.size(); i++) {
+ ps.format("<p><a href=\"#%d\">%s</a></p>", i, mSections.get(i));
+ }
+ ps.println("</div>");
+ }
+
@Override
public void close() {
+ sidebar();
ps.println("</body>");
ps.println("</html>");
ps.close();
diff --git a/tools/ahat/src/main/com/android/ahat/ObjectsHandler.java b/tools/ahat/src/main/com/android/ahat/ObjectsHandler.java
index 81611b6..4cdbaf4 100644
--- a/tools/ahat/src/main/com/android/ahat/ObjectsHandler.java
+++ b/tools/ahat/src/main/com/android/ahat/ObjectsHandler.java
@@ -111,7 +111,7 @@
heapChoice.append(")");
doc.description(DocString.text("Heap"), heapChoice);
- doc.description(DocString.text("Count"), DocString.format("%,14d", insts.size()));
+ doc.description(DocString.text("Count"), DocString.format("%,d", insts.size()));
doc.end();
doc.println(DocString.text(""));
diff --git a/tools/ahat/src/main/com/android/ahat/OverviewHandler.java b/tools/ahat/src/main/com/android/ahat/OverviewHandler.java
index 5f0b473..c6f4a54 100644
--- a/tools/ahat/src/main/com/android/ahat/OverviewHandler.java
+++ b/tools/ahat/src/main/com/android/ahat/OverviewHandler.java
@@ -57,8 +57,6 @@
doc.section("Bytes Retained by Heap");
printHeapSizes(doc);
-
- doc.big(Menu.getMenu());
}
private void printHeapSizes(Doc doc) {
diff --git a/tools/ahat/src/main/com/android/ahat/SiteHandler.java b/tools/ahat/src/main/com/android/ahat/SiteHandler.java
index 5093f0d..671784e 100644
--- a/tools/ahat/src/main/com/android/ahat/SiteHandler.java
+++ b/tools/ahat/src/main/com/android/ahat/SiteHandler.java
@@ -102,7 +102,7 @@
DocString.link(
DocString.formattedUri("objects?id=%d&heap=%s&class=%s",
site.getId(), info.heap.getName(), className),
- DocString.format("%,14d", info.numInstances)),
+ DocString.format("%,d", info.numInstances)),
DocString.delta(false, false, info.numInstances, baseinfo.numInstances),
DocString.text(info.heap.getName()),
Summarizer.summarize(info.classObj));
diff --git a/tools/ahat/src/test-dump/Main.java b/tools/ahat/src/test-dump/Main.java
index 2e29076..711d662 100644
--- a/tools/ahat/src/test-dump/Main.java
+++ b/tools/ahat/src/test-dump/Main.java
@@ -43,6 +43,10 @@
// Allocate the instance of DumpedStuff.
stuff = new DumpedStuff(baseline);
+ // Preemptively garbage collect to avoid an inopportune GC triggering
+ // after this.
+ Runtime.getRuntime().gc();
+
// Create a bunch of unreachable objects pointing to basicString for the
// reverseReferencesAreNotUnreachable test
for (int i = 0; i < 100; i++) {
diff --git a/tools/ahat/src/test/com/android/ahat/InstanceTest.java b/tools/ahat/src/test/com/android/ahat/InstanceTest.java
index 376122b..1f29030 100644
--- a/tools/ahat/src/test/com/android/ahat/InstanceTest.java
+++ b/tools/ahat/src/test/com/android/ahat/InstanceTest.java
@@ -224,6 +224,7 @@
@Test
public void reachability() throws IOException {
TestDump dump = TestDump.getTestDump();
+ // We were careful to avoid GC before dumping, so nothing here should be null.
AhatInstance strong1 = dump.getDumpedAhatInstance("reachabilityReferenceChain");
AhatInstance soft1 = strong1.getField("referent").asAhatInstance();
AhatInstance strong2 = soft1.getField("referent").asAhatInstance();
diff --git a/tools/build_linux_bionic.sh b/tools/build_linux_bionic.sh
index 8992512..bbe71b6 100755
--- a/tools/build_linux_bionic.sh
+++ b/tools/build_linux_bionic.sh
@@ -16,83 +16,38 @@
# This will build a target using linux_bionic. It can be called with normal make
# flags.
-#
-# TODO This runs a 'm clean' prior to building the targets in order to ensure
-# that obsolete kati files don't mess up the build.
-if [[ -z $ANDROID_BUILD_TOP ]]; then
- pushd .
-else
- pushd $ANDROID_BUILD_TOP
-fi
+set -e
if [ ! -d art ]; then
echo "Script needs to be run at the root of the android tree"
exit 1
fi
+export TARGET_PRODUCT=linux_bionic
+
+# Avoid Soong error about invalid dependencies on disabled libLLVM_android,
+# which we get due to the --soong-only mode. (Another variant is to set
+# SOONG_ALLOW_MISSING_DEPENDENCIES).
+export FORCE_BUILD_LLVM_COMPONENTS=true
+
# TODO(b/194433871): Set MODULE_BUILD_FROM_SOURCE to disable prebuilt modules,
# which Soong otherwise can create duplicate install rules for in --soong-only
# mode.
-soong_args="MODULE_BUILD_FROM_SOURCE=true"
+export MODULE_BUILD_FROM_SOURCE=true
# Switch the build system to unbundled mode in the reduced manifest branch.
if [ ! -d frameworks/base ]; then
- soong_args="$soong_args TARGET_BUILD_UNBUNDLED=true"
+ export TARGET_BUILD_UNBUNDLED=true
fi
-source build/envsetup.sh >&/dev/null # for get_build_var
-# Soong needs a bunch of variables set and will not run if they are missing.
-# The default values of these variables is only contained in make, so use
-# nothing to create the variables then remove all the other artifacts.
-# Lunch since it seems we cannot find the build-number otherwise.
-lunch aosp_x86-eng
-build/soong/soong_ui.bash --make-mode $soong_args nothing
+vars="$(build/soong/soong_ui.bash --dumpvars-mode --vars="OUT_DIR BUILD_NUMBER")"
+# Assign to a variable and eval that, since bash ignores any error status from
+# the command substitution if it's directly on the eval line.
+eval $vars
-if [ $? != 0 ]; then
- exit 1
-fi
+# This file is currently not created in --soong-only mode, but some build
+# targets depend on it.
+printf %s "${BUILD_NUMBER}" > ${OUT_DIR}/soong/build_number.txt
-out_dir=$(get_build_var OUT_DIR)
-host_out=$(get_build_var HOST_OUT)
-
-# TODO(b/31559095) Figure out a better way to do this.
-#
-# There is no good way to force soong to generate host-bionic builds currently
-# so this is a hacky workaround.
-tmp_soong_var=$(mktemp --tmpdir soong.variables.bak.XXXXXX)
-tmp_build_number=$(cat ${out_dir}/soong/build_number.txt)
-
-cat $out_dir/soong/soong.variables > ${tmp_soong_var}
-
-# See comment above about b/123645297 for why we cannot just do m clean. Clear
-# out all files except for intermediates and installed files and dexpreopt.config.
-find $out_dir/ -maxdepth 1 -mindepth 1 \
- -not -name soong \
- -not -name host \
- -not -name target | xargs -I '{}' rm -rf '{}'
-find $out_dir/soong/ -maxdepth 1 -mindepth 1 \
- -not -name .intermediates \
- -not -name host \
- -not -name dexpreopt.config \
- -not -name target | xargs -I '{}' rm -rf '{}'
-
-python3 <<END - ${tmp_soong_var} ${out_dir}/soong/soong.variables
-import json
-import sys
-x = json.load(open(sys.argv[1]))
-x['Allow_missing_dependencies'] = True
-x['HostArch'] = 'x86_64'
-x['CrossHost'] = 'linux_bionic'
-x['CrossHostArch'] = 'x86_64'
-if 'CrossHostSecondaryArch' in x:
- del x['CrossHostSecondaryArch']
-json.dump(x, open(sys.argv[2], mode='w'))
-END
-
-rm $tmp_soong_var
-
-# Write a new build-number
-echo ${tmp_build_number}_SOONG_ONLY_BUILD > ${out_dir}/soong/build_number.txt
-
-build/soong/soong_ui.bash --make-mode --skip-config --soong-only $soong_args $@
+build/soong/soong_ui.bash --make-mode --soong-only "$@"
diff --git a/tools/build_linux_bionic_tests.sh b/tools/build_linux_bionic_tests.sh
index 7379e9a..0470d6d 100755
--- a/tools/build_linux_bionic_tests.sh
+++ b/tools/build_linux_bionic_tests.sh
@@ -14,64 +14,36 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-
-if [[ -z $ANDROID_BUILD_TOP ]]; then
- pushd .
-else
- pushd $ANDROID_BUILD_TOP
-fi
+set -e
if [ ! -d art ]; then
echo "Script needs to be run at the root of the android tree"
exit 1
fi
-soong_args=""
-
# Switch the build system to unbundled mode in the reduced manifest branch.
if [ ! -d frameworks/base ]; then
- soong_args="$soong_args TARGET_BUILD_UNBUNDLED=true"
+ export TARGET_BUILD_UNBUNDLED=true
fi
-source build/envsetup.sh >&/dev/null # for get_build_var
-
-out_dir=$(get_build_var OUT_DIR)
-host_out=$(get_build_var HOST_OUT)
-
-# TODO(b/31559095) Figure out a better way to do this.
-#
-# There is no good way to force soong to generate host-bionic builds currently
-# so this is a hacky workaround.
+vars="$(build/soong/soong_ui.bash --dumpvars-mode --vars="OUT_DIR HOST_OUT")"
+# Assign to a variable and eval that, since bash ignores any error status from
+# the command substitution if it's directly on the eval line.
+eval $vars
# First build all the targets still in .mk files (also build normal glibc host
# targets so we know what's needed to run the tests).
-build/soong/soong_ui.bash --make-mode $soong_args "$@" test-art-host-run-test-dependencies build-art-host-tests
-if [ $? != 0 ]; then
- exit 1
-fi
+build/soong/soong_ui.bash --make-mode "$@" test-art-host-run-test-dependencies build-art-host-tests
-tmp_soong_var=$(mktemp --tmpdir soong.variables.bak.XXXXXX)
+# Next build the Linux host Bionic targets in --soong-only mode.
+export TARGET_PRODUCT=linux_bionic
-echo "Saving soong.variables to " $tmp_soong_var
-cat $out_dir/soong/soong.variables > ${tmp_soong_var}
-python3 <<END - ${tmp_soong_var} ${out_dir}/soong/soong.variables
-import json
-import sys
-x = json.load(open(sys.argv[1]))
-x['Allow_missing_dependencies'] = True
-x['HostArch'] = 'x86_64'
-x['CrossHost'] = 'linux_bionic'
-x['CrossHostArch'] = 'x86_64'
-if 'CrossHostSecondaryArch' in x:
- del x['CrossHostSecondaryArch']
-json.dump(x, open(sys.argv[2], mode='w'))
-END
-if [ $? != 0 ]; then
- mv $tmp_soong_var $out_dir/soong/soong.variables
- exit 2
-fi
+# Avoid Soong error about invalid dependencies on disabled libLLVM_android,
+# which we get due to the --soong-only mode. (Another variant is to set
+# SOONG_ALLOW_MISSING_DEPENDENCIES).
+export FORCE_BUILD_LLVM_COMPONENTS=true
-soong_out=$out_dir/soong/host/linux_bionic-x86
+soong_out=$OUT_DIR/soong/host/linux_bionic-x86
declare -a bionic_targets
# These are the binaries actually used in tests. Since some of the files are
# java targets or 32 bit we cannot just do the same find for the bin files.
@@ -89,24 +61,10 @@
$soong_out/bin/hprof-conv
$soong_out/bin/signal_dumper
$soong_out/lib64/libclang_rt.ubsan_standalone-x86_64-android.so
- $(find $host_out/apex/com.android.art.host.zipapex -type f | sed "s:$host_out:$soong_out:g")
- $(find $host_out/lib64 -type f | sed "s:$host_out:$soong_out:g")
- $(find $host_out/nativetest64 -type f | sed "s:$host_out:$soong_out:g"))
+ $(find $HOST_OUT/apex/com.android.art.host.zipapex -type f | sed "s:$HOST_OUT:$soong_out:g")
+ $(find $HOST_OUT/lib64 -type f | sed "s:$HOST_OUT:$soong_out:g")
+ $(find $HOST_OUT/nativetest64 -type f | sed "s:$HOST_OUT:$soong_out:g"))
echo building ${bionic_targets[*]}
-build/soong/soong_ui.bash --make-mode --skip-config --soong-only $soong_args "$@" ${bionic_targets[*]}
-ret=$?
-
-mv $tmp_soong_var $out_dir/soong/soong.variables
-
-# Having built with host-bionic confuses soong somewhat by making it think the
-# linux_bionic targets are needed for art phony targets like
-# test-art-host-run-test-dependencies. To work around this blow away all
-# ninja files in OUT_DIR. The build system is smart enough to not need to
-# rebuild stuff so this should be fine.
-rm -f $OUT_DIR/*.ninja $OUT_DIR/soong/*.ninja
-
-popd
-
-exit $ret
+build/soong/soong_ui.bash --make-mode --soong-only "$@" ${bionic_targets[*]}
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index 48fc004..cad218f 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -25,7 +25,7 @@
exit 1
fi
-TARGET_ARCH=$(source build/envsetup.sh > /dev/null; get_build_var TARGET_ARCH)
+TARGET_ARCH=$(build/soong/soong_ui.bash --dumpvar-mode TARGET_ARCH)
# Logic for setting out_dir from build/make/core/envsetup.mk:
if [[ -z $OUT_DIR ]]; then
@@ -69,6 +69,9 @@
elif [[ "$1" == "--showcommands" ]]; then
showcommands="showcommands"
shift
+ elif [[ "$1" == "--dist" ]]; then
+ common_targets="$common_targets dist"
+ shift
elif [[ "$1" == "" ]]; then
break
else
@@ -120,13 +123,12 @@
# Indirect dependencies in the platform, e.g. through heapprofd_client_api.
# These are built to go into system/lib(64) to be part of the system linker
# namespace.
- make_command+=" libbacktrace libnetd_client-target libprocinfo libtombstoned_client libunwindstack"
+ make_command+=" libnetd_client-target libprocinfo libtombstoned_client libunwindstack"
# Stubs for other APEX SDKs, for use by vogar. Referenced from DEVICE_JARS in
# external/vogar/src/vogar/ModeId.java.
# Note these go into out/target/common/obj/JAVA_LIBRARIES which isn't removed
# by "m installclean".
make_command+=" i18n.module.public.api.stubs conscrypt.module.public.api.stubs"
- make_command+=" ${ANDROID_PRODUCT_OUT#"${ANDROID_BUILD_TOP}/"}/system/etc/public.libraries.txt"
# Targets required to generate a linker configuration for device within the
# chroot environment. The *.libraries.txt targets are required by
# the source linkerconfig but not included in the prebuilt one.
@@ -166,6 +168,8 @@
# Extract prebuilt APEXes.
debugfs=$ANDROID_HOST_OUT/bin/debugfs_static
+ fsckerofs=$ANDROID_HOST_OUT/bin/fsck.erofs
+ blkid=$ANDROID_HOST_OUT/bin/blkid_static
for apex in ${apexes[@]}; do
dir="$ANDROID_PRODUCT_OUT/system/apex/${apex}"
apexbase="$ANDROID_PRODUCT_OUT/system/apex/${apex}"
@@ -179,18 +183,24 @@
msginfo "Extracting APEX file:" "${file}"
rm -rf $dir
mkdir -p $dir
- $ANDROID_HOST_OUT/bin/deapexer --debugfs_path $debugfs extract $file $dir
+ $ANDROID_HOST_OUT/bin/deapexer --debugfs_path $debugfs --fsckerofs_path $fsckerofs \
+ --blkid_path $blkid extract $file $dir
fi
done
- # Replace stub libraries with implemenation libraries: because we do chroot
+ # Replace stub libraries with implementation libraries: because we do chroot
# testing, we need to install an implementation of the libraries (and cannot
# rely on the one already installed on the device, if the device is post R and
# has it).
implementation_libs=(
"heapprofd_client_api.so"
+ "libandroid_runtime_lazy.so"
"libartpalette-system.so"
- "liblog.so"
+ "libbinder.so"
+ "libbinder_ndk.so"
+ "libcutils.so"
+ "libutils.so"
+ "libvndksupport.so"
)
if [ -d prebuilts/runtime/mainline/platform/impl ]; then
if [[ $TARGET_ARCH = arm* ]]; then
@@ -299,6 +309,11 @@
mkdir -p $linkerconfig_root/system
cp -r $ANDROID_PRODUCT_OUT/system/etc $linkerconfig_root/system
+ # Use our smaller public.libraries.txt that contains only the public libraries
+ # pushed to the chroot directory.
+ cp $ANDROID_BUILD_TOP/art/tools/public.libraries.buildbot.txt \
+ $linkerconfig_root/system/etc/public.libraries.txt
+
# For linkerconfig to pick up the APEXes correctly we need to make them
# available in $linkerconfig_root/apex.
mkdir -p $linkerconfig_root/apex
diff --git a/tools/buildbot-setup-device.sh b/tools/buildbot-setup-device.sh
index ad2c59c..811ba80 100755
--- a/tools/buildbot-setup-device.sh
+++ b/tools/buildbot-setup-device.sh
@@ -26,6 +26,7 @@
fi
# Setup as root, as some actions performed here require it.
+adb version
adb root
adb wait-for-device
@@ -36,7 +37,11 @@
adb shell date
host_seconds_since_epoch=$(date -u +%s)
-device_seconds_since_epoch=$(adb shell date -u +%s)
+
+# Get the device time in seconds, but filter the output as some
+# devices emit CRLF at the end of the command which then breaks the
+# time comparisons in this script (Hammerhead, MRA59G 2457013).
+device_seconds_since_epoch=$(adb shell date -u +%s | tr -c -d '[:digit:]')
abs_time_difference_in_seconds=$(expr $host_seconds_since_epoch - $device_seconds_since_epoch)
if [ $abs_time_difference_in_seconds -lt 0 ]; then
@@ -173,6 +178,8 @@
|| adb shell mount -o bind /dev "$ART_TEST_CHROOT/dev"
adb shell mount | grep -q "^devpts on $ART_TEST_CHROOT/dev/pts type devpts " \
|| adb shell mount -o bind /dev/pts "$ART_TEST_CHROOT/dev/pts"
+ adb shell mount | grep -q " on $ART_TEST_CHROOT/dev/cpuset type cgroup " \
+ || adb shell mount -o bind /dev/cpuset "$ART_TEST_CHROOT/dev/cpuset"
# Create /apex directory in chroot.
adb shell mkdir -p "$ART_TEST_CHROOT/apex"
diff --git a/tools/buildbot-sync.sh b/tools/buildbot-sync.sh
index 28dab0c..ba49c61 100755
--- a/tools/buildbot-sync.sh
+++ b/tools/buildbot-sync.sh
@@ -95,6 +95,8 @@
msginfo "Extracting APEX ${src_apex_file}..."
mkdir -p $src_apex_path
$ANDROID_HOST_OUT/bin/deapexer --debugfs_path $ANDROID_HOST_OUT/bin/debugfs_static \
+ --fsckerofs_path $ANDROID_HOST_OUT/bin/fsck.erofs \
+ --blkid_path $ANDROID_HOST_OUT/bin/blkid_static \
extract ${src_apex_file} $src_apex_path
fi
diff --git a/tools/buildbot-teardown-device.sh b/tools/buildbot-teardown-device.sh
index 927e3c5..156b4f1 100755
--- a/tools/buildbot-teardown-device.sh
+++ b/tools/buildbot-teardown-device.sh
@@ -79,7 +79,7 @@
local remove_dir=$3
local dir="$ART_TEST_CHROOT/$dir_in_chroot"
adb shell test -d "$dir" \
- && adb shell mount | grep -q "^$fstype on $dir type $fstype " \
+ && adb shell mount | grep -q " on $dir type $fstype " \
&& if adb shell umount "$dir"; then
$remove_dir && adb shell rmdir "$dir"
else
@@ -95,6 +95,7 @@
adb shell rm -rf "$ART_TEST_CHROOT/apex"
# Remove /dev from chroot.
+ remove_filesystem_from_chroot dev/cpuset cgroup false
remove_filesystem_from_chroot dev/pts devpts false
remove_filesystem_from_chroot dev tmpfs true
diff --git a/tools/check_presubmit_json_expectations.sh b/tools/check_presubmit_json_expectations.sh
new file mode 100755
index 0000000..ecb1e3e
--- /dev/null
+++ b/tools/check_presubmit_json_expectations.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 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.
+
+set -e
+
+REPO_ROOT="$1"
+
+FILES_TO_CHECK=()
+for i in "${@:2}"; do
+ if [[ $i == *_failures.txt ]]; then
+ FILES_TO_CHECK+=($i)
+ fi
+done
+
+# if no libcore_*_failures.txt files were changed
+if [ ${#FILES_TO_CHECK[@]} -eq 0 ]; then
+ exit 0
+fi
+
+TMP_DIR=`mktemp -d`
+# check if tmp dir was created
+if [[ ! "$TMP_DIR" || ! -d "$TMP_DIR" ]]; then
+ echo "Could not create temp dir"
+ exit 1
+fi
+
+function cleanup {
+ rm -rf "$TMP_DIR"
+}
+
+# register the cleanup function to be called on the EXIT signal
+trap cleanup EXIT
+
+GSON_JAR="${REPO_ROOT}/external/caliper/lib/gson-2.2.2.jar"
+
+javac --class-path "$GSON_JAR" "${REPO_ROOT}/art/tools/PresubmitJsonLinter.java" -d "$TMP_DIR"
+java --class-path "$TMP_DIR:$GSON_JAR" PresubmitJsonLinter "${FILES_TO_CHECK[@]}"
diff --git a/tools/checker/Android.bp b/tools/checker/Android.bp
new file mode 100644
index 0000000..db2c597
--- /dev/null
+++ b/tools/checker/Android.bp
@@ -0,0 +1,41 @@
+//
+// Copyright (C) 2022 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "art_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["art_license"],
+}
+
+python_binary_host {
+ name: "art-run-test-checker",
+ srcs: [
+ "**/*.py",
+ ],
+ main: "checker.py",
+ version: {
+ py3: {
+ embedded_launcher: true,
+ },
+ },
+ test_suites: [
+ "general-tests",
+ "mts-art",
+ ],
+}
diff --git a/tools/cpp-define-generator/globals.def b/tools/cpp-define-generator/globals.def
index 2572ea6..459e5a8 100644
--- a/tools/cpp-define-generator/globals.def
+++ b/tools/cpp-define-generator/globals.def
@@ -28,6 +28,7 @@
#include "mirror/object_reference.h"
#include "runtime_globals.h"
#include "stack.h"
+#include "entrypoints/quick/callee_save_frame.h"
#endif
ASM_DEFINE(ACCESS_FLAGS_METHOD_IS_NATIVE,
@@ -82,3 +83,11 @@
std::memory_order_relaxed)
ASM_DEFINE(STACK_OVERFLOW_RESERVED_BYTES,
GetStackOverflowReservedBytes(art::kRuntimeISA))
+ASM_DEFINE(CALLEE_SAVE_EVERYTHING_NUM_CORE_SPILLS,
+ art::POPCOUNT(art::RuntimeCalleeSaveFrame::GetCoreSpills(
+ art::CalleeSaveType::kSaveEverything)))
+ASM_DEFINE(TAGGED_JNI_SP_MASK, art::ManagedStack::kTaggedJniSpMask)
+ASM_DEFINE(TAGGED_JNI_SP_MASK_TOGGLED32,
+ ~static_cast<uint32_t>(art::ManagedStack::kTaggedJniSpMask))
+ASM_DEFINE(TAGGED_JNI_SP_MASK_TOGGLED64,
+ ~static_cast<uint64_t>(art::ManagedStack::kTaggedJniSpMask))
diff --git a/tools/cpp-define-generator/lockword.def b/tools/cpp-define-generator/lockword.def
index a170c15..5494d59 100644
--- a/tools/cpp-define-generator/lockword.def
+++ b/tools/cpp-define-generator/lockword.def
@@ -30,10 +30,8 @@
art::LockWord::kMarkBitStateMaskShifted)
ASM_DEFINE(LOCK_WORD_MARK_BIT_SHIFT,
art::LockWord::kMarkBitStateShift)
-ASM_DEFINE(LOCK_WORD_READ_BARRIER_STATE_MASK,
+ASM_DEFINE(LOCK_WORD_READ_BARRIER_STATE_MASK_SHIFTED,
art::LockWord::kReadBarrierStateMaskShifted)
-ASM_DEFINE(LOCK_WORD_READ_BARRIER_STATE_MASK_TOGGLED,
- art::LockWord::kReadBarrierStateMaskShiftedToggled)
ASM_DEFINE(LOCK_WORD_READ_BARRIER_STATE_SHIFT,
art::LockWord::kReadBarrierStateShift)
ASM_DEFINE(LOCK_WORD_STATE_FORWARDING_ADDRESS,
diff --git a/tools/cpp-define-generator/mirror_class.def b/tools/cpp-define-generator/mirror_class.def
index 8cfd54e..062a7aa 100644
--- a/tools/cpp-define-generator/mirror_class.def
+++ b/tools/cpp-define-generator/mirror_class.def
@@ -16,6 +16,7 @@
#if ASM_DEFINE_INCLUDE_DEPENDENCIES
#include "mirror/class.h"
+#include "subtype_check.h"
#endif
ASM_DEFINE(MIRROR_CLASS_ACCESS_FLAGS_OFFSET,
@@ -49,3 +50,17 @@
ASM_DEFINE(MIRROR_CLASS_IS_INTERFACE_FLAG, art::kAccInterface)
ASM_DEFINE(MIRROR_CLASS_IS_INTERFACE_FLAG_BIT,
art::WhichPowerOf2(art::kAccInterface))
+ASM_DEFINE(MIRROR_CLASS_IS_VISIBLY_INITIALIZED_OFFSET,
+ art::mirror::Class::StatusOffset().SizeValue() +
+ (art::SubtypeCheckBits::BitStructSizeOf() / art::kBitsPerByte))
+ASM_DEFINE(MIRROR_CLASS_IS_VISIBLY_INITIALIZED_VALUE,
+ art::enum_cast<uint32_t>(art::ClassStatus::kVisiblyInitialized) <<
+ (art::SubtypeCheckBits::BitStructSizeOf() % art::kBitsPerByte))
+ASM_DEFINE(MIRROR_CLASS_IS_INITIALIZING_VALUE,
+ art::enum_cast<uint32_t>(art::ClassStatus::kInitializing) <<
+ (art::SubtypeCheckBits::BitStructSizeOf() % art::kBitsPerByte))
+ASM_DEFINE(MIRROR_CLASS_IS_INITIALIZED_VALUE,
+ art::enum_cast<uint32_t>(art::ClassStatus::kInitialized) <<
+ (art::SubtypeCheckBits::BitStructSizeOf() % art::kBitsPerByte))
+ASM_DEFINE(MIRROR_CLASS_CLINIT_THREAD_ID_OFFSET,
+ art::mirror::Class::ClinitThreadIdOffset().Int32Value())
diff --git a/tools/cpp-define-generator/runtime.def b/tools/cpp-define-generator/runtime.def
index 2a2e303..89a3578 100644
--- a/tools/cpp-define-generator/runtime.def
+++ b/tools/cpp-define-generator/runtime.def
@@ -30,3 +30,7 @@
art::Runtime::GetCalleeSaveMethodOffset(art::CalleeSaveType::kSaveRefsAndArgs))
ASM_DEFINE(RUNTIME_SAVE_REFS_ONLY_METHOD_OFFSET,
art::Runtime::GetCalleeSaveMethodOffset(art::CalleeSaveType::kSaveRefsOnly))
+ASM_DEFINE(RUNTIME_INSTRUMENTATION_OFFSET, art::Runtime::GetInstrumentationOffset().Int32Value())
+ASM_DEFINE(INSTRUMENTATION_STUBS_INSTALLED_OFFSET_FROM_RUNTIME_INSTANCE,
+ art::Runtime::GetInstrumentationOffset().Int32Value() +
+ art::instrumentation::Instrumentation::NeedsExitHooksOffset().Int32Value())
diff --git a/tools/cpp-define-generator/thread.def b/tools/cpp-define-generator/thread.def
index bae9200..97033fc 100644
--- a/tools/cpp-define-generator/thread.def
+++ b/tools/cpp-define-generator/thread.def
@@ -37,6 +37,8 @@
(art::WhichPowerOf2(sizeof(art::InterpreterCache::Entry)) - 2))
ASM_DEFINE(THREAD_IS_GC_MARKING_OFFSET,
art::Thread::IsGcMarkingOffset<art::kRuntimePointerSize>().Int32Value())
+ASM_DEFINE(THREAD_DEOPT_CHECK_REQUIRED_OFFSET,
+ art::Thread::DeoptCheckRequiredOffset<art::kRuntimePointerSize>().Int32Value())
ASM_DEFINE(THREAD_LOCAL_ALLOC_STACK_END_OFFSET,
art::Thread::ThreadLocalAllocStackEndOffset<art::kRuntimePointerSize>().Int32Value())
ASM_DEFINE(THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET,
@@ -69,3 +71,5 @@
art::Thread::ReadBarrierMarkEntryPointsOffset<art::kRuntimePointerSize>(0))
ASM_DEFINE(THREAD_SHARED_METHOD_HOTNESS_OFFSET,
art::Thread::SharedMethodHotnessOffset<art::kRuntimePointerSize>().Int32Value())
+ASM_DEFINE(THREAD_TID_OFFSET,
+ art::Thread::TidOffset<art::kRuntimePointerSize>().Int32Value())
diff --git a/tools/dexanalyze/dexanalyze_test.cc b/tools/dexanalyze/dexanalyze_test.cc
index 9e6ed6d..474615d 100644
--- a/tools/dexanalyze/dexanalyze_test.cc
+++ b/tools/dexanalyze/dexanalyze_test.cc
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-#include "common_runtime_test.h"
+#include "base/common_art_test.h"
#include "exec_utils.h"
namespace art {
-class DexAnalyzeTest : public CommonRuntimeTest {
+class DexAnalyzeTest : public CommonArtTest {
public:
std::string GetDexAnalyzePath() {
return GetArtBinDir() + "/dexanalyze";
diff --git a/tools/dexfuzz/.clang-format b/tools/dexfuzz/.clang-format
new file mode 120000
index 0000000..88ab38e
--- /dev/null
+++ b/tools/dexfuzz/.clang-format
@@ -0,0 +1 @@
+../../.clang-format-java-2
\ No newline at end of file
diff --git a/tools/dexfuzz/Android.bp b/tools/dexfuzz/Android.bp
index 02bda0e..083ecd7 100644
--- a/tools/dexfuzz/Android.bp
+++ b/tools/dexfuzz/Android.bp
@@ -33,6 +33,6 @@
// --- dexfuzz script ----------------
sh_binary_host {
name: "dexfuzz-script",
- src: "dexfuzz",
- filename_from_src: true,
+ src: "dexfuzz.sh",
+ filename: "dexfuzz",
}
diff --git a/tools/dexfuzz/dexfuzz b/tools/dexfuzz/dexfuzz.sh
similarity index 100%
rename from tools/dexfuzz/dexfuzz
rename to tools/dexfuzz/dexfuzz.sh
diff --git a/tools/dist_linux_bionic.sh b/tools/dist_linux_bionic.sh
index 4c7ba1c..f710310 100755
--- a/tools/dist_linux_bionic.sh
+++ b/tools/dist_linux_bionic.sh
@@ -19,12 +19,6 @@
# Builds the given targets using linux-bionic and moves the output files to the
# DIST_DIR. Takes normal make arguments.
-if [[ -z $ANDROID_BUILD_TOP ]]; then
- pushd .
-else
- pushd $ANDROID_BUILD_TOP
-fi
-
if [[ -z $DIST_DIR ]]; then
echo "DIST_DIR must be set!"
exit 1
@@ -35,10 +29,12 @@
exit 1
fi
-source build/envsetup.sh >&/dev/null # for get_build_var
-out_dir=$(get_build_var OUT_DIR)
+vars="$(build/soong/soong_ui.bash --dumpvars-mode --vars="OUT_DIR")"
+# Assign to a variable and eval that, since bash ignores any error status from
+# the command substitution if it's directly on the eval line.
+eval $vars
-./art/tools/build_linux_bionic.sh $@
+./art/tools/build_linux_bionic.sh "$@"
mkdir -p $DIST_DIR
-cp -R ${out_dir}/soong/host/* $DIST_DIR/
+cp -R ${OUT_DIR}/soong/host/* $DIST_DIR/
diff --git a/tools/dmtracedump/createtesttrace.cc b/tools/dmtracedump/createtesttrace.cc
index 444cce4..d55b3e4 100644
--- a/tools/dmtracedump/createtesttrace.cc
+++ b/tools/dmtracedump/createtesttrace.cc
@@ -22,6 +22,7 @@
#include <assert.h>
#include <ctype.h>
#include <errno.h>
+#include <memory>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -143,7 +144,7 @@
/* Add space for a sentinel record at the end */
numRecords += 1;
records = new dataRecord[numRecords];
- stack* callStack = new stack[numThreads];
+ std::unique_ptr<stack[]> callStack(new stack[numThreads]);
for (int32_t ii = 0; ii < numThreads; ++ii) {
callStack[ii].frames = nullptr;
callStack[ii].indentLevel = 0;
diff --git a/tools/dmtracedump/tracedump.cc b/tools/dmtracedump/tracedump.cc
index 3cb7374..ecae6c1 100644
--- a/tools/dmtracedump/tracedump.cc
+++ b/tools/dmtracedump/tracedump.cc
@@ -1490,10 +1490,8 @@
char classBuf[HTML_BUFSIZE], methodBuf[HTML_BUFSIZE];
char signatureBuf[HTML_BUFSIZE];
char anchor_buf[80];
- const char* anchor_close = "";
anchor_buf[0] = 0;
if (gOptions.outputHtml) {
- anchor_close = "</a>";
printf("<a name=\"inclusive\"></a>\n");
printf("<hr>\n");
outputNavigationBar();
diff --git a/tools/external_oj_libjdwp_art_no_read_barrier_failures.txt b/tools/external_oj_libjdwp_art_no_read_barrier_failures.txt
new file mode 100644
index 0000000..920b611
--- /dev/null
+++ b/tools/external_oj_libjdwp_art_no_read_barrier_failures.txt
@@ -0,0 +1,9 @@
+/*
+ * This file contains expectations for ART's buildbot. The purpose of this file is
+ * to temporarily list failing tests and not break the bots.
+ *
+ * This file contains the expectations for the 'libjdwp-aot' and 'libjdwp-jit'
+ * test groups on the chromium buildbot running without read-barrier.
+ */
+[
+]
diff --git a/tools/generate_cmake_lists.py b/tools/generate_cmake_lists.py
index b19c292..3fda003 100755
--- a/tools/generate_cmake_lists.py
+++ b/tools/generate_cmake_lists.py
@@ -32,6 +32,7 @@
(Also, exclude projects that you don't bother about. This will make
the indexing faster).
"""
+from __future__ import print_function
import sys
import os
@@ -47,7 +48,7 @@
path_to_top = os.path.realpath(path_to_top)
if not os.path.exists(os.path.join(path_to_top, 'build/envsetup.sh')):
- print path_to_top
+ print(path_to_top)
raise AssertionError("geneate_cmake_lists.py must be located inside an android source tree")
return path_to_top
diff --git a/tools/generate_operator_out.py b/tools/generate_operator_out.py
index f1491d8..f3de61c 100755
--- a/tools/generate_operator_out.py
+++ b/tools/generate_operator_out.py
@@ -65,7 +65,7 @@
continue
# Is this the start or end of a namespace?
- m = re.search(r'^namespace (\S+) \{', raw_line)
+ m = re.search(r'^namespace (\S+) (HIDDEN |EXPORT )?\{', raw_line)
if m:
namespaces.append(m.group(1))
continue
diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc
index aee3f9a..644ec72 100644
--- a/tools/hiddenapi/hiddenapi.cc
+++ b/tools/hiddenapi/hiddenapi.cc
@@ -1057,6 +1057,7 @@
std::map<std::string, ApiList> api_flag_map;
size_t line_number = 1;
+ bool errors = false;
for (std::string line; std::getline(api_file, line); line_number++) {
// Every line contains a comma separated list with the signature as the
// first element and the api flags as the rest
@@ -1074,13 +1075,21 @@
std::vector<std::string>::iterator apiListBegin = values.begin() + 1;
std::vector<std::string>::iterator apiListEnd = values.end();
bool success = ApiList::FromNames(apiListBegin, apiListEnd, &membership);
- CHECK(success) << path << ":" << line_number
- << ": Some flags were not recognized: " << line << kErrorHelp;
- CHECK(membership.IsValid()) << path << ":" << line_number
- << ": Invalid combination of flags: " << line << kErrorHelp;
+ if (!success) {
+ LOG(ERROR) << path << ":" << line_number
+ << ": Some flags were not recognized: " << line << kErrorHelp;
+ errors = true;
+ continue;
+ } else if (!membership.IsValid()) {
+ LOG(ERROR) << path << ":" << line_number
+ << ": Invalid combination of flags: " << line << kErrorHelp;
+ errors = true;
+ continue;
+ }
api_flag_map.emplace(signature, membership);
}
+ CHECK(!errors) << "Errors encountered while parsing file " << path;
api_file.close();
return api_flag_map;
diff --git a/tools/hiddenapi/hiddenapi_test.cc b/tools/hiddenapi/hiddenapi_test.cc
index 3a0e625..f408c66 100644
--- a/tools/hiddenapi/hiddenapi_test.cc
+++ b/tools/hiddenapi/hiddenapi_test.cc
@@ -143,7 +143,7 @@
std::map<std::string, std::string> flags;
for (std::string line; std::getline(ifs, line);) {
- std::size_t comma = line.find(",");
+ std::size_t comma = line.find(',');
if (comma == std::string::npos) {
flags.emplace(line, "");
} else {
diff --git a/tools/jvmti-agents/chain-agents/chainagents.cc b/tools/jvmti-agents/chain-agents/chainagents.cc
index 1242409..d272fc1 100644
--- a/tools/jvmti-agents/chain-agents/chainagents.cc
+++ b/tools/jvmti-agents/chain-agents/chainagents.cc
@@ -53,7 +53,7 @@
OnLoad,
};
-static std::pair<std::string, std::string> Split(std::string source, char delim) {
+static std::pair<std::string, std::string> Split(const std::string& source, char delim) {
std::string first(source.substr(0, source.find(delim)));
if (source.find(delim) == std::string::npos) {
return std::pair(first, "");
diff --git a/tools/jvmti-agents/field-counts/fieldcount.cc b/tools/jvmti-agents/field-counts/fieldcount.cc
index c31a973..5a4b00e 100644
--- a/tools/jvmti-agents/field-counts/fieldcount.cc
+++ b/tools/jvmti-agents/field-counts/fieldcount.cc
@@ -182,7 +182,7 @@
<< "\t" << "<ALL_TYPES>"
<< "\t" << obj_len
<< "\t" << total_size;
- for (auto sz : class_sizes) {
+ for (const std::pair<std::string, size_t> sz : class_sizes) {
size_t count = class_counts[sz.first];
LOG(INFO) << "\t" << field_class_name << "." << field_name << ":" << field_sig
<< "\t" << sz.first
diff --git a/tools/jvmti-agents/simple-force-redefine/forceredefine.cc b/tools/jvmti-agents/simple-force-redefine/forceredefine.cc
index 055fb8a..3474238 100644
--- a/tools/jvmti-agents/simple-force-redefine/forceredefine.cc
+++ b/tools/jvmti-agents/simple-force-redefine/forceredefine.cc
@@ -94,7 +94,7 @@
jvmtiEnv* jvmti_;
};
-static void Transform(std::shared_ptr<ir::DexFile> ir) {
+static void Transform(const std::shared_ptr<ir::DexFile>& ir) {
std::unique_ptr<ir::Builder> builder;
for (auto& method : ir->encoded_methods) {
// Do not look into abstract/bridge/native/synthetic methods.
diff --git a/tools/jvmti-agents/simple-profile/simple_profile.cc b/tools/jvmti-agents/simple-profile/simple_profile.cc
index 5ead97e..7161142 100644
--- a/tools/jvmti-agents/simple-profile/simple_profile.cc
+++ b/tools/jvmti-agents/simple-profile/simple_profile.cc
@@ -26,6 +26,7 @@
#include <sstream>
#include <string>
#include <unordered_map>
+#include <utility>
#include <vector>
#include "android-base/unique_fd.h"
@@ -55,7 +56,7 @@
SimpleProfileData(
jvmtiEnv* env, std::string out_fd_name, int fd, bool dump_on_shutdown, bool dump_on_main_stop)
: dump_id_(0),
- out_fd_name_(out_fd_name),
+ out_fd_name_(std::move(out_fd_name)),
out_fd_(fd),
shutdown_(false),
dump_on_shutdown_(dump_on_shutdown || dump_on_main_stop),
@@ -79,7 +80,7 @@
void Shutdown(jvmtiEnv* jvmti, JNIEnv* jni);
private:
- void DoDump(jvmtiEnv* jvmti, JNIEnv* jni, std::unordered_map<jmethodID, uint64_t> copy);
+ void DoDump(jvmtiEnv* jvmti, JNIEnv* jni, const std::unordered_map<jmethodID, uint64_t>& copy);
jlong dump_id_;
jrawMonitorID mon_;
@@ -320,7 +321,7 @@
void SimpleProfileData::DoDump(jvmtiEnv* jvmti,
JNIEnv* jni,
- std::unordered_map<jmethodID, uint64_t> copy) {
+ const std::unordered_map<jmethodID, uint64_t>& copy) {
std::ostringstream oss;
oss << "[";
bool is_first = true;
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index 6e6ccb8..b5c220e 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -330,5 +330,11 @@
bug: 228441328,
names: ["tck.java.time",
"test.java.time"]
+},
+{
+ description: "Timing out after ojluni tests were enabled",
+ result: ERROR,
+ bug: 231439593,
+ names: ["org.apache.harmony.tests.java.math.BigIntegerConstructorsTest#testConstructorPrime"]
}
]
diff --git a/tools/libcore_fugu_failures.txt b/tools/libcore_fugu_failures.txt
index 0fff814..60b43d0 100644
--- a/tools/libcore_fugu_failures.txt
+++ b/tools/libcore_fugu_failures.txt
@@ -25,6 +25,8 @@
names: [
"libcore.java.math.BigIntegerTest#test_Constructor_IILjava_util_Random",
"libcore.java.math.BigIntegerTest#test_probablePrime",
+ "libcore.java.util.UUIDTest#testJava11Implementation_invalidInputs",
+ "libcore.java.util.UUIDTest#testJava8Implementation_allowsLongInputs",
"libcore.javax.crypto.CipherInputStreamTest#testDecryptCorruptGCM",
"libcore.javax.crypto.CipherOutputStreamTest#testDecryptCorruptGCM",
"libcore.libcore.timezone.TelephonyLookupTest#createInstanceWithFallback",
@@ -112,7 +114,6 @@
"org.apache.harmony.crypto.tests.javax.crypto.func.CipherRSATest#test_RSANoPadding",
"org.apache.harmony.crypto.tests.javax.crypto.func.CipherRSATest#test_RSAShortKey",
"org.apache.harmony.crypto.tests.javax.crypto.func.KeyGeneratorFunctionalTest#test_",
- "org.apache.harmony.tests.java.math.BigIntegerConstructorsTest#testConstructorPrime",
"org.apache.harmony.tests.java.math.BigIntegerTest#test_isProbablePrimeI",
"org.apache.harmony.tests.java.math.OldBigIntegerTest#test_ConstructorIILjava_util_Random",
"org.apache.harmony.tests.java.math.OldBigIntegerTest#test_isProbablePrimeI",
@@ -127,5 +128,109 @@
"org.apache.harmony.tests.javax.security.OldSHA1PRNGSecureRandomTest#testNextBytesbyteArray03",
"org.apache.harmony.tests.javax.security.OldSHA1PRNGSecureRandomTest#testSetSeedbyteArray02"
]
+},
+{
+ description: "Test using the getrandom() syscall, only available from Linux 3.17.",
+ result: ERROR,
+ bug: 141230711,
+ modes: [device],
+ names: [
+ "test.java.awt",
+ "test.java.io.ByteArrayInputStream",
+ "test.java.io.ByteArrayOutputStream",
+ "test.java.io.FileReader",
+ "test.java.io.FileWriter",
+ "test.java.io.InputStream",
+ "test.java.io.OutputStream",
+ "test.java.io.PrintStream",
+ "test.java.io.PrintWriter",
+ "test.java.io.Reader",
+ "test.java.io.Writer",
+ "test.java.lang.Boolean",
+ "test.java.lang.ClassLoader",
+ "test.java.lang.Double",
+ "test.java.lang.Float",
+ "test.java.lang.Integer",
+ "test.java.lang.Long",
+ "test.java.lang.StackWalker#main",
+ "test.java.lang.StrictMath.CubeRootTests",
+ "test.java.lang.StrictMath.ExactArithTests",
+ "test.java.lang.StrictMath.Expm1Tests",
+ "test.java.lang.StrictMath.ExpTests",
+ "test.java.lang.StrictMath.HyperbolicTests",
+ "test.java.lang.StrictMath.HypotTests#testAgainstTranslit_shard1",
+ "test.java.lang.StrictMath.HypotTests#testAgainstTranslit_shard2",
+ "test.java.lang.StrictMath.HypotTests#testAgainstTranslit_shard3",
+ "test.java.lang.StrictMath.HypotTests#testAgainstTranslit_shard4",
+ "test.java.lang.StrictMath.HypotTests#testHypot",
+ "test.java.lang.StrictMath.Log1pTests",
+ "test.java.lang.StrictMath.Log10Tests",
+ "test.java.lang.StrictMath.MultiplicationTests",
+ "test.java.lang.StrictMath.PowTests",
+ "test.java.lang.String",
+ "test.java.lang.Thread",
+ "test.java.lang.invoke",
+ "test.java.lang.ref.SoftReference",
+ "test.java.lang.ref.BasicTest",
+ "test.java.lang.ref.EnqueueNullRefTest",
+ "test.java.lang.ref.EnqueuePollRaceTest",
+ "test.java.lang.ref.ReferenceCloneTest",
+ "test.java.lang.ref.ReferenceEnqueuePendingTest",
+ "test.java.math.BigDecimal",
+ "test.java.math.BigInteger#testArithmetic",
+ "test.java.math.BigInteger#testBitCount",
+ "test.java.math.BigInteger#testBitLength",
+ "test.java.math.BigInteger#testbitOps",
+ "test.java.math.BigInteger#testBitwise",
+ "test.java.math.BigInteger#testByteArrayConv",
+ "test.java.math.BigInteger#testConstructor",
+ "test.java.math.BigInteger#testDivideAndReminder",
+ "test.java.math.BigInteger#testDivideLarge",
+ "test.java.math.BigInteger#testModExp",
+ "test.java.math.BigInteger#testMultiplyLarge",
+ "test.java.math.BigInteger#testNextProbablePrime",
+ "test.java.math.BigInteger#testPow",
+ "test.java.math.BigInteger#testSerialize",
+ "test.java.math.BigInteger#testShift",
+ "test.java.math.BigInteger#testSquare",
+ "test.java.math.BigInteger#testSquareLarge",
+ "test.java.math.BigInteger#testSquareRootAndReminder",
+ "test.java.math.BigInteger#testStringConv_generic",
+ "test.java.math.RoundingMode",
+ "test.java.net.DatagramSocket",
+ "test.java.net.Socket",
+ "test.java.net.SocketOptions",
+ "test.java.net.URLDecoder",
+ "test.java.net.URLEncoder",
+ "test.java.nio.channels.Channels",
+ "test.java.nio.channels.SelectionKey",
+ "test.java.nio.channels.Selector",
+ "test.java.nio.file",
+ "test.java.security.cert",
+ "test.java.security.KeyAgreement.KeyAgreementTest",
+ "test.java.security.KeyAgreement.KeySizeTest#testECDHKeySize",
+ "test.java.security.KeyAgreement.KeySpecTest",
+ "test.java.security.KeyAgreement.MultiThreadTest",
+ "test.java.security.KeyAgreement.NegativeTest",
+ "test.java.security.KeyStore",
+ "test.java.security.Provider",
+ "test.java.util.Arrays",
+ "test.java.util.Collection",
+ "test.java.util.Collections",
+ "test.java.util.Date",
+ "test.java.util.EnumMap",
+ "test.java.util.EnumSet",
+ "test.java.util.GregorianCalendar",
+ "test.java.util.LinkedHashMap",
+ "test.java.util.LinkedHashSet",
+ "test.java.util.List",
+ "test.java.util.Map",
+ "test.java.util.Optional",
+ "test.java.util.TimeZone",
+ "test.java.util.concurrent",
+ "test.java.util.function",
+ "test.java.util.stream",
+ "test.java.util.zip.ZipFile"
+ ]
}
]
diff --git a/tools/libcore_gcstress_debug_failures.txt b/tools/libcore_gcstress_debug_failures.txt
index 2193189..c931610 100644
--- a/tools/libcore_gcstress_debug_failures.txt
+++ b/tools/libcore_gcstress_debug_failures.txt
@@ -50,7 +50,6 @@
"org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnectionTest#testConsequentProxyConnection",
"org.apache.harmony.tests.java.lang.ref.ReferenceQueueTest#test_removeJ",
"org.apache.harmony.tests.java.lang.ProcessManagerTest#testSleep",
- "org.apache.harmony.tests.java.math.BigIntegerConstructorsTest#testConstructorPrime",
"org.apache.harmony.tests.java.util.TimerTest#testOverdueTaskExecutesImmediately",
"org.apache.harmony.tests.java.util.WeakHashMapTest#test_keySet_hasNext"
]
@@ -71,5 +70,11 @@
names: ["jsr166.CompletableFutureTest#testCompleteOnTimeout_completed",
"jsr166.CompletableFutureTest#testDelayedExecutor"
]
+},
+{
+ description: "SocketTimeout test gcstress and debug.",
+ result: EXEC_FAILED,
+ bug: 259530489,
+ names: ["org.apache.harmony.luni.tests.java.net.URLConnectionTest#test_setReadTimeoutI_SocketTimeoutException"]
}
]
diff --git a/tools/libcore_gcstress_failures.txt b/tools/libcore_gcstress_failures.txt
index 55bba72..81d6ca0 100644
--- a/tools/libcore_gcstress_failures.txt
+++ b/tools/libcore_gcstress_failures.txt
@@ -36,7 +36,6 @@
"libcore.java.util.stream.CollectorsTest#counting_largeStream",
"org.apache.harmony.tests.java.lang.ref.ReferenceQueueTest#test_remove",
"org.apache.harmony.tests.java.lang.String2Test#test_getBytes",
- "org.apache.harmony.tests.java.math.BigIntegerConstructorsTest#testConstructorPrime",
"org.apache.harmony.tests.java.text.DateFormatTest#test_getAvailableLocales",
"org.apache.harmony.tests.java.util.TimerTest#testOverdueTaskExecutesImmediately",
"org.apache.harmony.tests.java.util.WeakHashMapTest#test_keySet_hasNext"]
diff --git a/tools/luci/config/generated/cr-buildbucket.cfg b/tools/luci/config/generated/cr-buildbucket.cfg
index e4a5923..1da398c 100644
--- a/tools/luci/config/generated/cr-buildbucket.cfg
+++ b/tools/luci/config/generated/cr-buildbucket.cfg
@@ -17,7 +17,8 @@
builders {
name: "angler-armv7-debug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:angler-armv7-debug"
+ dimensions: "device_type:bonito|oriole|walleye"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -35,13 +36,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "angler-armv7-ndebug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:angler-armv7-ndebug"
+ dimensions: "device_type:bonito|oriole|walleye"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -59,13 +61,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "angler-armv7-non-gen-cc"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:angler-armv7-non-gen-cc"
+ dimensions: "device_type:oriole"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -83,13 +86,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "angler-armv8-debug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:angler-armv8-debug"
+ dimensions: "device_type:bonito|oriole|walleye"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -107,13 +111,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "angler-armv8-ndebug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:angler-armv8-ndebug"
+ dimensions: "device_type:bonito|oriole|walleye"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -131,13 +136,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "angler-armv8-non-gen-cc"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:angler-armv8-non-gen-cc"
+ dimensions: "device_type:oriole"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -155,13 +161,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "bullhead-armv7-gcstress-ndebug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:bullhead-armv7-gcstress-ndebug"
+ dimensions: "device_type:bonito|oriole|walleye"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -179,13 +186,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "bullhead-armv8-gcstress-debug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:bullhead-armv8-gcstress-debug"
+ dimensions: "device_type:bonito|oriole|walleye"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -203,13 +211,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "bullhead-armv8-gcstress-ndebug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:bullhead-armv8-gcstress-ndebug"
+ dimensions: "device_type:bonito|oriole|walleye"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -227,13 +236,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "fugu-debug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:fugu-debug"
+ dimensions: "device_type:fugu"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -251,13 +261,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "fugu-ndebug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:fugu-ndebug"
+ dimensions: "device_type:fugu"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -275,13 +286,13 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "host-x86-cms"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:host-x86-cms"
+ dimensions: "os:Linux"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -299,13 +310,13 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "host-x86-debug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:host-x86-debug"
+ dimensions: "os:Linux"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -323,13 +334,13 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "host-x86-gcstress-debug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:host-x86-gcstress-debug"
+ dimensions: "os:Linux"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -347,13 +358,13 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "host-x86-ndebug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:host-x86-ndebug"
+ dimensions: "os:Linux"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -371,13 +382,13 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "host-x86-poison-debug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:host-x86-poison-debug"
+ dimensions: "os:Linux"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -395,13 +406,13 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "host-x86_64-cdex-fast"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:host-x86_64-cdex-fast"
+ dimensions: "os:Linux"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -419,13 +430,13 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "host-x86_64-cms"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:host-x86_64-cms"
+ dimensions: "os:Linux"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -443,13 +454,13 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "host-x86_64-debug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:host-x86_64-debug"
+ dimensions: "os:Linux"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -467,13 +478,13 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "host-x86_64-ndebug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:host-x86_64-ndebug"
+ dimensions: "os:Linux"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -491,13 +502,13 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "host-x86_64-non-gen-cc"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:host-x86_64-non-gen-cc"
+ dimensions: "os:Linux"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -515,13 +526,13 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "host-x86_64-poison-debug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:host-x86_64-poison-debug"
+ dimensions: "os:Linux"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -539,13 +550,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "walleye-armv7-poison-debug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:walleye-armv7-poison-debug"
+ dimensions: "device_type:bonito|oriole|walleye"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -563,13 +575,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "walleye-armv8-poison-debug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:walleye-armv8-poison-debug"
+ dimensions: "device_type:bonito|oriole|walleye"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -587,13 +600,14 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
builders {
name: "walleye-armv8-poison-ndebug"
swarming_host: "chromium-swarm.appspot.com"
- dimensions: "builder:walleye-armv8-poison-ndebug"
+ dimensions: "device_type:bonito|oriole|walleye"
+ dimensions: "os:Android"
dimensions: "pool:luci.art.ci"
recipe {
name: "art"
@@ -611,7 +625,7 @@
service_account: "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com"
experiments {
key: "luci.recipes.use_python3"
- value: 10
+ value: 100
}
}
}
diff --git a/tools/luci/config/generated/luci-scheduler.cfg b/tools/luci/config/generated/luci-scheduler.cfg
index d6c6ab5..ba7bcee 100644
--- a/tools/luci/config/generated/luci-scheduler.cfg
+++ b/tools/luci/config/generated/luci-scheduler.cfg
@@ -356,6 +356,40 @@
refs: "regexp:refs/heads/master-art"
}
}
+trigger {
+ id: "vogar"
+ realm: "ci"
+ acl_sets: "ci"
+ triggers: "angler-armv7-debug"
+ triggers: "angler-armv7-ndebug"
+ triggers: "angler-armv7-non-gen-cc"
+ triggers: "angler-armv8-debug"
+ triggers: "angler-armv8-ndebug"
+ triggers: "angler-armv8-non-gen-cc"
+ triggers: "bullhead-armv7-gcstress-ndebug"
+ triggers: "bullhead-armv8-gcstress-debug"
+ triggers: "bullhead-armv8-gcstress-ndebug"
+ triggers: "fugu-debug"
+ triggers: "fugu-ndebug"
+ triggers: "host-x86-cms"
+ triggers: "host-x86-debug"
+ triggers: "host-x86-gcstress-debug"
+ triggers: "host-x86-ndebug"
+ triggers: "host-x86-poison-debug"
+ triggers: "host-x86_64-cdex-fast"
+ triggers: "host-x86_64-cms"
+ triggers: "host-x86_64-debug"
+ triggers: "host-x86_64-ndebug"
+ triggers: "host-x86_64-non-gen-cc"
+ triggers: "host-x86_64-poison-debug"
+ triggers: "walleye-armv7-poison-debug"
+ triggers: "walleye-armv8-poison-debug"
+ triggers: "walleye-armv8-poison-ndebug"
+ gitiles {
+ repo: "https://android.googlesource.com/platform/external/vogar"
+ refs: "regexp:refs/heads/master"
+ }
+}
acl_sets {
name: "ci"
acls {
diff --git a/tools/luci/config/generated/project.cfg b/tools/luci/config/generated/project.cfg
index 4844ab4..f9f7877 100644
--- a/tools/luci/config/generated/project.cfg
+++ b/tools/luci/config/generated/project.cfg
@@ -7,7 +7,7 @@
name: "art"
access: "group:all"
lucicfg {
- version: "1.30.11"
+ version: "1.33.7"
package_dir: ".."
config_dir: "generated"
entry_point: "main.star"
diff --git a/tools/luci/config/main.star b/tools/luci/config/main.star
index f4ad488..b1ecde1 100755
--- a/tools/luci/config/main.star
+++ b/tools/luci/config/main.star
@@ -23,7 +23,7 @@
lucicfg.check_version("1.30.9", "Please update depot_tools")
luci.builder.defaults.experiments.set({
- "luci.recipes.use_python3": 10,
+ "luci.recipes.use_python3": 100,
})
# Use LUCI Scheduler BBv2 names and add Scheduler realms configs.
@@ -141,13 +141,20 @@
)
luci.gitiles_poller(
+ name = "vogar",
+ bucket = "ci",
+ repo = "https://android.googlesource.com/platform/external/vogar",
+ refs = ["refs/heads/master"],
+)
+
+luci.gitiles_poller(
name = "manifest",
bucket = "ci",
repo = "https://android.googlesource.com/platform/manifest",
refs = ["refs/heads/master-art"],
)
-def ci_builder(name, category, short_name):
+def ci_builder(name, category, short_name, dimensions):
luci.builder(
name = name,
bucket = "ci",
@@ -156,11 +163,8 @@
cipd_version = "refs/heads/main",
name = "art",
),
- dimensions = {
+ dimensions = dimensions | {
"pool": "luci.art.ci",
-
- # Some builders require specific hardware, so we make the assignment in bots.cfg
- "builder": name,
},
service_account = "art-ci-builder@chops-service-accounts.iam.gserviceaccount.com",
@@ -185,6 +189,7 @@
"art",
"libcore",
"manifest",
+ "vogar",
],
)
luci.console_view_entry(
@@ -194,28 +199,37 @@
short_name = short_name,
)
-ci_builder("angler-armv7-debug", "angler|armv7", "dbg")
-ci_builder("angler-armv7-non-gen-cc", "angler|armv7", "ngen")
-ci_builder("angler-armv7-ndebug", "angler|armv7", "ndbg")
-ci_builder("angler-armv8-debug", "angler|armv8", "dbg")
-ci_builder("angler-armv8-non-gen-cc", "angler|armv8", "ngen")
-ci_builder("angler-armv8-ndebug", "angler|armv8", "ndbg")
-ci_builder("bullhead-armv7-gcstress-ndebug", "bullhead|armv7|gcstress", "dbg")
-ci_builder("bullhead-armv8-gcstress-debug", "bullhead|armv8|gcstress", "dbg")
-ci_builder("bullhead-armv8-gcstress-ndebug", "bullhead|armv8|gcstress", "ndbg")
-ci_builder("fugu-debug", "fugu", "dbg")
-ci_builder("fugu-ndebug", "fugu", "ndbg")
-ci_builder("host-x86-cms", "host|x86", "cms")
-ci_builder("host-x86-debug", "host|x86", "dbg")
-ci_builder("host-x86-ndebug", "host|x86", "ndbg")
-ci_builder("host-x86-gcstress-debug", "host|x86", "gcs")
-ci_builder("host-x86-poison-debug", "host|x86", "psn")
-ci_builder("host-x86_64-cdex-fast", "host|x64", "cdx")
-ci_builder("host-x86_64-cms", "host|x64", "cms")
-ci_builder("host-x86_64-debug", "host|x64", "dbg")
-ci_builder("host-x86_64-non-gen-cc", "host|x64", "ngen")
-ci_builder("host-x86_64-ndebug", "host|x64", "ndbg")
-ci_builder("host-x86_64-poison-debug", "host|x64", "psn")
-ci_builder("walleye-armv7-poison-debug", "walleye|armv7|poison", "dbg")
-ci_builder("walleye-armv8-poison-debug", "walleye|armv8|poison", "dbg")
-ci_builder("walleye-armv8-poison-ndebug", "walleye|armv8|poison", "ndbg")
+# Dimensions specify which bots we can run on.
+host_dims = {"os": "Linux"}
+target_dims = {"os": "Android"}
+arm_target_dims = target_dims | {"device_type": "bonito|oriole|walleye"}
+x86_target_dims = target_dims | {"device_type": "fugu"}
+
+# userfault-GC configurations must be run on Pixel 6.
+userfault_gc_target_dims = target_dims | {"device_type": "oriole"}
+
+ci_builder("angler-armv7-debug", "angler|armv7", "dbg", arm_target_dims)
+ci_builder("angler-armv7-non-gen-cc", "angler|armv7", "ngen", userfault_gc_target_dims)
+ci_builder("angler-armv7-ndebug", "angler|armv7", "ndbg", arm_target_dims)
+ci_builder("angler-armv8-debug", "angler|armv8", "dbg", arm_target_dims)
+ci_builder("angler-armv8-non-gen-cc", "angler|armv8", "ngen", userfault_gc_target_dims)
+ci_builder("angler-armv8-ndebug", "angler|armv8", "ndbg", arm_target_dims)
+ci_builder("bullhead-armv7-gcstress-ndebug", "bullhead|armv7|gcstress", "dbg", arm_target_dims)
+ci_builder("bullhead-armv8-gcstress-debug", "bullhead|armv8|gcstress", "dbg", arm_target_dims)
+ci_builder("bullhead-armv8-gcstress-ndebug", "bullhead|armv8|gcstress", "ndbg", arm_target_dims)
+ci_builder("fugu-debug", "fugu", "dbg", x86_target_dims)
+ci_builder("fugu-ndebug", "fugu", "ndbg", x86_target_dims)
+ci_builder("host-x86-cms", "host|x86", "cms", host_dims)
+ci_builder("host-x86-debug", "host|x86", "dbg", host_dims)
+ci_builder("host-x86-ndebug", "host|x86", "ndbg", host_dims)
+ci_builder("host-x86-gcstress-debug", "host|x86", "gcs", host_dims)
+ci_builder("host-x86-poison-debug", "host|x86", "psn", host_dims)
+ci_builder("host-x86_64-cdex-fast", "host|x64", "cdx", host_dims)
+ci_builder("host-x86_64-cms", "host|x64", "cms", host_dims)
+ci_builder("host-x86_64-debug", "host|x64", "dbg", host_dims)
+ci_builder("host-x86_64-non-gen-cc", "host|x64", "ngen", host_dims)
+ci_builder("host-x86_64-ndebug", "host|x64", "ndbg", host_dims)
+ci_builder("host-x86_64-poison-debug", "host|x64", "psn", host_dims)
+ci_builder("walleye-armv7-poison-debug", "walleye|armv7|poison", "dbg", arm_target_dims)
+ci_builder("walleye-armv8-poison-debug", "walleye|armv8|poison", "dbg", arm_target_dims)
+ci_builder("walleye-armv8-poison-ndebug", "walleye|armv8|poison", "ndbg", arm_target_dims)
diff --git a/tools/prebuilt_libjdwp_art_failures.txt b/tools/prebuilt_libjdwp_art_failures.txt
index ee59315..4ded7d5 100644
--- a/tools/prebuilt_libjdwp_art_failures.txt
+++ b/tools/prebuilt_libjdwp_art_failures.txt
@@ -106,5 +106,5 @@
result: EXEC_FAILED,
bug: 69169846,
name: "org.apache.harmony.jpda.tests.jdwp.DDM_DDMTest#testChunk001"
-},
+}
]
diff --git a/tools/public.libraries.buildbot.txt b/tools/public.libraries.buildbot.txt
index e23cf2c..9b0dc68 100644
--- a/tools/public.libraries.buildbot.txt
+++ b/tools/public.libraries.buildbot.txt
@@ -1,6 +1,6 @@
-libbacktrace.so
libc.so
libc++.so
libdl.so
libm.so
libnativehelper.so
+libunwindstack.so
diff --git a/tools/run-libcore-tests.py b/tools/run-libcore-tests.py
index 1e6070a..c6958f1 100755
--- a/tools/run-libcore-tests.py
+++ b/tools/run-libcore-tests.py
@@ -42,6 +42,7 @@
help='Enable GC stress configuration (device|host only).')
parser.add_argument('tests', nargs="*",
help='Name(s) of the test(s) to run')
+ parser.add_argument('--verbose', action='store_true', help='Print verbose output from vogar.')
return parser.parse_args()
ART_TEST_ANDROID_ROOT = os.environ.get("ART_TEST_ANDROID_ROOT", "/system")
@@ -91,7 +92,10 @@
"libcore.sun.util",
"libcore.xml",
"org.apache.harmony.annotation",
- "org.apache.harmony.luni",
+ "org.apache.harmony.luni.tests.internal.net.www.protocol.http.HttpURLConnection",
+ "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnection",
+ "org.apache.harmony.luni.tests.java.io",
+ "org.apache.harmony.luni.tests.java.net",
"org.apache.harmony.nio",
"org.apache.harmony.regex",
"org.apache.harmony.testframework",
@@ -122,7 +126,8 @@
"test.java.lang.Long",
# Sharded test.java.lang.StrictMath
"test.java.lang.StrictMath.CubeRootTests",
- "test.java.lang.StrictMath.ExactArithTests",
+ # TODO: disable the test until b/248208762 is fixed.
+ # "test.java.lang.StrictMath.ExactArithTests",
"test.java.lang.StrictMath.Expm1Tests",
"test.java.lang.StrictMath.ExpTests",
"test.java.lang.StrictMath.HyperbolicTests",
@@ -159,21 +164,15 @@
"test.java.math.BigInteger#testDivideAndReminder",
"test.java.math.BigInteger#testDivideLarge",
"test.java.math.BigInteger#testModExp",
- "test.java.math.BigInteger#testModInv",
"test.java.math.BigInteger#testMultiplyLarge",
"test.java.math.BigInteger#testNextProbablePrime",
"test.java.math.BigInteger#testPow",
- "test.java.math.BigInteger#testPrime",
"test.java.math.BigInteger#testSerialize",
"test.java.math.BigInteger#testShift",
"test.java.math.BigInteger#testSquare",
"test.java.math.BigInteger#testSquareLarge",
- "test.java.math.BigInteger#testSquareRoot",
"test.java.math.BigInteger#testSquareRootAndReminder",
"test.java.math.BigInteger#testStringConv_generic",
- "test.java.math.BigInteger#testStringConv_schoenhage_threshold_pow0",
- "test.java.math.BigInteger#testStringConv_schoenhage_threshold_pow1",
- "test.java.math.BigInteger#testStringConv_schoenhage_threshold_pow2",
"test.java.math.RoundingMode",
# test.java.net
"test.java.net.DatagramSocket",
@@ -190,7 +189,6 @@
"test.java.security.cert",
# Sharded test.java.security.KeyAgreement
"test.java.security.KeyAgreement.KeyAgreementTest",
- "test.java.security.KeyAgreement.KeySizeTest#testDHKeySize",
"test.java.security.KeyAgreement.KeySizeTest#testECDHKeySize",
"test.java.security.KeyAgreement.KeySpecTest",
"test.java.security.KeyAgreement.MultiThreadTest",
@@ -242,6 +240,130 @@
CLASSPATH = ["core-tests", "core-ojtests", "jsr166-tests", "mockito-target"]
+SLOW_OJLUNI_TESTS = {
+ "test.java.awt",
+ "test.java.lang.String",
+ "test.java.lang.invoke",
+ "test.java.nio.channels.Selector",
+ "test.java.time",
+ "test.java.util.Arrays",
+ "test.java.util.Map",
+ "test.java.util.concurrent",
+ "test.java.util.stream",
+ "test.java.util.zip.ZipFile",
+ "tck.java.time",
+}
+
+# Disabled to unblock art-buildbot
+# These tests fail with "java.io.IOException: Stream closed", tracked in
+# http://b/235566533 and http://b/208639267
+DISABLED_GCSTRESS_DEBUG_TESTS = {
+ "test.java.lang.StrictMath.HypotTests#testAgainstTranslit_shard1",
+ "test.java.lang.StrictMath.HypotTests#testAgainstTranslit_shard2",
+ "test.java.lang.StrictMath.HypotTests#testAgainstTranslit_shard3",
+ "test.java.lang.StrictMath.HypotTests#testAgainstTranslit_shard4",
+ "test.java.math.BigDecimal",
+ "test.java.math.BigInteger#testConstructor",
+}
+
+DISABLED_FUGU_TESTS = {
+ "org.apache.harmony.luni.tests.internal.net.www.protocol.http.HttpURLConnection",
+ "org.apache.harmony.luni.tests.internal.net.www.protocol.https.HttpsURLConnection",
+ "test.java.awt",
+ "test.java.io.ByteArrayInputStream",
+ "test.java.io.ByteArrayOutputStream",
+ "test.java.io.InputStream",
+ "test.java.io.OutputStream",
+ "test.java.io.PrintStream",
+ "test.java.io.PrintWriter",
+ "test.java.io.Reader",
+ "test.java.io.Writer",
+ "test.java.lang.Boolean",
+ "test.java.lang.ClassLoader",
+ "test.java.lang.Double",
+ "test.java.lang.Float",
+ "test.java.lang.Integer",
+ "test.java.lang.Long",
+ "test.java.lang.StrictMath.CubeRootTests",
+ "test.java.lang.StrictMath.Expm1Tests",
+ "test.java.lang.StrictMath.ExpTests",
+ "test.java.lang.StrictMath.HyperbolicTests",
+ "test.java.lang.StrictMath.HypotTests#testAgainstTranslit_shard1",
+ "test.java.lang.StrictMath.HypotTests#testAgainstTranslit_shard2",
+ "test.java.lang.StrictMath.HypotTests#testAgainstTranslit_shard3",
+ "test.java.lang.StrictMath.HypotTests#testAgainstTranslit_shard4",
+ "test.java.lang.StrictMath.HypotTests#testHypot",
+ "test.java.lang.StrictMath.Log1pTests",
+ "test.java.lang.StrictMath.Log10Tests",
+ "test.java.lang.StrictMath.MultiplicationTests",
+ "test.java.lang.StrictMath.PowTests",
+ "test.java.lang.String",
+ "test.java.lang.Thread",
+ "test.java.lang.invoke",
+ "test.java.lang.ref.SoftReference",
+ "test.java.lang.ref.BasicTest",
+ "test.java.lang.ref.EnqueueNullRefTest",
+ "test.java.lang.ref.EnqueuePollRaceTest",
+ "test.java.lang.ref.ReferenceCloneTest",
+ "test.java.lang.ref.ReferenceEnqueuePendingTest",
+ "test.java.math.BigDecimal",
+ "test.java.math.BigInteger#testArithmetic",
+ "test.java.math.BigInteger#testBitCount",
+ "test.java.math.BigInteger#testBitLength",
+ "test.java.math.BigInteger#testbitOps",
+ "test.java.math.BigInteger#testBitwise",
+ "test.java.math.BigInteger#testByteArrayConv",
+ "test.java.math.BigInteger#testConstructor",
+ "test.java.math.BigInteger#testDivideAndReminder",
+ "test.java.math.BigInteger#testDivideLarge",
+ "test.java.math.BigInteger#testModExp",
+ "test.java.math.BigInteger#testMultiplyLarge",
+ "test.java.math.BigInteger#testNextProbablePrime",
+ "test.java.math.BigInteger#testPow",
+ "test.java.math.BigInteger#testSerialize",
+ "test.java.math.BigInteger#testShift",
+ "test.java.math.BigInteger#testSquare",
+ "test.java.math.BigInteger#testSquareLarge",
+ "test.java.math.BigInteger#testSquareRootAndReminder",
+ "test.java.math.BigInteger#testStringConv_generic",
+ "test.java.math.RoundingMode",
+ "test.java.net.DatagramSocket",
+ "test.java.net.Socket",
+ "test.java.net.SocketOptions",
+ "test.java.net.URLDecoder",
+ "test.java.net.URLEncoder",
+ "test.java.nio.channels.Channels",
+ "test.java.nio.channels.SelectionKey",
+ "test.java.nio.channels.Selector",
+ "test.java.nio.file",
+ "test.java.security.cert",
+ "test.java.security.KeyAgreement.KeyAgreementTest",
+ "test.java.security.KeyAgreement.KeySizeTest#testECDHKeySize",
+ "test.java.security.KeyAgreement.KeySpecTest",
+ "test.java.security.KeyAgreement.MultiThreadTest",
+ "test.java.security.KeyAgreement.NegativeTest",
+ "test.java.security.KeyStore",
+ "test.java.security.Provider",
+ "test.java.time",
+ "test.java.util.Arrays",
+ "test.java.util.Collection",
+ "test.java.util.Collections",
+ "test.java.util.Date",
+ "test.java.util.EnumMap",
+ "test.java.util.EnumSet",
+ "test.java.util.GregorianCalendar",
+ "test.java.util.LinkedHashMap",
+ "test.java.util.LinkedHashSet",
+ "test.java.util.List",
+ "test.java.util.Map",
+ "test.java.util.Optional",
+ "test.java.util.TestFormatter",
+ "test.java.util.TimeZone",
+ "test.java.util.function",
+ "test.java.util.stream",
+ "tck.java.time",
+}
+
def get_jar_filename(classpath):
base_path = (ANDROID_PRODUCT_OUT + "/../..") if ANDROID_PRODUCT_OUT else "out/target"
base_path = os.path.normpath(base_path) # Normalize ".." components for readability.
@@ -275,6 +397,13 @@
# See b/78228743 and b/178351808.
if args.gcstress or args.debug or args.mode == "jvm":
test_names = list(t for t in test_names if not t.startswith("libcore.highmemorytest"))
+ test_names = list(filter(lambda x: x not in SLOW_OJLUNI_TESTS, test_names))
+ if args.gcstress and args.debug:
+ test_names = list(filter(lambda x: x not in DISABLED_GCSTRESS_DEBUG_TESTS, test_names))
+ if not args.getrandom:
+ # Disable libcore.highmemorytest due to limited ram on fugu. http://b/258173036
+ test_names = list(filter(lambda x: x not in DISABLED_FUGU_TESTS and
+ not x.startswith("libcore.highmemorytest"), test_names))
return test_names
def get_vogar_command(test_name):
@@ -282,6 +411,7 @@
if args.mode == "device":
cmd.append("--mode=device --vm-arg -Ximage:/system/framework/art_boot_images/boot.art")
cmd.append("--vm-arg -Xbootclasspath:" + ":".join(BOOT_CLASSPATH))
+
if args.mode == "host":
# We explicitly give a wrong path for the image, to ensure vogar
# will create a boot image with the default compiler. Note that
@@ -298,11 +428,16 @@
if args.debug:
cmd.append("--vm-arg -XXlib:libartd.so --vm-arg -XX:SlowDebug=true")
+ # The only device in go/art-buildbot without getrandom is fugu. We limit the amount of memory
+ # per runtime for fugu to avoid low memory killer, fugu has 4-cores 1GB RAM (b/258171768).
+ if not args.getrandom:
+ cmd.append("--vm-arg -Xmx128M")
+
if args.mode == "device":
if ART_TEST_CHROOT:
cmd.append(f"--chroot {ART_TEST_CHROOT} --device-dir=/tmp/vogar/test-{test_name}")
else:
- cmd.append("--device-dir=/data/local/tmp/vogar/test-{test_name}")
+ cmd.append(f"--device-dir=/data/local/tmp/vogar/test-{test_name}")
cmd.append(f"--vm-command={ART_TEST_ANDROID_ROOT}/bin/art")
else:
cmd.append(f"--device-dir=/tmp/vogar/test-{test_name}")
@@ -314,6 +449,9 @@
cmd.append("--vm-arg -Xcompiler-option --vm-arg --compiler-filter=quicken")
cmd.append("--vm-arg -Xusejit:{}".format(str(args.jit).lower()))
+ if args.verbose:
+ cmd.append("--verbose")
+
# Suppress color codes if not attached to a terminal
if not sys.stdout.isatty():
cmd.append("--no-color")
@@ -370,7 +508,7 @@
print(f"Running {len(futures)} tasks on {args.jobs} core(s)...\n")
for i, future in enumerate(concurrent.futures.as_completed(futures)):
test_name, cmd, stdout, exit_code = future.result()
- if exit_code != 0 or args.dry_run:
+ if exit_code != 0 or args.dry_run or args.verbose:
print(cmd)
print(stdout.strip())
else:
diff --git a/tools/run-libjdwp-tests.sh b/tools/run-libjdwp-tests.sh
index efb2737..06e34f9 100755
--- a/tools/run-libjdwp-tests.sh
+++ b/tools/run-libjdwp-tests.sh
@@ -138,6 +138,10 @@
expectations="$expectations --expectations $PWD/art/tools/external_oj_libjdwp_art_gcstress_debug_failures.txt"
fi
+if [[ "${ART_USE_READ_BARRIER}" = "false" ]]; then
+ expectations="$expectations --expectations $PWD/art/tools/external_oj_libjdwp_art_no_read_barrier_failures.txt"
+fi
+
function verbose_run() {
echo "$@"
env "$@"
diff --git a/tools/signal_dumper/Android.bp b/tools/signal_dumper/Android.bp
index 33450d1..00948b8 100644
--- a/tools/signal_dumper/Android.bp
+++ b/tools/signal_dumper/Android.bp
@@ -48,18 +48,6 @@
},
}
-cc_defaults {
- name: "signal_dumper_libbacktrace_static_deps",
- defaults: [
- "signal_dumper_libbase_static_deps",
- "signal_dumper_libunwindstack_static_deps",
- ],
- static_libs: [
- "libbase",
- "libunwindstack",
- ],
-}
-
art_cc_binary {
name: "signal_dumper",
@@ -73,14 +61,14 @@
defaults: [
"art_defaults",
- "signal_dumper_libbacktrace_static_deps",
"signal_dumper_libbase_static_deps",
+ "signal_dumper_libunwindstack_static_deps",
],
srcs: ["signal_dumper.cc"],
static_libs: [
- "libbacktrace",
"libbase",
+ "libunwindstack",
],
}
diff --git a/tools/signal_dumper/signal_dumper.cc b/tools/signal_dumper/signal_dumper.cc
index e9a589e..e88ac19 100644
--- a/tools/signal_dumper/signal_dumper.cc
+++ b/tools/signal_dumper/signal_dumper.cc
@@ -15,6 +15,7 @@
*/
#include <dirent.h>
+#include <inttypes.h>
#include <poll.h>
#include <sys/prctl.h>
#include <sys/ptrace.h>
@@ -38,8 +39,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-#include <backtrace/Backtrace.h>
-#include <backtrace/BacktraceMap.h>
+#include <unwindstack/AndroidUnwinder.h>
namespace art {
namespace {
@@ -492,11 +492,10 @@
constexpr bool kIs64Bit = false;
#endif
-void DumpThread(pid_t pid,
+void DumpThread(unwindstack::AndroidRemoteUnwinder& unwinder, pid_t pid,
pid_t tid,
const std::string* addr2line_path,
- const char* prefix,
- BacktraceMap* map) {
+ const char* prefix) {
LOG(ERROR) << std::endl << "=== pid: " << pid << " tid: " << tid << " ===" << std::endl;
constexpr uint32_t kMaxWaitMicros = 1000 * 1000; // 1s.
@@ -504,50 +503,41 @@
LOG(ERROR) << "Failed to wait for sigstop on " << tid;
}
- std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map));
- if (backtrace == nullptr) {
- LOG(ERROR) << prefix << "(failed to create Backtrace for thread " << tid << ")";
- return;
- }
- backtrace->SetSkipFrames(false);
- if (!backtrace->Unwind(0, nullptr)) {
- LOG(ERROR) << prefix << "(backtrace::Unwind failed for thread " << tid
- << ": " << backtrace->GetErrorString(backtrace->GetError()) << ")";
- return;
- }
- if (backtrace->NumFrames() == 0) {
- LOG(ERROR) << prefix << "(no native stack frames for thread " << tid << ")";
+ unwindstack::AndroidUnwinderData data;
+ if (!unwinder.Unwind(tid, data)) {
+ LOG(ERROR) << prefix << "(Unwind failed for thread " << tid << ": "
+ << data.GetErrorString() << ")";
return;
}
std::unique_ptr<addr2line::Addr2linePipe> addr2line_state;
-
- for (Backtrace::const_iterator it = backtrace->begin();
- it != backtrace->end(); ++it) {
+ data.DemangleFunctionNames();
+ for (const unwindstack::FrameData& frame : data.frames) {
std::ostringstream oss;
- oss << prefix << StringPrintf("#%02zu pc ", it->num);
+ oss << prefix << StringPrintf("#%02zu pc ", frame.num);
bool try_addr2line = false;
- if (!BacktraceMap::IsValid(it->map)) {
- oss << StringPrintf(kIs64Bit ? "%016" PRIx64 " ???" : "%08" PRIx64 " ???", it->pc);
+ if (frame.map_info == nullptr) {
+ oss << StringPrintf(kIs64Bit ? "%016" PRIx64 " ???" : "%08" PRIx64 " ???", frame.pc);
} else {
- oss << StringPrintf(kIs64Bit ? "%016" PRIx64 " " : "%08" PRIx64 " ", it->rel_pc);
- if (it->map.name.empty()) {
- oss << StringPrintf("<anonymous:%" PRIx64 ">", it->map.start);
+ oss << StringPrintf(kIs64Bit ? "%016" PRIx64 " " : "%08" PRIx64 " ", frame.rel_pc);
+ if (frame.map_info->name().empty()) {
+ oss << StringPrintf("<anonymous:%" PRIx64 ">", frame.map_info->start());
} else {
- oss << it->map.name;
+ oss << frame.map_info->name().c_str();
}
- if (it->map.offset != 0) {
- oss << StringPrintf(" (offset %" PRIx64 ")", it->map.offset);
+ if (frame.map_info->offset() != 0) {
+ oss << StringPrintf(" (offset %" PRIx64 ")", frame.map_info->offset());
}
oss << " (";
- if (!it->func_name.empty()) {
- oss << it->func_name;
- if (it->func_offset != 0) {
- oss << "+" << it->func_offset;
+ const std::string& function_name = frame.function_name;
+ if (!function_name.empty()) {
+ oss << function_name;
+ if (frame.function_offset != 0) {
+ oss << "+" << frame.function_offset;
}
// Functions found using the gdb jit interface will be in an empty
// map that cannot be found using addr2line.
- if (!it->map.name.empty()) {
+ if (!frame.map_info->name().empty()) {
try_addr2line = true;
}
} else {
@@ -558,8 +548,8 @@
LOG(ERROR) << oss.str() << std::endl;
if (try_addr2line && addr2line_path != nullptr) {
addr2line::Addr2line(*addr2line_path,
- it->map.name,
- it->rel_pc,
+ frame.map_info->name(),
+ frame.rel_pc,
LOG_STREAM(ERROR),
prefix,
&addr2line_state);
@@ -593,14 +583,9 @@
LOG(ERROR) << "Did not receive SIGSTOP for pid " << forked_pid;
}
- std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(forked_pid));
- if (backtrace_map == nullptr) {
- LOG(ERROR) << "Could not create BacktraceMap";
- return;
- }
-
+ unwindstack::AndroidRemoteUnwinder unwinder(forked_pid);
for (pid_t tid : tids) {
- DumpThread(forked_pid, tid, addr2line_path.get(), " ", backtrace_map.get());
+ DumpThread(unwinder, forked_pid, tid, addr2line_path.get(), " ");
}
}
diff --git a/tools/tracefast-plugin/tracefast.cc b/tools/tracefast-plugin/tracefast.cc
index 618742d..734dce0 100644
--- a/tools/tracefast-plugin/tracefast.cc
+++ b/tools/tracefast-plugin/tracefast.cc
@@ -60,7 +60,6 @@
override REQUIRES_SHARED(art::Locks::mutator_lock_) { }
void MethodUnwind(art::Thread* thread ATTRIBUTE_UNUSED,
- art::Handle<art::mirror::Object> this_object ATTRIBUTE_UNUSED,
art::ArtMethod* method ATTRIBUTE_UNUSED,
uint32_t dex_pc ATTRIBUTE_UNUSED)
override REQUIRES_SHARED(art::Locks::mutator_lock_) { }
diff --git a/tools/veridex/Android.mk b/tools/veridex/Android.mk
index f7c8d50..9ea9b3a 100644
--- a/tools/veridex/Android.mk
+++ b/tools/veridex/Android.mk
@@ -23,14 +23,14 @@
system_stub_dex := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/core_dex_intermediates/classes.dex
$(system_stub_dex): PRIVATE_MIN_SDK_VERSION := 1000
-$(system_stub_dex): $(call resolve-prebuilt-sdk-jar-path,system_current) | $(ZIP2ZIP) $(DX)
+$(system_stub_dex): $(call resolve-prebuilt-sdk-jar-path,system_current) | $(ZIP2ZIP) $(D8)
$(transform-classes.jar-to-dex)
$(call declare-1p-target,$(system_stub_dex),art)
oahl_stub_dex := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/oahl_dex_intermediates/classes.dex
$(oahl_stub_dex): PRIVATE_MIN_SDK_VERSION := 1000
-$(oahl_stub_dex): $(call get-prebuilt-sdk-dir,current)/org.apache.http.legacy.jar | $(ZIP2ZIP) $(DX)
+$(oahl_stub_dex): $(call get-prebuilt-sdk-dir,current)/org.apache.http.legacy.jar | $(ZIP2ZIP) $(D8)
$(transform-classes.jar-to-dex)
$(call declare-1p-target,$(oahl_stub_dex),art)
diff --git a/tools/veridex/hidden_api.cc b/tools/veridex/hidden_api.cc
index 71ea56b..69e6fe4 100644
--- a/tools/veridex/hidden_api.cc
+++ b/tools/veridex/hidden_api.cc
@@ -29,14 +29,22 @@
CHECK(filename != nullptr);
std::ifstream in(filename);
+ bool errors = false;
for (std::string str; std::getline(in, str);) {
std::vector<std::string> values = android::base::Split(str, ",");
const std::string& signature = values[0];
hiddenapi::ApiList membership;
bool success = hiddenapi::ApiList::FromNames(values.begin() + 1, values.end(), &membership);
- CHECK(success) << "Unknown ApiList flag: " << str;
- CHECK(membership.IsValid()) << "Invalid ApiList: " << membership;
+ if (!success) {
+ LOG(ERROR) << "Unknown ApiList flag: " << str;
+ errors = true;
+ continue;
+ } else if (!membership.IsValid()) {
+ LOG(ERROR) << "Invalid ApiList: " << membership;
+ errors = true;
+ continue;
+ }
AddSignatureToApiList(signature, membership);
size_t pos = signature.find("->");
@@ -55,6 +63,7 @@
}
}
}
+ CHECK(!errors) << "Errors encountered while parsing file " << filename;
}
void HiddenApi::AddSignatureToApiList(const std::string& signature, hiddenapi::ApiList membership) {