Skip to content

Commit 60ea17e

Browse files
author
Serguei Spitsyn
committedJun 5, 2024
8311177: Switching to interpreter only mode in carrier thread can lead to crashes
Reviewed-by: pchilanomate, amenkov
1 parent 7564949 commit 60ea17e

File tree

9 files changed

+239
-22
lines changed

9 files changed

+239
-22
lines changed
 

‎src/hotspot/share/prims/jvmtiEventController.cpp

+11-5
Original file line numberDiff line numberDiff line change
@@ -205,11 +205,17 @@ JvmtiEnvEventEnable::~JvmtiEnvEventEnable() {
205205
class EnterInterpOnlyModeClosure : public HandshakeClosure {
206206
private:
207207
bool _completed;
208+
JvmtiThreadState* _state;
209+
208210
public:
209-
EnterInterpOnlyModeClosure() : HandshakeClosure("EnterInterpOnlyMode"), _completed(false) { }
211+
EnterInterpOnlyModeClosure(JvmtiThreadState* state)
212+
: HandshakeClosure("EnterInterpOnlyMode"),
213+
_completed(false),
214+
_state(state) { }
215+
210216
void do_thread(Thread* th) {
211217
JavaThread* jt = JavaThread::cast(th);
212-
JvmtiThreadState* state = jt->jvmti_thread_state();
218+
JvmtiThreadState* state = _state;
213219

214220
assert(state != nullptr, "sanity check");
215221
assert(state->get_thread() == jt, "handshake unsafe conditions");
@@ -369,7 +375,7 @@ void JvmtiEventControllerPrivate::enter_interp_only_mode(JvmtiThreadState *state
369375
if (target == nullptr) { // an unmounted virtual thread
370376
return; // EnterInterpOnlyModeClosure will be executed right after mount.
371377
}
372-
EnterInterpOnlyModeClosure hs;
378+
EnterInterpOnlyModeClosure hs(state);
373379
if (target->is_handshake_safe_for(current)) {
374380
hs.do_thread(target);
375381
} else {
@@ -1101,9 +1107,9 @@ JvmtiEventController::set_extension_event_callback(JvmtiEnvBase *env,
11011107

11021108
// Called by just mounted virtual thread if pending_interp_only_mode() is set.
11031109
void
1104-
JvmtiEventController::enter_interp_only_mode() {
1110+
JvmtiEventController::enter_interp_only_mode(JvmtiThreadState* state) {
11051111
Thread *current = Thread::current();
1106-
EnterInterpOnlyModeClosure hs;
1112+
EnterInterpOnlyModeClosure hs(state);
11071113
hs.do_thread(current);
11081114
}
11091115

‎src/hotspot/share/prims/jvmtiEventController.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ class JvmtiEventController : AllStatic {
228228
jint extension_event_index,
229229
jvmtiExtensionEvent callback);
230230

231-
static void enter_interp_only_mode();
231+
static void enter_interp_only_mode(JvmtiThreadState* state);
232232
static void set_frame_pop(JvmtiEnvThreadState *env_thread, JvmtiFramePop fpop);
233233
static void clear_frame_pop(JvmtiEnvThreadState *env_thread, JvmtiFramePop fpop);
234234

‎src/hotspot/share/prims/jvmtiThreadState.cpp

-8
Original file line numberDiff line numberDiff line change
@@ -639,14 +639,6 @@ JvmtiVTMSTransitionDisabler::VTMS_mount_end(jobject vthread) {
639639

640640
thread->rebind_to_jvmti_thread_state_of(vt);
641641

642-
JvmtiThreadState* state = thread->jvmti_thread_state();
643-
if (state != nullptr && state->is_pending_interp_only_mode()) {
644-
MutexLocker mu(JvmtiThreadState_lock);
645-
state = thread->jvmti_thread_state();
646-
if (state != nullptr && state->is_pending_interp_only_mode()) {
647-
JvmtiEventController::enter_interp_only_mode();
648-
}
649-
}
650642
assert(thread->is_in_VTMS_transition(), "sanity check");
651643
assert(!thread->is_in_tmp_VTMS_transition(), "sanity check");
652644
finish_VTMS_transition(vthread, /* is_mount */ true);

‎src/hotspot/share/prims/jvmtiThreadState.hpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -279,6 +279,7 @@ class JvmtiThreadState : public CHeapObj<mtInternal> {
279279

280280
static void unbind_from(JvmtiThreadState* state, JavaThread* thread);
281281
static void bind_to(JvmtiThreadState* state, JavaThread* thread);
282+
static void process_pending_interp_only(JavaThread* current);
282283

283284
// access to the linked list of all JVMTI thread states
284285
static JvmtiThreadState *first() {

‎src/hotspot/share/prims/jvmtiThreadState.inline.hpp

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2006, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2006, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -163,4 +163,16 @@ inline void JvmtiThreadState::bind_to(JvmtiThreadState* state, JavaThread* threa
163163
state->set_thread(thread);
164164
}
165165
}
166+
167+
inline void JvmtiThreadState::process_pending_interp_only(JavaThread* current) {
168+
JvmtiThreadState* state = current->jvmti_thread_state();
169+
170+
if (state != nullptr && state->is_pending_interp_only_mode()) {
171+
MutexLocker mu(JvmtiThreadState_lock);
172+
state = current->jvmti_thread_state();
173+
if (state != nullptr && state->is_pending_interp_only_mode()) {
174+
JvmtiEventController::enter_interp_only_mode(state);
175+
}
176+
}
177+
}
166178
#endif // SHARE_PRIMS_JVMTITHREADSTATE_INLINE_HPP

‎src/hotspot/share/runtime/javaThread.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -1860,6 +1860,9 @@ JvmtiThreadState* JavaThread::rebind_to_jvmti_thread_state_of(oop thread_oop) {
18601860
// bind new JvmtiThreadState to JavaThread
18611861
JvmtiThreadState::bind_to(java_lang_Thread::jvmti_thread_state(thread_oop), this);
18621862

1863+
// enable interp_only_mode for virtual or carrier thread if it has pending bit
1864+
JvmtiThreadState::process_pending_interp_only(this);
1865+
18631866
return jvmti_thread_state();
18641867
}
18651868
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/**
25+
* @test
26+
* @bug 8311177
27+
* @requires vm.continuations
28+
* @library /test/lib
29+
* @run main/othervm/native -agentlib:CarrierThreadEventNotification CarrierThreadEventNotification
30+
*/
31+
32+
import java.util.concurrent.*;
33+
import java.util.ArrayList;
34+
import java.util.List;
35+
36+
public class CarrierThreadEventNotification {
37+
static final int VTHREAD_COUNT = 64;
38+
static volatile boolean stopRunning = false;
39+
40+
private static native void setSingleSteppingMode(boolean enable);
41+
42+
final Runnable FOO = () -> {
43+
while(!stopRunning) {
44+
recurse(10);
45+
}
46+
};
47+
48+
private void recurse(int depth) {
49+
if (depth > 0) {
50+
recurse(depth -1);
51+
} else {
52+
Thread.yield();
53+
}
54+
}
55+
56+
private void runTest() throws Exception {
57+
List<Thread> virtualThreads = new ArrayList<>();
58+
for (int i = 0; i < VTHREAD_COUNT; i++) {
59+
virtualThreads.add(Thread.ofVirtual().start(FOO));
60+
}
61+
for (int cnt = 0; cnt < 500; cnt++) {
62+
setSingleSteppingMode(true);
63+
Thread.sleep(10);
64+
setSingleSteppingMode(false);
65+
}
66+
stopRunning = true;
67+
for (Thread t : virtualThreads) {
68+
t.join();
69+
}
70+
}
71+
72+
public static void main(String[] args) throws Exception {
73+
CarrierThreadEventNotification obj = new CarrierThreadEventNotification();
74+
obj.runTest();
75+
}
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/*
2+
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
#include <jni.h>
25+
#include <jvmti.h>
26+
#include <stdio.h>
27+
#include <string.h>
28+
#include "jvmti_common.hpp"
29+
30+
31+
extern "C" {
32+
33+
// set by Agent_OnLoad
34+
static jvmtiEnv* jvmti = nullptr;
35+
36+
static jthread* carrier_threads = nullptr;
37+
static jint cthread_cnt = 0;
38+
39+
static const char* CTHREAD_NAME_START = "ForkJoinPool";
40+
static const size_t CTHREAD_NAME_START_LEN = strlen("ForkJoinPool");
41+
42+
static jint
43+
get_cthreads(JNIEnv* jni, jthread** cthreads_p) {
44+
jthread* cthreads = nullptr;
45+
jint all_cnt = 0;
46+
jint ct_cnt = 0;
47+
48+
jvmtiError err = jvmti->GetAllThreads(&all_cnt, &cthreads);
49+
check_jvmti_status(jni, err, "get_cthreads: error in JVMTI GetAllThreads");
50+
51+
for (int idx = 0; idx < all_cnt; idx++) {
52+
jthread thread = cthreads[idx];
53+
char* tname = get_thread_name(jvmti, jni, thread);
54+
55+
if (strncmp(tname, CTHREAD_NAME_START, CTHREAD_NAME_START_LEN) == 0) {
56+
cthreads[ct_cnt++] = jni->NewGlobalRef(thread);
57+
}
58+
deallocate(jvmti, jni, tname);
59+
}
60+
*cthreads_p = cthreads;
61+
return ct_cnt;
62+
}
63+
64+
static void JNICALL
65+
SingleStep(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread,
66+
jmethodID method, jlocation location) {
67+
jboolean is_virtual = jni->IsVirtualThread(thread);
68+
if (is_virtual) {
69+
jni->FatalError("Virtual thread should not have posted single stepping event");
70+
}
71+
}
72+
73+
JNIEXPORT void JNICALL
74+
Java_CarrierThreadEventNotification_setSingleSteppingMode(JNIEnv* jni, jclass klass, jboolean enable) {
75+
if (enable) {
76+
if (cthread_cnt != 0 || carrier_threads != nullptr) {
77+
jni->FatalError("Should not be set");
78+
}
79+
cthread_cnt = get_cthreads(jni, &carrier_threads);
80+
for (int i = 0; i < cthread_cnt; i++) {
81+
jthread thread = carrier_threads[i];
82+
jvmtiError err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_SINGLE_STEP, thread);
83+
check_jvmti_status(jni, err, "event handler: error in JVMTI SetEventNotificationMode for event JVMTI_EVENT_SINGLE_STEP");
84+
}
85+
} else {
86+
if (carrier_threads == nullptr) {
87+
jni->FatalError("Should be set");
88+
}
89+
for (int i = 0; i < cthread_cnt; i++) {
90+
jthread thread = carrier_threads[i];
91+
jvmtiError err = jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_SINGLE_STEP, thread);
92+
check_jvmti_status(jni, err, "event handler: error in JVMTI SetEventNotificationMode for event JVMTI_EVENT_SINGLE_STEP");
93+
jni->DeleteGlobalRef(thread);
94+
}
95+
deallocate(jvmti, jni, carrier_threads);
96+
cthread_cnt = 0;
97+
carrier_threads = nullptr;
98+
}
99+
}
100+
101+
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm, char* options, void* reserved) {
102+
jvmtiEventCallbacks callbacks;
103+
jvmtiCapabilities caps;
104+
jvmtiError err;
105+
106+
printf("Agent_OnLoad: started\n");
107+
if (jvm->GetEnv((void **) (&jvmti), JVMTI_VERSION) != JNI_OK) {
108+
LOG("error in GetEnv");
109+
return JNI_ERR;
110+
}
111+
112+
memset(&caps, 0, sizeof(caps));
113+
caps.can_generate_single_step_events = 1;
114+
caps.can_support_virtual_threads = 1;
115+
116+
err = jvmti->AddCapabilities(&caps);
117+
if (err != JVMTI_ERROR_NONE) {
118+
LOG("error in JVMTI AddCapabilities: %d\n", err);
119+
}
120+
121+
memset(&callbacks, 0, sizeof(callbacks));
122+
callbacks.SingleStep = &SingleStep;
123+
err = jvmti->SetEventCallbacks(&callbacks, sizeof(jvmtiEventCallbacks));
124+
if (err != JVMTI_ERROR_NONE) {
125+
LOG("Agent_OnLoad: Error in JVMTI SetEventCallbacks: %d\n", err);
126+
}
127+
128+
return 0;
129+
}
130+
131+
} // extern "C"

‎test/hotspot/jtreg/serviceability/jvmti/vthread/MethodExitTest/libMethodExitTest.cpp

+2-6
Original file line numberDiff line numberDiff line change
@@ -197,12 +197,8 @@ breakpoint_hit2(jvmtiEnv *jvmti, JNIEnv* jni,
197197
jboolean is_virtual, char* mname) {
198198
jvmtiError err;
199199

200-
// Verify that we did not get a METHOD_EXIT events when enabled on the cthread.
201-
if (received_method_exit_event) {
202-
passed = JNI_FALSE;
203-
received_method_exit_event = JNI_FALSE;
204-
LOG("FAILED: got METHOD_EXIT event on the cthread: %p\n", cthread);
205-
}
200+
// need to reset this value after the breakpoint_hit1
201+
received_method_exit_event = JNI_FALSE;
206202

207203
// Disable METHOD_EXIT events on the cthread.
208204
LOG("Hit #2: Breakpoint: %s: disabling MethodExit events on carrier thread: %p\n",

0 commit comments

Comments
 (0)
Please sign in to comment.