8288387: GetLocalXXX/SetLocalXXX spec should require suspending target thread

Reviewed-by: lmesnik, dsamersoff
This commit is contained in:
Serguei Spitsyn 2022-10-17 23:36:22 +00:00
parent 8d751de319
commit 21a825e059
12 changed files with 508 additions and 745 deletions
src/hotspot/share/prims
test/hotspot/jtreg
serviceability/jvmti
vmTestbase/nsk/jvmti/scenarios/capability/CM01/cm01t011

@ -5831,6 +5831,8 @@ class C2 extends C1 implements I2 {
<description>
This function can be used to retrieve the value of a local
variable whose type is <code>Object</code> or a subclass of <code>Object</code>.
<p/>
The specified thread must be suspended or must be the current thread.
</description>
<origin>jvmdi</origin>
<capabilities>
@ -5873,6 +5875,9 @@ class C2 extends C1 implements I2 {
<error id="JVMTI_ERROR_OPAQUE_FRAME">
Not a visible frame
</error>
<error id="JVMTI_ERROR_THREAD_NOT_SUSPENDED">
Thread was not suspended and was not the current thread.
</error>
</errors>
</function>
@ -5884,6 +5889,8 @@ class C2 extends C1 implements I2 {
frames. This function can retrieve the "<code>this</code>" object from
native method frames, whereas <code>GetLocalObject()</code> would
return <code>JVMTI_ERROR_OPAQUE_FRAME</code> in those cases.
<p/>
The specified thread must be suspended or must be the current thread.
</description>
<origin>new</origin>
<capabilities>
@ -5913,6 +5920,9 @@ class C2 extends C1 implements I2 {
<error id="JVMTI_ERROR_INVALID_SLOT">
If the specified frame is a static method frame.
</error>
<error id="JVMTI_ERROR_THREAD_NOT_SUSPENDED">
Thread was not suspended and was not the current thread.
</error>
</errors>
</function>
<function id="GetLocalInt" num="22">
@ -5922,6 +5932,8 @@ class C2 extends C1 implements I2 {
variable whose type is <code>int</code>,
<code>short</code>, <code>char</code>, <code>byte</code>, or
<code>boolean</code>.
<p/>
The specified thread must be suspended or must be the current thread.
</description>
<origin>jvmdi</origin>
<capabilities>
@ -5966,6 +5978,9 @@ class C2 extends C1 implements I2 {
<error id="JVMTI_ERROR_OPAQUE_FRAME">
Not a visible frame
</error>
<error id="JVMTI_ERROR_THREAD_NOT_SUSPENDED">
Thread was not suspended and was not the current thread.
</error>
</errors>
</function>
@ -5974,6 +5989,8 @@ class C2 extends C1 implements I2 {
<description>
This function can be used to retrieve the value of a local
variable whose type is <code>long</code>.
<p/>
The specified thread must be suspended or must be the current thread.
</description>
<origin>jvmdi</origin>
<capabilities>
@ -6015,6 +6032,9 @@ class C2 extends C1 implements I2 {
<error id="JVMTI_ERROR_OPAQUE_FRAME">
Not a visible frame
</error>
<error id="JVMTI_ERROR_THREAD_NOT_SUSPENDED">
Thread was not suspended and was not the current thread.
</error>
</errors>
</function>
@ -6023,6 +6043,8 @@ class C2 extends C1 implements I2 {
<description>
This function can be used to retrieve the value of a local
variable whose type is <code>float</code>.
<p/>
The specified thread must be suspended or must be the current thread.
</description>
<origin>jvmdi</origin>
<capabilities>
@ -6064,6 +6086,9 @@ class C2 extends C1 implements I2 {
<error id="JVMTI_ERROR_OPAQUE_FRAME">
Not a visible frame
</error>
<error id="JVMTI_ERROR_THREAD_NOT_SUSPENDED">
Thread was not suspended and was not the current thread.
</error>
</errors>
</function>
@ -6072,6 +6097,8 @@ class C2 extends C1 implements I2 {
<description>
This function can be used to retrieve the value of a local
variable whose type is <code>long</code>.
<p/>
The specified thread must be suspended or must be the current thread.
</description>
<origin>jvmdi</origin>
<capabilities>
@ -6113,6 +6140,9 @@ class C2 extends C1 implements I2 {
<error id="JVMTI_ERROR_OPAQUE_FRAME">
Not a visible frame
</error>
<error id="JVMTI_ERROR_THREAD_NOT_SUSPENDED">
Thread was not suspended and was not the current thread.
</error>
</errors>
</function>
@ -6121,6 +6151,8 @@ class C2 extends C1 implements I2 {
<description>
This function can be used to set the value of a local
variable whose type is <code>Object</code> or a subclass of <code>Object</code>.
<p/>
The specified thread must be suspended or must be the current thread.
</description>
<origin>jvmdi</origin>
<capabilities>
@ -6172,6 +6204,9 @@ class C2 extends C1 implements I2 {
setting the value of locals in the frame of the given depth.
See <internallink id="local">Local Variables</internallink>.
</error>
<error id="JVMTI_ERROR_THREAD_NOT_SUSPENDED">
Thread was not suspended and was not the current thread.
</error>
</errors>
</function>
@ -6182,6 +6217,8 @@ class C2 extends C1 implements I2 {
variable whose type is <code>int</code>,
<code>short</code>, <code>char</code>, <code>byte</code>, or
<code>boolean</code>.
<p/>
The specified thread must be suspended or must be the current thread.
</description>
<origin>jvmdi</origin>
<capabilities>
@ -6231,6 +6268,9 @@ class C2 extends C1 implements I2 {
setting the value of locals in the frame of the given depth.
See <internallink id="local">Local Variables</internallink>.
</error>
<error id="JVMTI_ERROR_THREAD_NOT_SUSPENDED">
Thread was not suspended and was not the current thread.
</error>
</errors>
</function>
@ -6239,6 +6279,8 @@ class C2 extends C1 implements I2 {
<description>
This function can be used to set the value of a local
variable whose type is <code>long</code>.
<p/>
The specified thread must be suspended or must be the current thread.
</description>
<origin>jvmdi</origin>
<capabilities>
@ -6285,6 +6327,9 @@ class C2 extends C1 implements I2 {
setting the value of locals in the frame of the given depth.
See <internallink id="local">Local Variables</internallink>.
</error>
<error id="JVMTI_ERROR_THREAD_NOT_SUSPENDED">
Thread was not suspended and was not the current thread.
</error>
</errors>
</function>
@ -6293,6 +6338,8 @@ class C2 extends C1 implements I2 {
<description>
This function can be used to set the value of a local
variable whose type is <code>float</code>.
<p/>
The specified thread must be suspended or must be the current thread.
</description>
<origin>jvmdi</origin>
<capabilities>
@ -6339,6 +6386,9 @@ class C2 extends C1 implements I2 {
setting the value of locals in the frame of the given depth.
See <internallink id="local">Local Variables</internallink>.
</error>
<error id="JVMTI_ERROR_THREAD_NOT_SUSPENDED">
Thread was not suspended and was not the current thread.
</error>
</errors>
</function>
@ -6347,6 +6397,8 @@ class C2 extends C1 implements I2 {
<description>
This function can be used to set the value of a local
variable whose type is <code>double</code>.
<p/>
The specified thread must be suspended or must be the current thread.
</description>
<origin>jvmdi</origin>
<capabilities>
@ -6393,6 +6445,9 @@ class C2 extends C1 implements I2 {
setting the value of locals in the frame of the given depth.
See <internallink id="local">Local Variables</internallink>.
</error>
<error id="JVMTI_ERROR_THREAD_NOT_SUSPENDED">
Thread was not suspended and was not the current thread.
</error>
</errors>
</function>
</category>

@ -2246,12 +2246,19 @@ JvmtiEnv::GetLocalObject(jthread thread, jint depth, jint slot, jobject* value_p
ResourceMark rm(current_thread);
HandleMark hm(current_thread);
JvmtiVTMSTransitionDisabler disabler;
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
oop thread_obj = NULL;
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_obj);
if (err != JVMTI_ERROR_NONE) {
return err;
}
bool self = is_JavaThread_current(java_thread, thread_obj);
jvmtiError err = JVMTI_ERROR_NONE;
oop thread_obj = current_thread_obj_or_resolve_external_guard(thread);
if (java_lang_VirtualThread::is_instance(thread_obj)) {
VM_VirtualThreadGetOrSetLocal op(this, Handle(current_thread, thread_obj),
current_thread, depth, slot);
current_thread, depth, slot, self);
VMThread::execute(&op);
err = op.result();
if (err == JVMTI_ERROR_NONE) {
@ -2259,13 +2266,7 @@ JvmtiEnv::GetLocalObject(jthread thread, jint depth, jint slot, jobject* value_p
}
} else {
// Support for ordinary threads
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
err = get_JavaThread(tlh.list(), thread, &java_thread);
if (err != JVMTI_ERROR_NONE) {
return err;
}
VM_GetOrSetLocal op(java_thread, current_thread, depth, slot);
VM_GetOrSetLocal op(java_thread, current_thread, depth, slot, self);
VMThread::execute(&op);
err = op.result();
if (err == JVMTI_ERROR_NONE) {
@ -2286,12 +2287,19 @@ JvmtiEnv::GetLocalInstance(jthread thread, jint depth, jobject* value_ptr){
ResourceMark rm(current_thread);
HandleMark hm(current_thread);
JvmtiVTMSTransitionDisabler disabler;
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
oop thread_obj = NULL;
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_obj);
if (err != JVMTI_ERROR_NONE) {
return err;
}
bool self = is_JavaThread_current(java_thread, thread_obj);
jvmtiError err = JVMTI_ERROR_NONE;
oop thread_obj = current_thread_obj_or_resolve_external_guard(thread);
if (java_lang_VirtualThread::is_instance(thread_obj)) {
VM_VirtualThreadGetReceiver op(this, Handle(current_thread, thread_obj),
current_thread, depth);
current_thread, depth, self);
VMThread::execute(&op);
err = op.result();
if (err == JVMTI_ERROR_NONE) {
@ -2299,13 +2307,7 @@ JvmtiEnv::GetLocalInstance(jthread thread, jint depth, jobject* value_ptr){
}
} else {
// Support for ordinary threads
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
err = get_JavaThread(tlh.list(), thread, &java_thread);
if (err != JVMTI_ERROR_NONE) {
return err;
}
VM_GetReceiver op(java_thread, current_thread, depth);
VM_GetReceiver op(java_thread, current_thread, depth, self);
VMThread::execute(&op);
err = op.result();
if (err == JVMTI_ERROR_NONE) {
@ -2327,12 +2329,19 @@ JvmtiEnv::GetLocalInt(jthread thread, jint depth, jint slot, jint* value_ptr) {
ResourceMark rm(current_thread);
HandleMark hm(current_thread);
JvmtiVTMSTransitionDisabler disabler;
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
oop thread_obj = NULL;
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_obj);
if (err != JVMTI_ERROR_NONE) {
return err;
}
bool self = is_JavaThread_current(java_thread, thread_obj);
jvmtiError err = JVMTI_ERROR_NONE;
oop thread_obj = current_thread_obj_or_resolve_external_guard(thread);
if (java_lang_VirtualThread::is_instance(thread_obj)) {
VM_VirtualThreadGetOrSetLocal op(this, Handle(current_thread, thread_obj),
depth, slot, T_INT);
depth, slot, T_INT, self);
VMThread::execute(&op);
err = op.result();
if (err == JVMTI_ERROR_NONE) {
@ -2340,13 +2349,7 @@ JvmtiEnv::GetLocalInt(jthread thread, jint depth, jint slot, jint* value_ptr) {
}
} else {
// Support for ordinary threads
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
err = get_JavaThread(tlh.list(), thread, &java_thread);
if (err != JVMTI_ERROR_NONE) {
return err;
}
VM_GetOrSetLocal op(java_thread, depth, slot, T_INT);
VM_GetOrSetLocal op(java_thread, depth, slot, T_INT, self);
VMThread::execute(&op);
err = op.result();
if (err == JVMTI_ERROR_NONE) {
@ -2368,12 +2371,19 @@ JvmtiEnv::GetLocalLong(jthread thread, jint depth, jint slot, jlong* value_ptr)
ResourceMark rm(current_thread);
HandleMark hm(current_thread);
JvmtiVTMSTransitionDisabler disabler;
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
oop thread_obj = NULL;
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_obj);
if (err != JVMTI_ERROR_NONE) {
return err;
}
bool self = is_JavaThread_current(java_thread, thread_obj);
jvmtiError err = JVMTI_ERROR_NONE;
oop thread_obj = current_thread_obj_or_resolve_external_guard(thread);
if (java_lang_VirtualThread::is_instance(thread_obj)) {
VM_VirtualThreadGetOrSetLocal op(this, Handle(current_thread, thread_obj),
depth, slot, T_LONG);
depth, slot, T_LONG, self);
VMThread::execute(&op);
err = op.result();
if (err == JVMTI_ERROR_NONE) {
@ -2381,13 +2391,7 @@ JvmtiEnv::GetLocalLong(jthread thread, jint depth, jint slot, jlong* value_ptr)
}
} else {
// Support for ordinary threads
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
err = get_JavaThread(tlh.list(), thread, &java_thread);
if (err != JVMTI_ERROR_NONE) {
return err;
}
VM_GetOrSetLocal op(java_thread, depth, slot, T_LONG);
VM_GetOrSetLocal op(java_thread, depth, slot, T_LONG, self);
VMThread::execute(&op);
err = op.result();
if (err == JVMTI_ERROR_NONE) {
@ -2409,12 +2413,19 @@ JvmtiEnv::GetLocalFloat(jthread thread, jint depth, jint slot, jfloat* value_ptr
ResourceMark rm(current_thread);
HandleMark hm(current_thread);
JvmtiVTMSTransitionDisabler disabler;
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
oop thread_obj = NULL;
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_obj);
if (err != JVMTI_ERROR_NONE) {
return err;
}
bool self = is_JavaThread_current(java_thread, thread_obj);
jvmtiError err = JVMTI_ERROR_NONE;
oop thread_obj = current_thread_obj_or_resolve_external_guard(thread);
if (java_lang_VirtualThread::is_instance(thread_obj)) {
VM_VirtualThreadGetOrSetLocal op(this, Handle(current_thread, thread_obj),
depth, slot, T_FLOAT);
depth, slot, T_FLOAT, self);
VMThread::execute(&op);
err = op.result();
if (err == JVMTI_ERROR_NONE) {
@ -2422,13 +2433,7 @@ JvmtiEnv::GetLocalFloat(jthread thread, jint depth, jint slot, jfloat* value_ptr
}
} else {
// Support for ordinary threads
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
err = get_JavaThread(tlh.list(), thread, &java_thread);
if (err != JVMTI_ERROR_NONE) {
return err;
}
VM_GetOrSetLocal op(java_thread, depth, slot, T_FLOAT);
VM_GetOrSetLocal op(java_thread, depth, slot, T_FLOAT, self);
VMThread::execute(&op);
err = op.result();
if (err == JVMTI_ERROR_NONE) {
@ -2450,12 +2455,19 @@ JvmtiEnv::GetLocalDouble(jthread thread, jint depth, jint slot, jdouble* value_p
ResourceMark rm(current_thread);
HandleMark hm(current_thread);
JvmtiVTMSTransitionDisabler disabler;
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
oop thread_obj = NULL;
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_obj);
if (err != JVMTI_ERROR_NONE) {
return err;
}
bool self = is_JavaThread_current(java_thread, thread_obj);
jvmtiError err = JVMTI_ERROR_NONE;
oop thread_obj = current_thread_obj_or_resolve_external_guard(thread);
if (java_lang_VirtualThread::is_instance(thread_obj)) {
VM_VirtualThreadGetOrSetLocal op(this, Handle(current_thread, thread_obj),
depth, slot, T_DOUBLE);
depth, slot, T_DOUBLE, self);
VMThread::execute(&op);
err = op.result();
if (err == JVMTI_ERROR_NONE) {
@ -2463,13 +2475,7 @@ JvmtiEnv::GetLocalDouble(jthread thread, jint depth, jint slot, jdouble* value_p
}
} else {
// Support for ordinary threads
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
err = get_JavaThread(tlh.list(), thread, &java_thread);
if (err != JVMTI_ERROR_NONE) {
return err;
}
VM_GetOrSetLocal op(java_thread, depth, slot, T_DOUBLE);
VM_GetOrSetLocal op(java_thread, depth, slot, T_DOUBLE, self);
VMThread::execute(&op);
err = op.result();
if (err == JVMTI_ERROR_NONE) {
@ -2490,25 +2496,26 @@ JvmtiEnv::SetLocalObject(jthread thread, jint depth, jint slot, jobject value) {
ResourceMark rm(current_thread);
HandleMark hm(current_thread);
JvmtiVTMSTransitionDisabler disabler;
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
oop thread_obj = NULL;
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_obj);
if (err != JVMTI_ERROR_NONE) {
return err;
}
bool self = is_JavaThread_current(java_thread, thread_obj);
jvalue val;
val.l = value;
jvmtiError err = JVMTI_ERROR_NONE;
oop thread_obj = current_thread_obj_or_resolve_external_guard(thread);
if (java_lang_VirtualThread::is_instance(thread_obj)) {
VM_VirtualThreadGetOrSetLocal op(this, Handle(current_thread, thread_obj),
depth, slot, T_OBJECT, val);
depth, slot, T_OBJECT, val, self);
VMThread::execute(&op);
err = op.result();
} else {
// Support for ordinary threads
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
err = get_JavaThread(tlh.list(), thread, &java_thread);
if (err != JVMTI_ERROR_NONE) {
return err;
}
VM_GetOrSetLocal op(java_thread, depth, slot, T_OBJECT, val);
VM_GetOrSetLocal op(java_thread, depth, slot, T_OBJECT, val, self);
VMThread::execute(&op);
err = op.result();
}
@ -2526,25 +2533,26 @@ JvmtiEnv::SetLocalInt(jthread thread, jint depth, jint slot, jint value) {
ResourceMark rm(current_thread);
HandleMark hm(current_thread);
JvmtiVTMSTransitionDisabler disabler;
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
oop thread_obj = NULL;
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_obj);
if (err != JVMTI_ERROR_NONE) {
return err;
}
bool self = is_JavaThread_current(java_thread, thread_obj);
jvalue val;
val.i = value;
jvmtiError err = JVMTI_ERROR_NONE;
oop thread_obj = current_thread_obj_or_resolve_external_guard(thread);
if (java_lang_VirtualThread::is_instance(thread_obj)) {
VM_VirtualThreadGetOrSetLocal op(this, Handle(current_thread, thread_obj),
depth, slot, T_INT, val);
depth, slot, T_INT, val, self);
VMThread::execute(&op);
err = op.result();
} else {
// Support for ordinary threads
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
err = get_JavaThread(tlh.list(), thread, &java_thread);
if (err != JVMTI_ERROR_NONE) {
return err;
}
VM_GetOrSetLocal op(java_thread, depth, slot, T_INT, val);
VM_GetOrSetLocal op(java_thread, depth, slot, T_INT, val, self);
VMThread::execute(&op);
err = op.result();
}
@ -2562,25 +2570,26 @@ JvmtiEnv::SetLocalLong(jthread thread, jint depth, jint slot, jlong value) {
ResourceMark rm(current_thread);
HandleMark hm(current_thread);
JvmtiVTMSTransitionDisabler disabler;
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
oop thread_obj = NULL;
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_obj);
if (err != JVMTI_ERROR_NONE) {
return err;
}
bool self = is_JavaThread_current(java_thread, thread_obj);
jvalue val;
val.j = value;
jvmtiError err = JVMTI_ERROR_NONE;
oop thread_obj = current_thread_obj_or_resolve_external_guard(thread);
if (java_lang_VirtualThread::is_instance(thread_obj)) {
VM_VirtualThreadGetOrSetLocal op(this, Handle(current_thread, thread_obj),
depth, slot, T_LONG, val);
depth, slot, T_LONG, val, self);
VMThread::execute(&op);
err = op.result();
} else {
// Support for ordinary threads
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
err = get_JavaThread(tlh.list(), thread, &java_thread);
if (err != JVMTI_ERROR_NONE) {
return err;
}
VM_GetOrSetLocal op(java_thread, depth, slot, T_LONG, val);
VM_GetOrSetLocal op(java_thread, depth, slot, T_LONG, val, self);
VMThread::execute(&op);
err = op.result();
}
@ -2598,25 +2607,26 @@ JvmtiEnv::SetLocalFloat(jthread thread, jint depth, jint slot, jfloat value) {
ResourceMark rm(current_thread);
HandleMark hm(current_thread);
JvmtiVTMSTransitionDisabler disabler;
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
oop thread_obj = NULL;
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_obj);
if (err != JVMTI_ERROR_NONE) {
return err;
}
bool self = is_JavaThread_current(java_thread, thread_obj);
jvalue val;
val.f = value;
jvmtiError err = JVMTI_ERROR_NONE;
oop thread_obj = current_thread_obj_or_resolve_external_guard(thread);
if (java_lang_VirtualThread::is_instance(thread_obj)) {
VM_VirtualThreadGetOrSetLocal op(this, Handle(current_thread, thread_obj),
depth, slot, T_FLOAT, val);
depth, slot, T_FLOAT, val, self);
VMThread::execute(&op);
err = op.result();
} else {
// Support for ordinary threads
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
err = get_JavaThread(tlh.list(), thread, &java_thread);
if (err != JVMTI_ERROR_NONE) {
return err;
}
VM_GetOrSetLocal op(java_thread, depth, slot, T_FLOAT, val);
VM_GetOrSetLocal op(java_thread, depth, slot, T_FLOAT, val, self);
VMThread::execute(&op);
err = op.result();
}
@ -2634,25 +2644,26 @@ JvmtiEnv::SetLocalDouble(jthread thread, jint depth, jint slot, jdouble value) {
ResourceMark rm(current_thread);
HandleMark hm(current_thread);
JvmtiVTMSTransitionDisabler disabler;
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
oop thread_obj = NULL;
jvmtiError err = get_threadOop_and_JavaThread(tlh.list(), thread, &java_thread, &thread_obj);
if (err != JVMTI_ERROR_NONE) {
return err;
}
bool self = is_JavaThread_current(java_thread, thread_obj);
jvalue val;
val.d = value;
jvmtiError err = JVMTI_ERROR_NONE;
oop thread_obj = current_thread_obj_or_resolve_external_guard(thread);
if (java_lang_VirtualThread::is_instance(thread_obj)) {
VM_VirtualThreadGetOrSetLocal op(this, Handle(current_thread, thread_obj),
depth, slot, T_DOUBLE, val);
depth, slot, T_DOUBLE, val, self);
VMThread::execute(&op);
err = op.result();
} else {
// Support for ordinary threads
ThreadsListHandle tlh(current_thread);
JavaThread* java_thread = NULL;
err = get_JavaThread(tlh.list(), thread, &java_thread);
if (err != JVMTI_ERROR_NONE) {
return err;
}
VM_GetOrSetLocal op(java_thread, depth, slot, T_DOUBLE, val);
VM_GetOrSetLocal op(java_thread, depth, slot, T_DOUBLE, val, self);
VMThread::execute(&op);
err = op.result();
}

@ -171,6 +171,21 @@ class JvmtiEnvBase : public CHeapObj<mtInternal> {
// Otherwise return JNIHandles::resolve_external_guard(thread).
static oop current_thread_obj_or_resolve_external_guard(jthread thread);
// Return true if the thread identified with a pair <jt,thr_obj> is current.
// A passive carrier thread is not treated as current.
static bool is_JavaThread_current(JavaThread* jt, oop thr_obj) {
JavaThread* current = JavaThread::current();
// jt can be NULL in case of a virtual thread
if (jt == NULL || jt != current) {
return false;
}
oop cur_obj = current->jvmti_vthread();
// cur_obj == NULL is true for normal platform threads only
// otherwise it can be virtual or carrier thread.
return cur_obj == NULL || cur_obj == thr_obj;
}
static jvmtiError get_JavaThread(ThreadsList* tlist, jthread thread, JavaThread** jt_pp) {
jvmtiError err = JVMTI_ERROR_NONE;
if (thread == NULL) {

@ -432,7 +432,7 @@ const jvalue VM_BaseGetOrSetLocal::_DEFAULT_VALUE = {0L};
// Constructor for non-object getter
VM_BaseGetOrSetLocal::VM_BaseGetOrSetLocal(JavaThread* calling_thread, jint depth,
jint index, BasicType type, jvalue value, bool set)
jint index, BasicType type, jvalue value, bool set, bool self)
: _calling_thread(calling_thread)
, _depth(depth)
, _index(index)
@ -440,6 +440,7 @@ VM_BaseGetOrSetLocal::VM_BaseGetOrSetLocal(JavaThread* calling_thread, jint dept
, _value(value)
, _jvf(NULL)
, _set(set)
, _self(self)
, _result(JVMTI_ERROR_NONE)
{
}
@ -743,24 +744,24 @@ bool VM_BaseGetOrSetLocal::allow_nested_vm_operations() const {
//
// Constructor for non-object getter
VM_GetOrSetLocal::VM_GetOrSetLocal(JavaThread* thread, jint depth, jint index, BasicType type)
: VM_BaseGetOrSetLocal((JavaThread*)NULL, depth, index, type, _DEFAULT_VALUE, false),
VM_GetOrSetLocal::VM_GetOrSetLocal(JavaThread* thread, jint depth, jint index, BasicType type, bool self)
: VM_BaseGetOrSetLocal((JavaThread*)NULL, depth, index, type, _DEFAULT_VALUE, false, self),
_thread(thread),
_eb(false, NULL, NULL)
{
}
// Constructor for object or non-object setter
VM_GetOrSetLocal::VM_GetOrSetLocal(JavaThread* thread, jint depth, jint index, BasicType type, jvalue value)
: VM_BaseGetOrSetLocal((JavaThread*)NULL, depth, index, type, value, true),
VM_GetOrSetLocal::VM_GetOrSetLocal(JavaThread* thread, jint depth, jint index, BasicType type, jvalue value, bool self)
: VM_BaseGetOrSetLocal((JavaThread*)NULL, depth, index, type, value, true, self),
_thread(thread),
_eb(type == T_OBJECT, JavaThread::current(), thread)
{
}
// Constructor for object getter
VM_GetOrSetLocal::VM_GetOrSetLocal(JavaThread* thread, JavaThread* calling_thread, jint depth, int index)
: VM_BaseGetOrSetLocal(calling_thread, depth, index, T_OBJECT, _DEFAULT_VALUE, false),
VM_GetOrSetLocal::VM_GetOrSetLocal(JavaThread* thread, JavaThread* calling_thread, jint depth, int index, bool self)
: VM_BaseGetOrSetLocal(calling_thread, depth, index, T_OBJECT, _DEFAULT_VALUE, false, self),
_thread(thread),
_eb(true, calling_thread, thread)
{
@ -785,6 +786,10 @@ vframe *VM_GetOrSetLocal::get_vframe() {
javaVFrame *VM_GetOrSetLocal::get_java_vframe() {
vframe* vf = get_vframe();
if (!(_self || _thread->is_carrier_thread_suspended())) {
_result = JVMTI_ERROR_THREAD_NOT_SUSPENDED;
return NULL;
}
if (vf == NULL) {
_result = JVMTI_ERROR_NO_MORE_FRAMES;
return NULL;
@ -799,8 +804,8 @@ javaVFrame *VM_GetOrSetLocal::get_java_vframe() {
}
VM_GetReceiver::VM_GetReceiver(
JavaThread* thread, JavaThread* caller_thread, jint depth)
: VM_GetOrSetLocal(thread, caller_thread, depth, 0) {}
JavaThread* thread, JavaThread* caller_thread, jint depth, bool self)
: VM_GetOrSetLocal(thread, caller_thread, depth, 0, self) {}
///////////////////////////////////////////////////////////////
@ -810,8 +815,8 @@ VM_GetReceiver::VM_GetReceiver(
// Constructor for non-object getter
VM_VirtualThreadGetOrSetLocal::VM_VirtualThreadGetOrSetLocal(JvmtiEnv* env, Handle vthread_h, jint depth,
jint index, BasicType type)
: VM_BaseGetOrSetLocal((JavaThread*)NULL, depth, index, type, _DEFAULT_VALUE, false)
jint index, BasicType type, bool self)
: VM_BaseGetOrSetLocal((JavaThread*)NULL, depth, index, type, _DEFAULT_VALUE, false, self)
{
_env = env;
_vthread_h = vthread_h;
@ -819,8 +824,8 @@ VM_VirtualThreadGetOrSetLocal::VM_VirtualThreadGetOrSetLocal(JvmtiEnv* env, Hand
// Constructor for object or non-object setter
VM_VirtualThreadGetOrSetLocal::VM_VirtualThreadGetOrSetLocal(JvmtiEnv* env, Handle vthread_h, jint depth,
jint index, BasicType type, jvalue value)
: VM_BaseGetOrSetLocal((JavaThread*)NULL, depth, index, type, value, true)
jint index, BasicType type, jvalue value, bool self)
: VM_BaseGetOrSetLocal((JavaThread*)NULL, depth, index, type, value, true, self)
{
_env = env;
_vthread_h = vthread_h;
@ -828,8 +833,8 @@ VM_VirtualThreadGetOrSetLocal::VM_VirtualThreadGetOrSetLocal(JvmtiEnv* env, Hand
// Constructor for object getter
VM_VirtualThreadGetOrSetLocal::VM_VirtualThreadGetOrSetLocal(JvmtiEnv* env, Handle vthread_h, JavaThread* calling_thread,
jint depth, int index)
: VM_BaseGetOrSetLocal(calling_thread, depth, index, T_OBJECT, _DEFAULT_VALUE, false)
jint depth, int index, bool self)
: VM_BaseGetOrSetLocal(calling_thread, depth, index, T_OBJECT, _DEFAULT_VALUE, false, self)
{
_env = env;
_vthread_h = vthread_h;
@ -844,6 +849,11 @@ javaVFrame *VM_VirtualThreadGetOrSetLocal::get_java_vframe() {
JavaThread* java_thread = JvmtiEnvBase::get_JavaThread_or_null(_vthread_h());
bool is_cont_mounted = (java_thread != NULL);
if (!(_self || JvmtiVTSuspender::is_vthread_suspended(_vthread_h()))) {
_result = JVMTI_ERROR_THREAD_NOT_SUSPENDED;
return NULL;
}
if (is_cont_mounted) {
vframeStream vfs(java_thread);
@ -878,8 +888,8 @@ javaVFrame *VM_VirtualThreadGetOrSetLocal::get_java_vframe() {
}
VM_VirtualThreadGetReceiver::VM_VirtualThreadGetReceiver(
JvmtiEnv* env, Handle vthread_h, JavaThread* caller_thread, jint depth)
: VM_VirtualThreadGetOrSetLocal(env, vthread_h, caller_thread, depth, 0) {}
JvmtiEnv* env, Handle vthread_h, JavaThread* caller_thread, jint depth, bool self)
: VM_VirtualThreadGetOrSetLocal(env, vthread_h, caller_thread, depth, 0, self) {}
/////////////////////////////////////////////////////////////////////////////////////////

@ -319,6 +319,7 @@ class VM_BaseGetOrSetLocal : public VM_Operation {
jvalue _value;
javaVFrame* _jvf;
bool _set;
bool _self;
static const jvalue _DEFAULT_VALUE;
@ -334,7 +335,7 @@ class VM_BaseGetOrSetLocal : public VM_Operation {
public:
VM_BaseGetOrSetLocal(JavaThread* calling_thread, jint depth, jint index,
BasicType type, jvalue value, bool set);
BasicType type, jvalue value, bool set, bool self);
jvalue value() { return _value; }
jvmtiError result() { return _result; }
@ -358,14 +359,13 @@ class VM_GetOrSetLocal : public VM_BaseGetOrSetLocal {
public:
// Constructor for non-object getter
VM_GetOrSetLocal(JavaThread* thread, jint depth, jint index, BasicType type);
VM_GetOrSetLocal(JavaThread* thread, jint depth, jint index, BasicType type, bool self);
// Constructor for object or non-object setter
VM_GetOrSetLocal(JavaThread* thread, jint depth, jint index, BasicType type, jvalue value);
VM_GetOrSetLocal(JavaThread* thread, jint depth, jint index, BasicType type, jvalue value, bool self);
// Constructor for object getter
VM_GetOrSetLocal(JavaThread* thread, JavaThread* calling_thread, jint depth,
int index);
VM_GetOrSetLocal(JavaThread* thread, JavaThread* calling_thread, jint depth, int index, bool self);
VMOp_Type type() const { return VMOp_GetOrSetLocal; }
@ -379,7 +379,7 @@ class VM_GetReceiver : public VM_GetOrSetLocal {
virtual bool getting_receiver() const { return true; }
public:
VM_GetReceiver(JavaThread* thread, JavaThread* calling_thread, jint depth);
VM_GetReceiver(JavaThread* thread, JavaThread* calling_thread, jint depth, bool self);
const char* name() const { return "get receiver"; }
};
@ -393,15 +393,15 @@ class VM_VirtualThreadGetOrSetLocal : public VM_BaseGetOrSetLocal {
public:
// Constructor for non-object getter.
VM_VirtualThreadGetOrSetLocal(JvmtiEnv* env, Handle vthread_h, jint depth, jint index, BasicType type);
VM_VirtualThreadGetOrSetLocal(JvmtiEnv* env, Handle vthread_h, jint depth, jint index, BasicType type, bool self);
// Constructor for object or non-object setter.
VM_VirtualThreadGetOrSetLocal(JvmtiEnv* env, Handle vthread_h, jint depth,
jint index, BasicType type, jvalue value);
jint index, BasicType type, jvalue value, bool self);
// Constructor for object getter.
VM_VirtualThreadGetOrSetLocal(JvmtiEnv* env, Handle vthread_h, JavaThread* calling_thread,
jint depth, int index);
jint depth, int index, bool self);
VMOp_Type type() const { return VMOp_VirtualThreadGetOrSetLocal; }
@ -413,7 +413,7 @@ class VM_VirtualThreadGetReceiver : public VM_VirtualThreadGetOrSetLocal {
virtual bool getting_receiver() const { return true; }
public:
VM_VirtualThreadGetReceiver(JvmtiEnv* env, Handle vthread_h, JavaThread* calling_thread, jint depth);
VM_VirtualThreadGetReceiver(JvmtiEnv* env, Handle vthread_h, JavaThread* calling_thread, jint depth, bool self);
const char* name() const { return "virtual thread get receiver"; }
};

@ -1,149 +0,0 @@
/*
* Copyright (c) 2020 SAP SE. 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.
*/
/**
* @test
* @bug 8249293
*
* @summary Test if stack walk to get local variable in the JVMTI implementation is safe if the
* target thread is not suspended.
*
* @comment The main/target thread uses recursion to build a large stack, then
* calls a native method to notify the JVMTI agent thread to get a
* local variable deep in the stack. This prolongs the stack walk. The
* target thread's stack is walkable while in native. After sending the
* notification it waits a while to give the agent time to reach the
* stack walk, then it returns from native. This is when its stack
* becomes not walkable again.
*
* @library /test/lib
* @compile GetLocalWithoutSuspendTest.java
* @run main/othervm/native
* -agentlib:GetLocalWithoutSuspendTest
* -Xbatch
* GetLocalWithoutSuspendTest
*/
public class GetLocalWithoutSuspendTest {
public static final int M = 1 << 20;
public static final int TEST_ITERATIONS = 200;
/**
* Native method to notify the agent thread to call GetLocalObject() on this thread.
*
* @param depth Depth of target frame for GetLocalObject() call. Should be
* large value to prolong the unsafe stack walk.
* @param waitTime Time to wait after notify with
* walkable stack before returning an becoming unsafe again.
* @return Dummy value.
*/
public static native void notifyAgentToGetLocal(int depth, int waitTime);
/**
* Notify agent thread that we are shutting down and wait for it to terminate.
*/
public static native void shutDown();
/**
* Provide agent thread with reference to target thread.
* @param target The target thread
*/
public static native void setTargetThread(Thread target);
public static void main(String[] args) throws Exception {
new GetLocalWithoutSuspendTest().runTest();
}
/**
* Wait cycles in native, i.e. with walkable stack, after notifying agent
* thread to do GetLocalObject() call.
*/
public int waitCycles = 1;
public void runTest() throws Exception {
log("Set target thread for get local variable calls by agent.");
setTargetThread(Thread.currentThread());
log("Test how many frames fit on the stack by performing recursive calls until");
log("StackOverflowError is thrown");
int targetDepth = recursiveMethod(0, M);
log("Testing with target depth: " + targetDepth);
log("Begin Test.");
long start = System.currentTimeMillis();
for (int iterations = 0; iterations < TEST_ITERATIONS; iterations++) {
long now = System.currentTimeMillis();
log((now - start) + " ms Iteration : " + iterations +
" waitTime : " + waitCycles);
int newTargetDepth = recursiveMethod(0, targetDepth);
if (newTargetDepth < targetDepth) {
// A StackOverflowError can occur due to (re-)compilation. We
// don't reach the native method notifyAgentToGetLocal() then
// which is a prerequisite to trigger the problematic race
// condition. So we reduce the targetDepth to avoid stack
// overflow.
log("StackOverflowError during test.");
log("Old target depth: " + targetDepth);
log("Retry with new target depth: " + newTargetDepth);
targetDepth = newTargetDepth;
}
iterations++;
// Double wait time, but limit to roughly 10^6 cycles.
waitCycles = (waitCycles << 1) & (M - 1);
waitCycles = waitCycles == 0 ? 1 : waitCycles;
}
// Notify agent thread that we are shutting down and wait for it to terminate.
shutDown();
log("Successfully finished test");
}
/**
* Perform recursive calls until the target stack depth is reached or the stack overflows.
* Call {@link #notifyAgentToGetLocal(int, int)} if the target depth is reached.
*
* @param depth Current recursion depth
* @param targetStackDepth Target recursion depth
* @return Depth at which the recursion was ended
*/
public int recursiveMethod(int depth, int targetStackDepth) {
int maxDepth = depth;
try {
if (depth == targetStackDepth) {
notifyAgentToGetLocal(depth - 100, waitCycles);
} else {
maxDepth = recursiveMethod(depth + 1, targetStackDepth);
}
} catch (StackOverflowError e) {
// Don't print message here, because this would likely trigger a new StackOverflowError
}
return maxDepth;
}
public static void log(String m) {
System.out.println("### Java-Test: " + m);
}
}

@ -0,0 +1,89 @@
/*
* Copyright (c) 2022, 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.
*/
/**
* @test
* @summary Verifies JVMTI GetLocalXXX/SetLocalXXX return errors for unsuspended vthreads
* @requires vm.continuations
* @enablePreview
* @library /test/lib
* @run main/othervm/native -agentlib:GetSetLocalUnsuspended GetSetLocalUnsuspended
*/
public class GetSetLocalUnsuspended {
private static final String agentLib = "GetSetLocalUnsuspended";
private static native void testUnsuspendedThread(Thread thread);
private static volatile boolean doStop;
private static void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
throw new RuntimeException("Interruption in Thread.sleep: \n\t" + e);
}
}
static final Runnable SLEEPING_THREAD = () -> {
while (!doStop) {
sleep(1);
}
};
private static void testPlatformThread() throws Exception {
doStop = false;
Thread thread = Thread.ofPlatform().name("SleepingPlatformThread").start(SLEEPING_THREAD);
testUnsuspendedThread(thread);
doStop = true;
thread.join();
}
private static void testVirtualThread() throws Exception {
doStop = false;
Thread thread = Thread.ofVirtual().name("SleepingVirtualThread").start(SLEEPING_THREAD);
testUnsuspendedThread(thread);
doStop = true;
thread.join();
}
private void runTest() throws Exception {
testPlatformThread();
testVirtualThread();
}
public static void main(String[] args) throws Exception {
try {
System.loadLibrary(agentLib);
} catch (UnsatisfiedLinkError ex) {
System.err.println("Failed to load " + agentLib + " lib");
System.err.println("java.library.path: " + System.getProperty("java.library.path"));
throw ex;
}
GetSetLocalUnsuspended obj = new GetSetLocalUnsuspended();
obj.runTest();
}
}

@ -1,449 +0,0 @@
/*
* Copyright (c) 2020 SAP SE. All rights reserved.
* Copyright (c) 2022, 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 <stdio.h>
#include <string.h>
#include "jvmti.h"
#include "jni.h"
//
// Please also read the @comment in GetLocalWithoutSuspendTest.java
//
extern "C" {
////////////////////////////////////////////////////
// BEGIN: Shared Variables
// The following variables are shared between agent and target thread
// glws_monitor is used to synchronize access to shared variables.
static jrawMonitorID glws_monitor;
// Target thread for agent operations.
static jthread target_thread = NULL;
// Depth of the frame for GetLocalObject() call by the agent thread. It is set by the target thread.
// -1 is the signal to shut down.
static int depth_for_get_local = 0;
enum TestState {
Initial,
TargetInNative, // The agent waits for the target thread to reach
// the native method notifyAgentToGetLocal. Then it
// reads depth_for_get_local and changes the state
// to AgentInGetLocal. After that it
// calls GetLocalObject().
AgentInGetLocal, // The target thread waits for the agent to call
// GetLocalObject(). When this state is reached it
// resets the state to Initial and returns from
// native after a short spin wait racing the agent
// thread doing the unsafe stack walk.
ShutDown,
Terminated
};
// Current test state. It is used to synchronize agent and target thread execution.
static TestState test_state;
// END: Shared Variables
////////////////////////////////////////////////////
// Dummy counter used in spin wait. It is declared volatile to prevent the compiler
// from eliminating the whole spin loop.
static volatile int dummy_counter = 0;
// Makes a string of the argument (which is not macro-expanded)
#define STR(a) #a
// Makes a string of the macro expansion of a
#define XSTR(a) STR(a)
#define AT_LINE " ERROR at line " XSTR(__LINE__)
static jvmtiEnv* jvmti = NULL;
static jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved);
static char* GetErrorMessage(jvmtiEnv *jvmti, jvmtiError errCode) {
char *errMsg;
jvmtiError result = jvmti->GetErrorName(errCode, &errMsg);
return result == JVMTI_ERROR_NONE ? errMsg : NULL;
}
static void ShowErrorMessage(jvmtiEnv *jvmti, jvmtiError errCode, const char *message) {
char* errMsg = GetErrorMessage(jvmti, errCode);
if (errMsg != NULL) {
fprintf(stderr, "AGENT: %s: %s (%d)\n", message, errMsg, errCode);
jvmti->Deallocate((unsigned char *)errMsg);
} else {
fprintf(stderr, "AGENT: %s (%d)\n", message, errCode);
}
}
static void monitor_enter(jvmtiEnv* jvmti, JNIEnv* env, const char* loc) {
jvmtiError err = jvmti->RawMonitorEnter(glws_monitor);
if (err != JVMTI_ERROR_NONE) {
ShowErrorMessage(jvmti, err, loc);
env->FatalError(loc);
}
}
static void monitor_exit(jvmtiEnv* jvmti, JNIEnv* env, const char* loc) {
jvmtiError err = jvmti->RawMonitorExit(glws_monitor);
if (err != JVMTI_ERROR_NONE) {
ShowErrorMessage(jvmti, err, loc);
env->FatalError(loc);
}
}
static void monitor_wait(jvmtiEnv* jvmti, JNIEnv* env, const char* loc) {
jvmtiError err = jvmti->RawMonitorWait(glws_monitor, 0);
if (err != JVMTI_ERROR_NONE) {
ShowErrorMessage(jvmti, err, loc);
env->FatalError(loc);
}
}
static void monitor_notify(jvmtiEnv* jvmti, JNIEnv* env, const char* loc) {
jvmtiError err = jvmti->RawMonitorNotify(glws_monitor);
if (err != JVMTI_ERROR_NONE) {
ShowErrorMessage(jvmti, err, loc);
env->FatalError(loc);
}
}
static void monitor_destroy(jvmtiEnv* jvmti, JNIEnv* env, const char* loc) {
jvmtiError err = jvmti->DestroyRawMonitor(glws_monitor);
if (err != JVMTI_ERROR_NONE) {
ShowErrorMessage(jvmti, err, loc);
env->FatalError(loc);
}
}
// Perform GetLocalObject() at the requested depth while target thread is running.
// Note that the JVMTI spec does not require to suspend the target thread.
void test_GetLocalObject(jvmtiEnv* jvmti, JNIEnv* env, int depth) {
jvmtiError err;
jobject obj;
char* errMsg;
printf("AGENT: calling GetLocalObject() with depth %d\n", depth);
err = jvmti->GetLocalObject(target_thread, depth, 0, &obj);
errMsg = GetErrorMessage(jvmti, err);
printf("AGENT: GetLocalObject() result code %s (%d)\n", errMsg != NULL ? errMsg : "N/A", err);
if (errMsg != NULL) {
jvmti->Deallocate((unsigned char *)errMsg);
}
fflush(stdout);
// If the target thread wins the race we can get errors because we
// don't find a frame at the given depth or we find a non-java frame
// there (e.g. native frame). This is expected.
// JVMTI_ERROR_INVALID_SLOT can occur also because the target thread is
// running and the GetLocalObject() call might coincidentally refer to the
// frame of a static method without parameters.
if (err != JVMTI_ERROR_NONE &&
err != JVMTI_ERROR_NO_MORE_FRAMES &&
err != JVMTI_ERROR_OPAQUE_FRAME &&
err != JVMTI_ERROR_INVALID_SLOT) {
ShowErrorMessage(jvmti, err, "AgentThreadLoop: error in JVMTI GetLocalObject");
env->FatalError("AgentThreadLoop: error in JVMTI GetLocalObject\n");
}
}
// Function holding the main loop for the test agent thread.
//
// The agent does the following in each loop iteration:
//
// - Wait for the target thread either to start a new test iteration or to
// signal shutdown.
//
// Shutdown is signalled by setting test_state to ShutDown. The agent reacts
// to it by changing test_state to Terminated and then exits.
//
// In the case of a new test iteration the target thread builds a deep call
// stack and then calls the native method notifyAgentToGetLocal(). While in
// native code its stack is walkable. It sets the shared variable test_state
// to TargetInNative and then uses the glws_monitor to send the
// notification to the agent thread.
//
// - Read the shared variable depth_for_get_local which was set by the target
// thread before sending the notification.
//
// - Set test_state to AgentInGetLocal and notify the target thread.
//
// - Perform the JVMTI GetLocal call at depth_for_get_local racing the target
// thread returning from the native call making its stack not walkable. The VM
// will crash if this happens while the stack is walked to find the frame for
// the GetLocal operation. The deeper the frame the more likely the crash
// because the stack walk takes longer.
//
JNIEXPORT void JNICALL
AgentThreadLoop(jvmtiEnv * jvmti, JNIEnv* env, void * arg) {
jvmtiError err;
jvmtiThreadInfo thread_info;
// Wait until target_thread is set by target thread.
monitor_enter(jvmti, env, AT_LINE);
while (target_thread == NULL) {
monitor_wait(jvmti, env, AT_LINE);
}
monitor_exit(jvmti, env, AT_LINE);
err = jvmti->GetThreadInfo(target_thread, &thread_info);
if (err != JVMTI_ERROR_NONE) {
ShowErrorMessage(jvmti, err, "AgentThreadLoop: error in JVMTI GetThreadInfo");
env->FatalError("AgentThreadLoop: error in JVMTI GetThreadInfo\n");
}
printf("AGENT: AgentThreadLoop thread started. Polling thread '%s' for local variables\n",
thread_info.name);
jvmti->Deallocate((unsigned char *) thread_info.name);
do {
int depth;
monitor_enter(jvmti, env, AT_LINE);
// Wait for java part to build large stack and then become stack walk
// save by calling the native method notifyAgentToGetLocal or to signal
// shutdown.
while (test_state != TargetInNative) {
if (test_state == ShutDown) {
test_state = Terminated;
monitor_notify(jvmti, env, AT_LINE);
monitor_exit(jvmti, env, AT_LINE);
return;
}
monitor_wait(jvmti, env, AT_LINE);
}
depth = depth_for_get_local;
// Notify target thread that this thread is about to query the local value.
test_state = AgentInGetLocal;
monitor_notify(jvmti, env, AT_LINE);
monitor_exit(jvmti, env, AT_LINE);
// Now get the local object from the target thread's stack.
test_GetLocalObject(jvmti, env, depth);
} while (true);
printf("AGENT: AgentThreadLoop thread: exiting\n");
}
// Called by target thread after building a large stack.
// By calling this native method, the thread's stack becomes walkable.
// It notifies the agent to do the GetLocalObject() call and then races
// it to make its stack not walkable by returning from the native call.
JNIEXPORT void JNICALL
Java_GetLocalWithoutSuspendTest_notifyAgentToGetLocal(JNIEnv *env, jclass cls, jint depth, jint waitCycles) {
monitor_enter(jvmti, env, AT_LINE);
// Set depth_for_get_local and notify agent that the target thread is ready for the GetLocalObject() call
depth_for_get_local = depth;
test_state = TargetInNative;
monitor_notify(jvmti, env, AT_LINE);
// Wait for agent thread to read depth_for_get_local and do the GetLocalObject() call
while (test_state != AgentInGetLocal) {
monitor_wait(jvmti, env, AT_LINE);
}
// Reset state to Initial
test_state = Initial;
monitor_exit(jvmti, env, AT_LINE);
// Wait a little until agent thread is in unsafe stack walk.
// This needs to be a spin wait or sleep because we cannot get a notification
// from there.
while (--waitCycles > 0) {
dummy_counter++;
}
}
// Called by target thread to signal shutdown. The target thread waits for the
// agent's acknowledge by changing test_state to Terminated.
JNIEXPORT void JNICALL
Java_GetLocalWithoutSuspendTest_shutDown(JNIEnv *env, jclass cls) {
monitor_enter(jvmti, env, AT_LINE);
// Notify agent thread to shut down
test_state = ShutDown;
monitor_notify(jvmti, env, AT_LINE);
// Wait for agent to terminate
while (test_state != Terminated) {
monitor_wait(jvmti, env, AT_LINE);
}
monitor_exit(jvmti, env, AT_LINE);
// Destroy glws_monitor
monitor_destroy(jvmti, env, AT_LINE);
}
// Called by target thread to provide agent with its thread object
JNIEXPORT void JNICALL
Java_GetLocalWithoutSuspendTest_setTargetThread(JNIEnv *env, jclass cls, jthread target) {
monitor_enter(jvmti, env, AT_LINE);
target_thread = env->NewGlobalRef(target);
monitor_notify(jvmti, env, AT_LINE);
monitor_exit(jvmti, env, AT_LINE);
}
void JNICALL VMInit(jvmtiEnv *jvmti, JNIEnv *env, jthread thr) {
jvmtiError err;
jobject agent_thread_name;
jclass thread_clas;
jmethodID thread_ctor;
jthread agent_thread;
printf("AGENT: VM init event\n");
printf("AGENT: Start new thread that performs GetLocalObject calls on a running target thread\n");
agent_thread_name = env->NewStringUTF("GetLocalWithoutSuspendTest Agent Thread");
if (agent_thread_name == NULL) {
env->FatalError("VMInit: NewStringUTF failed\n");
}
thread_clas = env->FindClass("java/lang/Thread");
if (thread_clas == NULL) {
env->FatalError("VMInit: java.lang.Thread class not found\n");
}
thread_ctor = env->GetMethodID(thread_clas, "<init>", "(Ljava/lang/String;)V");
if (thread_ctor == NULL) {
env->FatalError("VMInit: failed to get ID for the Thread ctor\n");
}
agent_thread = (jthread) env->NewObject(thread_clas, thread_ctor, agent_thread_name);
if (agent_thread == NULL) {
env->FatalError("VMInit: Failed to allocate thread object\n");
}
err = jvmti->RunAgentThread(agent_thread, &AgentThreadLoop, NULL,
JVMTI_THREAD_NORM_PRIORITY);
if (err != JVMTI_ERROR_NONE) {
ShowErrorMessage(jvmti, err, "VMInit: failed to start GetLocalWithoutSuspendTest thread");
return;
}
}
JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
printf("AGENT: Agent_OnLoad started.\n");
return Agent_Initialize(jvm, options, reserved);
}
JNIEXPORT jint JNICALL
Agent_OnAttach(JavaVM *jvm, char *options, void *reserved) {
printf("AGENT: Agent_OnAttach started.");
return Agent_Initialize(jvm, options, reserved);
}
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved) {
jint res;
JNIEnv *env;
printf("AGENT: JNI_OnLoad started.");
res = jvm->GetEnv((void **) &env, JNI_VERSION_9);
if (res != JNI_OK || env == NULL) {
fprintf(stderr, "Error: GetEnv call failed(%d)!\n", res);
return JNI_ERR;
}
return JNI_VERSION_9;
}
static
jint Agent_Initialize(JavaVM *jvm, char *options, void *reserved) {
jint res;
jvmtiError err;
jvmtiCapabilities caps;
jvmtiEventCallbacks callbacks;
printf("AGENT: Agent_Initialize started\n");
memset(&caps, 0, sizeof(caps));
memset(&callbacks, 0, sizeof(callbacks));
res = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_9);
if (res != JNI_OK || jvmti == NULL) {
fprintf(stderr, "Error: GetEnv(JVMTI_VERSION_9) call failed(%d)!\n", res);
return JNI_ERR;
}
caps.can_access_local_variables = 1;
err = jvmti->AddCapabilities(&caps);
if (err != JVMTI_ERROR_NONE) {
ShowErrorMessage(jvmti, err, "Agent_OnLoad: error in JVMTI AddCapabilities");
return JNI_ERR;
}
err = jvmti->GetCapabilities(&caps);
if (err != JVMTI_ERROR_NONE) {
ShowErrorMessage(jvmti, err, "Agent_OnLoad: error in JVMTI GetCapabilities");
return JNI_ERR;
}
if (!caps.can_access_local_variables) {
fprintf(stderr, "Warning: Access to local variables is not implemented\n");
return JNI_ERR;
}
callbacks.VMInit = &VMInit;
err = jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
if (err != JVMTI_ERROR_NONE) {
ShowErrorMessage(jvmti, err, "Agent_OnLoad: error in JVMTI SetEventCallbacks");
return JNI_ERR;
}
err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL);
if (err != JVMTI_ERROR_NONE) {
ShowErrorMessage(jvmti, err, "Agent_OnLoad: error in JVMTI SetEventNotificationMode");
return JNI_ERR;
}
err = jvmti->CreateRawMonitor("GetLocalWithoutSuspend Test Monitor", &glws_monitor);
printf("AGENT: Agent_Initialize finished\n");
return JNI_OK;
}
}

@ -0,0 +1,178 @@
/*
* Copyright (c) 2022, 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 <string.h>
#include "jvmti.h"
#include "jvmti_common.h"
extern "C" {
#define MAX_FRAME_COUNT 30
enum Slots {
SlotInvalid0 = -1,
SlotString = 0,
SlotThread = 1,
SlotInt = 2,
SlotLong = 3,
SlotUnaligned = 4,
SlotFloat = 5,
SlotDouble = 6,
};
static jvmtiEnv *jvmti = NULL;
static void
check_jvmti_error_not_suspended(JNIEnv* jni, const char* func_name, jvmtiError err) {
if (err != JVMTI_ERROR_THREAD_NOT_SUSPENDED) {
LOG("%s failed: expected JVMTI_ERROR_THREAD_NOT_SUSPENDED instead of: %d\n", func_name, err);
fatal(jni, func_name);
}
}
static void
test_GetLocal(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread) {
jvmtiError err;
const int depth = 0;
jobject msg = NULL;
jint ii = 0;
jlong ll = 0L;
jfloat ff = 0.0;
jdouble dd = 0.0;
LOG("\ntest_GetLocal started\n");
// #0: Test JVMTI GetLocalInstance function
err = jvmti->GetLocalInstance(thread, depth, &msg);
check_jvmti_error_not_suspended(jni, "GetLocalInstance", err);
LOG("check for JVMTI GetLocalInstance succeeded\n");
// #1: Test JVMTI GetLocalObject function
err = jvmti->GetLocalObject(thread, depth, SlotString, &msg);
check_jvmti_error_not_suspended(jni, "GetLocalObject", err);
LOG("check for JVMTI GetLocalObject succeeded\n");
// #2: Test JVMTI GetLocalInt function
err = jvmti->GetLocalInt(thread, depth, SlotInt, &ii);
check_jvmti_error_not_suspended(jni, "GetLocalInt", err);
LOG("check for JVMTI GetLocalInt succeeded\n");
// #3: Test JVMTI GetLocalLong function
err = jvmti->GetLocalLong(thread, depth, SlotLong, &ll);
check_jvmti_error_not_suspended(jni, "GetLocalLong", err);
LOG("check for JVMTI GetLocalLong succeeded\n");
// #4: Test JVMTI GetLocalFloat function
err = jvmti->GetLocalFloat(thread, depth, SlotFloat, &ff);
check_jvmti_error_not_suspended(jni, "GetLocalFloat", err);
LOG("check for JVMTI GetLocalFloat succeeded\n");
// #5: Test JVMTI GetLocalDouble function
err = jvmti->GetLocalDouble(thread, depth, SlotDouble, &dd);
check_jvmti_error_not_suspended(jni, "GetLocalDouble", err);
LOG("check for JVMTI GetLocalDouble succeeded\n");
LOG("test_GetLocal finished\n");
}
static void
test_SetLocal(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread) {
jvmtiError err;
const int depth = 0;
const jobject msg = NULL;
const jint ii = 0;
const jlong ll = 0L;
const jfloat ff = 0.0;
const jdouble dd = 0.0;
LOG("\ntest_SetLocal started\n");
// #1: Test JVMTI SetLocalObject function
err = jvmti->SetLocalObject(thread, depth, SlotString, msg);
check_jvmti_error_not_suspended(jni, "SetLocalObject", err);
LOG("check for JVMTI SetLocalObject succeeded\n");
// #2: Test JVMTI SetLocalInt function
err = jvmti->SetLocalInt(thread, depth, SlotInt, ii);
check_jvmti_error_not_suspended(jni, "SetLocalInt", err);
LOG("check for JVMTI SetLocalInt succeeded\n");
// #3: Test JVMTI SetLocalLong function
err = jvmti->SetLocalLong(thread, depth, SlotLong, ll);
check_jvmti_error_not_suspended(jni, "SetLocalLong", err);
LOG("check for JVMTI SetLocalLong succeeded\n");
// #4: Test JVMTI SetLocalFloat function
err = jvmti->SetLocalFloat(thread, depth, SlotFloat, ff);
check_jvmti_error_not_suspended(jni, "SetLocalFloat", err);
LOG("check for JVMTI SetLocalFloat succeeded\n");
// #5: Test JVMTI SetLocalDouble function
err = jvmti->SetLocalDouble(thread, depth, SlotDouble, dd);
check_jvmti_error_not_suspended(jni, "SetLocalDouble", err);
LOG("check for JVMTI SetLocalDouble succeeded\n");
LOG("test_SetLocal finished\n");
}
JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
jvmtiCapabilities caps;
jvmtiError err;
LOG("Agent_OnLoad started\n");
if (jvm->GetEnv((void **) (&jvmti), JVMTI_VERSION) != JNI_OK) {
return JNI_ERR;
}
memset(&caps, 0, sizeof(caps));
caps.can_support_virtual_threads = 1;
caps.can_access_local_variables = 1;
err = jvmti->AddCapabilities(&caps);
if (err != JVMTI_ERROR_NONE) {
LOG("error in JVMTI AddCapabilities: %d\n", err);
return JNI_ERR;
}
LOG("Agent_OnLoad finished\n");
return JNI_OK;
}
JNIEXPORT void JNICALL
Java_GetSetLocalUnsuspended_testUnsuspendedThread(JNIEnv *jni, jclass klass, jthread thread) {
char* tname = get_thread_name(jvmti, jni, thread);
jmethodID method = NULL;
jlocation location = 0;
LOG("\ntestUnsuspendedThread: started for thread: %s\n", tname);
test_GetLocal(jvmti, jni, thread);
test_SetLocal(jvmti, jni, thread);
LOG("\ntestUnsuspendedThread: finished for thread: %s\n", tname);
deallocate(jvmti, jni, (void*)tname);
}
} // extern "C"

@ -127,9 +127,13 @@ test_GetLocal(jvmtiEnv *jvmti, JNIEnv *jni, jthread cthread, jthread vthread,
// #0: Test JVMTI GetLocalInstance function for carrier thread
if (cthread != NULL) {
suspend_thread(jvmti, jni, cthread);
err = jvmti->GetLocalInstance(cthread, 3, &msg);
check_jvmti_status(jni, err, "error in JVMTI GetLocalInstance for carrier thread top frame Continuation.run");
LOG("JVMTI GetLocalInstance succeed for carrier thread top frame Continuation.run()\n");
resume_thread(jvmti, jni, cthread);
}
// #1: Test JVMTI GetLocalObject function with negative frame depth

@ -378,10 +378,15 @@ test_GetLocal(jvmtiEnv *jvmti, JNIEnv *jni, jthread cthread, jthread vthread, co
}
// #0: Test JVMTI GetLocalInstance function for carrier thread
err = jvmti->GetLocalInstance(cthread, 3, &obj);
check_jvmti_status(jni, err, "event handler: error in JVMTI GetLocalInstance for carrier thread top frame Continuation.run");
{
suspend_thread(jvmti, jni, cthread);
LOG("JVMTI GetLocalInstance succeed for carrier thread top frame Continuation.run()\n");
err = jvmti->GetLocalInstance(cthread, 3, &obj);
check_jvmti_status(jni, err, "event handler: error in JVMTI GetLocalInstance for carrier thread top frame Continuation.run");
LOG("JVMTI GetLocalInstance succeed for carrier thread top frame Continuation.run()\n");
resume_thread(jvmti, jni, cthread);
}
depth = find_method_depth(jvmti, jni, vthread, "producer");
if (depth == -1) {
@ -589,6 +594,7 @@ Agent_OnLoad(JavaVM *jvm, char *options,
memset(&caps, 0, sizeof(caps));
caps.can_support_virtual_threads = 1;
caps.can_access_local_variables = 1;
caps.can_suspend = 1;
err = jvmti->AddCapabilities(&caps);
if (err != JVMTI_ERROR_NONE) {

@ -316,23 +316,18 @@ static int checkLocalVariableFunctions() {
jfloat float_value;
jdouble double_value;
int i;
/* DEBUG -- while thread should be suspended
jvmtiCapabilities caps;
*/
NSK_DISPLAY0("Checking positive: GetLocalVariableTable\n");
if (!NSK_JVMTI_VERIFY(jvmti->GetLocalVariableTable(method, &count, &local_variable_table)))
return NSK_FALSE;
/* DEBUG -- while thread should be suspended
memset(&caps, 0, sizeof(caps));
caps.can_suspend = 1;
if (!NSK_JVMTI_VERIFY(jvmti->AddCapabilities(&caps)))
return JNI_ERR;
if (!NSK_JVMTI_VERIFY(jvmti->SuspendThread(thread)))
return NSK_FALSE;
*/
for (i = 0; i < count; i++) {
if (strcmp(local_variable_table[i].name, "o") == 0) {
@ -388,12 +383,10 @@ static int checkLocalVariableFunctions() {
}
}
/* DEBUG -- while thread should be suspended
if (!NSK_JVMTI_VERIFY(jvmti->ResumeThread(thread)))
return NSK_FALSE;
if (!NSK_JVMTI_VERIFY(jvmti->RelinquishCapabilities(&caps)))
return JNI_ERR;
*/
if (!NSK_JVMTI_VERIFY(jvmti->Deallocate((unsigned char*)local_variable_table)))
return NSK_FALSE;