diff options
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, ©_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", + ¬ify_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" ] } ] |