summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk4
-rw-r--r--build/Android.common.mk2
-rw-r--r--build/Android.gtest.mk6
-rw-r--r--build/Android.oat.mk38
-rw-r--r--dex2oat/dex2oat.cc28
-rw-r--r--openjdkjvmti/OpenjdkJvmTi.cc4
-rw-r--r--openjdkjvmti/art_jvmti.h5
-rw-r--r--openjdkjvmti/events-inl.h70
-rw-r--r--openjdkjvmti/events.h7
-rw-r--r--openjdkjvmti/ti_stack.cc112
-rw-r--r--openjdkjvmti/ti_stack.h5
-rw-r--r--openjdkjvmti/ti_thread.cc35
-rw-r--r--openjdkjvmti/ti_thread.h20
-rw-r--r--runtime/Android.bp1
-rw-r--r--runtime/arch/arch_test.cc29
-rw-r--r--runtime/asm_support.h213
-rw-r--r--runtime/asm_support_check.h65
-rw-r--r--runtime/common_dex_operations.h9
-rw-r--r--runtime/entrypoints/quick/quick_trampoline_entrypoints.cc12
-rw-r--r--runtime/gc/collector/concurrent_copying.cc16
-rw-r--r--runtime/interpreter/interpreter.cc43
-rw-r--r--runtime/interpreter/interpreter.h6
-rw-r--r--runtime/interpreter/interpreter_common.cc11
-rw-r--r--runtime/interpreter/interpreter_switch_impl.cc118
-rw-r--r--runtime/interpreter/mterp/mterp.cc5
-rw-r--r--runtime/interpreter/shadow_frame.h61
-rw-r--r--runtime/native/dalvik_system_VMRuntime.cc6
-rw-r--r--runtime/quick_exception_handler.cc2
-rw-r--r--runtime/runtime.cc3
-rw-r--r--runtime/runtime.h12
-rw-r--r--runtime/stack.cc31
-rw-r--r--runtime/thread.cc41
-rwxr-xr-xtest/1953-pop-frame/check21
-rw-r--r--test/1953-pop-frame/class-loading-expected.patch21
-rw-r--r--test/1953-pop-frame/expected.txt98
-rw-r--r--test/1953-pop-frame/info.txt7
-rw-r--r--test/1953-pop-frame/pop_frame.cc998
-rwxr-xr-xtest/1953-pop-frame/run24
-rw-r--r--test/1953-pop-frame/src/Main.java (renamed from tools/cpp-define-generator/constant_card_table.def)16
-rw-r--r--test/1953-pop-frame/src/art/Breakpoint.java202
-rw-r--r--test/1953-pop-frame/src/art/Redefinition.java91
-rw-r--r--test/1953-pop-frame/src/art/StackTrace.java68
-rw-r--r--test/1953-pop-frame/src/art/Suspension.java (renamed from tools/cpp-define-generator/common_undef.def)20
-rw-r--r--test/1953-pop-frame/src/art/Test1953.java976
-rwxr-xr-xtest/1954-pop-frame-jit/check21
-rw-r--r--test/1954-pop-frame-jit/expected.txt118
-rw-r--r--test/1954-pop-frame-jit/info.txt7
-rw-r--r--test/1954-pop-frame-jit/jvm-expected.patch21
-rwxr-xr-xtest/1954-pop-frame-jit/run24
-rw-r--r--test/1954-pop-frame-jit/src/Main.java60
-rw-r--r--test/1954-pop-frame-jit/src/art/Breakpoint.java202
-rw-r--r--test/1954-pop-frame-jit/src/art/Redefinition.java91
-rw-r--r--test/1954-pop-frame-jit/src/art/StackTrace.java68
-rw-r--r--test/1954-pop-frame-jit/src/art/Suspension.java30
l---------test/1954-pop-frame-jit/src/art/Test1953.java1
-rwxr-xr-xtest/1955-pop-frame-jit-called/check21
-rw-r--r--test/1955-pop-frame-jit-called/expected.txt118
-rw-r--r--test/1955-pop-frame-jit-called/info.txt7
-rw-r--r--test/1955-pop-frame-jit-called/jvm-expected.patch21
-rwxr-xr-xtest/1955-pop-frame-jit-called/run26
-rw-r--r--test/1955-pop-frame-jit-called/src/Main.java53
-rw-r--r--test/1955-pop-frame-jit-called/src/art/Breakpoint.java202
-rw-r--r--test/1955-pop-frame-jit-called/src/art/Redefinition.java91
-rw-r--r--test/1955-pop-frame-jit-called/src/art/StackTrace.java68
-rw-r--r--test/1955-pop-frame-jit-called/src/art/Suspension.java30
l---------test/1955-pop-frame-jit-called/src/art/Test1953.java1
-rwxr-xr-xtest/1956-pop-frame-jit-calling/check21
-rw-r--r--test/1956-pop-frame-jit-calling/expected.txt118
-rw-r--r--test/1956-pop-frame-jit-calling/info.txt7
-rw-r--r--test/1956-pop-frame-jit-calling/jvm-expected.patch21
-rwxr-xr-xtest/1956-pop-frame-jit-calling/run26
-rw-r--r--test/1956-pop-frame-jit-calling/src/Main.java53
-rw-r--r--test/1956-pop-frame-jit-calling/src/art/Breakpoint.java202
-rw-r--r--test/1956-pop-frame-jit-calling/src/art/Redefinition.java91
-rw-r--r--test/1956-pop-frame-jit-calling/src/art/StackTrace.java68
-rw-r--r--test/1956-pop-frame-jit-calling/src/art/Suspension.java30
l---------test/1956-pop-frame-jit-calling/src/art/Test1953.java1
-rw-r--r--test/686-get-this/expected.txt1
-rw-r--r--test/686-get-this/info.txt2
-rw-r--r--test/686-get-this/smali/Test.smali45
-rw-r--r--test/686-get-this/src/Main.java47
-rw-r--r--test/Android.bp2
-rw-r--r--test/Android.run-test.mk28
-rw-r--r--test/common/runtime_state.cc71
-rw-r--r--test/common/stack_inspect.cc20
-rw-r--r--test/knownfailures.json21
-rwxr-xr-xtest/run-test12
-rwxr-xr-xtest/testrunner/testrunner.py6
-rw-r--r--test/ti-agent/jni_binder.cc4
-rw-r--r--test/ti-agent/jni_binder.h2
-rw-r--r--tools/cpp-define-generator/Android.bp53
-rw-r--r--tools/cpp-define-generator/art_method.def32
-rw-r--r--tools/cpp-define-generator/asm_defines.cc36
-rw-r--r--tools/cpp-define-generator/asm_defines.def (renamed from tools/cpp-define-generator/constant_jit.def)27
-rw-r--r--tools/cpp-define-generator/constant_class.def30
-rw-r--r--tools/cpp-define-generator/constant_dexcache.def32
-rw-r--r--tools/cpp-define-generator/constant_globals.def38
-rw-r--r--tools/cpp-define-generator/constant_lockword.def51
-rw-r--r--tools/cpp-define-generator/constant_reference.def30
-rw-r--r--tools/cpp-define-generator/constant_rosalloc.def40
-rw-r--r--tools/cpp-define-generator/constant_thread.def30
-rw-r--r--tools/cpp-define-generator/globals.def66
-rw-r--r--tools/cpp-define-generator/lockword.def58
-rw-r--r--tools/cpp-define-generator/main.cc114
-rwxr-xr-xtools/cpp-define-generator/make_header.py56
-rwxr-xr-xtools/cpp-define-generator/make_header_test.py49
-rw-r--r--tools/cpp-define-generator/mirror_array.def40
-rw-r--r--tools/cpp-define-generator/mirror_class.def38
-rw-r--r--tools/cpp-define-generator/mirror_dex_cache.def34
-rw-r--r--tools/cpp-define-generator/mirror_object.def (renamed from tools/cpp-define-generator/constant_heap.def)17
-rw-r--r--tools/cpp-define-generator/mirror_string.def (renamed from tools/cpp-define-generator/common.def)16
-rw-r--r--tools/cpp-define-generator/offset_art_method.def43
-rw-r--r--tools/cpp-define-generator/offset_mirror_class.def32
-rw-r--r--tools/cpp-define-generator/offset_mirror_dex_cache.def32
-rw-r--r--tools/cpp-define-generator/offset_mirror_object.def33
-rw-r--r--tools/cpp-define-generator/offset_runtime.def49
-rw-r--r--tools/cpp-define-generator/offset_shadow_frame.def41
-rw-r--r--tools/cpp-define-generator/offset_thread.def40
-rw-r--r--tools/cpp-define-generator/offsets_all.def68
-rw-r--r--tools/cpp-define-generator/rosalloc.def38
-rw-r--r--tools/cpp-define-generator/runtime.def32
-rw-r--r--tools/cpp-define-generator/shadow_frame.def42
-rw-r--r--tools/cpp-define-generator/thread.def64
-rw-r--r--tools/libcore_gcstress_debug_failures.txt3
124 files changed, 5880 insertions, 1266 deletions
diff --git a/Android.mk b/Android.mk
index 925f6e4451..b9f617035c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -98,6 +98,8 @@ include $(art_path)/build/Android.common_test.mk
include $(art_path)/build/Android.gtest.mk
include $(art_path)/test/Android.run-test.mk
+TEST_ART_TARGET_SYNC_DEPS += $(ART_TEST_TARGET_GTEST_DEPENDENCIES) $(ART_TEST_TARGET_RUN_TEST_DEPENDENCIES)
+
# Make sure /system is writable on the device.
TEST_ART_ADB_ROOT_AND_REMOUNT := \
($(ADB) root && \
@@ -484,7 +486,7 @@ build-art-unbundled-golem: art-runtime linker oatdump $(TARGET_CORE_JARS) crash_
build-art-host-tests: build-art-host $(TEST_ART_RUN_TEST_DEPENDENCIES) $(ART_TEST_HOST_RUN_TEST_DEPENDENCIES) $(ART_TEST_HOST_GTEST_DEPENDENCIES) | $(TEST_ART_RUN_TEST_ORDERONLY_DEPENDENCIES)
.PHONY: build-art-target-tests
-build-art-target-tests: build-art-target $(TEST_ART_RUN_TEST_DEPENDENCIES) | $(TEST_ART_RUN_TEST_ORDERONLY_DEPENDENCIES)
+build-art-target-tests: build-art-target $(TEST_ART_RUN_TEST_DEPENDENCIES) $(ART_TEST_TARGET_RUN_TEST_DEPENDENCIES) $(ART_TEST_TARGET_GTEST_DEPENDENCIES) | $(TEST_ART_RUN_TEST_ORDERONLY_DEPENDENCIES)
########################################################################
# targets to switch back and forth from libdvm to libart
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 316ce646ab..d024e7767e 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -93,6 +93,6 @@ else
endif
ADB_EXECUTABLE := $(HOST_OUT_EXECUTABLES)/adb
-ADB := $(ADB_EXECUTABLE)
+ADB ?= $(ADB_EXECUTABLE)
endif # ART_ANDROID_COMMON_MK
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index c6fdf27525..4badc5a907 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -404,6 +404,7 @@ ART_TEST_TARGET_GTEST$(ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_GTEST$(2ND_ART_PHONY_TEST_TARGET_SUFFIX)_RULES :=
ART_TEST_TARGET_GTEST_RULES :=
ART_TEST_HOST_GTEST_DEPENDENCIES :=
+ART_TEST_TARGET_GTEST_DEPENDENCIES :=
ART_GTEST_TARGET_ANDROID_ROOT := '/system'
ifneq ($(ART_TEST_ANDROID_ROOT),)
@@ -432,7 +433,7 @@ define define-art-gtest-rule-target
# Add the test dependencies to test-art-target-sync, which will be a prerequisite for the test
# to ensure files are pushed to the device.
- TEST_ART_TARGET_SYNC_DEPS += \
+ gtest_deps := \
$$(ART_GTEST_$(1)_TARGET_DEPS) \
$(foreach file,$(ART_GTEST_$(1)_DEX_DEPS),$(ART_TEST_TARGET_GTEST_$(file)_DEX)) \
$$(gtest_exe) \
@@ -442,6 +443,8 @@ define define-art-gtest-rule-target
$$(TARGET_OUT_JAVA_LIBRARIES)/core-oj-testdex.jar \
$$(TARGET_OUT_JAVA_LIBRARIES)/core-simple-testdex.jar
+ ART_TEST_TARGET_GTEST_DEPENDENCIES += $$(gtest_deps)
+
$$(gtest_rule): PRIVATE_TARGET_EXE := $$(gtest_target_exe)
$$(gtest_rule): PRIVATE_MAYBE_CHROOT_COMMAND := $$(maybe_chroot_command)
@@ -473,6 +476,7 @@ $$(gtest_rule): test-art-target-sync
maybe_chroot_command :=
maybe_art_test_chroot :=
gtest_target_exe :=
+ gtest_deps :=
gtest_exe :=
gtest_rule :=
endef # define-art-gtest-rule-target
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index 2824a099b2..e2adac1660 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -39,7 +39,6 @@ endif
# Use dex2oat debug version for better error reporting
# $(1): compiler - optimizing, interpreter or interp-ac (interpreter-access-checks).
# $(2): 2ND_ or undefined, 2ND_ for 32-bit host builds.
-# $(3): multi-image.
# NB depending on HOST_CORE_DEX_LOCATIONS so we are sure to have the dex files in frameworks for
# run-test --no-image
define create-core-oat-host-rules
@@ -66,25 +65,14 @@ define create-core-oat-host-rules
$$(error found $(1) expected interpreter, interp-ac, or optimizing)
endif
- # If $(3) is true, generate a multi-image.
- ifeq ($(3),true)
- core_multi_infix := -multi
- core_multi_param := --multi-image --no-inline-from=core-oj-hostdex.jar
- core_multi_group := _multi
- else
- core_multi_infix :=
- core_multi_param :=
- core_multi_group :=
- endif
-
- core_image_name := $($(2)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(CORE_IMG_SUFFIX)
- core_oat_name := $($(2)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$$(core_multi_infix)$(CORE_OAT_SUFFIX)
+ core_image_name := $($(2)HOST_CORE_IMG_OUT_BASE)$$(core_infix)$(CORE_IMG_SUFFIX)
+ core_oat_name := $($(2)HOST_CORE_OAT_OUT_BASE)$$(core_infix)$(CORE_OAT_SUFFIX)
# Using the bitness suffix makes it easier to add as a dependency for the run-test mk.
ifeq ($(2),)
- HOST_CORE_IMAGE_$(1)$$(core_multi_group)_64 := $$(core_image_name)
+ HOST_CORE_IMAGE_$(1)_64 := $$(core_image_name)
else
- HOST_CORE_IMAGE_$(1)$$(core_multi_group)_32 := $$(core_image_name)
+ HOST_CORE_IMAGE_$(1)_32 := $$(core_image_name)
endif
HOST_CORE_IMG_OUTS += $$(core_image_name)
HOST_CORE_OAT_OUTS += $$(core_oat_name)
@@ -92,7 +80,6 @@ define create-core-oat-host-rules
$$(core_image_name): PRIVATE_CORE_COMPILE_OPTIONS := $$(core_compile_options)
$$(core_image_name): PRIVATE_CORE_IMG_NAME := $$(core_image_name)
$$(core_image_name): PRIVATE_CORE_OAT_NAME := $$(core_oat_name)
-$$(core_image_name): PRIVATE_CORE_MULTI_PARAM := $$(core_multi_param)
$$(core_image_name): $$(HOST_CORE_DEX_LOCATIONS) $$(core_dex2oat_dependency)
@echo "host dex2oat: $$@"
@mkdir -p $$(dir $$@)
@@ -106,7 +93,8 @@ $$(core_image_name): $$(HOST_CORE_DEX_LOCATIONS) $$(core_dex2oat_dependency)
--host --android-root=$$(HOST_OUT) \
--generate-debug-info --generate-build-id \
--runtime-arg -XX:SlowDebug=true \
- $$(PRIVATE_CORE_MULTI_PARAM) $$(PRIVATE_CORE_COMPILE_OPTIONS)
+ --no-inline-from=core-oj-hostdex.jar \
+ $$(PRIVATE_CORE_COMPILE_OPTIONS)
$$(core_oat_name): $$(core_image_name)
@@ -119,21 +107,17 @@ $$(core_oat_name): $$(core_image_name)
endef # create-core-oat-host-rules
# $(1): compiler - optimizing, interpreter or interp-ac (interpreter-access-checks).
-# $(2): multi-image.
define create-core-oat-host-rule-combination
- $(call create-core-oat-host-rules,$(1),,$(2))
+ $(call create-core-oat-host-rules,$(1),)
ifneq ($(HOST_PREFER_32_BIT),true)
- $(call create-core-oat-host-rules,$(1),2ND_,$(2))
+ $(call create-core-oat-host-rules,$(1),2ND_)
endif
endef
-$(eval $(call create-core-oat-host-rule-combination,optimizing,false))
-$(eval $(call create-core-oat-host-rule-combination,interpreter,false))
-$(eval $(call create-core-oat-host-rule-combination,interp-ac,false))
-$(eval $(call create-core-oat-host-rule-combination,optimizing,true))
-$(eval $(call create-core-oat-host-rule-combination,interpreter,true))
-$(eval $(call create-core-oat-host-rule-combination,interp-ac,true))
+$(eval $(call create-core-oat-host-rule-combination,optimizing))
+$(eval $(call create-core-oat-host-rule-combination,interpreter))
+$(eval $(call create-core-oat-host-rule-combination,interp-ac))
.PHONY: test-art-host-dex2oat-host
test-art-host-dex2oat-host: $(HOST_CORE_IMG_OUTS)
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 48d0b17927..71cdfd2c08 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -416,8 +416,7 @@ NO_RETURN static void Usage(const char* fmt, ...) {
UsageError(" --app-image-file=<file-name>: specify a file name for app image.");
UsageError(" Example: --app-image-file=/data/dalvik-cache/system@app@Calculator.apk.art");
UsageError("");
- UsageError(" --multi-image: specify that separate oat and image files be generated for each "
- "input dex file.");
+ UsageError(" --multi-image: obsolete, ignored");
UsageError("");
UsageError(" --force-determinism: force the compiler to emit a deterministic output.");
UsageError("");
@@ -634,7 +633,6 @@ class Dex2Oat final {
image_storage_mode_(ImageHeader::kStorageModeUncompressed),
passes_to_run_filename_(nullptr),
dirty_image_objects_filename_(nullptr),
- multi_image_(false),
is_host_(false),
elf_writers_(),
oat_writers_(),
@@ -914,20 +912,6 @@ class Dex2Oat final {
break;
}
- if (!IsBootImage() && multi_image_) {
- Usage("--multi-image can only be used when creating boot images");
- }
- if (IsBootImage() && multi_image_ && image_filenames_.size() > 1) {
- Usage("--multi-image cannot be used with multiple image names");
- }
-
- // For now, if we're on the host and compile the boot image, *always* use multiple image files.
- if (!kIsTargetBuild && IsBootImage()) {
- if (image_filenames_.size() == 1) {
- multi_image_ = true;
- }
- }
-
// Done with usage checks, enable watchdog if requested
if (parser_options->watch_dog_enabled) {
int64_t timeout = parser_options->watch_dog_timeout_in_ms > 0
@@ -973,7 +957,7 @@ class Dex2Oat final {
std::string base_oat = oat_filenames_[0];
size_t last_oat_slash = base_oat.rfind('/');
if (last_oat_slash == std::string::npos) {
- Usage("--multi-image used with unusable oat filename %s", base_oat.c_str());
+ Usage("Unusable boot image oat filename %s", base_oat.c_str());
}
// We also need to honor path components that were encoded through '@'. Otherwise the loading
// code won't be able to find the images.
@@ -985,7 +969,7 @@ class Dex2Oat final {
std::string base_img = image_filenames_[0];
size_t last_img_slash = base_img.rfind('/');
if (last_img_slash == std::string::npos) {
- Usage("--multi-image used with unusable image filename %s", base_img.c_str());
+ Usage("Unusable boot image filename %s", base_img.c_str());
}
// We also need to honor path components that were encoded through '@'. Otherwise the loading
// code won't be able to find the images.
@@ -1010,7 +994,7 @@ class Dex2Oat final {
base_symbol_oat = oat_unstripped_[0];
size_t last_symbol_oat_slash = base_symbol_oat.rfind('/');
if (last_symbol_oat_slash == std::string::npos) {
- Usage("--multi-image used with unusable symbol filename %s", base_symbol_oat.c_str());
+ Usage("Unusable boot image symbol filename %s", base_symbol_oat.c_str());
}
base_symbol_oat = base_symbol_oat.substr(0, last_symbol_oat_slash + 1);
}
@@ -1194,7 +1178,6 @@ class Dex2Oat final {
AssignTrueIfExists(args, M::Host, &is_host_);
AssignTrueIfExists(args, M::AvoidStoringInvocation, &avoid_storing_invocation_);
- AssignTrueIfExists(args, M::MultiImage, &multi_image_);
AssignIfExists(args, M::CopyDexFiles, &copy_dex_files_);
if (args.Exists(M::ForceDeterminism)) {
@@ -1258,7 +1241,7 @@ class Dex2Oat final {
PruneNonExistentDexFiles();
// Expand oat and image filenames for multi image.
- if (IsBootImage() && multi_image_) {
+ if (IsBootImage() && image_filenames_.size() == 1) {
ExpandOatAndImageFilenames();
}
@@ -2780,7 +2763,6 @@ class Dex2Oat final {
const char* dirty_image_objects_filename_;
std::unique_ptr<HashSet<std::string>> dirty_image_objects_;
std::unique_ptr<std::vector<std::string>> passes_to_run_;
- bool multi_image_;
bool is_host_;
std::string android_root_;
std::string no_inline_from_string_;
diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc
index 3213bbe91a..48f326a54d 100644
--- a/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/openjdkjvmti/OpenjdkJvmTi.cc
@@ -313,10 +313,10 @@ class JvmtiFunctions {
return StackUtil::GetFrameCount(env, thread, count_ptr);
}
- static jvmtiError PopFrame(jvmtiEnv* env, jthread thread ATTRIBUTE_UNUSED) {
+ static jvmtiError PopFrame(jvmtiEnv* env, jthread thread) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_pop_frame);
- return ERR(NOT_IMPLEMENTED);
+ return StackUtil::PopFrame(env, thread);
}
static jvmtiError GetFrameLocation(jvmtiEnv* env,
diff --git a/openjdkjvmti/art_jvmti.h b/openjdkjvmti/art_jvmti.h
index 82f3866c65..1218e3b9a7 100644
--- a/openjdkjvmti/art_jvmti.h
+++ b/openjdkjvmti/art_jvmti.h
@@ -249,7 +249,7 @@ const jvmtiCapabilities kPotentialCapabilities = {
.can_get_owned_monitor_info = 1,
.can_get_current_contended_monitor = 1,
.can_get_monitor_info = 1,
- .can_pop_frame = 0,
+ .can_pop_frame = 1,
.can_redefine_classes = 1,
.can_signal_thread = 1,
.can_get_source_file_name = 1,
@@ -291,6 +291,7 @@ const jvmtiCapabilities kPotentialCapabilities = {
// can_retransform_classes:
// can_redefine_any_class:
// can_redefine_classes:
+// can_pop_frame:
// We need to ensure that inlined code is either not present or can always be deoptimized. This
// is not guaranteed for non-debuggable processes since we might have inlined bootclasspath code
// on a threads stack.
@@ -303,7 +304,7 @@ const jvmtiCapabilities kNonDebuggableUnsupportedCapabilities = {
.can_get_owned_monitor_info = 0,
.can_get_current_contended_monitor = 0,
.can_get_monitor_info = 0,
- .can_pop_frame = 0,
+ .can_pop_frame = 1,
.can_redefine_classes = 1,
.can_signal_thread = 0,
.can_get_source_file_name = 0,
diff --git a/openjdkjvmti/events-inl.h b/openjdkjvmti/events-inl.h
index e98517fdff..ca66556bb0 100644
--- a/openjdkjvmti/events-inl.h
+++ b/openjdkjvmti/events-inl.h
@@ -26,7 +26,9 @@
#include "jni/jni_internal.h"
#include "nativehelper/scoped_local_ref.h"
#include "scoped_thread_state_change-inl.h"
+#include "stack.h"
#include "ti_breakpoint.h"
+#include "ti_thread.h"
#include "art_jvmti.h"
@@ -359,6 +361,7 @@ inline bool EventHandler::ShouldDispatch<ArtJvmtiEvent::kFramePop>(
// have to deal with use-after-free or the frames being reallocated later.
art::WriterMutexLock lk(art::Thread::Current(), env->event_info_mutex_);
return env->notify_frames.erase(frame) != 0 &&
+ !frame->GetForcePopFrame() &&
ShouldDispatchOnThread<ArtJvmtiEvent::kFramePop>(env, thread);
}
@@ -418,6 +421,67 @@ inline void EventHandler::ExecuteCallback<ArtJvmtiEvent::kFramePop>(
ExecuteCallback<ArtJvmtiEvent::kFramePop>(event, jnienv, jni_thread, jmethod, is_exception);
}
+struct ScopedDisablePopFrame {
+ public:
+ explicit ScopedDisablePopFrame(art::Thread* thread) : thread_(thread) {
+ art::Locks::mutator_lock_->AssertSharedHeld(thread_);
+ art::MutexLock mu(thread_, *art::Locks::thread_list_lock_);
+ JvmtiGlobalTLSData* data = ThreadUtil::GetOrCreateGlobalTLSData(thread_);
+ current_top_frame_ = art::StackVisitor::ComputeNumFrames(
+ thread_, art::StackVisitor::StackWalkKind::kIncludeInlinedFrames);
+ old_disable_frame_pop_depth_ = data->disable_pop_frame_depth;
+ data->disable_pop_frame_depth = current_top_frame_;
+ DCHECK(old_disable_frame_pop_depth_ == JvmtiGlobalTLSData::kNoDisallowedPopFrame ||
+ current_top_frame_ > old_disable_frame_pop_depth_)
+ << "old: " << old_disable_frame_pop_depth_ << " current: " << current_top_frame_;
+ }
+
+ ~ScopedDisablePopFrame() {
+ art::Locks::mutator_lock_->AssertSharedHeld(thread_);
+ art::MutexLock mu(thread_, *art::Locks::thread_list_lock_);
+ JvmtiGlobalTLSData* data = ThreadUtil::GetGlobalTLSData(thread_);
+ DCHECK_EQ(data->disable_pop_frame_depth, current_top_frame_);
+ data->disable_pop_frame_depth = old_disable_frame_pop_depth_;
+ }
+
+ private:
+ art::Thread* thread_;
+ size_t current_top_frame_;
+ size_t old_disable_frame_pop_depth_;
+};
+// We want to prevent the use of PopFrame when reporting either of these events.
+template <ArtJvmtiEvent kEvent>
+inline void EventHandler::DispatchClassLoadOrPrepareEvent(art::Thread* thread,
+ JNIEnv* jnienv,
+ jthread jni_thread,
+ jclass klass) const {
+ ScopedDisablePopFrame sdpf(thread);
+ art::ScopedThreadStateChange stsc(thread, art::ThreadState::kNative);
+ std::vector<impl::EventHandlerFunc<kEvent>> events = CollectEvents<kEvent>(thread,
+ jnienv,
+ jni_thread,
+ klass);
+
+ for (auto event : events) {
+ ExecuteCallback<kEvent>(event, jnienv, jni_thread, klass);
+ }
+}
+
+template <>
+inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassLoad>(art::Thread* thread,
+ JNIEnv* jnienv,
+ jthread jni_thread,
+ jclass klass) const {
+ DispatchClassLoadOrPrepareEvent<ArtJvmtiEvent::kClassLoad>(thread, jnienv, jni_thread, klass);
+}
+template <>
+inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kClassPrepare>(art::Thread* thread,
+ JNIEnv* jnienv,
+ jthread jni_thread,
+ jclass klass) const {
+ DispatchClassLoadOrPrepareEvent<ArtJvmtiEvent::kClassPrepare>(thread, jnienv, jni_thread, klass);
+}
+
// Need to give a custom specialization for NativeMethodBind since it has to deal with an out
// variable.
template <>
@@ -553,6 +617,7 @@ inline bool EventHandler::NeedsEventUpdate(ArtJvmTiEnv* env,
: ArtJvmtiEvent::kClassFileLoadHookRetransformable;
return (added && caps.can_access_local_variables == 1) ||
caps.can_generate_breakpoint_events == 1 ||
+ caps.can_pop_frame == 1 ||
(caps.can_retransform_classes == 1 &&
IsEventEnabledAnywhere(event) &&
env->event_masks.IsEnabledAnywhere(event));
@@ -573,6 +638,11 @@ inline void EventHandler::HandleChangedCapabilities(ArtJvmTiEnv* env,
if (caps.can_generate_breakpoint_events == 1) {
HandleBreakpointEventsChanged(added);
}
+ if (caps.can_pop_frame == 1 && added) {
+ // TODO We should keep track of how many of these have been enabled and remove it if there are
+ // no more possible users. This isn't expected to be too common.
+ art::Runtime::Current()->SetNonStandardExitsEnabled();
+ }
}
}
diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h
index bf12cb191e..9f91a08b8b 100644
--- a/openjdkjvmti/events.h
+++ b/openjdkjvmti/events.h
@@ -301,6 +301,13 @@ class EventHandler {
unsigned char** new_class_data) const
REQUIRES(!envs_lock_);
+ template <ArtJvmtiEvent kEvent>
+ ALWAYS_INLINE inline void DispatchClassLoadOrPrepareEvent(art::Thread* thread,
+ JNIEnv* jnienv,
+ jthread jni_thread,
+ jclass klass) const
+ REQUIRES(!envs_lock_);
+
void HandleEventType(ArtJvmtiEvent event, bool enable);
void HandleLocalAccessCapabilityAdded();
void HandleBreakpointEventsChanged(bool enable);
diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc
index 220ad22be5..5a98755c67 100644
--- a/openjdkjvmti/ti_stack.cc
+++ b/openjdkjvmti/ti_stack.cc
@@ -112,6 +112,23 @@ struct GetStackTraceVisitor : public art::StackVisitor {
size_t stop;
};
+art::ShadowFrame* FindFrameAtDepthVisitor::GetOrCreateShadowFrame(bool* created_frame) {
+ art::ShadowFrame* cur = GetCurrentShadowFrame();
+ if (cur == nullptr) {
+ *created_frame = true;
+ art::ArtMethod* method = GetMethod();
+ const uint16_t num_regs = method->DexInstructionData().RegistersSize();
+ cur = GetThread()->FindOrCreateDebuggerShadowFrame(GetFrameId(),
+ num_regs,
+ method,
+ GetDexPc());
+ DCHECK(cur != nullptr);
+ } else {
+ *created_frame = false;
+ }
+ return cur;
+}
+
template <typename FrameFn>
GetStackTraceVisitor<FrameFn> MakeStackTraceVisitor(art::Thread* thread_in,
size_t start,
@@ -1065,16 +1082,7 @@ jvmtiError StackUtil::NotifyFramePop(jvmtiEnv* env, jthread thread, jint depth)
// From here we are sure to succeed.
bool needs_instrument = false;
// Get/create a shadow frame
- art::ShadowFrame* shadow_frame = visitor.GetCurrentShadowFrame();
- if (shadow_frame == nullptr) {
- needs_instrument = true;
- const size_t frame_id = visitor.GetFrameId();
- const uint16_t num_regs = method->DexInstructionData().RegistersSize();
- shadow_frame = target->FindOrCreateDebuggerShadowFrame(frame_id,
- num_regs,
- method,
- visitor.GetDexPc());
- }
+ art::ShadowFrame* shadow_frame = visitor.GetOrCreateShadowFrame(&needs_instrument);
{
art::WriterMutexLock lk(self, tienv->event_info_mutex_);
// Mark shadow frame as needs_notify_pop_
@@ -1089,4 +1097,88 @@ jvmtiError StackUtil::NotifyFramePop(jvmtiEnv* env, jthread thread, jint depth)
} while (true);
}
+jvmtiError StackUtil::PopFrame(jvmtiEnv* env ATTRIBUTE_UNUSED, jthread thread) {
+ art::Thread* self = art::Thread::Current();
+ art::Thread* target;
+ do {
+ ThreadUtil::SuspendCheck(self);
+ art::MutexLock ucsl_mu(self, *art::Locks::user_code_suspension_lock_);
+ // Make sure we won't be suspended in the middle of holding the thread_suspend_count_lock_ by a
+ // user-code suspension. We retry and do another SuspendCheck to clear this.
+ if (ThreadUtil::WouldSuspendForUserCodeLocked(self)) {
+ continue;
+ }
+ // From now on we know we cannot get suspended by user-code.
+ // NB This does a SuspendCheck (during thread state change) so we need to make sure we don't
+ // have the 'suspend_lock' locked here.
+ art::ScopedObjectAccess soa(self);
+ art::MutexLock tll_mu(self, *art::Locks::thread_list_lock_);
+ jvmtiError err = ERR(INTERNAL);
+ if (!ThreadUtil::GetAliveNativeThread(thread, soa, &target, &err)) {
+ return err;
+ }
+ {
+ art::MutexLock tscl_mu(self, *art::Locks::thread_suspend_count_lock_);
+ if (target == self || target->GetUserCodeSuspendCount() == 0) {
+ // We cannot be the current thread for this function.
+ return ERR(THREAD_NOT_SUSPENDED);
+ }
+ }
+ JvmtiGlobalTLSData* tls_data = ThreadUtil::GetGlobalTLSData(target);
+ constexpr art::StackVisitor::StackWalkKind kWalkKind =
+ art::StackVisitor::StackWalkKind::kIncludeInlinedFrames;
+ if (tls_data != nullptr &&
+ tls_data->disable_pop_frame_depth != JvmtiGlobalTLSData::kNoDisallowedPopFrame &&
+ tls_data->disable_pop_frame_depth == art::StackVisitor::ComputeNumFrames(target,
+ kWalkKind)) {
+ LOG(WARNING) << "Disallowing frame pop due to in-progress class-load/prepare. Frame at depth "
+ << tls_data->disable_pop_frame_depth << " was marked as un-poppable by the "
+ << "jvmti plugin. See b/117615146 for more information.";
+ return ERR(OPAQUE_FRAME);
+ }
+ // We hold the user_code_suspension_lock_ so the target thread is staying suspended until we are
+ // done.
+ std::unique_ptr<art::Context> context(art::Context::Create());
+ FindFrameAtDepthVisitor final_frame(target, context.get(), 0);
+ FindFrameAtDepthVisitor penultimate_frame(target, context.get(), 1);
+ final_frame.WalkStack();
+ penultimate_frame.WalkStack();
+
+ if (!final_frame.FoundFrame() || !penultimate_frame.FoundFrame()) {
+ // Cannot do it if there is only one frame!
+ return ERR(NO_MORE_FRAMES);
+ }
+
+ art::ArtMethod* called_method = final_frame.GetMethod();
+ art::ArtMethod* calling_method = penultimate_frame.GetMethod();
+ if (calling_method->IsNative() || called_method->IsNative()) {
+ return ERR(OPAQUE_FRAME);
+ }
+ // From here we are sure to succeed.
+
+ // Get/create a shadow frame
+ bool created_final_frame = false;
+ bool created_penultimate_frame = false;
+ art::ShadowFrame* called_shadow_frame =
+ final_frame.GetOrCreateShadowFrame(&created_final_frame);
+ art::ShadowFrame* calling_shadow_frame =
+ penultimate_frame.GetOrCreateShadowFrame(&created_penultimate_frame);
+
+ CHECK_NE(called_shadow_frame, calling_shadow_frame)
+ << "Frames at different depths not different!";
+
+ // Tell the shadow-frame to return immediately and skip all exit events.
+ called_shadow_frame->SetForcePopFrame(true);
+ calling_shadow_frame->SetForceRetryInstruction(true);
+
+ // Make sure can we will go to the interpreter and use the shadow frames. The early return for
+ // the final frame will force everything to the interpreter so we only need to instrument if it
+ // was not present.
+ if (created_final_frame) {
+ DeoptManager::Get()->DeoptimizeThread(target);
+ }
+ return OK;
+ } while (true);
+}
+
} // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_stack.h b/openjdkjvmti/ti_stack.h
index b41fa4bf5a..55c4269086 100644
--- a/openjdkjvmti/ti_stack.h
+++ b/openjdkjvmti/ti_stack.h
@@ -81,6 +81,8 @@ class StackUtil {
jobject** owned_monitors_ptr);
static jvmtiError NotifyFramePop(jvmtiEnv* env, jthread thread, jint depth);
+
+ static jvmtiError PopFrame(jvmtiEnv* env, jthread thread);
};
struct FindFrameAtDepthVisitor : art::StackVisitor {
@@ -110,6 +112,9 @@ struct FindFrameAtDepthVisitor : art::StackVisitor {
}
}
+ art::ShadowFrame* GetOrCreateShadowFrame(/*out*/bool* created_frame)
+ REQUIRES_SHARED(art::Locks::mutator_lock_);
+
private:
bool found_frame_;
size_t cnt_;
diff --git a/openjdkjvmti/ti_thread.cc b/openjdkjvmti/ti_thread.cc
index b54c77da50..a0e5b5c926 100644
--- a/openjdkjvmti/ti_thread.cc
+++ b/openjdkjvmti/ti_thread.cc
@@ -623,18 +623,10 @@ jvmtiError ThreadUtil::GetAllThreads(jvmtiEnv* env,
return ERR(NONE);
}
-// The struct that we store in the art::Thread::custom_tls_ that maps the jvmtiEnvs to the data
-// stored with that thread. This is needed since different jvmtiEnvs are not supposed to share TLS
-// data but we only have a single slot in Thread objects to store data.
-struct JvmtiGlobalTLSData : public art::TLSData {
- std::unordered_map<jvmtiEnv*, const void*> data GUARDED_BY(art::Locks::thread_list_lock_);
-};
-
static void RemoveTLSData(art::Thread* target, void* ctx) REQUIRES(art::Locks::thread_list_lock_) {
jvmtiEnv* env = reinterpret_cast<jvmtiEnv*>(ctx);
art::Locks::thread_list_lock_->AssertHeld(art::Thread::Current());
- JvmtiGlobalTLSData* global_tls =
- reinterpret_cast<JvmtiGlobalTLSData*>(target->GetCustomTLS(kJvmtiTlsKey));
+ JvmtiGlobalTLSData* global_tls = ThreadUtil::GetGlobalTLSData(target);
if (global_tls != nullptr) {
global_tls->data.erase(env);
}
@@ -657,19 +649,27 @@ jvmtiError ThreadUtil::SetThreadLocalStorage(jvmtiEnv* env, jthread thread, cons
return err;
}
- JvmtiGlobalTLSData* global_tls =
- reinterpret_cast<JvmtiGlobalTLSData*>(target->GetCustomTLS(kJvmtiTlsKey));
- if (global_tls == nullptr) {
- // Synchronized using thread_list_lock_ to prevent racing sets.
- target->SetCustomTLS(kJvmtiTlsKey, new JvmtiGlobalTLSData);
- global_tls = reinterpret_cast<JvmtiGlobalTLSData*>(target->GetCustomTLS(kJvmtiTlsKey));
- }
+ JvmtiGlobalTLSData* global_tls = GetOrCreateGlobalTLSData(target);
global_tls->data[env] = data;
return ERR(NONE);
}
+JvmtiGlobalTLSData* ThreadUtil::GetOrCreateGlobalTLSData(art::Thread* thread) {
+ JvmtiGlobalTLSData* data = GetGlobalTLSData(thread);
+ if (data != nullptr) {
+ return data;
+ } else {
+ thread->SetCustomTLS(kJvmtiTlsKey, new JvmtiGlobalTLSData);
+ return GetGlobalTLSData(thread);
+ }
+}
+
+JvmtiGlobalTLSData* ThreadUtil::GetGlobalTLSData(art::Thread* thread) {
+ return reinterpret_cast<JvmtiGlobalTLSData*>(thread->GetCustomTLS(kJvmtiTlsKey));
+}
+
jvmtiError ThreadUtil::GetThreadLocalStorage(jvmtiEnv* env,
jthread thread,
void** data_ptr) {
@@ -686,8 +686,7 @@ jvmtiError ThreadUtil::GetThreadLocalStorage(jvmtiEnv* env,
return err;
}
- JvmtiGlobalTLSData* global_tls =
- reinterpret_cast<JvmtiGlobalTLSData*>(target->GetCustomTLS(kJvmtiTlsKey));
+ JvmtiGlobalTLSData* global_tls = GetGlobalTLSData(target);
if (global_tls == nullptr) {
*data_ptr = nullptr;
return OK;
diff --git a/openjdkjvmti/ti_thread.h b/openjdkjvmti/ti_thread.h
index c6b6af1035..39f1f0725c 100644
--- a/openjdkjvmti/ti_thread.h
+++ b/openjdkjvmti/ti_thread.h
@@ -32,11 +32,14 @@
#ifndef ART_OPENJDKJVMTI_TI_THREAD_H_
#define ART_OPENJDKJVMTI_TI_THREAD_H_
+#include <unordered_map>
+
#include "jni.h"
#include "jvmti.h"
#include "base/macros.h"
#include "base/mutex.h"
+#include "thread.h"
namespace art {
class ArtField;
@@ -49,6 +52,18 @@ namespace openjdkjvmti {
class EventHandler;
+// The struct that we store in the art::Thread::custom_tls_ that maps the jvmtiEnvs to the data
+// stored with that thread. This is needed since different jvmtiEnvs are not supposed to share TLS
+// data but we only have a single slot in Thread objects to store data.
+struct JvmtiGlobalTLSData : public art::TLSData {
+ std::unordered_map<jvmtiEnv*, const void*> data GUARDED_BY(art::Locks::thread_list_lock_);
+
+ // The depth of the last frame where popping using PopFrame it is not allowed. It is set to
+ // kNoDisallowedPopFrame if all frames can be popped. See b/117615146 for more information.
+ static constexpr size_t kNoDisallowedPopFrame = -1;
+ size_t disable_pop_frame_depth = kNoDisallowedPopFrame;
+};
+
class ThreadUtil {
public:
static void Register(EventHandler* event_handler);
@@ -134,6 +149,11 @@ class ThreadUtil {
REQUIRES(!art::Locks::user_code_suspension_lock_,
!art::Locks::thread_suspend_count_lock_);
+ static JvmtiGlobalTLSData* GetGlobalTLSData(art::Thread* thread)
+ REQUIRES(art::Locks::thread_list_lock_);
+ static JvmtiGlobalTLSData* GetOrCreateGlobalTLSData(art::Thread* thread)
+ REQUIRES(art::Locks::thread_list_lock_);
+
private:
// We need to make sure only one thread tries to suspend threads at a time so we can get the
// 'suspend-only-once' behavior the spec requires. Internally, ART considers suspension to be a
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 7372056935..33ad987ad6 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -639,6 +639,7 @@ art_cc_test {
],
header_libs: [
"art_cmdlineparser_headers", // For parsed_options_test.
+ "cpp-define-generator-definitions",
],
include_dirs: [
"external/zlib",
diff --git a/runtime/arch/arch_test.cc b/runtime/arch/arch_test.cc
index d4dbbf9541..dcc3affb6b 100644
--- a/runtime/arch/arch_test.cc
+++ b/runtime/arch/arch_test.cc
@@ -17,30 +17,16 @@
#include <stdint.h>
#include "art_method-inl.h"
+#include "asm_defines.h"
#include "base/callee_save_type.h"
#include "entrypoints/quick/callee_save_frame.h"
#include "common_runtime_test.h"
#include "quick/quick_method_frame_info.h"
-// asm_support.h declares tests next to the #defines. We use asm_support_check.h to (safely)
-// generate CheckAsmSupportOffsetsAndSizes using gtest's EXPECT for the tests. We also use the
-// RETURN_TYPE, HEADER and FOOTER defines from asm_support_check.h to try to ensure that any
-// tests are actually generated.
-
-// Let CheckAsmSupportOffsetsAndSizes return a size_t (the count).
-#define ASM_SUPPORT_CHECK_RETURN_TYPE size_t
-
-// Declare the counter that will be updated per test.
-#define ASM_SUPPORT_CHECK_HEADER size_t count = 0;
-
-// Use EXPECT_EQ for tests, and increment the counter.
-#define ADD_TEST_EQ(x, y) EXPECT_EQ(x, y); count++;
-
-// Return the counter at the end of CheckAsmSupportOffsetsAndSizes.
-#define ASM_SUPPORT_CHECK_FOOTER return count;
-
-// Generate CheckAsmSupportOffsetsAndSizes().
-#include "asm_support_check.h"
+// Static asserts to check the values of generated #defines for assembly.
+#define ASM_DEFINE(NAME, EXPR) static_assert((NAME) == (EXPR), "Unexpected value of " #NAME);
+#include "asm_defines.def"
+#undef ASM_DEFINE
namespace art {
@@ -60,11 +46,6 @@ class ArchTest : public CommonRuntimeTest {
}
};
-TEST_F(ArchTest, CheckCommonOffsetsAndSizes) {
- size_t test_count = CheckAsmSupportOffsetsAndSizes();
- EXPECT_GT(test_count, 0u);
-}
-
// Grab architecture specific constants.
namespace arm {
#include "arch/arm/asm_support_arm.h"
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 5247a0edc2..eac985685d 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -20,216 +20,7 @@
#include "heap_poisoning.h"
#include "read_barrier_config.h"
-// To generate tests related to the constants in this header, either define ADD_TEST_EQ before
-// including, or use asm_support_check.h.
-#ifndef ADD_TEST_EQ // Allow #include-r to replace with their own.
-#define DEFINED_ADD_TEST_EQ 1
-#define ADD_TEST_EQ(x, y)
-#endif
-
-// Rounds the value n up to the nearest multiple of sz. sz must be a multiple of two.
-#define ALIGN_UP(n, sz) (((n) + (sz - 1)) & ~((sz) - 1))
-
-#if defined(__LP64__)
-#define POINTER_SIZE_SHIFT 3
-#define POINTER_SIZE art::PointerSize::k64
-#else
-#define POINTER_SIZE_SHIFT 2
-#define POINTER_SIZE art::PointerSize::k32
-#endif
-ADD_TEST_EQ(static_cast<size_t>(1U << POINTER_SIZE_SHIFT),
- static_cast<size_t>(__SIZEOF_POINTER__))
-
-// Import platform-independent constant defines from our autogenerated list.
-// Export new defines (for assembly use) by editing cpp-define-generator def files.
-#define DEFINE_CHECK_EQ ADD_TEST_EQ
-#include "asm_support_gen.h"
-#undef DEFINE_CHECK_EQ
-
-// Offset of field Thread::tlsPtr_.exception.
-#define THREAD_EXCEPTION_OFFSET (THREAD_CARD_TABLE_OFFSET + __SIZEOF_POINTER__)
-ADD_TEST_EQ(THREAD_EXCEPTION_OFFSET,
- art::Thread::ExceptionOffset<POINTER_SIZE>().Int32Value())
-
-// Offset of field Thread::tlsPtr_.managed_stack.top_quick_frame_.
-#define THREAD_TOP_QUICK_FRAME_OFFSET (THREAD_CARD_TABLE_OFFSET + (3 * __SIZEOF_POINTER__))
-ADD_TEST_EQ(THREAD_TOP_QUICK_FRAME_OFFSET,
- art::Thread::TopOfManagedStackOffset<POINTER_SIZE>().Int32Value())
-
-// Offset of field Thread::tlsPtr_.self.
-#define THREAD_SELF_OFFSET (THREAD_CARD_TABLE_OFFSET + (9 * __SIZEOF_POINTER__))
-ADD_TEST_EQ(THREAD_SELF_OFFSET,
- art::Thread::SelfOffset<POINTER_SIZE>().Int32Value())
-
-// Offset of field Thread::tlsPtr_.thread_local_pos.
-#define THREAD_LOCAL_POS_OFFSET (THREAD_CARD_TABLE_OFFSET + 34 * __SIZEOF_POINTER__)
-ADD_TEST_EQ(THREAD_LOCAL_POS_OFFSET,
- art::Thread::ThreadLocalPosOffset<POINTER_SIZE>().Int32Value())
-// Offset of field Thread::tlsPtr_.thread_local_end.
-#define THREAD_LOCAL_END_OFFSET (THREAD_LOCAL_POS_OFFSET + __SIZEOF_POINTER__)
-ADD_TEST_EQ(THREAD_LOCAL_END_OFFSET,
- art::Thread::ThreadLocalEndOffset<POINTER_SIZE>().Int32Value())
-// Offset of field Thread::tlsPtr_.thread_local_objects.
-#define THREAD_LOCAL_OBJECTS_OFFSET (THREAD_LOCAL_END_OFFSET + 2 * __SIZEOF_POINTER__)
-ADD_TEST_EQ(THREAD_LOCAL_OBJECTS_OFFSET,
- art::Thread::ThreadLocalObjectsOffset<POINTER_SIZE>().Int32Value())
-
-// Offset of field Thread::tlsPtr_.mterp_current_ibase.
-#define THREAD_CURRENT_IBASE_OFFSET \
- (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 166) * __SIZEOF_POINTER__)
-ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET,
- art::Thread::MterpCurrentIBaseOffset<POINTER_SIZE>().Int32Value())
-// Offset of field Thread::tlsPtr_.mterp_default_ibase.
-#define THREAD_DEFAULT_IBASE_OFFSET (THREAD_CURRENT_IBASE_OFFSET + __SIZEOF_POINTER__)
-ADD_TEST_EQ(THREAD_DEFAULT_IBASE_OFFSET,
- art::Thread::MterpDefaultIBaseOffset<POINTER_SIZE>().Int32Value())
-// Offset of field Thread::tlsPtr_.mterp_alt_ibase.
-#define THREAD_ALT_IBASE_OFFSET (THREAD_DEFAULT_IBASE_OFFSET + __SIZEOF_POINTER__)
-ADD_TEST_EQ(THREAD_ALT_IBASE_OFFSET,
- art::Thread::MterpAltIBaseOffset<POINTER_SIZE>().Int32Value())
-// Offset of field Thread::tlsPtr_.rosalloc_runs.
-#define THREAD_ROSALLOC_RUNS_OFFSET (THREAD_ALT_IBASE_OFFSET + __SIZEOF_POINTER__)
-ADD_TEST_EQ(THREAD_ROSALLOC_RUNS_OFFSET,
- art::Thread::RosAllocRunsOffset<POINTER_SIZE>().Int32Value())
-// Offset of field Thread::tlsPtr_.thread_local_alloc_stack_top.
-#define THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET (THREAD_ROSALLOC_RUNS_OFFSET + 16 * __SIZEOF_POINTER__)
-ADD_TEST_EQ(THREAD_LOCAL_ALLOC_STACK_TOP_OFFSET,
- art::Thread::ThreadLocalAllocStackTopOffset<POINTER_SIZE>().Int32Value())
-// Offset of field Thread::tlsPtr_.thread_local_alloc_stack_end.
-#define THREAD_LOCAL_ALLOC_STACK_END_OFFSET (THREAD_ROSALLOC_RUNS_OFFSET + 17 * __SIZEOF_POINTER__)
-ADD_TEST_EQ(THREAD_LOCAL_ALLOC_STACK_END_OFFSET,
- art::Thread::ThreadLocalAllocStackEndOffset<POINTER_SIZE>().Int32Value())
-// Offset of field Thread::interpreter_cache_. This is aligned on a 16 byte boundary so we need to
-// round up depending on the size of tlsPtr_.
-#define THREAD_INTERPRETER_CACHE_OFFSET \
- (ALIGN_UP((THREAD_CARD_TABLE_OFFSET + 301 * __SIZEOF_POINTER__), 16))
-ADD_TEST_EQ(THREAD_INTERPRETER_CACHE_OFFSET,
- art::Thread::InterpreterCacheOffset<POINTER_SIZE>().Int32Value())
-
-// Offsets within ShadowFrame.
-#define SHADOWFRAME_LINK_OFFSET 0
-ADD_TEST_EQ(SHADOWFRAME_LINK_OFFSET,
- static_cast<int32_t>(art::ShadowFrame::LinkOffset()))
-#define SHADOWFRAME_METHOD_OFFSET (SHADOWFRAME_LINK_OFFSET + 1 * __SIZEOF_POINTER__)
-ADD_TEST_EQ(SHADOWFRAME_METHOD_OFFSET,
- static_cast<int32_t>(art::ShadowFrame::MethodOffset()))
-#define SHADOWFRAME_RESULT_REGISTER_OFFSET (SHADOWFRAME_LINK_OFFSET + 2 * __SIZEOF_POINTER__)
-ADD_TEST_EQ(SHADOWFRAME_RESULT_REGISTER_OFFSET,
- static_cast<int32_t>(art::ShadowFrame::ResultRegisterOffset()))
-#define SHADOWFRAME_DEX_PC_PTR_OFFSET (SHADOWFRAME_LINK_OFFSET + 3 * __SIZEOF_POINTER__)
-ADD_TEST_EQ(SHADOWFRAME_DEX_PC_PTR_OFFSET,
- static_cast<int32_t>(art::ShadowFrame::DexPCPtrOffset()))
-#define SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET (SHADOWFRAME_LINK_OFFSET + 4 * __SIZEOF_POINTER__)
-ADD_TEST_EQ(SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET,
- static_cast<int32_t>(art::ShadowFrame::DexInstructionsOffset()))
-#define SHADOWFRAME_LOCK_COUNT_DATA_OFFSET (SHADOWFRAME_LINK_OFFSET + 5 * __SIZEOF_POINTER__)
-ADD_TEST_EQ(SHADOWFRAME_LOCK_COUNT_DATA_OFFSET,
- static_cast<int32_t>(art::ShadowFrame::LockCountDataOffset()))
-#define SHADOWFRAME_NUMBER_OF_VREGS_OFFSET (SHADOWFRAME_LINK_OFFSET + 6 * __SIZEOF_POINTER__)
-ADD_TEST_EQ(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET,
- static_cast<int32_t>(art::ShadowFrame::NumberOfVRegsOffset()))
-#define SHADOWFRAME_DEX_PC_OFFSET (SHADOWFRAME_NUMBER_OF_VREGS_OFFSET + 4)
-ADD_TEST_EQ(SHADOWFRAME_DEX_PC_OFFSET,
- static_cast<int32_t>(art::ShadowFrame::DexPCOffset()))
-#define SHADOWFRAME_CACHED_HOTNESS_COUNTDOWN_OFFSET (SHADOWFRAME_NUMBER_OF_VREGS_OFFSET + 8)
-ADD_TEST_EQ(SHADOWFRAME_CACHED_HOTNESS_COUNTDOWN_OFFSET,
- static_cast<int32_t>(art::ShadowFrame::CachedHotnessCountdownOffset()))
-#define SHADOWFRAME_HOTNESS_COUNTDOWN_OFFSET (SHADOWFRAME_NUMBER_OF_VREGS_OFFSET + 10)
-ADD_TEST_EQ(SHADOWFRAME_HOTNESS_COUNTDOWN_OFFSET,
- static_cast<int32_t>(art::ShadowFrame::HotnessCountdownOffset()))
-#define SHADOWFRAME_VREGS_OFFSET (SHADOWFRAME_NUMBER_OF_VREGS_OFFSET + 16)
-ADD_TEST_EQ(SHADOWFRAME_VREGS_OFFSET,
- static_cast<int32_t>(art::ShadowFrame::VRegsOffset()))
-
-#if defined(USE_BROOKS_READ_BARRIER)
-#define MIRROR_OBJECT_HEADER_SIZE 16
-#else
-#define MIRROR_OBJECT_HEADER_SIZE 8
-#endif
-ADD_TEST_EQ(size_t(MIRROR_OBJECT_HEADER_SIZE), sizeof(art::mirror::Object))
-
-// Offsets within java.lang.Class.
-#define MIRROR_CLASS_COMPONENT_TYPE_OFFSET (4 + MIRROR_OBJECT_HEADER_SIZE)
-ADD_TEST_EQ(MIRROR_CLASS_COMPONENT_TYPE_OFFSET,
- art::mirror::Class::ComponentTypeOffset().Int32Value())
-#define MIRROR_CLASS_IF_TABLE_OFFSET (16 + MIRROR_OBJECT_HEADER_SIZE)
-ADD_TEST_EQ(MIRROR_CLASS_IF_TABLE_OFFSET,
- art::mirror::Class::IfTableOffset().Int32Value())
-#define MIRROR_CLASS_ACCESS_FLAGS_OFFSET (56 + MIRROR_OBJECT_HEADER_SIZE)
-ADD_TEST_EQ(MIRROR_CLASS_ACCESS_FLAGS_OFFSET,
- art::mirror::Class::AccessFlagsOffset().Int32Value())
-#define MIRROR_CLASS_OBJECT_SIZE_OFFSET (88 + MIRROR_OBJECT_HEADER_SIZE)
-ADD_TEST_EQ(MIRROR_CLASS_OBJECT_SIZE_OFFSET,
- art::mirror::Class::ObjectSizeOffset().Int32Value())
-#define MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET (92 + MIRROR_OBJECT_HEADER_SIZE)
-ADD_TEST_EQ(MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET,
- art::mirror::Class::ObjectSizeAllocFastPathOffset().Int32Value())
-#define MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET (96 + MIRROR_OBJECT_HEADER_SIZE)
-ADD_TEST_EQ(MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET,
- art::mirror::Class::PrimitiveTypeOffset().Int32Value())
-#define MIRROR_CLASS_STATUS_OFFSET (104 + MIRROR_OBJECT_HEADER_SIZE)
-ADD_TEST_EQ(MIRROR_CLASS_STATUS_OFFSET,
- art::mirror::Class::StatusOffset().Int32Value())
-
-#define PRIMITIVE_TYPE_SIZE_SHIFT_SHIFT 16
-ADD_TEST_EQ(PRIMITIVE_TYPE_SIZE_SHIFT_SHIFT,
- static_cast<int>(art::mirror::Class::kPrimitiveTypeSizeShiftShift))
-
-// Array offsets.
-#define MIRROR_ARRAY_LENGTH_OFFSET MIRROR_OBJECT_HEADER_SIZE
-ADD_TEST_EQ(MIRROR_ARRAY_LENGTH_OFFSET, art::mirror::Array::LengthOffset().Int32Value())
-
-#define MIRROR_CHAR_ARRAY_DATA_OFFSET (4 + MIRROR_OBJECT_HEADER_SIZE)
-ADD_TEST_EQ(MIRROR_CHAR_ARRAY_DATA_OFFSET,
- art::mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value())
-
-#define MIRROR_BOOLEAN_ARRAY_DATA_OFFSET MIRROR_CHAR_ARRAY_DATA_OFFSET
-ADD_TEST_EQ(MIRROR_BOOLEAN_ARRAY_DATA_OFFSET,
- art::mirror::Array::DataOffset(sizeof(uint8_t)).Int32Value())
-
-#define MIRROR_BYTE_ARRAY_DATA_OFFSET MIRROR_CHAR_ARRAY_DATA_OFFSET
-ADD_TEST_EQ(MIRROR_BYTE_ARRAY_DATA_OFFSET,
- art::mirror::Array::DataOffset(sizeof(int8_t)).Int32Value())
-
-#define MIRROR_SHORT_ARRAY_DATA_OFFSET MIRROR_CHAR_ARRAY_DATA_OFFSET
-ADD_TEST_EQ(MIRROR_SHORT_ARRAY_DATA_OFFSET,
- art::mirror::Array::DataOffset(sizeof(int16_t)).Int32Value())
-
-#define MIRROR_INT_ARRAY_DATA_OFFSET MIRROR_CHAR_ARRAY_DATA_OFFSET
-ADD_TEST_EQ(MIRROR_INT_ARRAY_DATA_OFFSET,
- art::mirror::Array::DataOffset(sizeof(int32_t)).Int32Value())
-
-#define MIRROR_WIDE_ARRAY_DATA_OFFSET (8 + MIRROR_OBJECT_HEADER_SIZE)
-ADD_TEST_EQ(MIRROR_WIDE_ARRAY_DATA_OFFSET,
- art::mirror::Array::DataOffset(sizeof(uint64_t)).Int32Value())
-
-#define MIRROR_OBJECT_ARRAY_DATA_OFFSET (4 + MIRROR_OBJECT_HEADER_SIZE)
-ADD_TEST_EQ(MIRROR_OBJECT_ARRAY_DATA_OFFSET,
- art::mirror::Array::DataOffset(
- sizeof(art::mirror::HeapReference<art::mirror::Object>)).Int32Value())
-
-#define MIRROR_OBJECT_ARRAY_COMPONENT_SIZE 4
-ADD_TEST_EQ(static_cast<size_t>(MIRROR_OBJECT_ARRAY_COMPONENT_SIZE),
- sizeof(art::mirror::HeapReference<art::mirror::Object>))
-
-#define MIRROR_LONG_ARRAY_DATA_OFFSET (8 + MIRROR_OBJECT_HEADER_SIZE)
-ADD_TEST_EQ(MIRROR_LONG_ARRAY_DATA_OFFSET,
- art::mirror::Array::DataOffset(sizeof(uint64_t)).Int32Value())
-
-// Offsets within java.lang.String.
-#define MIRROR_STRING_COUNT_OFFSET MIRROR_OBJECT_HEADER_SIZE
-ADD_TEST_EQ(MIRROR_STRING_COUNT_OFFSET, art::mirror::String::CountOffset().Int32Value())
-
-#define MIRROR_STRING_VALUE_OFFSET (8 + MIRROR_OBJECT_HEADER_SIZE)
-ADD_TEST_EQ(MIRROR_STRING_VALUE_OFFSET, art::mirror::String::ValueOffset().Int32Value())
-
-// String compression feature.
-#define STRING_COMPRESSION_FEATURE 1
-ADD_TEST_EQ(STRING_COMPRESSION_FEATURE, art::mirror::kUseStringCompression);
-
-#ifdef DEFINED_ADD_TEST_EQ
-#undef ADD_TEST_EQ
-#undef DEFINED_ADD_TEST_EQ
-#endif
+// Automatically generated header based on the asm_defines.def file.
+#include "asm_defines.h"
#endif // ART_RUNTIME_ASM_SUPPORT_H_
diff --git a/runtime/asm_support_check.h b/runtime/asm_support_check.h
deleted file mode 100644
index 3163506e72..0000000000
--- a/runtime/asm_support_check.h
+++ /dev/null
@@ -1,65 +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_ASM_SUPPORT_CHECK_H_
-#define ART_RUNTIME_ASM_SUPPORT_CHECK_H_
-
-#include "art_method.h"
-#include "base/bit_utils.h"
-#include "base/callee_save_type.h"
-#include "gc/accounting/card_table.h"
-#include "gc/allocator/rosalloc.h"
-#include "gc/heap.h"
-#include "jit/jit.h"
-#include "lock_word.h"
-#include "mirror/class.h"
-#include "mirror/dex_cache.h"
-#include "mirror/string.h"
-#include "runtime.h"
-#include "stack.h"
-#include "thread.h"
-#include "utils/dex_cache_arrays_layout.h"
-
-#ifndef ADD_TEST_EQ
-#define ADD_TEST_EQ(x, y) CHECK_EQ(x, y);
-#endif
-
-#ifndef ASM_SUPPORT_CHECK_RETURN_TYPE
-#define ASM_SUPPORT_CHECK_RETURN_TYPE void
-#endif
-
-// Prepare for re-include of asm_support.h.
-#ifdef ART_RUNTIME_ASM_SUPPORT_H_
-#undef ART_RUNTIME_ASM_SUPPORT_H_
-#endif
-
-namespace art {
-
-static inline ASM_SUPPORT_CHECK_RETURN_TYPE CheckAsmSupportOffsetsAndSizes() {
-#ifdef ASM_SUPPORT_CHECK_HEADER
- ASM_SUPPORT_CHECK_HEADER
-#endif
-
-#include "asm_support.h"
-
-#ifdef ASM_SUPPORT_CHECK_FOOTER
- ASM_SUPPORT_CHECK_FOOTER
-#endif
-}
-
-} // namespace art
-
-#endif // ART_RUNTIME_ASM_SUPPORT_CHECK_H_
diff --git a/runtime/common_dex_operations.h b/runtime/common_dex_operations.h
index c29043e7c6..15ab5f0387 100644
--- a/runtime/common_dex_operations.h
+++ b/runtime/common_dex_operations.h
@@ -27,6 +27,7 @@
#include "dex/primitive.h"
#include "handle_scope-inl.h"
#include "instrumentation.h"
+#include "interpreter/interpreter.h"
#include "interpreter/shadow_frame.h"
#include "interpreter/unstarted_runtime.h"
#include "jvalue-inl.h"
@@ -172,6 +173,14 @@ ALWAYS_INLINE bool DoFieldPutCommon(Thread* self,
if (UNLIKELY(self->IsExceptionPending())) {
return false;
}
+ if (shadow_frame.GetForcePopFrame()) {
+ // We need to check this here since we expect that the FieldWriteEvent happens before the
+ // actual field write. If one pops the stack we should not modify the field. The next
+ // instruction will force a pop. Return true.
+ DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
+ DCHECK(interpreter::PrevFrameWillRetry(self, shadow_frame));
+ return true;
+ }
}
switch (field_type) {
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index fccfce4589..84631c377e 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -753,6 +753,7 @@ extern "C" uint64_t artQuickToInterpreterBridge(ArtMethod* method, Thread* self,
const char* shorty = non_proxy_method->GetShorty(&shorty_len);
JValue result;
+ bool force_frame_pop = false;
if (UNLIKELY(deopt_frame != nullptr)) {
HandleDeoptimization(&result, method, deopt_frame, &fragment);
@@ -788,6 +789,7 @@ extern "C" uint64_t artQuickToInterpreterBridge(ArtMethod* method, Thread* self,
}
result = interpreter::EnterInterpreterFromEntryPoint(self, accessor, shadow_frame);
+ force_frame_pop = shadow_frame->GetForcePopFrame();
}
// Pop transition.
@@ -804,12 +806,20 @@ extern "C" uint64_t artQuickToInterpreterBridge(ArtMethod* method, Thread* self,
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(!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 */
- self->GetException(),
+ force_frame_pop ? nullptr : self->GetException(),
false /* from_code */,
DeoptimizationMethodType::kDefault);
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index ae9acd6d9b..46cc79ce9c 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -573,8 +573,10 @@ class ConcurrentCopying::VerifyGrayImmuneObjectsVisitor {
if (ref != nullptr) {
if (!collector_->immune_spaces_.ContainsObject(ref.Ptr())) {
// Not immune, must be a zygote large object.
- CHECK(Runtime::Current()->GetHeap()->GetLargeObjectsSpace()->IsZygoteLargeObject(
- Thread::Current(), ref.Ptr()))
+ space::LargeObjectSpace* large_object_space =
+ Runtime::Current()->GetHeap()->GetLargeObjectsSpace();
+ CHECK(large_object_space->Contains(ref.Ptr()) &&
+ large_object_space->IsZygoteLargeObject(Thread::Current(), ref.Ptr()))
<< "Non gray object references non immune, non zygote large object "<< ref << " "
<< mirror::Object::PrettyTypeOf(ref) << " in holder " << holder << " "
<< mirror::Object::PrettyTypeOf(holder) << " offset=" << offset.Uint32Value();
@@ -2960,7 +2962,13 @@ mirror::Object* ConcurrentCopying::MarkNonMoving(Thread* const self,
// Since the mark bitmap is still filled in from last GC, we can not use that or else the
// mutator may see references to the from space. Instead, use the Baker pointer itself as
// the mark bit.
- if (ref->AtomicSetReadBarrierState(ReadBarrier::NonGrayState(), ReadBarrier::GrayState())) {
+ //
+ // We need to avoid marking objects that are on allocation stack as that will lead to a
+ // situation (after this GC cycle is finished) where some object(s) are on both allocation
+ // stack and live bitmap. This leads to visiting the same object(s) twice during a heapdump
+ // (b/117426281).
+ if (!IsOnAllocStack(ref) &&
+ ref->AtomicSetReadBarrierState(ReadBarrier::NonGrayState(), ReadBarrier::GrayState())) {
// TODO: We don't actually need to scan this object later, we just need to clear the gray
// bit.
// Also make sure the object is marked.
@@ -2969,6 +2977,8 @@ mirror::Object* ConcurrentCopying::MarkNonMoving(Thread* const self,
} else {
mark_bitmap->AtomicTestAndSet(ref);
}
+ // We don't need to mark newly allocated objects (those in allocation stack) as they can
+ // only point to to-space objects. Also, they are considered live till the next GC cycle.
PushOntoMarkStack(self, ref);
}
return ref;
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index df66061d01..2ae95dcc41 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -261,6 +261,12 @@ static inline JValue Execute(
shadow_frame.GetThisObject(accessor.InsSize()),
method,
0);
+ if (UNLIKELY(shadow_frame.GetForcePopFrame())) {
+ // The caller will retry this invoke. Just return immediately without any value.
+ DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
+ DCHECK(PrevFrameWillRetry(self, shadow_frame));
+ return JValue();
+ }
if (UNLIKELY(self->IsExceptionPending())) {
instrumentation->MethodUnwindEvent(self,
shadow_frame.GetThisObject(accessor.InsSize()),
@@ -494,8 +500,8 @@ void EnterInterpreterFromDeoptimize(Thread* self,
JValue value;
// Set value to last known result in case the shadow frame chain is empty.
value.SetJ(ret_val->GetJ());
- // Are we executing the first shadow frame?
- bool first = true;
+ // How many frames we have executed.
+ size_t frame_cnt = 0;
while (shadow_frame != nullptr) {
// We do not want to recover lock state for lock counting when deoptimizing. Currently,
// the compiler should not have compiled a method that failed structured-locking checks.
@@ -510,24 +516,30 @@ void EnterInterpreterFromDeoptimize(Thread* self,
// the instrumentation. To prevent from reporting it a second time, we simply pass a
// null Instrumentation*.
const instrumentation::Instrumentation* const instrumentation =
- first ? nullptr : Runtime::Current()->GetInstrumentation();
+ frame_cnt == 0 ? nullptr : Runtime::Current()->GetInstrumentation();
new_dex_pc = MoveToExceptionHandler(
self, *shadow_frame, instrumentation) ? shadow_frame->GetDexPC() : dex::kDexNoIndex;
} else if (!from_code) {
// Deoptimization is not called from code directly.
const Instruction* instr = &accessor.InstructionAt(dex_pc);
- if (deopt_method_type == DeoptimizationMethodType::kKeepDexPc) {
- DCHECK(first);
+ if (deopt_method_type == DeoptimizationMethodType::kKeepDexPc ||
+ shadow_frame->GetForceRetryInstruction()) {
+ DCHECK(frame_cnt == 0 || (frame_cnt == 1 && shadow_frame->GetForceRetryInstruction()))
+ << "frame_cnt: " << frame_cnt
+ << " force-retry: " << shadow_frame->GetForceRetryInstruction();
// Need to re-execute the dex instruction.
// (1) An invocation might be split into class initialization and invoke.
// In this case, the invoke should not be skipped.
// (2) A suspend check should also execute the dex instruction at the
// corresponding dex pc.
+ // If the ForceRetryInstruction bit is set this must be the second frame (the first being
+ // the one that is being popped).
DCHECK_EQ(new_dex_pc, dex_pc);
+ shadow_frame->SetForceRetryInstruction(false);
} else if (instr->Opcode() == Instruction::MONITOR_ENTER ||
instr->Opcode() == Instruction::MONITOR_EXIT) {
DCHECK(deopt_method_type == DeoptimizationMethodType::kDefault);
- DCHECK(first);
+ DCHECK_EQ(frame_cnt, 0u);
// Non-idempotent dex instruction should not be re-executed.
// On the other hand, if a MONITOR_ENTER is at the dex_pc of a suspend
// check, that MONITOR_ENTER should be executed. That case is handled
@@ -553,7 +565,7 @@ void EnterInterpreterFromDeoptimize(Thread* self,
DCHECK_EQ(new_dex_pc, dex_pc);
} else {
DCHECK(deopt_method_type == DeoptimizationMethodType::kDefault);
- DCHECK(first);
+ DCHECK_EQ(frame_cnt, 0u);
// By default, we re-execute the dex instruction since if they are not
// an invoke, so that we don't have to decode the dex instruction to move
// result into the right vreg. All slow paths have been audited to be
@@ -566,7 +578,7 @@ void EnterInterpreterFromDeoptimize(Thread* self,
} else {
// Nothing to do, the dex_pc is the one at which the code requested
// the deoptimization.
- DCHECK(first);
+ DCHECK_EQ(frame_cnt, 0u);
DCHECK_EQ(new_dex_pc, dex_pc);
}
if (new_dex_pc != dex::kDexNoIndex) {
@@ -585,7 +597,7 @@ void EnterInterpreterFromDeoptimize(Thread* self,
// and should advance dex pc past the invoke instruction.
from_code = false;
deopt_method_type = DeoptimizationMethodType::kDefault;
- first = false;
+ frame_cnt++;
}
ret_val->SetJ(value.GetJ());
}
@@ -657,5 +669,18 @@ void InitInterpreterTls(Thread* self) {
InitMterpTls(self);
}
+bool PrevFrameWillRetry(Thread* self, const ShadowFrame& frame) {
+ ShadowFrame* prev_frame = frame.GetLink();
+ if (prev_frame == nullptr) {
+ NthCallerVisitor vis(self, 1, false);
+ vis.WalkStack();
+ prev_frame = vis.GetCurrentShadowFrame();
+ if (prev_frame == nullptr) {
+ prev_frame = self->FindDebuggerShadowFrame(vis.GetFrameId());
+ }
+ }
+ return prev_frame != nullptr && prev_frame->GetForceRetryInstruction();
+}
+
} // namespace interpreter
} // namespace art
diff --git a/runtime/interpreter/interpreter.h b/runtime/interpreter/interpreter.h
index 0d43b9090a..d7e69a6755 100644
--- a/runtime/interpreter/interpreter.h
+++ b/runtime/interpreter/interpreter.h
@@ -69,6 +69,12 @@ void CheckInterpreterAsmConstants();
void InitInterpreterTls(Thread* self);
+// Returns true if the previous frame has the ForceRetryInstruction bit set. This is required for
+// ForPopFrame to work correctly since that will cause the java function return with null/0 which
+// might not be expected by the code being run.
+bool PrevFrameWillRetry(Thread* self, const ShadowFrame& frame)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
} // namespace interpreter
} // namespace art
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 60c8ef34b6..b17023208e 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -372,6 +372,12 @@ bool DoIPutQuick(const ShadowFrame& shadow_frame, const Instruction* inst, uint1
if (UNLIKELY(self->IsExceptionPending())) {
return false;
}
+ if (UNLIKELY(shadow_frame.GetForcePopFrame())) {
+ // Don't actually set the field. The next instruction will force us to pop.
+ DCHECK(Runtime::Current()->AreNonStandardExitsEnabled());
+ DCHECK(PrevFrameWillRetry(self, shadow_frame));
+ return true;
+ }
}
// Note: iput-x-quick instructions are only for non-volatile fields.
switch (field_type) {
@@ -441,6 +447,11 @@ bool MoveToExceptionHandler(Thread* self,
self->IsExceptionThrownByCurrentMethod(exception.Get())) {
// See b/65049545 for why we don't need to check to see if the exception has changed.
instrumentation->ExceptionThrownEvent(self, exception.Get());
+ if (shadow_frame.GetForcePopFrame()) {
+ // We will check in the caller for GetForcePopFrame again. We need to bail out early to
+ // prevent an ExceptionHandledEvent from also being sent before popping.
+ return true;
+ }
}
bool clear_exception = false;
uint32_t found_dex_pc = shadow_frame.GetMethod()->FindCatchBlock(
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 04935cfe42..cb64ff402a 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -24,16 +24,39 @@
#include "interpreter_common.h"
#include "jit/jit.h"
#include "jvalue-inl.h"
+#include "nth_caller_visitor.h"
#include "safe_math.h"
#include "shadow_frame-inl.h"
+#include "thread.h"
namespace art {
namespace interpreter {
+#define CHECK_FORCE_RETURN() \
+ do { \
+ if (UNLIKELY(shadow_frame.GetForcePopFrame())) { \
+ DCHECK(PrevFrameWillRetry(self, shadow_frame)) \
+ << "Pop frame forced without previous frame ready to retry instruction!"; \
+ DCHECK(Runtime::Current()->AreNonStandardExitsEnabled()); \
+ if (UNLIKELY(NeedsMethodExitEvent(instrumentation))) { \
+ SendMethodExitEvents(self, \
+ instrumentation, \
+ shadow_frame, \
+ shadow_frame.GetThisObject(accessor.InsSize()), \
+ shadow_frame.GetMethod(), \
+ inst->GetDexPc(insns), \
+ JValue()); \
+ } \
+ ctx->result = JValue(); /* Handled in caller. */ \
+ return; \
+ } \
+ } while (false)
+
#define HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(instr) \
do { \
DCHECK(self->IsExceptionPending()); \
self->AllowThreadSuspension(); \
+ CHECK_FORCE_RETURN(); \
if (!MoveToExceptionHandler(self, shadow_frame, instr)) { \
/* Structured locking is to be enforced for abnormal termination, too. */ \
DoMonitorCheckOnExit<do_assignability_check>(self, &shadow_frame); \
@@ -44,6 +67,7 @@ namespace interpreter {
ctx->result = JValue(); /* Handled in caller. */ \
return; \
} else { \
+ CHECK_FORCE_RETURN(); \
int32_t displacement = \
static_cast<int32_t>(shadow_frame.GetDexPC()) - static_cast<int32_t>(dex_pc); \
inst = inst->RelativeAt(displacement); \
@@ -52,8 +76,39 @@ namespace interpreter {
#define HANDLE_PENDING_EXCEPTION() HANDLE_PENDING_EXCEPTION_WITH_INSTRUMENTATION(instrumentation)
+#define POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_IMPL(_is_exception_pending, _next_function) \
+ do { \
+ if (UNLIKELY(shadow_frame.GetForceRetryInstruction())) { \
+ /* Don't need to do anything except clear the flag and exception. We leave the */ \
+ /* instruction the same so it will be re-executed on the next go-around. */ \
+ DCHECK(inst->IsInvoke()); \
+ shadow_frame.SetForceRetryInstruction(false); \
+ if (UNLIKELY(_is_exception_pending)) { \
+ DCHECK(self->IsExceptionPending()); \
+ if (kIsDebugBuild) { \
+ LOG(WARNING) << "Suppressing exception for instruction-retry: " \
+ << self->GetException()->Dump(); \
+ } \
+ self->ClearException(); \
+ } \
+ } else if (UNLIKELY(_is_exception_pending)) { \
+ /* Should have succeeded. */ \
+ DCHECK(!shadow_frame.GetForceRetryInstruction()); \
+ HANDLE_PENDING_EXCEPTION(); \
+ } else { \
+ inst = inst->_next_function(); \
+ } \
+ } while (false)
+
+#define POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC(_is_exception_pending) \
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_IMPL(_is_exception_pending, Next_4xx)
+#define POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(_is_exception_pending) \
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_IMPL(_is_exception_pending, Next_3xx)
+
#define POSSIBLY_HANDLE_PENDING_EXCEPTION(_is_exception_pending, _next_function) \
do { \
+ /* Should only be on invoke instructions. */ \
+ DCHECK(!shadow_frame.GetForceRetryInstruction()); \
if (UNLIKELY(_is_exception_pending)) { \
HANDLE_PENDING_EXCEPTION(); \
} else { \
@@ -67,17 +122,22 @@ namespace interpreter {
}
// Code to run before each dex instruction.
-#define PREAMBLE_SAVE(save_ref) \
+#define PREAMBLE_SAVE(save_ref) \
{ \
- if (UNLIKELY(instrumentation->HasDexPcListeners()) && \
- UNLIKELY(!DoDexPcMoveEvent(self, \
- accessor, \
- shadow_frame, \
- dex_pc, \
- instrumentation, \
- save_ref))) { \
- HANDLE_PENDING_EXCEPTION(); \
- break; \
+ /* We need to put this before & after the instrumentation to avoid having to put in a */ \
+ /* post-script macro. */ \
+ CHECK_FORCE_RETURN(); \
+ if (UNLIKELY(instrumentation->HasDexPcListeners())) { \
+ if (UNLIKELY(!DoDexPcMoveEvent(self, \
+ accessor, \
+ shadow_frame, \
+ dex_pc, \
+ instrumentation, \
+ save_ref))) { \
+ HANDLE_PENDING_EXCEPTION(); \
+ break; \
+ } \
+ CHECK_FORCE_RETURN(); \
} \
} \
do {} while (false)
@@ -181,7 +241,8 @@ NO_INLINE static bool SendMethodExitEvents(Thread* self,
const JValue& result)
REQUIRES_SHARED(Locks::mutator_lock_) {
bool had_event = false;
- if (UNLIKELY(instrumentation->HasMethodExitListeners())) {
+ // We don't send method-exit if it's a pop-frame. We still send frame_popped though.
+ if (UNLIKELY(instrumentation->HasMethodExitListeners() && !frame.GetForcePopFrame())) {
had_event = true;
instrumentation->MethodExitEvent(self, thiz.Ptr(), method, dex_pc, result);
}
@@ -221,6 +282,9 @@ ATTRIBUTE_NO_SANITIZE_ADDRESS void ExecuteSwitchImplCpp(SwitchImplContext* ctx)
uint16_t inst_data;
jit::Jit* jit = Runtime::Current()->GetJit();
+ DCHECK(!shadow_frame.GetForceRetryInstruction())
+ << "Entered interpreter from invoke without retry instruction being handled!";
+
do {
dex_pc = inst->GetDexPc(insns);
shadow_frame.SetDexPC(dex_pc);
@@ -1607,84 +1671,84 @@ ATTRIBUTE_NO_SANITIZE_ADDRESS void ExecuteSwitchImplCpp(SwitchImplContext* ctx)
PREAMBLE();
bool success = DoInvoke<kVirtual, false, do_access_check>(
self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
break;
}
case Instruction::INVOKE_VIRTUAL_RANGE: {
PREAMBLE();
bool success = DoInvoke<kVirtual, true, do_access_check>(
self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
break;
}
case Instruction::INVOKE_SUPER: {
PREAMBLE();
bool success = DoInvoke<kSuper, false, do_access_check>(
self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
break;
}
case Instruction::INVOKE_SUPER_RANGE: {
PREAMBLE();
bool success = DoInvoke<kSuper, true, do_access_check>(
self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
break;
}
case Instruction::INVOKE_DIRECT: {
PREAMBLE();
bool success = DoInvoke<kDirect, false, do_access_check>(
self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
break;
}
case Instruction::INVOKE_DIRECT_RANGE: {
PREAMBLE();
bool success = DoInvoke<kDirect, true, do_access_check>(
self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
break;
}
case Instruction::INVOKE_INTERFACE: {
PREAMBLE();
bool success = DoInvoke<kInterface, false, do_access_check>(
self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
break;
}
case Instruction::INVOKE_INTERFACE_RANGE: {
PREAMBLE();
bool success = DoInvoke<kInterface, true, do_access_check>(
self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
break;
}
case Instruction::INVOKE_STATIC: {
PREAMBLE();
bool success = DoInvoke<kStatic, false, do_access_check>(
self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
break;
}
case Instruction::INVOKE_STATIC_RANGE: {
PREAMBLE();
bool success = DoInvoke<kStatic, true, do_access_check>(
self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
break;
}
case Instruction::INVOKE_VIRTUAL_QUICK: {
PREAMBLE();
bool success = DoInvokeVirtualQuick<false>(
self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
break;
}
case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: {
PREAMBLE();
bool success = DoInvokeVirtualQuick<true>(
self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
break;
}
case Instruction::INVOKE_POLYMORPHIC: {
@@ -1692,7 +1756,7 @@ ATTRIBUTE_NO_SANITIZE_ADDRESS void ExecuteSwitchImplCpp(SwitchImplContext* ctx)
DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
bool success = DoInvokePolymorphic<false /* is_range */>(
self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_4xx);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC(!success);
break;
}
case Instruction::INVOKE_POLYMORPHIC_RANGE: {
@@ -1700,7 +1764,7 @@ ATTRIBUTE_NO_SANITIZE_ADDRESS void ExecuteSwitchImplCpp(SwitchImplContext* ctx)
DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
bool success = DoInvokePolymorphic<true /* is_range */>(
self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_4xx);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE_POLYMORPHIC(!success);
break;
}
case Instruction::INVOKE_CUSTOM: {
@@ -1708,7 +1772,7 @@ ATTRIBUTE_NO_SANITIZE_ADDRESS void ExecuteSwitchImplCpp(SwitchImplContext* ctx)
DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
bool success = DoInvokeCustom<false /* is_range */>(
self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
break;
}
case Instruction::INVOKE_CUSTOM_RANGE: {
@@ -1716,7 +1780,7 @@ ATTRIBUTE_NO_SANITIZE_ADDRESS void ExecuteSwitchImplCpp(SwitchImplContext* ctx)
DCHECK(Runtime::Current()->IsMethodHandlesEnabled());
bool success = DoInvokeCustom<true /* is_range */>(
self, shadow_frame, inst, inst_data, &result_register);
- POSSIBLY_HANDLE_PENDING_EXCEPTION(!success, Next_3xx);
+ POSSIBLY_HANDLE_PENDING_EXCEPTION_ON_INVOKE(!success);
break;
}
case Instruction::NEG_INT:
diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc
index fbc96f7e18..c385fb9417 100644
--- a/runtime/interpreter/mterp/mterp.cc
+++ b/runtime/interpreter/mterp/mterp.cc
@@ -152,6 +152,11 @@ extern "C" size_t MterpShouldSwitchInterpreters()
const instrumentation::Instrumentation* const instrumentation = runtime->GetInstrumentation();
return instrumentation->NonJitProfilingActive() ||
Dbg::IsDebuggerActive() ||
+ // mterp only knows how to deal with the normal exits. It cannot handle any of the
+ // non-standard force-returns.
+ // TODO We really only need to switch interpreters if a PopFrame has actually happened. We
+ // should check this here.
+ UNLIKELY(runtime->AreNonStandardExitsEnabled()) ||
// An async exception has been thrown. We need to go to the switch interpreter. MTerp doesn't
// know how to deal with these so we could end up never dealing with it if we are in an
// infinite loop. Since this can be called in a tight loop and getting the current thread
diff --git a/runtime/interpreter/shadow_frame.h b/runtime/interpreter/shadow_frame.h
index 91371d1e4e..c0920a8466 100644
--- a/runtime/interpreter/shadow_frame.h
+++ b/runtime/interpreter/shadow_frame.h
@@ -49,6 +49,17 @@ using ShadowFrameAllocaUniquePtr = std::unique_ptr<ShadowFrame, ShadowFrameDelet
// - interpreter - separate VRegs and reference arrays. References are in the reference array.
// - JNI - just VRegs, but where every VReg holds a reference.
class ShadowFrame {
+ private:
+ // Used to keep track of extra state the shadowframe has.
+ enum class FrameFlags : uint32_t {
+ // 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,
+ // We have been asked to re-execute the last instruction.
+ kForceRetryInst = 1 << 2,
+ };
+
public:
// Compute size of ShadowFrame in bytes assuming it has a reference array.
static size_t ComputeSize(uint32_t num_vregs) {
@@ -179,12 +190,8 @@ class ShadowFrame {
mirror::Object* GetVRegReference(size_t i) const REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK_LT(i, NumberOfVRegs());
mirror::Object* ref;
- if (HasReferenceArray()) {
- ref = References()[i].AsMirrorPtr();
- } else {
- const uint32_t* vreg_ptr = &vregs_[i];
- ref = reinterpret_cast<const StackReference<mirror::Object>*>(vreg_ptr)->AsMirrorPtr();
- }
+ DCHECK(HasReferenceArray());
+ ref = References()[i].AsMirrorPtr();
ReadBarrier::MaybeAssertToSpaceInvariant(ref);
if (kVerifyFlags & kVerifyReads) {
VerifyObject(ref);
@@ -345,11 +352,27 @@ class ShadowFrame {
}
bool NeedsNotifyPop() const {
- return needs_notify_pop_;
+ return GetFrameFlag(FrameFlags::kNotifyFramePop);
}
void SetNotifyPop(bool notify) {
- needs_notify_pop_ = notify;
+ UpdateFrameFlag(notify, FrameFlags::kNotifyFramePop);
+ }
+
+ bool GetForcePopFrame() const {
+ return GetFrameFlag(FrameFlags::kForcePopFrame);
+ }
+
+ void SetForcePopFrame(bool enable) {
+ UpdateFrameFlag(enable, FrameFlags::kForcePopFrame);
+ }
+
+ bool GetForceRetryInstruction() const {
+ return GetFrameFlag(FrameFlags::kForceRetryInst);
+ }
+
+ void SetForceRetryInstruction(bool enable) {
+ UpdateFrameFlag(enable, FrameFlags::kForceRetryInst);
}
private:
@@ -364,7 +387,7 @@ class ShadowFrame {
dex_pc_(dex_pc),
cached_hotness_countdown_(0),
hotness_countdown_(0),
- needs_notify_pop_(0) {
+ frame_flags_(0) {
// TODO(iam): Remove this parameter, it's an an artifact of portable removal
DCHECK(has_reference_array);
if (has_reference_array) {
@@ -374,6 +397,18 @@ class ShadowFrame {
}
}
+ void UpdateFrameFlag(bool enable, FrameFlags flag) {
+ if (enable) {
+ frame_flags_ |= static_cast<uint32_t>(flag);
+ } else {
+ frame_flags_ &= ~static_cast<uint32_t>(flag);
+ }
+ }
+
+ bool GetFrameFlag(FrameFlags flag) const {
+ return (frame_flags_ & static_cast<uint32_t>(flag)) != 0;
+ }
+
const StackReference<mirror::Object>* References() const {
DCHECK(HasReferenceArray());
const uint32_t* vreg_end = &vregs_[NumberOfVRegs()];
@@ -397,9 +432,11 @@ class ShadowFrame {
uint32_t dex_pc_;
int16_t cached_hotness_countdown_;
int16_t hotness_countdown_;
- // TODO Might be worth it to try to bit-pack this into some other field to reduce stack usage.
- // NB alignment requires that this field takes 4 bytes. Only 1 bit is actually ever used.
- bool needs_notify_pop_;
+
+ // This is a set of ShadowFrame::FrameFlags which denote special states this frame is in.
+ // NB alignment requires that this field takes 4 bytes no matter its size. Only 3 bits are
+ // currently used.
+ uint32_t frame_flags_;
// This is a two-part array:
// - [0..number_of_vregs) holds the raw virtual registers, and each element here is always 4
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index b18a048c60..861d1db880 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -682,6 +682,11 @@ static void VMRuntime_setProcessPackageName(JNIEnv* env,
Runtime::Current()->SetProcessPackageName(package_name.c_str());
}
+static jboolean VMRuntime_hasBootImageSpaces(JNIEnv* env ATTRIBUTE_UNUSED,
+ jclass klass ATTRIBUTE_UNUSED) {
+ return Runtime::Current()->GetHeap()->HasBootImageSpace() ? JNI_TRUE : JNI_FALSE;
+}
+
static JNINativeMethod gMethods[] = {
FAST_NATIVE_METHOD(VMRuntime, addressOf, "(Ljava/lang/Object;)J"),
NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"),
@@ -690,6 +695,7 @@ static JNINativeMethod gMethods[] = {
NATIVE_METHOD(VMRuntime, clearGrowthLimit, "()V"),
NATIVE_METHOD(VMRuntime, concurrentGC, "()V"),
NATIVE_METHOD(VMRuntime, disableJitCompilation, "()V"),
+ FAST_NATIVE_METHOD(VMRuntime, hasBootImageSpaces, "()Z"), // Could be CRITICAL.
NATIVE_METHOD(VMRuntime, hasUsedHiddenApi, "()Z"),
NATIVE_METHOD(VMRuntime, setHiddenApiExemptions, "([Ljava/lang/String;)V"),
NATIVE_METHOD(VMRuntime, setHiddenApiAccessLogSamplingRate, "(I)V"),
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index e882e7370a..36a6b7fc47 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -402,6 +402,8 @@ class DeoptimizeStackVisitor final : public StackVisitor {
bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) {
exception_handler_->SetHandlerFrameDepth(GetFrameDepth());
ArtMethod* method = GetMethod();
+ VLOG(deopt) << "Deoptimizing stack: depth: " << GetFrameDepth()
+ << " at method " << ArtMethod::PrettyMethod(method);
if (method == nullptr || single_frame_done_) {
FinishStackWalk();
return false; // End stack walk.
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index a48f1feeb9..027193765a 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -49,7 +49,6 @@
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "asm_support.h"
-#include "asm_support_check.h"
#include "base/aborting.h"
#include "base/arena_allocator.h"
#include "base/atomic.h"
@@ -257,6 +256,7 @@ Runtime::Runtime()
is_native_bridge_loaded_(false),
is_native_debuggable_(false),
async_exceptions_thrown_(false),
+ non_standard_exits_enabled_(false),
is_java_debuggable_(false),
zygote_max_failed_boots_(0),
experimental_flags_(ExperimentalFlags::kNone),
@@ -277,7 +277,6 @@ Runtime::Runtime()
static_assert(Runtime::kCalleeSaveSize ==
static_cast<uint32_t>(CalleeSaveType::kLastCalleeSaveType), "Unexpected size");
- CheckAsmSupportOffsetsAndSizes();
std::fill(callee_save_methods_, callee_save_methods_ + arraysize(callee_save_methods_), 0u);
interpreter::CheckInterpreterAsmConstants();
callbacks_.reset(new RuntimeCallbacks());
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 478ff502d9..398a48d935 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -654,6 +654,14 @@ class Runtime {
is_native_debuggable_ = value;
}
+ bool AreNonStandardExitsEnabled() const {
+ return non_standard_exits_enabled_;
+ }
+
+ void SetNonStandardExitsEnabled() {
+ non_standard_exits_enabled_ = true;
+ }
+
bool AreAsyncExceptionsThrown() const {
return async_exceptions_thrown_;
}
@@ -986,6 +994,10 @@ class Runtime {
// MterpShouldSwitchInterpreters function.
bool async_exceptions_thrown_;
+ // Whether anything is going to be using the shadow-frame APIs to force a function to return
+ // early. Doing this requires that (1) we be debuggable and (2) that mterp is exited.
+ bool non_standard_exits_enabled_;
+
// Whether Java code needs to be debuggable.
bool is_java_debuggable_;
diff --git a/runtime/stack.cc b/runtime/stack.cc
index eb9c661d18..25939d2f2c 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -139,9 +139,9 @@ mirror::Object* StackVisitor::GetThisObject() const {
} else {
uint16_t reg = accessor.RegistersSize() - accessor.InsSize();
uint32_t value = 0;
- bool success = GetVReg(m, reg, kReferenceVReg, &value);
- // We currently always guarantee the `this` object is live throughout the method.
- CHECK(success) << "Failed to read the this object in " << ArtMethod::PrettyMethod(m);
+ if (!GetVReg(m, reg, kReferenceVReg, &value)) {
+ return nullptr;
+ }
return reinterpret_cast<mirror::Object*>(value);
}
}
@@ -223,20 +223,39 @@ bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKin
switch (location_kind) {
case DexRegisterLocation::Kind::kInStack: {
const int32_t offset = dex_register_map[vreg].GetStackOffsetInBytes();
+ BitMemoryRegion stack_mask = code_info.GetStackMaskOf(stack_map);
+ if (kind == kReferenceVReg && !stack_mask.LoadBit(offset / kFrameSlotSize)) {
+ return false;
+ }
const uint8_t* addr = reinterpret_cast<const uint8_t*>(cur_quick_frame_) + offset;
*val = *reinterpret_cast<const uint32_t*>(addr);
return true;
}
- case DexRegisterLocation::Kind::kInRegister:
+ case DexRegisterLocation::Kind::kInRegister: {
+ uint32_t register_mask = code_info.GetRegisterMaskOf(stack_map);
+ uint32_t reg = dex_register_map[vreg].GetMachineRegister();
+ if (kind == kReferenceVReg && !(register_mask & (1 << reg))) {
+ return false;
+ }
+ return GetRegisterIfAccessible(reg, kind, val);
+ }
case DexRegisterLocation::Kind::kInRegisterHigh:
case DexRegisterLocation::Kind::kInFpuRegister:
case DexRegisterLocation::Kind::kInFpuRegisterHigh: {
+ if (kind == kReferenceVReg) {
+ return false;
+ }
uint32_t reg = dex_register_map[vreg].GetMachineRegister();
return GetRegisterIfAccessible(reg, kind, val);
}
- case DexRegisterLocation::Kind::kConstant:
- *val = dex_register_map[vreg].GetConstant();
+ case DexRegisterLocation::Kind::kConstant: {
+ uint32_t result = dex_register_map[vreg].GetConstant();
+ if (kind == kReferenceVReg && result != 0) {
+ return false;
+ }
+ *val = result;
return true;
+ }
case DexRegisterLocation::Kind::kNone:
return false;
default:
diff --git a/runtime/thread.cc b/runtime/thread.cc
index b51fc30950..68c82295a6 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -3370,11 +3370,34 @@ void Thread::QuickDeliverException() {
HandleWrapperObjPtr<mirror::Throwable> h_exception(hs.NewHandleWrapper(&exception));
instrumentation->ExceptionThrownEvent(this, exception.Ptr());
}
- // Does instrumentation need to deoptimize the stack?
- // Note: we do this *after* reporting the exception to instrumentation in case it
- // now requires deoptimization. It may happen if a debugger is attached and requests
- // new events (single-step, breakpoint, ...) when the exception is reported.
- if (Dbg::IsForcedInterpreterNeededForException(this)) {
+ // Does instrumentation need to deoptimize the stack or otherwise go to interpreter for something?
+ // Note: we do this *after* reporting the exception to instrumentation in case it now requires
+ // deoptimization. It may happen if a debugger is attached and requests new events (single-step,
+ // breakpoint, ...) when the exception is reported.
+ ShadowFrame* cf;
+ bool force_frame_pop = false;
+ {
+ NthCallerVisitor visitor(this, 0, false);
+ visitor.WalkStack();
+ cf = visitor.GetCurrentShadowFrame();
+ if (cf == nullptr) {
+ cf = FindDebuggerShadowFrame(visitor.GetFrameId());
+ }
+ force_frame_pop = cf != nullptr && cf->GetForcePopFrame();
+ if (kIsDebugBuild && force_frame_pop) {
+ 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());
+ }
+ DCHECK(penultimate_frame != nullptr &&
+ penultimate_frame->GetForceRetryInstruction())
+ << "Force pop frame without retry instruction found. penultimate frame is null: "
+ << (penultimate_frame == nullptr ? "true" : "false");
+ }
+ }
+ if (Dbg::IsForcedInterpreterNeededForException(this) || force_frame_pop) {
NthCallerVisitor visitor(this, 0, false);
visitor.WalkStack();
if (Runtime::Current()->IsAsyncDeoptimizeable(visitor.caller_pc)) {
@@ -3382,10 +3405,16 @@ void Thread::QuickDeliverException() {
const DeoptimizationMethodType method_type = DeoptimizationMethodType::kDefault;
// Save the exception into the deoptimization context so it can be restored
// before entering the interpreter.
+ if (force_frame_pop) {
+ 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.
+ ClearException();
+ }
PushDeoptimizationContext(
JValue(),
false /* is_reference */,
- exception,
+ (force_frame_pop ? nullptr : exception),
false /* from_code */,
method_type);
artDeoptimize(this);
diff --git a/test/1953-pop-frame/check b/test/1953-pop-frame/check
new file mode 100755
index 0000000000..d552272bca
--- /dev/null
+++ b/test/1953-pop-frame/check
@@ -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.
+
+# 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.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
new file mode 100644
index 0000000000..2edef15dfd
--- /dev/null
+++ b/test/1953-pop-frame/class-loading-expected.patch
@@ -0,0 +1,21 @@
+74a75,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.txt b/test/1953-pop-frame/expected.txt
new file mode 100644
index 0000000000..906703d715
--- /dev/null
+++ b/test/1953-pop-frame/expected.txt
@@ -0,0 +1,98 @@
+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 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
diff --git a/test/1953-pop-frame/info.txt b/test/1953-pop-frame/info.txt
new file mode 100644
index 0000000000..b5eb5464b7
--- /dev/null
+++ b/test/1953-pop-frame/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/1953-pop-frame/pop_frame.cc b/test/1953-pop-frame/pop_frame.cc
new file mode 100644
index 0000000000..1c2d2a145a
--- /dev/null
+++ b/test/1953-pop-frame/pop_frame.cc
@@ -0,0 +1,998 @@
+/*
+ * 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 <inttypes.h>
+
+#include <cstdio>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+
+#include "jni.h"
+#include "jvmti.h"
+#include "scoped_local_ref.h"
+#include "scoped_utf_chars.h"
+
+// Test infrastructure
+#include "jni_binder.h"
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
+#include "ti_macros.h"
+
+namespace art {
+namespace Test1953PopFrame {
+
+struct TestData {
+ jlocation target_loc;
+ jmethodID target_method;
+ jclass target_klass;
+ jfieldID target_field;
+ jrawMonitorID notify_monitor;
+ jint frame_pop_offset;
+ jmethodID frame_pop_setup_method;
+ std::vector<std::string> interesting_classes;
+ bool hit_location;
+
+ TestData(jvmtiEnv* jvmti,
+ JNIEnv* env,
+ jlocation loc,
+ jobject meth,
+ jclass klass,
+ jobject field,
+ jobject setup_meth,
+ jint pop_offset,
+ const std::vector<std::string>&& interesting)
+ : target_loc(loc),
+ target_method(meth != nullptr ? env->FromReflectedMethod(meth) : nullptr),
+ target_klass(reinterpret_cast<jclass>(env->NewGlobalRef(klass))),
+ target_field(field != nullptr ? env->FromReflectedField(field) : nullptr),
+ frame_pop_offset(pop_offset),
+ frame_pop_setup_method(setup_meth != nullptr ? env->FromReflectedMethod(setup_meth)
+ : nullptr),
+ interesting_classes(interesting),
+ hit_location(false) {
+ JvmtiErrorToException(env, jvmti, jvmti->CreateRawMonitor("SuspendStopMonitor",
+ &notify_monitor));
+ }
+
+ void PerformSuspend(jvmtiEnv* jvmti, JNIEnv* env) {
+ // Wake up the waiting thread.
+ JvmtiErrorToException(env, jvmti, jvmti->RawMonitorEnter(notify_monitor));
+ hit_location = true;
+ JvmtiErrorToException(env, jvmti, jvmti->RawMonitorNotifyAll(notify_monitor));
+ JvmtiErrorToException(env, jvmti, jvmti->RawMonitorExit(notify_monitor));
+ // Suspend ourself
+ jvmti->SuspendThread(nullptr);
+ }
+};
+
+void JNICALL cbSingleStep(jvmtiEnv* jvmti,
+ JNIEnv* env,
+ jthread thr,
+ jmethodID meth,
+ jlocation loc) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti,
+ jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data != nullptr);
+ if (meth != data->target_method || loc != data->target_loc) {
+ return;
+ }
+ data->PerformSuspend(jvmti, env);
+}
+
+void JNICALL cbExceptionCatch(jvmtiEnv *jvmti,
+ JNIEnv* env,
+ jthread thr,
+ jmethodID method,
+ jlocation location ATTRIBUTE_UNUSED,
+ jobject exception ATTRIBUTE_UNUSED) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti,
+ jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data != nullptr);
+ if (method != data->target_method) {
+ return;
+ }
+ data->PerformSuspend(jvmti, env);
+}
+
+void JNICALL cbException(jvmtiEnv *jvmti,
+ JNIEnv* env,
+ jthread thr,
+ jmethodID method,
+ jlocation location ATTRIBUTE_UNUSED,
+ jobject exception ATTRIBUTE_UNUSED,
+ jmethodID catch_method ATTRIBUTE_UNUSED,
+ jlocation catch_location ATTRIBUTE_UNUSED) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti,
+ jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data != nullptr);
+ if (method != data->target_method) {
+ return;
+ }
+ data->PerformSuspend(jvmti, env);
+}
+
+void JNICALL cbMethodEntry(jvmtiEnv *jvmti,
+ JNIEnv* env,
+ jthread thr,
+ jmethodID method) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti,
+ jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data != nullptr);
+ if (method != data->target_method) {
+ return;
+ }
+ data->PerformSuspend(jvmti, env);
+}
+
+void JNICALL cbMethodExit(jvmtiEnv *jvmti,
+ JNIEnv* env,
+ jthread thr,
+ jmethodID method,
+ jboolean was_popped_by_exception ATTRIBUTE_UNUSED,
+ jvalue return_value ATTRIBUTE_UNUSED) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti,
+ jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data != nullptr);
+ if (method != data->target_method) {
+ return;
+ }
+ data->PerformSuspend(jvmti, env);
+}
+
+void JNICALL cbFieldModification(jvmtiEnv* jvmti,
+ JNIEnv* env,
+ jthread thr,
+ jmethodID method ATTRIBUTE_UNUSED,
+ jlocation location ATTRIBUTE_UNUSED,
+ jclass field_klass ATTRIBUTE_UNUSED,
+ jobject object ATTRIBUTE_UNUSED,
+ jfieldID field,
+ char signature_type ATTRIBUTE_UNUSED,
+ jvalue new_value ATTRIBUTE_UNUSED) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti,
+ jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data != nullptr);
+ if (field != data->target_field) {
+ // TODO What to do here.
+ LOG(FATAL) << "Strange, shouldn't get here!";
+ }
+ data->PerformSuspend(jvmti, env);
+}
+
+void JNICALL cbFieldAccess(jvmtiEnv* jvmti,
+ JNIEnv* env,
+ jthread thr,
+ jmethodID method ATTRIBUTE_UNUSED,
+ jlocation location ATTRIBUTE_UNUSED,
+ jclass field_klass,
+ jobject object ATTRIBUTE_UNUSED,
+ jfieldID field) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti,
+ jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data != nullptr);
+ if (field != data->target_field || !env->IsSameObject(field_klass, data->target_klass)) {
+ // TODO What to do here.
+ LOG(FATAL) << "Strange, shouldn't get here!";
+ }
+ data->PerformSuspend(jvmti, env);
+}
+
+void JNICALL cbBreakpointHit(jvmtiEnv* jvmti,
+ JNIEnv* env,
+ jthread thr,
+ jmethodID method,
+ jlocation loc) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti,
+ jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data != nullptr);
+ if (data->frame_pop_setup_method == method) {
+ CHECK(loc == 0) << "We should have stopped at location 0";
+ if (JvmtiErrorToException(env,
+ jvmti,
+ jvmti->NotifyFramePop(thr, data->frame_pop_offset))) {
+ return;
+ }
+ return;
+ }
+ if (method != data->target_method || loc != data->target_loc) {
+ // TODO What to do here.
+ LOG(FATAL) << "Strange, shouldn't get here!";
+ }
+ data->PerformSuspend(jvmti, env);
+}
+
+void JNICALL cbFramePop(jvmtiEnv* jvmti,
+ JNIEnv* env,
+ jthread thr,
+ jmethodID method ATTRIBUTE_UNUSED,
+ jboolean was_popped_by_exception ATTRIBUTE_UNUSED) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti,
+ jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data != nullptr);
+ data->PerformSuspend(jvmti, env);
+}
+
+void JNICALL cbClassLoadOrPrepare(jvmtiEnv* jvmti,
+ JNIEnv* env,
+ jthread thr,
+ jclass klass) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti,
+ jvmti->GetThreadLocalStorage(thr, reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data != nullptr);
+ char* name;
+ if (JvmtiErrorToException(env, jvmti, jvmti->GetClassSignature(klass, &name, nullptr))) {
+ return;
+ }
+ std::string name_str(name);
+ if (JvmtiErrorToException(env,
+ jvmti,
+ jvmti->Deallocate(reinterpret_cast<unsigned char*>(name)))) {
+ return;
+ }
+ if (std::find(data->interesting_classes.cbegin(),
+ data->interesting_classes.cend(),
+ name_str) != data->interesting_classes.cend()) {
+ data->PerformSuspend(jvmti, env);
+ }
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test1953_setupTest(JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) {
+ jvmtiCapabilities caps;
+ memset(&caps, 0, sizeof(caps));
+ // Most of these will already be there but might as well be complete.
+ caps.can_pop_frame = 1;
+ caps.can_generate_single_step_events = 1;
+ caps.can_generate_breakpoint_events = 1;
+ caps.can_suspend = 1;
+ caps.can_generate_method_entry_events = 1;
+ caps.can_generate_method_exit_events = 1;
+ caps.can_generate_monitor_events = 1;
+ caps.can_generate_exception_events = 1;
+ caps.can_generate_frame_pop_events = 1;
+ caps.can_generate_field_access_events = 1;
+ caps.can_generate_field_modification_events = 1;
+ caps.can_redefine_classes = 1;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->AddCapabilities(&caps))) {
+ return;
+ }
+ jvmtiEventCallbacks cb;
+ memset(&cb, 0, sizeof(cb));
+ // TODO Add the rest of these.
+ cb.Breakpoint = cbBreakpointHit;
+ cb.SingleStep = cbSingleStep;
+ cb.FieldAccess = cbFieldAccess;
+ cb.FieldModification = cbFieldModification;
+ cb.MethodEntry = cbMethodEntry;
+ cb.MethodExit = cbMethodExit;
+ cb.Exception = cbException;
+ cb.ExceptionCatch = cbExceptionCatch;
+ cb.FramePop = cbFramePop;
+ cb.ClassLoad = cbClassLoadOrPrepare;
+ cb.ClassPrepare = cbClassLoadOrPrepare;
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)));
+}
+
+static bool DeleteTestData(JNIEnv* env, jthread thr, TestData* data) {
+ env->DeleteGlobalRef(data->target_klass);
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetThreadLocalStorage(thr, nullptr))) {
+ return false;
+ }
+ return JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->Deallocate(reinterpret_cast<uint8_t*>(data)));
+}
+
+static TestData* SetupTestData(JNIEnv* env,
+ jobject meth,
+ jlocation loc,
+ jclass target_klass,
+ jobject field,
+ jobject setup_meth,
+ jint pop_offset,
+ const std::vector<std::string>&& interesting_names) {
+ void* data_ptr;
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->Allocate(sizeof(TestData),
+ reinterpret_cast<uint8_t**>(&data_ptr)))) {
+ return nullptr;
+ }
+ data = new (data_ptr) TestData(jvmti_env,
+ env,
+ loc,
+ meth,
+ target_klass,
+ field,
+ setup_meth,
+ pop_offset,
+ std::move(interesting_names));
+ if (env->ExceptionCheck()) {
+ env->DeleteGlobalRef(data->target_klass);
+ jvmti_env->Deallocate(reinterpret_cast<uint8_t*>(data));
+ return nullptr;
+ }
+ return data;
+}
+
+static TestData* SetupTestData(JNIEnv* env,
+ jobject meth,
+ jlocation loc,
+ jclass target_klass,
+ jobject field,
+ jobject setup_meth,
+ jint pop_offset) {
+ std::vector<std::string> empty;
+ return SetupTestData(
+ env, meth, loc, target_klass, field, setup_meth, pop_offset, std::move(empty));
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test1953_setupSuspendClassEvent(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jint event_num,
+ jobjectArray interesting_names,
+ jthread thr) {
+ CHECK(event_num == JVMTI_EVENT_CLASS_LOAD || event_num == JVMTI_EVENT_CLASS_PREPARE);
+ std::vector<std::string> names;
+ jint cnt = env->GetArrayLength(interesting_names);
+ for (jint i = 0; i < cnt; i++) {
+ env->PushLocalFrame(1);
+ jstring name_obj = reinterpret_cast<jstring>(env->GetObjectArrayElement(interesting_names, i));
+ const char* name_chr = env->GetStringUTFChars(name_obj, nullptr);
+ names.push_back(std::string(name_chr));
+ env->ReleaseStringUTFChars(name_obj, name_chr);
+ env->PopLocalFrame(nullptr);
+ }
+ TestData* data;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->GetThreadLocalStorage(thr,
+ reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data == nullptr) << "Data was not cleared!";
+ data = SetupTestData(env, nullptr, 0, nullptr, nullptr, nullptr, 0, std::move(names));
+ if (data == nullptr) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetThreadLocalStorage(thr, data))) {
+ return;
+ }
+ JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ static_cast<jvmtiEvent>(event_num),
+ thr));
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test1953_clearSuspendClassEvent(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jthread thr) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->GetThreadLocalStorage(thr,
+ reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data != nullptr);
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_CLASS_LOAD,
+ thr))) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_CLASS_PREPARE,
+ thr))) {
+ return;
+ }
+ DeleteTestData(env, thr, data);
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test1953_setupSuspendSingleStepAt(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jobject meth,
+ jlocation loc,
+ jthread thr) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->GetThreadLocalStorage(thr,
+ reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data == nullptr) << "Data was not cleared!";
+ data = SetupTestData(env, meth, loc, nullptr, nullptr, nullptr, 0);
+ if (data == nullptr) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetThreadLocalStorage(thr, data))) {
+ return;
+ }
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_SINGLE_STEP,
+ thr));
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test1953_clearSuspendSingleStepFor(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jthread thr) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->GetThreadLocalStorage(thr,
+ reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data != nullptr);
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_SINGLE_STEP,
+ thr))) {
+ return;
+ }
+ DeleteTestData(env, thr, data);
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test1953_setupSuspendPopFrameEvent(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jint offset,
+ jobject breakpoint_func,
+ jthread thr) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->GetThreadLocalStorage(thr,
+ reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data == nullptr) << "Data was not cleared!";
+ data = SetupTestData(env, nullptr, 0, nullptr, nullptr, breakpoint_func, offset);
+ CHECK(data != nullptr);
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetThreadLocalStorage(thr, data))) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_FRAME_POP,
+ thr))) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_BREAKPOINT,
+ thr))) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetBreakpoint(data->frame_pop_setup_method, 0))) {
+ return;
+ }
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test1953_clearSuspendPopFrameEvent(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jthread thr) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->GetThreadLocalStorage(thr,
+ reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data != nullptr);
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_FRAME_POP,
+ thr))) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_BREAKPOINT,
+ thr))) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->ClearBreakpoint(data->frame_pop_setup_method, 0))) {
+ return;
+ }
+ DeleteTestData(env, thr, data);
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test1953_setupSuspendBreakpointFor(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jobject meth,
+ jlocation loc,
+ jthread thr) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->GetThreadLocalStorage(thr,
+ reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data == nullptr) << "Data was not cleared!";
+ data = SetupTestData(env, meth, loc, nullptr, nullptr, nullptr, 0);
+ if (data == nullptr) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetThreadLocalStorage(thr, data))) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_BREAKPOINT,
+ thr))) {
+ return;
+ }
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetBreakpoint(data->target_method,
+ data->target_loc));
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test1953_clearSuspendBreakpointFor(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jthread thr) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->GetThreadLocalStorage(thr,
+ reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data != nullptr);
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_BREAKPOINT,
+ thr))) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->ClearBreakpoint(data->target_method,
+ data->target_loc))) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetThreadLocalStorage(thr, nullptr))) {
+ return;
+ }
+ DeleteTestData(env, thr, data);
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test1953_setupSuspendExceptionEvent(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jobject method,
+ jboolean is_catch,
+ jthread thr) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->GetThreadLocalStorage(
+ thr, reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data == nullptr) << "Data was not cleared!";
+ data = SetupTestData(env, method, 0, nullptr, nullptr, nullptr, 0);
+ if (data == nullptr) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetThreadLocalStorage(thr, data))) {
+ return;
+ }
+ JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(
+ JVMTI_ENABLE,
+ is_catch ? JVMTI_EVENT_EXCEPTION_CATCH : JVMTI_EVENT_EXCEPTION,
+ thr));
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test1953_clearSuspendExceptionEvent(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jthread thr) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->GetThreadLocalStorage(thr,
+ reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data != nullptr);
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_EXCEPTION_CATCH,
+ thr))) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_EXCEPTION,
+ thr))) {
+ return;
+ }
+ DeleteTestData(env, thr, data);
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test1953_setupSuspendMethodEvent(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jobject method,
+ jboolean enter,
+ jthread thr) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->GetThreadLocalStorage(
+ thr, reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data == nullptr) << "Data was not cleared!";
+ data = SetupTestData(env, method, 0, nullptr, nullptr, nullptr, 0);
+ if (data == nullptr) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetThreadLocalStorage(thr, data))) {
+ return;
+ }
+ JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(
+ JVMTI_ENABLE,
+ enter ? JVMTI_EVENT_METHOD_ENTRY : JVMTI_EVENT_METHOD_EXIT,
+ thr));
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test1953_clearSuspendMethodEvent(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jthread thr) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->GetThreadLocalStorage(thr,
+ reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data != nullptr);
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_METHOD_EXIT,
+ thr))) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_METHOD_ENTRY,
+ thr))) {
+ return;
+ }
+ DeleteTestData(env, thr, data);
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test1953_setupFieldSuspendFor(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jclass target_klass,
+ jobject field,
+ jboolean access,
+ jthread thr) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->GetThreadLocalStorage(
+ thr, reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data == nullptr) << "Data was not cleared!";
+ data = SetupTestData(env, nullptr, 0, target_klass, field, nullptr, 0);
+ if (data == nullptr) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetThreadLocalStorage(thr, data))) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(
+ JVMTI_ENABLE,
+ access ? JVMTI_EVENT_FIELD_ACCESS : JVMTI_EVENT_FIELD_MODIFICATION,
+ thr))) {
+ return;
+ }
+ if (access) {
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetFieldAccessWatch(data->target_klass,
+ data->target_field));
+ } else {
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetFieldModificationWatch(data->target_klass,
+ data->target_field));
+ }
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test1953_clearFieldSuspendFor(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jthread thr) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->GetThreadLocalStorage(thr,
+ reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data != nullptr);
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_FIELD_ACCESS,
+ thr))) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_FIELD_MODIFICATION,
+ thr))) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->ClearFieldModificationWatch(
+ data->target_klass, data->target_field)) &&
+ JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->ClearFieldAccessWatch(
+ data->target_klass, data->target_field))) {
+ return;
+ } else {
+ env->ExceptionClear();
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetThreadLocalStorage(thr, nullptr))) {
+ return;
+ }
+ DeleteTestData(env, thr, data);
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test1953_setupWaitForNativeCall(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jthread thr) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->GetThreadLocalStorage(
+ thr, reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data == nullptr) << "Data was not cleared!";
+ data = SetupTestData(env, nullptr, 0, nullptr, nullptr, nullptr, 0);
+ if (data == nullptr) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetThreadLocalStorage(thr, data))) {
+ return;
+ }
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test1953_clearWaitForNativeCall(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jthread thr) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->GetThreadLocalStorage(thr,
+ reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data != nullptr);
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetThreadLocalStorage(thr, nullptr))) {
+ return;
+ }
+ DeleteTestData(env, thr, data);
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test1953_waitForSuspendHit(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jthread thr) {
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->GetThreadLocalStorage(thr,
+ reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data != nullptr);
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorEnter(data->notify_monitor))) {
+ return;
+ }
+ while (!data->hit_location) {
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorWait(data->notify_monitor, -1))) {
+ return;
+ }
+ }
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->RawMonitorExit(data->notify_monitor))) {
+ return;
+ }
+ jint state = 0;
+ while (!JvmtiErrorToException(env, jvmti_env, jvmti_env->GetThreadState(thr, &state)) &&
+ (state & JVMTI_THREAD_STATE_SUSPENDED) == 0) { }
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test1953_popFrame(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jthread thr) {
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->PopFrame(thr));
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test1953_00024NativeCalledObject_calledFunction(
+ JNIEnv* env, jobject thiz) {
+ env->PushLocalFrame(1);
+ jclass klass = env->GetObjectClass(thiz);
+ jfieldID cnt = env->GetFieldID(klass, "cnt", "I");
+ env->SetIntField(thiz, cnt, env->GetIntField(thiz, cnt) + 1);
+ env->PopLocalFrame(nullptr);
+ TestData *data;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->GetThreadLocalStorage(/* thread */ nullptr,
+ reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ CHECK(data != nullptr);
+ data->PerformSuspend(jvmti_env, env);
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test1953_00024NativeCallerObject_run(
+ JNIEnv* env, jobject thiz) {
+ env->PushLocalFrame(1);
+ jclass klass = env->GetObjectClass(thiz);
+ jfieldID baseCnt = env->GetFieldID(klass, "baseCnt", "I");
+ env->SetIntField(thiz, baseCnt, env->GetIntField(thiz, baseCnt) + 1);
+ jmethodID called = env->GetMethodID(klass, "calledFunction", "()V");
+ env->CallVoidMethod(thiz, called);
+ env->PopLocalFrame(nullptr);
+}
+
+extern "C" JNIEXPORT
+jboolean JNICALL Java_art_Test1953_isClassLoaded(JNIEnv* env, jclass, jstring name) {
+ ScopedUtfChars chr(env, name);
+ if (env->ExceptionCheck()) {
+ return false;
+ }
+ jint cnt = 0;
+ jclass* klasses = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetLoadedClasses(&cnt, &klasses))) {
+ return false;
+ }
+ bool res = false;
+ for (jint i = 0; !res && i < cnt; i++) {
+ char* sig;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->GetClassSignature(klasses[i], &sig, nullptr))) {
+ return false;
+ }
+ res = (strcmp(sig, chr.c_str()) == 0);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(sig));
+ }
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(klasses));
+ return res;
+}
+
+} // namespace Test1953PopFrame
+} // namespace art
+
diff --git a/test/1953-pop-frame/run b/test/1953-pop-frame/run
new file mode 100755
index 0000000000..d16d4e6091
--- /dev/null
+++ b/test/1953-pop-frame/run
@@ -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.
+
+# 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/tools/cpp-define-generator/constant_card_table.def b/test/1953-pop-frame/src/Main.java
index ae3e8f399f..156076e7a3 100644
--- a/tools/cpp-define-generator/constant_card_table.def
+++ b/test/1953-pop-frame/src/Main.java
@@ -14,12 +14,10 @@
* limitations under the License.
*/
-// Export heap values.
-
-#if defined(DEFINE_INCLUDE_DEPENDENCIES)
-#include "gc/accounting/card_table.h"
-#endif
-
-// Size of references to the heap on the stack.
-DEFINE_EXPR(CARD_TABLE_CARD_SHIFT, size_t, art::gc::accounting::CardTable::kCardShift)
-
+import java.util.Arrays;
+import java.util.List;
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test1953.run(!Arrays.asList(args).contains("DISABLE_CLASS_LOAD_TESTS"));
+ }
+}
diff --git a/test/1953-pop-frame/src/art/Breakpoint.java b/test/1953-pop-frame/src/art/Breakpoint.java
new file mode 100644
index 0000000000..bbb89f707f
--- /dev/null
+++ b/test/1953-pop-frame/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * 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;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/1953-pop-frame/src/art/Redefinition.java b/test/1953-pop-frame/src/art/Redefinition.java
new file mode 100644
index 0000000000..56d2938a01
--- /dev/null
+++ b/test/1953-pop-frame/src/art/Redefinition.java
@@ -0,0 +1,91 @@
+/*
+ * 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;
+
+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 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);
+
+ 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);
+ }
+ 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);
+ }
+ }
+
+ 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/1953-pop-frame/src/art/StackTrace.java b/test/1953-pop-frame/src/art/StackTrace.java
new file mode 100644
index 0000000000..2ea2f201e8
--- /dev/null
+++ b/test/1953-pop-frame/src/art/StackTrace.java
@@ -0,0 +1,68 @@
+/*
+ * 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;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Executable;
+
+public class StackTrace {
+ public static class StackFrameData {
+ public final Thread thr;
+ public final Executable method;
+ public final long current_location;
+ public final int depth;
+
+ public StackFrameData(Thread thr, Executable e, long loc, int depth) {
+ this.thr = thr;
+ this.method = e;
+ this.current_location = loc;
+ this.depth = depth;
+ }
+ @Override
+ public String toString() {
+ return String.format(
+ "StackFrameData { thr: '%s', method: '%s', loc: %d, depth: %d }",
+ this.thr,
+ this.method,
+ this.current_location,
+ this.depth);
+ }
+ }
+
+ public static native int GetStackDepth(Thread thr);
+
+ private static native StackFrameData[] nativeGetStackTrace(Thread thr);
+
+ public static StackFrameData[] GetStackTrace(Thread thr) {
+ // The RI seems to give inconsistent (and sometimes nonsensical) results if the thread is not
+ // suspended. The spec says that not being suspended is fine but since we want this to be
+ // consistent we will suspend for the RI.
+ boolean suspend_thread =
+ !System.getProperty("java.vm.name").equals("Dalvik") &&
+ !thr.equals(Thread.currentThread()) &&
+ !Suspension.isSuspended(thr);
+ if (suspend_thread) {
+ Suspension.suspend(thr);
+ }
+ StackFrameData[] out = nativeGetStackTrace(thr);
+ if (suspend_thread) {
+ Suspension.resume(thr);
+ }
+ return out;
+ }
+}
+
diff --git a/tools/cpp-define-generator/common_undef.def b/test/1953-pop-frame/src/art/Suspension.java
index c44aba7c3b..16e62ccac9 100644
--- a/tools/cpp-define-generator/common_undef.def
+++ b/test/1953-pop-frame/src/art/Suspension.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,7 +14,17 @@
* limitations under the License.
*/
-#ifdef DEFINE_OFFSET_EXPR_STANDARD_DEFINITION
-#undef DEFINE_OFFSET_EXPR_STANDARD_DEFINITION
-#undef DEFINE_OFFSET_EXPR
-#endif
+package art;
+
+public class Suspension {
+ // Suspends a thread using jvmti.
+ public native static void suspend(Thread thr);
+
+ // Resumes a thread using jvmti.
+ public native static void resume(Thread thr);
+
+ public native static boolean isSuspended(Thread thr);
+
+ public native static int[] suspendList(Thread... threads);
+ public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1953-pop-frame/src/art/Test1953.java b/test/1953-pop-frame/src/art/Test1953.java
new file mode 100644
index 0000000000..adec7762b1
--- /dev/null
+++ b/test/1953-pop-frame/src/art/Test1953.java
@@ -0,0 +1,976 @@
+/*
+ * 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;
+
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Base64;
+import java.util.EnumSet;
+import java.util.concurrent.CountDownLatch;
+import java.util.function.Consumer;
+
+public class Test1953 {
+ public final boolean canRunClassLoadTests;
+ public static void doNothing() {}
+
+ public interface TestRunnable extends Runnable {
+ public int getBaseCallCount();
+ public Method getCalledMethod() throws Exception;
+ public default Method getCallingMethod() throws Exception {
+ return this.getClass().getMethod("run");
+ };
+ }
+
+ public static interface TestSuspender {
+ public void setup(Thread thr);
+ public void waitForSuspend(Thread thr);
+ public void cleanup(Thread thr);
+ }
+
+ public static interface ThreadRunnable { public void run(Thread thr); }
+ public static TestSuspender makeSuspend(final ThreadRunnable setup, final ThreadRunnable clean) {
+ return new TestSuspender() {
+ public void setup(Thread thr) { setup.run(thr); }
+ public void waitForSuspend(Thread thr) { Test1953.waitForSuspendHit(thr); }
+ public void cleanup(Thread thr) { clean.run(thr); }
+ };
+ }
+
+ public void runTestOn(TestRunnable testObj, ThreadRunnable su, ThreadRunnable cl) throws
+ Exception {
+ runTestOn(testObj, makeSuspend(su, cl));
+ }
+
+ private static void SafePrintStackTrace(StackTraceElement st[]) {
+ for (StackTraceElement e : st) {
+ System.out.println("\t" + e.getClassName() + "." + e.getMethodName() + "(" +
+ (e.isNativeMethod() ? "Native Method" : e.getFileName()) + ")");
+ if (e.getClassName().equals("art.Test1953") && e.getMethodName().equals("runTests")) {
+ System.out.println("\t<Additional frames hidden>");
+ break;
+ }
+ }
+ }
+
+ public void runTestOn(TestRunnable testObj, TestSuspender su) throws Exception {
+ System.out.println("Single call with PopFrame on " + testObj + " base-call-count: " +
+ testObj.getBaseCallCount());
+ final CountDownLatch continue_latch = new CountDownLatch(1);
+ final CountDownLatch startup_latch = new CountDownLatch(1);
+ Runnable await = () -> {
+ try {
+ startup_latch.countDown();
+ continue_latch.await();
+ } catch (Exception e) {
+ throw new Error("Failed to await latch", e);
+ }
+ };
+ Thread thr = new Thread(() -> { await.run(); testObj.run(); });
+ thr.start();
+
+ // Wait until the other thread is started.
+ startup_latch.await();
+
+ // Do any final setup.
+ preTest.accept(testObj);
+
+ // Setup suspension method on the thread.
+ su.setup(thr);
+
+ // Let the other thread go.
+ continue_latch.countDown();
+
+ // Wait for the other thread to hit the breakpoint/watchpoint/whatever and suspend itself
+ // (without re-entering java)
+ su.waitForSuspend(thr);
+
+ // Cleanup the breakpoint/watchpoint/etc.
+ su.cleanup(thr);
+
+ try {
+ // Pop the frame.
+ popFrame(thr);
+ } catch (Exception e) {
+ System.out.println("Failed to pop frame due to " + e);
+ SafePrintStackTrace(e.getStackTrace());
+ }
+
+ // Start the other thread going again.
+ Suspension.resume(thr);
+
+ // Wait for the other thread to finish.
+ thr.join();
+
+ // See how many times calledFunction was called.
+ System.out.println("result is " + testObj + " base-call count: " + testObj.getBaseCallCount());
+ }
+
+ public static abstract class AbstractTestObject implements TestRunnable {
+ public int callerCnt;
+
+ public AbstractTestObject() {
+ callerCnt = 0;
+ }
+
+ public int getBaseCallCount() {
+ return callerCnt;
+ }
+
+ public void run() {
+ callerCnt++;
+ // This function should be re-executed by the popFrame.
+ calledFunction();
+ }
+
+ public Method getCalledMethod() throws Exception {
+ return this.getClass().getMethod("calledFunction");
+ }
+
+ public abstract void calledFunction();
+ }
+
+ public static class RedefineTestObject extends AbstractTestObject implements Runnable {
+ public static enum RedefineState { ORIGINAL, REDEFINED, };
+ /* public static class RedefineTestObject extends AbstractTestObject implements Runnable {
+ * public static final byte[] CLASS_BYTES;
+ * public static final byte[] DEX_BYTES;
+ * static {
+ * CLASS_BYTES = null;
+ * DEX_BYTES = null;
+ * }
+ *
+ * public EnumSet<RedefineState> redefine_states;
+ * public RedefineTestObject() {
+ * super();
+ * redefine_states = EnumSet.noneOf(RedefineState.class);
+ * }
+ * public String toString() {
+ * return "RedefineTestObject { states: " + redefine_states.toString()
+ * + " current: REDEFINED }";
+ * }
+ * public void calledFunction() {
+ * redefine_states.add(RedefineState.REDEFINED); // line +0
+ * // We will trigger the redefinition using a breakpoint on the next line.
+ * doNothing(); // line +2
+ * }
+ * }
+ */
+ public static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADUATQoADQAjBwAkCgAlACYJAAwAJwoAJQAoEgAAACwJAAIALQoAJQAuCgAvADAJAAwA" +
+ "MQkADAAyBwAzBwA0BwA2AQASUmVkZWZpbmVUZXN0T2JqZWN0AQAMSW5uZXJDbGFzc2VzAQANUmVk" +
+ "ZWZpbmVTdGF0ZQEAC0NMQVNTX0JZVEVTAQACW0IBAAlERVhfQllURVMBAA9yZWRlZmluZV9zdGF0" +
+ "ZXMBABNMamF2YS91dGlsL0VudW1TZXQ7AQAJU2lnbmF0dXJlAQBETGphdmEvdXRpbC9FbnVtU2V0" +
+ "PExhcnQvVGVzdDE5NTMkUmVkZWZpbmVUZXN0T2JqZWN0JFJlZGVmaW5lU3RhdGU7PjsBAAY8aW5p" +
+ "dD4BAAMoKVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAIdG9TdHJpbmcBABQoKUxqYXZhL2xh" +
+ "bmcvU3RyaW5nOwEADmNhbGxlZEZ1bmN0aW9uAQAIPGNsaW5pdD4BAApTb3VyY2VGaWxlAQANVGVz" +
+ "dDE5NTMuamF2YQwAGQAaAQAtYXJ0L1Rlc3QxOTUzJFJlZGVmaW5lVGVzdE9iamVjdCRSZWRlZmlu" +
+ "ZVN0YXRlBwA3DAA4ADkMABUAFgwAHQAeAQAQQm9vdHN0cmFwTWV0aG9kcw8GADoIADsMADwAPQwA" +
+ "PgA/DABAAEEHAEIMAEMAGgwAEgATDAAUABMBAB9hcnQvVGVzdDE5NTMkUmVkZWZpbmVUZXN0T2Jq" +
+ "ZWN0AQAfYXJ0L1Rlc3QxOTUzJEFic3RyYWN0VGVzdE9iamVjdAEAEkFic3RyYWN0VGVzdE9iamVj" +
+ "dAEAEmphdmEvbGFuZy9SdW5uYWJsZQEAEWphdmEvdXRpbC9FbnVtU2V0AQAGbm9uZU9mAQAmKExq" +
+ "YXZhL2xhbmcvQ2xhc3M7KUxqYXZhL3V0aWwvRW51bVNldDsKAEQARQEAM1JlZGVmaW5lVGVzdE9i" +
+ "amVjdCB7IHN0YXRlczogASBjdXJyZW50OiBSRURFRklORUQgfQEAF21ha2VDb25jYXRXaXRoQ29u" +
+ "c3RhbnRzAQAmKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZzsBAAlSRURFRklO" +
+ "RUQBAC9MYXJ0L1Rlc3QxOTUzJFJlZGVmaW5lVGVzdE9iamVjdCRSZWRlZmluZVN0YXRlOwEAA2Fk" +
+ "ZAEAFShMamF2YS9sYW5nL09iamVjdDspWgEADGFydC9UZXN0MTk1MwEACWRvTm90aGluZwcARgwA" +
+ "PABJAQAkamF2YS9sYW5nL2ludm9rZS9TdHJpbmdDb25jYXRGYWN0b3J5BwBLAQAGTG9va3VwAQCY" +
+ "KExqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZXMkTG9va3VwO0xqYXZhL2xhbmcvU3RyaW5n" +
+ "O0xqYXZhL2xhbmcvaW52b2tlL01ldGhvZFR5cGU7TGphdmEvbGFuZy9TdHJpbmc7W0xqYXZhL2xh" +
+ "bmcvT2JqZWN0OylMamF2YS9sYW5nL2ludm9rZS9DYWxsU2l0ZTsHAEwBACVqYXZhL2xhbmcvaW52" +
+ "b2tlL01ldGhvZEhhbmRsZXMkTG9va3VwAQAeamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVz" +
+ "ACEADAANAAEADgADABkAEgATAAAAGQAUABMAAAABABUAFgABABcAAAACABgABAABABkAGgABABsA" +
+ "AAAuAAIAAQAAAA4qtwABKhICuAADtQAEsQAAAAEAHAAAAA4AAwAAACEABAAiAA0AIwABAB0AHgAB" +
+ "ABsAAAAlAAEAAQAAAA0qtAAEtgAFugAGAACwAAAAAQAcAAAABgABAAAAJQABAB8AGgABABsAAAAv" +
+ "AAIAAQAAAA8qtAAEsgAHtgAIV7gACbEAAAABABwAAAAOAAMAAAApAAsAKwAOACwACAAgABoAAQAb" +
+ "AAAAKQABAAAAAAAJAbMACgGzAAuxAAAAAQAcAAAADgADAAAAGwAEABwACAAdAAMAIQAAAAIAIgAQ" +
+ "AAAAIgAEAAwALwAPAAkAAgAMABFAGQANAC8ANQQJAEcASgBIABkAKQAAAAgAAQAqAAEAKw==");
+ public static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQAaR23N6WpunLRVX+BexSuzzNNiHNOvQpFoBwAAcAAAAHhWNBIAAAAAAAAAAKQGAAAq" +
+ "AAAAcAAAABEAAAAYAQAABQAAAFwBAAAEAAAAmAEAAAwAAAC4AQAAAQAAABgCAAAwBQAAOAIAACID" +
+ "AAA5AwAAQwMAAEsDAABPAwAAXAMAAGcDAABqAwAAbgMAAJEDAADCAwAA5QMAAPUDAAAZBAAAOQQA" +
+ "AFwEAAB7BAAAjgQAAKIEAAC4BAAAzAQAAOcEAAD8BAAAEQUAABwFAAAwBQAATwUAAF4FAABhBQAA" +
+ "ZAUAAGgFAABsBQAAeQUAAH4FAACGBQAAlgUAAKEFAACnBQAArwUAAMAFAADKBQAA0QUAAAgAAAAJ" +
+ "AAAACgAAAAsAAAAMAAAADQAAAA4AAAAPAAAAEAAAABEAAAASAAAAEwAAABQAAAAVAAAAGwAAABwA" +
+ "AAAeAAAABgAAAAsAAAAAAAAABwAAAAwAAAAMAwAABwAAAA0AAAAUAwAAGwAAAA4AAAAAAAAAHQAA" +
+ "AA8AAAAcAwAAAQABABcAAAACABAABAAAAAIAEAAFAAAAAgANACYAAAAAAAMAAgAAAAIAAwABAAAA" +
+ "AgADAAIAAAACAAMAIgAAAAIAAAAnAAAAAwADACMAAAAMAAMAAgAAAAwAAQAhAAAADAAAACcAAAAN" +
+ "AAQAIAAAAA0AAgAlAAAADQAAACcAAAACAAAAAQAAAAAAAAAEAwAAGgAAAIwGAABRBgAAAAAAAAQA" +
+ "AQACAAAA+gIAAB0AAABUMAMAbhALAAAADAAiAQwAcBAGAAEAGgIZAG4gBwAhAG4gBwABABoAAABu" +
+ "IAcAAQBuEAgAAQAMABEAAAABAAAAAAAAAPQCAAAGAAAAEgBpAAEAaQACAA4AAgABAAEAAADuAgAA" +
+ "DAAAAHAQAAABABwAAQBxEAoAAAAMAFsQAwAOAAMAAQACAAAA/gIAAAsAAABUIAMAYgEAAG4gCQAQ" +
+ "AHEABQAAAA4AIQAOPIcAGwAOPC0AJQAOACkADnk8AAEAAAAKAAAAAQAAAAsAAAABAAAACAAAAAEA" +
+ "AAAJABUgY3VycmVudDogUkVERUZJTkVEIH0ACDxjbGluaXQ+AAY8aW5pdD4AAj47AAtDTEFTU19C" +
+ "WVRFUwAJREVYX0JZVEVTAAFMAAJMTAAhTGFydC9UZXN0MTk1MyRBYnN0cmFjdFRlc3RPYmplY3Q7" +
+ "AC9MYXJ0L1Rlc3QxOTUzJFJlZGVmaW5lVGVzdE9iamVjdCRSZWRlZmluZVN0YXRlOwAhTGFydC9U" +
+ "ZXN0MTk1MyRSZWRlZmluZVRlc3RPYmplY3Q7AA5MYXJ0L1Rlc3QxOTUzOwAiTGRhbHZpay9hbm5v" +
+ "dGF0aW9uL0VuY2xvc2luZ0NsYXNzOwAeTGRhbHZpay9hbm5vdGF0aW9uL0lubmVyQ2xhc3M7ACFM" +
+ "ZGFsdmlrL2Fubm90YXRpb24vTWVtYmVyQ2xhc3NlczsAHUxkYWx2aWsvYW5ub3RhdGlvbi9TaWdu" +
+ "YXR1cmU7ABFMamF2YS9sYW5nL0NsYXNzOwASTGphdmEvbGFuZy9PYmplY3Q7ABRMamF2YS9sYW5n" +
+ "L1J1bm5hYmxlOwASTGphdmEvbGFuZy9TdHJpbmc7ABlMamF2YS9sYW5nL1N0cmluZ0J1aWxkZXI7" +
+ "ABNMamF2YS91dGlsL0VudW1TZXQ7ABNMamF2YS91dGlsL0VudW1TZXQ8AAlSRURFRklORUQAElJl" +
+ "ZGVmaW5lVGVzdE9iamVjdAAdUmVkZWZpbmVUZXN0T2JqZWN0IHsgc3RhdGVzOiAADVRlc3QxOTUz" +
+ "LmphdmEAAVYAAVoAAlpMAAJbQgALYWNjZXNzRmxhZ3MAA2FkZAAGYXBwZW5kAA5jYWxsZWRGdW5j" +
+ "dGlvbgAJZG9Ob3RoaW5nAARuYW1lAAZub25lT2YAD3JlZGVmaW5lX3N0YXRlcwAIdG9TdHJpbmcA" +
+ "BXZhbHVlAFt+fkQ4eyJtaW4tYXBpIjoxLCJzaGEtMSI6IjUyNzNjM2RmZWUxMDQ2NzIwYWY0MjVm" +
+ "YTg1NTMxNmM5OWM4NmM4ZDIiLCJ2ZXJzaW9uIjoiMS4zLjE4LWRldiJ9AAIHASgcAxcWFwkXAwIE" +
+ "ASgYAwIFAh8ECSQXGAIGASgcARgBAgECAgEZARkDAQGIgASEBQGBgASgBQMByAUBAbgEAAAAAAAB" +
+ "AAAALgYAAAMAAAA6BgAAQAYAAEkGAAB8BgAAAQAAAAAAAAAAAAAAAwAAAHQGAAAQAAAAAAAAAAEA" +
+ "AAAAAAAAAQAAACoAAABwAAAAAgAAABEAAAAYAQAAAwAAAAUAAABcAQAABAAAAAQAAACYAQAABQAA" +
+ "AAwAAAC4AQAABgAAAAEAAAAYAgAAASAAAAQAAAA4AgAAAyAAAAQAAADuAgAAARAAAAQAAAAEAwAA" +
+ "AiAAACoAAAAiAwAABCAAAAQAAAAuBgAAACAAAAEAAABRBgAAAxAAAAMAAABwBgAABiAAAAEAAACM" +
+ "BgAAABAAAAEAAACkBgAA");
+
+ public EnumSet<RedefineState> redefine_states;
+ public RedefineTestObject() {
+ super();
+ redefine_states = EnumSet.noneOf(RedefineState.class);
+ }
+
+ public String toString() {
+ return "RedefineTestObject { states: " + redefine_states.toString() + " current: ORIGINAL }";
+ }
+
+ public void calledFunction() {
+ redefine_states.add(RedefineState.ORIGINAL); // line +0
+ // We will trigger the redefinition using a breakpoint on the next line.
+ doNothing(); // line +2
+ }
+ }
+
+ public static class ClassLoadObject implements TestRunnable {
+ public int cnt;
+ public int baseCallCnt;
+
+ public static final String[] CLASS_NAMES = new String[] {
+ "Lart/Test1953$ClassLoadObject$TC0;",
+ "Lart/Test1953$ClassLoadObject$TC1;",
+ "Lart/Test1953$ClassLoadObject$TC2;",
+ "Lart/Test1953$ClassLoadObject$TC3;",
+ "Lart/Test1953$ClassLoadObject$TC4;",
+ "Lart/Test1953$ClassLoadObject$TC5;",
+ "Lart/Test1953$ClassLoadObject$TC6;",
+ "Lart/Test1953$ClassLoadObject$TC7;",
+ "Lart/Test1953$ClassLoadObject$TC8;",
+ "Lart/Test1953$ClassLoadObject$TC9;",
+ };
+
+ private static int curClass = 0;
+
+ private static class TC0 { public static int foo; static { foo = 1; } }
+ private static class TC1 { public static int foo; static { foo = 2; } }
+ private static class TC2 { public static int foo; static { foo = 3; } }
+ private static class TC3 { public static int foo; static { foo = 4; } }
+ private static class TC4 { public static int foo; static { foo = 5; } }
+ private static class TC5 { public static int foo; static { foo = 6; } }
+ private static class TC6 { public static int foo; static { foo = 7; } }
+ private static class TC7 { public static int foo; static { foo = 8; } }
+ private static class TC8 { public static int foo; static { foo = 9; } }
+ private static class TC9 { public static int foo; static { foo = 10; } }
+
+ public ClassLoadObject() {
+ super();
+ cnt = 0;
+ baseCallCnt = 0;
+ }
+
+ public int getBaseCallCount() {
+ return baseCallCnt;
+ }
+
+ public void run() {
+ baseCallCnt++;
+ if (curClass == 0) {
+ $noprecompile$calledFunction0();
+ } else if (curClass == 1) {
+ $noprecompile$calledFunction1();
+ } else if (curClass == 2) {
+ $noprecompile$calledFunction2();
+ } else if (curClass == 3) {
+ $noprecompile$calledFunction3();
+ } else if (curClass == 4) {
+ $noprecompile$calledFunction4();
+ } else if (curClass == 5) {
+ $noprecompile$calledFunction5();
+ } else if (curClass == 6) {
+ $noprecompile$calledFunction6();
+ } else if (curClass == 7) {
+ $noprecompile$calledFunction7();
+ } else if (curClass == 8) {
+ $noprecompile$calledFunction8();
+ } else if (curClass == 9) {
+ $noprecompile$calledFunction9();
+ }
+ curClass++;
+ }
+
+ public Method getCalledMethod() throws Exception {
+ return this.getClass().getMethod("jnoprecompile$calledFunction" + curClass);
+ }
+
+ // Give these all a tag to prevent 1954 from compiling them (and loading the class as a
+ // consequence).
+ public void $noprecompile$calledFunction0() {
+ cnt++;
+ System.out.println("TC0.foo == " + TC0.foo);
+ }
+
+ public void $noprecompile$calledFunction1() {
+ cnt++;
+ System.out.println("TC1.foo == " + TC1.foo);
+ }
+
+ public void $noprecompile$calledFunction2() {
+ cnt++;
+ System.out.println("TC2.foo == " + TC2.foo);
+ }
+
+ public void $noprecompile$calledFunction3() {
+ cnt++;
+ System.out.println("TC3.foo == " + TC3.foo);
+ }
+
+ public void $noprecompile$calledFunction4() {
+ cnt++;
+ System.out.println("TC4.foo == " + TC4.foo);
+ }
+
+ public void $noprecompile$calledFunction5() {
+ cnt++;
+ System.out.println("TC5.foo == " + TC5.foo);
+ }
+
+ public void $noprecompile$calledFunction6() {
+ cnt++;
+ System.out.println("TC6.foo == " + TC6.foo);
+ }
+
+ public void $noprecompile$calledFunction7() {
+ cnt++;
+ System.out.println("TC7.foo == " + TC7.foo);
+ }
+
+ public void $noprecompile$calledFunction8() {
+ cnt++;
+ System.out.println("TC8.foo == " + TC8.foo);
+ }
+
+ public void $noprecompile$calledFunction9() {
+ cnt++;
+ System.out.println("TC9.foo == " + TC9.foo);
+ }
+
+ public String toString() {
+ return "ClassLoadObject { cnt: " + cnt + ", curClass: " + curClass + "}";
+ }
+ }
+
+ public static class FieldBasedTestObject extends AbstractTestObject implements Runnable {
+ public int cnt;
+ public int TARGET_FIELD;
+ public FieldBasedTestObject() {
+ super();
+ cnt = 0;
+ TARGET_FIELD = 0;
+ }
+
+ public void calledFunction() {
+ cnt++;
+ // We put a watchpoint here and PopFrame when we are at it.
+ TARGET_FIELD += 10;
+ if (cnt == 1) { System.out.println("FAILED: No pop on first call!"); }
+ }
+
+ public String toString() {
+ return "FieldBasedTestObject { cnt: " + cnt + ", TARGET_FIELD: " + TARGET_FIELD + " }";
+ }
+ }
+
+ public static class StandardTestObject extends AbstractTestObject implements Runnable {
+ public int cnt;
+ public final boolean check;
+
+ public StandardTestObject(boolean check) {
+ super();
+ cnt = 0;
+ this.check = check;
+ }
+
+ public StandardTestObject() {
+ this(true);
+ }
+
+ public void calledFunction() {
+ cnt++; // line +0
+ // We put a breakpoint here and PopFrame when we are at it.
+ doNothing(); // line +2
+ if (check && cnt == 1) { System.out.println("FAILED: No pop on first call!"); }
+ }
+
+ public String toString() {
+ return "StandardTestObject { cnt: " + cnt + " }";
+ }
+ }
+
+ public static class SynchronizedFunctionTestObject extends AbstractTestObject implements Runnable {
+ public int cnt;
+
+ public SynchronizedFunctionTestObject() {
+ super();
+ cnt = 0;
+ }
+
+ public synchronized void calledFunction() {
+ cnt++; // line +0
+ // We put a breakpoint here and PopFrame when we are at it.
+ doNothing(); // line +2
+ }
+
+ public String toString() {
+ return "SynchronizedFunctionTestObject { cnt: " + cnt + " }";
+ }
+ }
+ public static class SynchronizedTestObject extends AbstractTestObject implements Runnable {
+ public int cnt;
+ public final Object lock;
+
+ public SynchronizedTestObject() {
+ super();
+ cnt = 0;
+ lock = new Object();
+ }
+
+ public void calledFunction() {
+ synchronized (lock) { // line +0
+ cnt++; // line +1
+ // We put a breakpoint here and PopFrame when we are at it.
+ doNothing(); // line +3
+ }
+ }
+
+ public String toString() {
+ return "SynchronizedTestObject { cnt: " + cnt + " }";
+ }
+ }
+
+ public static class ExceptionCatchTestObject extends AbstractTestObject implements Runnable {
+ public static class TestError extends Error {}
+
+ public int cnt;
+ public ExceptionCatchTestObject() {
+ super();
+ cnt = 0;
+ }
+
+ public void calledFunction() {
+ cnt++;
+ try {
+ doThrow();
+ } catch (TestError e) {
+ System.out.println(e.getClass().getName() + " caught in called function.");
+ }
+ }
+
+ public void doThrow() {
+ throw new TestError();
+ }
+
+ public String toString() {
+ return "ExceptionCatchTestObject { cnt: " + cnt + " }";
+ }
+ }
+
+ public static class ExceptionThrowFarTestObject implements TestRunnable {
+ public static class TestError extends Error {}
+
+ public int cnt;
+ public int baseCallCnt;
+ public final boolean catchInCalled;
+ public ExceptionThrowFarTestObject(boolean catchInCalled) {
+ super();
+ cnt = 0;
+ baseCallCnt = 0;
+ this.catchInCalled = catchInCalled;
+ }
+
+ public int getBaseCallCount() {
+ return baseCallCnt;
+ }
+
+ public void run() {
+ baseCallCnt++;
+ try {
+ callingFunction();
+ } catch (TestError e) {
+ System.out.println(e.getClass().getName() + " thrown and caught!");
+ }
+ }
+
+ public void callingFunction() {
+ calledFunction();
+ }
+ public void calledFunction() {
+ cnt++;
+ if (catchInCalled) {
+ try {
+ throw new TestError(); // We put a watch here.
+ } catch (TestError e) {
+ System.out.println(e.getClass().getName() + " caught in same function.");
+ }
+ } else {
+ throw new TestError(); // We put a watch here.
+ }
+ }
+
+ public Method getCallingMethod() throws Exception {
+ return this.getClass().getMethod("callingFunction");
+ }
+
+ public Method getCalledMethod() throws Exception {
+ return this.getClass().getMethod("calledFunction");
+ }
+
+ public String toString() {
+ return "ExceptionThrowFarTestObject { cnt: " + cnt + " }";
+ }
+ }
+
+ public static class ExceptionOnceObject extends AbstractTestObject {
+ public static final class TestError extends Error {}
+ public int cnt;
+ public final boolean throwInSub;
+ public ExceptionOnceObject(boolean throwInSub) {
+ super();
+ cnt = 0;
+ this.throwInSub = throwInSub;
+ }
+
+ public void calledFunction() {
+ cnt++;
+ if (cnt == 1) {
+ if (throwInSub) {
+ doThrow();
+ } else {
+ throw new TestError();
+ }
+ }
+ }
+
+ public void doThrow() {
+ throw new TestError();
+ }
+
+ public String toString() {
+ return "ExceptionOnceObject { cnt: " + cnt + ", throwInSub: " + throwInSub + " }";
+ }
+ }
+
+ public static class ExceptionThrowTestObject implements TestRunnable {
+ public static class TestError extends Error {}
+
+ public int cnt;
+ public int baseCallCnt;
+ public final boolean catchInCalled;
+ public ExceptionThrowTestObject(boolean catchInCalled) {
+ super();
+ cnt = 0;
+ baseCallCnt = 0;
+ this.catchInCalled = catchInCalled;
+ }
+
+ public int getBaseCallCount() {
+ return baseCallCnt;
+ }
+
+ public void run() {
+ baseCallCnt++;
+ try {
+ calledFunction();
+ } catch (TestError e) {
+ System.out.println(e.getClass().getName() + " thrown and caught!");
+ }
+ }
+
+ public void calledFunction() {
+ cnt++;
+ if (catchInCalled) {
+ try {
+ throw new TestError(); // We put a watch here.
+ } catch (TestError e) {
+ System.out.println(e.getClass().getName() + " caught in same function.");
+ }
+ } else {
+ throw new TestError(); // We put a watch here.
+ }
+ }
+
+ public Method getCalledMethod() throws Exception {
+ return this.getClass().getMethod("calledFunction");
+ }
+
+ public String toString() {
+ return "ExceptionThrowTestObject { cnt: " + cnt + " }";
+ }
+ }
+
+ public static class NativeCalledObject extends AbstractTestObject {
+ public int cnt = 0;
+
+ public native void calledFunction();
+
+ public String toString() {
+ return "NativeCalledObject { cnt: " + cnt + " }";
+ }
+ }
+
+ public static class NativeCallerObject implements TestRunnable {
+ public int baseCnt = 0;
+ public int cnt = 0;
+
+ public int getBaseCallCount() {
+ return baseCnt;
+ }
+
+ public native void run();
+
+ public void calledFunction() {
+ cnt++;
+ // We will stop using a MethodExit event.
+ }
+
+ public Method getCalledMethod() throws Exception {
+ return this.getClass().getMethod("calledFunction");
+ }
+
+ public String toString() {
+ return "NativeCallerObject { cnt: " + cnt + " }";
+ }
+ }
+ public static class SuspendSuddenlyObject extends AbstractTestObject {
+ public volatile boolean stop_spinning = false;
+ public volatile boolean is_spinning = false;
+ public int cnt = 0;
+
+ public void calledFunction() {
+ cnt++;
+ while (!stop_spinning) {
+ is_spinning = true;
+ }
+ }
+
+ public String toString() {
+ return "SuspendSuddenlyObject { cnt: " + cnt + " }";
+ }
+ }
+
+ public static void run(boolean canRunClassLoadTests) throws Exception {
+ new Test1953(canRunClassLoadTests, (x)-> {}).runTests();
+ }
+
+ // This entrypoint is used by CTS only. */
+ public static void run() throws Exception {
+ /* TODO: Due to the way that CTS tests are verified we cannot run class-load-tests since the
+ * verifier will be delayed until runtime and then load the classes all at once. This
+ * makes the test impossible to run.
+ */
+ run(/*canRunClassLoadTests*/ false);
+ }
+
+ public Test1953(boolean canRunClassLoadTests, Consumer<TestRunnable> preTest) {
+ this.canRunClassLoadTests = canRunClassLoadTests;
+ this.preTest = preTest;
+ }
+
+ private Consumer<TestRunnable> preTest;
+
+ public void runTests() throws Exception {
+ setupTest();
+
+ final Method calledFunction = StandardTestObject.class.getDeclaredMethod("calledFunction");
+ final Method doNothingMethod = Test1953.class.getDeclaredMethod("doNothing");
+ // Add a breakpoint on the second line after the start of the function
+ final int line = Breakpoint.locationToLine(calledFunction, 0) + 2;
+ final long loc = Breakpoint.lineToLocation(calledFunction, line);
+ System.out.println("Test stopped using breakpoint");
+ runTestOn(new StandardTestObject(),
+ (thr) -> setupSuspendBreakpointFor(calledFunction, loc, thr),
+ Test1953::clearSuspendBreakpointFor);
+
+ final Method syncFunctionCalledFunction =
+ SynchronizedFunctionTestObject.class.getDeclaredMethod("calledFunction");
+ // Add a breakpoint on the second line after the start of the function
+ // Annoyingly r8 generally has the first instruction (a monitor enter) not be marked as being
+ // on any line but javac has it marked as being on the first line of the function. Just use the
+ // second entry on the line-number table to get the breakpoint. This should be good for both.
+ final long syncFunctionLoc =
+ Breakpoint.getLineNumberTable(syncFunctionCalledFunction)[1].location;
+ System.out.println("Test stopped using breakpoint with declared synchronized function");
+ runTestOn(new SynchronizedFunctionTestObject(),
+ (thr) -> setupSuspendBreakpointFor(syncFunctionCalledFunction, syncFunctionLoc, thr),
+ Test1953::clearSuspendBreakpointFor);
+
+ final Method syncCalledFunction =
+ SynchronizedTestObject.class.getDeclaredMethod("calledFunction");
+ // Add a breakpoint on the second line after the start of the function
+ final int syncLine = Breakpoint.locationToLine(syncCalledFunction, 0) + 3;
+ final long syncLoc = Breakpoint.lineToLocation(syncCalledFunction, syncLine);
+ System.out.println("Test stopped using breakpoint with synchronized block");
+ runTestOn(new SynchronizedTestObject(),
+ (thr) -> setupSuspendBreakpointFor(syncCalledFunction, syncLoc, thr),
+ Test1953::clearSuspendBreakpointFor);
+
+ System.out.println("Test stopped on single step");
+ runTestOn(new StandardTestObject(),
+ (thr) -> setupSuspendSingleStepAt(calledFunction, loc, thr),
+ Test1953::clearSuspendSingleStepFor);
+
+ final Field target_field = FieldBasedTestObject.class.getDeclaredField("TARGET_FIELD");
+ System.out.println("Test stopped on field access");
+ runTestOn(new FieldBasedTestObject(),
+ (thr) -> setupFieldSuspendFor(FieldBasedTestObject.class, target_field, true, thr),
+ Test1953::clearFieldSuspendFor);
+
+ System.out.println("Test stopped on field modification");
+ runTestOn(new FieldBasedTestObject(),
+ (thr) -> setupFieldSuspendFor(FieldBasedTestObject.class, target_field, false, thr),
+ Test1953::clearFieldSuspendFor);
+
+ System.out.println("Test stopped during Method Exit of doNothing");
+ runTestOn(new StandardTestObject(false),
+ (thr) -> setupSuspendMethodEvent(doNothingMethod, /*enter*/ false, thr),
+ Test1953::clearSuspendMethodEvent);
+
+ // NB We need another test to make sure the MethodEntered event is triggered twice.
+ System.out.println("Test stopped during Method Enter of doNothing");
+ runTestOn(new StandardTestObject(false),
+ (thr) -> setupSuspendMethodEvent(doNothingMethod, /*enter*/ true, thr),
+ Test1953::clearSuspendMethodEvent);
+
+ System.out.println("Test stopped during Method Exit of calledFunction");
+ runTestOn(new StandardTestObject(false),
+ (thr) -> setupSuspendMethodEvent(calledFunction, /*enter*/ false, thr),
+ Test1953::clearSuspendMethodEvent);
+
+ System.out.println("Test stopped during Method Enter of calledFunction");
+ runTestOn(new StandardTestObject(false),
+ (thr) -> setupSuspendMethodEvent(calledFunction, /*enter*/ true, thr),
+ Test1953::clearSuspendMethodEvent);
+
+ final Method exceptionOnceCalledMethod =
+ ExceptionOnceObject.class.getDeclaredMethod("calledFunction");
+ System.out.println("Test stopped during Method Exit due to exception thrown in same function");
+ runTestOn(new ExceptionOnceObject(/*throwInSub*/ false),
+ (thr) -> setupSuspendMethodEvent(exceptionOnceCalledMethod, /*enter*/ false, thr),
+ Test1953::clearSuspendMethodEvent);
+
+ System.out.println("Test stopped during Method Exit due to exception thrown in subroutine");
+ runTestOn(new ExceptionOnceObject(/*throwInSub*/ true),
+ (thr) -> setupSuspendMethodEvent(exceptionOnceCalledMethod, /*enter*/ false, thr),
+ Test1953::clearSuspendMethodEvent);
+
+ System.out.println("Test stopped during notifyFramePop without exception on pop of calledFunction");
+ runTestOn(new StandardTestObject(false),
+ (thr) -> setupSuspendPopFrameEvent(1, doNothingMethod, thr),
+ Test1953::clearSuspendPopFrameEvent);
+
+ System.out.println("Test stopped during notifyFramePop without exception on pop of doNothing");
+ runTestOn(new StandardTestObject(false),
+ (thr) -> setupSuspendPopFrameEvent(0, doNothingMethod, thr),
+ Test1953::clearSuspendPopFrameEvent);
+
+ final Method exceptionThrowCalledMethod =
+ ExceptionThrowTestObject.class.getDeclaredMethod("calledFunction");
+ System.out.println("Test stopped during notifyFramePop with exception on pop of calledFunction");
+ runTestOn(new ExceptionThrowTestObject(false),
+ (thr) -> setupSuspendPopFrameEvent(0, exceptionThrowCalledMethod, thr),
+ Test1953::clearSuspendPopFrameEvent);
+
+ final Method exceptionCatchThrowMethod =
+ ExceptionCatchTestObject.class.getDeclaredMethod("doThrow");
+ System.out.println("Test stopped during notifyFramePop with exception on pop of doThrow");
+ runTestOn(new ExceptionCatchTestObject(),
+ (thr) -> setupSuspendPopFrameEvent(0, exceptionCatchThrowMethod, thr),
+ Test1953::clearSuspendPopFrameEvent);
+
+ System.out.println("Test stopped during ExceptionCatch event of calledFunction " +
+ "(catch in called function, throw in called function)");
+ runTestOn(new ExceptionThrowTestObject(true),
+ (thr) -> setupSuspendExceptionEvent(exceptionThrowCalledMethod, /*catch*/ true, thr),
+ Test1953::clearSuspendExceptionEvent);
+
+ final Method exceptionCatchCalledMethod =
+ ExceptionCatchTestObject.class.getDeclaredMethod("calledFunction");
+ System.out.println("Test stopped during ExceptionCatch event of calledFunction " +
+ "(catch in called function, throw in subroutine)");
+ runTestOn(new ExceptionCatchTestObject(),
+ (thr) -> setupSuspendExceptionEvent(exceptionCatchCalledMethod, /*catch*/ true, thr),
+ Test1953::clearSuspendExceptionEvent);
+
+ System.out.println("Test stopped during Exception event of calledFunction " +
+ "(catch in calling function)");
+ runTestOn(new ExceptionThrowTestObject(false),
+ (thr) -> setupSuspendExceptionEvent(exceptionThrowCalledMethod, /*catch*/ false, thr),
+ Test1953::clearSuspendExceptionEvent);
+
+ System.out.println("Test stopped during Exception event of calledFunction " +
+ "(catch in called function)");
+ runTestOn(new ExceptionThrowTestObject(true),
+ (thr) -> setupSuspendExceptionEvent(exceptionThrowCalledMethod, /*catch*/ false, thr),
+ Test1953::clearSuspendExceptionEvent);
+
+ final Method exceptionThrowFarCalledMethod =
+ ExceptionThrowFarTestObject.class.getDeclaredMethod("calledFunction");
+ System.out.println("Test stopped during Exception event of calledFunction " +
+ "(catch in parent of calling function)");
+ runTestOn(new ExceptionThrowFarTestObject(false),
+ (thr) -> setupSuspendExceptionEvent(exceptionThrowFarCalledMethod, /*catch*/ false, thr),
+ Test1953::clearSuspendExceptionEvent);
+
+ System.out.println("Test stopped during Exception event of calledFunction " +
+ "(catch in called function)");
+ runTestOn(new ExceptionThrowFarTestObject(true),
+ (thr) -> setupSuspendExceptionEvent(exceptionThrowFarCalledMethod, /*catch*/ false, thr),
+ Test1953::clearSuspendExceptionEvent);
+
+ // These tests are disabled for either the RI (b/116003018) or for jvmti-stress. For the
+ // later it is due to the additional agent causing classes to be loaded earlier as it forces
+ // deeper verification during class redefinition, causing failures.
+ // NB the agent is prevented from popping frames in either of these events in ART. See
+ // b/117615146 for more information about this restriction.
+ if (canRunClassLoadTests && CanRunClassLoadingTests()) {
+ // This test doesn't work on RI since the RI disallows use of PopFrame during a ClassLoad
+ // event. See b/116003018 for more information.
+ System.out.println("Test stopped during a ClassLoad event.");
+ runTestOn(new ClassLoadObject(),
+ (thr) -> setupSuspendClassEvent(EVENT_TYPE_CLASS_LOAD, ClassLoadObject.CLASS_NAMES, thr),
+ Test1953::clearSuspendClassEvent);
+
+ // The RI handles a PopFrame during a ClassPrepare event incorrectly. See b/116003018 for
+ // more information.
+ System.out.println("Test stopped during a ClassPrepare event.");
+ runTestOn(new ClassLoadObject(),
+ (thr) -> setupSuspendClassEvent(EVENT_TYPE_CLASS_PREPARE,
+ ClassLoadObject.CLASS_NAMES,
+ thr),
+ Test1953::clearSuspendClassEvent);
+ }
+ System.out.println("Test stopped during random Suspend.");
+ final SuspendSuddenlyObject sso = new SuspendSuddenlyObject();
+ runTestOn(
+ sso,
+ new TestSuspender() {
+ public void setup(Thread thr) { }
+ public void waitForSuspend(Thread thr) {
+ while (!sso.is_spinning) {}
+ Suspension.suspend(thr);
+ }
+ public void cleanup(Thread thr) {
+ sso.stop_spinning = true;
+ }
+ });
+
+ final Method redefineCalledFunction =
+ RedefineTestObject.class.getDeclaredMethod("calledFunction");
+ final int redefLine = Breakpoint.locationToLine(redefineCalledFunction, 0) + 2;
+ final long redefLoc = Breakpoint.lineToLocation(redefineCalledFunction, redefLine);
+ System.out.println("Test redefining frame being popped.");
+ runTestOn(new RedefineTestObject(),
+ (thr) -> setupSuspendBreakpointFor(redefineCalledFunction, redefLoc, thr),
+ (thr) -> {
+ clearSuspendBreakpointFor(thr);
+ Redefinition.doCommonClassRedefinition(RedefineTestObject.class,
+ RedefineTestObject.CLASS_BYTES,
+ RedefineTestObject.DEX_BYTES);
+ });
+
+ System.out.println("Test stopped during a native method fails");
+ runTestOn(new NativeCalledObject(),
+ Test1953::setupWaitForNativeCall,
+ Test1953::clearWaitForNativeCall);
+
+ System.out.println("Test stopped in a method called by native fails");
+ final Method nativeCallerMethod = NativeCallerObject.class.getDeclaredMethod("calledFunction");
+ runTestOn(new NativeCallerObject(),
+ (thr) -> setupSuspendMethodEvent(nativeCallerMethod, /*enter*/ false, thr),
+ Test1953::clearSuspendMethodEvent);
+ }
+
+ // Volatile is to prevent any future optimizations that could invalidate this test by doing
+ // constant propagation and eliminating the failing paths before the verifier is able to load the
+ // class.
+ static volatile boolean ranClassLoadTest = false;
+ static boolean classesPreverified = false;
+ private static final class RCLT0 { public void foo() {} }
+ private static final class RCLT1 { public void foo() {} }
+ // If classes are not preverified for some reason (interp-ac, no-image, etc) the verifier will
+ // actually load classes as it runs. This means that we cannot use the class-load tests as they
+ // are written. TODO Support this.
+ public boolean CanRunClassLoadingTests() {
+ if (ranClassLoadTest) {
+ return classesPreverified;
+ }
+ if (!ranClassLoadTest) {
+ // Only this will ever be executed.
+ new RCLT0().foo();
+ } else {
+ // This will never be executed. If classes are not preverified the verifier will load RCLT1
+ // when the enclosing method is run. This behavior makes the class-load/prepare test cases
+ // impossible to successfully run (they will deadlock).
+ new RCLT1().foo();
+ System.out.println("FAILURE: UNREACHABLE Location!");
+ }
+ classesPreverified = !isClassLoaded("Lart/Test1953$RCLT1;");
+ ranClassLoadTest = true;
+ return classesPreverified;
+ }
+
+ public static native boolean isClassLoaded(String name);
+
+ public static native void setupTest();
+ public static native void popFrame(Thread thr);
+
+ public static native void setupSuspendBreakpointFor(Executable meth, long loc, Thread thr);
+ public static native void clearSuspendBreakpointFor(Thread thr);
+
+ public static native void setupSuspendSingleStepAt(Executable meth, long loc, Thread thr);
+ public static native void clearSuspendSingleStepFor(Thread thr);
+
+ public static native void setupFieldSuspendFor(Class klass, Field f, boolean access, Thread thr);
+ public static native void clearFieldSuspendFor(Thread thr);
+
+ public static native void setupSuspendMethodEvent(Executable meth, boolean enter, Thread thr);
+ public static native void clearSuspendMethodEvent(Thread thr);
+
+ public static native void setupSuspendExceptionEvent(
+ Executable meth, boolean is_catch, Thread thr);
+ public static native void clearSuspendExceptionEvent(Thread thr);
+
+ public static native void setupSuspendPopFrameEvent(
+ int offset, Executable breakpointFunction, Thread thr);
+ public static native void clearSuspendPopFrameEvent(Thread thr);
+
+ public static final int EVENT_TYPE_CLASS_LOAD = 55;
+ public static final int EVENT_TYPE_CLASS_PREPARE = 56;
+ public static native void setupSuspendClassEvent(
+ int eventType, String[] interestingNames, Thread thr);
+ public static native void clearSuspendClassEvent(Thread thr);
+
+ public static native void setupWaitForNativeCall(Thread thr);
+ public static native void clearWaitForNativeCall(Thread thr);
+
+ public static native void waitForSuspendHit(Thread thr);
+}
diff --git a/test/1954-pop-frame-jit/check b/test/1954-pop-frame-jit/check
new file mode 100755
index 0000000000..10b87cc286
--- /dev/null
+++ b/test/1954-pop-frame-jit/check
@@ -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.
+
+# 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.txt < jvm-expected.patch >/dev/null && ./default-check "$@")
diff --git a/test/1954-pop-frame-jit/expected.txt b/test/1954-pop-frame-jit/expected.txt
new file mode 100644
index 0000000000..a20a045ffa
--- /dev/null
+++ b/test/1954-pop-frame-jit/expected.txt
@@ -0,0 +1,118 @@
+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
diff --git a/test/1954-pop-frame-jit/info.txt b/test/1954-pop-frame-jit/info.txt
new file mode 100644
index 0000000000..b5eb5464b7
--- /dev/null
+++ b/test/1954-pop-frame-jit/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/1954-pop-frame-jit/jvm-expected.patch b/test/1954-pop-frame-jit/jvm-expected.patch
new file mode 100644
index 0000000000..718f8ad839
--- /dev/null
+++ b/test/1954-pop-frame-jit/jvm-expected.patch
@@ -0,0 +1,21 @@
+75,94d74
+< 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
new file mode 100755
index 0000000000..d16d4e6091
--- /dev/null
+++ b/test/1954-pop-frame-jit/run
@@ -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.
+
+# 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/src/Main.java b/test/1954-pop-frame-jit/src/Main.java
new file mode 100644
index 0000000000..12defcda9a
--- /dev/null
+++ b/test/1954-pop-frame-jit/src/Main.java
@@ -0,0 +1,60 @@
+/*
+ * 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 java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import java.time.Duration;
+
+import java.util.concurrent.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.Random;
+import java.util.Stack;
+import java.util.Vector;
+
+import java.util.function.Supplier;
+
+import art.*;
+
+public class Main extends Test1953 {
+ public Main(boolean run_class_load_tests) {
+ super(run_class_load_tests, (testObj) -> {
+ try {
+ // Make sure everything is jitted in the method. We do this before calling setup since the
+ // suspend setup might make it impossible to jit the methods (by setting breakpoints or
+ // something).
+ for (Method m : testObj.getClass().getMethods()) {
+ if ((m.getModifiers() & Modifier.NATIVE) == 0 &&
+ !m.getName().startsWith("$noprecompile$")) {
+ ensureMethodJitCompiled(m);
+ }
+ }
+ } catch (Exception e) {}
+ });
+ }
+
+ public static void main(String[] args) throws Exception {
+ new Main(!Arrays.asList(args).contains("DISABLE_CLASS_LOAD_TESTS")).runTests();
+ }
+
+ public static native void ensureMethodJitCompiled(Method meth);
+}
diff --git a/test/1954-pop-frame-jit/src/art/Breakpoint.java b/test/1954-pop-frame-jit/src/art/Breakpoint.java
new file mode 100644
index 0000000000..bbb89f707f
--- /dev/null
+++ b/test/1954-pop-frame-jit/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * 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;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/1954-pop-frame-jit/src/art/Redefinition.java b/test/1954-pop-frame-jit/src/art/Redefinition.java
new file mode 100644
index 0000000000..56d2938a01
--- /dev/null
+++ b/test/1954-pop-frame-jit/src/art/Redefinition.java
@@ -0,0 +1,91 @@
+/*
+ * 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;
+
+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 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);
+
+ 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);
+ }
+ 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);
+ }
+ }
+
+ 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/1954-pop-frame-jit/src/art/StackTrace.java b/test/1954-pop-frame-jit/src/art/StackTrace.java
new file mode 100644
index 0000000000..2ea2f201e8
--- /dev/null
+++ b/test/1954-pop-frame-jit/src/art/StackTrace.java
@@ -0,0 +1,68 @@
+/*
+ * 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;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Executable;
+
+public class StackTrace {
+ public static class StackFrameData {
+ public final Thread thr;
+ public final Executable method;
+ public final long current_location;
+ public final int depth;
+
+ public StackFrameData(Thread thr, Executable e, long loc, int depth) {
+ this.thr = thr;
+ this.method = e;
+ this.current_location = loc;
+ this.depth = depth;
+ }
+ @Override
+ public String toString() {
+ return String.format(
+ "StackFrameData { thr: '%s', method: '%s', loc: %d, depth: %d }",
+ this.thr,
+ this.method,
+ this.current_location,
+ this.depth);
+ }
+ }
+
+ public static native int GetStackDepth(Thread thr);
+
+ private static native StackFrameData[] nativeGetStackTrace(Thread thr);
+
+ public static StackFrameData[] GetStackTrace(Thread thr) {
+ // The RI seems to give inconsistent (and sometimes nonsensical) results if the thread is not
+ // suspended. The spec says that not being suspended is fine but since we want this to be
+ // consistent we will suspend for the RI.
+ boolean suspend_thread =
+ !System.getProperty("java.vm.name").equals("Dalvik") &&
+ !thr.equals(Thread.currentThread()) &&
+ !Suspension.isSuspended(thr);
+ if (suspend_thread) {
+ Suspension.suspend(thr);
+ }
+ StackFrameData[] out = nativeGetStackTrace(thr);
+ if (suspend_thread) {
+ Suspension.resume(thr);
+ }
+ return out;
+ }
+}
+
diff --git a/test/1954-pop-frame-jit/src/art/Suspension.java b/test/1954-pop-frame-jit/src/art/Suspension.java
new file mode 100644
index 0000000000..16e62ccac9
--- /dev/null
+++ b/test/1954-pop-frame-jit/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * 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 Suspension {
+ // Suspends a thread using jvmti.
+ public native static void suspend(Thread thr);
+
+ // Resumes a thread using jvmti.
+ public native static void resume(Thread thr);
+
+ public native static boolean isSuspended(Thread thr);
+
+ public native static int[] suspendList(Thread... threads);
+ public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1954-pop-frame-jit/src/art/Test1953.java b/test/1954-pop-frame-jit/src/art/Test1953.java
new file mode 120000
index 0000000000..f28143484a
--- /dev/null
+++ b/test/1954-pop-frame-jit/src/art/Test1953.java
@@ -0,0 +1 @@
+../../../1953-pop-frame/src/art/Test1953.java \ No newline at end of file
diff --git a/test/1955-pop-frame-jit-called/check b/test/1955-pop-frame-jit-called/check
new file mode 100755
index 0000000000..10b87cc286
--- /dev/null
+++ b/test/1955-pop-frame-jit-called/check
@@ -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.
+
+# 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.txt < jvm-expected.patch >/dev/null && ./default-check "$@")
diff --git a/test/1955-pop-frame-jit-called/expected.txt b/test/1955-pop-frame-jit-called/expected.txt
new file mode 100644
index 0000000000..a20a045ffa
--- /dev/null
+++ b/test/1955-pop-frame-jit-called/expected.txt
@@ -0,0 +1,118 @@
+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
diff --git a/test/1955-pop-frame-jit-called/info.txt b/test/1955-pop-frame-jit-called/info.txt
new file mode 100644
index 0000000000..b5eb5464b7
--- /dev/null
+++ b/test/1955-pop-frame-jit-called/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/1955-pop-frame-jit-called/jvm-expected.patch b/test/1955-pop-frame-jit-called/jvm-expected.patch
new file mode 100644
index 0000000000..718f8ad839
--- /dev/null
+++ b/test/1955-pop-frame-jit-called/jvm-expected.patch
@@ -0,0 +1,21 @@
+75,94d74
+< 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
new file mode 100755
index 0000000000..2984461057
--- /dev/null
+++ b/test/1955-pop-frame-jit-called/run
@@ -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.
+
+# 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/src/Main.java b/test/1955-pop-frame-jit-called/src/Main.java
new file mode 100644
index 0000000000..30a42ea15a
--- /dev/null
+++ b/test/1955-pop-frame-jit-called/src/Main.java
@@ -0,0 +1,53 @@
+/*
+ * 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 java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import java.time.Duration;
+
+import java.util.concurrent.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.Random;
+import java.util.Stack;
+import java.util.Vector;
+
+import java.util.function.Supplier;
+
+import art.*;
+
+public class Main extends Test1953 {
+ public Main(boolean run_class_load_tests) {
+ super(run_class_load_tests, (testObj) -> {
+ try {
+ // Make sure the called method is jitted
+ ensureMethodJitCompiled(testObj.getCalledMethod());
+ } catch (Exception e) {}
+ });
+ }
+
+ public static void main(String[] args) throws Exception {
+ new Main(!Arrays.asList(args).contains("DISABLE_CLASS_LOAD_TESTS")).runTests();
+ }
+
+ public static native void ensureMethodJitCompiled(Method meth);
+}
diff --git a/test/1955-pop-frame-jit-called/src/art/Breakpoint.java b/test/1955-pop-frame-jit-called/src/art/Breakpoint.java
new file mode 100644
index 0000000000..bbb89f707f
--- /dev/null
+++ b/test/1955-pop-frame-jit-called/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * 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;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/1955-pop-frame-jit-called/src/art/Redefinition.java b/test/1955-pop-frame-jit-called/src/art/Redefinition.java
new file mode 100644
index 0000000000..56d2938a01
--- /dev/null
+++ b/test/1955-pop-frame-jit-called/src/art/Redefinition.java
@@ -0,0 +1,91 @@
+/*
+ * 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;
+
+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 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);
+
+ 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);
+ }
+ 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);
+ }
+ }
+
+ 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/1955-pop-frame-jit-called/src/art/StackTrace.java b/test/1955-pop-frame-jit-called/src/art/StackTrace.java
new file mode 100644
index 0000000000..2ea2f201e8
--- /dev/null
+++ b/test/1955-pop-frame-jit-called/src/art/StackTrace.java
@@ -0,0 +1,68 @@
+/*
+ * 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;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Executable;
+
+public class StackTrace {
+ public static class StackFrameData {
+ public final Thread thr;
+ public final Executable method;
+ public final long current_location;
+ public final int depth;
+
+ public StackFrameData(Thread thr, Executable e, long loc, int depth) {
+ this.thr = thr;
+ this.method = e;
+ this.current_location = loc;
+ this.depth = depth;
+ }
+ @Override
+ public String toString() {
+ return String.format(
+ "StackFrameData { thr: '%s', method: '%s', loc: %d, depth: %d }",
+ this.thr,
+ this.method,
+ this.current_location,
+ this.depth);
+ }
+ }
+
+ public static native int GetStackDepth(Thread thr);
+
+ private static native StackFrameData[] nativeGetStackTrace(Thread thr);
+
+ public static StackFrameData[] GetStackTrace(Thread thr) {
+ // The RI seems to give inconsistent (and sometimes nonsensical) results if the thread is not
+ // suspended. The spec says that not being suspended is fine but since we want this to be
+ // consistent we will suspend for the RI.
+ boolean suspend_thread =
+ !System.getProperty("java.vm.name").equals("Dalvik") &&
+ !thr.equals(Thread.currentThread()) &&
+ !Suspension.isSuspended(thr);
+ if (suspend_thread) {
+ Suspension.suspend(thr);
+ }
+ StackFrameData[] out = nativeGetStackTrace(thr);
+ if (suspend_thread) {
+ Suspension.resume(thr);
+ }
+ return out;
+ }
+}
+
diff --git a/test/1955-pop-frame-jit-called/src/art/Suspension.java b/test/1955-pop-frame-jit-called/src/art/Suspension.java
new file mode 100644
index 0000000000..16e62ccac9
--- /dev/null
+++ b/test/1955-pop-frame-jit-called/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * 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 Suspension {
+ // Suspends a thread using jvmti.
+ public native static void suspend(Thread thr);
+
+ // Resumes a thread using jvmti.
+ public native static void resume(Thread thr);
+
+ public native static boolean isSuspended(Thread thr);
+
+ public native static int[] suspendList(Thread... threads);
+ public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1955-pop-frame-jit-called/src/art/Test1953.java b/test/1955-pop-frame-jit-called/src/art/Test1953.java
new file mode 120000
index 0000000000..f28143484a
--- /dev/null
+++ b/test/1955-pop-frame-jit-called/src/art/Test1953.java
@@ -0,0 +1 @@
+../../../1953-pop-frame/src/art/Test1953.java \ No newline at end of file
diff --git a/test/1956-pop-frame-jit-calling/check b/test/1956-pop-frame-jit-calling/check
new file mode 100755
index 0000000000..10b87cc286
--- /dev/null
+++ b/test/1956-pop-frame-jit-calling/check
@@ -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.
+
+# 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.txt < jvm-expected.patch >/dev/null && ./default-check "$@")
diff --git a/test/1956-pop-frame-jit-calling/expected.txt b/test/1956-pop-frame-jit-calling/expected.txt
new file mode 100644
index 0000000000..a20a045ffa
--- /dev/null
+++ b/test/1956-pop-frame-jit-calling/expected.txt
@@ -0,0 +1,118 @@
+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
diff --git a/test/1956-pop-frame-jit-calling/info.txt b/test/1956-pop-frame-jit-calling/info.txt
new file mode 100644
index 0000000000..b5eb5464b7
--- /dev/null
+++ b/test/1956-pop-frame-jit-calling/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/1956-pop-frame-jit-calling/jvm-expected.patch b/test/1956-pop-frame-jit-calling/jvm-expected.patch
new file mode 100644
index 0000000000..718f8ad839
--- /dev/null
+++ b/test/1956-pop-frame-jit-calling/jvm-expected.patch
@@ -0,0 +1,21 @@
+75,94d74
+< 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
new file mode 100755
index 0000000000..2984461057
--- /dev/null
+++ b/test/1956-pop-frame-jit-calling/run
@@ -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.
+
+# 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/src/Main.java b/test/1956-pop-frame-jit-calling/src/Main.java
new file mode 100644
index 0000000000..c44e035a8d
--- /dev/null
+++ b/test/1956-pop-frame-jit-calling/src/Main.java
@@ -0,0 +1,53 @@
+/*
+ * 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 java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import java.time.Duration;
+
+import java.util.concurrent.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.Random;
+import java.util.Stack;
+import java.util.Vector;
+
+import java.util.function.Supplier;
+
+import art.*;
+
+public class Main extends Test1953 {
+ public Main(boolean run_class_load_tests) {
+ super(run_class_load_tests, (testObj) -> {
+ try {
+ // Make sure the calling method is jitted
+ ensureMethodJitCompiled(testObj.getCallingMethod());
+ } catch (Exception e) {}
+ });
+ }
+
+ public static void main(String[] args) throws Exception {
+ new Main(!Arrays.asList(args).contains("DISABLE_CLASS_LOAD_TESTS")).runTests();
+ }
+
+ public static native void ensureMethodJitCompiled(Method meth);
+}
diff --git a/test/1956-pop-frame-jit-calling/src/art/Breakpoint.java b/test/1956-pop-frame-jit-calling/src/art/Breakpoint.java
new file mode 100644
index 0000000000..bbb89f707f
--- /dev/null
+++ b/test/1956-pop-frame-jit-calling/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * 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;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/1956-pop-frame-jit-calling/src/art/Redefinition.java b/test/1956-pop-frame-jit-calling/src/art/Redefinition.java
new file mode 100644
index 0000000000..56d2938a01
--- /dev/null
+++ b/test/1956-pop-frame-jit-calling/src/art/Redefinition.java
@@ -0,0 +1,91 @@
+/*
+ * 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;
+
+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 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);
+
+ 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);
+ }
+ 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);
+ }
+ }
+
+ 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/1956-pop-frame-jit-calling/src/art/StackTrace.java b/test/1956-pop-frame-jit-calling/src/art/StackTrace.java
new file mode 100644
index 0000000000..2ea2f201e8
--- /dev/null
+++ b/test/1956-pop-frame-jit-calling/src/art/StackTrace.java
@@ -0,0 +1,68 @@
+/*
+ * 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;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Executable;
+
+public class StackTrace {
+ public static class StackFrameData {
+ public final Thread thr;
+ public final Executable method;
+ public final long current_location;
+ public final int depth;
+
+ public StackFrameData(Thread thr, Executable e, long loc, int depth) {
+ this.thr = thr;
+ this.method = e;
+ this.current_location = loc;
+ this.depth = depth;
+ }
+ @Override
+ public String toString() {
+ return String.format(
+ "StackFrameData { thr: '%s', method: '%s', loc: %d, depth: %d }",
+ this.thr,
+ this.method,
+ this.current_location,
+ this.depth);
+ }
+ }
+
+ public static native int GetStackDepth(Thread thr);
+
+ private static native StackFrameData[] nativeGetStackTrace(Thread thr);
+
+ public static StackFrameData[] GetStackTrace(Thread thr) {
+ // The RI seems to give inconsistent (and sometimes nonsensical) results if the thread is not
+ // suspended. The spec says that not being suspended is fine but since we want this to be
+ // consistent we will suspend for the RI.
+ boolean suspend_thread =
+ !System.getProperty("java.vm.name").equals("Dalvik") &&
+ !thr.equals(Thread.currentThread()) &&
+ !Suspension.isSuspended(thr);
+ if (suspend_thread) {
+ Suspension.suspend(thr);
+ }
+ StackFrameData[] out = nativeGetStackTrace(thr);
+ if (suspend_thread) {
+ Suspension.resume(thr);
+ }
+ return out;
+ }
+}
+
diff --git a/test/1956-pop-frame-jit-calling/src/art/Suspension.java b/test/1956-pop-frame-jit-calling/src/art/Suspension.java
new file mode 100644
index 0000000000..16e62ccac9
--- /dev/null
+++ b/test/1956-pop-frame-jit-calling/src/art/Suspension.java
@@ -0,0 +1,30 @@
+/*
+ * 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 Suspension {
+ // Suspends a thread using jvmti.
+ public native static void suspend(Thread thr);
+
+ // Resumes a thread using jvmti.
+ public native static void resume(Thread thr);
+
+ public native static boolean isSuspended(Thread thr);
+
+ public native static int[] suspendList(Thread... threads);
+ public native static int[] resumeList(Thread... threads);
+}
diff --git a/test/1956-pop-frame-jit-calling/src/art/Test1953.java b/test/1956-pop-frame-jit-calling/src/art/Test1953.java
new file mode 120000
index 0000000000..f28143484a
--- /dev/null
+++ b/test/1956-pop-frame-jit-calling/src/art/Test1953.java
@@ -0,0 +1 @@
+../../../1953-pop-frame/src/art/Test1953.java \ No newline at end of file
diff --git a/test/686-get-this/expected.txt b/test/686-get-this/expected.txt
new file mode 100644
index 0000000000..6a5618ebc6
--- /dev/null
+++ b/test/686-get-this/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/686-get-this/info.txt b/test/686-get-this/info.txt
new file mode 100644
index 0000000000..7227bad602
--- /dev/null
+++ b/test/686-get-this/info.txt
@@ -0,0 +1,2 @@
+Test that we can successfully call StackVisitor.GetThis() even when
+'this' gets overwritten.
diff --git a/test/686-get-this/smali/Test.smali b/test/686-get-this/smali/Test.smali
new file mode 100644
index 0000000000..533f60774e
--- /dev/null
+++ b/test/686-get-this/smali/Test.smali
@@ -0,0 +1,45 @@
+# 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.
+
+.class public LTest;
+.super Ljava/lang/Object;
+
+.method public constructor <init>()V
+ .registers 2
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ const/4 v0, 0x1
+ sput v0, LTest;->field:I
+ return-void
+.end method
+
+
+.method public testEmpty()V
+ .registers 2
+ const/4 p0, 0x1
+ invoke-static {}, LMain;->getThisOfCaller()Ljava/lang/Object;
+ move-result-object v0
+ sput-object v0, LMain;->field:Ljava/lang/Object;
+ return-void
+.end method
+
+.method public testPrimitive()I
+ .registers 2
+ sget p0, LTest;->field:I
+ invoke-static {}, LMain;->getThisOfCaller()Ljava/lang/Object;
+ move-result-object v0
+ sput-object v0, LMain;->field:Ljava/lang/Object;
+ return p0
+.end method
+
+.field static public field:I
diff --git a/test/686-get-this/src/Main.java b/test/686-get-this/src/Main.java
new file mode 100644
index 0000000000..4ea5301f2d
--- /dev/null
+++ b/test/686-get-this/src/Main.java
@@ -0,0 +1,47 @@
+/*
+ * 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.Method;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+
+ Class<?> c = Class.forName("Test");
+ ensureJitCompiled(c, "testEmpty");
+ ensureJitCompiled(c, "testPrimitive");
+
+ Method m = c.getMethod("testEmpty");
+ m.invoke(c.newInstance());
+ if (field != null) {
+ throw new Error("Expected null");
+ }
+
+ m = c.getMethod("testPrimitive");
+ int a = (Integer)m.invoke(c.newInstance());
+ if (a != 1) {
+ throw new Error("Expected 1, got " + a);
+ }
+ if (field != null) {
+ throw new Error("Expected null");
+ }
+ }
+
+ public static Object field;
+
+ private static native void ensureJitCompiled(Class<?> itf, String method_name);
+ public static native Object getThisOfCaller();
+}
diff --git a/test/Android.bp b/test/Android.bp
index 8f23058cf7..8c1c1bf32e 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -291,6 +291,7 @@ art_cc_defaults {
"1946-list-descriptors/descriptors.cc",
"1950-unprepared-transform/unprepared_transform.cc",
"1951-monitor-enter-no-suspend/raw_monitor.cc",
+ "1953-pop-frame/pop_frame.cc",
],
// Use NDK-compatible headers for ctstiagent.
header_libs: [
@@ -320,6 +321,7 @@ art_cc_defaults {
"983-source-transform-verify/source_transform_art.cc",
"1940-ddms-ext/ddm_ext.cc",
"1944-sudden-exit/sudden_exit.cc",
+ // "1952-pop-frame-jit/pop_frame.cc",
],
static_libs: [
"libz",
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index ffaa2cd3c7..64c1d4f1b8 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -27,32 +27,32 @@ TEST_ART_RUN_TEST_DEPENDENCIES := \
# We need dex2oat and dalvikvm on the target as well as the core images (all images as we sync
# only once).
-TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_EXECUTABLES) $(TARGET_CORE_IMG_OUTS)
+ART_TEST_TARGET_RUN_TEST_DEPENDENCIES := $(ART_TARGET_EXECUTABLES) $(TARGET_CORE_IMG_OUTS)
# Also need libartagent.
-TEST_ART_TARGET_SYNC_DEPS += libartagent-target libartagentd-target
+ART_TEST_TARGET_RUN_TEST_DEPENDENCIES += libartagent-target libartagentd-target
# Also need libtiagent.
-TEST_ART_TARGET_SYNC_DEPS += libtiagent-target libtiagentd-target
+ART_TEST_TARGET_RUN_TEST_DEPENDENCIES += libtiagent-target libtiagentd-target
# Also need libtistress.
-TEST_ART_TARGET_SYNC_DEPS += libtistress-target libtistressd-target
+ART_TEST_TARGET_RUN_TEST_DEPENDENCIES += libtistress-target libtistressd-target
# Also need libarttest.
-TEST_ART_TARGET_SYNC_DEPS += libarttest-target libarttestd-target
+ART_TEST_TARGET_RUN_TEST_DEPENDENCIES += libarttest-target libarttestd-target
# Also need libnativebridgetest.
-TEST_ART_TARGET_SYNC_DEPS += libnativebridgetest-target
+ART_TEST_TARGET_RUN_TEST_DEPENDENCIES += libnativebridgetest-target
# Also need libopenjdkjvmti.
-TEST_ART_TARGET_SYNC_DEPS += libopenjdkjvmti-target libopenjdkjvmtid-target
-
-TEST_ART_TARGET_SYNC_DEPS += $(TARGET_OUT_JAVA_LIBRARIES)/core-libart-testdex.jar
-TEST_ART_TARGET_SYNC_DEPS += $(TARGET_OUT_JAVA_LIBRARIES)/core-oj-testdex.jar
-TEST_ART_TARGET_SYNC_DEPS += $(TARGET_OUT_JAVA_LIBRARIES)/core-simple-testdex.jar
-TEST_ART_TARGET_SYNC_DEPS += $(TARGET_OUT_JAVA_LIBRARIES)/okhttp-testdex.jar
-TEST_ART_TARGET_SYNC_DEPS += $(TARGET_OUT_JAVA_LIBRARIES)/bouncycastle-testdex.jar
-TEST_ART_TARGET_SYNC_DEPS += $(TARGET_OUT_JAVA_LIBRARIES)/conscrypt-testdex.jar
+ART_TEST_TARGET_RUN_TEST_DEPENDENCIES += libopenjdkjvmti-target libopenjdkjvmtid-target
+
+ART_TEST_TARGET_RUN_TEST_DEPENDENCIES += $(TARGET_OUT_JAVA_LIBRARIES)/core-libart-testdex.jar
+ART_TEST_TARGET_RUN_TEST_DEPENDENCIES += $(TARGET_OUT_JAVA_LIBRARIES)/core-oj-testdex.jar
+ART_TEST_TARGET_RUN_TEST_DEPENDENCIES += $(TARGET_OUT_JAVA_LIBRARIES)/core-simple-testdex.jar
+ART_TEST_TARGET_RUN_TEST_DEPENDENCIES += $(TARGET_OUT_JAVA_LIBRARIES)/okhttp-testdex.jar
+ART_TEST_TARGET_RUN_TEST_DEPENDENCIES += $(TARGET_OUT_JAVA_LIBRARIES)/bouncycastle-testdex.jar
+ART_TEST_TARGET_RUN_TEST_DEPENDENCIES += $(TARGET_OUT_JAVA_LIBRARIES)/conscrypt-testdex.jar
# All tests require the host executables. The tests also depend on the core images, but on
# specific version depending on the compiler.
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index 4967834490..65127fcab1 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -21,11 +21,13 @@
#include "art_method-inl.h"
#include "base/enums.h"
+#include "common_throws.h"
#include "dex/dex_file-inl.h"
#include "instrumentation.h"
#include "jit/jit.h"
#include "jit/jit_code_cache.h"
#include "jit/profiling_info.h"
+#include "jni/jni_internal.h"
#include "mirror/class-inl.h"
#include "nativehelper/ScopedUtfChars.h"
#include "oat_file.h"
@@ -195,6 +197,56 @@ extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasJitCompiledCode(JNIEnv* env,
return jit->GetCodeCache()->ContainsMethod(method);
}
+static void ForceJitCompiled(Thread* self, ArtMethod* method) REQUIRES(!Locks::mutator_lock_) {
+ {
+ ScopedObjectAccess soa(self);
+ if (method->IsNative()) {
+ std::string msg(method->PrettyMethod());
+ msg += ": is native";
+ ThrowIllegalArgumentException(msg.c_str());
+ return;
+ } else if (!Runtime::Current()->GetRuntimeCallbacks()->IsMethodSafeToJit(method)) {
+ std::string msg(method->PrettyMethod());
+ msg += ": is not safe to jit!";
+ ThrowIllegalStateException(msg.c_str());
+ return;
+ }
+ }
+ jit::Jit* jit = GetJitIfEnabled();
+ jit::JitCodeCache* code_cache = jit->GetCodeCache();
+ // Update the code cache to make sure the JIT code does not get deleted.
+ // Note: this will apply to all JIT compilations.
+ code_cache->SetGarbageCollectCode(false);
+ while (true) {
+ if (code_cache->WillExecuteJitCode(method)) {
+ break;
+ } else {
+ // Sleep to yield to the compiler thread.
+ usleep(1000);
+ ScopedObjectAccess soa(self);
+ // Make sure there is a profiling info, required by the compiler.
+ ProfilingInfo::Create(self, method, /* retry_allocation */ true);
+ // Will either ensure it's compiled or do the compilation itself.
+ jit->CompileMethod(method, self, /* osr */ false);
+ }
+ }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_ensureMethodJitCompiled(JNIEnv*, jclass, jobject meth) {
+ jit::Jit* jit = GetJitIfEnabled();
+ if (jit == nullptr) {
+ return;
+ }
+
+ Thread* self = Thread::Current();
+ ArtMethod* method;
+ {
+ ScopedObjectAccess soa(self);
+ method = ArtMethod::FromReflectedMethod(soa, meth);
+ }
+ ForceJitCompiled(self, method);
+}
+
extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitCompiled(JNIEnv* env,
jclass,
jclass cls,
@@ -219,24 +271,7 @@ extern "C" JNIEXPORT void JNICALL Java_Main_ensureJitCompiled(JNIEnv* env,
}
DCHECK(method != nullptr) << "Unable to find method called " << chars.c_str();
}
-
- jit::JitCodeCache* code_cache = jit->GetCodeCache();
- // Update the code cache to make sure the JIT code does not get deleted.
- // Note: this will apply to all JIT compilations.
- code_cache->SetGarbageCollectCode(false);
- while (true) {
- if (code_cache->WillExecuteJitCode(method)) {
- break;
- } else {
- // Sleep to yield to the compiler thread.
- usleep(1000);
- ScopedObjectAccess soa(self);
- // Make sure there is a profiling info, required by the compiler.
- ProfilingInfo::Create(self, method, /* retry_allocation */ true);
- // Will either ensure it's compiled or do the compilation itself.
- jit->CompileMethod(method, self, /* osr */ false);
- }
- }
+ ForceJitCompiled(self, method);
}
extern "C" JNIEXPORT jboolean JNICALL Java_Main_hasSingleImplementation(JNIEnv* env,
diff --git a/test/common/stack_inspect.cc b/test/common/stack_inspect.cc
index d74d2efa12..581aa74d4e 100644
--- a/test/common/stack_inspect.cc
+++ b/test/common/stack_inspect.cc
@@ -196,4 +196,24 @@ extern "C" JNIEXPORT void JNICALL Java_Main_assertCallerIsManaged(JNIEnv* env, j
}
}
+struct GetCallingFrameVisitor : public StackVisitor {
+ GetCallingFrameVisitor(Thread* thread, Context* context)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ : StackVisitor(thread, context, StackVisitor::StackWalkKind::kIncludeInlinedFrames) {}
+
+ bool VisitFrame() override NO_THREAD_SAFETY_ANALYSIS {
+ // Discard stubs and Main.getThisOfCaller.
+ return GetMethod() == nullptr || GetMethod()->IsNative();
+ }
+};
+
+extern "C" JNIEXPORT jobject JNICALL Java_Main_getThisOfCaller(
+ JNIEnv* env, jclass cls ATTRIBUTE_UNUSED) {
+ ScopedObjectAccess soa(env);
+ std::unique_ptr<art::Context> context(art::Context::Create());
+ GetCallingFrameVisitor visitor(soa.Self(), context.get());
+ visitor.WalkStack();
+ return soa.AddLocalReference<jobject>(visitor.GetThisObject());
+}
+
} // namespace art
diff --git a/test/knownfailures.json b/test/knownfailures.json
index e27a4d6f44..d769b48bfd 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -947,6 +947,7 @@
"676-resolve-field-type",
"685-deoptimizeable",
"685-shifts",
+ "686-get-this",
"706-checker-scheduler",
"707-checker-invalid-profile",
"714-invoke-custom-lambda-metafactory",
@@ -1089,6 +1090,14 @@
"description": ["We do not inline with debuggable."]
},
{
+ "tests": ["1955-pop-frame-jit-called", "1956-pop-frame-jit-calling"],
+ "variant": "jit-on-first-use",
+ "description": [
+ "These tests directly set -Xjitthreshold:1000 to prevent the jit from compiling any",
+ "extra methods. jit-at-first-use would disrupt this."
+ ]
+ },
+ {
"tests": ["135-MirandaDispatch"],
"variant": "interp-ac & 32 & host",
"env_vars": {"SANITIZE_HOST": "address"},
@@ -1099,5 +1108,17 @@
"tests": ["454-get-vreg", "457-regs"],
"variant": "baseline",
"description": ["Tests are expected to fail with baseline."]
+ },
+ {
+ "tests": ["050-sync-test"],
+ "variant": "target & gcstress & debug",
+ "bug": "b/117597114",
+ "description": ["Looks timing dependent"]
+ },
+ {
+ "tests": ["920-objects"],
+ "variant": "jit-on-first-use",
+ "bug": "b/117638896",
+ "description": ["SIGSEGVs on jit-on-first-use configuration."]
}
]
diff --git a/test/run-test b/test/run-test
index c6b88dc54f..229e2019dd 100755
--- a/test/run-test
+++ b/test/run-test
@@ -156,7 +156,6 @@ strace="false"
always_clean="no"
never_clean="no"
have_image="yes"
-multi_image_suffix=""
android_root="/system"
bisection_search="no"
suspend_timeout="500000"
@@ -201,9 +200,6 @@ while true; do
elif [ "x$1" = "x--no-image" ]; then
have_image="no"
shift
- elif [ "x$1" = "x--multi-image" ]; then
- multi_image_suffix="-multi"
- shift
elif [ "x$1" = "x--relocate" ]; then
relocate="yes"
shift
@@ -587,12 +583,12 @@ if [ "$runtime" = "dalvik" ]; then
elif [ "$runtime" = "art" ]; then
if [ "$target_mode" = "no" ]; then
guess_host_arch_name
- run_args="${run_args} --boot ${ANDROID_HOST_OUT}/framework/core${image_suffix}${multi_image_suffix}.art"
+ run_args="${run_args} --boot ${ANDROID_HOST_OUT}/framework/core${image_suffix}.art"
run_args="${run_args} --runtime-option -Djava.library.path=${ANDROID_HOST_OUT}/lib${suffix64}:${ANDROID_HOST_OUT}/nativetest${suffix64}"
else
guess_target_arch_name
run_args="${run_args} --runtime-option -Djava.library.path=/data/nativetest${suffix64}/art/${target_arch_name}"
- run_args="${run_args} --boot /data/art-test/core${image_suffix}${multi_image_suffix}.art"
+ run_args="${run_args} --boot /data/art-test/core${image_suffix}.art"
fi
if [ "$relocate" = "yes" ]; then
run_args="${run_args} --relocate"
@@ -731,8 +727,6 @@ if [ "$usage" = "yes" ]; then
echo " --dex2oat-swap Use a dex2oat swap file."
echo " --instruction-set-features [string]"
echo " Set instruction-set-features for compilation."
- echo " --multi-image Use a set of images compiled with dex2oat multi-image for"
- echo " the boot class path."
echo " --quiet Don't print anything except failure messages"
echo " --bisection-search Perform bisection bug search."
echo " --vdex Test using vdex as in input to dex2oat. Only works with --prebuild."
@@ -763,7 +757,7 @@ fi
echo "${test_dir}: building..." 1>&2
rm -rf "$tmp_dir"
-cp -Rp "$test_dir" "$tmp_dir"
+cp -LRp "$test_dir" "$tmp_dir"
cd "$tmp_dir"
if [ '!' -r "$build" ]; then
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index 45c3f88ecf..4e873c1ba2 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -142,7 +142,7 @@ def gather_test_info():
VARIANT_TYPE_DICT['run'] = {'ndebug', 'debug'}
VARIANT_TYPE_DICT['target'] = {'target', 'host', 'jvm'}
VARIANT_TYPE_DICT['trace'] = {'trace', 'ntrace', 'stream'}
- VARIANT_TYPE_DICT['image'] = {'picimage', 'no-image', 'multipicimage'}
+ VARIANT_TYPE_DICT['image'] = {'picimage', 'no-image'}
VARIANT_TYPE_DICT['debuggable'] = {'ndebuggable', 'debuggable'}
VARIANT_TYPE_DICT['gc'] = {'gcstress', 'gcverify', 'cms'}
VARIANT_TYPE_DICT['prebuild'] = {'no-prebuild', 'prebuild'}
@@ -458,8 +458,6 @@ def run_tests(tests):
if image == 'no-image':
options_test += ' --no-image'
- elif image == 'multipicimage':
- options_test += ' --multi-image'
if debuggable == 'debuggable':
options_test += ' --debuggable'
@@ -1014,7 +1012,7 @@ def main():
build_targets += 'test-art-target-run-test-dependencies '
if 'jvm' in _user_input_variants['target']:
build_targets += 'test-art-host-run-test-dependencies '
- build_command = 'build/soong/soong_ui.bash --make-mode'
+ build_command = env.ANDROID_BUILD_TOP + '/build/soong/soong_ui.bash --make-mode'
build_command += ' DX='
build_command += ' ' + build_targets
if subprocess.call(build_command.split()):
diff --git a/test/ti-agent/jni_binder.cc b/test/ti-agent/jni_binder.cc
index 585b4a4956..a115c22930 100644
--- a/test/ti-agent/jni_binder.cc
+++ b/test/ti-agent/jni_binder.cc
@@ -174,7 +174,7 @@ static jclass FindClassWithClassLoader(JNIEnv* env, const char* class_name, jobj
class_loader));
}
-jclass FindClass(jvmtiEnv* jvmti_env, JNIEnv* env, const char* class_name, jobject class_loader) {
+jclass GetClass(jvmtiEnv* jvmti_env, JNIEnv* env, const char* class_name, jobject class_loader) {
if (class_loader != nullptr) {
return FindClassWithClassLoader(env, class_name, class_loader);
}
@@ -251,7 +251,7 @@ void BindFunctionsOnClass(jvmtiEnv* jvmti_env, JNIEnv* env, jclass klass) {
void BindFunctions(jvmtiEnv* jvmti_env, JNIEnv* env, const char* class_name, jobject class_loader) {
// Use JNI to load the class.
- ScopedLocalRef<jclass> klass(env, FindClass(jvmti_env, env, class_name, class_loader));
+ ScopedLocalRef<jclass> klass(env, GetClass(jvmti_env, env, class_name, class_loader));
CHECK(klass.get() != nullptr) << class_name;
BindFunctionsOnClass(jvmti_env, env, klass.get());
}
diff --git a/test/ti-agent/jni_binder.h b/test/ti-agent/jni_binder.h
index e998dc561c..3d2ff9c47a 100644
--- a/test/ti-agent/jni_binder.h
+++ b/test/ti-agent/jni_binder.h
@@ -24,7 +24,7 @@ namespace art {
// Find the given classname. First try the implied classloader, then the system classloader,
// then use JVMTI to find all classloaders.
-jclass FindClass(jvmtiEnv* jvmti_env, JNIEnv* env, const char* class_name, jobject class_loader);
+jclass GetClass(jvmtiEnv* jvmti_env, JNIEnv* env, const char* class_name, jobject class_loader);
// Load the class through JNI. Inspect it, find all native methods. Construct the corresponding
// mangled name, run dlsym and bind the method.
diff --git a/tools/cpp-define-generator/Android.bp b/tools/cpp-define-generator/Android.bp
index a94f4044b8..027f128561 100644
--- a/tools/cpp-define-generator/Android.bp
+++ b/tools/cpp-define-generator/Android.bp
@@ -14,16 +14,11 @@
// limitations under the License.
//
-// Build a "data" binary which will hold all the symbol values that will be parsed by the other scripts.
-//
-// Builds are for host only, target-specific define generation is possibly but is trickier and would need extra tooling.
-//
-// In the future we may wish to parameterize this on (32,64)x(read_barrier,no_read_barrier).
-
-cc_binary { // Do not use art_cc_binary because HOST_PREFER_32_BIT is incompatible with genrule.
- name: "cpp-define-generator-data",
+// This produces human-readable asm_defines.s with the embedded compile-time constants.
+cc_object {
+ name: "asm_defines.s",
host_supported: true,
- device_supported: false,
+ device_supported: true,
defaults: [
"art_debug_defaults",
"art_defaults",
@@ -33,20 +28,36 @@ cc_binary { // Do not use art_cc_binary because HOST_PREFER_32_BIT is incompatib
"art/libdexfile",
"art/libartbase",
"art/runtime",
+ "system/core/base/include",
],
- srcs: ["main.cc"],
- shared_libs: [
- "libbase",
- ],
+ // Produce text file rather than binary.
+ cflags: ["-S"],
+ srcs: ["asm_defines.cc"],
}
-// Note: See $OUT_DIR/soong/build.ninja
-// For the exact filename that this generates to run make command on just
-// this rule later.
-genrule {
+// This extracts the compile-time constants from asm_defines.s and creates the header.
+cc_genrule {
name: "cpp-define-generator-asm-support",
- out: ["asm_support_gen.h"],
- tools: ["cpp-define-generator-data"],
- tool_files: ["*.def"],
- cmd: "$(location cpp-define-generator-data) > \"$(out)\"",
+ host_supported: true,
+ device_supported: true,
+ srcs: [":asm_defines.s"],
+ out: ["asm_defines.h"],
+ tool_files: ["make_header.py"],
+ cmd: "$(location make_header.py) \"$(in)\" > \"$(out)\"",
+}
+
+cc_library_headers {
+ name: "cpp-define-generator-definitions",
+ host_supported: true,
+ export_include_dirs: ["."],
+}
+
+python_binary_host {
+ name: "cpp-define-generator-test",
+ main: "make_header_test.py",
+ srcs: [
+ "make_header.py",
+ "make_header_test.py",
+ ],
+ test_suites: ["general-tests"],
}
diff --git a/tools/cpp-define-generator/art_method.def b/tools/cpp-define-generator/art_method.def
new file mode 100644
index 0000000000..21859dc9ce
--- /dev/null
+++ b/tools/cpp-define-generator/art_method.def
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+#if ASM_DEFINE_INCLUDE_DEPENDENCIES
+#include "art_method.h"
+#endif
+
+ASM_DEFINE(ART_METHOD_ACCESS_FLAGS_OFFSET,
+ art::ArtMethod::AccessFlagsOffset().Int32Value())
+ASM_DEFINE(ART_METHOD_DECLARING_CLASS_OFFSET,
+ art::ArtMethod::DeclaringClassOffset().Int32Value())
+ASM_DEFINE(ART_METHOD_JNI_OFFSET_32,
+ art::ArtMethod::EntryPointFromJniOffset(art::PointerSize::k32).Int32Value())
+ASM_DEFINE(ART_METHOD_JNI_OFFSET_64,
+ art::ArtMethod::EntryPointFromJniOffset(art::PointerSize::k64).Int32Value())
+ASM_DEFINE(ART_METHOD_QUICK_CODE_OFFSET_32,
+ art::ArtMethod::EntryPointFromQuickCompiledCodeOffset(art::PointerSize::k32).Int32Value())
+ASM_DEFINE(ART_METHOD_QUICK_CODE_OFFSET_64,
+ art::ArtMethod::EntryPointFromQuickCompiledCodeOffset(art::PointerSize::k64).Int32Value())
diff --git a/tools/cpp-define-generator/asm_defines.cc b/tools/cpp-define-generator/asm_defines.cc
new file mode 100644
index 0000000000..c105c1a7ce
--- /dev/null
+++ b/tools/cpp-define-generator/asm_defines.cc
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+//
+// This file is used to generate #defines for use in assembly source code.
+//
+// The content of this file will be used to compile an object file
+// (generated as human readable assembly text file, not as binary).
+// This text file will then be post-processed by a python script to find
+// and extract the constants and generate the final asm_defines.h header.
+//
+
+// We use "asm volatile" to generate text that will stand out in the
+// compiler generated intermediate assembly file (eg. ">>FOO 42 0<<").
+// We emit all values as 64-bit integers (which we will printed as text).
+// We also store a flag which specifies whether the constant is negative.
+// Note that "asm volatile" must be inside a method to please the compiler.
+#define ASM_DEFINE(NAME, EXPR) \
+void AsmDefineHelperFor_##NAME() { \
+ asm volatile("\n.ascii \">>" #NAME " %0 %1<<\"" \
+ :: "i" (static_cast<int64_t>(EXPR)), "i" (EXPR < 0 ? 1 : 0)); \
+}
+#include "asm_defines.def"
diff --git a/tools/cpp-define-generator/constant_jit.def b/tools/cpp-define-generator/asm_defines.def
index 5fa5194d00..7a77e8ebe3 100644
--- a/tools/cpp-define-generator/constant_jit.def
+++ b/tools/cpp-define-generator/asm_defines.def
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,16 +14,19 @@
* limitations under the License.
*/
-// Constants within jit.h.
-
-#if defined(DEFINE_INCLUDE_DEPENDENCIES)
-#include "jit/jit.h" // art::kSuspendRequest, etc.
+#if !defined(ASM_DEFINE_INCLUDE_DEPENDENCIES)
+#define ASM_DEFINE_INCLUDE_DEPENDENCIES 1
#endif
-#define DEFINE_JIT_CONSTANT(macro_name, type, expr) \
- DEFINE_EXPR(JIT_ ## macro_name, type, (expr))
-
-DEFINE_JIT_CONSTANT(CHECK_OSR, int16_t, art::jit::kJitCheckForOSR)
-DEFINE_JIT_CONSTANT(HOTNESS_DISABLE, int16_t, art::jit::kJitHotnessDisabled)
-
-#undef DEFINE_JIT_CONSTANT
+#include "globals.def"
+#include "art_method.def"
+#include "lockword.def"
+#include "mirror_array.def"
+#include "mirror_class.def"
+#include "mirror_dex_cache.def"
+#include "mirror_object.def"
+#include "mirror_string.def"
+#include "rosalloc.def"
+#include "runtime.def"
+#include "shadow_frame.def"
+#include "thread.def"
diff --git a/tools/cpp-define-generator/constant_class.def b/tools/cpp-define-generator/constant_class.def
deleted file mode 100644
index 1310103ab7..0000000000
--- a/tools/cpp-define-generator/constant_class.def
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.
- */
-
-#if defined(DEFINE_INCLUDE_DEPENDENCIES)
-#include "base/bit_utils.h" // MostSignificantBit
-#include "dex/modifiers.h" // kAccClassIsFinalizable
-#endif
-
-#define DEFINE_FLAG_OFFSET(type_name, field_name, expr) \
- DEFINE_EXPR(type_name ## _ ## field_name, uint32_t, (expr))
-
-DEFINE_FLAG_OFFSET(ACCESS_FLAGS, CLASS_IS_FINALIZABLE, art::kAccClassIsFinalizable)
-DEFINE_FLAG_OFFSET(ACCESS_FLAGS, CLASS_IS_INTERFACE, art::kAccInterface)
-// TODO: We should really have a BitPosition which also checks it's a power of 2.
-DEFINE_FLAG_OFFSET(ACCESS_FLAGS, CLASS_IS_FINALIZABLE_BIT, art::MostSignificantBit(art::kAccClassIsFinalizable))
-
-#undef DEFINE_FLAG_OFFSET
diff --git a/tools/cpp-define-generator/constant_dexcache.def b/tools/cpp-define-generator/constant_dexcache.def
deleted file mode 100644
index 743ebb7453..0000000000
--- a/tools/cpp-define-generator/constant_dexcache.def
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.
- */
-
-#if defined(DEFINE_INCLUDE_DEPENDENCIES)
-#include "mirror/dex_cache.h" // art::mirror::DexCache, StringDexCachePair
-#endif
-
-DEFINE_EXPR(STRING_DEX_CACHE_ELEMENT_SIZE_SHIFT, int32_t,
- art::WhichPowerOf2(sizeof(art::mirror::StringDexCachePair)))
-DEFINE_EXPR(STRING_DEX_CACHE_SIZE_MINUS_ONE, int32_t,
- art::mirror::DexCache::kDexCacheStringCacheSize - 1)
-DEFINE_EXPR(STRING_DEX_CACHE_HASH_BITS, int32_t,
- art::LeastSignificantBit(art::mirror::DexCache::kDexCacheStringCacheSize))
-DEFINE_EXPR(STRING_DEX_CACHE_ELEMENT_SIZE, int32_t,
- sizeof(art::mirror::StringDexCachePair))
-DEFINE_EXPR(METHOD_DEX_CACHE_SIZE_MINUS_ONE, int32_t,
- art::mirror::DexCache::kDexCacheMethodCacheSize - 1)
-DEFINE_EXPR(METHOD_DEX_CACHE_HASH_BITS, int32_t,
- art::LeastSignificantBit(art::mirror::DexCache::kDexCacheMethodCacheSize))
diff --git a/tools/cpp-define-generator/constant_globals.def b/tools/cpp-define-generator/constant_globals.def
deleted file mode 100644
index d0d6350f80..0000000000
--- a/tools/cpp-define-generator/constant_globals.def
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.
- */
-
-// Export global values.
-
-#if defined(DEFINE_INCLUDE_DEPENDENCIES)
-#include <atomic> // std::memory_order_relaxed
-#include "base/globals.h" // art::kObjectAlignment
-#include "dex/modifiers.h"
-#endif
-
-DEFINE_EXPR(STD_MEMORY_ORDER_RELAXED, int32_t, std::memory_order_relaxed)
-
-#define DEFINE_OBJECT_EXPR(macro_name, type, constant_field_name) \
- DEFINE_EXPR(OBJECT_ ## macro_name, type, constant_field_name)
-
-DEFINE_OBJECT_EXPR(ALIGNMENT_MASK, size_t, art::kObjectAlignment - 1)
-DEFINE_OBJECT_EXPR(ALIGNMENT_MASK_TOGGLED, uint32_t, ~static_cast<uint32_t>(art::kObjectAlignment - 1))
-DEFINE_OBJECT_EXPR(ALIGNMENT_MASK_TOGGLED64, uint64_t, ~static_cast<uint64_t>(art::kObjectAlignment - 1))
-
-DEFINE_EXPR(ACC_OBSOLETE_METHOD, int32_t, art::kAccObsoleteMethod)
-DEFINE_EXPR(ACC_OBSOLETE_METHOD_SHIFT, int32_t, art::WhichPowerOf2(art::kAccObsoleteMethod))
-
-#undef DEFINE_OBJECT_EXPR
-
diff --git a/tools/cpp-define-generator/constant_lockword.def b/tools/cpp-define-generator/constant_lockword.def
deleted file mode 100644
index 977d1ca12d..0000000000
--- a/tools/cpp-define-generator/constant_lockword.def
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.
- */
-
-// Export lockword values.
-
-#if defined(DEFINE_INCLUDE_DEPENDENCIES)
-#include "lock_word.h" // art::LockWord
-#endif
-
-#define DEFINE_LOCK_WORD_EXPR(macro_name, type, constant_field_name) \
- DEFINE_EXPR(LOCK_WORD_ ## macro_name, type, art::LockWord::constant_field_name)
-
-// FIXME: The naming is inconsistent, the `Shifted` -> `_SHIFTED` suffix is sometimes missing.
-DEFINE_LOCK_WORD_EXPR(STATE_SHIFT, int32_t, kStateShift)
-DEFINE_LOCK_WORD_EXPR(STATE_MASK_SHIFTED, uint32_t, kStateMaskShifted)
-DEFINE_LOCK_WORD_EXPR(READ_BARRIER_STATE_SHIFT, int32_t, kReadBarrierStateShift)
-DEFINE_LOCK_WORD_EXPR(READ_BARRIER_STATE_MASK, uint32_t, kReadBarrierStateMaskShifted)
-DEFINE_LOCK_WORD_EXPR(READ_BARRIER_STATE_MASK_TOGGLED, uint32_t, kReadBarrierStateMaskShiftedToggled)
-DEFINE_LOCK_WORD_EXPR(THIN_LOCK_COUNT_SIZE, int32_t, kThinLockCountSize)
-DEFINE_LOCK_WORD_EXPR(THIN_LOCK_COUNT_SHIFT, int32_t, kThinLockCountShift)
-DEFINE_LOCK_WORD_EXPR(THIN_LOCK_COUNT_MASK_SHIFTED, uint32_t, kThinLockCountMaskShifted)
-DEFINE_LOCK_WORD_EXPR(THIN_LOCK_COUNT_ONE, uint32_t, kThinLockCountOne)
-DEFINE_LOCK_WORD_EXPR(THIN_LOCK_OWNER_MASK_SHIFTED, uint32_t, kThinLockOwnerMaskShifted)
-
-DEFINE_LOCK_WORD_EXPR(STATE_FORWARDING_ADDRESS, uint32_t, kStateForwardingAddress)
-DEFINE_LOCK_WORD_EXPR(STATE_FORWARDING_ADDRESS_OVERFLOW, uint32_t, kStateForwardingAddressOverflow)
-DEFINE_LOCK_WORD_EXPR(STATE_FORWARDING_ADDRESS_SHIFT, uint32_t, kForwardingAddressShift)
-
-DEFINE_LOCK_WORD_EXPR(GC_STATE_MASK_SHIFTED, uint32_t, kGCStateMaskShifted)
-DEFINE_LOCK_WORD_EXPR(GC_STATE_MASK_SHIFTED_TOGGLED, uint32_t, kGCStateMaskShiftedToggled)
-DEFINE_LOCK_WORD_EXPR(GC_STATE_SIZE, int32_t, kGCStateSize)
-DEFINE_LOCK_WORD_EXPR(GC_STATE_SHIFT, int32_t, kGCStateShift)
-
-DEFINE_LOCK_WORD_EXPR(MARK_BIT_SHIFT, int32_t, kMarkBitStateShift)
-DEFINE_LOCK_WORD_EXPR(MARK_BIT_MASK_SHIFTED, uint32_t, kMarkBitStateMaskShifted)
-
-#undef DEFINE_LOCK_WORD_EXPR
-
diff --git a/tools/cpp-define-generator/constant_reference.def b/tools/cpp-define-generator/constant_reference.def
deleted file mode 100644
index d312f76e0a..0000000000
--- a/tools/cpp-define-generator/constant_reference.def
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.
- */
-
-#if defined(DEFINE_INCLUDE_DEPENDENCIES)
-#include "mirror/object.h" // mirror::Object
-#include "stack.h" // StackReference
-#include "mirror/object_reference.h" // mirror::CompressedReference
-#include "base/bit_utils.h" // WhichPowerOf2
-#endif
-
-// Size of references to the heap on the stack.
-DEFINE_EXPR(STACK_REFERENCE_SIZE, size_t, sizeof(art::StackReference<art::mirror::Object>))
-// Size of heap references
-DEFINE_EXPR(COMPRESSED_REFERENCE_SIZE, size_t, sizeof(art::mirror::CompressedReference<art::mirror::Object>))
-DEFINE_EXPR(COMPRESSED_REFERENCE_SIZE_SHIFT, size_t, art::WhichPowerOf2(sizeof(art::mirror::CompressedReference<art::mirror::Object>)))
-
-#undef DEFINE_REFERENCE_OFFSET
diff --git a/tools/cpp-define-generator/constant_rosalloc.def b/tools/cpp-define-generator/constant_rosalloc.def
deleted file mode 100644
index 2007cef5ad..0000000000
--- a/tools/cpp-define-generator/constant_rosalloc.def
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.
- */
-
-// Constants within RosAlloc.
-
-#if defined(DEFINE_INCLUDE_DEPENDENCIES)
-#include "gc/allocator/rosalloc.h" // art::gc::allocator::RosAlloc
-#endif
-
-#define DEFINE_ROSALLOC_CONSTANT(macro_name, type, expr) \
- DEFINE_EXPR(ROSALLOC_ ## macro_name, type, (expr))
-
-DEFINE_ROSALLOC_CONSTANT(MAX_THREAD_LOCAL_BRACKET_SIZE, int32_t, art::gc::allocator::RosAlloc::kMaxThreadLocalBracketSize)
-DEFINE_ROSALLOC_CONSTANT(BRACKET_QUANTUM_SIZE_SHIFT, int32_t, art::gc::allocator::RosAlloc::kThreadLocalBracketQuantumSizeShift)
-// TODO: This should be a BitUtils helper, e.g. BitMaskFromSize or something like that.
-DEFINE_ROSALLOC_CONSTANT(BRACKET_QUANTUM_SIZE_MASK, int32_t, static_cast<int32_t>(art::gc::allocator::RosAlloc::kThreadLocalBracketQuantumSize - 1))
-DEFINE_ROSALLOC_CONSTANT(BRACKET_QUANTUM_SIZE_MASK_TOGGLED32,\
- uint32_t, ~static_cast<uint32_t>(art::gc::allocator::RosAlloc::kThreadLocalBracketQuantumSize - 1))
-DEFINE_ROSALLOC_CONSTANT(BRACKET_QUANTUM_SIZE_MASK_TOGGLED64,\
- uint64_t, ~static_cast<uint64_t>(art::gc::allocator::RosAlloc::kThreadLocalBracketQuantumSize - 1))
-DEFINE_ROSALLOC_CONSTANT(RUN_FREE_LIST_OFFSET, int32_t, art::gc::allocator::RosAlloc::RunFreeListOffset())
-DEFINE_ROSALLOC_CONSTANT(RUN_FREE_LIST_HEAD_OFFSET, int32_t, art::gc::allocator::RosAlloc::RunFreeListHeadOffset())
-DEFINE_ROSALLOC_CONSTANT(RUN_FREE_LIST_SIZE_OFFSET, int32_t, art::gc::allocator::RosAlloc::RunFreeListSizeOffset())
-DEFINE_ROSALLOC_CONSTANT(SLOT_NEXT_OFFSET, int32_t, art::gc::allocator::RosAlloc::RunSlotNextOffset())
-
-
-#undef DEFINE_ROSALLOC_CONSTANT
diff --git a/tools/cpp-define-generator/constant_thread.def b/tools/cpp-define-generator/constant_thread.def
deleted file mode 100644
index 7e1df6b267..0000000000
--- a/tools/cpp-define-generator/constant_thread.def
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.
- */
-
-// Constants within thread.h.
-
-#if defined(DEFINE_INCLUDE_DEPENDENCIES)
-#include "thread.h" // art::kSuspendRequest, etc.
-#endif
-
-#define DEFINE_THREAD_CONSTANT(macro_name, type, expr) \
- DEFINE_EXPR(THREAD_ ## macro_name, type, (expr))
-
-DEFINE_THREAD_CONSTANT(SUSPEND_REQUEST, int32_t, art::kSuspendRequest)
-DEFINE_THREAD_CONSTANT(CHECKPOINT_REQUEST, int32_t, art::kCheckpointRequest)
-DEFINE_THREAD_CONSTANT(EMPTY_CHECKPOINT_REQUEST, int32_t, art::kEmptyCheckpointRequest)
-DEFINE_THREAD_CONSTANT(SUSPEND_OR_CHECKPOINT_REQUEST, int32_t, art::kSuspendRequest | art::kCheckpointRequest | art::kEmptyCheckpointRequest)
-DEFINE_THREAD_CONSTANT(INTERPRETER_CACHE_SIZE_LOG2, int32_t, art::Thread::InterpreterCacheSizeLog2())
diff --git a/tools/cpp-define-generator/globals.def b/tools/cpp-define-generator/globals.def
new file mode 100644
index 0000000000..2324f5168e
--- /dev/null
+++ b/tools/cpp-define-generator/globals.def
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#if ASM_DEFINE_INCLUDE_DEPENDENCIES
+#include "base/bit_utils.h"
+#include "base/callee_save_type.h"
+#include "base/enums.h"
+#include "base/globals.h"
+#include "dex/modifiers.h"
+#include "gc/accounting/card_table.h"
+#include "gc/heap.h"
+#include "jit/jit.h"
+#include "mirror/object.h"
+#include "mirror/object_reference.h"
+#include "stack.h"
+#endif
+
+ASM_DEFINE(ACCESS_FLAGS_CLASS_IS_FINALIZABLE,
+ art::kAccClassIsFinalizable)
+ASM_DEFINE(ACCESS_FLAGS_CLASS_IS_FINALIZABLE_BIT,
+ art::MostSignificantBit(art::kAccClassIsFinalizable))
+ASM_DEFINE(ACCESS_FLAGS_CLASS_IS_INTERFACE,
+ art::kAccInterface)
+ASM_DEFINE(ACC_OBSOLETE_METHOD,
+ art::kAccObsoleteMethod)
+ASM_DEFINE(ACC_OBSOLETE_METHOD_SHIFT,
+ art::WhichPowerOf2(art::kAccObsoleteMethod))
+ASM_DEFINE(CARD_TABLE_CARD_SHIFT,
+ art::gc::accounting::CardTable::kCardShift)
+ASM_DEFINE(COMPRESSED_REFERENCE_SIZE,
+ sizeof(art::mirror::CompressedReference<art::mirror::Object>))
+ASM_DEFINE(COMPRESSED_REFERENCE_SIZE_SHIFT,
+ art::WhichPowerOf2(sizeof(art::mirror::CompressedReference<art::mirror::Object>)))
+ASM_DEFINE(JIT_CHECK_OSR,
+ art::jit::kJitCheckForOSR)
+ASM_DEFINE(JIT_HOTNESS_DISABLE,
+ art::jit::kJitHotnessDisabled)
+ASM_DEFINE(MIN_LARGE_OBJECT_THRESHOLD,
+ art::gc::Heap::kMinLargeObjectThreshold)
+ASM_DEFINE(OBJECT_ALIGNMENT_MASK,
+ art::kObjectAlignment - 1)
+ASM_DEFINE(OBJECT_ALIGNMENT_MASK_TOGGLED,
+ ~static_cast<uint32_t>(art::kObjectAlignment - 1))
+ASM_DEFINE(OBJECT_ALIGNMENT_MASK_TOGGLED64,
+ ~static_cast<uint64_t>(art::kObjectAlignment - 1))
+ASM_DEFINE(POINTER_SIZE,
+ static_cast<size_t>(art::kRuntimePointerSize))
+ASM_DEFINE(POINTER_SIZE_SHIFT,
+ art::WhichPowerOf2(static_cast<size_t>(art::kRuntimePointerSize)))
+ASM_DEFINE(STACK_REFERENCE_SIZE,
+ sizeof(art::StackReference<art::mirror::Object>))
+ASM_DEFINE(STD_MEMORY_ORDER_RELAXED,
+ std::memory_order_relaxed)
diff --git a/tools/cpp-define-generator/lockword.def b/tools/cpp-define-generator/lockword.def
new file mode 100644
index 0000000000..a170c15f8b
--- /dev/null
+++ b/tools/cpp-define-generator/lockword.def
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#if ASM_DEFINE_INCLUDE_DEPENDENCIES
+#include "lock_word.h"
+#endif
+
+ASM_DEFINE(LOCK_WORD_GC_STATE_MASK_SHIFTED,
+ art::LockWord::kGCStateMaskShifted)
+ASM_DEFINE(LOCK_WORD_GC_STATE_MASK_SHIFTED_TOGGLED,
+ art::LockWord::kGCStateMaskShiftedToggled)
+ASM_DEFINE(LOCK_WORD_GC_STATE_SHIFT,
+ art::LockWord::kGCStateShift)
+ASM_DEFINE(LOCK_WORD_GC_STATE_SIZE,
+ art::LockWord::kGCStateSize)
+ASM_DEFINE(LOCK_WORD_MARK_BIT_MASK_SHIFTED,
+ art::LockWord::kMarkBitStateMaskShifted)
+ASM_DEFINE(LOCK_WORD_MARK_BIT_SHIFT,
+ art::LockWord::kMarkBitStateShift)
+ASM_DEFINE(LOCK_WORD_READ_BARRIER_STATE_MASK,
+ 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,
+ art::LockWord::kStateForwardingAddress)
+ASM_DEFINE(LOCK_WORD_STATE_FORWARDING_ADDRESS_OVERFLOW,
+ art::LockWord::kStateForwardingAddressOverflow)
+ASM_DEFINE(LOCK_WORD_STATE_FORWARDING_ADDRESS_SHIFT,
+ art::LockWord::kForwardingAddressShift)
+ASM_DEFINE(LOCK_WORD_STATE_MASK_SHIFTED,
+ art::LockWord::kStateMaskShifted)
+ASM_DEFINE(LOCK_WORD_STATE_SHIFT,
+ art::LockWord::kStateShift)
+ASM_DEFINE(LOCK_WORD_THIN_LOCK_COUNT_MASK_SHIFTED,
+ art::LockWord::kThinLockCountMaskShifted)
+ASM_DEFINE(LOCK_WORD_THIN_LOCK_COUNT_ONE,
+ art::LockWord::kThinLockCountOne)
+ASM_DEFINE(LOCK_WORD_THIN_LOCK_COUNT_SHIFT,
+ art::LockWord::kThinLockCountShift)
+ASM_DEFINE(LOCK_WORD_THIN_LOCK_COUNT_SIZE,
+ art::LockWord::kThinLockCountSize)
+ASM_DEFINE(LOCK_WORD_THIN_LOCK_OWNER_MASK_SHIFTED,
+ art::LockWord::kThinLockOwnerMaskShifted)
diff --git a/tools/cpp-define-generator/main.cc b/tools/cpp-define-generator/main.cc
deleted file mode 100644
index 7c515be12f..0000000000
--- a/tools/cpp-define-generator/main.cc
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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.
- */
-
-#include <algorithm>
-#include <ios>
-#include <iostream>
-#include <sstream>
-#include <string>
-#include <type_traits>
-
-// Art Offset file dependencies
-#define DEFINE_INCLUDE_DEPENDENCIES
-#include "offsets_all.def"
-
-std::string to_upper(std::string input) {
- std::transform(input.begin(), input.end(), input.begin(), ::toupper);
- return input;
-}
-
-template <typename T, typename = void>
-typename std::enable_if<!std::is_signed<T>::value, std::string>::type
-pretty_format(T value) {
- // Print most values as hex.
- std::stringstream ss;
- ss << std::showbase << std::hex << value;
- return ss.str();
-}
-
-template <typename T, typename = void>
-typename std::enable_if<std::is_signed<T>::value, std::string>::type
-pretty_format(T value) {
- // Print "signed" values as decimal so that the negativity doesn't get lost.
- std::stringstream ss;
-
- // For negative values add a (). Omit it from positive values for conciseness.
- if (value < 0) {
- ss << "(";
- }
-
- ss << value;
-
- if (value < 0) {
- ss << ")";
- }
- return ss.str();
-}
-
-template <typename T>
-void cpp_define(const std::string& name, T value) {
- std::cout << "#define " << name << " " << pretty_format(value) << std::endl;
-}
-
-template <typename T>
-void emit_check_eq(T value, const std::string& expr) {
- std::cout << "DEFINE_CHECK_EQ(" << value << ", (" << expr << "))" << std::endl;
-}
-
-const char *kFileHeader = /* // NOLINT [readability/multiline_string] [5] */ R"L1C3NS3(
-/*
- * 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.
- */
-
-#ifndef ART_RUNTIME_GENERATED_ASM_SUPPORT_GEN_H_
-#define ART_RUNTIME_GENERATED_ASM_SUPPORT_GEN_H_
-
-// This file has been auto-generated by cpp-define-generator; do not edit directly.
-)L1C3NS3"; // NOLINT [readability/multiline_string] [5]
-
-const char *kFileFooter = /* // NOLINT [readability/multiline_string] [5] */ R"F00T3R(
-#endif // ART_RUNTIME_GENERATED_ASM_SUPPORT_GEN_H_
-)F00T3R"; // NOLINT [readability/multiline_string] [5]
-
-#define MACROIZE(holder_type, field_name) to_upper(#holder_type "_" #field_name "_OFFSET")
-
-int main() {
- std::cout << kFileHeader << std::endl;
-
- std::string z = "";
-
- // Print every constant expression to stdout as a #define or a CHECK_EQ
-#define DEFINE_EXPR(macro_name, field_type, expr) \
- cpp_define(to_upper(#macro_name), static_cast<field_type>(expr)); \
- emit_check_eq(z + "static_cast<" #field_type ">(" + to_upper(#macro_name) + ")", \
- "static_cast<" #field_type ">(" #expr ")");
-#include "offsets_all.def"
-
- std::cout << kFileFooter << std::endl;
- return 0;
-}
diff --git a/tools/cpp-define-generator/make_header.py b/tools/cpp-define-generator/make_header.py
new file mode 100755
index 0000000000..1b13923b50
--- /dev/null
+++ b/tools/cpp-define-generator/make_header.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+#
+# 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.
+
+# This script looks through compiled object file (stored human readable text),
+# and looks for the compile-time constants (added through custom "asm" block).
+# For example: .ascii ">>OBJECT_ALIGNMENT_MASK $7 $0<<"
+#
+# It will transform each such line to #define which is usabe in assembly code.
+# For example: #define OBJECT_ALIGNMENT_MASK 0x7
+#
+# Usage: make_header.py out/soong/.intermediates/.../asm_defines.o
+#
+
+import argparse
+import re
+import sys
+
+def convert(input):
+ """Find all defines in the compiler generated assembly and convert them to #define pragmas"""
+
+ asm_define_re = re.compile(r'">>(\w+) (?:\$|#)([-0-9]+) (?:\$|#)(0|1)<<"')
+ asm_defines = asm_define_re.findall(input)
+ if not asm_defines:
+ raise RuntimeError("Failed to find any asm defines in the input")
+
+ # Convert the found constants to #define pragmas.
+ # In case the C++ compiler decides to reorder the AsmDefinesFor_${name} functions,
+ # we don't want the order of the .h file to change from one compilation to another.
+ # Sorting ensures deterministic order of the #defines.
+ output = []
+ for name, value, negative_value in sorted(asm_defines):
+ value = int(value)
+ if value < 0 and negative_value == "0":
+ # Overflow - uint64_t constant was pretty printed as negative value.
+ value += 2 ** 64 # Python will use arbitrary precision arithmetic.
+ output.append("#define {0} {1:#x}".format(name, value))
+ return "\n".join(output)
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ parser.add_argument('input', help="Object file as text")
+ args = parser.parse_args()
+ print(convert(open(args.input, "r").read()))
diff --git a/tools/cpp-define-generator/make_header_test.py b/tools/cpp-define-generator/make_header_test.py
new file mode 100755
index 0000000000..a484285e5c
--- /dev/null
+++ b/tools/cpp-define-generator/make_header_test.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+#
+# 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 unittest
+import make_header
+
+test_input = r'''
+// Check that the various other assembly lines are ignored.
+.globl _Z49AsmDefineHelperFor_MIRROR_OBJECT_LOCK_WORD_OFFSETv
+.type _Z49AsmDefineHelperFor_MIRROR_OBJECT_LOCK_WORD_OFFSETv,%function
+.ascii ">>MIRROR_OBJECT_LOCK_WORD_OFFSET #4 #0<<"
+bx lr
+
+// Check large positive 32-bit constant.
+.ascii ">>OBJECT_ALIGNMENT_MASK_TOGGLED #4294967288 #0<<"
+
+// Check large positive 64-bit constant (it overflows into negative value).
+.ascii ">>OBJECT_ALIGNMENT_MASK_TOGGLED64 #-8 #0<<"
+
+// Check negative constant.
+.ascii ">>JIT_CHECK_OSR #-1 #1<<"
+'''
+
+test_output = r'''
+#define JIT_CHECK_OSR -0x1
+#define MIRROR_OBJECT_LOCK_WORD_OFFSET 0x4
+#define OBJECT_ALIGNMENT_MASK_TOGGLED 0xfffffff8
+#define OBJECT_ALIGNMENT_MASK_TOGGLED64 0xfffffffffffffff8
+'''
+
+class CppDefineGeneratorTest(unittest.TestCase):
+ def test_convert(self):
+ self.assertEqual(test_output.strip(), make_header.convert(test_input))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tools/cpp-define-generator/mirror_array.def b/tools/cpp-define-generator/mirror_array.def
new file mode 100644
index 0000000000..f600b41237
--- /dev/null
+++ b/tools/cpp-define-generator/mirror_array.def
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#if ASM_DEFINE_INCLUDE_DEPENDENCIES
+#include "mirror/array.h"
+#endif
+
+ASM_DEFINE(MIRROR_ARRAY_LENGTH_OFFSET,
+ art::mirror::Array::LengthOffset().Int32Value())
+ASM_DEFINE(MIRROR_BOOLEAN_ARRAY_DATA_OFFSET,
+ art::mirror::Array::DataOffset(sizeof(uint8_t)).Int32Value())
+ASM_DEFINE(MIRROR_BYTE_ARRAY_DATA_OFFSET,
+ art::mirror::Array::DataOffset(sizeof(int8_t)).Int32Value())
+ASM_DEFINE(MIRROR_CHAR_ARRAY_DATA_OFFSET,
+ art::mirror::Array::DataOffset(sizeof(uint16_t)).Int32Value())
+ASM_DEFINE(MIRROR_INT_ARRAY_DATA_OFFSET,
+ art::mirror::Array::DataOffset(sizeof(int32_t)).Int32Value())
+ASM_DEFINE(MIRROR_LONG_ARRAY_DATA_OFFSET,
+ art::mirror::Array::DataOffset(sizeof(uint64_t)).Int32Value())
+ASM_DEFINE(MIRROR_OBJECT_ARRAY_COMPONENT_SIZE,
+ sizeof(art::mirror::HeapReference<art::mirror::Object>))
+ASM_DEFINE(MIRROR_OBJECT_ARRAY_DATA_OFFSET,
+ art::mirror::Array::DataOffset(sizeof(art::mirror::HeapReference<art::mirror::Object>)).Int32Value())
+ASM_DEFINE(MIRROR_SHORT_ARRAY_DATA_OFFSET,
+ art::mirror::Array::DataOffset(sizeof(int16_t)).Int32Value())
+ASM_DEFINE(MIRROR_WIDE_ARRAY_DATA_OFFSET,
+ art::mirror::Array::DataOffset(sizeof(uint64_t)).Int32Value())
diff --git a/tools/cpp-define-generator/mirror_class.def b/tools/cpp-define-generator/mirror_class.def
new file mode 100644
index 0000000000..c15ae92ece
--- /dev/null
+++ b/tools/cpp-define-generator/mirror_class.def
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#if ASM_DEFINE_INCLUDE_DEPENDENCIES
+#include "mirror/class.h"
+#endif
+
+ASM_DEFINE(MIRROR_CLASS_ACCESS_FLAGS_OFFSET,
+ art::mirror::Class::AccessFlagsOffset().Int32Value())
+ASM_DEFINE(MIRROR_CLASS_COMPONENT_TYPE_OFFSET,
+ art::mirror::Class::ComponentTypeOffset().Int32Value())
+ASM_DEFINE(MIRROR_CLASS_DEX_CACHE_OFFSET,
+ art::mirror::Class::DexCacheOffset().Int32Value())
+ASM_DEFINE(MIRROR_CLASS_IF_TABLE_OFFSET,
+ art::mirror::Class::IfTableOffset().Int32Value())
+ASM_DEFINE(MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET,
+ art::mirror::Class::PrimitiveTypeOffset().Int32Value())
+ASM_DEFINE(MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET,
+ art::mirror::Class::ObjectSizeAllocFastPathOffset().Int32Value())
+ASM_DEFINE(MIRROR_CLASS_OBJECT_SIZE_OFFSET,
+ art::mirror::Class::ObjectSizeOffset().Int32Value())
+ASM_DEFINE(MIRROR_CLASS_STATUS_OFFSET,
+ art::mirror::Class::StatusOffset().Int32Value())
+ASM_DEFINE(PRIMITIVE_TYPE_SIZE_SHIFT_SHIFT,
+ art::mirror::Class::kPrimitiveTypeSizeShiftShift)
diff --git a/tools/cpp-define-generator/mirror_dex_cache.def b/tools/cpp-define-generator/mirror_dex_cache.def
new file mode 100644
index 0000000000..5272e86846
--- /dev/null
+++ b/tools/cpp-define-generator/mirror_dex_cache.def
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#if ASM_DEFINE_INCLUDE_DEPENDENCIES
+#include "mirror/dex_cache.h"
+#endif
+
+ASM_DEFINE(METHOD_DEX_CACHE_SIZE_MINUS_ONE,
+ art::mirror::DexCache::kDexCacheMethodCacheSize - 1)
+ASM_DEFINE(MIRROR_DEX_CACHE_RESOLVED_METHODS_OFFSET,
+ art::mirror::DexCache::ResolvedMethodsOffset().Int32Value())
+ASM_DEFINE(STRING_DEX_CACHE_ELEMENT_SIZE,
+ sizeof(art::mirror::StringDexCachePair))
+ASM_DEFINE(STRING_DEX_CACHE_ELEMENT_SIZE_SHIFT,
+ art::WhichPowerOf2(sizeof(art::mirror::StringDexCachePair)))
+ASM_DEFINE(STRING_DEX_CACHE_HASH_BITS,
+ art::LeastSignificantBit(art::mirror::DexCache::kDexCacheStringCacheSize))
+ASM_DEFINE(STRING_DEX_CACHE_SIZE_MINUS_ONE,
+ art::mirror::DexCache::kDexCacheStringCacheSize - 1)
+ASM_DEFINE(METHOD_DEX_CACHE_HASH_BITS,
+ art::LeastSignificantBit(art::mirror::DexCache::kDexCacheMethodCacheSize))
diff --git a/tools/cpp-define-generator/constant_heap.def b/tools/cpp-define-generator/mirror_object.def
index dc76736505..facb037c97 100644
--- a/tools/cpp-define-generator/constant_heap.def
+++ b/tools/cpp-define-generator/mirror_object.def
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,12 +14,13 @@
* limitations under the License.
*/
-// Export heap values.
-
-#if defined(DEFINE_INCLUDE_DEPENDENCIES)
-#include "gc/heap.h"
+#if ASM_DEFINE_INCLUDE_DEPENDENCIES
+#include "mirror/object.h"
#endif
-// Size of references to the heap on the stack.
-DEFINE_EXPR(MIN_LARGE_OBJECT_THRESHOLD, size_t, art::gc::Heap::kMinLargeObjectThreshold)
-
+ASM_DEFINE(MIRROR_OBJECT_CLASS_OFFSET,
+ art::mirror::Object::ClassOffset().Int32Value())
+ASM_DEFINE(MIRROR_OBJECT_HEADER_SIZE,
+ sizeof(art::mirror::Object))
+ASM_DEFINE(MIRROR_OBJECT_LOCK_WORD_OFFSET,
+ art::mirror::Object::MonitorOffset().Int32Value())
diff --git a/tools/cpp-define-generator/common.def b/tools/cpp-define-generator/mirror_string.def
index 76c64c97c2..3632b96d5c 100644
--- a/tools/cpp-define-generator/common.def
+++ b/tools/cpp-define-generator/mirror_string.def
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,11 +14,13 @@
* limitations under the License.
*/
-// Convenience macro to define an offset expression.
-
-#ifndef DEFINE_OFFSET_EXPR
-#define DEFINE_OFFSET_EXPR(holder_type, field_name, field_type, expr) \
- DEFINE_EXPR(holder_type ## _ ## field_name ## _OFFSET, field_type, expr)
-#define DEFINE_OFFSET_EXPR_STANDARD_DEFINITION
+#if ASM_DEFINE_INCLUDE_DEPENDENCIES
+#include "mirror/string.h"
#endif
+ASM_DEFINE(MIRROR_STRING_COUNT_OFFSET,
+ art::mirror::String::CountOffset().Int32Value())
+ASM_DEFINE(MIRROR_STRING_VALUE_OFFSET,
+ art::mirror::String::ValueOffset().Int32Value())
+ASM_DEFINE(STRING_COMPRESSION_FEATURE,
+ art::mirror::kUseStringCompression)
diff --git a/tools/cpp-define-generator/offset_art_method.def b/tools/cpp-define-generator/offset_art_method.def
deleted file mode 100644
index e6a0907759..0000000000
--- a/tools/cpp-define-generator/offset_art_method.def
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.
- */
-
-// Offsets within art::ArtMethod.
-
-#if defined(DEFINE_INCLUDE_DEPENDENCIES)
-#include "art_method.h" // art::ArtMethod
-#include "base/enums.h" // PointerSize
-#include "mirror/dex_cache.h" // art::DexCache
-#endif
-
-#define DEFINE_ART_METHOD_OFFSET_SIZED(field_name, method_name) \
- DEFINE_EXPR(ART_METHOD_ ## field_name ## _OFFSET_32, int32_t, art::ArtMethod::method_name##Offset(art::PointerSize::k32).Int32Value()) \
- DEFINE_EXPR(ART_METHOD_ ## field_name ## _OFFSET_64, int32_t, art::ArtMethod::method_name##Offset(art::PointerSize::k64).Int32Value())
-
-#define DEFINE_ART_METHOD_OFFSET(field_name, method_name) \
- DEFINE_EXPR(ART_METHOD_ ## field_name ## _OFFSET, int32_t, art::ArtMethod::method_name##Offset().Int32Value())
-
-#define DEFINE_DECLARING_CLASS_OFFSET(field_name, method_name) \
- DEFINE_EXPR(DECLARING_CLASS_ ## field_name ## _OFFSET, int32_t, art::mirror::Class::method_name##Offset().Int32Value())
-
-// New macro suffix Method Name (of the Offset method)
-DEFINE_ART_METHOD_OFFSET_SIZED(JNI, EntryPointFromJni)
-DEFINE_ART_METHOD_OFFSET_SIZED(QUICK_CODE, EntryPointFromQuickCompiledCode)
-DEFINE_ART_METHOD_OFFSET(DECLARING_CLASS, DeclaringClass)
-DEFINE_ART_METHOD_OFFSET(ACCESS_FLAGS, AccessFlags)
-
-#undef DEFINE_ART_METHOD_OFFSET
-#undef DEFINE_ART_METHOD_OFFSET_32
-#undef DEFINE_DECLARING_CLASS_OFFSET
diff --git a/tools/cpp-define-generator/offset_mirror_class.def b/tools/cpp-define-generator/offset_mirror_class.def
deleted file mode 100644
index 9b7bfce0c0..0000000000
--- a/tools/cpp-define-generator/offset_mirror_class.def
+++ /dev/null
@@ -1,32 +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.
- */
-
-// Offsets within java.lang.Class (mirror::Class).
-
-#if defined(DEFINE_INCLUDE_DEPENDENCIES)
-#include "mirror/class.h" // art::mirror::Object
-#endif
-
-#include "common.def" // DEFINE_OFFSET_EXPR
-
-#define DEFINE_MIRROR_CLASS_OFFSET(field_name, method_name) \
- DEFINE_OFFSET_EXPR(MIRROR_CLASS, field_name, int32_t, art::mirror::Class::method_name##Offset().Int32Value())
-
-// New macro suffix Method Name (of the Offset method)
-DEFINE_MIRROR_CLASS_OFFSET(DEX_CACHE, DexCache)
-
-#undef DEFINE_MIRROR_CLASS_OFFSET
-#include "common_undef.def" // undef DEFINE_OFFSET_EXPR
diff --git a/tools/cpp-define-generator/offset_mirror_dex_cache.def b/tools/cpp-define-generator/offset_mirror_dex_cache.def
deleted file mode 100644
index 8f008bb631..0000000000
--- a/tools/cpp-define-generator/offset_mirror_dex_cache.def
+++ /dev/null
@@ -1,32 +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.
- */
-
-// Offsets within java.lang.DexCache (mirror::DexCache).
-
-#if defined(DEFINE_INCLUDE_DEPENDENCIES)
-#include "mirror/class.h" // art::mirror::Object
-#endif
-
-#include "common.def" // DEFINE_OFFSET_EXPR
-
-#define DEFINE_MIRROR_DEX_CACHE_OFFSET(field_name, method_name) \
- DEFINE_OFFSET_EXPR(MIRROR_DEX_CACHE, field_name, int32_t, art::mirror::DexCache::method_name##Offset().Int32Value())
-
-// New macro suffix Method Name (of the Offset method)
-DEFINE_MIRROR_DEX_CACHE_OFFSET(RESOLVED_METHODS, ResolvedMethods)
-
-#undef DEFINE_MIRROR_CLASS_OFFSET
-#include "common_undef.def" // undef DEFINE_OFFSET_EXPR
diff --git a/tools/cpp-define-generator/offset_mirror_object.def b/tools/cpp-define-generator/offset_mirror_object.def
deleted file mode 100644
index 9b99634e6e..0000000000
--- a/tools/cpp-define-generator/offset_mirror_object.def
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.
- */
-
-// Offsets within java.lang.Object (mirror::Object).
-
-#if defined(DEFINE_INCLUDE_DEPENDENCIES)
-#include "mirror/object.h" // art::mirror::Object
-#endif
-
-#include "common.def" // DEFINE_OFFSET_EXPR
-
-#define DEFINE_MIRROR_OBJECT_OFFSET(field_name, method_name) \
- DEFINE_OFFSET_EXPR(MIRROR_OBJECT, field_name, int32_t, art::mirror::Object::method_name##Offset().Int32Value())
-
-// New macro suffix Method Name (of the Offset method)
-DEFINE_MIRROR_OBJECT_OFFSET(CLASS, Class)
-DEFINE_MIRROR_OBJECT_OFFSET(LOCK_WORD, Monitor)
-
-#undef DEFINE_MIRROR_OBJECT_OFFSET
-#include "common_undef.def" // undef DEFINE_OFFSET_EXPR
diff --git a/tools/cpp-define-generator/offset_runtime.def b/tools/cpp-define-generator/offset_runtime.def
deleted file mode 100644
index 1d5ce7dd49..0000000000
--- a/tools/cpp-define-generator/offset_runtime.def
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.
- */
-
-// Offsets within ShadowFrame.
-
-#if defined(DEFINE_INCLUDE_DEPENDENCIES)
-#include "base/callee_save_type.h" // art::CalleeSaveType
-#include "runtime.h" // art::Runtime
-#endif
-
-#include "common.def" // DEFINE_OFFSET_EXPR
-
-// Note: these callee save methods loads require read barriers.
-
-#define DEFINE_RUNTIME_CALLEE_SAVE_OFFSET(field_name, constant_name) \
- DEFINE_OFFSET_EXPR(Runtime, \
- field_name ## _METHOD, \
- size_t, \
- art::Runtime::GetCalleeSaveMethodOffset(constant_name))
-
- // Macro substring Constant name
-// Offset of field Runtime::callee_save_methods_[kSaveAllCalleeSaves]
-DEFINE_RUNTIME_CALLEE_SAVE_OFFSET(SAVE_ALL_CALLEE_SAVES, art::CalleeSaveType::kSaveAllCalleeSaves)
-// Offset of field Runtime::callee_save_methods_[kSaveRefsOnly]
-DEFINE_RUNTIME_CALLEE_SAVE_OFFSET(SAVE_REFS_ONLY, art::CalleeSaveType::kSaveRefsOnly)
-// Offset of field Runtime::callee_save_methods_[kSaveRefsAndArgs]
-DEFINE_RUNTIME_CALLEE_SAVE_OFFSET(SAVE_REFS_AND_ARGS, art::CalleeSaveType::kSaveRefsAndArgs)
-// Offset of field Runtime::callee_save_methods_[kSaveEverything]
-DEFINE_RUNTIME_CALLEE_SAVE_OFFSET(SAVE_EVERYTHING, art::CalleeSaveType::kSaveEverything)
-// Offset of field Runtime::callee_save_methods_[kSaveEverythingForClinit]
-DEFINE_RUNTIME_CALLEE_SAVE_OFFSET(SAVE_EVERYTHING_FOR_CLINIT, art::CalleeSaveType::kSaveEverythingForClinit)
-// Offset of field Runtime::callee_save_methods_[kSaveEverythingForSuspendCheck]
-DEFINE_RUNTIME_CALLEE_SAVE_OFFSET(SAVE_EVERYTHING_FOR_SUSPEND_CHECK, art::CalleeSaveType::kSaveEverythingForSuspendCheck)
-
-#undef DEFINE_RUNTIME_CALLEE_SAVE_OFFSET
-#include "common_undef.def" // undef DEFINE_OFFSET_EXPR
diff --git a/tools/cpp-define-generator/offset_shadow_frame.def b/tools/cpp-define-generator/offset_shadow_frame.def
deleted file mode 100644
index b49a3400d3..0000000000
--- a/tools/cpp-define-generator/offset_shadow_frame.def
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.
- */
-
-// Offsets within ShadowFrame.
-
-#if defined(DEFINE_INCLUDE_DEPENDENCIES)
-#include "stack.h" // art::ShadowFrame
-#endif
-
-#include "common.def" // DEFINE_OFFSET_EXPR
-
-#define DEFINE_SHADOW_FRAME_OFFSET(field_name, method_name) \
- DEFINE_OFFSET_EXPR(ShadowFrame, field_name, int32_t, art::ShadowFrame::method_name##Offset())
-
-// New macro suffix Method Name (of the Offset method)
-DEFINE_SHADOW_FRAME_OFFSET(LINK, Link)
-DEFINE_SHADOW_FRAME_OFFSET(METHOD, Method)
-DEFINE_SHADOW_FRAME_OFFSET(RESULT_REGISTER, ResultRegister)
-DEFINE_SHADOW_FRAME_OFFSET(DEX_PC_PTR, DexPCPtr)
-DEFINE_SHADOW_FRAME_OFFSET(CODE_ITEM, CodeItem)
-DEFINE_SHADOW_FRAME_OFFSET(LOCK_COUNT_DATA, LockCountData)
-DEFINE_SHADOW_FRAME_OFFSET(NUMBER_OF_VREGS, NumberOfVRegs)
-DEFINE_SHADOW_FRAME_OFFSET(DEX_PC, DexPC)
-DEFINE_SHADOW_FRAME_OFFSET(CACHED_HOTNESS_COUNTDOWN, CachedHotnessCountdown)
-DEFINE_SHADOW_FRAME_OFFSET(VREGS, VRegs)
-
-#undef DEFINE_SHADOW_FRAME_OFFSET
-#include "common_undef.def" // undef DEFINE_OFFSET_EXPR
diff --git a/tools/cpp-define-generator/offset_thread.def b/tools/cpp-define-generator/offset_thread.def
deleted file mode 100644
index 6f94d38870..0000000000
--- a/tools/cpp-define-generator/offset_thread.def
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.
- */
-
-// Offsets within ShadowFrame.
-
-#if defined(DEFINE_INCLUDE_DEPENDENCIES)
-#include "base/enums.h" // PointerSize
-#include "stack.h" // art::ShadowFrame
-#endif
-
-#include "common.def" // DEFINE_OFFSET_EXPR
-
-#define DEFINE_THREAD_OFFSET(field_name, method_name) \
- DEFINE_OFFSET_EXPR(Thread, field_name, int32_t, art::Thread::method_name##Offset<art::kRuntimePointerSize>().Int32Value())
-
-// New macro suffix Method Name (of the Offset method)
-DEFINE_THREAD_OFFSET(FLAGS, ThreadFlags)
-DEFINE_THREAD_OFFSET(ID, ThinLockId)
-DEFINE_THREAD_OFFSET(IS_GC_MARKING, IsGcMarking)
-DEFINE_THREAD_OFFSET(CARD_TABLE, CardTable)
-
-// TODO: The rest of the offsets
-// are dependent on __SIZEOF_POINTER__
-
-#undef DEFINE_THREAD_OFFSET
-
-#include "common_undef.def" // undef DEFINE_OFFSET_EXPR
diff --git a/tools/cpp-define-generator/offsets_all.def b/tools/cpp-define-generator/offsets_all.def
deleted file mode 100644
index 31587d8d62..0000000000
--- a/tools/cpp-define-generator/offsets_all.def
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.
- */
-
-// Includes every single offset file in art.
-// Useful for processing every single offset together.
-
-// Usage:
-// #define DEFINE_INCLUDE_DEPENDENCIES
-// #include "offsets_all.def"
-// to automatically include each def file's header dependencies.
-//
-// Afterwards,
-// #define DEFINE_EXPR(define_name, field_type, expr) ...
-// #include "offsets_all.def"
-// to process each offset however one wants.
-#if defined(DEFINE_INCLUDE_DEPENDENCIES)
-#define DEFINE_EXPR(define_name, field_type, expr)
-#endif
-
-#if !defined(DEFINE_EXPR)
-#error "Either DEFINE_INCLUDE_DEPENDENCIES or DEFINE_EXPR must be defined"
-#endif
-
-#include "constant_reference.def"
-#include "offset_runtime.def"
-// TODO: rest of THREAD_ offsets (depends on __SIZEOF__POINTER__).
-#include "offset_thread.def"
-// TODO: SHADOW_FRAME depends on __SIZEOF__POINTER__
-// #include "offset_shadow_frame.def"
-// TODO: MIRROR_OBJECT_HEADER_SIZE (depends on #ifdef read barrier)
-#include "offset_mirror_class.def"
-#include "offset_mirror_dex_cache.def"
-#include "offset_mirror_object.def"
-#include "constant_class.def"
-// TODO: MIRROR_*_ARRAY offsets (depends on header size)
-// TODO: MIRROR_STRING offsets (depends on header size)
-#include "offset_art_method.def"
-#include "constant_dexcache.def"
-#include "constant_card_table.def"
-#include "constant_heap.def"
-#include "constant_lockword.def"
-#include "constant_globals.def"
-#include "constant_rosalloc.def"
-#include "constant_thread.def"
-#include "constant_jit.def"
-
-// TODO: MIRROR_OBJECT_HEADER_SIZE #ifdef depends on read barriers
-// TODO: Array offsets (depends on MIRROR_OBJECT_HEADER_SIZE)
-
-#if defined(DEFINE_INCLUDE_DEPENDENCIES)
-#undef DEFINE_EXPR
-#undef DEFINE_INCLUDE_DEPENDENCIES
-#endif
-
-
diff --git a/tools/cpp-define-generator/rosalloc.def b/tools/cpp-define-generator/rosalloc.def
new file mode 100644
index 0000000000..eb8d8f2954
--- /dev/null
+++ b/tools/cpp-define-generator/rosalloc.def
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#if ASM_DEFINE_INCLUDE_DEPENDENCIES
+#include "gc/allocator/rosalloc.h"
+#endif
+
+ASM_DEFINE(ROSALLOC_BRACKET_QUANTUM_SIZE_MASK,
+ art::gc::allocator::RosAlloc::kThreadLocalBracketQuantumSize - 1)
+ASM_DEFINE(ROSALLOC_BRACKET_QUANTUM_SIZE_MASK_TOGGLED32,
+ ~static_cast<uint32_t>(art::gc::allocator::RosAlloc::kThreadLocalBracketQuantumSize - 1))
+ASM_DEFINE(ROSALLOC_BRACKET_QUANTUM_SIZE_MASK_TOGGLED64,
+ ~static_cast<uint64_t>(art::gc::allocator::RosAlloc::kThreadLocalBracketQuantumSize - 1))
+ASM_DEFINE(ROSALLOC_BRACKET_QUANTUM_SIZE_SHIFT,
+ art::gc::allocator::RosAlloc::kThreadLocalBracketQuantumSizeShift)
+ASM_DEFINE(ROSALLOC_MAX_THREAD_LOCAL_BRACKET_SIZE,
+ art::gc::allocator::RosAlloc::kMaxThreadLocalBracketSize)
+ASM_DEFINE(ROSALLOC_RUN_FREE_LIST_HEAD_OFFSET,
+ art::gc::allocator::RosAlloc::RunFreeListHeadOffset())
+ASM_DEFINE(ROSALLOC_RUN_FREE_LIST_OFFSET,
+ art::gc::allocator::RosAlloc::RunFreeListOffset())
+ASM_DEFINE(ROSALLOC_RUN_FREE_LIST_SIZE_OFFSET,
+ art::gc::allocator::RosAlloc::RunFreeListSizeOffset())
+ASM_DEFINE(ROSALLOC_SLOT_NEXT_OFFSET,
+ art::gc::allocator::RosAlloc::RunSlotNextOffset())
diff --git a/tools/cpp-define-generator/runtime.def b/tools/cpp-define-generator/runtime.def
new file mode 100644
index 0000000000..2a2e303ba2
--- /dev/null
+++ b/tools/cpp-define-generator/runtime.def
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+#if ASM_DEFINE_INCLUDE_DEPENDENCIES
+#include "runtime.h"
+#endif
+
+ASM_DEFINE(RUNTIME_SAVE_ALL_CALLEE_SAVES_METHOD_OFFSET,
+ art::Runtime::GetCalleeSaveMethodOffset(art::CalleeSaveType::kSaveAllCalleeSaves))
+ASM_DEFINE(RUNTIME_SAVE_EVERYTHING_FOR_CLINIT_METHOD_OFFSET,
+ art::Runtime::GetCalleeSaveMethodOffset(art::CalleeSaveType::kSaveEverythingForClinit))
+ASM_DEFINE(RUNTIME_SAVE_EVERYTHING_FOR_SUSPEND_CHECK_METHOD_OFFSET,
+ art::Runtime::GetCalleeSaveMethodOffset(art::CalleeSaveType::kSaveEverythingForSuspendCheck))
+ASM_DEFINE(RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET,
+ art::Runtime::GetCalleeSaveMethodOffset(art::CalleeSaveType::kSaveEverything))
+ASM_DEFINE(RUNTIME_SAVE_REFS_AND_ARGS_METHOD_OFFSET,
+ art::Runtime::GetCalleeSaveMethodOffset(art::CalleeSaveType::kSaveRefsAndArgs))
+ASM_DEFINE(RUNTIME_SAVE_REFS_ONLY_METHOD_OFFSET,
+ art::Runtime::GetCalleeSaveMethodOffset(art::CalleeSaveType::kSaveRefsOnly))
diff --git a/tools/cpp-define-generator/shadow_frame.def b/tools/cpp-define-generator/shadow_frame.def
new file mode 100644
index 0000000000..10a309cbdb
--- /dev/null
+++ b/tools/cpp-define-generator/shadow_frame.def
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#if ASM_DEFINE_INCLUDE_DEPENDENCIES
+#include "interpreter/shadow_frame.h"
+#endif
+
+ASM_DEFINE(SHADOWFRAME_CACHED_HOTNESS_COUNTDOWN_OFFSET,
+ art::ShadowFrame::CachedHotnessCountdownOffset())
+ASM_DEFINE(SHADOWFRAME_DEX_INSTRUCTIONS_OFFSET,
+ art::ShadowFrame::DexInstructionsOffset())
+ASM_DEFINE(SHADOWFRAME_DEX_PC_OFFSET,
+ art::ShadowFrame::DexPCOffset())
+ASM_DEFINE(SHADOWFRAME_DEX_PC_PTR_OFFSET,
+ art::ShadowFrame::DexPCPtrOffset())
+ASM_DEFINE(SHADOWFRAME_HOTNESS_COUNTDOWN_OFFSET,
+ art::ShadowFrame::HotnessCountdownOffset())
+ASM_DEFINE(SHADOWFRAME_LINK_OFFSET,
+ art::ShadowFrame::LinkOffset())
+ASM_DEFINE(SHADOWFRAME_LOCK_COUNT_DATA_OFFSET,
+ art::ShadowFrame::LockCountDataOffset())
+ASM_DEFINE(SHADOWFRAME_METHOD_OFFSET,
+ art::ShadowFrame::MethodOffset())
+ASM_DEFINE(SHADOWFRAME_NUMBER_OF_VREGS_OFFSET,
+ art::ShadowFrame::NumberOfVRegsOffset())
+ASM_DEFINE(SHADOWFRAME_RESULT_REGISTER_OFFSET,
+ art::ShadowFrame::ResultRegisterOffset())
+ASM_DEFINE(SHADOWFRAME_VREGS_OFFSET,
+ art::ShadowFrame::VRegsOffset())
diff --git a/tools/cpp-define-generator/thread.def b/tools/cpp-define-generator/thread.def
new file mode 100644
index 0000000000..2dd90fae3f
--- /dev/null
+++ b/tools/cpp-define-generator/thread.def
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#if ASM_DEFINE_INCLUDE_DEPENDENCIES
+#include "thread.h"
+#endif
+
+ASM_DEFINE(THREAD_ALT_IBASE_OFFSET,
+ art::Thread::MterpAltIBaseOffset<art::kRuntimePointerSize>().Int32Value())
+ASM_DEFINE(THREAD_CARD_TABLE_OFFSET,
+ art::Thread::CardTableOffset<art::kRuntimePointerSize>().Int32Value())
+ASM_DEFINE(THREAD_CHECKPOINT_REQUEST,
+ art::kCheckpointRequest)
+ASM_DEFINE(THREAD_CURRENT_IBASE_OFFSET,
+ art::Thread::MterpCurrentIBaseOffset<art::kRuntimePointerSize>().Int32Value())
+ASM_DEFINE(THREAD_DEFAULT_IBASE_OFFSET,
+ art::Thread::MterpDefaultIBaseOffset<art::kRuntimePointerSize>().Int32Value())
+ASM_DEFINE(THREAD_EMPTY_CHECKPOINT_REQUEST,
+ art::kEmptyCheckpointRequest)
+ASM_DEFINE(THREAD_EXCEPTION_OFFSET,
+ art::Thread::ExceptionOffset<art::kRuntimePointerSize>().Int32Value())
+ASM_DEFINE(THREAD_FLAGS_OFFSET,
+ art::Thread::ThreadFlagsOffset<art::kRuntimePointerSize>().Int32Value())
+ASM_DEFINE(THREAD_ID_OFFSET,
+ art::Thread::ThinLockIdOffset<art::kRuntimePointerSize>().Int32Value())
+ASM_DEFINE(THREAD_INTERPRETER_CACHE_OFFSET,
+ art::Thread::InterpreterCacheOffset<art::kRuntimePointerSize>().Int32Value())
+ASM_DEFINE(THREAD_INTERPRETER_CACHE_SIZE_LOG2,
+ art::Thread::InterpreterCacheSizeLog2())
+ASM_DEFINE(THREAD_IS_GC_MARKING_OFFSET,
+ art::Thread::IsGcMarkingOffset<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,
+ art::Thread::ThreadLocalAllocStackTopOffset<art::kRuntimePointerSize>().Int32Value())
+ASM_DEFINE(THREAD_LOCAL_END_OFFSET,
+ art::Thread::ThreadLocalEndOffset<art::kRuntimePointerSize>().Int32Value())
+ASM_DEFINE(THREAD_LOCAL_OBJECTS_OFFSET,
+ art::Thread::ThreadLocalObjectsOffset<art::kRuntimePointerSize>().Int32Value())
+ASM_DEFINE(THREAD_LOCAL_POS_OFFSET,
+ art::Thread::ThreadLocalPosOffset<art::kRuntimePointerSize>().Int32Value())
+ASM_DEFINE(THREAD_ROSALLOC_RUNS_OFFSET,
+ art::Thread::RosAllocRunsOffset<art::kRuntimePointerSize>().Int32Value())
+ASM_DEFINE(THREAD_SELF_OFFSET,
+ art::Thread::SelfOffset<art::kRuntimePointerSize>().Int32Value())
+ASM_DEFINE(THREAD_SUSPEND_OR_CHECKPOINT_REQUEST,
+ art::kSuspendRequest | art::kCheckpointRequest | art::kEmptyCheckpointRequest)
+ASM_DEFINE(THREAD_SUSPEND_REQUEST,
+ art::kSuspendRequest)
+ASM_DEFINE(THREAD_TOP_QUICK_FRAME_OFFSET,
+ art::Thread::TopOfManagedStackOffset<art::kRuntimePointerSize>().Int32Value())
diff --git a/tools/libcore_gcstress_debug_failures.txt b/tools/libcore_gcstress_debug_failures.txt
index b8ad955221..23533af02b 100644
--- a/tools/libcore_gcstress_debug_failures.txt
+++ b/tools/libcore_gcstress_debug_failures.txt
@@ -45,8 +45,7 @@
"libcore.libcore.icu.RelativeDateTimeFormatterTest#test_getRelativeTimeSpanStringAbbrev",
"libcore.libcore.icu.RelativeDateTimeFormatterTest#test_getRelativeTimeSpanStringCTS",
"libcore.libcore.icu.RelativeDateTimeFormatterTest#test_getRelativeTimeSpanStringFrench",
- "libcore.libcore.icu.RelativeDateTimeFormatterTest#test_getRelativeTimeSpanStringGerman",
- "org.apache.harmony.tests.java.lang.ProcessManagerTest#testSleep"
+ "libcore.libcore.icu.RelativeDateTimeFormatterTest#test_getRelativeTimeSpanStringGerman"
]
}
]