Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support restore with -XX:CRaCRestoreFrom=PATH #810

Merged
merged 1 commit into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
303 changes: 303 additions & 0 deletions closed/src/java.base/share/native/libjli/criuhelpers.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
/*
* ===========================================================================
* (c) Copyright IBM Corp. 2024, 2024 All Rights Reserved
* ===========================================================================
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* IBM designates this particular file as subject to the "Classpath" exception
* as provided by IBM in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, see <http://www.gnu.org/licenses/>.
*
* ===========================================================================
*/

#include "criuhelpers.h"

#if defined(J9VM_OPT_CRAC_SUPPORT)

#include "java.h"
#include <ctype.h>
#include <sys/wait.h>

/**
* Get the value of a command line option in the array of command line arguments.
* @param[in] optionName The name of the command line option to search for
* @param[in] argc The number of command line arguments
* @param[in] argv The array of command line arguments
* @param[out] error A pointer to an integer for the error code
* @return A pointer to the value of the command line option if found, NULL otherwise
*/
static const char *
getCommandLineOptionValue(const char *optionName, int argc, char **argv, int *error)
{
const char *value = NULL;
int i = 0;
size_t optionNameLength = strlen(optionName);
*error = -1;
for (i = argc - 1; i >= 0; i--) {
const char *arg = argv[i];
if (0 == strncmp(arg, optionName, optionNameLength)) {
const char *equals = arg + optionNameLength;
if (('=' == *equals) || ('\0' == *equals)) {
*error = 0;
value = equals;
if ('=' == *value) {
value += 1;
}
if ('\0' == *value) {
value = NULL;
}
break;
}
}
}
return value;
}

/**
* Get the checkpoint directory from command line options.
* @param[in] argc The number of command line arguments
* @param[in] argv The array of command line arguments
* @param[in/out] error A pointer to an integer for the error code
* @return A pointer to the checkpoint directory if found, NULL otherwise
*/
static const char *
getCheckpointDirectory(int argc, char **argv, int *error)
{
const char *checkpointDirectory = NULL;
const char *checkpointDirectoryPropertyValue = getCommandLineOptionValue("-XX:CRaCRestoreFrom", argc, argv, error);
if (0 == *error) {
if (NULL != checkpointDirectoryPropertyValue) {
checkpointDirectory = checkpointDirectoryPropertyValue;
} else {
JLI_ReportErrorMessage("The value of the command line option -XX:CRaCRestoreFrom was not found.");
*error = -2;
}
}
return checkpointDirectory;
}

/**
* Get the log level specified in the command line arguments.
* Valid log levels are from 0 to 4, inclusive; the default is 2.
* @param[in] argc The number of command line arguments
* @param[in] argv The array of command line arguments
* @param[in/out] error A pointer to an integer for the error code
* @return The log level integer if successful, default of 2 otherwise
*/
static int
getLogLevel(int argc, char **argv, int *error)
{
int logLevelValue = 2; /* default */
const char *logLevelPropertyValue = getCommandLineOptionValue("-Dopenj9.internal.criu.logLevel", argc, argv, error);
if (0 == *error) {
const char *c = logLevelPropertyValue;
if (NULL == c) {
goto done;
}
for (; '\0' != *c; c++) {
if (!isdigit(*c)) {
goto setLogLevelOptionValueNotValidError;
}
}
logLevelValue = atoi(logLevelPropertyValue);
if ((0 <= logLevelValue) && (logLevelValue <= 4)) {
goto done;
} else {
goto setLogLevelOptionValueNotValidError;
}
} else if (-1 == *error) {
goto done;
}
setLogLevelOptionValueNotValidError:
JLI_ReportErrorMessage(
"The option '-Dopenj9.internal.criu.logLevel=%s' is not valid.",
logLevelPropertyValue);
*error = -2;
done:
return logLevelValue;
}

/**
* Check if the unprivileged mode is specified in the command line arguments.
* @param[in] argc The number of command line arguments
* @param[in] argv The array of command line arguments
* @param[in/out] error A pointer to an integer for the error code
* @return true if the unprivileged mode option is specified in the command line arguments, false otherwise
*/
static jboolean
isUnprivilegedModeOn(int argc, char **argv, int *error)
{
jboolean isUnprivilegedModeOn = JNI_FALSE;
const char *unprivilegedModePropertyValue = getCommandLineOptionValue("-Dopenj9.internal.criu.unprivilegedMode", argc, argv, error);
if (0 == *error) {
if (NULL == unprivilegedModePropertyValue) {
isUnprivilegedModeOn = JNI_TRUE;
} else {
JLI_ReportErrorMessage(
"The option '-Dopenj9.internal.criu.unprivilegedMode=%s' does not accept a value.",
unprivilegedModePropertyValue);
*error = -2;
}
}
return isUnprivilegedModeOn;
}

/**
* Get the log file specified in the command line arguments.
* @param[in] argc The number of command line arguments
* @param[in] argv The array of command line arguments
* @param[in/out] error A pointer to an integer for the error code
* @return A pointer to the log file string if successful, NULL otherwise
*/
static const char *
getLogFile(int argc, char **argv, int *error)
{
const char *logFile = NULL;
const char *logFilePropertyValue = getCommandLineOptionValue("-Dopenj9.internal.criu.logFile", argc, argv, error);
if (0 == *error) {
if (NULL != logFilePropertyValue) {
logFile = logFilePropertyValue;
} else {
JLI_ReportErrorMessage("The option -Dopenj9.internal.criu.logFile requires a value.");
*error = -2;
}
}
return logFile;
}

/**
* Restore the system state from a checkpoint using the CRIU tool.
* @param[in] checkpointDirectory The directory containing the checkpoint data
* @param[in] logLevel The log level for CRIU logging
* @param[in] unprivilegedModeOn Indicates whether the unprivileged mode option is on
* @param[in] logFile The log file option for CRIU
* @return 0 if the execution of the 'criu restore' command succeeds, -1 otherwise
*/
static int
restoreFromCheckpoint(const char *checkpointDirectory, int logLevel, jboolean unprivilegedModeOn, const char *logFile)
{
int length = -1;
char logLevelOption[4] = { 0 };
char *logFileOption = NULL;
int argc = 0;
const char *argv[9] = { NULL };
argv[argc++] = "criu";
argv[argc++] = "restore";
argv[argc++] = "-D";
argv[argc++] = checkpointDirectory;
/* The log level is a single digit. */
snprintf(logLevelOption, sizeof(logLevelOption), "-v%d", logLevel);
argv[argc++] = logLevelOption;
argv[argc++] = "--shell-job";
if (unprivilegedModeOn) {
argv[argc++] = "--unprivileged";
}
if (NULL != logFile) {
length = strlen(logFile) + sizeof("--log-file=%s") - 1;
logFileOption = (char *)JLI_MemAlloc(length + 1);
if (NULL == logFileOption) {
JLI_ReportErrorMessage("Failed to allocate memory for option '--log-file=%s'.", logFile);
goto fail;
}
if (snprintf(logFileOption, length + 1, "--log-file=%s", logFile) < 0) {
JLI_ReportErrorMessage("Failed to format option '--log-file=%s'.", logFile);
goto fail;
}
argv[argc++] = logFileOption;
}
argv[argc] = NULL;
execvp(argv[0], (char * const *)argv);
/* If execvp returns, there was an error. */
fail:
if (NULL != logFileOption) {
JLI_MemFree((void *)logFileOption);
}
return -1;
}

/**
* Handle the restoration of the system state from a checkpoint.
* @param[in] argc The number of command line arguments
* @param[in] argv The array of command line arguments
*/
void
handleCRaCRestore(int argc, char **argv)
{
const char *checkpointDirectory = NULL;
int error = 0;
int parentProcessExitStatus = EXIT_SUCCESS;
pid_t childProcessPid = 0;
int logLevel = 0;
int childProcessExitStatus = EXIT_SUCCESS;
jboolean unprivilegedModeOn = JNI_FALSE;
const char *logFile = NULL;
int childProcessPidStatus = 0;
int childProcessPidExitStatus = 0;
checkpointDirectory = getCheckpointDirectory(argc, argv, &error);
if (-1 == error) {
/* Option -XX:CRaCRestoreFrom not specified. */
return;
}
if (-2 == error) {
JLI_ReportErrorMessage("Failed to get the CRIU checkpoint directory.");
parentProcessExitStatus = EXIT_FAILURE;
goto doneParentProcess;
}
/*
* The if block will be invoked by the child process,
* and the else block will be invoked by the parent process.
*/
childProcessPid = fork();
if (0 == childProcessPid) {
logLevel = getLogLevel(argc, argv, &error);
if (-2 == error) {
JLI_ReportErrorMessage("Failed to get the CRIU log level.");
childProcessExitStatus = EXIT_FAILURE;
goto doneChildProcess;
}
unprivilegedModeOn = isUnprivilegedModeOn(argc, argv, &error);
if (-2 == error) {
JLI_ReportErrorMessage("Failed to get the CRIU unprivileged mode.");
childProcessExitStatus = EXIT_FAILURE;
goto doneChildProcess;
}
logFile = getLogFile(argc, argv, &error);
if (-2 == error) {
JLI_ReportErrorMessage("Failed to get the CRIU log file.");
childProcessExitStatus = EXIT_FAILURE;
goto doneChildProcess;
}
childProcessExitStatus = restoreFromCheckpoint(checkpointDirectory, logLevel, unprivilegedModeOn, logFile);
doneChildProcess:
exit(childProcessExitStatus);
} else {
waitpid(childProcessPid, &childProcessPidStatus, 0);
if (WIFEXITED(childProcessPidStatus)) {
childProcessPidExitStatus = WEXITSTATUS(childProcessPidStatus);
if (EXIT_SUCCESS == childProcessPidExitStatus) {
JLI_ReportMessage("Completed restore with -XX:CRaCRestoreFrom=PATH.");
} else {
JLI_ReportErrorMessage("Failed to restore from checkpoint, error=%d.", childProcessPidExitStatus);
parentProcessExitStatus = EXIT_FAILURE;
}
} else {
JLI_ReportErrorMessage("The CRIU restore child process failed.");
parentProcessExitStatus = EXIT_FAILURE;
}
}
doneParentProcess:
exit(parentProcessExitStatus);
}
#endif /* defined(J9VM_OPT_CRAC_SUPPORT) */
34 changes: 34 additions & 0 deletions closed/src/java.base/share/native/libjli/criuhelpers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* ===========================================================================
* (c) Copyright IBM Corp. 2024, 2024 All Rights Reserved
* ===========================================================================
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* IBM designates this particular file as subject to the "Classpath" exception
* as provided by IBM in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, see <http://www.gnu.org/licenses/>.
*
* ===========================================================================
*/

#ifndef CRIUHELPERS_H
#define CRIUHELPERS_H

#include "j9cfg.h"

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

#endif /* CRIUHELPERS_H */
7 changes: 6 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 @@ -57,6 +57,7 @@
*/


#include "criuhelpers.h"
#include "java.h"
#include "jni.h"

Expand Down Expand Up @@ -475,6 +476,10 @@ JavaMain(void* _args)
int ret = 0;
jlong start = 0, end = 0;

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

RegisterThread();

/* Initialize the virtual machine */
Expand Down