| /* |
| * Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| * |
| */ |
| |
| #include "precompiled.hpp" |
| #include "classfile/javaClasses.inline.hpp" |
| #include "jvmtifiles/jvmtiEnv.hpp" |
| #include "memory/resourceArea.hpp" |
| #include "oops/oopHandle.inline.hpp" |
| #include "prims/jvmtiEnvBase.hpp" |
| #include "prims/jvmtiEventController.inline.hpp" |
| #include "prims/jvmtiImpl.hpp" |
| #include "prims/jvmtiThreadState.inline.hpp" |
| #include "runtime/handles.inline.hpp" |
| #include "runtime/interfaceSupport.inline.hpp" |
| #include "runtime/jniHandles.inline.hpp" |
| #include "runtime/safepointVerifiers.hpp" |
| #include "runtime/stackFrameStream.inline.hpp" |
| #include "runtime/vframe.hpp" |
| |
| // marker for when the stack depth has been reset and is now unknown. |
| // any negative number would work but small ones might obscure an |
| // underrun error. |
| static const int UNKNOWN_STACK_DEPTH = -99; |
| |
| /////////////////////////////////////////////////////////////// |
| // |
| // class JvmtiThreadState |
| // |
| // Instances of JvmtiThreadState hang off of each thread. |
| // Thread local storage for JVMTI. |
| // |
| |
| JvmtiThreadState *JvmtiThreadState::_head = nullptr; |
| |
| JvmtiThreadState::JvmtiThreadState(JavaThread* thread, oop thread_oop) |
| : _thread_event_enable() { |
| assert(JvmtiThreadState_lock->is_locked(), "sanity check"); |
| _thread = thread; |
| _thread_saved = nullptr; |
| _exception_state = ES_CLEARED; |
| _debuggable = true; |
| _hide_single_stepping = false; |
| _pending_interp_only_mode = false; |
| _hide_level = 0; |
| _pending_step_for_popframe = false; |
| _class_being_redefined = nullptr; |
| _class_load_kind = jvmti_class_load_kind_load; |
| _classes_being_redefined = nullptr; |
| _head_env_thread_state = nullptr; |
| _dynamic_code_event_collector = nullptr; |
| _vm_object_alloc_event_collector = nullptr; |
| _sampled_object_alloc_event_collector = nullptr; |
| _the_class_for_redefinition_verification = nullptr; |
| _scratch_class_for_redefinition_verification = nullptr; |
| _cur_stack_depth = UNKNOWN_STACK_DEPTH; |
| _saved_interp_only_mode = 0; |
| |
| // JVMTI ForceEarlyReturn support |
| _pending_step_for_earlyret = false; |
| _earlyret_state = earlyret_inactive; |
| _earlyret_tos = ilgl; |
| _earlyret_value.j = 0L; |
| _earlyret_oop = nullptr; |
| _jvmti_event_queue = nullptr; |
| _is_virtual = false; |
| |
| _thread_oop_h = OopHandle(JvmtiExport::jvmti_oop_storage(), thread_oop); |
| |
| // add all the JvmtiEnvThreadState to the new JvmtiThreadState |
| { |
| JvmtiEnvIterator it; |
| for (JvmtiEnvBase* env = it.first(); env != nullptr; env = it.next(env)) { |
| if (env->is_valid()) { |
| add_env(env); |
| } |
| } |
| } |
| |
| // link us into the list |
| { |
| // The thread state list manipulation code must not have safepoints. |
| // See periodic_clean_up(). |
| debug_only(NoSafepointVerifier nosafepoint;) |
| |
| _prev = nullptr; |
| _next = _head; |
| if (_head != nullptr) { |
| _head->_prev = this; |
| } |
| _head = this; |
| } |
| |
| if (thread_oop != nullptr) { |
| java_lang_Thread::set_jvmti_thread_state(thread_oop, this); |
| _is_virtual = java_lang_VirtualThread::is_instance(thread_oop); |
| } |
| |
| if (thread != nullptr) { |
| if (thread_oop == nullptr || thread->jvmti_vthread() == nullptr || thread->jvmti_vthread() == thread_oop) { |
| // The JavaThread for carrier or mounted virtual thread case. |
| // Set this only if thread_oop is current thread->jvmti_vthread(). |
| thread->set_jvmti_thread_state(this); |
| } |
| thread->set_interp_only_mode(0); |
| } |
| } |
| |
| |
| JvmtiThreadState::~JvmtiThreadState() { |
| assert(JvmtiThreadState_lock->is_locked(), "sanity check"); |
| |
| if (_classes_being_redefined != nullptr) { |
| delete _classes_being_redefined; // free the GrowableArray on C heap |
| } |
| |
| // clear this as the state for the thread |
| get_thread()->set_jvmti_thread_state(nullptr); |
| |
| // zap our env thread states |
| { |
| JvmtiEnvBase::entering_dying_thread_env_iteration(); |
| JvmtiEnvThreadStateIterator it(this); |
| for (JvmtiEnvThreadState* ets = it.first(); ets != nullptr; ) { |
| JvmtiEnvThreadState* zap = ets; |
| ets = it.next(ets); |
| delete zap; |
| } |
| JvmtiEnvBase::leaving_dying_thread_env_iteration(); |
| } |
| |
| // remove us from the list |
| { |
| // The thread state list manipulation code must not have safepoints. |
| // See periodic_clean_up(). |
| debug_only(NoSafepointVerifier nosafepoint;) |
| |
| if (_prev == nullptr) { |
| assert(_head == this, "sanity check"); |
| _head = _next; |
| } else { |
| assert(_head != this, "sanity check"); |
| _prev->_next = _next; |
| } |
| if (_next != nullptr) { |
| _next->_prev = _prev; |
| } |
| _next = nullptr; |
| _prev = nullptr; |
| } |
| if (get_thread_oop() != nullptr) { |
| java_lang_Thread::set_jvmti_thread_state(get_thread_oop(), nullptr); |
| } |
| _thread_oop_h.release(JvmtiExport::jvmti_oop_storage()); |
| } |
| |
| |
| void |
| JvmtiThreadState::periodic_clean_up() { |
| assert(SafepointSynchronize::is_at_safepoint(), "at safepoint"); |
| |
| // This iteration is initialized with "_head" instead of "JvmtiThreadState::first()" |
| // because the latter requires the JvmtiThreadState_lock. |
| // This iteration is safe at a safepoint as well, see the NoSafepointVerifier |
| // asserts at all list manipulation sites. |
| for (JvmtiThreadState *state = _head; state != nullptr; state = state->next()) { |
| // For each environment thread state corresponding to an invalid environment |
| // unlink it from the list and deallocate it. |
| JvmtiEnvThreadStateIterator it(state); |
| JvmtiEnvThreadState* previous_ets = nullptr; |
| JvmtiEnvThreadState* ets = it.first(); |
| while (ets != nullptr) { |
| if (ets->get_env()->is_valid()) { |
| previous_ets = ets; |
| ets = it.next(ets); |
| } else { |
| // This one isn't valid, remove it from the list and deallocate it |
| JvmtiEnvThreadState* defunct_ets = ets; |
| ets = ets->next(); |
| if (previous_ets == nullptr) { |
| assert(state->head_env_thread_state() == defunct_ets, "sanity check"); |
| state->set_head_env_thread_state(ets); |
| } else { |
| previous_ets->set_next(ets); |
| } |
| delete defunct_ets; |
| } |
| } |
| } |
| } |
| |
| // |
| // Virtual Threads Mount State transition (VTMS transition) mechanism |
| // |
| |
| // VTMS transitions cannot be disabled while this counter is positive. |
| volatile int JvmtiVTMSTransitionDisabler::_VTMS_transition_count = 0; |
| |
| // VTMS transitions for one virtual thread are disabled while it is positive |
| volatile int JvmtiVTMSTransitionDisabler::_VTMS_transition_disable_for_one_count = 0; |
| |
| // VTMS transitions for all virtual threads are disabled while it is positive |
| volatile int JvmtiVTMSTransitionDisabler::_VTMS_transition_disable_for_all_count = 0; |
| |
| // There is an active suspender or resumer. |
| volatile bool JvmtiVTMSTransitionDisabler::_SR_mode = false; |
| |
| // Notifications from VirtualThread about VTMS events are enabled. |
| bool JvmtiVTMSTransitionDisabler::_VTMS_notify_jvmti_events = false; |
| |
| #ifdef ASSERT |
| void |
| JvmtiVTMSTransitionDisabler::print_info() { |
| log_error(jvmti)("_VTMS_transition_count: %d\n", _VTMS_transition_count); |
| log_error(jvmti)("_VTMS_transition_disable_for_one_count: %d\n", _VTMS_transition_disable_for_one_count); |
| log_error(jvmti)("_VTMS_transition_disable_for_all_count: %d\n\n", _VTMS_transition_disable_for_all_count); |
| int attempts = 10000; |
| for (JavaThreadIteratorWithHandle jtiwh; JavaThread *java_thread = jtiwh.next(); ) { |
| ResourceMark rm; |
| // Handshake with target. |
| PrintStackTraceClosure pstc; |
| Handshake::execute(&pstc, java_thread); |
| } |
| } |
| #endif |
| |
| // disable VTMS transitions for one virtual thread |
| // no-op if thread is non-null and not a virtual thread |
| JvmtiVTMSTransitionDisabler::JvmtiVTMSTransitionDisabler(jthread thread) |
| : _is_SR(false), _thread(thread) |
| { |
| if (!Continuations::enabled()) { |
| return; // JvmtiVTMSTransitionDisabler is no-op without virtual threads |
| } |
| if (Thread::current_or_null() == nullptr) { |
| return; // Detached thread, can be a call from Agent_OnLoad. |
| } |
| if (_thread != nullptr) { |
| VTMS_transition_disable_for_one(); // disable VTMS transitions for one virtual thread |
| } else { |
| VTMS_transition_disable_for_all(); // disable VTMS transitions for all virtual threads |
| } |
| } |
| |
| // disable VTMS transitions for all virtual threads |
| JvmtiVTMSTransitionDisabler::JvmtiVTMSTransitionDisabler(bool is_SR) |
| : _is_SR(is_SR), _thread(nullptr) |
| { |
| if (!Continuations::enabled()) { |
| return; // JvmtiVTMSTransitionDisabler is no-op without virtual threads |
| } |
| if (Thread::current_or_null() == nullptr) { |
| return; // Detached thread, can be a call from Agent_OnLoad. |
| } |
| VTMS_transition_disable_for_all(); |
| } |
| |
| JvmtiVTMSTransitionDisabler::~JvmtiVTMSTransitionDisabler() { |
| if (!Continuations::enabled()) { |
| return; // JvmtiVTMSTransitionDisabler is a no-op without virtual threads |
| } |
| if (Thread::current_or_null() == nullptr) { |
| return; // Detached thread, can be a call from Agent_OnLoad. |
| } |
| if (_thread != nullptr) { |
| VTMS_transition_enable_for_one(); // enable VTMS transitions for one virtual thread |
| } else { |
| VTMS_transition_enable_for_all(); // enable VTMS transitions for all virtual threads |
| } |
| } |
| |
| // disable VTMS transitions for one virtual thread |
| void |
| JvmtiVTMSTransitionDisabler::VTMS_transition_disable_for_one() { |
| assert(_thread != nullptr, "sanity check"); |
| JavaThread* thread = JavaThread::current(); |
| HandleMark hm(thread); |
| Handle vth = Handle(thread, JNIHandles::resolve_external_guard(_thread)); |
| if (!java_lang_VirtualThread::is_instance(vth())) { |
| return; // no-op if _thread is not a virtual thread |
| } |
| MonitorLocker ml(JvmtiVTMSTransition_lock); |
| |
| while (_SR_mode) { // suspender or resumer is a JvmtiVTMSTransitionDisabler monopolist |
| ml.wait(10); // wait while there is an active suspender or resumer |
| } |
| Atomic::inc(&_VTMS_transition_disable_for_one_count); |
| java_lang_Thread::inc_VTMS_transition_disable_count(vth()); |
| |
| while (java_lang_Thread::is_in_VTMS_transition(vth())) { |
| ml.wait(10); // wait while the virtual thread is in transition |
| } |
| #ifdef ASSERT |
| thread->set_is_VTMS_transition_disabler(true); |
| #endif |
| } |
| |
| // disable VTMS transitions for all virtual threads |
| void |
| JvmtiVTMSTransitionDisabler::VTMS_transition_disable_for_all() { |
| JavaThread* thread = JavaThread::current(); |
| int attempts = 50000; |
| { |
| MonitorLocker ml(JvmtiVTMSTransition_lock); |
| |
| assert(!thread->is_in_tmp_VTMS_transition(), "sanity check"); |
| assert(!thread->is_in_VTMS_transition(), "VTMS_transition sanity check"); |
| while (_SR_mode) { // Suspender or resumer is a JvmtiVTMSTransitionDisabler monopolist. |
| ml.wait(10); // Wait while there is an active suspender or resumer. |
| } |
| if (_is_SR) { |
| _SR_mode = true; |
| while (_VTMS_transition_disable_for_all_count > 0 || |
| _VTMS_transition_disable_for_one_count > 0) { |
| ml.wait(10); // Wait while there is any active jvmtiVTMSTransitionDisabler. |
| } |
| } |
| Atomic::inc(&_VTMS_transition_disable_for_all_count); |
| |
| // Block while some mount/unmount transitions are in progress. |
| // Debug version fails and prints diagnostic information. |
| while (_VTMS_transition_count > 0) { |
| if (ml.wait(10)) { |
| attempts--; |
| } |
| DEBUG_ONLY(if (attempts == 0) break;) |
| } |
| assert(!thread->is_VTMS_transition_disabler(), "VTMS_transition sanity check"); |
| #ifdef ASSERT |
| if (attempts > 0) { |
| thread->set_is_VTMS_transition_disabler(true); |
| } |
| #endif |
| } |
| #ifdef ASSERT |
| if (attempts == 0) { |
| print_info(); |
| fatal("stuck in JvmtiVTMSTransitionDisabler::VTMS_transition_disable"); |
| } |
| #endif |
| } |
| |
| // enable VTMS transitions for one virtual thread |
| void |
| JvmtiVTMSTransitionDisabler::VTMS_transition_enable_for_one() { |
| JavaThread* thread = JavaThread::current(); |
| HandleMark hm(thread); |
| Handle vth = Handle(thread, JNIHandles::resolve_external_guard(_thread)); |
| if (!java_lang_VirtualThread::is_instance(vth())) { |
| return; // no-op if _thread is not a virtual thread |
| } |
| MonitorLocker ml(JvmtiVTMSTransition_lock); |
| java_lang_Thread::dec_VTMS_transition_disable_count(vth()); |
| Atomic::dec(&_VTMS_transition_disable_for_one_count); |
| if (_VTMS_transition_disable_for_one_count == 0) { |
| ml.notify_all(); |
| } |
| #ifdef ASSERT |
| thread->set_is_VTMS_transition_disabler(false); |
| #endif |
| } |
| |
| // enable VTMS transitions for all virtual threads |
| void |
| JvmtiVTMSTransitionDisabler::VTMS_transition_enable_for_all() { |
| JavaThread* current = JavaThread::current(); |
| { |
| MonitorLocker ml(JvmtiVTMSTransition_lock); |
| assert(_VTMS_transition_disable_for_all_count > 0, "VTMS_transition sanity check"); |
| |
| if (_is_SR) { // Disabler is suspender or resumer. |
| _SR_mode = false; |
| } |
| Atomic::dec(&_VTMS_transition_disable_for_all_count); |
| if (_VTMS_transition_disable_for_all_count == 0 || _is_SR) { |
| ml.notify_all(); |
| } |
| #ifdef ASSERT |
| current->set_is_VTMS_transition_disabler(false); |
| #endif |
| } |
| } |
| |
| void |
| JvmtiVTMSTransitionDisabler::start_VTMS_transition(jthread vthread, bool is_mount) { |
| JavaThread* thread = JavaThread::current(); |
| HandleMark hm(thread); |
| Handle vth = Handle(thread, JNIHandles::resolve_external_guard(vthread)); |
| int attempts = 50000; |
| |
| // Avoid using MonitorLocker on performance critical path, use |
| // two-level synchronization with lock-free operations on counters. |
| Atomic::inc(&_VTMS_transition_count); // Try to enter VTMS transition section optmistically. |
| java_lang_Thread::set_is_in_VTMS_transition(vth(), true); |
| |
| // Do not allow suspends inside VTMS transitions. |
| // Block while transitions are disabled or there are suspend requests. |
| int64_t thread_id = java_lang_Thread::thread_id(vth()); // Cannot use oops while blocked. |
| |
| if (_VTMS_transition_disable_for_all_count > 0 || |
| java_lang_Thread::VTMS_transition_disable_count(vth()) > 0 || |
| thread->is_suspended() || |
| JvmtiVTSuspender::is_vthread_suspended(thread_id) |
| ) { |
| // Slow path: undo unsuccessful optimistic counter incrementation. |
| // It can cause an extra waiting cycle for VTMS transition disablers. |
| Atomic::dec(&_VTMS_transition_count); |
| java_lang_Thread::set_is_in_VTMS_transition(vth(), false); |
| |
| while (true) { |
| MonitorLocker ml(JvmtiVTMSTransition_lock); |
| |
| // Do not allow suspends inside VTMS transitions. |
| // Block while transitions are disabled or there are suspend requests. |
| if (_VTMS_transition_disable_for_all_count > 0 || |
| java_lang_Thread::VTMS_transition_disable_count(vth()) > 0 || |
| thread->is_suspended() || |
| JvmtiVTSuspender::is_vthread_suspended(thread_id) |
| ) { |
| // Block while transitions are disabled or there are suspend requests. |
| if (ml.wait(10)) { |
| attempts--; |
| } |
| DEBUG_ONLY(if (attempts == 0) break;) |
| continue; // ~ThreadBlockInVM has handshake-based suspend point. |
| } |
| Atomic::inc(&_VTMS_transition_count); |
| java_lang_Thread::set_is_in_VTMS_transition(vth(), true); |
| break; |
| } |
| } |
| #ifdef ASSERT |
| if (attempts == 0) { |
| log_error(jvmti)("start_VTMS_transition: thread->is_suspended: %d is_vthread_suspended: %d\n\n", |
| thread->is_suspended(), JvmtiVTSuspender::is_vthread_suspended(thread_id)); |
| print_info(); |
| fatal("stuck in JvmtiVTMSTransitionDisabler::start_VTMS_transition"); |
| } |
| #endif |
| // Enter VTMS transition section. |
| assert(!thread->is_in_VTMS_transition(), "VTMS_transition sanity check"); |
| thread->set_is_in_VTMS_transition(true); |
| } |
| |
| void |
| JvmtiVTMSTransitionDisabler::finish_VTMS_transition(jthread vthread, bool is_mount) { |
| JavaThread* thread = JavaThread::current(); |
| |
| assert(thread->is_in_VTMS_transition(), "sanity check"); |
| thread->set_is_in_VTMS_transition(false); |
| oop vt = JNIHandles::resolve_external_guard(vthread); |
| int64_t thread_id = java_lang_Thread::thread_id(vt); |
| java_lang_Thread::set_is_in_VTMS_transition(vt, false); |
| |
| Atomic::dec(&_VTMS_transition_count); |
| |
| // Unblock waiting VTMS transition disablers. |
| if (_VTMS_transition_disable_for_one_count > 0 || |
| _VTMS_transition_disable_for_all_count > 0) { |
| MonitorLocker ml(JvmtiVTMSTransition_lock); |
| ml.notify_all(); |
| } |
| // In unmount case the carrier thread is attached after unmount transition. |
| // Check and block it if there was external suspend request. |
| int attempts = 10000; |
| if (!is_mount && thread->is_carrier_thread_suspended()) { |
| while (true) { |
| MonitorLocker ml(JvmtiVTMSTransition_lock); |
| |
| // Block while there are suspend requests. |
| if ((!is_mount && thread->is_carrier_thread_suspended()) || |
| (is_mount && JvmtiVTSuspender::is_vthread_suspended(thread_id)) |
| ) { |
| // Block while there are suspend requests. |
| if (ml.wait(10)) { |
| attempts--; |
| } |
| DEBUG_ONLY(if (attempts == 0) break;) |
| continue; |
| } |
| break; |
| } |
| } |
| #ifdef ASSERT |
| if (attempts == 0) { |
| log_error(jvmti)("finish_VTMS_transition: thread->is_suspended: %d is_vthread_suspended: %d\n\n", |
| thread->is_suspended(), JvmtiVTSuspender::is_vthread_suspended(thread_id)); |
| print_info(); |
| fatal("stuck in JvmtiVTMSTransitionDisabler::finish_VTMS_transition"); |
| } |
| #endif |
| } |
| |
| // set VTMS transition bit value in JavaThread and java.lang.VirtualThread object |
| void JvmtiVTMSTransitionDisabler::set_is_in_VTMS_transition(JavaThread* thread, jobject vthread, bool in_trans) { |
| oop vt = JNIHandles::resolve_external_guard(vthread); |
| java_lang_Thread::set_is_in_VTMS_transition(vt, in_trans); |
| thread->set_is_in_VTMS_transition(in_trans); |
| } |
| |
| void |
| JvmtiVTMSTransitionDisabler::VTMS_vthread_start(jobject vthread) { |
| VTMS_mount_end(vthread); |
| JavaThread* thread = JavaThread::current(); |
| |
| assert(!thread->is_in_VTMS_transition(), "sanity check"); |
| assert(!thread->is_in_tmp_VTMS_transition(), "sanity check"); |
| |
| JvmtiEventController::thread_started(thread); |
| if (JvmtiExport::can_support_virtual_threads()) { |
| if (JvmtiExport::should_post_vthread_start()) { |
| JvmtiExport::post_vthread_start(vthread); |
| } |
| } |
| // post VirtualThreadMount event after VirtualThreadStart |
| if (JvmtiExport::should_post_vthread_mount()) { |
| JvmtiExport::post_vthread_mount(vthread); |
| } |
| } |
| |
| void |
| JvmtiVTMSTransitionDisabler::VTMS_vthread_end(jobject vthread) { |
| JavaThread* thread = JavaThread::current(); |
| |
| assert(!thread->is_in_VTMS_transition(), "sanity check"); |
| assert(!thread->is_in_tmp_VTMS_transition(), "sanity check"); |
| |
| // post VirtualThreadUnmount event before VirtualThreadEnd |
| if (JvmtiExport::should_post_vthread_unmount()) { |
| JvmtiExport::post_vthread_unmount(vthread); |
| } |
| if (JvmtiExport::can_support_virtual_threads()) { |
| if (JvmtiExport::should_post_vthread_end()) { |
| JvmtiExport::post_vthread_end(vthread); |
| } |
| } |
| VTMS_unmount_begin(vthread, /* last_unmount */ true); |
| if (thread->jvmti_thread_state() != nullptr) { |
| JvmtiExport::cleanup_thread(thread); |
| assert(thread->jvmti_thread_state() == nullptr, "should be null"); |
| assert(java_lang_Thread::jvmti_thread_state(JNIHandles::resolve(vthread)) == nullptr, "should be null"); |
| } |
| thread->rebind_to_jvmti_thread_state_of(thread->threadObj()); |
| } |
| |
| void |
| JvmtiVTMSTransitionDisabler::VTMS_vthread_mount(jobject vthread, bool hide) { |
| if (hide) { |
| VTMS_mount_begin(vthread); |
| } else { |
| VTMS_mount_end(vthread); |
| if (JvmtiExport::should_post_vthread_mount()) { |
| JvmtiExport::post_vthread_mount(vthread); |
| } |
| } |
| } |
| |
| void |
| JvmtiVTMSTransitionDisabler::VTMS_vthread_unmount(jobject vthread, bool hide) { |
| if (hide) { |
| if (JvmtiExport::should_post_vthread_unmount()) { |
| JvmtiExport::post_vthread_unmount(vthread); |
| } |
| VTMS_unmount_begin(vthread, /* last_unmount */ false); |
| } else { |
| VTMS_unmount_end(vthread); |
| } |
| } |
| |
| void |
| JvmtiVTMSTransitionDisabler::VTMS_mount_begin(jobject vthread) { |
| JavaThread* thread = JavaThread::current(); |
| assert(!thread->is_in_tmp_VTMS_transition(), "sanity check"); |
| assert(!thread->is_in_VTMS_transition(), "sanity check"); |
| start_VTMS_transition(vthread, /* is_mount */ true); |
| } |
| |
| void |
| JvmtiVTMSTransitionDisabler::VTMS_mount_end(jobject vthread) { |
| JavaThread* thread = JavaThread::current(); |
| oop vt = JNIHandles::resolve(vthread); |
| |
| thread->rebind_to_jvmti_thread_state_of(vt); |
| |
| { |
| MutexLocker mu(JvmtiThreadState_lock); |
| JvmtiThreadState* state = thread->jvmti_thread_state(); |
| if (state != nullptr && state->is_pending_interp_only_mode()) { |
| JvmtiEventController::enter_interp_only_mode(); |
| } |
| } |
| assert(thread->is_in_VTMS_transition(), "sanity check"); |
| assert(!thread->is_in_tmp_VTMS_transition(), "sanity check"); |
| finish_VTMS_transition(vthread, /* is_mount */ true); |
| } |
| |
| void |
| JvmtiVTMSTransitionDisabler::VTMS_unmount_begin(jobject vthread, bool last_unmount) { |
| JavaThread* thread = JavaThread::current(); |
| |
| assert(!thread->is_in_tmp_VTMS_transition(), "sanity check"); |
| assert(!thread->is_in_VTMS_transition(), "sanity check"); |
| |
| start_VTMS_transition(vthread, /* is_mount */ false); |
| if (!last_unmount) { |
| thread->rebind_to_jvmti_thread_state_of(thread->threadObj()); |
| } |
| } |
| |
| void |
| JvmtiVTMSTransitionDisabler::VTMS_unmount_end(jobject vthread) { |
| JavaThread* thread = JavaThread::current(); |
| assert(thread->is_in_VTMS_transition(), "sanity check"); |
| assert(!thread->is_in_tmp_VTMS_transition(), "sanity check"); |
| finish_VTMS_transition(vthread, /* is_mount */ false); |
| } |
| |
| |
| // |
| // Virtual Threads Suspend/Resume management |
| // |
| |
| JvmtiVTSuspender::SR_Mode |
| JvmtiVTSuspender::_SR_mode = SR_none; |
| |
| VirtualThreadList* |
| JvmtiVTSuspender::_suspended_list = new VirtualThreadList(); |
| |
| VirtualThreadList* |
| JvmtiVTSuspender::_not_suspended_list = new VirtualThreadList(); |
| |
| void |
| JvmtiVTSuspender::register_all_vthreads_suspend() { |
| MonitorLocker ml(JvmtiVTMSTransition_lock); |
| |
| _SR_mode = SR_all; |
| _suspended_list->invalidate(); |
| _not_suspended_list->invalidate(); |
| } |
| |
| void |
| JvmtiVTSuspender::register_all_vthreads_resume() { |
| MonitorLocker ml(JvmtiVTMSTransition_lock); |
| |
| _SR_mode = SR_none; |
| _suspended_list->invalidate(); |
| _not_suspended_list->invalidate(); |
| } |
| |
| void |
| JvmtiVTSuspender::register_vthread_suspend(oop vt) { |
| int64_t id = java_lang_Thread::thread_id(vt); |
| MonitorLocker ml(JvmtiVTMSTransition_lock); |
| |
| if (_SR_mode == SR_all) { |
| assert(_not_suspended_list->contains(id), |
| "register_vthread_suspend sanity check"); |
| _not_suspended_list->remove(id); |
| } else { |
| assert(!_suspended_list->contains(id), |
| "register_vthread_suspend sanity check"); |
| _SR_mode = SR_ind; |
| _suspended_list->append(id); |
| } |
| } |
| |
| void |
| JvmtiVTSuspender::register_vthread_resume(oop vt) { |
| int64_t id = java_lang_Thread::thread_id(vt); |
| MonitorLocker ml(JvmtiVTMSTransition_lock); |
| |
| if (_SR_mode == SR_all) { |
| assert(!_not_suspended_list->contains(id), |
| "register_vthread_resume sanity check"); |
| _not_suspended_list->append(id); |
| } else if (_SR_mode == SR_ind) { |
| assert(_suspended_list->contains(id), |
| "register_vthread_resume check"); |
| _suspended_list->remove(id); |
| if (_suspended_list->length() == 0) { |
| _SR_mode = SR_none; |
| } |
| } else { |
| assert(false, "register_vthread_resume: no suspend mode enabled"); |
| } |
| } |
| |
| bool |
| JvmtiVTSuspender::is_vthread_suspended(int64_t thread_id) { |
| bool suspend_is_needed = |
| (_SR_mode == SR_all && !_not_suspended_list->contains(thread_id)) || |
| (_SR_mode == SR_ind && _suspended_list->contains(thread_id)); |
| |
| return suspend_is_needed; |
| } |
| |
| bool |
| JvmtiVTSuspender::is_vthread_suspended(oop vt) { |
| return is_vthread_suspended(java_lang_Thread::thread_id(vt)); |
| } |
| |
| void JvmtiThreadState::add_env(JvmtiEnvBase *env) { |
| assert(JvmtiThreadState_lock->is_locked(), "sanity check"); |
| |
| JvmtiEnvThreadState *new_ets = new JvmtiEnvThreadState(this, env); |
| // add this environment thread state to the end of the list (order is important) |
| { |
| // list deallocation (which occurs at a safepoint) cannot occur simultaneously |
| debug_only(NoSafepointVerifier nosafepoint;) |
| |
| JvmtiEnvThreadStateIterator it(this); |
| JvmtiEnvThreadState* previous_ets = nullptr; |
| for (JvmtiEnvThreadState* ets = it.first(); ets != nullptr; ets = it.next(ets)) { |
| previous_ets = ets; |
| } |
| if (previous_ets == nullptr) { |
| set_head_env_thread_state(new_ets); |
| } else { |
| previous_ets->set_next(new_ets); |
| } |
| } |
| } |
| |
| void JvmtiThreadState::enter_interp_only_mode() { |
| assert(_thread != nullptr, "sanity check"); |
| _thread->increment_interp_only_mode(); |
| invalidate_cur_stack_depth(); |
| } |
| |
| void JvmtiThreadState::leave_interp_only_mode() { |
| assert(is_interp_only_mode(), "leaving interp only when not in interp only mode"); |
| if (_thread == nullptr) { |
| // Unmounted virtual thread updates the saved value. |
| --_saved_interp_only_mode; |
| } else { |
| _thread->decrement_interp_only_mode(); |
| } |
| } |
| |
| |
| // Helper routine used in several places |
| int JvmtiThreadState::count_frames() { |
| JavaThread* thread = get_thread_or_saved(); |
| javaVFrame *jvf; |
| ResourceMark rm; |
| if (thread == nullptr) { |
| oop thread_obj = get_thread_oop(); |
| jvf = JvmtiEnvBase::get_vthread_jvf(thread_obj); |
| } else { |
| #ifdef ASSERT |
| Thread *current_thread = Thread::current(); |
| #endif |
| assert(SafepointSynchronize::is_at_safepoint() || |
| thread->is_handshake_safe_for(current_thread), |
| "call by myself / at safepoint / at handshake"); |
| if (!thread->has_last_Java_frame()) return 0; // No Java frames. |
| // TBD: This might need to be corrected for detached carrier threads. |
| RegisterMap reg_map(thread, |
| RegisterMap::UpdateMap::skip, |
| RegisterMap::ProcessFrames::skip, |
| RegisterMap::WalkContinuation::include); |
| jvf = thread->last_java_vframe(®_map); |
| jvf = JvmtiEnvBase::check_and_skip_hidden_frames(thread, jvf); |
| } |
| return (int)JvmtiEnvBase::get_frame_count(jvf); |
| } |
| |
| |
| void JvmtiThreadState::invalidate_cur_stack_depth() { |
| assert(SafepointSynchronize::is_at_safepoint() || |
| get_thread()->is_handshake_safe_for(Thread::current()), |
| "bad synchronization with owner thread"); |
| |
| _cur_stack_depth = UNKNOWN_STACK_DEPTH; |
| } |
| |
| void JvmtiThreadState::incr_cur_stack_depth() { |
| guarantee(JavaThread::current() == get_thread(), "must be current thread"); |
| |
| if (!is_interp_only_mode()) { |
| _cur_stack_depth = UNKNOWN_STACK_DEPTH; |
| } |
| if (_cur_stack_depth != UNKNOWN_STACK_DEPTH) { |
| ++_cur_stack_depth; |
| } |
| } |
| |
| void JvmtiThreadState::decr_cur_stack_depth() { |
| guarantee(JavaThread::current() == get_thread(), "must be current thread"); |
| |
| if (!is_interp_only_mode()) { |
| _cur_stack_depth = UNKNOWN_STACK_DEPTH; |
| } |
| if (_cur_stack_depth != UNKNOWN_STACK_DEPTH) { |
| --_cur_stack_depth; |
| assert(_cur_stack_depth >= 0, "incr/decr_cur_stack_depth mismatch"); |
| } |
| } |
| |
| int JvmtiThreadState::cur_stack_depth() { |
| Thread *current = Thread::current(); |
| guarantee(get_thread()->is_handshake_safe_for(current), |
| "must be current thread or direct handshake"); |
| |
| if (!is_interp_only_mode() || _cur_stack_depth == UNKNOWN_STACK_DEPTH) { |
| _cur_stack_depth = count_frames(); |
| } else { |
| #ifdef ASSERT |
| if (EnableJVMTIStackDepthAsserts) { |
| // heavy weight assert |
| jint num_frames = count_frames(); |
| assert(_cur_stack_depth == num_frames, "cur_stack_depth out of sync _cur_stack_depth: %d num_frames: %d", _cur_stack_depth, num_frames); |
| } |
| #endif |
| } |
| return _cur_stack_depth; |
| } |
| |
| void JvmtiThreadState::process_pending_step_for_popframe() { |
| // We are single stepping as the last part of the PopFrame() dance |
| // so we have some house keeping to do. |
| |
| JavaThread *thr = get_thread(); |
| if (thr->popframe_condition() != JavaThread::popframe_inactive) { |
| // If the popframe_condition field is not popframe_inactive, then |
| // we missed all of the popframe_field cleanup points: |
| // |
| // - unpack_frames() was not called (nothing to deopt) |
| // - remove_activation_preserving_args_entry() was not called |
| // (did not get suspended in a call_vm() family call and did |
| // not complete a call_vm() family call on the way here) |
| thr->clear_popframe_condition(); |
| } |
| |
| // clearing the flag indicates we are done with the PopFrame() dance |
| clr_pending_step_for_popframe(); |
| |
| // If exception was thrown in this frame, need to reset jvmti thread state. |
| // Single stepping may not get enabled correctly by the agent since |
| // exception state is passed in MethodExit event which may be sent at some |
| // time in the future. JDWP agent ignores MethodExit events if caused by |
| // an exception. |
| // |
| if (is_exception_detected()) { |
| clear_exception_state(); |
| } |
| // If step is pending for popframe then it may not be |
| // a repeat step. The new_bci and method_id is same as current_bci |
| // and current method_id after pop and step for recursive calls. |
| // Force the step by clearing the last location. |
| JvmtiEnvThreadStateIterator it(this); |
| for (JvmtiEnvThreadState* ets = it.first(); ets != nullptr; ets = it.next(ets)) { |
| ets->clear_current_location(); |
| } |
| } |
| |
| |
| // Class: JvmtiThreadState |
| // Function: update_for_pop_top_frame |
| // Description: |
| // This function removes any frame pop notification request for |
| // the top frame and invalidates both the current stack depth and |
| // all cached frameIDs. |
| // |
| // Called by: PopFrame |
| // |
| void JvmtiThreadState::update_for_pop_top_frame() { |
| if (is_interp_only_mode()) { |
| // remove any frame pop notification request for the top frame |
| // in any environment |
| int popframe_number = cur_stack_depth(); |
| { |
| JvmtiEnvThreadStateIterator it(this); |
| for (JvmtiEnvThreadState* ets = it.first(); ets != nullptr; ets = it.next(ets)) { |
| if (ets->is_frame_pop(popframe_number)) { |
| ets->clear_frame_pop(popframe_number); |
| } |
| } |
| } |
| // force stack depth to be recalculated |
| invalidate_cur_stack_depth(); |
| } else { |
| assert(!is_enabled(JVMTI_EVENT_FRAME_POP), "Must have no framepops set"); |
| } |
| } |
| |
| |
| void JvmtiThreadState::process_pending_step_for_earlyret() { |
| // We are single stepping as the last part of the ForceEarlyReturn |
| // dance so we have some house keeping to do. |
| |
| if (is_earlyret_pending()) { |
| // If the earlyret_state field is not earlyret_inactive, then |
| // we missed all of the earlyret_field cleanup points: |
| // |
| // - remove_activation() was not called |
| // (did not get suspended in a call_vm() family call and did |
| // not complete a call_vm() family call on the way here) |
| // |
| // One legitimate way for us to miss all the cleanup points is |
| // if we got here right after handling a compiled return. If that |
| // is the case, then we consider our return from compiled code to |
| // complete the ForceEarlyReturn request and we clear the condition. |
| clr_earlyret_pending(); |
| set_earlyret_oop(nullptr); |
| clr_earlyret_value(); |
| } |
| |
| // clearing the flag indicates we are done with |
| // the ForceEarlyReturn() dance |
| clr_pending_step_for_earlyret(); |
| |
| // If exception was thrown in this frame, need to reset jvmti thread state. |
| // Single stepping may not get enabled correctly by the agent since |
| // exception state is passed in MethodExit event which may be sent at some |
| // time in the future. JDWP agent ignores MethodExit events if caused by |
| // an exception. |
| // |
| if (is_exception_detected()) { |
| clear_exception_state(); |
| } |
| // If step is pending for earlyret then it may not be a repeat step. |
| // The new_bci and method_id is same as current_bci and current |
| // method_id after earlyret and step for recursive calls. |
| // Force the step by clearing the last location. |
| JvmtiEnvThreadStateIterator it(this); |
| for (JvmtiEnvThreadState* ets = it.first(); ets != nullptr; ets = it.next(ets)) { |
| ets->clear_current_location(); |
| } |
| } |
| |
| void JvmtiThreadState::oops_do(OopClosure* f, CodeBlobClosure* cf) { |
| f->do_oop((oop*) &_earlyret_oop); |
| |
| // Keep nmethods from unloading on the event queue |
| if (_jvmti_event_queue != nullptr) { |
| _jvmti_event_queue->oops_do(f, cf); |
| } |
| } |
| |
| void JvmtiThreadState::nmethods_do(CodeBlobClosure* cf) { |
| // Keep nmethods from unloading on the event queue |
| if (_jvmti_event_queue != nullptr) { |
| _jvmti_event_queue->nmethods_do(cf); |
| } |
| } |
| |
| // Thread local event queue. |
| void JvmtiThreadState::enqueue_event(JvmtiDeferredEvent* event) { |
| if (_jvmti_event_queue == nullptr) { |
| _jvmti_event_queue = new JvmtiDeferredEventQueue(); |
| } |
| // copy the event |
| _jvmti_event_queue->enqueue(*event); |
| } |
| |
| void JvmtiThreadState::post_events(JvmtiEnv* env) { |
| if (_jvmti_event_queue != nullptr) { |
| _jvmti_event_queue->post(env); // deletes each queue node |
| delete _jvmti_event_queue; |
| _jvmti_event_queue = nullptr; |
| } |
| } |
| |
| void JvmtiThreadState::run_nmethod_entry_barriers() { |
| if (_jvmti_event_queue != nullptr) { |
| _jvmti_event_queue->run_nmethod_entry_barriers(); |
| } |
| } |
| |
| oop JvmtiThreadState::get_thread_oop() { |
| return _thread_oop_h.resolve(); |
| } |
| |
| void JvmtiThreadState::set_thread(JavaThread* thread) { |
| _thread_saved = nullptr; // Common case. |
| if (!_is_virtual && thread == nullptr) { |
| // Save JavaThread* if carrier thread is being detached. |
| _thread_saved = _thread; |
| } |
| _thread = thread; |
| } |