diff --git a/runtime/jvmti/j9jvmti.tdf b/runtime/jvmti/j9jvmti.tdf
index 6978d635f60..b0914d370c0 100644
--- a/runtime/jvmti/j9jvmti.tdf
+++ b/runtime/jvmti/j9jvmti.tdf
@@ -683,3 +683,18 @@ TraceEntry=Trc_JVMTI_jvmtiHookVMCheckpoint_Entry Overhead=1 Level=2 Noenv Templa
TraceExit=Trc_JVMTI_jvmtiHookVMCheckpoint_Exit Overhead=1 Level=2 Noenv Template="HookVMCheckpoint"
TraceEntry=Trc_JVMTI_jvmtiHookVMRestore_Entry Overhead=1 Level=2 Noenv Template="HookVMRestore"
TraceExit=Trc_JVMTI_jvmtiHookVMRestore_Exit Overhead=1 Level=2 Noenv Template="HookVMRestore"
+
+TraceEvent=Trc_JVMTI_jvmtiAddCapabilities_no_capability Overhead=1 Level=1 Template="jvmtiAddCapabilities() failed: ~(((U_8 *) &potentialCapabilities)[%zu] = %x, result (%x)"
+TraceEvent=Trc_JVMTI_jvmtiAddCapabilities_not_onload Overhead=1 Level=1 Template="jvmtiAddCapabilities() failed: not JVMTI_PHASE_ONLOAD"
+TraceEvent=Trc_JVMTI_jvmtiAddCapabilities_object_alloc_set_already Overhead=1 Level=1 Template="jvmtiAddCapabilities() failed: J9JVMTI_FLAG_SAMPLED_OBJECT_ALLOC_ENABLED was set"
+TraceEvent=Trc_JVMTI_jvmtiAddCapabilities_mapCapabilitiesToEvents_failed Overhead=1 Level=1 Template="jvmtiAddCapabilities() failed: mapCapabilitiesToEvents()"
+TraceEvent=Trc_JVMTI_jvmtiAddCapabilities_hookNonEventCapabilities_failed Overhead=1 Level=1 Template="jvmtiAddCapabilities() failed: hookNonEventCapabilities()"
+
+TraceEvent=Trc_JVMTI_criuAddCapabilities_invoked Noenv Overhead=1 Level=3 Template="CRIU adds capabilities before checkpoint"
+TraceEvent=Trc_JVMTI_createAgentLibraryWithOption_OOM Noenv Overhead=1 Level=3 Template="createAgentLibraryWithOption j9mem_allocate_memory OOM"
+TraceEvent=Trc_JVMTI_createAgentLibraryWithOption_Xrunjdwp_result Noenv Overhead=1 Level=3 Template="createAgentLibraryWithOption Xrunjdwp optionsPtr (%s) optionsLengthTmp (%zu) agentLibrary (%p) result (%d)"
+TraceEvent=Trc_JVMTI_createAgentLibraryWithOption_agentlib_result Noenv Overhead=1 Level=3 Template="createAgentLibraryWithOption optionsPtr (%s) libraryLength (%zu) options (%s) optionsLength (%zu) agentLibrary (%p) result (%d)"
+TraceEntry=Trc_JVMTI_jvmtiHookVMRestoreCRIUInit_Entry Overhead=1 Level=3 Noenv Template="jvmtiHookVMRestoreCRIUInit"
+TraceExit=Trc_JVMTI_jvmtiHookVMRestoreCRIUInit_Exit Overhead=1 Level=3 Noenv Template="jvmtiHookVMRestoreCRIUInit"
+TraceEntry=Trc_JVMTI_jvmtiHookVMRestoreStartAgent_Entry Overhead=1 Level=3 Noenv Template="jvmtiHookVMRestoreStartAgent"
+TraceExit=Trc_JVMTI_jvmtiHookVMRestoreStartAgent_Exit Overhead=1 Level=3 Noenv Template="jvmtiHookVMRestoreStartAgent"
diff --git a/runtime/jvmti/jvmtiCapability.c b/runtime/jvmti/jvmtiCapability.c
index 204f920958a..3de11be2643 100644
--- a/runtime/jvmti/jvmtiCapability.c
+++ b/runtime/jvmti/jvmtiCapability.c
@@ -364,7 +364,9 @@ jvmtiAddCapabilities(jvmtiEnv* env,
/* Make sure all of the requested capabilities are available */
- if ((byte & ~(((U_8 *) &potentialCapabilities)[i])) != 0) {
+ if (0 != (byte & ~(((U_8 *) &potentialCapabilities)[i]))) {
+ Trc_JVMTI_jvmtiAddCapabilities_no_capability(currentThread,
+ i, ((U_8 *) &potentialCapabilities)[i], (byte & ~(((U_8 *) &potentialCapabilities)[i])));
goto fail;
}
@@ -382,6 +384,7 @@ jvmtiAddCapabilities(jvmtiEnv* env,
vm->requiredDebugAttributes |= J9VM_DEBUG_ATTRIBUTE_MAINTAIN_ORIGINAL_METHOD_ORDER;
} else {
rc = JVMTI_ERROR_NOT_AVAILABLE;
+ Trc_JVMTI_jvmtiAddCapabilities_not_onload(currentThread);
goto fail;
}
}
@@ -391,6 +394,7 @@ jvmtiAddCapabilities(jvmtiEnv* env,
if (newCapabilities.can_generate_sampled_object_alloc_events) {
if (J9_ARE_ANY_BITS_SET(jvmtiData->flags, J9JVMTI_FLAG_SAMPLED_OBJECT_ALLOC_ENABLED)) {
rc = JVMTI_ERROR_NOT_AVAILABLE;
+ Trc_JVMTI_jvmtiAddCapabilities_object_alloc_set_already(currentThread);
goto fail;
}
@@ -411,13 +415,15 @@ jvmtiAddCapabilities(jvmtiEnv* env,
/* Reserve hooks for any events now allowed by the new capabilities */
- if (mapCapabilitiesToEvents(j9env, &newCapabilities, reserveEvent) != 0) {
+ if (0 != mapCapabilitiesToEvents(j9env, &newCapabilities, reserveEvent)) {
+ Trc_JVMTI_jvmtiAddCapabilities_mapCapabilitiesToEvents_failed(currentThread);
goto fail;
}
/* Handle non-event hooks */
- if (hookNonEventCapabilities(j9env, &newCapabilities) != 0) {
+ if (0 != hookNonEventCapabilities(j9env, &newCapabilities)) {
+ Trc_JVMTI_jvmtiAddCapabilities_hookNonEventCapabilities_failed(currentThread);
goto fail;
}
@@ -540,55 +546,82 @@ mapCapabilitiesToEvents(J9JVMTIEnv * j9env, jvmtiCapabilities * capabilities, J9
{
IDATA rc = 0;
- if (capabilities->can_generate_single_step_events) {
- rc |= eventHookFunction(j9env, JVMTI_EVENT_SINGLE_STEP);
- }
-
- if (capabilities->can_generate_breakpoint_events) {
- rc |= eventHookFunction(j9env, JVMTI_EVENT_BREAKPOINT);
- }
-
- if (capabilities->can_generate_field_access_events) {
- rc |= eventHookFunction(j9env, JVMTI_EVENT_FIELD_ACCESS);
- }
-
- if (capabilities->can_generate_field_modification_events) {
- rc |= eventHookFunction(j9env, JVMTI_EVENT_FIELD_MODIFICATION);
+#if defined(J9VM_OPT_CRIU_SUPPORT)
+ J9JavaVM * vm = j9env->vm;
+ J9VMThread *mainThread = vm->mainThread;
+ J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
+ BOOLEAN skipHookReserve = vmFuncs->isCheckpointAllowed(mainThread)
+ && vmFuncs->isDebugOnRestoreEnabled(mainThread);
+ /* Skip J9HookReserve for the events required by JDWP agent pre-checkpoint when DebugOnRestore is enabled,
+ * these events will be registered post-restore if a JDWP agent is specified in the restore option file,
+ * otherwise they are going to be unregistered by J9HookUnregister() which only clears J9HOOK_FLAG_HOOKED,
+ * but not J9HOOK_FLAG_RESERVED.
+ * J9HookUnreserve() might clear the flag set by other callers.
+ */
+ if (!skipHookReserve)
+#endif /* defined(J9VM_OPT_CRIU_SUPPORT)*/
+ {
+ if (capabilities->can_generate_single_step_events) {
+ rc |= eventHookFunction(j9env, JVMTI_EVENT_SINGLE_STEP);
+ }
+ if (capabilities->can_generate_breakpoint_events) {
+ rc |= eventHookFunction(j9env, JVMTI_EVENT_BREAKPOINT);
+ }
+ if (capabilities->can_generate_field_access_events) {
+ rc |= eventHookFunction(j9env, JVMTI_EVENT_FIELD_ACCESS);
+ }
+ if (capabilities->can_generate_field_modification_events) {
+ rc |= eventHookFunction(j9env, JVMTI_EVENT_FIELD_MODIFICATION);
+ }
+ if (capabilities->can_generate_frame_pop_events) {
+ rc |= eventHookFunction(j9env, JVMTI_EVENT_FRAME_POP);
+ }
+ if (capabilities->can_generate_monitor_events) {
+ rc |= eventHookFunction(j9env, JVMTI_EVENT_MONITOR_CONTENDED_ENTER);
+ rc |= eventHookFunction(j9env, JVMTI_EVENT_MONITOR_CONTENDED_ENTERED);
+ rc |= eventHookFunction(j9env, JVMTI_EVENT_MONITOR_WAIT);
+ rc |= eventHookFunction(j9env, JVMTI_EVENT_MONITOR_WAITED);
+ }
+#if JAVA_SPEC_VERSION >= 19
+ if (capabilities->can_support_virtual_threads) {
+ rc |= eventHookFunction(j9env, JVMTI_EVENT_VIRTUAL_THREAD_START);
+ rc |= eventHookFunction(j9env, JVMTI_EVENT_VIRTUAL_THREAD_END);
+ }
+#endif /* JAVA_SPEC_VERSION >= 19 */
+ if (capabilities->can_generate_garbage_collection_events) {
+ rc |= eventHookFunction(j9env, JVMTI_EVENT_GARBAGE_COLLECTION_START);
+ rc |= eventHookFunction(j9env, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH);
+ }
}
-
- if (capabilities->can_generate_frame_pop_events) {
- rc |= eventHookFunction(j9env, JVMTI_EVENT_FRAME_POP);
+ /* CRIU can't skip J9HookReserve for the following events when they are requested via
+ * jvmtiAddCapabilities(), otherwise, the corresponding hooks will be disabled by JIT.
+ * For example, can_generate_method_exit_events/JVMTI_EVENT_METHOD_EXIT related hooks
+ * are J9HOOK_VM_METHOD_RETURN/J9HOOK_VM_NATIVE_METHOD_RETURN, the hook
+ * J9HOOK_VM_METHOD_RETURN is to be disabled if not reserved in advance.
+ * Similarly for the other events, if they are not reserved and a jdwp agent is
+ * specified at the restore option file, the capability can't be added,
+ * and the debugger won't be able to run.
+ */
+ if (capabilities->can_generate_method_exit_events) {
+ rc |= eventHookFunction(j9env, JVMTI_EVENT_METHOD_EXIT);
}
-
if (capabilities->can_generate_method_entry_events) {
rc |= eventHookFunction(j9env, JVMTI_EVENT_METHOD_ENTRY);
}
-
- if (capabilities->can_generate_method_exit_events) {
- rc |= eventHookFunction(j9env, JVMTI_EVENT_METHOD_EXIT);
+ if (capabilities->can_generate_exception_events) {
+ rc |= eventHookFunction(j9env, JVMTI_EVENT_EXCEPTION);
+ rc |= eventHookFunction(j9env, JVMTI_EVENT_EXCEPTION_CATCH);
}
if (capabilities->can_generate_native_method_bind_events) {
rc |= eventHookFunction(j9env, JVMTI_EVENT_NATIVE_METHOD_BIND);
}
- if (capabilities->can_generate_exception_events) {
- rc |= eventHookFunction(j9env, JVMTI_EVENT_EXCEPTION);
- rc |= eventHookFunction(j9env, JVMTI_EVENT_EXCEPTION_CATCH);
- }
-
if (capabilities->can_generate_compiled_method_load_events) {
rc |= eventHookFunction(j9env, JVMTI_EVENT_COMPILED_METHOD_LOAD);
rc |= eventHookFunction(j9env, JVMTI_EVENT_COMPILED_METHOD_UNLOAD);
}
- if (capabilities->can_generate_monitor_events) {
- rc |= eventHookFunction(j9env, JVMTI_EVENT_MONITOR_CONTENDED_ENTER);
- rc |= eventHookFunction(j9env, JVMTI_EVENT_MONITOR_CONTENDED_ENTERED);
- rc |= eventHookFunction(j9env, JVMTI_EVENT_MONITOR_WAIT);
- rc |= eventHookFunction(j9env, JVMTI_EVENT_MONITOR_WAITED);
- }
-
if (capabilities->can_generate_vm_object_alloc_events) {
rc |= eventHookFunction(j9env, JVMTI_EVENT_VM_OBJECT_ALLOC);
}
@@ -599,22 +632,10 @@ mapCapabilitiesToEvents(J9JVMTIEnv * j9env, jvmtiCapabilities * capabilities, J9
}
#endif /* JAVA_SPEC_VERSION >= 11 */
-#if JAVA_SPEC_VERSION >= 19
- if (capabilities->can_support_virtual_threads) {
- rc |= eventHookFunction(j9env, JVMTI_EVENT_VIRTUAL_THREAD_START);
- rc |= eventHookFunction(j9env, JVMTI_EVENT_VIRTUAL_THREAD_END);
- }
-#endif /* JAVA_SPEC_VERSION >= 19 */
-
if (capabilities->can_generate_object_free_events) {
rc |= eventHookFunction(j9env, JVMTI_EVENT_OBJECT_FREE);
}
- if (capabilities->can_generate_garbage_collection_events) {
- rc |= eventHookFunction(j9env, JVMTI_EVENT_GARBAGE_COLLECTION_START);
- rc |= eventHookFunction(j9env, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH);
- }
-
if (capabilities->can_generate_resource_exhaustion_heap_events ||
capabilities->can_generate_resource_exhaustion_threads_events) {
rc |= eventHookFunction(j9env, JVMTI_EVENT_RESOURCE_EXHAUSTED);
@@ -622,5 +643,3 @@ mapCapabilitiesToEvents(J9JVMTIEnv * j9env, jvmtiCapabilities * capabilities, J9
return rc;
}
-
-
diff --git a/runtime/jvmti/jvmtiHook.c b/runtime/jvmti/jvmtiHook.c
index 25e6b6880e7..bc083f0155d 100644
--- a/runtime/jvmti/jvmtiHook.c
+++ b/runtime/jvmti/jvmtiHook.c
@@ -155,6 +155,24 @@ static BOOLEAN shouldPostEvent(J9VMThread *currentThread, J9Method *method);
#if defined(J9VM_OPT_CRIU_SUPPORT)
static void jvmtiHookVMCheckpoint(J9HookInterface **hook, UDATA eventNum, void *eventData, void *userData);
static void jvmtiHookVMRestore(J9HookInterface **hook, UDATA eventNum, void *eventData, void *userData);
+static void jvmtiHookVMRestoreCRIUInit(J9HookInterface **hook, UDATA eventNum, void *eventData, void *userData);
+static void jvmtiHookVMRestoreStartAgent(J9HookInterface **hook, UDATA eventNum, void *eventData, void *userData);
+static void hookDisableHelper(J9JavaVM *vm, J9HookInterface **vmHook, UDATA eventNum, J9HookFunction function, BOOLEAN unreserve, void *userData);
+
+static void
+hookDisableHelper(J9JavaVM *vm, J9HookInterface **vmHook, UDATA eventNum, J9HookFunction function, BOOLEAN unreserve, void *userData)
+{
+ if (NULL == userData) {
+ (*vmHook)->J9HookUnregister(vmHook, eventNum, function, vm->checkpointState.jvmtienv);
+ } else {
+ (*vmHook)->J9HookUnregister(vmHook, eventNum, function, userData);
+ }
+ if (unreserve) {
+ /* for actual hookRegister calls */
+ (*vmHook)->J9HookUnreserve(vmHook, eventNum);
+ }
+ (*vmHook)->J9HookDisable(vmHook, eventNum);
+}
#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */
static void
@@ -539,6 +557,37 @@ jvmtiHookVMCheckpoint(J9HookInterface **hook, UDATA eventNum, void *eventData, v
TRACE_JVMTI_EVENT_RETURN(jvmtiHookVMCheckpoint);
}
+static void
+jvmtiHookVMRestoreCRIUInit(J9HookInterface **hook, UDATA eventNum, void *eventData, void *userData)
+{
+ Trc_JVMTI_jvmtiHookVMRestoreCRIUInit_Entry();
+ criuRestoreInitializeLib(((J9RestoreEvent *)eventData)->currentThread->javaVM, (J9JVMTIEnv *)userData);
+ TRACE_JVMTI_EVENT_RETURN(jvmtiHookVMRestoreCRIUInit);
+}
+
+static void
+jvmtiHookVMRestoreStartAgent(J9HookInterface **hook, UDATA eventNum, void *eventData, void *userData)
+{
+ J9VMThread *currentThread = ((J9RestoreEvent *)eventData)->currentThread;
+ J9JavaVM *vm = currentThread->javaVM;
+ Trc_JVMTI_jvmtiHookVMRestoreStartAgent_Entry();
+ if (J9_ARE_ANY_BITS_SET(vm->checkpointState.flags, J9VM_CRIU_IS_JDWP_ENABLED)) {
+ J9InternalVMFunctions const * const vmFuncs = vm->internalVMFunctions;
+
+ vmFuncs->internalExitVMToJNI(currentThread);
+ criuRestoreStartAgent(vm);
+ vmFuncs->internalEnterVMFromJNI(currentThread);
+ } else {
+ /* Last part of cleanup if there was no JDWP agent specified.
+ * This releases VM access hence can't be invoked within criuDisableHooks() from
+ * J9HOOK_VM_PREPARING_FOR_RESTORE.
+ */
+ jvmtiEnv *jvmti_env = vm->checkpointState.jvmtienv;
+ (*jvmti_env)->DisposeEnvironment(jvmti_env);
+ }
+ TRACE_JVMTI_EVENT_RETURN(jvmtiHookVMRestoreStartAgent);
+}
+
static void
jvmtiHookVMRestore(J9HookInterface **hook, UDATA eventNum, void *eventData, void *userData)
{
@@ -562,6 +611,89 @@ jvmtiHookVMRestore(J9HookInterface **hook, UDATA eventNum, void *eventData, void
TRACE_JVMTI_EVENT_RETURN(jvmtiHookVMRestore);
}
+
+void
+criuDisableHooks(J9JVMTIData *jvmtiData, J9JVMTIEnv *j9env)
+{
+ J9JavaVM *vm = jvmtiData->vm;
+ jvmtiEnv *jvmti_env = vm->checkpointState.jvmtienv;
+ J9HookInterface **vmHook = vm->internalVMFunctions->getVMHookInterface(vm);
+
+ Assert_JVMTI_true(J9_ARE_NO_BITS_SET(vm->checkpointState.flags, J9VM_CRIU_IS_JDWP_ENABLED));
+
+ /* can_access_local_variables, can_get_source_file_name, can_get_line_numbers, can_get_source_debug_extension
+ * can_maintain_original_method_order, can_generate_single_step_events
+ */
+ hookDisableHelper(vm, vmHook, J9HOOK_VM_REQUIRED_DEBUG_ATTRIBUTES, jvmtiHookRequiredDebugAttributes, FALSE, NULL);
+ vm->requiredDebugAttributes &= ~J9VM_DEBUG_ATTRIBUTE_CAN_ACCESS_LOCALS;
+ vm->requiredDebugAttributes &= ~J9VM_DEBUG_ATTRIBUTE_MAINTAIN_ORIGINAL_METHOD_ORDER;
+ vm->requiredDebugAttributes &= ~J9VM_DEBUG_ATTRIBUTE_SOURCE_DEBUG_EXTENSION;
+ if (NULL != vm->jitConfig) {
+ vm->requiredDebugAttributes &= ~J9VM_DEBUG_ATTRIBUTE_LINE_NUMBER_TABLE;
+ vm->requiredDebugAttributes &= ~J9VM_DEBUG_ATTRIBUTE_LOCAL_VARIABLE_TABLE;
+ vm->requiredDebugAttributes &= ~J9VM_DEBUG_ATTRIBUTE_SOURCE_FILE;
+ }
+
+ if (NULL != vm->jitConfig) {
+ J9VMHookInterface vmhookInterface = vm->hookInterface;
+
+ /* can_tag_objects */
+ hookDisableHelper(vm, vmHook, J9HOOK_MM_OMR_GLOBAL_GC_END, jvmtiHookGCEnd, FALSE, NULL);
+ hookDisableHelper(vm, vmHook, J9HOOK_MM_OMR_LOCAL_GC_END, jvmtiHookGCEnd, FALSE, NULL);
+
+ /* can_generate_single_step_events */
+ hookDisableHelper(vm, vmHook, J9HOOK_VM_SINGLE_STEP, jvmtiHookSingleStep, FALSE, NULL);
+
+ /* can_generate_exception_events */
+ hookDisableHelper(vm, vmHook, J9HOOK_VM_EXCEPTION_THROW, jvmtiHookExceptionThrow, TRUE, NULL);
+ hookDisableHelper(vm, vmHook, J9HOOK_VM_EXCEPTION_CATCH, jvmtiHookExceptionCatch, TRUE, NULL);
+
+ /* can_generate_frame_pop_events */
+ hookDisableHelper(vm, vmHook, J9HOOK_VM_FRAME_POP, jvmtiHookFramePop, FALSE, NULL);
+
+ /* can_generate_breakpoint_events */
+ hookDisableHelper(vm, vmHook, J9HOOK_VM_BREAKPOINT, jvmtiHookBreakpoint, TRUE, NULL);
+
+ /* can_generate_method_entry_events */
+ hookDisableHelper(vm, vmHook, J9HOOK_VM_METHOD_ENTER, jvmtiHookMethodEnter, TRUE, NULL);
+ hookDisableHelper(vm, vmHook, J9HOOK_VM_NATIVE_METHOD_ENTER, jvmtiHookMethodEnter, TRUE, NULL);
+
+ /* can_generate_method_exit_events */
+ hookDisableHelper(vm, vmHook, J9HOOK_VM_METHOD_RETURN, jvmtiHookMethodExit, TRUE, NULL);
+ hookDisableHelper(vm, vmHook, J9HOOK_VM_NATIVE_METHOD_RETURN, jvmtiHookMethodExit, TRUE, NULL);
+
+ /* can_generate_monitor_events */
+ hookDisableHelper(vm, vmHook, J9HOOK_VM_MONITOR_CONTENDED_ENTER, jvmtiHookMonitorContendedEnter, FALSE, NULL);
+ hookDisableHelper(vm, vmHook, J9HOOK_VM_MONITOR_CONTENDED_ENTERED, jvmtiHookMonitorContendedEntered, FALSE, NULL);
+ hookDisableHelper(vm, vmHook, J9HOOK_VM_MONITOR_WAIT, jvmtiHookMonitorWait, FALSE, NULL);
+ hookDisableHelper(vm, vmHook, J9HOOK_VM_MONITOR_WAITED, jvmtiHookMonitorWaited, FALSE, NULL);
+
+ /* can_generate_garbage_collection_events */
+ hookDisableHelper(vm, vmHook, J9HOOK_MM_OMR_GLOBAL_GC_START, jvmtiHookGCStart, FALSE, NULL);
+ hookDisableHelper(vm, vmHook, J9HOOK_MM_OMR_LOCAL_GC_START, jvmtiHookGCStart, FALSE, NULL);
+
+#if JAVA_SPEC_VERSION >= 21
+ /* can_support_virtual_threads */
+ hookDisableHelper(vm, vmHook, J9HOOK_VM_VIRTUAL_THREAD_STARTED, jvmtiHookVirtualThreadStarted, FALSE, NULL);
+ hookDisableHelper(vm, vmHook, J9HOOK_VM_VIRTUAL_THREAD_END, jvmtiHookVirtualThreadEnd, FALSE, NULL);
+#endif /* JAVA_SPEC_VERSION >= 21 */
+
+ /* can_generate_field_modification_events */
+ hookDisableHelper(vm, vmHook, J9HOOK_VM_PUT_FIELD, jvmtiHookFieldModification, FALSE, NULL);
+ hookDisableHelper(vm, vmHook, J9HOOK_VM_PUT_STATIC_FIELD, jvmtiHookFieldModification, FALSE, NULL);
+
+ /* can_generate_field_access_events */
+ hookDisableHelper(vm, vmHook, J9HOOK_VM_GET_FIELD, jvmtiHookFieldAccess, FALSE, NULL);
+ hookDisableHelper(vm, vmHook, J9HOOK_VM_GET_STATIC_FIELD, jvmtiHookFieldAccess, FALSE, NULL);
+
+ /* can_pop_frame & can_force_early_return */
+ if (J9_EVENT_IS_HOOKED_OR_RESERVED(vmhookInterface, J9HOOK_VM_POP_FRAMES_INTERRUPT)) {
+ hookDisableHelper(vm, vmHook, J9HOOK_VM_POP_FRAMES_INTERRUPT, jvmtiHookPopFramesInterrupt, TRUE, J9JVMTI_DATA_FROM_VM(vm));
+ }
+ }
+
+ (*jvmti_env)->RelinquishCapabilities(jvmti_env, &vm->checkpointState.requiredCapabilities);
+}
#endif /* defined(J9VM_OPT_CRIU_SUPPORT)*/
static IDATA
@@ -1914,6 +2046,17 @@ hookGlobalEvents(J9JVMTIData * jvmtiData)
return 1;
}
+#if defined(J9VM_OPT_CRIU_SUPPORT)
+ if (vm->internalVMFunctions->isDebugOnRestoreEnabled(vm->mainThread)) {
+ if ((*vmHook)->J9HookRegisterWithCallSite(vmHook, J9HOOK_TAG_AGENT_ID | J9HOOK_VM_PREPARING_FOR_RESTORE, jvmtiHookVMRestoreCRIUInit, OMR_GET_CALLSITE(), jvmtiData, J9HOOK_AGENTID_FIRST)) {
+ return 1;
+ }
+ if ((*vmHook)->J9HookRegisterWithCallSite(vmHook, J9HOOK_TAG_AGENT_ID | J9HOOK_VM_CRIU_RESTORE, jvmtiHookVMRestoreStartAgent, OMR_GET_CALLSITE(), jvmtiData, J9HOOK_AGENTID_FIRST)) {
+ return 1;
+ }
+ }
+#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */
+
if ((*vmHook)->J9HookRegisterWithCallSite(vmHook, J9HOOK_TAG_AGENT_ID | J9HOOK_VM_SHUTTING_DOWN, jvmtiHookVMShutdownLast, OMR_GET_CALLSITE(), jvmtiData, J9HOOK_AGENTID_LAST)) {
return 1;
}
@@ -1952,7 +2095,6 @@ unhookGlobalEvents(J9JVMTIData * jvmtiData)
(*vmHook)->J9HookUnregister(vmHook, J9HOOK_VM_SHUTTING_DOWN, jvmtiHookVMShutdownLast, NULL);
}
-
static void
jvmtiHookMonitorContendedEnter(J9HookInterface** hook, UDATA eventNum, void* eventData, void* userData)
{
diff --git a/runtime/jvmti/jvmtiStartup.c b/runtime/jvmti/jvmtiStartup.c
index bece5f40a93..2d50b521f63 100644
--- a/runtime/jvmti/jvmtiStartup.c
+++ b/runtime/jvmti/jvmtiStartup.c
@@ -46,19 +46,18 @@
#include "jvmtiHelpers.h"
#define THIS_DLL_NAME J9_JVMTI_DLL_NAME
-#define OPT_AGENTPATH_COLON "-agentpath:"
-#define OPT_AGENTLIB_COLON "-agentlib:"
-
-static void shutDownJVMTI (J9JavaVM * vm);
-static jint createAgentLibrary (J9JavaVM * vm, const char *libraryAndOptions, UDATA libraryLength, const char* options, UDATA optionsLength, UDATA decorate, J9JVMTIAgentLibrary **result);
-static jint initializeJVMTI (J9JavaVM * vm);
-static void shutDownAgentLibraries (J9JavaVM * vm, UDATA closeLibrary);
-static jint loadAgentLibrary (J9JavaVM * vm, J9JVMTIAgentLibrary * agentLibrary);
-static jint createXrunLibraries (J9JavaVM * vm);
-static J9JVMTIAgentLibrary * findAgentLibrary(J9JavaVM * vm, const char *libraryAndOptions, UDATA libraryLength);
-static jint issueAgentOnLoadAttach(J9JavaVM * vm, J9JVMTIAgentLibrary * agentLibrary, const char* options, char *loadFunctionName, BOOLEAN * foundLoadFn);
+
+static void shutDownJVMTI(J9JavaVM *vm);
+static jint createAgentLibrary(J9JavaVM *vm, const char *libraryName, UDATA libraryNameLength, const char *options, UDATA optionsLength, UDATA decorate, J9JVMTIAgentLibrary **result);
+static jint initializeJVMTI(J9JavaVM *vm);
+static void shutDownAgentLibraries(J9JavaVM *vm, UDATA closeLibrary);
+static jint loadAgentLibrary(J9JavaVM *vm, J9JVMTIAgentLibrary *agentLibrary);
+static jint createXrunLibraries(J9JavaVM *vm);
+static J9JVMTIAgentLibrary* findAgentLibrary(J9JavaVM *vm, const char *libraryAndOptions, UDATA libraryLength);
+static jint issueAgentOnLoadAttach(J9JavaVM *vm, J9JVMTIAgentLibrary *agentLibrary, const char *options, char *loadFunctionName, BOOLEAN *foundLoadFn);
I_32 JNICALL loadAgentLibraryOnAttach(struct J9JavaVM *vm, const char *library, const char *options, UDATA decorate);
static BOOLEAN isAgentLibraryLoaded(J9JavaVM *vm, const char *library, BOOLEAN decorate);
+static jint createAgentLibraryWithOption(J9JavaVM *vm, J9VMInitArgs *argsList, IDATA agentIndex, J9JVMTIAgentLibrary **agentLibrary, BOOLEAN isXrunjdwp, BOOLEAN *isJDWPagent);
#define INSTRUMENT_LIBRARY "instrument"
@@ -114,111 +113,232 @@ parseLibraryAndOptions(char *libraryAndOptions, UDATA *libraryLength, char **opt
#define OPTIONSBUFF_LEN 512
-IDATA J9VMDllMain(J9JavaVM* vm, IDATA stage, void* reserved)
+/**
+ * Create an agent library from an option index.
+ *
+ * @param[in] vm Java VM
+ * @param[in] argsList a J9VMInitArgs
+ * @param[in] agentIndex the option index for the agent library
+ * @param[in/out] agentLibrary environment for the agent
+ * @param[in] isXrunjdwp indicate if the agent option is MAPOPT_XRUNJDWP
+ * @param[in/out] isJDWPagent indicate if DebugOnRestore is enabled and the agent is JDWP
+ *
+ * @return JNI_OK if succeeded, otherwise JNI_ERR/JNI_ENOMEM
+ */
+static jint
+createAgentLibraryWithOption(J9JavaVM *vm, J9VMInitArgs *argsList, IDATA agentIndex, J9JVMTIAgentLibrary **agentLibrary, BOOLEAN isXrunjdwp, BOOLEAN *isJDWPagent)
{
- IDATA returnVal = J9VMDLLMAIN_OK;
- IDATA agentIndex;
- pool_state poolState;
- J9JVMTIAgentLibrary * agentLibrary;
- J9JVMTIData * jvmtiData;
+ jint result = JNI_OK;
+ char optionsBuf[OPTIONSBUFF_LEN];
+ char *optionsPtr = (char*)optionsBuf;
+ UDATA buflen = OPTIONSBUFF_LEN;
+ IDATA option_rc = 0;
+ PORT_ACCESS_FROM_JAVAVM(vm);
- switch(stage) {
+ do {
+ option_rc = COPY_OPTION_VALUE_ARGS(argsList, agentIndex, ':', &optionsPtr, buflen);
+ if (OPTION_BUFFER_OVERFLOW == option_rc) {
+ if (optionsPtr != (char*)optionsBuf) {
+ j9mem_free_memory(optionsPtr);
+ }
+ buflen *= 2;
+ optionsPtr = (char*)j9mem_allocate_memory(buflen, OMRMEM_CATEGORY_VM);
+ if (NULL == optionsPtr) {
+ Trc_JVMTI_createAgentLibraryWithOption_OOM();
+ result = JNI_ENOMEM;
+ break;
+ }
+ }
+ } while (OPTION_BUFFER_OVERFLOW == option_rc);
+ if (JNI_OK == result) {
+ UDATA libraryLength = 0;
+#define JDWP_AGENT "jdwp"
+ if (isXrunjdwp) {
+ UDATA optionsLengthTmp = strlen(optionsPtr);
+ result = createAgentLibrary(vm, JDWP_AGENT, LITERAL_STRLEN(JDWP_AGENT), optionsPtr, optionsLengthTmp, TRUE, agentLibrary);
+ Trc_JVMTI_createAgentLibraryWithOption_Xrunjdwp_result(optionsPtr, optionsLengthTmp, *agentLibrary, result);
+ } else {
+ char *options = NULL;
+ UDATA optionsLength = 0;
- case ALL_VM_ARGS_CONSUMED :
- {
- PORT_ACCESS_FROM_JAVAVM(vm);
- char optionsBuf[OPTIONSBUFF_LEN];
- char* optionsPtr = (char*)optionsBuf;
- UDATA buflen = OPTIONSBUFF_LEN;
- IDATA option_rc = 0;
+ parseLibraryAndOptions(optionsPtr, &libraryLength, &options, &optionsLength);
+ result = createAgentLibrary(vm, optionsPtr, libraryLength, options, optionsLength, TRUE, agentLibrary);
+ Trc_JVMTI_createAgentLibraryWithOption_agentlib_result(optionsPtr, libraryLength, options, optionsLength, *agentLibrary, result);
+ }
+#if defined(J9VM_OPT_CRIU_SUPPORT)
+ if ((JNI_OK == result)
+ && (isXrunjdwp || (0 == strncmp(JDWP_AGENT, optionsPtr, libraryLength)))
+ && (vm->internalVMFunctions->isDebugOnRestoreEnabled(vm->mainThread))
+ ){
+ *isJDWPagent = TRUE;
+ }
+#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */
+#undef JDWP_AGENT
- if (initializeJVMTI(vm) != JNI_OK) {
- goto _error;
- }
+ if (optionsPtr != (char*)optionsBuf) {
+ j9mem_free_memory(optionsPtr);
+ }
+ }
- agentIndex = FIND_AND_CONSUME_VMARG_FORWARD( STARTSWITH_MATCH, OPT_AGENTLIB_COLON, NULL);
- while (agentIndex >= 0) {
- UDATA libraryLength;
- char *options;
- UDATA optionsLength;
-
- do {
- option_rc = COPY_OPTION_VALUE(agentIndex, ':', &optionsPtr, buflen);
- if (OPTION_BUFFER_OVERFLOW == option_rc) {
- if (optionsPtr != (char*)optionsBuf) {
- j9mem_free_memory(optionsPtr);
- optionsPtr = NULL;
- }
- buflen *= 2;
- optionsPtr = (char*)j9mem_allocate_memory(buflen, OMRMEM_CATEGORY_VM);
- if (NULL == optionsPtr) {
- goto _error;
- }
- }
- } while (OPTION_BUFFER_OVERFLOW == option_rc);
+ return result;
+}
- parseLibraryAndOptions(optionsPtr, &libraryLength, &options, &optionsLength);
- if (createAgentLibrary(vm, optionsPtr, libraryLength, options, optionsLength, TRUE, NULL) != JNI_OK) {
- if (optionsPtr != (char*)optionsBuf) {
- j9mem_free_memory(optionsPtr);
- optionsPtr = NULL;
- }
- goto _error;
- }
- agentIndex = FIND_NEXT_ARG_IN_VMARGS_FORWARD( STARTSWITH_MATCH, OPT_AGENTLIB_COLON, NULL, agentIndex);
- }
- if (optionsPtr != (char*)optionsBuf) {
- j9mem_free_memory(optionsPtr);
- optionsPtr = NULL;
+/**
+ * Create an agent library from an option index.
+ *
+ * @param[in] vm Java VM
+ * @param[in] argsList a J9VMInitArgs
+ * @param[in] agentColon the agent option, VMOPT_AGENTLIB_COLON, VMOPT_AGENTPATH_COLON or MAPOPT_XRUNJDWP
+ * @param[in] loadLibrary indicate if the agent library created to be loaded or not
+ * @param[in] isXrunjdwp indicate if the agent option is MAPOPT_XRUNJDWP
+ *
+ * @return TRUE if succeeded, otherwise FALSE
+ */
+static BOOLEAN
+processAgentLibraryFromArgsList(J9JavaVM *vm, J9VMInitArgs *argsList, const char *agentColon, BOOLEAN loadLibrary, BOOLEAN isXrunjdwp)
+{
+ IDATA agentIndex = FIND_AND_CONSUME_ARG_FORWARD(argsList, STARTSWITH_MATCH, agentColon, NULL);
+ BOOLEAN result = TRUE;
+
+ while (agentIndex >= 0) {
+ J9JVMTIAgentLibrary *agentLibrary = NULL;
+ BOOLEAN isJDWPagent = FALSE;
+ if (JNI_OK != createAgentLibraryWithOption(vm, argsList, agentIndex, &agentLibrary, isXrunjdwp, &isJDWPagent)) {
+ result = FALSE;
+ break;
+ }
+#if defined(J9VM_OPT_CRIU_SUPPORT)
+ if (isJDWPagent) {
+ vm->checkpointState.flags |= J9VM_CRIU_IS_JDWP_ENABLED;
+ }
+ if (loadLibrary) {
+ if (JNI_OK != loadAgentLibrary(vm, agentLibrary)) {
+ result = FALSE;
+ break;
}
+ }
+#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */
- agentIndex = FIND_AND_CONSUME_VMARG_FORWARD( STARTSWITH_MATCH, OPT_AGENTPATH_COLON, NULL);
- optionsPtr = (char*)optionsBuf;
- buflen = OPTIONSBUFF_LEN;
- while (agentIndex >= 0) {
- UDATA libraryLength;
- char *options;
- UDATA optionsLength;
-
- do {
- option_rc = COPY_OPTION_VALUE(agentIndex, ':', &optionsPtr, buflen);
- if (OPTION_BUFFER_OVERFLOW == option_rc) {
- if (optionsPtr != (char*)optionsBuf) {
- j9mem_free_memory(optionsPtr);
- optionsPtr = NULL;
- }
- buflen *= 2;
- optionsPtr = (char*)j9mem_allocate_memory(buflen, OMRMEM_CATEGORY_VM);
- if (NULL == optionsPtr) {
- goto _error;
- }
- }
- } while (OPTION_BUFFER_OVERFLOW == option_rc);
+ agentIndex = FIND_NEXT_ARG_IN_ARGS_FORWARD(argsList, STARTSWITH_MATCH, agentColon, NULL, agentIndex);
+ }
- parseLibraryAndOptions(optionsPtr, &libraryLength, &options, &optionsLength);
- if (createAgentLibrary(vm, optionsPtr, libraryLength, options, optionsLength, FALSE, NULL) != JNI_OK) {
- if (optionsPtr != (char*)optionsBuf) {
- j9mem_free_memory(optionsPtr);
- optionsPtr = NULL;
- }
- goto _error;
- }
- agentIndex = FIND_NEXT_ARG_IN_VMARGS_FORWARD( STARTSWITH_MATCH, OPT_AGENTPATH_COLON, NULL, agentIndex);
+ return result;
+}
+
+#if defined(J9VM_OPT_CRIU_SUPPORT)
+/**
+ * Add JVMTI capabilities before checkpoint.
+ * This is required for debugger support when JIT is enabled.
+ * This function can be removed when JIT allows capabilities to be added after restore.
+ *
+ * @param[in] vm Java VM
+ * @param[in] jitEnabled FALSE if -Xint, otherwise TRUE
+ *
+ * @return JNI_OK if succeeded, otherwise JNI_ERR
+ */
+static jint
+criuAddCapabilities(J9JavaVM *vm, BOOLEAN jitEnabled) {
+ jvmtiError jvmtiRet = JVMTI_ERROR_NONE;
+ JavaVM *javaVM = (JavaVM*)vm;
+ jvmtiCapabilities *requiredCapabilities = &vm->checkpointState.requiredCapabilities;
+ J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
+ jvmtiEnv *jvmti_env = NULL;
+ jint rc = vmFuncs->GetEnv(javaVM, (void **)&jvmti_env, JVMTI_VERSION_1_1);
+ if (JNI_OK != rc) {
+ if ((JNI_EVERSION != rc) || (JNI_OK != (vmFuncs->GetEnv(javaVM, (void **)&jvmti_env, JVMTI_VERSION_1_0)))) {
+ return JNI_ERR;
+ }
+ }
+
+ memset(requiredCapabilities,0,sizeof(jvmtiCapabilities));
+ requiredCapabilities->can_access_local_variables = 1;
+ if (jitEnabled) {
+ jvmtiCapabilities potentialCapabilities;
+
+ requiredCapabilities->can_tag_objects = 1;
+ requiredCapabilities->can_get_source_file_name = 1;
+ requiredCapabilities->can_get_line_numbers = 1;
+ requiredCapabilities->can_get_source_debug_extension = 1;
+ requiredCapabilities->can_maintain_original_method_order = 1;
+ requiredCapabilities->can_generate_single_step_events = 1;
+ requiredCapabilities->can_generate_exception_events = 1;
+ requiredCapabilities->can_generate_frame_pop_events = 1;
+ requiredCapabilities->can_generate_breakpoint_events = 1;
+ requiredCapabilities->can_generate_method_entry_events = 1;
+ requiredCapabilities->can_generate_method_exit_events = 1;
+ requiredCapabilities->can_generate_monitor_events = 1;
+ requiredCapabilities->can_generate_garbage_collection_events = 1;
+#if JAVA_SPEC_VERSION >= 21
+ requiredCapabilities->can_support_virtual_threads = 1;
+#endif /* JAVA_SPEC_VERSION >= 21 */
+ memset(&potentialCapabilities, 0, sizeof(potentialCapabilities));
+ jvmtiRet = (*jvmti_env)->GetPotentialCapabilities(jvmti_env, &potentialCapabilities);
+ if (JVMTI_ERROR_NONE != jvmtiRet) {
+ return JNI_ERR;
+ }
+ requiredCapabilities->can_generate_field_modification_events = potentialCapabilities.can_generate_field_modification_events;
+ requiredCapabilities->can_generate_field_access_events = potentialCapabilities.can_generate_field_access_events;
+ requiredCapabilities->can_pop_frame = potentialCapabilities.can_pop_frame;
+ }
+ jvmtiRet = (*jvmti_env)->AddCapabilities(jvmti_env, requiredCapabilities);
+ if (JVMTI_ERROR_NONE != jvmtiRet) {
+ return JNI_ERR;
+ }
+
+ vm->checkpointState.jvmtienv = jvmti_env;
+ return JNI_OK;
+}
+
+void
+criuRestoreInitializeLib(J9JavaVM *vm, J9JVMTIEnv *j9env)
+{
+ J9VMInitArgs *criuRestoreArgsList = vm->checkpointState.restoreArgsList;
+
+ processAgentLibraryFromArgsList(vm, criuRestoreArgsList, VMOPT_AGENTLIB_COLON, FALSE, FALSE);
+ processAgentLibraryFromArgsList(vm, criuRestoreArgsList, VMOPT_AGENTPATH_COLON, FALSE, FALSE);
+ processAgentLibraryFromArgsList(vm, criuRestoreArgsList, MAPOPT_XRUNJDWP, FALSE, TRUE);
+
+ if (J9_ARE_NO_BITS_SET(vm->checkpointState.flags, J9VM_CRIU_IS_JDWP_ENABLED)) {
+ J9JVMTIData * jvmtiData = vm->jvmtiData;
+ if (NULL != jvmtiData) {
+ criuDisableHooks(jvmtiData, j9env);
+ }
+ }
+}
+
+void
+criuRestoreStartAgent(J9JavaVM *vm)
+{
+ J9VMInitArgs *criuRestoreArgsList = vm->checkpointState.restoreArgsList;
+
+ processAgentLibraryFromArgsList(vm, criuRestoreArgsList, VMOPT_AGENTLIB_COLON, TRUE, FALSE);
+ processAgentLibraryFromArgsList(vm, criuRestoreArgsList, VMOPT_AGENTPATH_COLON, TRUE, FALSE);
+ processAgentLibraryFromArgsList(vm, criuRestoreArgsList, MAPOPT_XRUNJDWP, TRUE, TRUE);
+}
+#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */
+
+IDATA J9VMDllMain(J9JavaVM *vm, IDATA stage, void *reserved)
+{
+ IDATA returnVal = J9VMDLLMAIN_OK;
+
+ switch(stage) {
+ case ALL_VM_ARGS_CONSUMED:
+ {
+ if (JNI_OK != initializeJVMTI(vm)) {
+ goto _error;
}
- if (optionsPtr != (char*)optionsBuf) {
- j9mem_free_memory(optionsPtr);
- optionsPtr = NULL;
+ if (FALSE == processAgentLibraryFromArgsList(vm, vm->vmArgsArray, VMOPT_AGENTLIB_COLON, FALSE, FALSE)) {
+ goto _error;
+ }
+ if (FALSE == processAgentLibraryFromArgsList(vm, vm->vmArgsArray, VMOPT_AGENTPATH_COLON, FALSE, FALSE)) {
+ goto _error;
}
-
/* -Xrun libraries that have an Agent_OnLoad are treated like -agentlib: */
-
- if (createXrunLibraries(vm) != JNI_OK) {
+ if (JNI_OK != createXrunLibraries(vm)) {
goto _error;
}
-
vm->loadAgentLibraryOnAttach = &loadAgentLibraryOnAttach;
vm->isAgentLibraryLoaded = &isAgentLibraryLoaded;
-
break;
}
@@ -235,7 +355,10 @@ IDATA J9VMDllMain(J9JavaVM* vm, IDATA stage, void* reserved)
break;
case AGENTS_STARTED:
- jvmtiData = J9JVMTI_DATA_FROM_VM(vm);
+ {
+ pool_state poolState;
+ J9JVMTIAgentLibrary *agentLibrary = NULL;
+ J9JVMTIData *jvmtiData = J9JVMTI_DATA_FROM_VM(vm);
if (hookGlobalEvents(jvmtiData)) {
PORT_ACCESS_FROM_JAVAVM(vm);
j9tty_err_printf(PORTLIB, "Need NLS message here\n");
@@ -243,20 +366,33 @@ IDATA J9VMDllMain(J9JavaVM* vm, IDATA stage, void* reserved)
}
agentLibrary = pool_startDo(jvmtiData->agentLibraries, &poolState);
- while (agentLibrary != NULL) {
- if (loadAgentLibrary(vm, agentLibrary) != JNI_OK) {
+ while (NULL != agentLibrary) {
+ if (JNI_OK != loadAgentLibrary(vm, agentLibrary)) {
goto _error;
}
agentLibrary = pool_nextDo(&poolState);
}
/* Register the hotswap helper trace points */
- hshelpUTRegister(vm);
+ hshelpUTRegister(vm);
+
+#if defined(J9VM_OPT_CRIU_SUPPORT)
+ /*
+ * Adding capabilities is required before checkpoint if JIT is enabled.
+ * Following code can be removed when JIT allows capabilities to be added after restore.
+ */
+ if (vm->internalVMFunctions->isDebugOnRestoreEnabled(vm->mainThread)) {
+ Trc_JVMTI_criuAddCapabilities_invoked();
+ /* ignore the failure, it won't cause a problem if JDWP is not enabled later */
+ criuAddCapabilities(vm, NULL != vm->jitConfig);
+ }
+#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */
jvmtiData->phase = JVMTI_PHASE_PRIMORDIAL;
break;
+ }
- case LIBRARIES_ONUNLOAD :
+ case LIBRARIES_ONUNLOAD:
shutDownJVMTI(vm);
break;
@@ -272,8 +408,6 @@ IDATA J9VMDllMain(J9JavaVM* vm, IDATA stage, void* reserved)
return returnVal;
}
-
-
/**
* \brief Mangle the agent library name to include full path
* \ingroup jvmti
@@ -961,45 +1095,44 @@ shutDownJVMTI(J9JavaVM * vm)
/**
* Allocate an instance of the J9JVMTIAgentLibrary structure.
* @param vm Java VM
- * @param libraryAndOptions library name and options concatenated
- * @param libraryLength library name substring length
- * @param options option substring start
- * @param optionsLength option
+ * @param libraryName library name and/or options concatenated
+ * @param libraryNameLength library name length
+ * @param options option string start
+ * @param optionsLength option string length
* @param decorate change the library path/name
* @param result pointer to return new J9JVMTIAgentLibrary by reference
- * @return JNI_ERR, JNI_ENOMEM, or JNI_OK
+ * @return JNI_OK on success, otherwis JNI_ERR or JNI_ENOMEM
*/
static jint
-createAgentLibrary(J9JavaVM * vm, const char *libraryAndOptions, UDATA libraryLength, const char* options, UDATA optionsLength, UDATA decorate, J9JVMTIAgentLibrary **result)
+createAgentLibrary(J9JavaVM *vm, const char *libraryName, UDATA libraryNameLength, const char *options, UDATA optionsLength, UDATA decorate, J9JVMTIAgentLibrary **result)
{
PORT_ACCESS_FROM_JAVAVM(vm);
- J9JVMTIData * jvmtiData = J9JVMTI_DATA_FROM_VM(vm);
- J9JVMTIAgentLibrary * agentLibrary = NULL;
+ J9JVMTIData *jvmtiData = J9JVMTI_DATA_FROM_VM(vm);
+ J9JVMTIAgentLibrary *agentLibrary = NULL;
- /* Create a new agent library */
+ /* Create a new agent library. */
omrthread_monitor_enter(jvmtiData->mutex);
agentLibrary = pool_newElement(jvmtiData->agentLibraries);
if (NULL == agentLibrary) {
- j9nls_printf(PORTLIB, J9NLS_ERROR, J9NLS_JVMTI_OUT_OF_MEMORY, libraryAndOptions);
+ j9nls_printf(PORTLIB, J9NLS_ERROR, J9NLS_JVMTI_OUT_OF_MEMORY, libraryName);
omrthread_monitor_exit(jvmtiData->mutex);
return JNI_ENOMEM;
}
- /* Initialize the structure with default values */
+ /* Initialize the structure with default values. */
vm->internalVMFunctions->initializeNativeLibrary(vm, &agentLibrary->nativeLib);
- /* Make a copy of the library name and options */
-
- agentLibrary->nativeLib.name = j9mem_allocate_memory(libraryLength + 1 + optionsLength + 1, J9MEM_CATEGORY_JVMTI);
+ /* Make a copy of the library name and options. */
+ agentLibrary->nativeLib.name = j9mem_allocate_memory(libraryNameLength + 1 + optionsLength + 1, J9MEM_CATEGORY_JVMTI);
if (NULL == agentLibrary->nativeLib.name) {
pool_removeElement(jvmtiData->agentLibraries, agentLibrary);
/* Print NLS message here? */
omrthread_monitor_exit(jvmtiData->mutex);
return JNI_ENOMEM;
}
- strncpy(agentLibrary->nativeLib.name, libraryAndOptions, libraryLength);
- *(agentLibrary->nativeLib.name+libraryLength) = '\0';
- agentLibrary->options = agentLibrary->nativeLib.name+libraryLength+1;
+ strncpy(agentLibrary->nativeLib.name, libraryName, libraryNameLength);
+ *(agentLibrary->nativeLib.name + libraryNameLength) = '\0';
+ agentLibrary->options = agentLibrary->nativeLib.name+libraryNameLength+1;
if (optionsLength > 0) {
strncpy(agentLibrary->options, options, optionsLength);
}
@@ -1013,7 +1146,7 @@ createAgentLibrary(J9JavaVM * vm, const char *libraryAndOptions, UDATA libraryL
agentLibrary->decorate = decorate;
agentLibrary->xRunLibrary = NULL;
agentLibrary->loadCount = 1;
- /* Jazz 99339: initialize agentLibrary->invocationJavaVM to NULL when a new library is created */
+ /* Jazz 99339: initialize agentLibrary->invocationJavaVM to NULL when a new library is created. */
agentLibrary->invocationJavaVM = NULL;
if (NULL != result) {
@@ -1024,8 +1157,6 @@ createAgentLibrary(J9JavaVM * vm, const char *libraryAndOptions, UDATA libraryL
return JNI_OK;
}
-
-
static jint
createXrunLibraries(J9JavaVM * vm)
{
diff --git a/runtime/jvmti/jvmti_internal.h b/runtime/jvmti/jvmti_internal.h
index e7d681c4384..56f4f5178be 100644
--- a/runtime/jvmti/jvmti_internal.h
+++ b/runtime/jvmti/jvmti_internal.h
@@ -1500,6 +1500,16 @@ unhookEvent(J9JVMTIEnv * j9env, jint event);
void
unhookGlobalEvents(J9JVMTIData * jvmtiData);
+#if defined(J9VM_OPT_CRIU_SUPPORT)
+/**
+* @brief Disable the debug related hooks & events if no jdwp agent is specified.
+* @param[in] jvmtiData - point to the JVMTI data
+* @param[in] j9env - pointer to the current JVMTI environment
+* @return void
+*/
+void
+criuDisableHooks(J9JVMTIData *jvmtiData, J9JVMTIEnv *j9env);
+#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */
/* ---------------- jvmtiJNIFunctionInterception.c ---------------- */
@@ -2196,6 +2206,27 @@ IDATA J9VMDllMain(J9JavaVM* vm, IDATA stage, void* reserved);
*/
jint JNICALL JVM_OnLoad(JavaVM *jvm, char* options, void *reserved);
+#if defined(J9VM_OPT_CRIU_SUPPORT)
+/**
+* Check if any agent library is specified in the CRIU restore option file,
+* if so, the checkpointState flag J9VM_CRIU_IS_JDWP_ENABLED is set, but the
+* actual library loading is deferred at criuRestoreStartAgent() invoked via
+* TRIGGER_J9HOOK_VM_CRIU_RESTORE().
+* @param[in] vm the pointer to the J9JavaVM struct
+* @param[in] j9env - pointer to the current JVMTI environment
+* @return void
+*/
+void
+criuRestoreInitializeLib(J9JavaVM *vm, J9JVMTIEnv *j9env);
+
+/**
+ * Load and initialize libraries from CRIU restore option file.
+ * @param[in] vm the pointer to the J9JavaVM struct
+ * @return void
+ */
+void
+criuRestoreStartAgent(J9JavaVM *vm);
+#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */
/* ---------------- jvmtiSystemProperties.c ---------------- */
diff --git a/runtime/oti/j9.h b/runtime/oti/j9.h
index 7061831a890..3e7e8740f6e 100644
--- a/runtime/oti/j9.h
+++ b/runtime/oti/j9.h
@@ -399,6 +399,8 @@ typedef struct {
char data[LITERAL_STRLEN(J9_UNMODIFIABLE_CLASS_ANNOTATION)];
} J9_UNMODIFIABLE_CLASS_ANNOTATION_DATA;
+#define J9_EVENT_IS_HOOKED_OR_RESERVED(interface, event) (J9_EVENT_IS_HOOKED(interface, event) || J9_EVENT_IS_RESERVED(interface, event))
+
#if defined(J9VM_ZOS_3164_INTEROPERABILITY)
#define J9_IS_31BIT_INTEROP_TARGET(handle) J9_ARE_ALL_BITS_SET((UDATA)(handle), OMRPORT_SL_ZOS_31BIT_TARGET_HIGHTAG)
#endif /* defined(J9VM_ZOS_3164_INTEROPERABILITY) */
diff --git a/runtime/oti/j9nonbuilder.h b/runtime/oti/j9nonbuilder.h
index b8af02c3ae9..3d394d6a970 100644
--- a/runtime/oti/j9nonbuilder.h
+++ b/runtime/oti/j9nonbuilder.h
@@ -4370,6 +4370,8 @@ typedef struct J9CRIUCheckpointState {
/* the array of threads is updated by the JDWP agent */
jthread javaDebugThreads[J9VM_CRIU_MAX_DEBUG_THREADS_STORED];
UDATA javaDebugThreadCount;
+ jvmtiEnv *jvmtienv;
+ jvmtiCapabilities requiredCapabilities;
} J9CRIUCheckpointState;
#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */
diff --git a/runtime/oti/j9vm.hdf b/runtime/oti/j9vm.hdf
index b21c4628e87..c9815309722 100644
--- a/runtime/oti/j9vm.hdf
+++ b/runtime/oti/j9vm.hdf
@@ -1407,7 +1407,6 @@ typedef UDATA (* lookupNativeAddressCallback)(struct J9VMThread *currentThread,
J9HOOK_VM_CRIU_RESTORE
Triggered at the beginning of CRIU restore, just after runInternalJVMRestoreHooks()
-
J9VMCRIURestoreEvent
diff --git a/runtime/oti/vm_api.h b/runtime/oti/vm_api.h
index d7071e1e73b..0f4067dfa81 100644
--- a/runtime/oti/vm_api.h
+++ b/runtime/oti/vm_api.h
@@ -2345,12 +2345,13 @@ javaLookupMethodImpl (J9VMThread *vmContext, J9Class *clazz, J9ROMNameAndSignatu
/* ---------------- lookuphelper.c ---------------- */
/**
-* @brief
-* @param vm
-* @return UDATA
+* @brief Disable hooks which must have been hooked by now if isDebugOnRestoreEnabled() returns false,
+* otherwise, check if the events are hooked or reserved instead.
+* @param[in] vm pointer to the J9JavaVM
+* @return TRUE if EnterStep or Breakpoint must be reported, otherwise FALSE.
*/
UDATA
-mustReportEnterStepOrBreakpoint(J9JavaVM * vm);
+mustReportEnterStepOrBreakpoint(J9JavaVM *vm);
/* ---------------- monhelpers.c ---------------- */
diff --git a/runtime/vm/CRIUHelpers.cpp b/runtime/vm/CRIUHelpers.cpp
index a2686d7cc98..4b4eaf3a125 100644
--- a/runtime/vm/CRIUHelpers.cpp
+++ b/runtime/vm/CRIUHelpers.cpp
@@ -182,7 +182,9 @@ isJVMInPortableRestoreMode(J9VMThread *currentThread)
BOOLEAN
isDebugOnRestoreEnabled(J9VMThread *currentThread)
{
- return J9_ARE_ALL_BITS_SET(currentThread->javaVM->checkpointState.flags, J9VM_CRIU_SUPPORT_DEBUG_ON_RESTORE) && isCRaCorCRIUSupportEnabled(currentThread);
+ return J9_ARE_NO_BITS_SET(currentThread->javaVM->checkpointState.flags, J9VM_CRIU_IS_JDWP_ENABLED)
+ && J9_ARE_ALL_BITS_SET(currentThread->javaVM->checkpointState.flags, J9VM_CRIU_SUPPORT_DEBUG_ON_RESTORE)
+ && isCRaCorCRIUSupportEnabled(currentThread);
}
void
diff --git a/runtime/vm/j9vm.tdf b/runtime/vm/j9vm.tdf
index ab33afda52d..e6ebdec48c5 100644
--- a/runtime/vm/j9vm.tdf
+++ b/runtime/vm/j9vm.tdf
@@ -982,3 +982,7 @@ TraceEvent=Trc_VM_criu_toggleSuspendOnJavaThreads_walkStatus Overhead=1 Level=5
TraceEvent=Trc_VM_criu_toggleSuspendOnJavaThreads_walkThread Test Overhead=1 Level=5 Template="toggleSuspendOnJavaThreads with walkThreadName(%s) suspendResumeFlag(%u) walkThread(%p) currentThread(%p)"
TraceException=Trc_VM_jfr_ErrorWritingChunk Overhead=1 Level=1 Template="Error writing JFR chunk; error=%d"
+
+TraceEvent=Trc_VM_VMInitStages_isDebugOnRestoreEnabled NoEnv Overhead=1 Level=3 Template="VMInitStages(ABOUT_TO_BOOTSTRAP) isDebugOnRestoreEnabled returns TRUE"
+TraceEvent=Trc_VM_hookAboutToBootstrapEvent_debugModeRequested Overhead=1 Level=3 Template="hookAboutToBootstrapEvent() debugModeRequested is TRUE"
+TraceEvent=Trc_VM_mustReportEnterStepOrBreakpoint_hookedOrReserved NoEnv Overhead=1 Level=5 Template="mustReportEnterStepOrBreakpoint() hookedOrReserved(%zu)"
diff --git a/runtime/vm/jvminit.c b/runtime/vm/jvminit.c
index 044efbb20d7..1cbf6310ba7 100644
--- a/runtime/vm/jvminit.c
+++ b/runtime/vm/jvminit.c
@@ -2985,6 +2985,17 @@ VMInitStages(J9JavaVM *vm, IDATA stage, void* reserved)
goto _error;
}
#endif /* defined(J9VM_INTERP_ATOMIC_FREE_JNI_USES_FLUSH) */
+#if defined(J9VM_OPT_CRIU_SUPPORT)
+ if (isDebugOnRestoreEnabled(vm->mainThread)) {
+ Trc_VM_VMInitStages_isDebugOnRestoreEnabled();
+ /* enable jvmtiCapabilities.can_get_source_debug_extension */
+ vm->requiredDebugAttributes |= J9VM_DEBUG_ATTRIBUTE_SOURCE_DEBUG_EXTENSION;
+ /* enable jvmtiCapabilities.can_access_local_variables */
+ vm->requiredDebugAttributes |= J9VM_DEBUG_ATTRIBUTE_CAN_ACCESS_LOCALS;
+ /* enable jvmtiCapabilities.can_maintain_original_method_order */
+ vm->requiredDebugAttributes |= J9VM_DEBUG_ATTRIBUTE_MAINTAIN_ORIGINAL_METHOD_ORDER;
+ }
+#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */
TRIGGER_J9HOOK_VM_ABOUT_TO_BOOTSTRAP(vm->hookInterface, vm->mainThread);
/* At this point, the decision about which interpreter to use has been made */
diff --git a/runtime/vm/lookuphelper.c b/runtime/vm/lookuphelper.c
index b5d6c7ba375..789e68bef8c 100644
--- a/runtime/vm/lookuphelper.c
+++ b/runtime/vm/lookuphelper.c
@@ -29,17 +29,26 @@
#include "vm_internal.h"
UDATA
-mustReportEnterStepOrBreakpoint(J9JavaVM * vm)
+mustReportEnterStepOrBreakpoint(J9JavaVM *vm)
{
- J9HookInterface** hookInterface = J9_HOOK_INTERFACE(vm->hookInterface);
-
- return
- ((*hookInterface)->J9HookDisable(hookInterface, J9HOOK_VM_METHOD_ENTER) != 0) ||
- ((*hookInterface)->J9HookDisable(hookInterface, J9HOOK_VM_METHOD_RETURN) != 0) ||
- ((*hookInterface)->J9HookDisable(hookInterface, J9HOOK_VM_SINGLE_STEP) != 0) ||
- ((*hookInterface)->J9HookDisable(hookInterface, J9HOOK_VM_BREAKPOINT) != 0);
-}
-
-
+ J9HookInterface **hookInterface = J9_HOOK_INTERFACE(vm->hookInterface);
+ UDATA hookedOrReserved = 0;
+#if defined(J9VM_OPT_CRIU_SUPPORT)
+ if (isDebugOnRestoreEnabled(vm->mainThread)) {
+ hookedOrReserved = J9_EVENT_IS_HOOKED_OR_RESERVED(vm->hookInterface, J9HOOK_VM_METHOD_ENTER)
+ || J9_EVENT_IS_HOOKED_OR_RESERVED(vm->hookInterface, J9HOOK_VM_METHOD_RETURN)
+ || J9_EVENT_IS_HOOKED_OR_RESERVED(vm->hookInterface, J9HOOK_VM_SINGLE_STEP)
+ || J9_EVENT_IS_HOOKED_OR_RESERVED(vm->hookInterface, J9HOOK_VM_BREAKPOINT);
+ } else
+#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */
+ {
+ hookedOrReserved = (0 != (*hookInterface)->J9HookDisable(hookInterface, J9HOOK_VM_METHOD_ENTER)) ||
+ (0 != (*hookInterface)->J9HookDisable(hookInterface, J9HOOK_VM_METHOD_RETURN)) ||
+ (0 != (*hookInterface)->J9HookDisable(hookInterface, J9HOOK_VM_SINGLE_STEP)) ||
+ (0 != (*hookInterface)->J9HookDisable(hookInterface, J9HOOK_VM_BREAKPOINT));
+ }
+ Trc_VM_mustReportEnterStepOrBreakpoint_hookedOrReserved(hookedOrReserved);
+ return hookedOrReserved;
+}
diff --git a/runtime/vm/vmhook.c b/runtime/vm/vmhook.c
index ce6e0637d93..489053eafad 100644
--- a/runtime/vm/vmhook.c
+++ b/runtime/vm/vmhook.c
@@ -106,19 +106,25 @@ hookRegistrationEvent(J9HookInterface** hook, UDATA eventNum, void* voidEventDat
}
}
-
/*
* The VM is about to start loading classes.
- * Disable hooks which must have been hooked by now
+ * Disable hooks which must have been hooked by now if isDebugOnRestoreEnabled() returns false,
+ * otherwise, check if the debug related events are hooked or reserved instead.
+ *
+ * @param[in] hook The pointer to the hook interface, not used
+ * @param[in] eventNum not used
+ * @param[in] voidEventData J9VMAboutToBootstrapEvent containing currentThread
+ * @param[in] userData not used
*/
static void
-hookAboutToBootstrapEvent(J9HookInterface** hook, UDATA eventNum, void* voidEventData, void* userData)
+hookAboutToBootstrapEvent(J9HookInterface **hook, UDATA eventNum, void *voidEventData, void *userData)
{
- J9VMAboutToBootstrapEvent* eventData = voidEventData;
- J9VMThread* vmThread = eventData->currentThread;
- J9JavaVM* vm = vmThread->javaVM;
- J9HookInterface** vmHook = vm->internalVMFunctions->getVMHookInterface(vm);
- J9HookInterface** gcHook = vm->memoryManagerFunctions->j9gc_get_hook_interface(vm);
+ J9VMAboutToBootstrapEvent *eventData = voidEventData;
+ J9VMThread *vmThread = eventData->currentThread;
+ J9JavaVM *vm = vmThread->javaVM;
+ J9HookInterface **vmHook = getVMHookInterface(vm);
+ J9HookInterface **gcHook = vm->memoryManagerFunctions->j9gc_get_hook_interface(vm);
+ BOOLEAN debugModeRequested = FALSE;
/* these hooks must be reserved by now. Attempt to disable them so that they're in a well-known state after this */
(*vmHook)->J9HookDisable(vmHook, J9HOOK_VM_MONITOR_CONTENDED_EXIT);
@@ -137,27 +143,44 @@ hookAboutToBootstrapEvent(J9HookInterface** hook, UDATA eventNum, void* voidEven
omrthread_monitor_exit(vm->runtimeFlagsMutex);
}
- if ((*vmHook)->J9HookDisable(vmHook, J9HOOK_VM_METHOD_ENTER)
- || (*vmHook)->J9HookDisable(vmHook, J9HOOK_VM_METHOD_RETURN)
- || (*vmHook)->J9HookDisable(vmHook, J9HOOK_VM_FRAME_POP)
- || (*vmHook)->J9HookDisable(vmHook, J9HOOK_VM_POP_FRAMES_INTERRUPT)
- || (*vmHook)->J9HookDisable(vmHook, J9HOOK_VM_SINGLE_STEP)
- || (*vmHook)->J9HookDisable(vmHook, J9HOOK_VM_BREAKPOINT)
- || (*vmHook)->J9HookDisable(vmHook, J9HOOK_VM_GET_FIELD)
- || (*vmHook)->J9HookDisable(vmHook, J9HOOK_VM_PUT_FIELD)
- || (*vmHook)->J9HookDisable(vmHook, J9HOOK_VM_GET_STATIC_FIELD)
- || (*vmHook)->J9HookDisable(vmHook, J9HOOK_VM_PUT_STATIC_FIELD)
- || (vm->extendedRuntimeFlags & J9_EXTENDED_RUNTIME_METHOD_TRACE_ENABLED)
- || (vm->requiredDebugAttributes & J9VM_DEBUG_ATTRIBUTE_CAN_ACCESS_LOCALS))
+#if defined(J9VM_OPT_CRIU_SUPPORT)
+ if (isDebugOnRestoreEnabled(vmThread)) {
+ debugModeRequested = J9_EVENT_IS_HOOKED_OR_RESERVED(vm->hookInterface, J9HOOK_VM_METHOD_ENTER)
+ || J9_EVENT_IS_HOOKED_OR_RESERVED(vm->hookInterface, J9HOOK_VM_METHOD_RETURN)
+ || J9_EVENT_IS_HOOKED_OR_RESERVED(vm->hookInterface, J9HOOK_VM_FRAME_POP)
+ || J9_EVENT_IS_HOOKED_OR_RESERVED(vm->hookInterface, J9HOOK_VM_POP_FRAMES_INTERRUPT)
+ || J9_EVENT_IS_HOOKED_OR_RESERVED(vm->hookInterface, J9HOOK_VM_SINGLE_STEP)
+ || J9_EVENT_IS_HOOKED_OR_RESERVED(vm->hookInterface, J9HOOK_VM_BREAKPOINT)
+ || J9_EVENT_IS_HOOKED_OR_RESERVED(vm->hookInterface, J9HOOK_VM_GET_FIELD)
+ || J9_EVENT_IS_HOOKED_OR_RESERVED(vm->hookInterface, J9HOOK_VM_PUT_FIELD)
+ || J9_EVENT_IS_HOOKED_OR_RESERVED(vm->hookInterface, J9HOOK_VM_GET_STATIC_FIELD)
+ || J9_EVENT_IS_HOOKED_OR_RESERVED(vm->hookInterface, J9HOOK_VM_PUT_STATIC_FIELD);
+ } else
+#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */
{
+ debugModeRequested = (*vmHook)->J9HookDisable(vmHook, J9HOOK_VM_METHOD_ENTER)
+ || (*vmHook)->J9HookDisable(vmHook, J9HOOK_VM_METHOD_RETURN)
+ || (*vmHook)->J9HookDisable(vmHook, J9HOOK_VM_FRAME_POP)
+ || (*vmHook)->J9HookDisable(vmHook, J9HOOK_VM_POP_FRAMES_INTERRUPT)
+ || (*vmHook)->J9HookDisable(vmHook, J9HOOK_VM_SINGLE_STEP)
+ || (*vmHook)->J9HookDisable(vmHook, J9HOOK_VM_BREAKPOINT)
+ || (*vmHook)->J9HookDisable(vmHook, J9HOOK_VM_GET_FIELD)
+ || (*vmHook)->J9HookDisable(vmHook, J9HOOK_VM_PUT_FIELD)
+ || (*vmHook)->J9HookDisable(vmHook, J9HOOK_VM_GET_STATIC_FIELD)
+ || (*vmHook)->J9HookDisable(vmHook, J9HOOK_VM_PUT_STATIC_FIELD);
+ }
+ debugModeRequested |= J9_ARE_ANY_BITS_SET(vm->extendedRuntimeFlags,
+ J9_EXTENDED_RUNTIME_METHOD_TRACE_ENABLED);
+ debugModeRequested |= J9_ARE_ANY_BITS_SET(vm->requiredDebugAttributes,
+ J9VM_DEBUG_ATTRIBUTE_CAN_ACCESS_LOCALS);
+ if (debugModeRequested) {
+ Trc_VM_hookAboutToBootstrapEvent_debugModeRequested(vmThread);
omrthread_monitor_enter(vm->runtimeFlagsMutex);
vm->extendedRuntimeFlags |= J9_EXTENDED_RUNTIME_DEBUG_MODE;
omrthread_monitor_exit(vm->runtimeFlagsMutex);
}
}
-
-
#if defined(J9VM_INTERP_NATIVE_SUPPORT)
/*
* Returns the JIT's hook interface.
diff --git a/test/functional/cmdLineTests/criu/criu_jitPostRestore.xml b/test/functional/cmdLineTests/criu/criu_jitPostRestore.xml
index 3f9b9554070..5c4d863d508 100644
--- a/test/functional/cmdLineTests/criu/criu_jitPostRestore.xml
+++ b/test/functional/cmdLineTests/criu/criu_jitPostRestore.xml
@@ -72,8 +72,9 @@
-
-
+
+
@@ -90,8 +91,9 @@
-
-
+
+