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 @@ Post-checkpoint CRIU is not enabled Operation not permitted - Some or all compiled code in the code cache invalidated post restore. - JIT compilation disabled post restore. + + AOT load and compilation disabled post restore. Thread pid mismatch @@ -90,8 +91,9 @@ CRIU is not enabled Operation not permitted Some or all compiled code in the code cache invalidated post restore. - JIT compilation disabled post restore. - AOT load and compilation disabled post restore. + + Thread pid mismatch do not match expected