Skip to content

Commit 60cd6f2

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 60cd6f2

File tree

3 files changed

+399
-1
lines changed

3 files changed

+399
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
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+
* Get the value of a command line option in the array of command line arguments.
30+
* @param[in] optionName The name of the command line option to search for
31+
* @param[in] argc The number of command line arguments
32+
* @param[in] argv The array of command line arguments
33+
* @param[out] error A pointer to an integer for the error code
34+
* @return A pointer to the value of the command line option if found, NULL otherwise
35+
*/
36+
static const char *
37+
getCommandLineOptionValue(const char *optionName, int argc, char **argv, int *error)
38+
{
39+
const char *value = NULL;
40+
int i = 0;
41+
size_t optionNameLength = strlen(optionName);
42+
jboolean optionNameFound = JNI_FALSE;
43+
*error = 0;
44+
for (i = 0; i < argc; i++) {
45+
const char *arg = argv[i];
46+
if (0 == strncmp(arg, optionName, optionNameLength)) {
47+
const char *equals = arg + optionNameLength;
48+
if (('=' == *equals) || ('\0' == *equals)) {
49+
optionNameFound = JNI_TRUE;
50+
value = equals;
51+
if ('=' == *value) {
52+
value += 1;
53+
}
54+
if ('\0' == *value) {
55+
value = NULL;
56+
}
57+
break;
58+
}
59+
}
60+
}
61+
if (!optionNameFound) {
62+
*error = -1;
63+
}
64+
return value;
65+
}
66+
67+
/**
68+
* Get the checkpoint directory from command line options.
69+
* @param[in] argc The number of command line arguments
70+
* @param[in] argv The array of command line arguments
71+
* @param[in/out] error A pointer to an integer for the error code
72+
* @return A pointer to the checkpoint directory if found, NULL otherwise
73+
*/
74+
static const char *
75+
getCheckpointDirectory(int argc, char **argv, int *error)
76+
{
77+
const char *checkpointDirectory = NULL;
78+
const char *checkpointDirectoryPropertyValue = getCommandLineOptionValue("-XX:CRaCRestoreFrom", argc, argv, error);
79+
if (0 == *error) {
80+
if (NULL != checkpointDirectoryPropertyValue) {
81+
checkpointDirectory = checkpointDirectoryPropertyValue;
82+
} else {
83+
JLI_ReportErrorMessage("The value of the command line option -XX:CRaCRestoreFrom was not found.");
84+
*error = -2;
85+
}
86+
}
87+
return checkpointDirectory;
88+
}
89+
90+
/**
91+
* Get the log level specified in the command line arguments.
92+
* @param[in] argc The number of command line arguments
93+
* @param[in] argv The array of command line arguments
94+
* @param[in/out] error A pointer to an integer for the error code
95+
* @return The log level integer if successful, default of 2 otherwise
96+
*/
97+
static int
98+
getLogLevel(int argc, char **argv, int *error)
99+
{
100+
int logLevelValue = 2; /* default */
101+
const char *logLevelPropertyValue = NULL;
102+
logLevelPropertyValue = getCommandLineOptionValue("-Dopenj9.internal.criu.logLevel", argc, argv, error);
103+
if (0 == *error) {
104+
const char *c = logLevelPropertyValue;
105+
if (NULL == c) {
106+
goto done;
107+
}
108+
for (; '\0' != *c; c++) {
109+
if (!isdigit(*c)) {
110+
goto setLogLevelOptionValueNotValidError;
111+
}
112+
}
113+
logLevelValue = atoi(logLevelPropertyValue);
114+
if ((0 <= logLevelValue) && (logLevelValue <= 4)) {
115+
goto done;
116+
} else {
117+
goto setLogLevelOptionValueNotValidError;
118+
}
119+
} else if (-1 == *error) {
120+
goto done;
121+
}
122+
setLogLevelOptionValueNotValidError:
123+
JLI_ReportErrorMessage("The value of the command line option is not valid, option=-Dopenj9.internal.criu.logLevel, value=%s.", logLevelPropertyValue);
124+
*error = -2;
125+
done:
126+
return logLevelValue;
127+
}
128+
129+
/**
130+
* Check if the unprivileged mode is specified in the command line arguments.
131+
* @param[in] argc The number of command line arguments
132+
* @param[in] argv The array of command line arguments
133+
* @param[in/out] error A pointer to an integer for the error code
134+
* @return true if the unprivileged mode option is specified in the command line arguments, false otherwise
135+
*/
136+
static jboolean
137+
isUnprivilegedModeOn(int argc, char **argv, int *error)
138+
{
139+
jboolean isUnprivilegedModeOn = JNI_FALSE;
140+
const char *unprivilegedModePropertyValue = getCommandLineOptionValue("-Dopenj9.internal.criu.unprivilegedMode", argc, argv, error);
141+
if (0 == *error) {
142+
if (NULL == unprivilegedModePropertyValue) {
143+
isUnprivilegedModeOn = JNI_TRUE;
144+
} else {
145+
JLI_ReportErrorMessage("The value of the command line option is not expected, option=-Dopenj9.internal.criu.unprivilegedMode, value=%s.", unprivilegedModePropertyValue);
146+
*error = -2;
147+
}
148+
}
149+
return isUnprivilegedModeOn;
150+
}
151+
152+
/**
153+
* Get the log file specified in the command line arguments.
154+
* @param[in] argc The number of command line arguments
155+
* @param[in] argv The array of command line arguments
156+
* @param[in/out] error A pointer to an integer for the error code
157+
* @return A pointer to the log file string if successful, NULL otherwise
158+
*/
159+
static const char *
160+
getLogFile(int argc, char **argv, int *error)
161+
{
162+
const char *logFile = NULL;
163+
const char *logFilePropertyValue = getCommandLineOptionValue("-Dopenj9.internal.criu.logFile", argc, argv, error);
164+
if (0 == *error) {
165+
if (NULL != logFilePropertyValue) {
166+
logFile = logFilePropertyValue;
167+
} else {
168+
JLI_ReportErrorMessage("The value of the command line option -Dopenj9.internal.criu.logFile was not found.");
169+
*error = -2;
170+
}
171+
}
172+
return logFile;
173+
}
174+
175+
/**
176+
* Restore the system state from a checkpoint using the CRIU tool.
177+
* @param[in] checkpointDirectory The directory containing the checkpoint data
178+
* @param[in] logLevel The log level for CRIU logging
179+
* @param[in] unprivilegedModeOn Indicates whether the unprivileged mode option is on
180+
* @param[in] logFile The log file option for CRIU
181+
* @return 0 if the execution of the 'criu restore' command succeeds, -1 otherwise
182+
*/
183+
static int
184+
restoreFromCheckpoint(const char *checkpointDirectory, int logLevel, jboolean unprivilegedModeOn, const char *logFile)
185+
{
186+
int restoreStatus = 0;
187+
int length = -1;
188+
char *logLevelOption = NULL;
189+
char *logFileOption = NULL;
190+
int argc = 0;
191+
const char *argv[9] = { NULL };
192+
argv[argc++] = "criu";
193+
argv[argc++] = "restore";
194+
argv[argc++] = "-D";
195+
argv[argc++] = checkpointDirectory;
196+
length = snprintf(NULL, 0, "-v%d", logLevel);
197+
if (length < 0) {
198+
char logLevelString[2] = { 0 };
199+
sprintf(logLevelString, "%d", logLevel);
200+
JLI_ReportErrorMessage("Failed to calculate the length of the command option, value=%s, format=%s.", logLevelString, "-v%d");
201+
restoreStatus = -1;
202+
goto done;
203+
}
204+
logLevelOption = (char *)JLI_MemAlloc(length + 1);
205+
if (NULL == logLevelOption) {
206+
char logLevelString[2] = { 0 };
207+
sprintf(logLevelString, "%d", logLevel);
208+
JLI_ReportErrorMessage("Failed to allocate memory for the command option, value=%s, format=%s.", logLevelString, "-v%d");
209+
restoreStatus = -1;
210+
goto done;
211+
}
212+
if (snprintf(logLevelOption, length + 1, "-v%d", logLevel) < 0) {
213+
char logLevelString[2] = { 0 };
214+
sprintf(logLevelString, "%d", logLevel);
215+
JLI_ReportErrorMessage("Failed to allocate memory for the command option, value=%s, format=%s.", logLevelString, "-v%d");
216+
restoreStatus = -1;
217+
goto freeLogLevelOption;
218+
}
219+
argv[argc++] = logLevelOption;
220+
argv[argc++] = "--shell-job";
221+
if (unprivilegedModeOn) {
222+
argv[argc++] = "--unprivileged";
223+
}
224+
if (NULL != logFile) {
225+
length = snprintf(NULL, 0, "--log-file=%s", logFile);
226+
if (length < 0) {
227+
JLI_ReportErrorMessage("Failed to calculate the length of the command option, value=%s, format=%s.", logFile, "--log-file=%s");
228+
restoreStatus = -1;
229+
goto freeLogLevelOption;
230+
}
231+
logFileOption = (char *)JLI_MemAlloc(length + 1);
232+
if (NULL == logFileOption) {
233+
JLI_ReportErrorMessage("Failed to allocate memory for the command option, value=%s, format=%s.", logFile, "--log-file=%s");
234+
restoreStatus = -1;
235+
goto freeLogLevelOption;
236+
}
237+
if (snprintf(logFileOption, length + 1, "--log-file=%s", logFile) < 0) {
238+
JLI_ReportErrorMessage("Failed to allocate memory for the command option, value=%s, format=%s.", logFile, "--log-file=%s");
239+
restoreStatus = -1;
240+
goto freeLogFileOption;
241+
}
242+
argv[argc++] = logFileOption;
243+
}
244+
argv[argc] = NULL;
245+
execvp(argv[0], (char * const *)argv);
246+
/* If execvp returns, there was an error. */
247+
restoreStatus = -1;
248+
freeLogFileOption:
249+
if (logFileOption != NULL) {
250+
JLI_MemFree((void *)logFileOption);
251+
logFileOption = NULL;
252+
}
253+
freeLogLevelOption:
254+
if (logLevelOption != NULL) {
255+
JLI_MemFree((void *)logLevelOption);
256+
logLevelOption = NULL;
257+
}
258+
done:
259+
return restoreStatus;
260+
}
261+
262+
/**
263+
* Handle the restoration of the system state from a checkpoint.
264+
* @param[in] argc The number of command line arguments
265+
* @param[in] argv The array of command line arguments
266+
*/
267+
void
268+
handleCRaCRestore(int argc, char **argv)
269+
{
270+
const char *checkpointDirectory = NULL;
271+
int error = 0;
272+
int parentProcessExitStatus = EXIT_SUCCESS;
273+
pid_t childProcessPid = 0;
274+
int logLevel = 0;
275+
int childProcessExitStatus = EXIT_SUCCESS;
276+
jboolean unprivilegedModeOn = JNI_FALSE;
277+
const char *logFile = NULL;
278+
int childProcessPidStatus = 0;
279+
int childProcessPidExitStatus = 0;
280+
checkpointDirectory = getCheckpointDirectory(argc, argv, &error);
281+
if (-1 == error) {
282+
return;
283+
}
284+
if (-2 == error) {
285+
JLI_ReportErrorMessage("Failed to get the CRIU checkpoint directory, error=%d.", error);
286+
parentProcessExitStatus = EXIT_FAILURE;
287+
goto doneParentProcess;
288+
}
289+
/*
290+
* The if block will be invoked by the child process,
291+
* and the else block will be invoked by the parent process.
292+
*/
293+
childProcessPid = fork();
294+
if (0 == childProcessPid) {
295+
logLevel = getLogLevel(argc, argv, &error);
296+
if (-2 == error) {
297+
JLI_ReportErrorMessage("Failed to get the CRIU log level, error=%d.", error);
298+
childProcessExitStatus = EXIT_FAILURE;
299+
goto doneChildProcess;
300+
}
301+
unprivilegedModeOn = isUnprivilegedModeOn(argc, argv, &error);
302+
if (-2 == error) {
303+
JLI_ReportErrorMessage("Failed to get the CRIU unprivileged mode, error=%d.", error);
304+
childProcessExitStatus = EXIT_FAILURE;
305+
goto doneChildProcess;
306+
}
307+
logFile = getLogFile(argc, argv, &error);
308+
if (-2 == error) {
309+
JLI_ReportErrorMessage("Failed to get the CRIU log file, error=%d.", error);
310+
childProcessExitStatus = EXIT_FAILURE;
311+
goto doneChildProcess;
312+
}
313+
childProcessExitStatus = restoreFromCheckpoint(checkpointDirectory, logLevel, unprivilegedModeOn, logFile);
314+
doneChildProcess:
315+
exit(childProcessExitStatus);
316+
} else {
317+
waitpid(childProcessPid, &childProcessPidStatus, 0);
318+
if (WIFEXITED(childProcessPidStatus)) {
319+
childProcessPidExitStatus = WEXITSTATUS(childProcessPidStatus);
320+
if (EXIT_SUCCESS == childProcessPidExitStatus) {
321+
JLI_ReportMessage("Completed restore with -XX:CRaCRestoreFrom=PATH.");
322+
} else {
323+
JLI_ReportErrorMessage("Failed to restore from checkpoint, error=%d.", childProcessPidExitStatus);
324+
parentProcessExitStatus = EXIT_FAILURE;
325+
}
326+
} else {
327+
JLI_ReportErrorMessage("The CRIU restore child process failed.");
328+
parentProcessExitStatus = EXIT_FAILURE;
329+
}
330+
}
331+
doneParentProcess:
332+
exit(parentProcessExitStatus);
333+
}
334+
#endif /* defined(J9VM_OPT_CRAC_SUPPORT) */
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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 <assert.h>
29+
30+
#include "java.h"
31+
#include "jni.h"
32+
#include "j9cfg.h"
33+
#if defined(J9VM_OPT_CRAC_SUPPORT)
34+
#include <ctype.h>
35+
#include <sys/wait.h>
36+
37+
static const char *
38+
getCommandLineOptionValue(const char *optionName, int argc, char **argv, int *error);
39+
40+
static const char *
41+
getCheckpointDirectory(int argc, char **argv, int *error);
42+
43+
static int
44+
getLogLevel(int argc, char **argv, int *error);
45+
46+
static jboolean
47+
isUnprivilegedModeOn(int argc, char **argv, int *error);
48+
49+
static const char *
50+
getLogFile(int argc, char **argv, int *error);
51+
52+
static int
53+
restoreFromCheckpoint(const char *checkpointDirectory, int logLevel, jboolean unprivilegedModeOn, const char *logFile);
54+
55+
void
56+
handleCRaCRestore(int argc, char **argv);
57+
#endif /* defined(J9VM_OPT_CRAC_SUPPORT) */
58+
59+
#endif /* CRIUHELPERS_H */

0 commit comments

Comments
 (0)