Skip to content

Commit 950c02c

Browse files
singh264JasonFengJ9
authored andcommitted
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: Amarpreet Singh <[email protected]> Signed-off-by: Jason Feng <[email protected]>
1 parent 4f827d7 commit 950c02c

File tree

3 files changed

+343
-1
lines changed

3 files changed

+343
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
/*
2+
* ===========================================================================
3+
* (c) Copyright IBM Corp. 2024, 2024 All Rights Reserved
4+
* ===========================================================================
5+
*
6+
* This code is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 only, as
8+
* published by the Free Software Foundation.
9+
*
10+
* IBM designates this particular file as subject to the "Classpath" exception
11+
* as provided by IBM in the LICENSE file that accompanied this code.
12+
*
13+
* This code is distributed in the hope that it will be useful, but WITHOUT
14+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16+
* version 2 for more details (a copy is included in the LICENSE file that
17+
* accompanied this code).
18+
*
19+
* You should have received a copy of the GNU General Public License version
20+
* 2 along with this work; if not, see <http://www.gnu.org/licenses/>.
21+
*
22+
* ===========================================================================
23+
*/
24+
25+
#include "criuhelpers.h"
26+
27+
#if defined(J9VM_OPT_CRAC_SUPPORT)
28+
29+
#include "java.h"
30+
#include <ctype.h>
31+
#include <sys/wait.h>
32+
33+
/**
34+
* Get the value of a command line option in the array of command line arguments.
35+
* @param[in] optionName The name of the command line option to search for
36+
* @param[in] argc The number of command line arguments
37+
* @param[in] argv The array of command line arguments
38+
* @param[out] error A pointer to an integer for the error code
39+
* @return A pointer to the value of the command line option if found, NULL otherwise
40+
*/
41+
static const char *
42+
getCommandLineOptionValue(const char *optionName, int argc, char **argv, int *error)
43+
{
44+
const char *value = NULL;
45+
int i = 0;
46+
size_t optionNameLength = strlen(optionName);
47+
*error = -1;
48+
for (i = argc - 1; i >= 0; i--) {
49+
const char *arg = argv[i];
50+
if (0 == strncmp(arg, optionName, optionNameLength)) {
51+
const char *equals = arg + optionNameLength;
52+
if (('=' == *equals) || ('\0' == *equals)) {
53+
*error = 0;
54+
value = equals;
55+
if ('=' == *value) {
56+
value += 1;
57+
}
58+
if ('\0' == *value) {
59+
value = NULL;
60+
}
61+
break;
62+
}
63+
}
64+
}
65+
return value;
66+
}
67+
68+
/**
69+
* Get the checkpoint directory from command line options.
70+
* @param[in] argc The number of command line arguments
71+
* @param[in] argv The array of command line arguments
72+
* @param[in/out] error A pointer to an integer for the error code
73+
* @return A pointer to the checkpoint directory if found, NULL otherwise
74+
*/
75+
static const char *
76+
getCheckpointDirectory(int argc, char **argv, int *error)
77+
{
78+
const char *checkpointDirectory = NULL;
79+
const char *checkpointDirectoryPropertyValue = getCommandLineOptionValue("-XX:CRaCRestoreFrom", argc, argv, error);
80+
if (0 == *error) {
81+
if (NULL != checkpointDirectoryPropertyValue) {
82+
checkpointDirectory = checkpointDirectoryPropertyValue;
83+
} else {
84+
JLI_ReportErrorMessage("The value of the command line option -XX:CRaCRestoreFrom was not found.");
85+
*error = -2;
86+
}
87+
}
88+
return checkpointDirectory;
89+
}
90+
91+
/**
92+
* Get the log level specified in the command line arguments.
93+
* Valid log levels are from 0 to 4, inclusive; the default is 2.
94+
* @param[in] argc The number of command line arguments
95+
* @param[in] argv The array of command line arguments
96+
* @param[in/out] error A pointer to an integer for the error code
97+
* @return The log level integer if successful, default of 2 otherwise
98+
*/
99+
static int
100+
getLogLevel(int argc, char **argv, int *error)
101+
{
102+
int logLevelValue = 2; /* default */
103+
const char *logLevelPropertyValue = getCommandLineOptionValue("-Dopenj9.internal.criu.logLevel", argc, argv, error);
104+
if (0 == *error) {
105+
const char *c = logLevelPropertyValue;
106+
if (NULL == c) {
107+
goto done;
108+
}
109+
for (; '\0' != *c; c++) {
110+
if (!isdigit(*c)) {
111+
goto setLogLevelOptionValueNotValidError;
112+
}
113+
}
114+
logLevelValue = atoi(logLevelPropertyValue);
115+
if ((0 <= logLevelValue) && (logLevelValue <= 4)) {
116+
goto done;
117+
} else {
118+
goto setLogLevelOptionValueNotValidError;
119+
}
120+
} else if (-1 == *error) {
121+
goto done;
122+
}
123+
setLogLevelOptionValueNotValidError:
124+
JLI_ReportErrorMessage(
125+
"The option '-Dopenj9.internal.criu.logLevel=%s' is not valid.",
126+
logLevelPropertyValue);
127+
*error = -2;
128+
done:
129+
return logLevelValue;
130+
}
131+
132+
/**
133+
* Check if the unprivileged mode is specified in the command line arguments.
134+
* @param[in] argc The number of command line arguments
135+
* @param[in] argv The array of command line arguments
136+
* @param[in/out] error A pointer to an integer for the error code
137+
* @return true if the unprivileged mode option is specified in the command line arguments, false otherwise
138+
*/
139+
static jboolean
140+
isUnprivilegedModeOn(int argc, char **argv, int *error)
141+
{
142+
jboolean isUnprivilegedModeOn = JNI_FALSE;
143+
const char *unprivilegedModePropertyValue = getCommandLineOptionValue("-Dopenj9.internal.criu.unprivilegedMode", argc, argv, error);
144+
if (0 == *error) {
145+
if (NULL == unprivilegedModePropertyValue) {
146+
isUnprivilegedModeOn = JNI_TRUE;
147+
} else {
148+
JLI_ReportErrorMessage(
149+
"The option '-Dopenj9.internal.criu.unprivilegedMode=%s' does not accept a value.",
150+
unprivilegedModePropertyValue);
151+
*error = -2;
152+
}
153+
}
154+
return isUnprivilegedModeOn;
155+
}
156+
157+
/**
158+
* Get the log file specified in the command line arguments.
159+
* @param[in] argc The number of command line arguments
160+
* @param[in] argv The array of command line arguments
161+
* @param[in/out] error A pointer to an integer for the error code
162+
* @return A pointer to the log file string if successful, NULL otherwise
163+
*/
164+
static const char *
165+
getLogFile(int argc, char **argv, int *error)
166+
{
167+
const char *logFile = NULL;
168+
const char *logFilePropertyValue = getCommandLineOptionValue("-Dopenj9.internal.criu.logFile", argc, argv, error);
169+
if (0 == *error) {
170+
if (NULL != logFilePropertyValue) {
171+
logFile = logFilePropertyValue;
172+
} else {
173+
JLI_ReportErrorMessage("The option -Dopenj9.internal.criu.logFile requires a value.");
174+
*error = -2;
175+
}
176+
}
177+
return logFile;
178+
}
179+
180+
/**
181+
* Restore the system state from a checkpoint using the CRIU tool.
182+
* @param[in] checkpointDirectory The directory containing the checkpoint data
183+
* @param[in] logLevel The log level for CRIU logging
184+
* @param[in] unprivilegedModeOn Indicates whether the unprivileged mode option is on
185+
* @param[in] logFile The log file option for CRIU
186+
* @return 0 if the execution of the 'criu restore' command succeeds, -1 otherwise
187+
*/
188+
static int
189+
restoreFromCheckpoint(const char *checkpointDirectory, int logLevel, jboolean unprivilegedModeOn, const char *logFile)
190+
{
191+
int length = -1;
192+
char logLevelOption[4] = { 0 };
193+
char *logFileOption = NULL;
194+
int argc = 0;
195+
const char *argv[9] = { NULL };
196+
argv[argc++] = "criu";
197+
argv[argc++] = "restore";
198+
argv[argc++] = "-D";
199+
argv[argc++] = checkpointDirectory;
200+
/* The log level is a single digit. */
201+
snprintf(logLevelOption, sizeof(logLevelOption), "-v%d", logLevel);
202+
argv[argc++] = logLevelOption;
203+
argv[argc++] = "--shell-job";
204+
if (unprivilegedModeOn) {
205+
argv[argc++] = "--unprivileged";
206+
}
207+
if (NULL != logFile) {
208+
length = strlen(logFile) + sizeof("--log-file=%s") - 1;
209+
logFileOption = (char *)JLI_MemAlloc(length + 1);
210+
if (NULL == logFileOption) {
211+
JLI_ReportErrorMessage("Failed to allocate memory for option '--log-file=%s'.", logFile);
212+
goto fail;
213+
}
214+
if (snprintf(logFileOption, length + 1, "--log-file=%s", logFile) < 0) {
215+
JLI_ReportErrorMessage("Failed to format option '--log-file=%s'.", logFile);
216+
goto fail;
217+
}
218+
argv[argc++] = logFileOption;
219+
}
220+
argv[argc] = NULL;
221+
execvp(argv[0], (char * const *)argv);
222+
/* If execvp returns, there was an error. */
223+
fail:
224+
if (NULL != logFileOption) {
225+
JLI_MemFree((void *)logFileOption);
226+
}
227+
return -1;
228+
}
229+
230+
/**
231+
* Handle the restoration of the system state from a checkpoint.
232+
* @param[in] argc The number of command line arguments
233+
* @param[in] argv The array of command line arguments
234+
*/
235+
void
236+
handleCRaCRestore(int argc, char **argv)
237+
{
238+
const char *checkpointDirectory = NULL;
239+
int error = 0;
240+
int parentProcessExitStatus = EXIT_SUCCESS;
241+
pid_t childProcessPid = 0;
242+
int logLevel = 0;
243+
int childProcessExitStatus = EXIT_SUCCESS;
244+
jboolean unprivilegedModeOn = JNI_FALSE;
245+
const char *logFile = NULL;
246+
int childProcessPidStatus = 0;
247+
int childProcessPidExitStatus = 0;
248+
checkpointDirectory = getCheckpointDirectory(argc, argv, &error);
249+
if (-1 == error) {
250+
/* Option -XX:CRaCRestoreFrom not specified. */
251+
return;
252+
}
253+
if (-2 == error) {
254+
JLI_ReportErrorMessage("Failed to get the CRIU checkpoint directory.");
255+
parentProcessExitStatus = EXIT_FAILURE;
256+
goto doneParentProcess;
257+
}
258+
/*
259+
* The if block will be invoked by the child process,
260+
* and the else block will be invoked by the parent process.
261+
*/
262+
childProcessPid = fork();
263+
if (0 == childProcessPid) {
264+
logLevel = getLogLevel(argc, argv, &error);
265+
if (-2 == error) {
266+
JLI_ReportErrorMessage("Failed to get the CRIU log level.");
267+
childProcessExitStatus = EXIT_FAILURE;
268+
goto doneChildProcess;
269+
}
270+
unprivilegedModeOn = isUnprivilegedModeOn(argc, argv, &error);
271+
if (-2 == error) {
272+
JLI_ReportErrorMessage("Failed to get the CRIU unprivileged mode.");
273+
childProcessExitStatus = EXIT_FAILURE;
274+
goto doneChildProcess;
275+
}
276+
logFile = getLogFile(argc, argv, &error);
277+
if (-2 == error) {
278+
JLI_ReportErrorMessage("Failed to get the CRIU log file.");
279+
childProcessExitStatus = EXIT_FAILURE;
280+
goto doneChildProcess;
281+
}
282+
childProcessExitStatus = restoreFromCheckpoint(checkpointDirectory, logLevel, unprivilegedModeOn, logFile);
283+
doneChildProcess:
284+
exit(childProcessExitStatus);
285+
} else {
286+
waitpid(childProcessPid, &childProcessPidStatus, 0);
287+
if (WIFEXITED(childProcessPidStatus)) {
288+
childProcessPidExitStatus = WEXITSTATUS(childProcessPidStatus);
289+
if (EXIT_SUCCESS == childProcessPidExitStatus) {
290+
JLI_ReportMessage("Completed restore with -XX:CRaCRestoreFrom=PATH.");
291+
} else {
292+
JLI_ReportErrorMessage("Failed to restore from checkpoint, error=%d.", childProcessPidExitStatus);
293+
parentProcessExitStatus = EXIT_FAILURE;
294+
}
295+
} else {
296+
JLI_ReportErrorMessage("The CRIU restore child process failed.");
297+
parentProcessExitStatus = EXIT_FAILURE;
298+
}
299+
}
300+
doneParentProcess:
301+
exit(parentProcessExitStatus);
302+
}
303+
#endif /* defined(J9VM_OPT_CRAC_SUPPORT) */
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* ===========================================================================
3+
* (c) Copyright IBM Corp. 2024, 2024 All Rights Reserved
4+
* ===========================================================================
5+
*
6+
* This code is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 only, as
8+
* published by the Free Software Foundation.
9+
*
10+
* IBM designates this particular file as subject to the "Classpath" exception
11+
* as provided by IBM in the LICENSE file that accompanied this code.
12+
*
13+
* This code is distributed in the hope that it will be useful, but WITHOUT
14+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16+
* version 2 for more details (a copy is included in the LICENSE file that
17+
* accompanied this code).
18+
*
19+
* You should have received a copy of the GNU General Public License version
20+
* 2 along with this work; if not, see <http://www.gnu.org/licenses/>.
21+
*
22+
* ===========================================================================
23+
*/
24+
25+
#ifndef CRIUHELPERS_H
26+
#define CRIUHELPERS_H
27+
28+
#include "j9cfg.h"
29+
30+
#if defined(J9VM_OPT_CRAC_SUPPORT)
31+
void handleCRaCRestore(int argc, char **argv);
32+
#endif /* defined(J9VM_OPT_CRAC_SUPPORT) */
33+
34+
#endif /* CRIUHELPERS_H */

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

+6-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

@@ -59,6 +59,7 @@
5959

6060
#include <assert.h>
6161

62+
#include "criuhelpers.h"
6263
#include "java.h"
6364
#include "jni.h"
6465

@@ -566,6 +567,10 @@ JavaMain(void* _args)
566567
jfieldID noArgMainField;
567568
jboolean noArgMain;
568569

570+
#if defined(J9VM_OPT_CRAC_SUPPORT)
571+
handleCRaCRestore(argc, argv);
572+
#endif /* defined(J9VM_OPT_CRAC_SUPPORT) */
573+
569574
RegisterThread();
570575

571576
/* Initialize the virtual machine */

0 commit comments

Comments
 (0)