8288387: GetLocalXXX/SetLocalXXX spec should require suspending target thread
Reviewed-by: lmesnik, dsamersoff
This commit is contained in:
parent
8d751de319
commit
21a825e059
src/hotspot/share/prims
test/hotspot/jtreg
serviceability/jvmti
GetLocalVariable
GetLocalWithoutSuspendTest.javaGetSetLocalUnsuspended.javalibGetLocalWithoutSuspendTest.cpplibGetSetLocalUnsuspended.cpp
vthread
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;
|
||||
}
|
||||
|
||||
}
|
178
test/hotspot/jtreg/serviceability/jvmti/GetLocalVariable/libGetSetLocalUnsuspended.cpp
Normal file
178
test/hotspot/jtreg/serviceability/jvmti/GetLocalVariable/libGetSetLocalUnsuspended.cpp
Normal file
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user