Skip to content

Commit

Permalink
CRIU DebugOnRestore mode is disabled when debug events are hooked
Browse files Browse the repository at this point in the history
isDebugOnRestoreEnabled() returns false if some debug-specific
flags/events are enabled/hooked, this matches J9::Options::isFSDNeeded()
in compiler/control/J9Options.cpp;
DebugOnRestore mode is enabled when CRIU is enabled, -XX:+DebugOnRestore
is specified, JDWP is not enabled, and no debug-specific flags/events
are enabled/hooked;
When DebugOnRestore mode is enabled, debug related events
and hooks can't be enabled or hooked pre-checkpoint,
isDebugAgentDisabled() returns TRUE in this case, otherwise FALSE.

Signed-off-by: Jason Feng <[email protected]>
  • Loading branch information
JasonFengJ9 committed Jan 21, 2025
1 parent cccf21d commit a048979
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 68 deletions.
26 changes: 17 additions & 9 deletions runtime/jvmti/jvmtiCapability.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@ jvmtiGetPotentialCapabilities(jvmtiEnv* env, jvmtiCapabilities* capabilities_ptr
J9JavaVM * vm = j9env->vm;
J9JVMTIData * jvmtiData = J9JVMTI_DATA_FROM_VM(vm);
jvmtiCapabilities rv_capabilities;
J9HookInterface ** vmHook = vm->internalVMFunctions->getVMHookInterface(vm);
J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
J9HookInterface **vmHook = vmFuncs->getVMHookInterface(vm);
jvmtiError rc;

Trc_JVMTI_jvmtiGetPotentialCapabilities_Entry(env);
Expand Down Expand Up @@ -206,8 +207,13 @@ jvmtiGetPotentialCapabilities(jvmtiEnv* env, jvmtiCapabilities* capabilities_ptr
}

if ((*vmHook)->J9HookIsEnabled(vmHook, J9HOOK_VM_POP_FRAMES_INTERRUPT)) {
rv_capabilities.can_pop_frame = 1;
rv_capabilities.can_force_early_return = 1;
#if defined(J9VM_OPT_CRIU_SUPPORT)
if (!vmFuncs->isDebugAgentDisabled(vm))
#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */
{
rv_capabilities.can_pop_frame = 1;
rv_capabilities.can_force_early_return = 1;
}
}

if ((*vmHook)->J9HookIsEnabled(vmHook, J9HOOK_VM_REQUIRED_DEBUG_ATTRIBUTES) ||
Expand All @@ -231,7 +237,12 @@ jvmtiGetPotentialCapabilities(jvmtiEnv* env, jvmtiCapabilities* capabilities_ptr
if ((*vmHook)->J9HookIsEnabled(vmHook, J9HOOK_VM_REQUIRED_DEBUG_ATTRIBUTES) ||
(vm->requiredDebugAttributes & J9VM_DEBUG_ATTRIBUTE_CAN_ACCESS_LOCALS)
) {
rv_capabilities.can_access_local_variables = 1;
#if defined(J9VM_OPT_CRIU_SUPPORT)
if (!vmFuncs->isDebugAgentDisabled(vm))
#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */
{
rv_capabilities.can_access_local_variables = 1;
}
}

rv_capabilities.can_tag_objects = 1;
Expand Down Expand Up @@ -548,16 +559,13 @@ mapCapabilitiesToEvents(J9JVMTIEnv * j9env, jvmtiCapabilities * capabilities, J9

#if defined(J9VM_OPT_CRIU_SUPPORT)
J9JavaVM *vm = j9env->vm;
J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
BOOLEAN skipHookReserve = vmFuncs->isCheckpointAllowed(vm)
&& vmFuncs->isDebugOnRestoreEnabled(vm);
/* Skip J9HookReserve for the events required by JDWP agent pre-checkpoint when DebugOnRestore is enabled,
/* Skip J9HookReserve for the events required by JDWP agent pre-checkpoint when isDebugAgentDisabled() returns TRUE,
* 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)
if (!vm->internalVMFunctions->isDebugAgentDisabled(vm))
#endif /* defined(J9VM_OPT_CRIU_SUPPORT)*/
{
if (capabilities->can_generate_single_step_events) {
Expand Down
71 changes: 27 additions & 44 deletions runtime/jvmti/jvmtiHook.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,6 @@ 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
Expand Down Expand Up @@ -557,37 +555,6 @@ 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;
J9InternalVMFunctions const * const vmFuncs = vm->internalVMFunctions;

Trc_JVMTI_jvmtiHookVMRestoreStartAgent_Entry();
vmFuncs->internalExitVMToJNI(currentThread);
if (J9_ARE_ANY_BITS_SET(vm->checkpointState.flags, J9VM_CRIU_IS_JDWP_ENABLED)) {
criuRestoreStartAgent(vm);
} 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);
}
vmFuncs->internalEnterVMFromJNI(currentThread);
TRACE_JVMTI_EVENT_RETURN(jvmtiHookVMRestoreStartAgent);
}

static void
jvmtiHookVMRestore(J9HookInterface **hook, UDATA eventNum, void *eventData, void *userData)
{
Expand Down Expand Up @@ -1075,6 +1042,21 @@ jvmtiHookClassLoad(J9HookInterface** hook, UDATA eventNum, void* eventData, void
UDATA
isEventHookable(J9JVMTIEnv * j9env, jvmtiEvent event)
{
#if defined(J9VM_OPT_CRIU_SUPPORT)
J9JavaVM *vm = j9env->vm;
if (vm->internalVMFunctions->isDebugAgentDisabled(vm)) {
switch(event) {
case JVMTI_EVENT_FRAME_POP: /* fall through */
case JVMTI_EVENT_FIELD_ACCESS: /* fall through */
case JVMTI_EVENT_FIELD_MODIFICATION: /* fall through */
case JVMTI_EVENT_SINGLE_STEP: /* fall through */
case JVMTI_EVENT_BREAKPOINT:
return FALSE;
default:
break;
}
}
#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */
return processEvent(j9env, event, hookIsDisabled) == 0;
}

Expand Down Expand Up @@ -1136,6 +1118,18 @@ hookNonEventCapabilities(J9JVMTIEnv * j9env, jvmtiCapabilities * capabilities)
J9JVMTIHookInterfaceWithID * gcOmrHook = &j9env->gcOmrHook;
J9JVMTIData * jvmtiData = J9JVMTI_DATA_FROM_VM(vm);

#if defined(J9VM_OPT_CRIU_SUPPORT)
if (vm->internalVMFunctions->isDebugAgentDisabled(vm)) {
if (capabilities->can_pop_frame
|| capabilities->can_force_early_return
|| capabilities->can_access_local_variables
|| capabilities->can_generate_breakpoint_events
) {
return 1;
}
}
#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */

if (capabilities->can_generate_breakpoint_events) {
if (hookRegister(vmHook, J9HOOK_VM_BREAKPOINT, jvmtiHookBreakpoint, OMR_GET_CALLSITE(), j9env)) {
return 1;
Expand Down Expand Up @@ -2046,17 +2040,6 @@ hookGlobalEvents(J9JVMTIData * jvmtiData)
return 1;
}

#if defined(J9VM_OPT_CRIU_SUPPORT)
if (vm->internalVMFunctions->isDebugOnRestoreEnabled(vm)) {
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;
}
Expand Down
98 changes: 90 additions & 8 deletions runtime/jvmti/jvmtiStartup.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ I_32 JNICALL loadAgentLibraryOnAttach(struct J9JavaVM *vm, const char *library,
static BOOLEAN isAgentLibraryLoaded(J9JavaVM *vm, const char *library, BOOLEAN decorate);
static jint createAgentLibraryWithOption(J9JavaVM *vm, J9VMInitArgs *argsList, IDATA agentIndex, J9JVMTIAgentLibrary **agentLibrary, CreateAgentOption createAgentOption, BOOLEAN *isJDWPagent);
static BOOLEAN processAgentLibraryFromArgsList(J9JavaVM *vm, J9VMInitArgs *argsList, BOOLEAN loadLibrary, CreateAgentOption createAgentOption);
#if defined(J9VM_OPT_CRIU_SUPPORT)
static void jvmtiHookVMRestoreCRIUInit(J9HookInterface **hook, UDATA eventNum, void *eventData, void *userData);
static void jvmtiHookVMRestoreStartAgent(J9HookInterface **hook, UDATA eventNum, void *eventData, void *userData);
#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */

#define INSTRUMENT_LIBRARY "instrument"

Expand Down Expand Up @@ -267,6 +271,53 @@ processAgentLibraryFromArgsList(J9JavaVM *vm, J9VMInitArgs *argsList, BOOLEAN lo
}

#if defined(J9VM_OPT_CRIU_SUPPORT)
/**
* A hook method to invoke criuRestoreInitializeLib().
*
* @param[in] hook the VM hook interface, not used
* @param[in] eventNum the event number, not used
* @param[in] eventData the event data, not used
* @param[in] userData the registered user data
*/
static void
jvmtiHookVMRestoreCRIUInit(J9HookInterface **hook, UDATA eventNum, void *eventData, void *userData)
{
Trc_JVMTI_jvmtiHookVMRestoreCRIUInit_Entry();
criuRestoreInitializeLib(((J9RestoreEvent *)eventData)->currentThread->javaVM, (J9JVMTIEnv *)userData);
Trc_JVMTI_jvmtiHookVMRestoreCRIUInit_Exit();
}

/**
* A hook method to start agents or cleanup post-restore.
*
* @param[in] hook the VM hook interface, not used
* @param[in] eventNum the event number, not used
* @param[in] eventData the event data
* @param[in] userData the registered user data, not used
*/
static void
jvmtiHookVMRestoreStartAgent(J9HookInterface **hook, UDATA eventNum, void *eventData, void *userData)
{
J9VMThread *currentThread = ((J9RestoreEvent *)eventData)->currentThread;
J9JavaVM *vm = currentThread->javaVM;
J9InternalVMFunctions const * const vmFuncs = vm->internalVMFunctions;

Trc_JVMTI_jvmtiHookVMRestoreStartAgent_Entry();
vmFuncs->internalExitVMToJNI(currentThread);
if (J9_ARE_ANY_BITS_SET(vm->checkpointState.flags, J9VM_CRIU_IS_JDWP_ENABLED)) {
criuRestoreStartAgent(vm);
} 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);
}
vmFuncs->internalEnterVMFromJNI(currentThread);
Trc_JVMTI_jvmtiHookVMRestoreStartAgent_Exit();
}

/**
* Add JVMTI capabilities before checkpoint.
* This is required for debugger support when JIT is enabled.
Expand Down Expand Up @@ -418,14 +469,45 @@ IDATA J9VMDllMain(J9JavaVM *vm, IDATA stage, void *reserved)
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)) {
Trc_JVMTI_criuAddCapabilities_invoked();
/* ignore the failure, it won't cause a problem if JDWP is not enabled later */
criuAddCapabilities(vm, NULL != vm->jitConfig);
{
/* The isDebugEventOrFlagEnabled calculation matches a part of J9::Options::isFSDNeeded()
* in compiler/control/J9Options.cpp.
*/
BOOLEAN isDebugEventOrFlagEnabled = J9_EVENT_IS_HOOKED_OR_RESERVED(vm->hookInterface, J9HOOK_VM_BREAKPOINT)
|| J9_EVENT_IS_HOOKED_OR_RESERVED(vm->hookInterface, J9HOOK_VM_FRAME_POP)
|| J9_EVENT_IS_HOOKED_OR_RESERVED(vm->hookInterface, J9HOOK_VM_FRAME_POPPED)
|| 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)
#if defined (J9VM_INTERP_HOT_CODE_REPLACEMENT)
|| J9_EVENT_IS_HOOKED_OR_RESERVED(vm->hookInterface, J9HOOK_VM_POP_FRAMES_INTERRUPT)
#endif
#if defined(J9VM_JIT_FULL_SPEED_DEBUG)
|| (vm->requiredDebugAttributes & J9VM_DEBUG_ATTRIBUTE_CAN_ACCESS_LOCALS)
#endif
|| J9_EVENT_IS_HOOKED_OR_RESERVED(vm->hookInterface, J9HOOK_VM_SINGLE_STEP);
J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;
BOOLEAN isDebugOnRestoreEnabled = !isDebugEventOrFlagEnabled
&& J9_ARE_ALL_BITS_SET(vm->checkpointState.flags, J9VM_CRIU_SUPPORT_DEBUG_ON_RESTORE)
&& vmFuncs->isCRaCorCRIUSupportEnabled(vm);

if (isDebugOnRestoreEnabled) {
J9HookInterface ** vmHook = vmFuncs->getVMHookInterface(vm);
if ((*vmHook)->J9HookRegisterWithCallSite(vmHook, J9HOOK_TAG_AGENT_ID | J9HOOK_VM_PREPARING_FOR_RESTORE, jvmtiHookVMRestoreCRIUInit, OMR_GET_CALLSITE(), jvmtiData, J9HOOK_AGENTID_FIRST)) {
goto _error;
}
if ((*vmHook)->J9HookRegisterWithCallSite(vmHook, J9HOOK_TAG_AGENT_ID | J9HOOK_VM_CRIU_RESTORE, jvmtiHookVMRestoreStartAgent, OMR_GET_CALLSITE(), jvmtiData, J9HOOK_AGENTID_FIRST)) {
goto _error;
}
/* Adding capabilities is required before checkpoint if JIT is enabled.
* Following code can be removed when JIT allows capabilities to be added after restore.
*/
Trc_JVMTI_criuAddCapabilities_invoked();
/* ignore the failure, it won't cause a problem if JDWP is not enabled later */
criuAddCapabilities(vm, NULL != vm->jitConfig);
vm->checkpointState.isDebugOnRestoreEnabled = isDebugOnRestoreEnabled;
}
}
#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */

Expand Down
2 changes: 2 additions & 0 deletions runtime/oti/j9nonbuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -4456,6 +4456,7 @@ typedef struct J9CRIUCheckpointState {
UDATA javaDebugThreadCount;
jvmtiEnv *jvmtienv;
jvmtiCapabilities requiredCapabilities;
BOOLEAN isDebugOnRestoreEnabled;
} J9CRIUCheckpointState;
#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */

Expand Down Expand Up @@ -5247,6 +5248,7 @@ typedef struct J9InternalVMFunctions {
BOOLEAN (*isNonPortableRestoreMode)(struct J9VMThread *currentThread);
BOOLEAN (*isJVMInPortableRestoreMode)(struct J9VMThread *currentThread);
BOOLEAN (*isDebugOnRestoreEnabled)(struct J9JavaVM *vm);
BOOLEAN (*isDebugAgentDisabled)(struct J9JavaVM *vm);
void (*setRequiredGhostFileLimit)(struct J9VMThread *currentThread, U_32 ghostFileLimit);
BOOLEAN (*runInternalJVMCheckpointHooks)(struct J9VMThread *currentThread, const char **nlsMsgFormat);
BOOLEAN (*runInternalJVMRestoreHooks)(struct J9VMThread *currentThread, const char **nlsMsgFormat);
Expand Down
38 changes: 34 additions & 4 deletions runtime/oti/vm_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -583,17 +583,47 @@ BOOLEAN
isJVMInPortableRestoreMode(J9VMThread *currentThread);

/**
* @brief Queries if debug on restore (specified via
* -XX:+DebugOnRestore) is supported. If so, the JVM
* will run in FSD mode pre-checkpoint and will transition out
* FSD mode on restore (unless debug is specified post restore).
* @brief This is a helper to query if debug on restore mode is enabled.
*
* A few use scenarios:
* 1. If CRIU is not enabled, FSD is disabled by default unless JDWP or some
* debug-specific flags/events are enabled/hooked, there is no notion of
* DebugOnRestore;
* 2. If CRIU is enabled (via -XX:+EnableCRIUSupport) but -XX:+DebugOnRestore
* is not specified, this is the same as use case #1;
* 3. If CRIU is enabled and -XX:+DebugOnRestore is specified,
* 3.1 If JDWP or some debug-specific flags/events are enabled/hooked, FSD
* mode is enabled, and JIT doesn't do any pre-emptive recompilation
* and debugging is enabled.
* 3.2 Otherwise FSD code is generated but FSD mode is not enabled,
* 3.2.1 If FSD is enabled via the post-restore option file, JIT will
* transmit to the interpreter as soon as possible, or VM triggers
* a transition via the FSD code generated pre-checkpoint;
* 3.2.2 Otherwise JIT throws all FSD code and uses the pre-emptively
* recompiled code in default mode.
*
* This helper method returns TRUE if CRIU is enabled, -XX:+DebugOnRestore is
* specified, JDWP is not enabled, and no debug-specific flags/events are
* enabled/hooked. JIT generates FSD code but FSD mode is not enabled.
* Otherwise, this returns FALSE, JIT checks if debug related flags/events are
* enabled/hooked and determines if FSD mode is to be enabled.
*
* @param vm javaVM token
* @return TRUE if enabled, FALSE otherwise
*/
BOOLEAN
isDebugOnRestoreEnabled(J9JavaVM *vm);

/**
* @brief This is a helper to query if the debug agent is disabled.
*
* @param vm javaVM token
* @return TRUE if isDebugOnRestoreEnabled() and isCheckpointAllowed() return TRUE,
* FALSE otherwise
*/
BOOLEAN
isDebugAgentDisabled(J9JavaVM *vm);

/**
* @brief Sets the maximum size for the CRIU ghost files.
* If the new limit is smaller or equal to the previous limit,
Expand Down
10 changes: 7 additions & 3 deletions runtime/vm/CRIUHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,13 @@ isJVMInPortableRestoreMode(J9VMThread *currentThread)
BOOLEAN
isDebugOnRestoreEnabled(J9JavaVM *vm)
{
return J9_ARE_NO_BITS_SET(vm->checkpointState.flags, J9VM_CRIU_IS_JDWP_ENABLED)
&& J9_ARE_ALL_BITS_SET(vm->checkpointState.flags, J9VM_CRIU_SUPPORT_DEBUG_ON_RESTORE)
&& isCRaCorCRIUSupportEnabled(vm);
return vm->checkpointState.isDebugOnRestoreEnabled;
}

BOOLEAN
isDebugAgentDisabled(J9JavaVM *vm)
{
return isCheckpointAllowed(vm) && vm->checkpointState.isDebugOnRestoreEnabled;
}

void
Expand Down
1 change: 1 addition & 0 deletions runtime/vm/intfunc.c
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ J9InternalVMFunctions J9InternalFunctions = {
isNonPortableRestoreMode,
isJVMInPortableRestoreMode,
isDebugOnRestoreEnabled,
isDebugAgentDisabled,
setRequiredGhostFileLimit,
runInternalJVMCheckpointHooks,
runInternalJVMRestoreHooks,
Expand Down

0 comments on commit a048979

Please sign in to comment.