Skip to content

Commit ae65960

Browse files
singh264tajilaJasonFengJ9keithc-ca
committed
Support restore with -XX:CRaCRestoreFrom=PATH
Support restore with -XX:CRaCRestoreFrom=PATH that specifies the path to the checkpoint image. Issue: eclipse-openj9/openj9#19261 Co-authored-by: Tobi Ajila <[email protected]> Co-authored-by: Jason Feng <[email protected]> Co-authored-by: Keith W. Campbell <[email protected]> Signed-off-by: Amarpreet Singh <[email protected]>
1 parent 0b87eb4 commit ae65960

File tree

1 file changed

+319
-1
lines changed
  • src/java.base/share/native/libjli

1 file changed

+319
-1
lines changed

src/java.base/share/native/libjli/java.c

+319-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
/*
2727
* ===========================================================================
28-
* (c) Copyright IBM Corp. 2022, 2023 All Rights Reserved
28+
* (c) Copyright IBM Corp. 2022, 2024 All Rights Reserved
2929
* ===========================================================================
3030
*/
3131

@@ -61,6 +61,11 @@
6161

6262
#include "java.h"
6363
#include "jni.h"
64+
#include "j9cfg.h"
65+
#if defined(J9VM_OPT_CRAC_SUPPORT)
66+
#include <ctype.h>
67+
#include <sys/wait.h>
68+
#endif /* defined(J9VM_OPT_CRAC_SUPPORT) */
6469

6570
/*
6671
* A NOTE TO DEVELOPERS: For performance reasons it is important that
@@ -543,6 +548,315 @@ invokeInstanceMainWithoutArgs(JNIEnv *env, jclass mainClass) {
543548
return 1; // method was invoked
544549
}
545550

551+
#if defined(J9VM_OPT_CRAC_SUPPORT)
552+
/**
553+
* Get the value of a command line option in the array of command line arguments.
554+
* @param[in] optionName The name of the command line option to search for
555+
* @param[in] argc The number of command line arguments
556+
* @param[in] argv The array of command line arguments
557+
* @param[out] error A pointer to an integer for the error code
558+
* @return A pointer to the value of the command line option if found, NULL otherwise
559+
*/
560+
static const char *
561+
getCommandLineOptionValue(const char *optionName, int argc, char **argv, int *error)
562+
{
563+
const char *value = NULL;
564+
int i = 0;
565+
size_t optionNameLength = strlen(optionName);
566+
jboolean optionNameFound = JNI_FALSE;
567+
*error = 0;
568+
for (i = 0; i < argc; i++) {
569+
const char *arg = argv[i];
570+
if (0 == strncmp(arg, optionName, optionNameLength)) {
571+
const char *equals = arg + optionNameLength;
572+
if (('=' == *equals) || ('\0' == *equals)) {
573+
optionNameFound = JNI_TRUE;
574+
value = equals;
575+
if ('=' == *value) {
576+
value += 1;
577+
}
578+
if ('\0' == *value) {
579+
value = NULL;
580+
}
581+
break;
582+
}
583+
}
584+
}
585+
if (!optionNameFound) {
586+
*error = -1;
587+
}
588+
return value;
589+
}
590+
591+
/**
592+
* Get the checkpoint directory from command line options.
593+
* @param[in] argc The number of command line arguments
594+
* @param[in] argv The array of command line arguments
595+
* @param[in/out] error A pointer to an integer for the error code
596+
* @return A pointer to the checkpoint directory if found, NULL otherwise
597+
*/
598+
static const char *
599+
getCheckpointDirectory(int argc, char **argv, int *error)
600+
{
601+
const char *checkpointDirectory = NULL;
602+
const char *checkpointDirectoryPropertyValue = getCommandLineOptionValue("-XX:CRaCRestoreFrom", argc, argv, error);
603+
if (0 == *error) {
604+
if (NULL != checkpointDirectoryPropertyValue) {
605+
checkpointDirectory = checkpointDirectoryPropertyValue;
606+
} else {
607+
JLI_ReportErrorMessage("The value of the command line option -XX:CRaCRestoreFrom was not found.");
608+
*error = -2;
609+
}
610+
}
611+
return checkpointDirectory;
612+
}
613+
614+
/**
615+
* Get the log level specified in the command line arguments.
616+
* @param[in] argc The number of command line arguments
617+
* @param[in] argv The array of command line arguments
618+
* @param[in/out] error A pointer to an integer for the error code
619+
* @return The log level integer if successful, default of 2 otherwise
620+
*/
621+
int
622+
getLogLevel(int argc, char **argv, int *error)
623+
{
624+
int logLevelValue = 2; /* default */
625+
const char *logLevelPropertyValue = NULL;
626+
logLevelPropertyValue = getCommandLineOptionValue("-Dopenj9.internal.criu.logLevel", argc, argv, error);
627+
if (0 == *error) {
628+
const char *c = logLevelPropertyValue;
629+
if (NULL == c) {
630+
goto done;
631+
}
632+
for (; '\0' != *c; c++) {
633+
if (!isdigit(*c)) {
634+
goto setLogLevelOptionValueNotValidError;
635+
}
636+
}
637+
logLevelValue = atoi(logLevelPropertyValue);
638+
if ((0 <= logLevelValue) && (logLevelValue <= 4)) {
639+
goto done;
640+
} else {
641+
goto setLogLevelOptionValueNotValidError;
642+
}
643+
} else if (-1 == *error) {
644+
goto done;
645+
}
646+
setLogLevelOptionValueNotValidError:
647+
JLI_ReportErrorMessage("The value of the command line option is not valid, option=-Dopenj9.internal.criu.logLevel, value=%s.", logLevelPropertyValue);
648+
*error = -2;
649+
done:
650+
return logLevelValue;
651+
}
652+
653+
/**
654+
* Check if the unprivileged mode is specified in the command line arguments.
655+
* @param[in] argc The number of command line arguments
656+
* @param[in] argv The array of command line arguments
657+
* @param[in/out] error A pointer to an integer for the error code
658+
* @return true if the unprivileged mode option is specified in the command line arguments, false otherwise
659+
*/
660+
static jboolean
661+
isUnprivilegedModeOn(int argc, char **argv, int *error)
662+
{
663+
jboolean isUnprivilegedModeOn = JNI_FALSE;
664+
const char *unprivilegedModePropertyValue = getCommandLineOptionValue("-Dopenj9.internal.criu.unprivilegedMode", argc, argv, error);
665+
if (0 == *error) {
666+
if (NULL == unprivilegedModePropertyValue) {
667+
isUnprivilegedModeOn = JNI_TRUE;
668+
} else {
669+
JLI_ReportErrorMessage("The value of the command line option is not expected, option=-Dopenj9.internal.criu.unprivilegedMode, value=%s.", unprivilegedModePropertyValue);
670+
*error = -2;
671+
}
672+
}
673+
return isUnprivilegedModeOn;
674+
}
675+
676+
/**
677+
* Get the log file specified in the command line arguments.
678+
* @param[in] argc The number of command line arguments
679+
* @param[in] argv The array of command line arguments
680+
* @param[in/out] error A pointer to an integer for the error code
681+
* @return A pointer to the log file string if successful, NULL otherwise
682+
*/
683+
static const char *
684+
getLogFile(int argc, char **argv, int *error)
685+
{
686+
const char *logFile = NULL;
687+
const char *logFilePropertyValue = getCommandLineOptionValue("-Dopenj9.internal.criu.logFile", argc, argv, error);
688+
if (0 == *error) {
689+
if (NULL != logFilePropertyValue) {
690+
logFile = logFilePropertyValue;
691+
} else {
692+
JLI_ReportErrorMessage("The value of the command line option -Dopenj9.internal.criu.logFile was not found.");
693+
*error = -2;
694+
}
695+
}
696+
return logFile;
697+
}
698+
699+
/**
700+
* Restore the system state from a checkpoint using the CRIU tool.
701+
* @param[in] checkpointDirectory The directory containing the checkpoint data
702+
* @param[in] logLevel The log level for CRIU logging
703+
* @param[in] unprivilegedModeOn Indicates whether the unprivileged mode option is on
704+
* @param[in] logFile The log file option for CRIU
705+
* @return 0 if the execution of the 'criu restore' command succeeds, -1 otherwise
706+
*/
707+
static int
708+
restoreFromCheckpoint(const char *checkpointDirectory, int logLevel, jboolean unprivilegedModeOn, const char *logFile)
709+
{
710+
int restoreStatus = 0;
711+
int length = -1;
712+
char *logLevelOption = NULL;
713+
char *logFileOption = NULL;
714+
int argc = 0;
715+
const char *argv[9] = { NULL };
716+
argv[argc++] = "criu";
717+
argv[argc++] = "restore";
718+
argv[argc++] = "-D";
719+
argv[argc++] = checkpointDirectory;
720+
length = snprintf(NULL, 0, "-v%d", logLevel);
721+
if (length < 0) {
722+
char logLevelString[2] = { 0 };
723+
sprintf(logLevelString, "%d", logLevel);
724+
JLI_ReportErrorMessage("Failed to calculate the length of the command option, value=%s, format=%s.", logLevelString, "-v%d");
725+
restoreStatus = -1;
726+
goto done;
727+
}
728+
logLevelOption = (char *)JLI_MemAlloc(length + 1);
729+
if (NULL == logLevelOption) {
730+
char logLevelString[2] = { 0 };
731+
sprintf(logLevelString, "%d", logLevel);
732+
JLI_ReportErrorMessage("Failed to allocate memory for the command option, value=%s, format=%s.", logLevelString, "-v%d");
733+
restoreStatus = -1;
734+
goto done;
735+
}
736+
if (snprintf(logLevelOption, length + 1, "-v%d", logLevel) < 0) {
737+
char logLevelString[2] = { 0 };
738+
sprintf(logLevelString, "%d", logLevel);
739+
JLI_ReportErrorMessage("Failed to allocate memory for the command option, value=%s, format=%s.", logLevelString, "-v%d");
740+
restoreStatus = -1;
741+
goto freeLogLevelOption;
742+
}
743+
argv[argc++] = logLevelOption;
744+
argv[argc++] = "--shell-job";
745+
if (unprivilegedModeOn) {
746+
argv[argc++] = "--unprivileged";
747+
}
748+
if (NULL != logFile) {
749+
length = snprintf(NULL, 0, "--log-file=%s", logFile);
750+
if (length < 0) {
751+
JLI_ReportErrorMessage("Failed to calculate the length of the command option, value=%s, format=%s.", logFile, "--log-file=%s");
752+
restoreStatus = -1;
753+
goto freeLogLevelOption;
754+
}
755+
logFileOption = (char *)JLI_MemAlloc(length + 1);
756+
if (NULL == logFileOption) {
757+
JLI_ReportErrorMessage("Failed to allocate memory for the command option, value=%s, format=%s.", logFile, "--log-file=%s");
758+
restoreStatus = -1;
759+
goto freeLogLevelOption;
760+
}
761+
if (snprintf(logFileOption, length + 1, "--log-file=%s", logFile) < 0) {
762+
JLI_ReportErrorMessage("Failed to allocate memory for the command option, value=%s, format=%s.", logFile, "--log-file=%s");
763+
restoreStatus = -1;
764+
goto freeLogFileOption;
765+
}
766+
argv[argc++] = logFileOption;
767+
}
768+
argv[argc] = NULL;
769+
execvp(argv[0], (char * const *)argv);
770+
/* If execvp returns, there was an error. */
771+
restoreStatus = -1;
772+
freeLogFileOption:
773+
if (logFileOption != NULL) {
774+
JLI_MemFree((void *)logFileOption);
775+
logFileOption = NULL;
776+
}
777+
freeLogLevelOption:
778+
if (logLevelOption != NULL) {
779+
JLI_MemFree((void *)logLevelOption);
780+
logLevelOption = NULL;
781+
}
782+
done:
783+
return restoreStatus;
784+
}
785+
786+
/**
787+
* Handle the restoration of the system state from a checkpoint.
788+
* @param[in] argc The number of command line arguments
789+
* @param[in] argv The array of command line arguments
790+
*/
791+
static void
792+
handleCRaCRestore(int argc, char **argv)
793+
{
794+
const char *checkpointDirectory = NULL;
795+
int error = 0;
796+
int parentProcessExitStatus = EXIT_SUCCESS;
797+
pid_t childProcessPid = 0;
798+
int logLevel = 0;
799+
int childProcessExitStatus = EXIT_SUCCESS;
800+
jboolean unprivilegedModeOn = JNI_FALSE;
801+
const char *logFile = NULL;
802+
int childProcessPidStatus = 0;
803+
int childProcessPidExitStatus = 0;
804+
checkpointDirectory = getCheckpointDirectory(argc, argv, &error);
805+
if (-1 == error) {
806+
return;
807+
}
808+
if (-2 == error) {
809+
JLI_ReportErrorMessage("Failed to get the CRIU checkpoint directory, error=%d.", error);
810+
parentProcessExitStatus = EXIT_FAILURE;
811+
goto doneParentProcess;
812+
}
813+
/*
814+
* The if block will be invoked by the child process,
815+
* and the else block will be invoked by the parent process.
816+
*/
817+
childProcessPid = fork();
818+
if (0 == childProcessPid) {
819+
logLevel = getLogLevel(argc, argv, &error);
820+
if (-2 == error) {
821+
JLI_ReportErrorMessage("Failed to get the CRIU log level, error=%d.", error);
822+
childProcessExitStatus = EXIT_FAILURE;
823+
goto doneChildProcess;
824+
}
825+
unprivilegedModeOn = isUnprivilegedModeOn(argc, argv, &error);
826+
if (-2 == error) {
827+
JLI_ReportErrorMessage("Failed to get the CRIU unprivileged mode, error=%d.", error);
828+
childProcessExitStatus = EXIT_FAILURE;
829+
goto doneChildProcess;
830+
}
831+
logFile = getLogFile(argc, argv, &error);
832+
if (-2 == error) {
833+
JLI_ReportErrorMessage("Failed to get the CRIU log file, error=%d.", error);
834+
childProcessExitStatus = EXIT_FAILURE;
835+
goto doneChildProcess;
836+
}
837+
childProcessExitStatus = restoreFromCheckpoint(checkpointDirectory, logLevel, unprivilegedModeOn, logFile);
838+
doneChildProcess:
839+
exit(childProcessExitStatus);
840+
} else {
841+
waitpid(childProcessPid, &childProcessPidStatus, 0);
842+
if (WIFEXITED(childProcessPidStatus)) {
843+
childProcessPidExitStatus = WEXITSTATUS(childProcessPidStatus);
844+
if (EXIT_SUCCESS == childProcessPidExitStatus) {
845+
JLI_ReportMessage("Completed restore with -XX:CRaCRestoreFrom=PATH.");
846+
} else {
847+
JLI_ReportErrorMessage("Failed to restore from checkpoint, error=%d.", childProcessPidExitStatus);
848+
parentProcessExitStatus = EXIT_FAILURE;
849+
}
850+
} else {
851+
JLI_ReportErrorMessage("The CRIU restore child process failed.");
852+
parentProcessExitStatus = EXIT_FAILURE;
853+
}
854+
}
855+
doneParentProcess:
856+
exit(parentProcessExitStatus);
857+
}
858+
#endif /* defined(J9VM_OPT_CRAC_SUPPORT) */
859+
546860
int
547861
JavaMain(void* _args)
548862
{
@@ -568,6 +882,10 @@ JavaMain(void* _args)
568882

569883
RegisterThread();
570884

885+
#if defined(J9VM_OPT_CRAC_SUPPORT)
886+
handleCRaCRestore(argc, argv);
887+
#endif /* defined(J9VM_OPT_CRAC_SUPPORT) */
888+
571889
/* Initialize the virtual machine */
572890
start = CurrentTimeMicros();
573891
if (!InitializeJVM(&vm, &env, &ifn)) {

0 commit comments

Comments
 (0)