Skip to content

Commit

Permalink
Support restore with -XX:CRaCRestoreFrom=PATH
Browse files Browse the repository at this point in the history
Support restore with -XX:CRaCRestoreFrom=PATH that
specifies the path to the checkpoint image.

Issue: eclipse-openj9/openj9#19261
Signed-off-by: Amarpreet Singh <[email protected]>
  • Loading branch information
singh264 committed Apr 23, 2024
1 parent 72c40b4 commit e1deb53
Showing 1 changed file with 270 additions and 1 deletion.
271 changes: 270 additions & 1 deletion src/java.base/share/native/libjli/java.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

/*
* ===========================================================================
* (c) Copyright IBM Corp. 2022, 2023 All Rights Reserved
* (c) Copyright IBM Corp. 2022, 2024 All Rights Reserved
* ===========================================================================
*/

Expand Down Expand Up @@ -61,6 +61,11 @@

#include "java.h"
#include "jni.h"
#include "j9cfg.h"
#if defined(J9VM_OPT_CRAC_SUPPORT)
#include <ctype.h>
#include <sys/wait.h>
#endif /* defined(J9VM_OPT_CRAC_SUPPORT) */

/*
* A NOTE TO DEVELOPERS: For performance reasons it is important that
Expand All @@ -72,6 +77,12 @@

#define USE_STDERR JNI_TRUE /* we usually print to stderr */
#define USE_STDOUT JNI_FALSE
#if defined(J9VM_OPT_CRAC_SUPPORT)
#define MEMORY_ALLOCATION_ERROR 1
#define OPTION_NOT_FOUND 2
#define INVALID_OPTION_VALUE 3
#define OPTION_VALUE_NOT_FOUND 4
#endif /* defined(J9VM_OPT_CRAC_SUPPORT) */

static jboolean printVersion = JNI_FALSE; /* print and exit */
static jboolean showVersion = JNI_FALSE; /* print but continue */
Expand Down Expand Up @@ -534,6 +545,260 @@ invokeInstanceMainWithoutArgs(JNIEnv *env, jclass mainClass) {
return 1;
}

#if defined(J9VM_OPT_CRAC_SUPPORT)
static char *getCommandLineOptionValue(const char *optionName, int argc, char **argv, int *error) {
*error = 0;
for (int i = 0; i < argc; i++) {
char *arg = argv[i];
if (0 == strncmp(arg, optionName, strlen(optionName))) {
char *equals = strchr(arg, '=');
if (NULL != equals) {
if ('\0' == *(equals + 1)) {
return NULL;
}
int length = strlen(equals + 1);
char *value = (char *)JLI_MemAlloc(length + 1);
if (NULL == value) {
JLI_ReportErrorMessage(
"Failed to allocate memory for the value of the command line option %s.",
optionName);
*error = MEMORY_ALLOCATION_ERROR;
return NULL;
}
snprintf(value, length + 1, "%s", equals + 1);
value[length] = '\0';
return value;
}
return NULL;
}
}
*error = OPTION_NOT_FOUND;
return NULL;
}

static void freeCommandLineOptionValue(char *value) {
if (value) {
JLI_MemFree(value);
value = NULL;
}
}

static jboolean isCommandLineOptionFoundWithError(int error) {
return OPTION_NOT_FOUND != error && error;
}

static char *getCheckpointDir(int argc, char **argv, int *error) {
const char *checkpointDirPropertyName = "-XX:CRaCRestoreFrom";
char *checkpointDirPropertyValue = getCommandLineOptionValue(checkpointDirPropertyName, argc, argv, error);
if (!*error) {
if (checkpointDirPropertyValue) {
return checkpointDirPropertyValue;
} else {
JLI_ReportErrorMessage("The value of the command line option %s was not found.", checkpointDirPropertyName);
*error = OPTION_VALUE_NOT_FOUND;
return NULL;
}
}
return NULL;
}

static char *getDefaultLogLevel(int *error) {
*error = 0;
char *defaultLogLevel = strdup("-v2");
if (NULL == defaultLogLevel) {
JLI_ReportErrorMessage("Failed to allocate memory for the CRIU default log level.");
*error = MEMORY_ALLOCATION_ERROR;
}
return defaultLogLevel;
}

static char *getLogLevel(int argc, char **argv, int *error) {
const char *logLevelPropertyName = "-Dopenj9.internal.criu.logLevel";
char *logLevelPropertyValue = getCommandLineOptionValue(logLevelPropertyName, argc, argv, error);
if (!isCommandLineOptionFoundWithError(*error)) {
if (logLevelPropertyValue) {
for (const char *c = logLevelPropertyValue; *c; c++) {
if (!isdigit(*c)) {
JLI_ReportErrorMessage(
"The value %s of the command line option %s is not valid.",
logLevelPropertyValue,
logLevelPropertyName);
freeCommandLineOptionValue(logLevelPropertyValue);
*error = INVALID_OPTION_VALUE;
return NULL;
}
}
int logLevelValue = atoi(logLevelPropertyValue);
if (0 <= logLevelValue && 4 >= logLevelValue) {
int logLevelLength = snprintf(NULL, 0, "-v%d", logLevelValue);
if (0 > logLevelLength) {
JLI_ReportErrorMessage("Failed to calculate the length of the CRIU log level string.");
freeCommandLineOptionValue(logLevelPropertyValue);
*error = MEMORY_ALLOCATION_ERROR;
return NULL;
}
char *logLevel = (char *)JLI_MemAlloc(logLevelLength + 1);
if (NULL == logLevel) {
JLI_ReportErrorMessage("Failed to allocate memory for the CRIU log level.");
freeCommandLineOptionValue(logLevelPropertyValue);
*error = MEMORY_ALLOCATION_ERROR;
return NULL;
}
snprintf(logLevel, logLevelLength + 1, "-v%d", logLevelValue);
freeCommandLineOptionValue(logLevelPropertyValue);
return logLevel;
} else {
JLI_ReportErrorMessage(
"The value %s of the command line option %s is not valid.",
logLevelPropertyValue,
logLevelPropertyName);
freeCommandLineOptionValue(logLevelPropertyValue);
*error = INVALID_OPTION_VALUE;
return NULL;
}
} else {
return getDefaultLogLevel(error);
}
}
return NULL;
}

static char *getUnprivilegedMode(int argc, char **argv, int *error) {
const char *unprivilegedModePropertyName = "-Dopenj9.internal.criu.unprivilegedMode";
char *unprivilegedModePropertyValue = getCommandLineOptionValue(unprivilegedModePropertyName, argc, argv, error);
if (!*error) {
if (!unprivilegedModePropertyValue) {
char *unprivilegedMode = strdup("--unprivileged");
if (NULL == unprivilegedMode) {
JLI_ReportErrorMessage("Failed to allocate memory for the CRIU unprivileged mode.");
*error = MEMORY_ALLOCATION_ERROR;
return NULL;
}
return unprivilegedMode;
} else {
JLI_ReportErrorMessage(
"The value %s of the command line option %s is not expected.",
unprivilegedModePropertyValue,
unprivilegedModePropertyName);
freeCommandLineOptionValue(unprivilegedModePropertyValue);
*error = INVALID_OPTION_VALUE;
return NULL;
}
}
return NULL;
}

static char *getLogFile(int argc, char **argv, int *error) {
const char *logFilePropertyName = "-Dopenj9.internal.criu.logFile";
char *logFilePropertyValue = getCommandLineOptionValue(logFilePropertyName, argc, argv, error);
if (!*error) {
if (logFilePropertyValue) {
int logFileLength = snprintf(NULL, 0, "--log-file=%s", logFilePropertyValue) + 1;
if (0 > logFileLength) {
JLI_ReportErrorMessage("Failed to calculate the length of the CRIU log file string.");
freeCommandLineOptionValue(logFilePropertyValue);
*error = MEMORY_ALLOCATION_ERROR;
return NULL;
}
char *logFile = (char *)JLI_MemAlloc(logFileLength);
if (NULL == logFile) {
JLI_ReportErrorMessage("Failed to allocate memory for the CRIU log file option.");
*error = MEMORY_ALLOCATION_ERROR;
return NULL;
}
snprintf(logFile, logFileLength, "--log-file=%s", logFilePropertyValue);
freeCommandLineOptionValue(logFilePropertyValue);
return logFile;
} else {
JLI_ReportErrorMessage("The value of the command line option %s was not found.", logFilePropertyName);
*error = OPTION_VALUE_NOT_FOUND;
return NULL;
}
}
return NULL;
}

static int restoreFromCheckpoint(char *checkpointDir, char *logLevel, char *unprivilegedMode, char *logFile) {
int argc = 6;
char *argv[9] = { NULL };
argv[0] = "criu";
argv[1] = "restore";
argv[2] = "-D";
argv[3] = checkpointDir;
argv[4] = logLevel;
argv[5] = "--shell-job";
if (unprivilegedMode) {
argv[argc++] = unprivilegedMode;
}
if (logFile) {
argv[argc++] = logFile;
}
argv[argc] = NULL;
if (-1 == execvp(argv[0], argv)) {
return -1;
}
return 0;
}

static void handleCRaCRestore(int argc, char **argv) {
int error = 0;
char *checkpointDir = getCheckpointDir(argc, argv, &error);
if (OPTION_NOT_FOUND == error) {
freeCommandLineOptionValue(checkpointDir);
return;
}
if (error) {
JLI_ReportErrorMessage("Failed to get the CRIU checkpoint directory, error=%d.", error);
exit(EXIT_FAILURE);
}
/*
* The if block will be invoked by the child process,
* and the else block will be invoked by the parent process.
*/
pid_t criuRestorePid = fork();
if (0 == criuRestorePid) {
char *logLevel = getLogLevel(argc, argv, &error);
if (isCommandLineOptionFoundWithError(error)) {
JLI_ReportErrorMessage("Failed to get the CRIU log level, error=%d.", error);
exit(EXIT_FAILURE);
}
char *unprivilegedMode = getUnprivilegedMode(argc, argv, &error);
if (isCommandLineOptionFoundWithError(error)) {
JLI_ReportErrorMessage("Failed to get the CRIU unprivileged mode, error=%d.", error);
freeCommandLineOptionValue(logLevel);
exit(EXIT_FAILURE);
}
char *logFile = getLogFile(argc, argv, &error);
if (isCommandLineOptionFoundWithError(error)) {
JLI_ReportErrorMessage("Failed to get the CRIU log file, error=%d.", error);
freeCommandLineOptionValue(logLevel);
freeCommandLineOptionValue(unprivilegedMode);
exit(EXIT_FAILURE);
}
int restoreResult = restoreFromCheckpoint(checkpointDir, logLevel, unprivilegedMode, logFile);
freeCommandLineOptionValue(logLevel);
freeCommandLineOptionValue(unprivilegedMode);
freeCommandLineOptionValue(logFile);
exit(restoreResult);
} else {
int exitStatus = EXIT_SUCCESS;
int criuRestorePidStatus = 0;
waitpid(criuRestorePid, &criuRestorePidStatus, 0);
if (WIFEXITED(criuRestorePidStatus)) {
if (0 != WEXITSTATUS(criuRestorePidStatus)) {
JLI_ReportErrorMessage("Failed to restore from checkpoint.");
exitStatus = EXIT_FAILURE;
}
} else {
JLI_ReportErrorMessage("The CRIU restore child process failed.");
exitStatus = EXIT_FAILURE;
}
freeCommandLineOptionValue(checkpointDir);
exit(exitStatus);
}
}
#endif /* defined(J9VM_OPT_CRAC_SUPPORT) */

int
JavaMain(void* _args)
{
Expand All @@ -554,6 +819,10 @@ JavaMain(void* _args)

RegisterThread();

#if defined(J9VM_OPT_CRAC_SUPPORT)
handleCRaCRestore(argc, argv);
#endif /* defined(J9VM_OPT_CRAC_SUPPORT) */

/* Initialize the virtual machine */
start = CurrentTimeMicros();
if (!InitializeJVM(&vm, &env, &ifn)) {
Expand Down

0 comments on commit e1deb53

Please sign in to comment.