Skip to content
Permalink
Browse files
reimplement JVMTI GetVirtualThread and GetCarrierThread with the exte…
…nsion mechanism
  • Loading branch information
sspitsyn committed Mar 18, 2021
1 parent 491604d commit 74aa86f1ef9df68062593efa621bae4c1e8a7a02
@@ -2239,6 +2239,7 @@ jvmtiEnv *jvmti;
<intro>
</intro>

<elide>
<function id="GetVirtualThread" num="113" since="16">
<synopsis>Get Virtual Thread</synopsis>
<description>
@@ -2266,7 +2267,9 @@ jvmtiEnv *jvmti;
<errors>
</errors>
</function>
</elide>

<elide>
<function id="GetCarrierThread" num="117" since="16">
<synopsis>Get Carrier Thread</synopsis>
<description>
@@ -2299,6 +2302,7 @@ jvmtiEnv *jvmti;
</error>
</errors>
</function>
</elide>

<function id="SuspendAllVirtualThreads" num="118" since="16">
<synopsis>Suspend All Virtual Threads</synopsis>
@@ -10589,19 +10593,8 @@ myInit() {
<description>
Can support virtual threads.
If this capability is enabled then the following functions can be called:
<functionlink id="IsVirtualThread"></functionlink>,
<functionlink id="GetVirtualThread"></functionlink>,
<functionlink id="GetCarrierThread"></functionlink>,
the following functions can be called with a virtual thread reference:
<functionlink id="GetStackTrace"></functionlink>
<functionlink id="GetFrameCount"></functionlink>
<functionlink id="GetFrameLocation"></functionlink>
<functionlink id="GetLocalObject"></functionlink>
<functionlink id="GetLocalInstance"></functionlink>
<functionlink id="GetLocalInt"></functionlink>
<functionlink id="GetLocalLong"></functionlink>
<functionlink id="GetLocalFloat"></functionlink>
<functionlink id="GetLocalDouble"></functionlink>
<functionlink id="SuspendAllVirtualThreads"></functionlink>,
<functionlink id="ResumeAllVirtualThreads"></functionlink>,
and the following events can be enabled:
<eventlink id="VirtualThreadStart"></eventlink>,
<eventlink id="VirtualThreadEnd"></eventlink>,
@@ -867,22 +867,19 @@ JvmtiEnv::GetJLocationFormat(jvmtiJlocationFormat* format_ptr) {
// Functions supporting virtual threads
//

#if 0
// java_thread - pre-checked
// vthread_ptr - pre-checked for NULL
jvmtiError
JvmtiEnv::GetVirtualThread(JavaThread* java_thread, jthread* vthread_ptr) {
JavaThread* current_thread = JavaThread::current();
ResourceMark rm(current_thread);
oop vthread_oop = NULL;
uint32_t debug_bits = 0;

JvmtiThreadState *state = JvmtiThreadState::state_for(java_thread);
if (state == NULL) {
return JVMTI_ERROR_THREAD_NOT_ALIVE;
}
// if (!java_thread->is_thread_fully_suspended(true, &debug_bits)) {
// return JVMTI_ERROR_THREAD_NOT_SUSPENDED;
// }
vthread_oop = java_thread->mounted_vthread();
if (!java_lang_VirtualThread::is_instance(vthread_oop)) { // not a virtual thread
vthread_oop = NULL;
@@ -905,6 +902,7 @@ JvmtiEnv::GetCarrierThread(jthread vthread, jthread* thread_ptr) {
Handshake::execute(&op, current_thread);
return op.result();
} /* end GetCarrierThread */
#endif // if 0

//
// Thread functions
@@ -1235,7 +1235,6 @@ JvmtiEnvBase::get_threadOop_and_JavaThread(ThreadsList* t_list, jthread thread,
JavaThread* cur_thread = JavaThread::current();
JavaThread* java_thread = NULL;
oop thread_oop = NULL;
ThreadsListHandle tlh(cur_thread);

if (thread == NULL) {
java_thread = cur_thread;
@@ -1244,7 +1243,7 @@ JvmtiEnvBase::get_threadOop_and_JavaThread(ThreadsList* t_list, jthread thread,
return JVMTI_ERROR_INVALID_THREAD;
}
} else {
jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &java_thread, &thread_oop);
jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(t_list, thread, &java_thread, &thread_oop);
if (err != JVMTI_ERROR_NONE) {
// We got an error code so we don't have a JavaThread*, but only return
// an error from here if we didn't get a valid thread_oop. In a vthread case
@@ -25,6 +25,9 @@
#include "precompiled.hpp"
#include "prims/jvmtiExport.hpp"
#include "prims/jvmtiExtensions.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/jniHandles.inline.hpp"

// the list of extension functions
GrowableArray<jvmtiExtensionFunctionInfo*>* JvmtiExtensions::_ext_functions;
@@ -33,7 +36,9 @@ GrowableArray<jvmtiExtensionFunctionInfo*>* JvmtiExtensions::_ext_functions;
GrowableArray<jvmtiExtensionEventInfo*>* JvmtiExtensions::_ext_events;


// extension function
/*
* Extension Functions
*/
static jvmtiError JNICALL IsClassUnloadingEnabled(const jvmtiEnv* env, ...) {
jboolean* enabled = NULL;
va_list ap;
@@ -49,6 +54,87 @@ static jvmtiError JNICALL IsClassUnloadingEnabled(const jvmtiEnv* env, ...) {
return JVMTI_ERROR_NONE;
}

// Parameters: (jthread thread, jthread* vthread_ptr)
static jvmtiError JNICALL GetVirtualThread(const jvmtiEnv* env, ...) {
JvmtiEnv* jvmti_env = JvmtiEnv::JvmtiEnv_from_jvmti_env((jvmtiEnv*)env);
if (jvmti_env->get_capabilities()->can_support_virtual_threads == 0) {
return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
}

JavaThread* current_thread = JavaThread::current();
ResourceMark rm(current_thread);
jthread thread = NULL;
jthread* vthread_ptr = NULL;
JavaThread* java_thread = NULL;
oop thread_oop = NULL;
va_list ap;

va_start(ap, env);
thread = va_arg(ap, jthread);
vthread_ptr = va_arg(ap, jthread*);
va_end(ap);

ThreadInVMfromNative tiv(current_thread);
ThreadsListHandle tlh(current_thread);

jvmtiError err;
if (thread == NULL) {
java_thread = current_thread;
} else {
err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), thread, &java_thread, NULL);
if (err != JVMTI_ERROR_NONE) {
return err;
}
}
if (vthread_ptr == NULL) {
return JVMTI_ERROR_NULL_POINTER;
}

JvmtiThreadState *state = JvmtiThreadState::state_for(java_thread);
if (state == NULL) {
return JVMTI_ERROR_THREAD_NOT_ALIVE;
}
oop vthread_oop = java_thread->mounted_vthread();
if (!java_lang_VirtualThread::is_instance(vthread_oop)) { // not a virtual thread
vthread_oop = NULL;
}
*vthread_ptr = (jthread)JNIHandles::make_local(current_thread, vthread_oop);
return JVMTI_ERROR_NONE;
}

// Parameters: (jthread vthread, jthread* thread_ptr)
static jvmtiError JNICALL GetCarrierThread(const jvmtiEnv* env, ...) {
JvmtiEnv* jvmti_env = JvmtiEnv::JvmtiEnv_from_jvmti_env((jvmtiEnv*)env);
if (jvmti_env->get_capabilities()->can_support_virtual_threads == 0) {
return JVMTI_ERROR_MUST_POSSESS_CAPABILITY;
}

JavaThread* current_thread = JavaThread::current();
HandleMark hm(current_thread);
jthread vthread = NULL;
jthread* thread_ptr = NULL;
va_list ap;

va_start(ap, env);
vthread = va_arg(ap, jthread);
thread_ptr = va_arg(ap, jthread*);
va_end(ap);

ThreadInVMfromNative tiv(current_thread);

oop vthread_obj = JNIHandles::resolve_external_guard(vthread);

if (!java_lang_VirtualThread::is_instance(vthread_obj)) {
return JVMTI_ERROR_INVALID_THREAD;
}
if (thread_ptr == NULL) {
return JVMTI_ERROR_NULL_POINTER;
}
VThreadGetThreadClosure op(Handle(current_thread, vthread_obj), thread_ptr);
Handshake::execute(&op, current_thread);
return op.result();
}

// register extension functions and events. In this implementation we
// have a single extension function (to prove the API) that tests if class
// unloading is enabled or disabled. We also have a single extension event
@@ -59,20 +145,57 @@ void JvmtiExtensions::register_extensions() {
_ext_functions = new (ResourceObj::C_HEAP, mtServiceability) GrowableArray<jvmtiExtensionFunctionInfo*>(1, mtServiceability);
_ext_events = new (ResourceObj::C_HEAP, mtServiceability) GrowableArray<jvmtiExtensionEventInfo*>(1, mtServiceability);

// register our extension function
static jvmtiParamInfo func_params[] = {
{ (char*)"IsClassUnloadingEnabled", JVMTI_KIND_OUT, JVMTI_TYPE_JBOOLEAN, JNI_FALSE }
// register our extension functions
static jvmtiParamInfo func_params0[] = {
{ (char*)"IsClassUnloadingEnabled", JVMTI_KIND_OUT, JVMTI_TYPE_JBOOLEAN, JNI_FALSE }
};
static jvmtiParamInfo func_params1[] = {
{ (char*)"GetVirtualThread", JVMTI_KIND_IN, JVMTI_TYPE_JTHREAD, JNI_FALSE },
{ (char*)"GetVirtualThread", JVMTI_KIND_OUT, JVMTI_TYPE_JTHREAD, JNI_FALSE }
};
static jvmtiParamInfo func_params2[] = {
{ (char*)"GetVirtualThread", JVMTI_KIND_IN, JVMTI_TYPE_JTHREAD, JNI_FALSE },
{ (char*)"GetCarrierThread", JVMTI_KIND_OUT, JVMTI_TYPE_JTHREAD, JNI_FALSE }
};

static jvmtiError errors[] = {
JVMTI_ERROR_MUST_POSSESS_CAPABILITY,
JVMTI_ERROR_INVALID_THREAD
};
static jvmtiExtensionFunctionInfo ext_func = {

static jvmtiExtensionFunctionInfo ext_func0 = {
(jvmtiExtensionFunction)IsClassUnloadingEnabled,
(char*)"com.sun.hotspot.functions.IsClassUnloadingEnabled",
(char*)"Tell if class unloading is enabled (-noclassgc)",
sizeof(func_params)/sizeof(func_params[0]),
func_params,
sizeof(func_params0)/sizeof(func_params0[0]),
func_params0,
0, // no non-universal errors
NULL
};
_ext_functions->append(&ext_func);

static jvmtiExtensionFunctionInfo ext_func1 = {
(jvmtiExtensionFunction)GetVirtualThread,
(char*)"com.sun.hotspot.functions.GetVirtualThread",
(char*)"Get virtual thread executed on carrier thread",
sizeof(func_params1)/sizeof(func_params1[0]),
func_params1,
sizeof(errors)/sizeof(jvmtiError), // non-universal errors
errors
};

static jvmtiExtensionFunctionInfo ext_func2 = {
(jvmtiExtensionFunction)GetCarrierThread,
(char*)"com.sun.hotspot.functions.GetCarrierThread",
(char*)"Get carrier thread executing virtual thread",
sizeof(func_params2)/sizeof(func_params2[0]),
func_params2,
sizeof(errors)/sizeof(jvmtiError), // non-universal errors
errors
};

_ext_functions->append(&ext_func0);
_ext_functions->append(&ext_func1);
_ext_functions->append(&ext_func2);

// register our extension event

@@ -85,7 +85,7 @@ agentProc(jvmtiEnv * jvmti, JNIEnv * jni, void * arg) {
for (int i = 0; i < count; i++) {
jthread tested_thread = NULL;

err = jvmti->GetVirtualThread(threads[i], &tested_thread);
err = GetVirtualThread(jvmti, jni, threads[i], &tested_thread);
if (err == JVMTI_ERROR_THREAD_NOT_ALIVE) {
continue;
}
@@ -71,12 +71,7 @@ jint get_thread_state(jvmtiEnv *jvmti, JNIEnv *jni, jthread thread) {
}

void check_link_consistency(jvmtiEnv *jvmti, JNIEnv *jni, jthread vthread) {
jthread cthread = NULL;
jvmtiError err;

err = jvmti->GetCarrierThread(vthread, &cthread);
check_jvmti_status(jni, err, "Error in GetCarrierThread.");

jthread cthread = get_carrier_thread(jvmti, jni, vthread);
jint vstate = get_thread_state(jvmti, jni, vthread);
jint cstate = get_thread_state(jvmti, jni, cthread);

@@ -88,14 +83,7 @@ void check_link_consistency(jvmtiEnv *jvmti, JNIEnv *jni, jthread vthread) {
}

if (cthread != NULL) {
jthread cthread_to_vthread = NULL;
err = jvmti->GetVirtualThread(cthread, &cthread_to_vthread);
if (err != JVMTI_ERROR_NONE) {
printf("Error %s in GetVirtualThread :\n", TranslateError(err));
print_thread_info(jvmti, jni, vthread);
print_thread_info(jvmti, jni, cthread);
fatal(jni, "");
}
jthread cthread_to_vthread = get_virtual_thread(jvmti, jni, cthread);
if (!jni->IsSameObject(vthread, cthread_to_vthread)) {
printf("GetVirtualThread(GetCarrierThread(vthread)) not equals to vthread.\n");
printf("Result: ");
@@ -141,7 +129,7 @@ agentProc(jvmtiEnv * jvmti, JNIEnv * jni, void * arg) {
for (int i = 0; i < count; i++) {
jthread tested_thread = NULL;

err = jvmti->GetVirtualThread(threads[i], &tested_thread);
err = GetVirtualThread(jvmti, jni, threads[i], &tested_thread);
if (err == JVMTI_ERROR_THREAD_NOT_ALIVE) {
continue;
}
@@ -136,7 +136,7 @@ agentProc(jvmtiEnv * jvmti, JNIEnv * jni, void * arg) {
jthread testedThread = NULL;
jvmtiError err;

err = jvmti->GetVirtualThread(threads[i], &testedThread);
err = GetVirtualThread(jvmti, jni, threads[i], &testedThread);
if (err == JVMTI_ERROR_THREAD_NOT_ALIVE) {
continue;
}
@@ -174,7 +174,7 @@ Breakpoint(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread,
"Breakpoint", ++breakpoint_count);
if (is_virtual) {
jthread cthread = NULL;
err = jvmti->GetCarrierThread(thread, &cthread);
cthread = get_carrier_thread(jvmti, jni, thread);
print_frame_event_info(jvmti, jni, cthread, method,
"Breakpoint", breakpoint_count);
// Continuation.run() should always be considered to be in the cthread, not the vthread.
@@ -214,8 +214,7 @@ Breakpoint(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread,
qPutBreakpointHit++;
print_frame_event_info(jvmti, jni, thread, method,
"Breakpoint", ++breakpoint_count);
err = jvmti->GetCarrierThread(thread, &cthread);
check_jvmti_status(jni, err, "Breakpoint: error in JVMTI GetCarrierThread");
cthread = get_carrier_thread(jvmti, jni, thread);
if (qPutBreakpointHit == 1) {
// We hit our 1st DoContinueSingleStepTest.qPut() breakpoint. Now setup single stepping
// on the carrier thread. We should not get a single step event before hitting this breakpoint
@@ -330,8 +329,7 @@ SingleStep(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread,
}
jthread cthread = thread;
if (is_virtual) {
err = jvmti->GetCarrierThread(thread, &cthread);
check_jvmti_status(jni, err, "Breakpoint: error in JVMTI GetCarrierThread");
cthread = get_carrier_thread(jvmti, jni, thread);
}
printf("SingleStep: %s: disabling SingleStep events on carrier thread\n", mname);
err = jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_SINGLE_STEP, cthread);
@@ -134,8 +134,8 @@ breakpoint_hit1(jvmtiEnv *jvmti, JNIEnv* jni,
// Test GetVirtualThread for carrier thread.
printf("Hit #1: Breakpoint: %s: checking GetVirtualThread on carrier thread: %p, %s\n",
mname, (void*)cthread, tname); fflush(0);
err = jvmti->GetVirtualThread(cthread, &vthread);
check_jvmti_status(jni, err, "Breakpoint: error in JVMTI GetVirtualThread");

vthread = get_virtual_thread(jvmti, jni, cthread);

if (jni->IsSameObject(thread, vthread) != JNI_TRUE) {
passed = JNI_FALSE;
@@ -286,7 +286,6 @@ Breakpoint(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread,
jthread cthread = NULL;
char* mname = get_method_name(jvmti, jni, method);
jboolean is_virtual = jni->IsVirtualThread(thread);
jvmtiError err;

if (strcmp(mname, "brkpt") != 0) {
printf("FAILED: got unexpected breakpoint in method %s()\n", mname);
@@ -301,8 +300,7 @@ Breakpoint(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread,
print_frame_event_info(jvmti, jni, thread, method,
"Breakpoint", ++breakpoint_count);

err = jvmti->GetCarrierThread(thread, &cthread);
check_jvmti_status(jni, err, "Breakpoint: error in JVMTI GetCarrierThread");
cthread = get_carrier_thread(jvmti, jni, thread);

if (brkptBreakpointHit == 1) { // 1st MethodExitTest.brkpt() breakpoint
breakpoint_hit1(jvmti, jni, thread, cthread, is_virtual, mname);

0 comments on commit 74aa86f

Please sign in to comment.