Skip to content

Commit b3c44b2

Browse files
committed
Support restore with -XX:CRaCRestoreFrom=PATH
Support restore with -XX:CRaCRestoreFrom=PATH that specifies the path to the checkpoint image. Signed-off-by: Amarpreet Singh <[email protected]>
1 parent dd51ad8 commit b3c44b2

File tree

1 file changed

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

1 file changed

+339
-1
lines changed

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

+339-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
@@ -72,6 +77,23 @@
7277

7378
#define USE_STDERR JNI_TRUE /* we usually print to stderr */
7479
#define USE_STDOUT JNI_FALSE
80+
#if defined(J9VM_OPT_CRAC_SUPPORT)
81+
#define MEMORY_ALLOCATION_ERROR 1
82+
#define OPTION_NOT_FOUND 2
83+
#define OPTION_VALUE_NOT_FOUND 3
84+
#define OPTION_NOT_SUPPORTED 4
85+
#define OPTION_VALUE_NOT_VALID 5
86+
#define CRAC_RESTORE_FROM_OPTION_NAME "-XX:CRaCRestoreFrom"
87+
#define LOG_LEVEL_OPTION_NAME "-Dopenj9.internal.criu.logLevel"
88+
#define UNPRIVILEGED_MODE_OPTION_NAME "-Dopenj9.internal.criu.unprivilegedMode"
89+
#define LOG_FILE_OPTION_NAME "-Dopenj9.internal.criu.logFile"
90+
static const char *supportedOptionNames[] = {
91+
CRAC_RESTORE_FROM_OPTION_NAME,
92+
LOG_LEVEL_OPTION_NAME,
93+
UNPRIVILEGED_MODE_OPTION_NAME,
94+
LOG_FILE_OPTION_NAME
95+
};
96+
#endif /* defined(J9VM_OPT_CRAC_SUPPORT) */
7597

7698
static jboolean printVersion = JNI_FALSE; /* print and exit */
7799
static jboolean showVersion = JNI_FALSE; /* print but continue */
@@ -534,6 +556,318 @@ invokeInstanceMainWithoutArgs(JNIEnv *env, jclass mainClass) {
534556
return 1;
535557
}
536558

559+
#if defined(J9VM_OPT_CRAC_SUPPORT)
560+
static char *getCommandLineOptionValue(const char *optionName, int argc, char **argv, int *error) {
561+
char *arg = NULL;
562+
unsigned int optionNameLength = strlen(optionName);
563+
char *equals = NULL;
564+
char *value = NULL;
565+
*error = 0;
566+
for (int i = 0; i < argc; i++) {
567+
arg = argv[i];
568+
if (0 == strncmp(arg, optionName, optionNameLength)) {
569+
equals = arg + optionNameLength;
570+
if (('=' == *equals) || ('\0' == *equals)) {
571+
if ('=' == *equals) {
572+
equals++;
573+
}
574+
if ('\0' != *equals) {
575+
value = strdup(equals);
576+
if (NULL == value) {
577+
JLI_ReportErrorMessage(
578+
"Failed to allocate memory for the value of the command line option %s.",
579+
optionName);
580+
*error = MEMORY_ALLOCATION_ERROR;
581+
}
582+
return value;
583+
}
584+
return NULL;
585+
}
586+
}
587+
}
588+
*error = OPTION_NOT_FOUND;
589+
return NULL;
590+
}
591+
592+
static void freeCommandLineOptionValue(char **value) {
593+
if ((NULL != value) && (NULL != *value)) {
594+
JLI_MemFree(*value);
595+
*value = NULL;
596+
}
597+
}
598+
599+
static jboolean hasGetCommandLineOptionValueFoundError(int error) {
600+
return 0 != error;
601+
}
602+
603+
static char *getCheckpointDir(int argc, char **argv, int *error) {
604+
char *checkpointDirPropertyValue = getCommandLineOptionValue(CRAC_RESTORE_FROM_OPTION_NAME, argc, argv, error);
605+
if (!hasGetCommandLineOptionValueFoundError(*error)) {
606+
if (NULL != checkpointDirPropertyValue) {
607+
return checkpointDirPropertyValue;
608+
} else {
609+
JLI_ReportErrorMessage("The value of the command line option %s was not found.", CRAC_RESTORE_FROM_OPTION_NAME);
610+
*error = OPTION_VALUE_NOT_FOUND;
611+
return NULL;
612+
}
613+
}
614+
return NULL;
615+
}
616+
617+
static char *getUnsupportedCommandLineOption(int argc, char **argv, int *error) {
618+
char *arg = NULL;
619+
jboolean isArgSupported = JNI_FALSE;
620+
unsigned int supportedOptionNameLength = 0;
621+
char *equals = NULL;
622+
const size_t numSupportedOptionNames = sizeof(supportedOptionNames) / sizeof(supportedOptionNames[0]);
623+
*error = 0;
624+
for (int i = 0; i < argc; i++) {
625+
arg = argv[i];
626+
isArgSupported = JNI_FALSE;
627+
for (size_t j = 0; j < numSupportedOptionNames; j++) {
628+
supportedOptionNameLength = strlen(supportedOptionNames[j]);
629+
if (0 == strncmp(arg, supportedOptionNames[j], supportedOptionNameLength)) {
630+
equals = arg + supportedOptionNameLength;
631+
if (('=' == *equals) || ('\0' == *equals)) {
632+
isArgSupported = JNI_TRUE;
633+
break;
634+
}
635+
}
636+
}
637+
if (!isArgSupported) {
638+
*error = OPTION_NOT_SUPPORTED;
639+
return arg;
640+
}
641+
}
642+
return NULL;
643+
}
644+
645+
static jboolean isCommandLineOptionFoundWithError(int error) {
646+
return (OPTION_NOT_FOUND != error) && hasGetCommandLineOptionValueFoundError(error);
647+
}
648+
649+
static char *getDefaultLogLevel(int *error) {
650+
char *defaultLogLevel = NULL;
651+
*error = 0;
652+
defaultLogLevel = strdup("-v2");
653+
if (NULL == defaultLogLevel) {
654+
JLI_ReportErrorMessage("Failed to allocate memory for the CRIU default log level.");
655+
*error = MEMORY_ALLOCATION_ERROR;
656+
}
657+
return defaultLogLevel;
658+
}
659+
660+
static char *getLogLevel(int argc, char **argv, int *error) {
661+
char *logLevelPropertyValue = NULL;
662+
int logLevelValue = 0;
663+
int logLevelLength = 0;
664+
char *logLevel = NULL;
665+
logLevelPropertyValue = getCommandLineOptionValue(LOG_LEVEL_OPTION_NAME, argc, argv, error);
666+
if (!isCommandLineOptionFoundWithError(*error)) {
667+
if (NULL != logLevelPropertyValue) {
668+
for (const char *c = logLevelPropertyValue; '\0' != *c; c++) {
669+
if (!isdigit(*c)) {
670+
JLI_ReportErrorMessage(
671+
"The value %s of the command line option %s is not valid.",
672+
logLevelPropertyValue,
673+
LOG_LEVEL_OPTION_NAME);
674+
freeCommandLineOptionValue(&logLevelPropertyValue);
675+
*error = OPTION_VALUE_NOT_VALID;
676+
return NULL;
677+
}
678+
}
679+
logLevelValue = atoi(logLevelPropertyValue);
680+
if ((logLevelValue >= 0) && (logLevelValue <= 4)) {
681+
logLevelLength = snprintf(NULL, 0, "-v%d", logLevelValue);
682+
if (logLevelLength < 0) {
683+
JLI_ReportErrorMessage("Failed to calculate the length of the CRIU log level string.");
684+
freeCommandLineOptionValue(&logLevelPropertyValue);
685+
*error = MEMORY_ALLOCATION_ERROR;
686+
return NULL;
687+
}
688+
logLevel = (char *)JLI_MemAlloc(logLevelLength + 1);
689+
if (NULL == logLevel) {
690+
JLI_ReportErrorMessage("Failed to allocate memory for the CRIU log level.");
691+
freeCommandLineOptionValue(&logLevelPropertyValue);
692+
*error = MEMORY_ALLOCATION_ERROR;
693+
return NULL;
694+
}
695+
snprintf(logLevel, logLevelLength + 1, "-v%d", logLevelValue);
696+
freeCommandLineOptionValue(&logLevelPropertyValue);
697+
return logLevel;
698+
} else {
699+
JLI_ReportErrorMessage(
700+
"The value %s of the command line option %s is not valid.",
701+
logLevelPropertyValue,
702+
LOG_LEVEL_OPTION_NAME);
703+
freeCommandLineOptionValue(&logLevelPropertyValue);
704+
*error = OPTION_VALUE_NOT_VALID;
705+
return NULL;
706+
}
707+
} else {
708+
return getDefaultLogLevel(error);
709+
}
710+
}
711+
return NULL;
712+
}
713+
714+
static char *getUnprivilegedMode(int argc, char **argv, int *error) {
715+
char *unprivilegedModePropertyValue = NULL;
716+
char *unprivilegedMode = NULL;
717+
unprivilegedModePropertyValue = getCommandLineOptionValue(UNPRIVILEGED_MODE_OPTION_NAME, argc, argv, error);
718+
if (!hasGetCommandLineOptionValueFoundError(*error)) {
719+
if (NULL == unprivilegedModePropertyValue) {
720+
unprivilegedMode = strdup("--unprivileged");
721+
if (NULL == unprivilegedMode) {
722+
JLI_ReportErrorMessage("Failed to allocate memory for the CRIU unprivileged mode.");
723+
*error = MEMORY_ALLOCATION_ERROR;
724+
return NULL;
725+
}
726+
return unprivilegedMode;
727+
} else {
728+
JLI_ReportErrorMessage(
729+
"The value %s of the command line option %s is not expected.",
730+
unprivilegedModePropertyValue,
731+
UNPRIVILEGED_MODE_OPTION_NAME);
732+
freeCommandLineOptionValue(&unprivilegedModePropertyValue);
733+
*error = OPTION_VALUE_NOT_VALID;
734+
return NULL;
735+
}
736+
}
737+
return NULL;
738+
}
739+
740+
static char *getLogFile(int argc, char **argv, int *error) {
741+
char *logFilePropertyValue = NULL;
742+
int logFileLength = 0;
743+
char *logFile = NULL;
744+
logFilePropertyValue = getCommandLineOptionValue(LOG_FILE_OPTION_NAME, argc, argv, error);
745+
if (!hasGetCommandLineOptionValueFoundError(*error)) {
746+
if (NULL != logFilePropertyValue) {
747+
logFileLength = snprintf(NULL, 0, "--log-file=%s", logFilePropertyValue) + 1;
748+
if (logFileLength < 0) {
749+
JLI_ReportErrorMessage("Failed to calculate the length of the CRIU log file string.");
750+
freeCommandLineOptionValue(&logFilePropertyValue);
751+
*error = MEMORY_ALLOCATION_ERROR;
752+
return NULL;
753+
}
754+
logFile = (char *)JLI_MemAlloc(logFileLength);
755+
if (NULL == logFile) {
756+
JLI_ReportErrorMessage("Failed to allocate memory for the CRIU log file option.");
757+
*error = MEMORY_ALLOCATION_ERROR;
758+
return NULL;
759+
}
760+
snprintf(logFile, logFileLength, "--log-file=%s", logFilePropertyValue);
761+
freeCommandLineOptionValue(&logFilePropertyValue);
762+
return logFile;
763+
} else {
764+
JLI_ReportErrorMessage("The value of the command line option %s was not found.", LOG_FILE_OPTION_NAME);
765+
*error = OPTION_VALUE_NOT_FOUND;
766+
return NULL;
767+
}
768+
}
769+
return NULL;
770+
}
771+
772+
static int restoreFromCheckpoint(char *checkpointDir, char *logLevel, char *unprivilegedMode, char *logFile) {
773+
int argc = 6;
774+
char *argv[9] = { NULL };
775+
argv[0] = "criu";
776+
argv[1] = "restore";
777+
argv[2] = "-D";
778+
argv[3] = checkpointDir;
779+
argv[4] = logLevel;
780+
argv[5] = "--shell-job";
781+
if (NULL != unprivilegedMode) {
782+
argv[argc++] = unprivilegedMode;
783+
}
784+
if (NULL != logFile) {
785+
argv[argc++] = logFile;
786+
}
787+
argv[argc] = NULL;
788+
if (-1 == execvp(argv[0], argv)) {
789+
return -1;
790+
}
791+
return 0;
792+
}
793+
794+
static void handleCRaCRestore(int argc, char **argv) {
795+
char *checkpointDir = NULL;
796+
int error = 0;
797+
char *unsupportedOption = NULL;
798+
int parentProcessExitStatus = EXIT_SUCCESS;
799+
pid_t childProcessPid = 0;
800+
char *logLevel = NULL;
801+
int childProcessExitStatus = EXIT_SUCCESS;
802+
char *unprivilegedMode = NULL;
803+
char *logFile = NULL;
804+
int childProcessPidStatus = 0;
805+
checkpointDir = getCheckpointDir(argc, argv, &error);
806+
if (OPTION_NOT_FOUND == error) {
807+
return;
808+
}
809+
if (hasGetCommandLineOptionValueFoundError(error)) {
810+
JLI_ReportErrorMessage("Failed to get the CRIU checkpoint directory, error=%d.", error);
811+
parentProcessExitStatus = EXIT_FAILURE;
812+
goto exitParentProcessHandleCRaCRestore;
813+
}
814+
unsupportedOption = getUnsupportedCommandLineOption(argc, argv, &error);
815+
if (OPTION_NOT_SUPPORTED == error) {
816+
JLI_ReportErrorMessage("The command line option %s is not supported for CRaC restore.", unsupportedOption);
817+
parentProcessExitStatus = EXIT_FAILURE;
818+
goto exitParentProcessHandleCRaCRestore;
819+
}
820+
/*
821+
* The if block will be invoked by the child process,
822+
* and the else block will be invoked by the parent process.
823+
*/
824+
childProcessPid = fork();
825+
if (0 == childProcessPid) {
826+
logLevel = getLogLevel(argc, argv, &error);
827+
if (isCommandLineOptionFoundWithError(error)) {
828+
JLI_ReportErrorMessage("Failed to get the CRIU log level, error=%d.", error);
829+
childProcessExitStatus = EXIT_FAILURE;
830+
goto exitChildProcessHandleCRaCRestore;
831+
}
832+
unprivilegedMode = getUnprivilegedMode(argc, argv, &error);
833+
if (isCommandLineOptionFoundWithError(error)) {
834+
JLI_ReportErrorMessage("Failed to get the CRIU unprivileged mode, error=%d.", error);
835+
childProcessExitStatus = EXIT_FAILURE;
836+
goto freeLogLevel;
837+
}
838+
logFile = getLogFile(argc, argv, &error);
839+
if (isCommandLineOptionFoundWithError(error)) {
840+
JLI_ReportErrorMessage("Failed to get the CRIU log file, error=%d.", error);
841+
childProcessExitStatus = EXIT_FAILURE;
842+
goto freeUnprivilegedMode;
843+
}
844+
childProcessExitStatus = restoreFromCheckpoint(checkpointDir, logLevel, unprivilegedMode, logFile);
845+
freeLogFile:
846+
freeCommandLineOptionValue(&logFile);
847+
freeUnprivilegedMode:
848+
freeCommandLineOptionValue(&unprivilegedMode);
849+
freeLogLevel:
850+
freeCommandLineOptionValue(&logLevel);
851+
exitChildProcessHandleCRaCRestore:
852+
exit(childProcessExitStatus);
853+
} else {
854+
waitpid(childProcessPid, &childProcessPidStatus, 0);
855+
if (WIFEXITED(childProcessPidStatus)) {
856+
if (0 != WEXITSTATUS(childProcessPidStatus)) {
857+
JLI_ReportErrorMessage("Failed to restore from checkpoint.");
858+
parentProcessExitStatus = EXIT_FAILURE;
859+
}
860+
} else {
861+
JLI_ReportErrorMessage("The CRIU restore child process failed.");
862+
parentProcessExitStatus = EXIT_FAILURE;
863+
}
864+
freeCommandLineOptionValue(&checkpointDir);
865+
}
866+
exitParentProcessHandleCRaCRestore:
867+
exit(parentProcessExitStatus);
868+
}
869+
#endif /* defined(J9VM_OPT_CRAC_SUPPORT) */
870+
537871
int
538872
JavaMain(void* _args)
539873
{
@@ -554,6 +888,10 @@ JavaMain(void* _args)
554888

555889
RegisterThread();
556890

891+
#if defined(J9VM_OPT_CRAC_SUPPORT)
892+
handleCRaCRestore(argc, argv);
893+
#endif /* defined(J9VM_OPT_CRAC_SUPPORT) */
894+
557895
/* Initialize the virtual machine */
558896
start = CurrentTimeMicros();
559897
if (!InitializeJVM(&vm, &env, &ifn)) {

0 commit comments

Comments
 (0)