Skip to content

Commit 5875c62

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 4f827d7 commit 5875c62

File tree

3 files changed

+353
-1
lines changed

3 files changed

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

@@ -568,6 +569,10 @@ JavaMain(void* _args)
568569

569570
RegisterThread();
570571

572+
#if defined(J9VM_OPT_CRAC_SUPPORT)
573+
handleCRaCRestore(argc, argv);
574+
#endif /* defined(J9VM_OPT_CRAC_SUPPORT) */
575+
571576
/* Initialize the virtual machine */
572577
start = CurrentTimeMicros();
573578
if (!InitializeJVM(&vm, &env, &ifn)) {

0 commit comments

Comments
 (0)