diff --git a/runtime/gc_base/ReferenceChainWalker.cpp b/runtime/gc_base/ReferenceChainWalker.cpp index c2cec652c3a..472e68ec911 100644 --- a/runtime/gc_base/ReferenceChainWalker.cpp +++ b/runtime/gc_base/ReferenceChainWalker.cpp @@ -36,6 +36,7 @@ #include "Forge.hpp" #include "GCExtensions.hpp" #include "Heap.hpp" +#include "HeapIteratorAPI.h" #include "HeapRegionIterator.hpp" #include "MixedObjectDeclarationOrderIterator.hpp" #include "ModronAssertions.h" @@ -44,6 +45,9 @@ #include "PointerArrayIterator.hpp" #include "SlotObject.hpp" #include "VMThreadIterator.hpp" +#if JAVA_SPEC_VERSION >= 19 +#include "ContinuationHelpers.hpp" +#endif /* JAVA_SPEC_VERSION >= 19 */ class GC_HashTableIterator; class GC_JVMTIObjectTagTableIterator; @@ -76,6 +80,7 @@ j9gc_ext_reachable_objects_do( uintptr_t walkFlags) { MM_EnvironmentBase *env = MM_EnvironmentBase::getEnvironment(vmThread->omrVMThread); + Assert_MM_mustHaveExclusiveVMAccess(env->getOmrVMThread()); /* Make sure the heap is walkable (flush TLH's, secure heap integrity) */ vmThread->javaVM->memoryManagerFunctions->j9gc_flush_caches_for_walk(vmThread->javaVM); @@ -87,6 +92,9 @@ j9gc_ext_reachable_objects_do( #endif /* J9VM_OPT_JVMTI */ referenceChainWalker.setTrackVisibleStackFrameDepth(0 != (walkFlags & J9_MU_WALK_TRACK_VISIBLE_FRAME_DEPTH)); referenceChainWalker.setPreindexInterfaceFields(0 != (walkFlags & J9_MU_WALK_PREINDEX_INTERFACE_FIELDS)); +#if JAVA_SPEC_VERSION >= 19 + referenceChainWalker.includeVThreadObject(); +#endif /* JAVA_SPEC_VERSION >= 19 */ /* walker configuration complete. Scan objects... */ referenceChainWalker.scanReachableObjects(env); referenceChainWalker.tearDown(env); @@ -112,6 +120,7 @@ j9gc_ext_reachable_from_object_do( uintptr_t walkFlags) { MM_EnvironmentBase *env = MM_EnvironmentBase::getEnvironment(vmThread->omrVMThread); + Assert_MM_mustHaveExclusiveVMAccess(env->getOmrVMThread()); /* Make sure the heap is walkable (flush TLH's, secure heap integrity) */ vmThread->javaVM->memoryManagerFunctions->j9gc_flush_caches_for_walk(vmThread->javaVM); @@ -119,6 +128,9 @@ j9gc_ext_reachable_from_object_do( MM_ReferenceChainWalker referenceChainWalker(env, TEMP_RCW_STACK_SIZE, userCallback, userData); if (referenceChainWalker.initialize(env)) { referenceChainWalker.setPreindexInterfaceFields(0 != (walkFlags & J9_MU_WALK_PREINDEX_INTERFACE_FIELDS)); +#if JAVA_SPEC_VERSION >= 19 + referenceChainWalker.includeVThreadObject(); +#endif /* JAVA_SPEC_VERSION >= 19 */ /* walker configuration complete. Scan objects... */ referenceChainWalker.scanReachableFromObject(env, objectPtr); referenceChainWalker.tearDown(env); @@ -400,7 +412,11 @@ MM_ReferenceChainWalker::scanContinuationNativeSlots(J9Object *objectPtr) if (MM_GCExtensions::needScanStacksForContinuationObject(currentThread, objectPtr, isConcurrentGC, isGlobalGC, beingMounted)) { StackIteratorData localData; localData.rootScanner = this; - +#if JAVA_SPEC_VERSION >= 19 + if (_includeVThreadObject) { + _vThreadObject = VM_ContinuationHelpers::getThreadObjectForContinuation(currentThread, NULL, objectPtr); + } +#endif /* JAVA_SPEC_VERSION >= 19 */ GC_VMThreadStackSlotIterator::scanContinuationSlots(currentThread, objectPtr, (void *)&localData, stackSlotIteratorForReferenceChainWalker, false, _trackVisibleStackFrameDepth); } } @@ -645,10 +661,17 @@ MM_ReferenceChainWalker::doStackSlot(J9Object **slotPtr, void *walkState, const /* Only report heap objects */ if (isHeapObject(slotValue) && !_heap->objectIsInGap(slotValue)) { +#if JAVA_SPEC_VERSION >= 19 + if (_includeVThreadObject && (NULL == ((J9StackWalkState *)walkState)->walkThread->threadObject)) { + /* Fill in the virtual thread object for jvmtiFollowReferences calls. */ + ((J9StackWalkState *)walkState)->walkThread->threadObject = _vThreadObject; + } +#endif /* JAVA_SPEC_VERSION >= 19 */ + J9MM_StackSlotDescriptor stackSlotDescriptor = {((J9StackWalkState *)walkState)->walkThread, (J9StackWalkState *)walkState}; if (J9_STACKWALK_SLOT_TYPE_JNI_LOCAL == ((J9StackWalkState *)walkState)->slotType) { - doSlot(slotPtr, J9GC_ROOT_TYPE_JNI_LOCAL, -1, NULL); + doSlot(slotPtr, J9GC_ROOT_TYPE_JNI_LOCAL, -1, (J9Object *)&stackSlotDescriptor); } else { - doSlot(slotPtr, J9GC_ROOT_TYPE_STACK_SLOT, -1, (J9Object *)walkState); + doSlot(slotPtr, J9GC_ROOT_TYPE_STACK_SLOT, -1, (J9Object *)&stackSlotDescriptor); } } } @@ -676,9 +699,11 @@ MM_ReferenceChainWalker::doVMThreadSlot(J9Object **slotPtr, GC_VMThreadIterator case vmthreaditerator_state_slots: doSlot(slotPtr, J9GC_ROOT_TYPE_THREAD_SLOT, -1, NULL); break; - case vmthreaditerator_state_jni_slots: - doSlot(slotPtr, J9GC_ROOT_TYPE_JNI_LOCAL, -1, NULL); + case vmthreaditerator_state_jni_slots: { + J9MM_StackSlotDescriptor stackSlotDescriptor = {vmThreadIterator->getVMThread(), NULL}; + doSlot(slotPtr, J9GC_ROOT_TYPE_JNI_LOCAL, -1, (J9Object *)&stackSlotDescriptor); break; + } #if defined(J9VM_INTERP_HOT_CODE_REPLACEMENT) case vmthreaditerator_state_monitor_records: if (isHeapObject(slotValue) && !_heap->objectIsInGap(slotValue)) { diff --git a/runtime/gc_base/ReferenceChainWalker.hpp b/runtime/gc_base/ReferenceChainWalker.hpp index 6a9e2fece0f..dd41009d095 100644 --- a/runtime/gc_base/ReferenceChainWalker.hpp +++ b/runtime/gc_base/ReferenceChainWalker.hpp @@ -74,11 +74,16 @@ class MM_ReferenceChainWalker : public MM_RootScanner bool _isProcessingOverflow; /**< Set when the queue is currently processing the overflow */ bool _isTerminating; /**< Set when no more callbacks should be queued */ bool _shouldPreindexInterfaceFields; /**< if true, indexes interface fields of the class being visited before class and superclass fields, otherwise, returns them in the order they appear in an object instance (CMVC 142897) */ +#if JAVA_SPEC_VERSION >= 19 + bool _includeVThreadObject; /**< Set when VirtualThread object is needed by callback function */ + J9Object *_vThreadObject; /**< Cached object ref of VirtualThread object */ +#endif /* JAVA_SPEC_VERSION >= 19 */ MM_ReferenceChainWalkerMarkMap *_markMap; /**< Mark Map created for Reference Chain Walker */ MM_Heap *_heap; /**< Cached pointer to the heap */ void *_heapBase; /**< Cached value of the heap base */ void *_heapTop; /**< Cached value of the heap top */ + void clearQueue(); void pushObject(J9Object *obj); J9Object *popObject(); @@ -235,6 +240,10 @@ class MM_ReferenceChainWalker : public MM_RootScanner _isProcessingOverflow(false), _isTerminating(false), _shouldPreindexInterfaceFields(true), /* default to behaviour required for Java6/heap11 */ +#if JAVA_SPEC_VERSION >= 19 + _includeVThreadObject(false), + _vThreadObject(NULL), +#endif /* JAVA_SPEC_VERSION >= 19 */ _markMap(NULL), _heap(NULL), _heapBase(NULL), @@ -259,6 +268,10 @@ class MM_ReferenceChainWalker : public MM_RootScanner completeScan(); } +#if JAVA_SPEC_VERSION >= 19 + void includeVThreadObject() { _includeVThreadObject = true; } +#endif /* JAVA_SPEC_VERSION >= 19 */ + /** * Added to support bi-modal interface indexing in JVMTI (CMVC 142897). * Detail: heap10 requires no pre-indexing in order to preserve Java5 behaviour but heap11 requires pre-indexing to pass a Java6 JCK diff --git a/runtime/gc_include/HeapIteratorAPI.h b/runtime/gc_include/HeapIteratorAPI.h index e478757602f..4246eef6a55 100644 --- a/runtime/gc_include/HeapIteratorAPI.h +++ b/runtime/gc_include/HeapIteratorAPI.h @@ -164,6 +164,11 @@ typedef struct J9MM_HeapRootSlotDescriptor { UDATA slotReachability; /**< Reachability of slot */ } J9MM_HeapRootSlotDescriptor; +typedef struct J9MM_StackSlotDescriptor { + J9VMThread *vmThread; /**< Thread containing the stack slot */ + J9StackWalkState *walkState; /**< WalkState that found the slot, or null if found directly in thread local tables */ +} J9MM_StackSlotDescriptor; + typedef jvmtiIterationControl (*rootIteratorCallBackFunc)(void* ptr, J9MM_HeapRootSlotDescriptor *rootDesc, void *userData); /** diff --git a/runtime/jvmti/jvmtiHeap.c b/runtime/jvmti/jvmtiHeap.c index 62d86f622f6..b4632c7a101 100644 --- a/runtime/jvmti/jvmtiHeap.c +++ b/runtime/jvmti/jvmtiHeap.c @@ -118,7 +118,7 @@ static jvmtiError getArrayPrimitiveElements(J9JVMTIHeapData * iteratorData, jvmt static jvmtiIterationControl followReferencesCallback(j9object_t * slotPtr, j9object_t referrer, void *userData, IDATA type, IDATA referrerIndex, IDATA wasReportedBefore); static void mapEventType(J9JVMTIHeapData * data, IDATA type, jint index, j9object_t referrer, j9object_t object); static void jvmtiFollowRefs_getTags(J9JVMTIHeapData * iteratorData, j9object_t referrer, j9object_t object); -static UDATA jvmtiHeapFollowRefs_getStackData(J9JVMTIHeapData * iteratorData, J9StackWalkState *walkState); +static UDATA jvmtiHeapFollowRefs_getStackData(J9JVMTIHeapData * iteratorData, J9MM_StackSlotDescriptor *stackSlotDescriptor); static IDATA heapReferenceFilter(J9JVMTIHeapData * iteratorData); @@ -756,7 +756,7 @@ wrap_heapReferenceCallback(J9JavaVM * vm, J9JVMTIHeapData * iteratorData) case JVMTI_HEAP_REFERENCE_JNI_LOCAL: if (iteratorData->referrer) { /* fill in the stack local and jni local data */ - if (jvmtiHeapFollowRefs_getStackData(iteratorData, (J9StackWalkState *) iteratorData->referrer) == 0) + if (jvmtiHeapFollowRefs_getStackData(iteratorData, (J9MM_StackSlotDescriptor *) iteratorData->referrer) == 0) return JVMTI_ITERATION_IGNORE; } else { /* No reference info available for heap roots, slime some bogus values @@ -840,20 +840,26 @@ wrap_heapReferenceCallback(J9JavaVM * vm, J9JVMTIHeapData * iteratorData) * */ static UDATA -jvmtiHeapFollowRefs_getStackData(J9JVMTIHeapData * iteratorData, J9StackWalkState *walkState) +jvmtiHeapFollowRefs_getStackData(J9JVMTIHeapData *iteratorData, J9MM_StackSlotDescriptor *stackSlotDescriptor) { J9JVMTIHeapEvent * e = &iteratorData->event; - jint slot = (jint) walkState->slotIndex; - jint depth = (jint) walkState->framesWalked; - J9Method * ramMethod = walkState->method; - jmethodID method; - J9JVMTIObjectTag search; - J9JVMTIObjectTag * result; - jlong threadID; - - - /* Convert internal slot type to JVMTI type */ - switch (walkState->slotType) { + jint slot = -1; + jint depth = -1; + J9Method *ramMethod = NULL; + jmethodID method = (jmethodID) -1; + J9JVMTIObjectTag search = {NULL, 0}; + J9JVMTIObjectTag *result = NULL; + jlong threadID = 0; + J9StackWalkState *walkState = stackSlotDescriptor->walkState; + + if (NULL != walkState) { + slot = (jint) walkState->slotIndex; + depth = (jint) walkState->framesWalked; + ramMethod = walkState->method; + + /* Convert internal slot type to JVMTI type. */ + + switch (walkState->slotType) { case J9_STACKWALK_SLOT_TYPE_JNI_LOCAL: e->refKind = JVMTI_HEAP_REFERENCE_JNI_LOCAL; break; @@ -861,35 +867,35 @@ jvmtiHeapFollowRefs_getStackData(J9JVMTIHeapData * iteratorData, J9StackWalkStat e->refKind = JVMTI_HEAP_REFERENCE_STACK_LOCAL; break; default: - /* Do not callback with stack slot types other then JNI or STACK Local */ + /* Do not callback with stack slot types other then JNI or STACK Local. */ return 0; - } + } - /* If there's no method, set slot, method and depth to -1 */ + /* If there's no method, slot, method and depth are set to -1 by default. */ - if (ramMethod == NULL) { - slot = -1; - depth = -1; - method = (jmethodID) -1; - } else { - /* Cheating here - should be current thread, but the walk thread will do */ - method = getCurrentMethodID(walkState->walkThread, ramMethod); - if (method == NULL) { - slot = -1; - depth = -1; - method = (jmethodID) -1; + if (NULL != ramMethod) { + /* Cheating here - should be current thread, but the walk thread will do. */ + method = getCurrentMethodID(walkState->walkThread, ramMethod); + if (NULL == method) { + method = (jmethodID) -1; + } } } /* Find thread tag */ - search.ref = (j9object_t ) walkState->walkThread->threadObject; - result = hashTableFind(iteratorData->env->objectTagTable, &search); + search.ref = (j9object_t)stackSlotDescriptor->vmThread->threadObject; - /* Figure out the Thread ID */ + if (NULL != search.ref) { + result = hashTableFind(iteratorData->env->objectTagTable, &search); - threadID = J9VMJAVALANGTHREAD_TID(iteratorData->currentThread, walkState->walkThread->threadObject); - + /* Retrieve the Thread ID. */ + threadID = J9VMJAVALANGTHREAD_TID(iteratorData->currentThread, stackSlotDescriptor->vmThread->threadObject); + } else { + /* Set 0 for tag/ID since ref to threadObject can be lost while walking a continuation. */ + result = NULL; + threadID = 0; + } switch (e->refKind) { case JVMTI_HEAP_REFERENCE_STACK_LOCAL: @@ -897,7 +903,7 @@ jvmtiHeapFollowRefs_getStackData(J9JVMTIHeapData * iteratorData, J9StackWalkStat e->refInfo.stack_local.thread_id = threadID; e->refInfo.stack_local.depth = depth; e->refInfo.stack_local.method = method; - e->refInfo.stack_local.location = (jlocation) walkState->bytecodePCOffset; + e->refInfo.stack_local.location = (jlocation) ((walkState) ? walkState->bytecodePCOffset : 0); e->refInfo.stack_local.slot = slot; break; @@ -1096,7 +1102,7 @@ mapEventType(J9JVMTIHeapData * data, IDATA type, jint index, j9object_t referrer break; case J9GC_ROOT_TYPE_JNI_LOCAL: - event->type = J9JVMTI_HEAP_EVENT_ROOT; + event->type = J9JVMTI_HEAP_EVENT_STACK; event->refKind = JVMTI_HEAP_REFERENCE_JNI_LOCAL; event->hasRefInfo = TRUE; break; diff --git a/runtime/jvmti/jvmtiHeap10.c b/runtime/jvmti/jvmtiHeap10.c index 45299b3c305..dac04d2e781 100644 --- a/runtime/jvmti/jvmtiHeap10.c +++ b/runtime/jvmti/jvmtiHeap10.c @@ -59,7 +59,7 @@ typedef struct J9JVMTIHeapEvent { } J9JVMTIHeapEvent; static jvmtiIterationControl processHeapRoot (J9JVMTIObjectIteratorData * data, J9JVMTIObjectTag * entry, jlong size, jlong classTag, jvmtiHeapRootKind kind); -static jvmtiIterationControl processStackRoot (J9JVMTIObjectIteratorData * data, J9JVMTIObjectTag * entry, jlong size, jlong classTag, J9StackWalkState * walkState); +static jvmtiIterationControl processStackRoot (J9JVMTIObjectIteratorData * data, J9JVMTIObjectTag * entry, jlong size, jlong classTag, jvmtiHeapRootKind kind, J9MM_StackSlotDescriptor * walkState); static jvmtiIterationControl wrapHeapIterationCallback(J9JavaVM * vm, J9MM_IterateObjectDescriptor *objectDesc, void * userData); static J9JVMTIHeapEvent mapEventType (J9VMThread *vmThread, J9JVMTIObjectIteratorData * data, IDATA type, IDATA index, j9object_t referrer, j9object_t object); static jvmtiIterationControl wrapObjectIterationCallback (j9object_t *slotPtr, j9object_t referrer, void *userData, IDATA type, IDATA referrerIndex, IDATA wasReportedBefore); @@ -309,7 +309,7 @@ mapEventType(J9VMThread *vmThread, J9JVMTIObjectIteratorData * data, IDATA type, break; case J9GC_ROOT_TYPE_JNI_LOCAL: - event.type = J9JVMTI_HEAP_EVENT_ROOT; + event.type = J9JVMTI_HEAP_EVENT_STACK; event.rootKind = JVMTI_HEAP_ROOT_JNI_LOCAL; break; @@ -462,19 +462,24 @@ processObjectRef(J9JVMTIObjectIteratorData * data, J9JVMTIObjectTag * entry, jlo static jvmtiIterationControl -processStackRoot(J9JVMTIObjectIteratorData * data, J9JVMTIObjectTag * entry, jlong size, jlong classTag, J9StackWalkState * walkState) +processStackRoot(J9JVMTIObjectIteratorData *data, J9JVMTIObjectTag *entry, jlong size, jlong classTag, jvmtiHeapRootKind kind, J9MM_StackSlotDescriptor *stackSlotDescriptor) { - jvmtiHeapRootKind kind; - jint slot = (jint) walkState->slotIndex; - jint depth = (jint) walkState->framesWalked; - J9Method * ramMethod = walkState->method; - jmethodID method; - J9JVMTIObjectTag search; - J9JVMTIObjectTag * result; - - /* Convert internal slot type to JVMTI type */ - - switch (walkState->slotType) { + jint slot = -1; + jint depth = -1; + J9Method *ramMethod = NULL; + jmethodID method = (jmethodID) -1; + J9JVMTIObjectTag search = {NULL, 0}; + J9JVMTIObjectTag *result = NULL; + + if (NULL != stackSlotDescriptor->walkState) { + J9StackWalkState *walkState = stackSlotDescriptor->walkState; + slot = (jint) walkState->slotIndex; + depth = (jint) walkState->framesWalked; + ramMethod = walkState->method; + + /* Convert internal slot type to JVMTI type. */ + + switch (walkState->slotType) { case J9_STACKWALK_SLOT_TYPE_JNI_LOCAL: kind = JVMTI_HEAP_ROOT_JNI_LOCAL; break; @@ -485,27 +490,21 @@ processStackRoot(J9JVMTIObjectIteratorData * data, J9JVMTIObjectTag * entry, jlo kind = JVMTI_HEAP_ROOT_JNI_LOCAL; ramMethod = NULL; break; - } + } - /* If there's no method, set slot, method and depth to -1 */ - - if (ramMethod == NULL) { - slot = -1; - depth = -1; - method = (jmethodID) -1; - } else { - /* Cheating here - should be current thread, but the walk thread will do */ - method = getCurrentMethodID(walkState->walkThread, ramMethod); - if (method == NULL) { - slot = -1; - depth = -1; - method = (jmethodID) -1; + /* If there's no method, slot, method and depth are set to -1 by default. */ + if (NULL != ramMethod) { + /* Cheating here - should be current thread, but the walk thread will do. */ + method = getCurrentMethodID(walkState->walkThread, ramMethod); + if (NULL == method) { + method = (jmethodID) -1; + } } } /* Find thread tag */ - search.ref = (j9object_t) walkState->walkThread->threadObject; + search.ref = (j9object_t)stackSlotDescriptor->vmThread->threadObject; result = hashTableFind(data->env->objectTagTable, &search); /* Call the callback */ @@ -639,7 +638,7 @@ wrapObjectIterationCallback(j9object_t *slotPtr, j9object_t referrer, void *user returnCode = processHeapRoot(iteratorData, result, objectSize, classTag, event.rootKind); break; case J9JVMTI_HEAP_EVENT_STACK: - returnCode = processStackRoot(iteratorData, result, objectSize, classTag, (J9StackWalkState *) referrer); + returnCode = processStackRoot(iteratorData, result, objectSize, classTag, event.rootKind, (J9MM_StackSlotDescriptor *)referrer); break; case J9JVMTI_HEAP_EVENT_OBJECT: returnCode = processObjectRef(iteratorData, result, objectSize, classTag, event.refKind, referrerTag, (jint)event.index);