8373367: interp-only mechanism fails to work for carrier threads in a corner case

Reviewed-by: amenkov, lmesnik
This commit is contained in:
Serguei Spitsyn 2026-02-27 04:47:48 +00:00
parent 6daca7ef99
commit dc06fede2a
7 changed files with 61 additions and 52 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2026, 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
@ -2491,7 +2491,7 @@ SetOrClearFramePopClosure::do_thread(Thread *target) {
_result = JVMTI_ERROR_NO_MORE_FRAMES;
return;
}
assert(_state->get_thread_or_saved() == java_thread, "Must be");
assert(_state->get_thread() == java_thread, "Must be");
RegisterMap reg_map(java_thread,
RegisterMap::UpdateMap::include,

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2026, 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
@ -151,11 +151,6 @@ bool JvmtiEnvThreadState::is_virtual() {
return _state->is_virtual();
}
// Use _thread_saved if cthread is detached from JavaThread (_thread == nullptr).
JavaThread* JvmtiEnvThreadState::get_thread_or_saved() {
return _state->get_thread_or_saved();
}
JavaThread* JvmtiEnvThreadState::get_thread() {
return _state->get_thread();
}
@ -344,7 +339,7 @@ void JvmtiEnvThreadState::reset_current_location(jvmtiEvent event_type, bool ena
if (enabled) {
// If enabling breakpoint, no need to reset.
// Can't do anything if empty stack.
JavaThread* thread = get_thread_or_saved();
JavaThread* thread = get_thread();
if (event_type == JVMTI_EVENT_SINGLE_STEP &&
((thread == nullptr && is_virtual()) || thread->has_last_Java_frame())) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2026, 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
@ -170,8 +170,6 @@ public:
inline JvmtiThreadState* jvmti_thread_state() { return _state; }
// use _thread_saved if cthread is detached from JavaThread
JavaThread *get_thread_or_saved();
JavaThread *get_thread();
inline JvmtiEnv *get_env() { return _env; }

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2026, 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
@ -217,6 +217,10 @@ class EnterInterpOnlyModeClosure : public HandshakeClosure {
assert(state != nullptr, "sanity check");
assert(state->get_thread() == jt, "handshake unsafe conditions");
assert(jt->jvmti_thread_state() == state, "sanity check");
assert(!jt->is_interp_only_mode(), "sanity check");
assert(!state->is_interp_only_mode(), "sanity check");
if (!state->is_pending_interp_only_mode()) {
_completed = true;
return; // The pending flag has been already cleared, so bail out.
@ -361,7 +365,8 @@ void VM_ChangeSingleStep::doit() {
void JvmtiEventControllerPrivate::enter_interp_only_mode(JvmtiThreadState *state) {
EC_TRACE(("[%s] # Entering interpreter only mode",
JvmtiTrace::safe_get_thread_name(state->get_thread_or_saved())));
JvmtiTrace::safe_get_thread_name(state->get_thread())));
JavaThread *target = state->get_thread();
Thread *current = Thread::current();
@ -371,8 +376,13 @@ void JvmtiEventControllerPrivate::enter_interp_only_mode(JvmtiThreadState *state
}
// This flag will be cleared in EnterInterpOnlyModeClosure handshake.
state->set_pending_interp_only_mode(true);
if (target == nullptr) { // an unmounted virtual thread
return; // EnterInterpOnlyModeClosure will be executed right after mount.
// There are two cases when entering interp_only_mode is postponed:
// 1. Unmounted virtual thread - EnterInterpOnlyModeClosure::do_thread will be executed at mount;
// 2. Carrier thread with mounted virtual thread - EnterInterpOnlyModeClosure::do_thread will be executed at unmount.
if (target == nullptr || // an unmounted virtual thread
JvmtiEnvBase::is_thread_carrying_vthread(target, state->get_thread_oop())) { // a vthread carrying thread
return; // EnterInterpOnlyModeClosure will be executed right after mount or unmount.
}
EnterInterpOnlyModeClosure hs(state);
if (target->is_handshake_safe_for(current)) {
@ -388,7 +398,8 @@ void JvmtiEventControllerPrivate::enter_interp_only_mode(JvmtiThreadState *state
void
JvmtiEventControllerPrivate::leave_interp_only_mode(JvmtiThreadState *state) {
EC_TRACE(("[%s] # Leaving interpreter only mode",
JvmtiTrace::safe_get_thread_name(state->get_thread_or_saved())));
JvmtiTrace::safe_get_thread_name(state->get_thread())));
if (state->is_pending_interp_only_mode()) {
state->set_pending_interp_only_mode(false); // Just clear the pending flag.
assert(!state->is_interp_only_mode(), "sanity check");
@ -409,7 +420,7 @@ JvmtiEventControllerPrivate::trace_changed(JvmtiThreadState *state, jlong now_en
if (changed & bit) {
// it changed, print it
log_trace(jvmti)("[%s] # %s event %s",
JvmtiTrace::safe_get_thread_name(state->get_thread_or_saved()),
JvmtiTrace::safe_get_thread_name(state->get_thread()),
(now_enabled & bit)? "Enabling" : "Disabling", JvmtiTrace::event_name((jvmtiEvent)ei));
}
}
@ -932,7 +943,7 @@ JvmtiEventControllerPrivate::set_user_enabled(JvmtiEnvBase *env, JavaThread *thr
void
JvmtiEventControllerPrivate::set_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) {
EC_TRACE(("[%s] # set frame pop - frame=%d",
JvmtiTrace::safe_get_thread_name(ets->get_thread_or_saved()),
JvmtiTrace::safe_get_thread_name(ets->get_thread()),
fpop.frame_number() ));
ets->get_frame_pops()->set(fpop);
@ -943,7 +954,7 @@ JvmtiEventControllerPrivate::set_frame_pop(JvmtiEnvThreadState *ets, JvmtiFrameP
void
JvmtiEventControllerPrivate::clear_frame_pop(JvmtiEnvThreadState *ets, JvmtiFramePop fpop) {
EC_TRACE(("[%s] # clear frame pop - frame=%d",
JvmtiTrace::safe_get_thread_name(ets->get_thread_or_saved()),
JvmtiTrace::safe_get_thread_name(ets->get_thread()),
fpop.frame_number() ));
ets->get_frame_pops()->clear(fpop);
@ -953,7 +964,7 @@ JvmtiEventControllerPrivate::clear_frame_pop(JvmtiEnvThreadState *ets, JvmtiFram
void
JvmtiEventControllerPrivate::clear_all_frame_pops(JvmtiEnvThreadState *ets) {
EC_TRACE(("[%s] # clear all frame pops",
JvmtiTrace::safe_get_thread_name(ets->get_thread_or_saved())
JvmtiTrace::safe_get_thread_name(ets->get_thread())
));
ets->get_frame_pops()->clear_all();
@ -965,7 +976,7 @@ JvmtiEventControllerPrivate::clear_to_frame_pop(JvmtiEnvThreadState *ets, JvmtiF
int cleared_cnt = ets->get_frame_pops()->clear_to(fpop);
EC_TRACE(("[%s] # clear to frame pop - frame=%d, count=%d",
JvmtiTrace::safe_get_thread_name(ets->get_thread_or_saved()),
JvmtiTrace::safe_get_thread_name(ets->get_thread()),
fpop.frame_number(),
cleared_cnt ));

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2026, 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
@ -57,7 +57,6 @@ 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;
_hide_single_stepping = false;
_pending_interp_only_mode = false;
@ -118,11 +117,11 @@ JvmtiThreadState::JvmtiThreadState(JavaThread* thread, oop 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.
// The JavaThread for an active carrier or a mounted virtual thread case.
// Set this only if thread_oop is current thread->jvmti_vthread().
thread->set_jvmti_thread_state(this);
assert(!thread->is_interp_only_mode(), "sanity check");
}
thread->set_interp_only_mode(false);
}
}
@ -135,7 +134,10 @@ JvmtiThreadState::~JvmtiThreadState() {
}
// clear this as the state for the thread
assert(get_thread() != nullptr, "sanity check");
assert(get_thread()->jvmti_thread_state() == this, "sanity check");
get_thread()->set_jvmti_thread_state(nullptr);
get_thread()->set_interp_only_mode(false);
// zap our env thread states
{
@ -323,6 +325,9 @@ void JvmtiThreadState::enter_interp_only_mode() {
assert(_thread != nullptr, "sanity check");
assert(JvmtiThreadState_lock->is_locked(), "sanity check");
assert(!is_interp_only_mode(), "entering interp only when in interp only mode");
assert(_thread->jvmti_vthread() == nullptr || _thread->jvmti_vthread() == get_thread_oop(), "sanity check");
assert(_thread->jvmti_thread_state() == this, "sanity check");
_saved_interp_only_mode = true;
_thread->set_interp_only_mode(true);
invalidate_cur_stack_depth();
}
@ -330,10 +335,9 @@ void JvmtiThreadState::enter_interp_only_mode() {
void JvmtiThreadState::leave_interp_only_mode() {
assert(JvmtiThreadState_lock->is_locked(), "sanity check");
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 = false;
} else {
_saved_interp_only_mode = false;
if (_thread != nullptr && _thread->jvmti_thread_state() == this) {
assert(_thread->jvmti_vthread() == nullptr || _thread->jvmti_vthread() == get_thread_oop(), "sanity check");
_thread->set_interp_only_mode(false);
}
}
@ -341,7 +345,7 @@ void JvmtiThreadState::leave_interp_only_mode() {
// Helper routine used in several places
int JvmtiThreadState::count_frames() {
JavaThread* thread = get_thread_or_saved();
JavaThread* thread = get_thread();
javaVFrame *jvf;
ResourceMark rm;
if (thread == nullptr) {
@ -578,11 +582,8 @@ void JvmtiThreadState::update_thread_oop_during_vm_start() {
}
}
// For virtual threads only.
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;
}
assert(is_virtual(), "sanity check");
_thread = thread;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2026, 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
@ -123,8 +123,11 @@ class JvmtiVTSuspender : AllStatic {
class JvmtiThreadState : public CHeapObj<mtInternal> {
private:
friend class JvmtiEnv;
// The _thread field is a link to the JavaThread associated with JvmtiThreadState.
// A platform (including carrier) thread should always have a stable link to its JavaThread.
// The _thread field of a virtual thread should point to the JavaThread when
// virtual thread is mounted. It should be set to null when it is unmounted.
JavaThread *_thread;
JavaThread *_thread_saved;
OopHandle _thread_oop_h;
// Jvmti Events that cannot be posted in their current context.
JvmtiDeferredEventQueue* _jvmti_event_queue;
@ -219,7 +222,7 @@ class JvmtiThreadState : public CHeapObj<mtInternal> {
// Used by the interpreter for fullspeed debugging support
bool is_interp_only_mode() {
return _thread == nullptr ? _saved_interp_only_mode : _thread->is_interp_only_mode();
return _saved_interp_only_mode;
}
void enter_interp_only_mode();
void leave_interp_only_mode();
@ -248,8 +251,10 @@ class JvmtiThreadState : public CHeapObj<mtInternal> {
int count_frames();
inline JavaThread *get_thread() { return _thread; }
inline JavaThread *get_thread_or_saved(); // return _thread_saved if _thread is null
inline JavaThread *get_thread() {
assert(is_virtual() || _thread != nullptr, "sanity check");
return _thread;
}
// Needed for virtual threads as they can migrate to different JavaThread's.
// Also used for carrier threads to clear/restore _thread.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2006, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2006, 2026, 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
@ -130,22 +130,21 @@ inline JvmtiThreadState* JvmtiThreadState::state_for(JavaThread *thread, Handle
return state;
}
inline JavaThread* JvmtiThreadState::get_thread_or_saved() {
// Use _thread_saved if cthread is detached from JavaThread (_thread == null).
return (_thread == nullptr && !is_virtual()) ? _thread_saved : _thread;
}
inline void JvmtiThreadState::set_should_post_on_exceptions(bool val) {
get_thread_or_saved()->set_should_post_on_exceptions_flag(val ? JNI_TRUE : JNI_FALSE);
get_thread()->set_should_post_on_exceptions_flag(val ? JNI_TRUE : JNI_FALSE);
}
inline void JvmtiThreadState::unbind_from(JvmtiThreadState* state, JavaThread* thread) {
if (state == nullptr) {
assert(!thread->is_interp_only_mode(), "sanity check");
return;
}
// Save thread's interp_only_mode.
state->_saved_interp_only_mode = thread->is_interp_only_mode();
state->set_thread(nullptr); // Make sure stale _thread value is never used.
assert(thread->jvmti_thread_state() == state, "sanity check");
assert(state->get_thread() == thread, "sanity check");
assert(thread->is_interp_only_mode() == state->_saved_interp_only_mode, "sanity check");
if (state->is_virtual()) { // clean _thread link for virtual threads only
state->set_thread(nullptr); // make sure stale _thread value is never used
}
}
inline void JvmtiThreadState::bind_to(JvmtiThreadState* state, JavaThread* thread) {
@ -158,7 +157,7 @@ inline void JvmtiThreadState::bind_to(JvmtiThreadState* state, JavaThread* threa
// Bind JavaThread to JvmtiThreadState.
thread->set_jvmti_thread_state(state);
if (state != nullptr) {
if (state != nullptr && state->is_virtual()) {
// Bind to JavaThread.
state->set_thread(thread);
}