< prev index next >

src/hotspot/share/prims/jvmtiThreadState.cpp

Print this page
@@ -23,15 +23,21 @@
   */
  
  #include "precompiled.hpp"
  #include "jvmtifiles/jvmtiEnv.hpp"
  #include "memory/resourceArea.hpp"
+ #include "oops/oopHandle.inline.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.hpp"
  #include "runtime/safepointVerifiers.hpp"
+ #include "runtime/stackFrameStream.inline.hpp"
  #include "runtime/vframe.hpp"
+ #include "prims/jvmtiEnvBase.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;

@@ -44,14 +50,15 @@
  // Thread local storage for JVMTI.
  //
  
  JvmtiThreadState *JvmtiThreadState::_head = NULL;
  
- JvmtiThreadState::JvmtiThreadState(JavaThread* thread)
+ JvmtiThreadState::JvmtiThreadState(JavaThread* thread, oop thread_oop)
    : _thread_event_enable() {
    assert(JvmtiThreadState_lock->is_locked(), "sanity check");
    _thread               = thread;
+   _thread_saved         = NULL;
    _exception_state      = ES_CLEARED;
    _debuggable           = true;
    _hide_single_stepping = false;
    _hide_level           = 0;
    _pending_step_for_popframe = false;

@@ -63,19 +70,24 @@
    _vm_object_alloc_event_collector = NULL;
    _sampled_object_alloc_event_collector = NULL;
    _the_class_for_redefinition_verification = NULL;
    _scratch_class_for_redefinition_verification = NULL;
    _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 = NULL;
  
    _jvmti_event_queue = NULL;
+   _is_in_VTMT = false;
+   _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 != NULL; env = it.next(env)) {

@@ -97,12 +109,23 @@
        _head->_prev = this;
      }
      _head = this;
    }
  
-   // set this as the state for the thread
-   thread->set_jvmti_thread_state(this);
+   if (thread_oop != NULL) {
+     java_lang_Thread::set_jvmti_thread_state(thread_oop, (JvmtiThreadState*)this);
+     _is_virtual = java_lang_VirtualThread::is_instance(thread_oop);
+   }
+ 
+   // thread can be NULL if virtual thread is unmounted
+   if (thread != NULL) {
+     // set this as the state for the thread only if thread_oop is current thread->mounted_vthread()
+     if (thread_oop == NULL || thread->mounted_vthread() == NULL || thread->mounted_vthread() == thread_oop) {
+       thread->set_jvmti_thread_state(this);
+     }
+     thread->set_interp_only_mode(0);
+   }
  }
  
  
  JvmtiThreadState::~JvmtiThreadState()   {
    assert(JvmtiThreadState_lock->is_locked(), "sanity check");

@@ -143,10 +166,14 @@
        _next->_prev = _prev;
      }
      _next = NULL;
      _prev = NULL;
    }
+   if (get_thread_oop() != NULL) {
+     java_lang_Thread::set_jvmti_thread_state(get_thread_oop(), NULL);
+   }
+   _thread_oop_h.release(JvmtiExport::jvmti_oop_storage());
  }
  
  
  void
  JvmtiThreadState::periodic_clean_up() {

@@ -180,14 +207,330 @@
        }
      }
    }
  }
  
+ /* Virtual Threads Mount Transition (VTMT) mechanism */
+ 
+ #ifdef ASSERT
+ // used for debugging
+ volatile unsigned short JvmtiVTMTDisabler::_suspend_count = 0;
+ #endif
+ 
+ // VTMT can not be disabled while this counter is positive
+ volatile unsigned short JvmtiVTMTDisabler::_VTMT_count = 0;
+ 
+ // VTMT is disabled while this counter is positive
+ volatile unsigned short JvmtiVTMTDisabler::_VTMT_disable_count = 0;
+ 
+ JvmtiVTMTDisabler::JvmtiVTMTDisabler(bool is_suspender) {
+   _self_suspend = false;
+   _is_suspender = is_suspender;
+ #ifdef ASSERT
+   {
+     ThreadBlockInVM tbivm(JavaThread::current());
+     MonitorLocker ml(JvmtiVTMT_lock, Mutex::_no_safepoint_check_flag);
+     if (_is_suspender) {
+       _suspend_count++;
+     }
+   }
+ #endif
+   disable_VTMT();
+ }
+ 
+ JvmtiVTMTDisabler::~JvmtiVTMTDisabler() {
+ #ifdef ASSERT
+   {
+     ThreadBlockInVM tbivm(JavaThread::current());
+     MonitorLocker ml(JvmtiVTMT_lock, Mutex::_no_safepoint_check_flag);
+     if (_is_suspender) {
+       _suspend_count--;
+     }
+   }
+ #endif
+   enable_VTMT();
+   if (_self_suspend) {
+     JvmtiSuspendControl::suspend(JavaThread::current());
+   }
+ }
+ 
+ void
+ JvmtiVTMTDisabler::set_self_suspend() {
+   _self_suspend = true;
+ }
+ 
+ #ifdef ASSERT
+ void
+ JvmtiVTMTDisabler::print_info() {
+   tty->print_cr("_VTMT_disable_count: %d _VTMT_count: %d _suspend_count: %d\n",
+                 _VTMT_disable_count, _VTMT_count, _suspend_count);
+   for (JavaThreadIteratorWithHandle jtiwh; JavaThread *java_thread = jtiwh.next(); ) {
+     ResourceMark rm;
+     // Handshake with target
+     PrintStackTraceClosure pstc;
+     Handshake::execute(&pstc, java_thread);
+   }
+ }
+ #endif
+ 
+ void
+ JvmtiVTMTDisabler::disable_VTMT() {
+   JavaThread* thread = JavaThread::current();
+   int attempts = 10;
+   {
+     ThreadBlockInVM tbivm(thread);
+     MonitorLocker ml(JvmtiVTMT_lock, Mutex::_no_safepoint_check_flag);
+ 
+     assert(!thread->is_in_VTMT(), "VTMT sanity check");
+     _VTMT_disable_count++;
+ #if 0
+     assert(_suspend_count < 2, "TMP assert if we ever have multiple suspenders");
+     assert(_suspend_count == 0 || _VTMT_disable_count < 2, "TMP assert if we ever have disablers with suspender");
+ #endif
+ 
+     // Block while some mount/unmount transitions are in progress.
+     // Debug version fails and print diagnostic information
+     while (_VTMT_count > 0) {
+       if (ml.wait(1000)) {
+         attempts--;
+       }
+       DEBUG_ONLY(if (attempts == 0) break;)
+     }
+     assert(!thread->is_VTMT_disabler(), "VTMT sanity check");
+     if (attempts != 0) {
+       thread->set_is_VTMT_disabler(true);
+     }
+   }
+ #ifdef ASSERT
+   if (attempts == 0) {
+     print_info();
+     assert(false, "stuck in JvmtiVTMTDisabler::disable_VTMT for 10 seconds.");
+   }
+ #endif
+ }
+ 
+ void
+ JvmtiVTMTDisabler::enable_VTMT() {
+   MonitorLocker ml(JvmtiVTMT_lock, Mutex::_no_safepoint_check_flag);
+   assert(_VTMT_count == 0 && _VTMT_disable_count > 0, "VTMT sanity check");
+ 
+ #if 0
+     assert(_suspend_count < 2, "TMP assert if we ever have multiple suspenders");
+     assert(_suspend_count == 0 || _VTMT_disable_count < 2, "TMP assert if we ever have disablers with suspender");
+ #endif
+  
+   if (--_VTMT_disable_count == 0) {
+     ml.notify_all();
+   }
+   JavaThread* thread = JavaThread::current();
+   thread->set_is_VTMT_disabler(false);
+ }
+ 
+ void
+ JvmtiVTMTDisabler::start_VTMT(jthread vthread, int callsite_tag) {
+   JavaThread* thread = JavaThread::current();
+   HandleMark hm(thread);
+   Handle vth = Handle(thread, JNIHandles::resolve_external_guard(vthread));
+ 
+   // Do not allow suspends inside VTMT transitions.
+   // Block while transitions are disabled or there are suspend requests.
+   int attempts = 10 * 100;
+   while (true) {
+     ThreadBlockInVM tbivm(thread);
+     MonitorLocker ml(JvmtiVTMT_lock, Mutex::_no_safepoint_check_flag);
+ 
+ #if 0
+     assert(_suspend_count < 2, "TMP assert if we ever have multiple suspenders");
+     assert(_suspend_count == 0 || _VTMT_disable_count < 2, "TMP assert if we ever have disablers with suspender");
+ #endif
+     // block while transitions are disabled or there are suspend requests
+     if (_VTMT_disable_count > 0 ||
+         thread->is_suspended() ||
+         JvmtiVTSuspender::is_vthread_suspended(vth())
+     ) {
+       if (ml.wait(10)) {
+         attempts--;
+       }
+       DEBUG_ONLY(if (attempts == 0) break;)
+       continue; // ~ThreadBlockInVM has handshake-based suspend point
+     }
+     assert(!thread->is_in_VTMT(), "VTMT sanity check");
+     thread->set_is_in_VTMT(true);
+     JvmtiThreadState* vstate = java_lang_Thread::jvmti_thread_state(vth());
+     if (vstate != NULL) {
+       vstate->set_is_in_VTMT(true);
+     }
+     _VTMT_count++;
+     break;
+   }
+ #ifdef ASSERT
+   if (attempts == 0) {
+     tty->print_cr("DBG: start_VTMT: thread->is_suspended: %d is_vthread_suspended: %d\n",
+                   thread->is_suspended(), JvmtiVTSuspender::is_vthread_suspended(vth()));
+     print_info();
+     assert(false, "stuck in JvmtiVTMTDisabler::start_VTMT for 10 seconds.");
+   }
+ #endif
+ }
+ 
+ void
+ JvmtiVTMTDisabler::finish_VTMT(jthread vthread, int callsite_tag) {
+   JavaThread* thread = JavaThread::current();
+   MonitorLocker ml(JvmtiVTMT_lock, Mutex::_no_safepoint_check_flag);
+ 
+ #if 0
+     assert(_suspend_count < 2, "TMP assert if we ever have multiple suspenders");
+     assert(_suspend_count == 0 || _VTMT_disable_count < 2, "TMP assert if we ever have disablers with suspender");
+ #endif
+   _VTMT_count--;
+ 
+   // unblock waiting VTMT disablers
+   if (_VTMT_disable_count > 0) {
+     ml.notify_all();
+   }
+   assert(thread->is_in_VTMT(), "sanity check");
+   thread->set_is_in_VTMT(false);
+   oop vt = JNIHandles::resolve_external_guard(vthread);
+   JvmtiThreadState* vstate = java_lang_Thread::jvmti_thread_state(vt);
+   if (vstate != NULL) {
+     vstate->set_is_in_VTMT(false);
+   }
+ }
+ 
+ /* VThreadList implementation */
+ 
+ int
+ VThreadList::find(oop vt) const {
+   for (int idx = 0; idx < length(); idx++) {
+     if (vt == at(idx).resolve()) return idx;
+   }
+   return -1;
+ }
+ 
+ bool
+ VThreadList::contains(oop vt) const {
+   int idx = find(vt);
+   return idx != -1;
+ }
+ 
+ static OopHandle NULLHandle = OopHandle(NULL);
+ 
+ void
+ VThreadList::append(oop vt) {
+   assert(!contains(vt), "VThreadList::append sanity check");
+ 
+   OopHandle vthandle(JvmtiExport::jvmti_oop_storage(), vt);
+   GrowableArrayCHeap<OopHandle, mtServiceability>::append(vthandle);
+ }
+ 
+ void
+ VThreadList::remove(oop vt) {
+   int idx = find(vt);
+   assert(idx != -1, "VThreadList::remove sanity check");
+   at(idx).release(JvmtiExport::jvmti_oop_storage());
+   at_put(idx, NULLHandle); // clear released OopHandle entry
+ 
+   // To work around assert in OopHandle assignment operator do not use remove_at().
+   // OopHandle doesn't allow overwrites if the oop pointer is non-null.
+   // Order doesn't matter, put the last element in idx
+   int last = length() - 1;
+   if (last > idx) {
+     at_put(idx, at(last));
+     at_put(last, NULLHandle); // clear moved OopHandle entry.
+   }
+   pop();
+ }
+ 
+ void
+ VThreadList::invalidate() {
+   for (int idx = length() - 1; idx >= 0; idx--) {
+     at(idx).release(JvmtiExport::jvmti_oop_storage());
+     at_put(idx, NULLHandle); // clear released OopHandle entries
+   }
+   clear();
+ }
+ 
+ /* Virtual Threads Suspend/Resume management */
+ 
+ JvmtiVTSuspender::VThreadSuspendMode
+ JvmtiVTSuspender::_vthread_suspend_mode = vthread_suspend_none;
+ 
+ VThreadList*
+ JvmtiVTSuspender::_vthread_suspend_list = new VThreadList();
+ 
+ VThreadList*
+ JvmtiVTSuspender::_vthread_resume_list = new VThreadList();
+ 
+ void
+ JvmtiVTSuspender::register_all_vthreads_suspend() {
+   MonitorLocker ml(JvmtiVTMT_lock, Mutex::_no_safepoint_check_flag);
+ 
+   _vthread_suspend_mode = vthread_suspend_all;
+   _vthread_suspend_list->invalidate();
+   _vthread_resume_list->invalidate();
+ }
+ 
+ void
+ JvmtiVTSuspender::register_all_vthreads_resume() {
+   MonitorLocker ml(JvmtiVTMT_lock, Mutex::_no_safepoint_check_flag);
+ 
+   _vthread_suspend_mode = vthread_suspend_none;
+   _vthread_suspend_list->invalidate();
+   _vthread_resume_list->invalidate();
+ }
+ 
+ bool
+ JvmtiVTSuspender::register_vthread_suspend(oop vt) {
+   MonitorLocker ml(JvmtiVTMT_lock, Mutex::_no_safepoint_check_flag);
+ 
+   if (_vthread_suspend_mode == vthread_suspend_all) {
+     assert(_vthread_resume_list->contains(vt),
+            "register_vthread_suspend sanity check");
+     _vthread_resume_list->remove(vt);
+   } else {
+     assert(!_vthread_suspend_list->contains(vt),
+            "register_vthread_suspend sanity check");
+     _vthread_suspend_mode = vthread_suspend_ind;
+     _vthread_suspend_list->append(vt);
+   }
+   return true;
+ }
+ 
+ bool
+ JvmtiVTSuspender::register_vthread_resume(oop vt) {
+   MonitorLocker ml(JvmtiVTMT_lock, Mutex::_no_safepoint_check_flag);
+ 
+   if (_vthread_suspend_mode == vthread_suspend_all) {
+     assert(!_vthread_resume_list->contains(vt),
+            "register_vthread_resume sanity check");
+     _vthread_resume_list->append(vt);
+   } else if (_vthread_suspend_mode == vthread_suspend_ind) {
+     assert(_vthread_suspend_list->contains(vt),
+            "register_vthread_resume check");
+     _vthread_suspend_list->remove(vt);
+     if (_vthread_suspend_list->length() == 0) {
+       _vthread_suspend_mode = vthread_suspend_none;
+     }
+   } else {
+     assert(false, "register_vthread_resume: no suspend mode enabled");
+   }
+   return true;
+ }
+ 
+ bool
+ JvmtiVTSuspender::is_vthread_suspended(oop vt) {
+   bool suspend_is_needed =
+    (_vthread_suspend_mode == vthread_suspend_all && !_vthread_resume_list->contains(vt)) ||
+    (_vthread_suspend_mode == vthread_suspend_ind && _vthread_suspend_list->contains(vt));
+ 
+   return suspend_is_needed;
+ }
+ 
  void JvmtiThreadState::add_env(JvmtiEnvBase *env) {
    assert(JvmtiThreadState_lock->is_locked(), "sanity check");
  
-   JvmtiEnvThreadState *new_ets = new JvmtiEnvThreadState(_thread, env);
+   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;)
  

@@ -202,46 +545,68 @@
        previous_ets->set_next(new_ets);
      }
    }
  }
  
- 
- 
- 
  void JvmtiThreadState::enter_interp_only_mode() {
-   assert(_thread->get_interp_only_mode() == 0, "entering interp only when mode not zero");
-   _thread->increment_interp_only_mode();
+   if (_thread == NULL) {
+     assert(!is_interp_only_mode(), "entering interp only when mode not zero");
+     ++_saved_interp_only_mode;
+     // TBD: It seems, invalidate_cur_stack_depth() has to be called at VTMT?
+   } else {
+ #ifdef DBG // TMP
+   if (is_interp_only_mode()) {
+     ResourceMark rm(JavaThread::current());
+     oop name_oop = java_lang_Thread::name(get_thread_oop());
+     const char* name_str = java_lang_String::as_utf8_string(name_oop);
+     name_str = name_str == NULL ? "<NULL>" : name_str;
+ 
+     const char* virt = is_virtual() ? "virtual" : "carrier";
+     printf("DBG: enter_interp_only_mode: %s state: %p, interp_only: %d, saved_interp_only: %d, %s\n",
+            virt, (void*)this, _thread->get_interp_only_mode(), _saved_interp_only_mode, name_str); fflush(0);
+   }
+ #endif
+     assert(!is_interp_only_mode(), "entering interp only when mode not zero");
+     _thread->increment_interp_only_mode();
+     invalidate_cur_stack_depth();
+   }
  }
  
  
  void JvmtiThreadState::leave_interp_only_mode() {
-   assert(_thread->get_interp_only_mode() == 1, "leaving interp only when mode not one");
-   _thread->decrement_interp_only_mode();
+   if (_thread == NULL) {
+     assert(is_interp_only_mode(), "leaving interp only when mode not one");
+     --_saved_interp_only_mode;
+   } else {
+     assert(is_interp_only_mode(), "leaving interp only when mode not one");
+     _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 == NULL) {
+     oop thread_obj = get_thread_oop();
+     jvf = JvmtiEnvBase::get_vthread_jvf(thread_obj);
+   } else {
  #ifdef ASSERT
-   Thread *current_thread = Thread::current();
+     Thread *current_thread = Thread::current();
  #endif
-   assert(SafepointSynchronize::is_at_safepoint() ||
-          get_thread()->is_handshake_safe_for(current_thread),
-          "call by myself / at safepoint / at handshake");
- 
-   if (!get_thread()->has_last_Java_frame()) return 0;  // no Java frames
- 
-   ResourceMark rm;
-   RegisterMap reg_map(get_thread());
-   javaVFrame *jvf = get_thread()->last_java_vframe(&reg_map);
-   int n = 0;
-   while (jvf != NULL) {
-     Method* method = jvf->method();
-     jvf = jvf->java_sender();
-     n++;
+     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, false, false, true);
+     jvf = thread->last_java_vframe(&reg_map);
+     jvf = JvmtiEnvBase::check_and_skip_hidden_frames(thread, jvf);
    }
-   return n;
+   return (int)JvmtiEnvBase::get_frame_count(jvf);
  }
  
  
  void JvmtiThreadState::invalidate_cur_stack_depth() {
    assert(SafepointSynchronize::is_at_safepoint() ||

@@ -257,20 +622,36 @@
    if (!is_interp_only_mode()) {
      _cur_stack_depth = UNKNOWN_STACK_DEPTH;
    }
    if (_cur_stack_depth != UNKNOWN_STACK_DEPTH) {
      ++_cur_stack_depth;
+ #ifdef ASSERT
+     if (EnableJVMTIStackDepthAsserts) {
+       // heavy weight assert
+       // fixme: remove this before merging loom with main jdk repo
+       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
    }
  }
  
  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) {
+ #ifdef ASSERT
+     if (EnableJVMTIStackDepthAsserts) {
+       // heavy weight assert
+       // fixme: remove this before merging loom with main jdk repo
+       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
      --_cur_stack_depth;
      assert(_cur_stack_depth >= 0, "incr/decr_cur_stack_depth mismatch");
    }
  }
  

@@ -284,12 +665,11 @@
    } 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);
+       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;
  }

@@ -443,5 +823,27 @@
  void JvmtiThreadState::run_nmethod_entry_barriers() {
    if (_jvmti_event_queue != NULL) {
      _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 = NULL; // common case;
+   if (!_is_virtual && thread == NULL) {
+     // Save JavaThread* if carrier thread is being detached.
+     _thread_saved = _thread;
+   }
+   _thread = thread;
+ }
+ 
+ int JvmtiVTMTDisabler::VTMT_disable_count() {
+   return _VTMT_disable_count;
+ }
+ 
+ int JvmtiVTMTDisabler::VTMT_count() {
+   return _VTMT_count;
+ }
+ 
< prev index next >