Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update jvmtiGetThreadGroupChildren for java 19 #15539

Merged
merged 1 commit into from
Aug 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
344 changes: 246 additions & 98 deletions runtime/jvmti/jvmtiThreadGroup.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,22 @@
#include "jvmtiHelpers.h"
#include "jvmti_internal.h"

/**
* Fetches the threads and child ThreadGroups of the given threadgroup group. Also return
* the number of threads and child threadgroups.
*
* @param[in] vm java vm
* @param[in] currentThread the current thread
* @param[in] group the ThreadGroup to fetch threads and child ThreadGroups from
* @param[out] rv_thread_count pointer to number of threads returned in the thread array
* @param[out] rv_threads pointer to array of the ThreadGroup's threads
* @param[out] rv_group_count pointer to number of child ThreadGroups returned in the ThreadGroup array
* @param[out] rv_groups pointer to array of the ThreadGroup's child ThreadGroups
*
* @return JVMTI_ERROR_NONE on success, a JVMTI error code otherwise
*/
static jvmtiError getThreadGroupChildrenImpl(J9JavaVM *vm, J9VMThread *currentThread, jobject group, jint *rv_thread_count, jthread **rv_threads, jint *rv_group_count, jthreadGroup **rv_groups);

jvmtiError JNICALL
jvmtiGetTopThreadGroups(jvmtiEnv* env,
jint* group_count_ptr,
Expand Down Expand Up @@ -148,126 +164,37 @@ jvmtiGetThreadGroupChildren(jvmtiEnv* env,
jthread *rv_threads = NULL;
jint rv_group_count = 0;
jthreadGroup *rv_groups = NULL;
#if JAVA_SPEC_VERSION < 19
J9JavaVM *vm = JAVAVM_FROM_ENV(env);
#endif /* JAVA_SPEC_VERSION < 19 */

Trc_JVMTI_jvmtiGetThreadGroupChildren_Entry(env);
#if JAVA_SPEC_VERSION < 19
if (J9_ARE_ANY_BITS_SET(vm->jclFlags, J9_JCL_FLAG_THREADGROUPS)) {
J9VMThread *currentThread = NULL;
J9InternalVMFunctions const * const vmFuncs = vm->internalVMFunctions;
PORT_ACCESS_FROM_JAVAVM(vm);

rc = getCurrentVMThread(vm, &currentThread);
if (JVMTI_ERROR_NONE == rc) {
j9object_t threadGroupObject = NULL;
j9object_t childrenThreadsLock = NULL;
j9object_t childrenGroupsLock = NULL;
jthreadGroup *groups = NULL;
jint numGroups = 0;
jthread *threads = NULL;
jint numThreads = 0;

vmFuncs->internalEnterVMFromJNI(currentThread);

ENSURE_PHASE_LIVE(env);
ENSURE_JTHREADGROUP_NON_NULL(group);
ENSURE_NON_NULL(thread_count_ptr);
ENSURE_NON_NULL(threads_ptr);
ENSURE_NON_NULL(group_count_ptr);
ENSURE_NON_NULL(groups_ptr);

/* Construct the Children Groups array under a lock */
threadGroupObject = *((j9object_t*)group);
childrenGroupsLock = J9VMJAVALANGTHREADGROUP_CHILDRENGROUPSLOCK(currentThread, threadGroupObject);
childrenGroupsLock = (j9object_t)vmFuncs->objectMonitorEnter(currentThread, childrenGroupsLock);
/* The threadGroupObject has to be reobtained as it might have been GC'ed while waiting for the lock */
threadGroupObject = *((j9object_t*)group);
if (J9_OBJECT_MONITOR_ENTER_FAILED(childrenGroupsLock)) {
#if defined(J9VM_OPT_CRIU_SUPPORT)
if (J9_OBJECT_MONITOR_CRIU_SINGLE_THREAD_MODE_THROW == (UDATA)childrenGroupsLock) {
vmFuncs->setCRIUSingleThreadModeJVMCRIUException(currentThread, 0, 0);
rc = JVMTI_ERROR_THREAD_SUSPENDED;
} else
#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */
if (J9_OBJECT_MONITOR_OOM == (UDATA)childrenGroupsLock) {
rc = JVMTI_ERROR_OUT_OF_MEMORY;
}
goto done;
}

numGroups = J9VMJAVALANGTHREADGROUP_NUMGROUPS(currentThread, threadGroupObject);
groups = j9mem_allocate_memory(sizeof(jthreadGroup) * numGroups, J9MEM_CATEGORY_JVMTI_ALLOCATE);
if (NULL == groups) {
rc = JVMTI_ERROR_OUT_OF_MEMORY;
} else {
jint i = 0;
j9object_t childrenGroups = (j9object_t)J9VMJAVALANGTHREADGROUP_CHILDRENGROUPS(currentThread, threadGroupObject);

for (i = 0; i < numGroups; ++i) {
j9object_t group = J9JAVAARRAYOFOBJECT_LOAD(currentThread, childrenGroups, i);

groups[i] = (jthreadGroup)vmFuncs->j9jni_createLocalRef((JNIEnv *)currentThread, group);
}
}

vmFuncs->objectMonitorExit(currentThread, childrenGroupsLock);

/* Construct the Children Threads array under a lock */
threadGroupObject = *((j9object_t*)group);
childrenThreadsLock = J9VMJAVALANGTHREADGROUP_CHILDRENTHREADSLOCK(currentThread, threadGroupObject);
childrenThreadsLock = (j9object_t)vmFuncs->objectMonitorEnter(currentThread, childrenThreadsLock);
/* The threadGroupObject has to be reobtained as it might have been GC'ed while waiting for the lock */
threadGroupObject = *((j9object_t*)group);
if (J9_OBJECT_MONITOR_ENTER_FAILED(childrenThreadsLock)) {
#if defined(J9VM_OPT_CRIU_SUPPORT)
if (J9_OBJECT_MONITOR_CRIU_SINGLE_THREAD_MODE_THROW == (UDATA)childrenThreadsLock) {
vmFuncs->setCRIUSingleThreadModeJVMCRIUException(currentThread, 0, 0);
rc = JVMTI_ERROR_THREAD_SUSPENDED;
} else
#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */
if (J9_OBJECT_MONITOR_OOM == (UDATA)childrenThreadsLock) {
rc = JVMTI_ERROR_OUT_OF_MEMORY;
}
j9mem_free_memory(groups);
goto done;
}

numThreads = J9VMJAVALANGTHREADGROUP_NUMTHREADS(currentThread, threadGroupObject);
threads = j9mem_allocate_memory(sizeof(jthread) * numThreads, J9MEM_CATEGORY_JVMTI_ALLOCATE);
if (NULL == threads) {
j9mem_free_memory(groups);
rc = JVMTI_ERROR_OUT_OF_MEMORY;
} else {
jint i = 0;
j9object_t childrenThreads = (j9object_t)J9VMJAVALANGTHREADGROUP_CHILDRENTHREADS(currentThread, threadGroupObject);
jint numLiveThreads = 0;

/* Include only live threads in the result */
numLiveThreads = 0;
for (i = 0; i < numThreads; ++i) {
j9object_t thread = J9JAVAARRAYOFOBJECT_LOAD(currentThread, childrenThreads, i);
J9VMThread *targetThread = NULL;

if (JVMTI_ERROR_NONE == getVMThread(currentThread, (jthread) &thread, &targetThread, FALSE, TRUE)) {
threads[numLiveThreads++] = (jthread)vmFuncs->j9jni_createLocalRef((JNIEnv *)currentThread, thread);
releaseVMThread(currentThread, targetThread);
}
}

rv_thread_count = numLiveThreads;
rv_threads = threads;
rv_group_count = numGroups;
rv_groups = groups;
}

vmFuncs->objectMonitorExit(currentThread, childrenThreadsLock);
rc = getThreadGroupChildrenImpl(
vm,
currentThread,
group,
&rv_thread_count,
&rv_threads,
&rv_group_count,
&rv_groups);

done:
vmFuncs->internalExitVMToJNI(currentThread);
}
}
#endif /* JAVA_SPEC_VERSION < 19 */

if (NULL != thread_count_ptr) {
*thread_count_ptr = rv_thread_count;
Expand All @@ -283,3 +210,224 @@ jvmtiGetThreadGroupChildren(jvmtiEnv* env,
}
TRACE_JVMTI_RETURN(jvmtiGetThreadGroupChildren);
}

#if JAVA_SPEC_VERSION < 19
static jvmtiError
getThreadGroupChildrenImpl(J9JavaVM *vm, J9VMThread *currentThread, jobject group,
jint *rv_thread_count,
jthread **rv_threads,
jint *rv_group_count,
jthreadGroup **rv_groups)
{
const J9InternalVMFunctions * const vmFuncs = vm->internalVMFunctions;
j9object_t threadGroupObject = NULL;
j9object_t childrenThreadsLock = NULL;
j9object_t childrenGroupsLock = NULL;
jthreadGroup *groups = NULL;
jint numGroups = 0;
jthread *threads = NULL;
jint numThreads = 0;
jvmtiError rc = JVMTI_ERROR_NONE;
PORT_ACCESS_FROM_JAVAVM(vm);

/* Construct the Children Groups array under a lock. */
threadGroupObject = J9_JNI_UNWRAP_REFERENCE(group);
childrenGroupsLock = J9VMJAVALANGTHREADGROUP_CHILDRENGROUPSLOCK(currentThread, threadGroupObject);
childrenGroupsLock = (j9object_t)vmFuncs->objectMonitorEnter(currentThread, childrenGroupsLock);
/* The threadGroupObject has to be reobtained as it might have been GC'ed while waiting for the lock. */
threadGroupObject = J9_JNI_UNWRAP_REFERENCE(group);
if (J9_OBJECT_MONITOR_ENTER_FAILED(childrenGroupsLock)) {
#if defined(J9VM_OPT_CRIU_SUPPORT)
if (J9_OBJECT_MONITOR_CRIU_SINGLE_THREAD_MODE_THROW == (UDATA)childrenGroupsLock) {
vmFuncs->setCRIUSingleThreadModeJVMCRIUException(currentThread, 0, 0);
rc = JVMTI_ERROR_THREAD_SUSPENDED;
} else
#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */
if (J9_OBJECT_MONITOR_OOM == (UDATA)childrenGroupsLock) {
rc = JVMTI_ERROR_OUT_OF_MEMORY;
}
goto done;
}

numGroups = J9VMJAVALANGTHREADGROUP_NUMGROUPS(currentThread, threadGroupObject);
groups = j9mem_allocate_memory(sizeof(jthreadGroup) * numGroups, J9MEM_CATEGORY_JVMTI_ALLOCATE);
if (NULL == groups) {
rc = JVMTI_ERROR_OUT_OF_MEMORY;
} else {
jint i = 0;
j9object_t childrenGroups = (j9object_t)J9VMJAVALANGTHREADGROUP_CHILDRENGROUPS(currentThread, threadGroupObject);

for (i = 0; i < numGroups; ++i) {
j9object_t childGroup = J9JAVAARRAYOFOBJECT_LOAD(currentThread, childrenGroups, i);

groups[i] = (jthreadGroup)vmFuncs->j9jni_createLocalRef((JNIEnv *)currentThread, childGroup);
}
}

vmFuncs->objectMonitorExit(currentThread, childrenGroupsLock);

/* Construct the Children Threads array under a lock. */
threadGroupObject = J9_JNI_UNWRAP_REFERENCE(group);
childrenThreadsLock = J9VMJAVALANGTHREADGROUP_CHILDRENTHREADSLOCK(currentThread, threadGroupObject);
childrenThreadsLock = (j9object_t)vmFuncs->objectMonitorEnter(currentThread, childrenThreadsLock);
/* The threadGroupObject has to be reobtained as it might have been GC'ed while waiting for the lock. */
threadGroupObject = J9_JNI_UNWRAP_REFERENCE(group);
if (J9_OBJECT_MONITOR_ENTER_FAILED(childrenThreadsLock)) {
#if defined(J9VM_OPT_CRIU_SUPPORT)
if (J9_OBJECT_MONITOR_CRIU_SINGLE_THREAD_MODE_THROW == (UDATA)childrenThreadsLock) {
vmFuncs->setCRIUSingleThreadModeJVMCRIUException(currentThread, 0, 0);
rc = JVMTI_ERROR_THREAD_SUSPENDED;
} else
#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */
if (J9_OBJECT_MONITOR_OOM == (UDATA)childrenThreadsLock) {
rc = JVMTI_ERROR_OUT_OF_MEMORY;
}
j9mem_free_memory(groups);
goto done;
}

numThreads = J9VMJAVALANGTHREADGROUP_NUMTHREADS(currentThread, threadGroupObject);
threads = j9mem_allocate_memory(sizeof(jthread) * numThreads, J9MEM_CATEGORY_JVMTI_ALLOCATE);
if (NULL == threads) {
j9mem_free_memory(groups);
rc = JVMTI_ERROR_OUT_OF_MEMORY;
} else {
jint i = 0;
j9object_t childrenThreads = (j9object_t)J9VMJAVALANGTHREADGROUP_CHILDRENTHREADS(currentThread, threadGroupObject);
jint numLiveThreads = 0;

/* Include only live threads in the result */
numLiveThreads = 0;
for (i = 0; i < numThreads; ++i) {
j9object_t thread = J9JAVAARRAYOFOBJECT_LOAD(currentThread, childrenThreads, i);
J9VMThread *targetThread = NULL;

if (JVMTI_ERROR_NONE == getVMThread(currentThread, (jthread)&thread, &targetThread, FALSE, TRUE)) {
threads[numLiveThreads++] = (jthread)vmFuncs->j9jni_createLocalRef((JNIEnv *)currentThread, thread);
releaseVMThread(currentThread, targetThread);
}
}

*rv_thread_count = numLiveThreads;
*rv_threads = threads;
*rv_group_count = numGroups;
*rv_groups = groups;
}

vmFuncs->objectMonitorExit(currentThread, childrenThreadsLock);

done:
return rc;
}
#else /* JAVA_SPEC_VERSION < 19 */
static jvmtiError
getThreadGroupChildrenImpl(J9JavaVM *vm, J9VMThread *currentThread, jobject group,
jint *rv_thread_count,
jthread **rv_threads,
jint *rv_group_count,
jthreadGroup **rv_groups)
{
const J9InternalVMFunctions * const vmFuncs = vm->internalVMFunctions;
j9object_t threadGroupObject = NULL;
jthreadGroup *groups = NULL;
jint nGroups = 0;
jint nWeaks = 0;
jint nGroupsTotal = 0;
jthread *threads = NULL;
jvmtiError rc = JVMTI_ERROR_NONE;
PORT_ACCESS_FROM_JAVAVM(vm);

/* Construct the child groups and threads arrays while locking on the parent group. */
threadGroupObject = J9_JNI_UNWRAP_REFERENCE(group);
threadGroupObject = (j9object_t)vmFuncs->objectMonitorEnter(currentThread, threadGroupObject);
if (J9_OBJECT_MONITOR_ENTER_FAILED(threadGroupObject)) {
#if defined(J9VM_OPT_CRIU_SUPPORT)
if (J9_OBJECT_MONITOR_CRIU_SINGLE_THREAD_MODE_THROW == (UDATA)threadGroupObject) {
vmFuncs->setCRIUSingleThreadModeJVMCRIUException(currentThread, 0, 0);
rc = JVMTI_ERROR_THREAD_SUSPENDED;
} else
#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */
if (J9_OBJECT_MONITOR_OOM == (UDATA)threadGroupObject) {
rc = JVMTI_ERROR_OUT_OF_MEMORY;
}
goto done;
}

nGroups = J9VMJAVALANGTHREADGROUP_NGROUPS(currentThread, threadGroupObject);
nWeaks = J9VMJAVALANGTHREADGROUP_NWEAKS(currentThread, threadGroupObject);
groups = j9mem_allocate_memory(sizeof(jthreadGroup) * (nGroups + nWeaks), J9MEM_CATEGORY_JVMTI_ALLOCATE);
if (NULL == groups) {
rc = JVMTI_ERROR_OUT_OF_MEMORY;
} else {
jint i = 0;
jint j = 0;
j9object_t strongGroups = (j9object_t)J9VMJAVALANGTHREADGROUP_GROUPS(currentThread, threadGroupObject);
j9object_t weakGroupRefs = (j9object_t)J9VMJAVALANGTHREADGROUP_WEAKS(currentThread, threadGroupObject);

for (i = 0; i < nGroups; ++i) {
j9object_t strongGroup = J9JAVAARRAYOFOBJECT_LOAD(currentThread, strongGroups, i);

groups[i] = (jthreadGroup)vmFuncs->j9jni_createLocalRef((JNIEnv *)currentThread, strongGroup);
}

for (j = 0; j < nWeaks; ++j) {
/* Fetch WeakReference at index (weaks[j]). */
j9object_t weakRef = J9JAVAARRAYOFOBJECT_LOAD(currentThread, weakGroupRefs, j);
/* Get the ThreadGroup referent. */
j9object_t weakGroup = vm->memoryManagerFunctions->j9gc_objaccess_referenceGet(currentThread, weakRef);

/* The groups array may have unused space as weakGroup may not be added. */
if (NULL != weakGroup) {
groups[i] = (jthreadGroup)vmFuncs->j9jni_createLocalRef((JNIEnv *)currentThread, weakGroup);
i += 1;
}
}

nGroupsTotal = i;
}

vmFuncs->acquireExclusiveVMAccess(currentThread);

threads = j9mem_allocate_memory(sizeof(jthread) * vm->totalThreadCount, J9MEM_CATEGORY_JVMTI_ALLOCATE);
if (NULL == threads) {
j9mem_free_memory(groups);
rc = JVMTI_ERROR_OUT_OF_MEMORY;
} else {
jthread *currentThreadPtr = threads;
J9VMThread *targetThread = vm->mainThread;
jint nLiveThreads = 0;

do {
j9object_t threadObject = targetThread->carrierThreadObject;

/* Only count live threads. */
if (NULL != threadObject) {
if (J9VMJAVALANGTHREAD_STARTED(currentThread, threadObject)
EricYangIBM marked this conversation as resolved.
Show resolved Hide resolved
&& (NULL != J9VMJAVALANGTHREAD_THREADREF(currentThread, threadObject))
) {
j9object_t threadHolder = J9VMJAVALANGTHREAD_HOLDER(currentThread, threadObject);
if (NULL != threadHolder) {
j9object_t threadGroup = (j9object_t)J9VMJAVALANGTHREADFIELDHOLDER_GROUP(currentThread, threadHolder);
/* The threads array will have unused space since many threads will not belong to the given threadgroup. */
if (threadGroup == threadGroupObject) {
*currentThreadPtr++ = (jthread)vmFuncs->j9jni_createLocalRef((JNIEnv *)currentThread, threadObject);
nLiveThreads += 1;
}
}
}
}
} while ((targetThread = targetThread->linkNext) != vm->mainThread);

*rv_thread_count = nLiveThreads;
*rv_threads = threads;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On return, the jthread* points to a newly allocated array of size*thread_count_ptr

threads array is of size totalThreadCount which is greater than nLiveThreads. Is it fine to return an array of larger size? We allow this in our current implementation; the answer may be "yes".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We return the actual number of threads in the array so it probably is fine

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of allocated memory will be unused if the filter excludes a lot of threads. Filter: include only platform threads which match the thread_group.

*rv_group_count = nGroupsTotal;
*rv_groups = groups;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On return, the jthreadGroup* points to a newly allocated array of size*group_count_ptr

groups array is of size nGroups + nWeaks, which is greater than nGroupsTotal. Is it fine to return an array of larger size? We allow this in our current implementation; the answer may be "yes".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of allocated memory will be unused if a lot of weak references are null.

}

vmFuncs->releaseExclusiveVMAccess(currentThread);
vmFuncs->objectMonitorExit(currentThread, threadGroupObject);

done:
return rc;
}
#endif /* JAVA_SPEC_VERSION < 19 */
Loading