From 97140bc3f19dff43882dc77f9bdb337ab0db067e Mon Sep 17 00:00:00 2001 From: Jack Lu Date: Mon, 28 Nov 2022 15:06:46 -0500 Subject: [PATCH 1/4] Add new internalVMFunctions walkAllStackFrames - Allow walk failure and continue to walk remaining stacks Signed-off-by: Jack Lu --- runtime/oti/j9nonbuilder.h | 3 +- runtime/oti/vm_api.h | 12 ++++++- runtime/vm/ContinuationHelpers.cpp | 58 +++++++++++++++++++++++++++++- runtime/vm/ContinuationHelpers.hpp | 2 +- runtime/vm/intfunc.c | 3 +- 5 files changed, 73 insertions(+), 5 deletions(-) diff --git a/runtime/oti/j9nonbuilder.h b/runtime/oti/j9nonbuilder.h index 53c291b5aa8..5ab19d2444e 100644 --- a/runtime/oti/j9nonbuilder.h +++ b/runtime/oti/j9nonbuilder.h @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 1991, 2022 IBM Corp. and others + * Copyright (c) 1991, 2023 IBM Corp. and others * * This program and the accompanying materials are made available under * the terms of the Eclipse Public License 2.0 which accompanies this @@ -4941,6 +4941,7 @@ typedef struct J9InternalVMFunctions { void (*freeContinuation)(struct J9VMThread *currentThread, j9object_t continuationObject); void (*freeTLS)(struct J9VMThread *currentThread, j9object_t threadObj); UDATA (*walkContinuationStackFrames)(struct J9VMThread *currentThread, struct J9VMContinuation *continuation, J9StackWalkState *walkState); + UDATA (*walkAllStackFrames)(struct J9VMThread *currentThread, J9StackWalkState *walkState); #endif /* JAVA_SPEC_VERSION >= 19 */ } J9InternalVMFunctions; diff --git a/runtime/oti/vm_api.h b/runtime/oti/vm_api.h index 3ee192d399a..19f6dde8a93 100644 --- a/runtime/oti/vm_api.h +++ b/runtime/oti/vm_api.h @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 1991, 2022 IBM Corp. and others + * Copyright (c) 1991, 2023 IBM Corp. and others * * This program and the accompanying materials are made available under * the terms of the Eclipse Public License 2.0 which accompanies this @@ -4367,6 +4367,16 @@ copyFieldsFromContinuation(J9VMThread *currentThread, J9VMThread *vmThread, J9VM */ UDATA walkContinuationStackFrames(J9VMThread *currentThread, J9VMContinuation *continuation, J9StackWalkState *walkState); + +/** + * @brief Walk all stackframes in the VM. + * + * @param currentThread + * @param walkState walkstate holding initial walk parameters to be used in each stackwalk + * @return 0 on success and non-zero on failure + */ +UDATA +walkAllStackFrames(J9VMThread *currentThread, J9StackWalkState *walkState); #endif /* JAVA_SPEC_VERSION >= 19 */ /* ---------------- hookableAsync.c ---------------- */ diff --git a/runtime/vm/ContinuationHelpers.cpp b/runtime/vm/ContinuationHelpers.cpp index 4127f225734..6d97c62a370 100644 --- a/runtime/vm/ContinuationHelpers.cpp +++ b/runtime/vm/ContinuationHelpers.cpp @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022, 2022 IBM Corp. and others + * Copyright (c) 2022, 2023 IBM Corp. and others * * This program and the accompanying materials are made available under * the terms of the Eclipse Public License 2.0 which accompanies this @@ -286,4 +286,60 @@ walkContinuationStackFrames(J9VMThread *currentThread, J9VMContinuation *continu return rc; } + +UDATA +walkAllStackFrames(J9VMThread *currentThread, J9StackWalkState *walkState) +{ + J9JavaVM *vm = currentThread->javaVM; + J9StackWalkState localWalkState = {0}; + UDATA rc = J9_STACKWALK_RC_NONE; + + Assert_VM_true((J9_XACCESS_EXCLUSIVE == vm->exclusiveAccessState) || (J9_XACCESS_EXCLUSIVE == vm->safePointState)); + + /* Walk all vmThread stacks */ + J9VMThread *targetThread = vm->mainThread; + do { + /* Reset localWalkState */ + localWalkState = *walkState; + localWalkState.walkThread = targetThread; + rc = vm->walkStackFrames(currentThread, &localWalkState); + targetThread = targetThread->linkNext; + } while (targetThread != vm->mainThread); + + /* Walk all contination stacks */ + if (NULL != vm->liveVirtualThreadList) { + j9object_t rootNode = J9_JNI_UNWRAP_REFERENCE(vm->liveVirtualThreadList); + j9object_t nextVThread = J9OBJECT_OBJECT_LOAD(currentThread, rootNode, vm->virtualThreadLinkNextOffset); + J9VMContinuation *continuation = NULL; + while (nextVThread != rootNode) { + j9object_t cont = J9VMJAVALANGVIRTUALTHREAD_CONT(currentThread, nextVThread); + continuation = J9VMJDKINTERNALVMCONTINUATION_VMREF(currentThread, cont); + if (NULL != continuation) { + /* Reset localWalkState */ + localWalkState = *walkState; + /* walk live continuation's stack */ + rc = walkContinuationStackFrames(currentThread, continuation, &localWalkState); + } + nextVThread = J9OBJECT_OBJECT_LOAD(currentThread, nextVThread, vm->virtualThreadLinkNextOffset); + } + } + /* Alternate approach to rely on GC Continuation object iterator + GC_ContinuationIterator iter = getContinuationIterator(); + J9VMContinuation *continuation = NULL; + while(NULL != iter) { + continuation = J9VMJDKINTERNALVMCONTINUATION_VMREF(currentThread, nextCont); + if (NULL != continuation) { + // Reset localWalkState + localWalkState = *walkState; + // walk live continuation's stack + rc = walkContinuationStackFrames(currentThread, continuation, &localWalkState); + if (J9_STACKWALK_RC_NONE != rc) { + goto exit; + } + } + iter = iter.next(); + } + */ + return rc; +} } /* extern "C" */ diff --git a/runtime/vm/ContinuationHelpers.hpp b/runtime/vm/ContinuationHelpers.hpp index d9092a6b321..3d06e4d6764 100644 --- a/runtime/vm/ContinuationHelpers.hpp +++ b/runtime/vm/ContinuationHelpers.hpp @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022, 2022 IBM Corp. and others + * Copyright (c) 2022, 2023 IBM Corp. and others * * This program and the accompanying materials are made available under * the terms of the Eclipse Public License 2.0 which accompanies this diff --git a/runtime/vm/intfunc.c b/runtime/vm/intfunc.c index 11f80cd119f..c107665e516 100644 --- a/runtime/vm/intfunc.c +++ b/runtime/vm/intfunc.c @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 1991, 2022 IBM Corp. and others + * Copyright (c) 1991, 2023 IBM Corp. and others * * This program and the accompanying materials are made available under * the terms of the Eclipse Public License 2.0 which accompanies this @@ -432,5 +432,6 @@ J9InternalVMFunctions J9InternalFunctions = { freeContinuation, freeTLS, walkContinuationStackFrames, + walkAllStackFrames, #endif /* JAVA_SPEC_VERSION >= 19 */ }; From 9ef500bab86801f986b8cc71833ac42a29754d93 Mon Sep 17 00:00:00 2001 From: Jack Lu Date: Mon, 28 Nov 2022 21:26:31 -0500 Subject: [PATCH 2/4] Update jitReleaseCodeStackWalk - Use walkAllStackFrames API for standard GC - Custom code handling for realtimeGC path Signed-off-by: Jack Lu --- runtime/compiler/control/HookedByTheJit.cpp | 104 +++++++++++++++++++- runtime/oti/j9nonbuilder.h | 1 + 2 files changed, 103 insertions(+), 2 deletions(-) diff --git a/runtime/compiler/control/HookedByTheJit.cpp b/runtime/compiler/control/HookedByTheJit.cpp index 36119a8111d..b09427c170a 100644 --- a/runtime/compiler/control/HookedByTheJit.cpp +++ b/runtime/compiler/control/HookedByTheJit.cpp @@ -6469,10 +6469,91 @@ static void jitReleaseCodeStackWalk(OMR_VMThread *omrVMThread, condYieldFromGCFu return; // nothing to do + bool isRealTimeGC = TR::Options::getCmdLineOptions()->realTimeGC(); +#if JAVA_SPEC_VERSION >= 19 + J9JavaVM *vm = vmThread->javaVM; + J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions; + if (isRealTimeGC && !TR::Options::getCmdLineOptions()->getOption(TR_DisableIncrementalCCR)) + { + bool yieldHappened = false; + + /* Set inspectingLiveVirtualThreadList to true to prevent any continuation mount/unmount */ + /* Is this monitor enter/wait needed? we should have exclusiveVMAccess here? */ + j9thread_monitor_enter(vm->liveVirtualThreadListMutex); + while (vm->inspectingLiveVirtualThreadList) { + /* Virtual thread list is being inspected, wait. */ + vmFuncs->internalExitVMToJNI(vmThread); + j9thread_monitor_wait(vm->liveVirtualThreadListMutex); + vmFuncs->internalEnterVMFromJNI(vmThread); + } + vm->inspectingLiveVirtualThreadList = true; + j9thread_monitor_exit(vm->liveVirtualThreadListMutex); + + do + { + J9VMThread *thread = vmThread; + yieldHappened = false; + do + { + if ((thread->dropFlags & 0x1) ? false : true) + { + J9StackWalkState walkState; + walkState.flags = J9_STACKWALK_ITERATE_HIDDEN_JIT_FRAMES | J9_STACKWALK_ITERATE_FRAMES | J9_STACKWALK_SKIP_INLINES; + walkState.skipCount = 0; + walkState.frameWalkFunction = jitReleaseCodeStackWalkFrame; + walkState.walkThread = thread; + vm->walkStackFrames(vmThread, &walkState); + thread->dropFlags |= 0x1; + + yieldHappened = condYield(omrVMThread, J9_GC_METRONOME_UTILIZATION_COMPONENT_JIT); + } + + if (!yieldHappened) + thread = thread->linkNext; + } + while ((thread != vmThread) && !yieldHappened); + } + while (yieldHappened); + + if (NULL != vm->liveVirtualThreadList) + { + do + { + yieldHappened = false; + /* Skip the root, which is a dummy virtual thread and global ref. */ + j9object_t walkVirtualThread = J9OBJECT_OBJECT_LOAD(vmThread, *(vm->liveVirtualThreadList), vm->virtualThreadLinkNextOffset); + while ((*(vm->liveVirtualThreadList) != walkVirtualThread) && !yieldHappened) + { + j9object_t contObject = (j9object_t)J9VMJAVALANGVIRTUALTHREAD_CONT(vmThread, walkVirtualThread); + J9VMContinuation *continuation = J9VMJDKINTERNALVMCONTINUATION_VMREF(vmThread, contObject); + if ((continuation->dropFlags & 0x1) ? false : true) + { + J9StackWalkState walkState; + walkState.flags = J9_STACKWALK_ITERATE_HIDDEN_JIT_FRAMES | J9_STACKWALK_ITERATE_FRAMES | J9_STACKWALK_SKIP_INLINES; + walkState.skipCount = 0; + walkState.frameWalkFunction = jitReleaseCodeStackWalkFrame; + vmFuncs->walkContinuationStackFrames(vmThread, continuation, &walkState); + continuation->dropFlags |= 0x1; + yieldHappened = condYield(omrVMThread, J9_GC_METRONOME_UTILIZATION_COMPONENT_JIT); + } + + if (!yieldHappened) + walkVirtualThread = J9OBJECT_OBJECT_LOAD(vmThread, walkVirtualThread, vm->virtualThreadLinkNextOffset); + } + } + while (yieldHappened); + } + } else { + J9StackWalkState walkState; + walkState.flags = J9_STACKWALK_ITERATE_HIDDEN_JIT_FRAMES | J9_STACKWALK_ITERATE_FRAMES | J9_STACKWALK_SKIP_INLINES; + walkState.skipCount = 0; + walkState.frameWalkFunction = jitReleaseCodeStackWalkFrame; + + vmFuncs->walkAllStackFrames(vmThread, &walkState); + } +#else /* JAVA_SPEC_VERSION >= 19 */ bool yieldHappened = false; bool doStackWalkForThread = true; - - bool isRealTimeGC = TR::Options::getCmdLineOptions()->realTimeGC(); do { J9VMThread *thread = vmThread; @@ -6504,6 +6585,7 @@ static void jitReleaseCodeStackWalk(OMR_VMThread *omrVMThread, condYieldFromGCFu while ((thread != vmThread) && !yieldHappened); } while (yieldHappened); +#endif /* JAVA_SPEC_VERSION >= 19 */ TR::CompilationInfo * compInfo = TR::CompilationInfo::get(jitConfig); @@ -6586,6 +6668,24 @@ static void jitReleaseCodeStackWalk(OMR_VMThread *omrVMThread, condYieldFromGCFu thr = thr->linkNext; } while (thr != vmThread); +#if JAVA_SPEC_VERSION >= 19 + if (NULL != vm->liveVirtualThreadList) + { + /* Skip the root, which is a dummy virtual thread and global ref. */ + j9object_t walkVirtualThread = J9OBJECT_OBJECT_LOAD(vmThread, *(vm->liveVirtualThreadList), vm->virtualThreadLinkNextOffset); + while (*(vm->liveVirtualThreadList) != walkVirtualThread) + { + j9object_t contObject = (j9object_t)J9VMJAVALANGVIRTUALTHREAD_CONT(vmThread, walkVirtualThread); + J9VMContinuation *continuation = J9VMJDKINTERNALVMCONTINUATION_VMREF(vmThread, contObject); + continuation->dropFlags &= 0x0; + walkVirtualThread = J9OBJECT_OBJECT_LOAD(vmThread, walkVirtualThread, vm->virtualThreadLinkNextOffset); + } + } + j9thread_monitor_enter(vm->liveVirtualThreadListMutex); + vm->inspectingLiveVirtualThreadList = FALSE; + j9thread_monitor_notify_all(vm->liveVirtualThreadListMutex); + j9thread_monitor_exit(vm->liveVirtualThreadListMutex); +#endif /* JAVA_SPEC_VERSION >= 19 */ } diff --git a/runtime/oti/j9nonbuilder.h b/runtime/oti/j9nonbuilder.h index 5ab19d2444e..6116555b087 100644 --- a/runtime/oti/j9nonbuilder.h +++ b/runtime/oti/j9nonbuilder.h @@ -5031,6 +5031,7 @@ typedef struct J9VMContinuation { struct J9I2JState i2jState; struct J9VMEntryLocalStorage* oldEntryLocalStorage; volatile UDATA state; /* it's a bit-wise struct of CarrierThread ID and ConcurrentlyScanned flag */ + UDATA dropFlags; } J9VMContinuation; #endif /* JAVA_SPEC_VERSION >= 19 */ From 8730014406f9c8cacdf5bfe1a8d2448fb7374241 Mon Sep 17 00:00:00 2001 From: Jack Lu Date: Tue, 29 Nov 2022 16:40:22 -0500 Subject: [PATCH 3/4] WalkAllStack without locking vthread mount/unmount for realtimeGC Signed-off-by: Jack Lu --- runtime/compiler/control/HookedByTheJit.cpp | 35 ++++++++++----------- runtime/vm/ContinuationHelpers.hpp | 1 + 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/runtime/compiler/control/HookedByTheJit.cpp b/runtime/compiler/control/HookedByTheJit.cpp index b09427c170a..ea8c42537dd 100644 --- a/runtime/compiler/control/HookedByTheJit.cpp +++ b/runtime/compiler/control/HookedByTheJit.cpp @@ -6476,19 +6476,6 @@ static void jitReleaseCodeStackWalk(OMR_VMThread *omrVMThread, condYieldFromGCFu if (isRealTimeGC && !TR::Options::getCmdLineOptions()->getOption(TR_DisableIncrementalCCR)) { bool yieldHappened = false; - - /* Set inspectingLiveVirtualThreadList to true to prevent any continuation mount/unmount */ - /* Is this monitor enter/wait needed? we should have exclusiveVMAccess here? */ - j9thread_monitor_enter(vm->liveVirtualThreadListMutex); - while (vm->inspectingLiveVirtualThreadList) { - /* Virtual thread list is being inspected, wait. */ - vmFuncs->internalExitVMToJNI(vmThread); - j9thread_monitor_wait(vm->liveVirtualThreadListMutex); - vmFuncs->internalEnterVMFromJNI(vmThread); - } - vm->inspectingLiveVirtualThreadList = true; - j9thread_monitor_exit(vm->liveVirtualThreadListMutex); - do { J9VMThread *thread = vmThread; @@ -6504,10 +6491,21 @@ static void jitReleaseCodeStackWalk(OMR_VMThread *omrVMThread, condYieldFromGCFu walkState.walkThread = thread; vm->walkStackFrames(vmThread, &walkState); thread->dropFlags |= 0x1; + } - yieldHappened = condYield(omrVMThread, J9_GC_METRONOME_UTILIZATION_COMPONENT_JIT); + if ((NULL!= thread->currentContinuation) && ((thread->currentContinuation->dropFlags & 0x1) ? false : true)) + /* If a continuation is mounted, always walk the continuation as that represent the CarrierThread */ + { + J9StackWalkState walkState; + walkState.flags = J9_STACKWALK_ITERATE_HIDDEN_JIT_FRAMES | J9_STACKWALK_ITERATE_FRAMES | J9_STACKWALK_SKIP_INLINES; + walkState.skipCount = 0; + walkState.frameWalkFunction = jitReleaseCodeStackWalkFrame; + vmFuncs->walkContinuationStackFrames(vmThread, thread->currentContinuation, &walkState); + thread->currentContinuation->dropFlags |= 0x1; } + yieldHappened = condYield(omrVMThread, J9_GC_METRONOME_UTILIZATION_COMPONENT_JIT); + if (!yieldHappened) thread = thread->linkNext; } @@ -6665,6 +6663,11 @@ static void jitReleaseCodeStackWalk(OMR_VMThread *omrVMThread, condYieldFromGCFu do { thr->dropFlags &=0x0; +#if JAVA_SPEC_VERSION >= 19 + if (NULL != thr->currentContinuation) { + thr->currentContinuation->dropFlags &=0x0; + } +#endif /* JAVA_SPEC_VERSION >= 19 */ thr = thr->linkNext; } while (thr != vmThread); @@ -6681,10 +6684,6 @@ static void jitReleaseCodeStackWalk(OMR_VMThread *omrVMThread, condYieldFromGCFu walkVirtualThread = J9OBJECT_OBJECT_LOAD(vmThread, walkVirtualThread, vm->virtualThreadLinkNextOffset); } } - j9thread_monitor_enter(vm->liveVirtualThreadListMutex); - vm->inspectingLiveVirtualThreadList = FALSE; - j9thread_monitor_notify_all(vm->liveVirtualThreadListMutex); - j9thread_monitor_exit(vm->liveVirtualThreadListMutex); #endif /* JAVA_SPEC_VERSION >= 19 */ } diff --git a/runtime/vm/ContinuationHelpers.hpp b/runtime/vm/ContinuationHelpers.hpp index 3d06e4d6764..40d86e6812b 100644 --- a/runtime/vm/ContinuationHelpers.hpp +++ b/runtime/vm/ContinuationHelpers.hpp @@ -69,6 +69,7 @@ class VM_ContinuationHelpers { SWAP_MEMBER(stackObject, J9JavaStack*, vmThread, continuation); SWAP_MEMBER(decompilationStack, J9JITDecompilationInfo*, vmThread, continuation); SWAP_MEMBER(j2iFrame, UDATA*, vmThread, continuation); + SWAP_MEMBER(dropFlags, UDATA, vmThread, continuation); J9VMEntryLocalStorage *threadELS = vmThread->entryLocalStorage; /* Swap the JIT GPR registers data referenced by ELS */ From 30cb4d59470a8b371aabe1160ff2c9f9b11a2d8a Mon Sep 17 00:00:00 2001 From: Jack Lu Date: Tue, 24 Jan 2023 10:37:03 -0800 Subject: [PATCH 4/4] Walk currentContinuation with vmthread list during walkAllStackFrames This is to ensure virtual/carrier threads in mount/unmount transition will be found during the walk process. Signed-off-by: Jack Lu --- runtime/vm/ContinuationHelpers.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/runtime/vm/ContinuationHelpers.cpp b/runtime/vm/ContinuationHelpers.cpp index 6d97c62a370..a0bb1bf0a87 100644 --- a/runtime/vm/ContinuationHelpers.cpp +++ b/runtime/vm/ContinuationHelpers.cpp @@ -303,6 +303,11 @@ walkAllStackFrames(J9VMThread *currentThread, J9StackWalkState *walkState) localWalkState = *walkState; localWalkState.walkThread = targetThread; rc = vm->walkStackFrames(currentThread, &localWalkState); + + if (NULL != targetThread->currentContinuation) { + localWalkState = *walkState; + walkContinuationStackFrames(currentThread, targetThread->currentContinuation, &localWalkState); + } targetThread = targetThread->linkNext; } while (targetThread != vm->mainThread);