Skip to content

Commit

Permalink
Merge pull request #18837 from JasonFengJ9/jdkcracjcmd
Browse files Browse the repository at this point in the history
CRIU jcmd trigger checkpoint
  • Loading branch information
tajila committed Feb 1, 2024
2 parents eb1c385 + 07432f5 commit 5d03ee9
Show file tree
Hide file tree
Showing 12 changed files with 154 additions and 29 deletions.
11 changes: 8 additions & 3 deletions jcl/src/java.base/share/classes/jdk/crac/Core.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,17 @@
* The CRIU Support API.
*/
public class Core {
private static CRIUSupportContext<?> globalContext = new CRIUSupportContext<>();
private static CRIUSupportContext<?> globalContext;

/**
* Get a global context.
*
* @return a Context
*/
public static Context<Resource> getGlobalContext() {
if ((globalContext == null) && InternalCRIUSupport.isCRaCSupportEnabled()) {
globalContext = new CRIUSupportContext<>();
}
return (Context<Resource>)globalContext;
}

Expand All @@ -52,7 +55,7 @@ public static Context<Resource> getGlobalContext() {
public static void checkpointRestore() throws CheckpointException, RestoreException {
if (InternalCRIUSupport.isCRaCSupportEnabled()) {
try {
globalContext.checkpointJVM();
((CRIUSupportContext<?>)getGlobalContext()).checkpointJVM();
} catch (Throwable t) {
throw new CheckpointException(t);
}
Expand All @@ -63,7 +66,9 @@ public static void checkpointRestore() throws CheckpointException, RestoreExcept
}

class CRIUSupportContext<R extends Resource> extends Context<R> {
private InternalCRIUSupport internalCRIUSupport = new InternalCRIUSupport(
// InternalCRIUSupport.getCRaCCheckpointToDir() is not null if
// InternalCRIUSupport.isCRaCSupportEnabled() returns true before creating CRIUSupportContext<>().
private final InternalCRIUSupport internalCRIUSupport = new InternalCRIUSupport(
Paths.get(InternalCRIUSupport.getCRaCCheckpointToDir())).setLeaveRunning(false).setShellJob(true)
.setFileLocks(true);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,13 @@
/*[ENDIF] Sidecar19-SE */
import static openj9.internal.tools.attach.target.IPC.LOCAL_CONNECTOR_ADDRESS;

/*[IF CRIU_SUPPORT]*/
import openj9.internal.criu.NotCheckpointSafe;
/*[ENDIF] CRIU_SUPPORT */

/**
* This class handles established connections initiated by another VM
*
*
*/
final class Attachment extends Thread implements Response {

Expand All @@ -71,7 +75,7 @@ private static final class MethodRefsHolder {
static final Throwable managementAgentMethodThrowable;
static {
managementAgentMethodThrowable = AccessController.doPrivileged((PrivilegedAction<Throwable>) () -> {
String agentClassName =
String agentClassName =
/*[IF Sidecar19-SE]*/
"jdk.internal.agent.Agent"; //$NON-NLS-1$
/*[ELSE] Sidecar19-SE
Expand Down Expand Up @@ -132,7 +136,7 @@ private static final class MethodRefsHolder {

/**
* Create an attachment with a socket connection to the attacher
*
*
* @param portNum
* for socket to connect to attacher
* @return true if successfully connected
Expand Down Expand Up @@ -201,6 +205,9 @@ public void run() {
* function when requested by the attacher.
* @return true on error, detach command, or command socket closed
*/
/*[IF CRIU_SUPPORT]*/
@NotCheckpointSafe
/*[ENDIF] CRIU_SUPPORT */
boolean doCommand(InputStream cmdStream, OutputStream respStream) {
try {
byte[] cmdBytes = AttachmentConnection.streamReceiveBytes(cmdStream, true);
Expand Down Expand Up @@ -237,7 +244,7 @@ boolean doCommand(InputStream cmdStream, OutputStream respStream) {
return false;
}
} else if (cmd.startsWith(Command.START_MANAGEMENT_AGENT)) {
/*
/*
* Check if the Properties argument is embedded with the command.
* If so, it will be separated by a null byte
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@

import com.ibm.oti.vm.VM;

/*[IF CRAC_SUPPORT]*/
import openj9.internal.criu.InternalCRIUSupport;
/*[ENDIF] CRAC_SUPPORT */
import openj9.internal.management.ClassLoaderInfoBaseImpl;
import openj9.management.internal.IDCacheInitializer;
import openj9.management.internal.InvalidDumpOptionExceptionBase;
Expand Down Expand Up @@ -78,6 +81,13 @@ public class DiagnosticUtils {
*/
private static final String DIAGNOSTICS_THREAD_PRINT = "Thread.print"; //$NON-NLS-1$

/*[IF CRAC_SUPPORT]*/
/**
* Generate a checkpoint via CRIUSupport using a compatability name.
*/
private static final String DIAGNOSTICS_JDK_CHECKPOINT = "JDK.checkpoint"; //$NON-NLS-1$
/*[ENDIF] CRAC_SUPPORT */

/**
* Run System.gc();
*/
Expand Down Expand Up @@ -400,6 +410,21 @@ private static DiagnosticProperties doHelp(String diagnosticCommand) {

}

/*[IF CRAC_SUPPORT]*/
private static DiagnosticProperties doCheckpointJVM(String diagnosticCommand) {
Thread checkpointThread = new Thread(() -> {
try {
jdk.crac.Core.checkpointRestore();
} catch (Throwable t) {
t.printStackTrace();
}
});
checkpointThread.start();

return DiagnosticProperties.makeStringResult("JVM checkpoint requested"); //$NON-NLS-1$
}
/*[ENDIF] CRAC_SUPPORT */

/* Help strings for the jcmd utilities */
@SuppressWarnings("nls")
private static final String DIAGNOSTICS_HELP_HELP = "Show help for a command%n"
Expand Down Expand Up @@ -447,6 +472,12 @@ private static DiagnosticProperties doHelp(String diagnosticCommand) {
+ " agentLibrary: the absolute path of the agent%n"
+ " agent option: (Optional) the agent option string%n";

/*[IF CRAC_SUPPORT]*/
private static final String DIAGNOSTICS_JDK_CHECKPOINT_HELP = "Produce a JVM checkpoint via CRIUSupport.%n" //$NON-NLS-1$
+ FORMAT_PREFIX + DIAGNOSTICS_JDK_CHECKPOINT + "%n" //$NON-NLS-1$
+ "NOTE: this utility might significantly affect the performance of the target VM.%n"; //$NON-NLS-1$
/*[ENDIF] CRAC_SUPPORT */

/* Initialize the command and help text tables */
static {
IDCacheInitializer.init();
Expand Down Expand Up @@ -485,5 +516,12 @@ private static DiagnosticProperties doHelp(String diagnosticCommand) {

commandTable.put(DIAGNOSTICS_LOAD_JVMTI_AGENT, DiagnosticUtils::loadJVMTIAgent);
helpTable.put(DIAGNOSTICS_LOAD_JVMTI_AGENT, DIAGNOSTICS_LOAD_JVMTI_AGENT_HELP);

/*[IF CRAC_SUPPORT]*/
if (InternalCRIUSupport.isCRaCSupportEnabled()) {
commandTable.put(DIAGNOSTICS_JDK_CHECKPOINT, DiagnosticUtils::doCheckpointJVM);
helpTable.put(DIAGNOSTICS_JDK_CHECKPOINT, DIAGNOSTICS_JDK_CHECKPOINT_HELP);
}
/*[ENDIF] CRAC_SUPPORT */
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
import com.sun.tools.attach.VirtualMachine;

@SuppressWarnings("nls")
abstract class AttachApiTest {
public abstract class AttachApiTest {

protected static Logger logger = Logger.getLogger(AttachApiTest.class);
protected String testName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
import com.sun.tools.attach.spi.AttachProvider;

@SuppressWarnings("nls")
class TargetManager {
public class TargetManager {
static final String OPENJ9_INTERNAL_TOOLS_ATTACH_TARGET_ATTACH_HANDLER = "openj9.internal.tools.attach.target.AttachHandler";
private static Logger logger = Logger.getLogger(TargetManager.class);
public static final String PID_PREAMBLE = "pid=";
Expand All @@ -58,7 +58,8 @@ class TargetManager {
public static final String STATUS_DUPLICATE_VMID = "duplicate_vmid";
public static final String STATUS_INIT_FAIL = "attach_init_fail";

String targetId, displayName;
public String targetId;
String displayName;
private Process proc;
private BufferedWriter targetInWriter;
private BufferedReader targetOutReader;
Expand Down Expand Up @@ -330,7 +331,7 @@ private Process launchTarget(String cmdName, String tgtId,
return target;
}

boolean syncWithTarget() {
public boolean syncWithTarget() {
targetVmStatus = readTargetPidAndStatus();
if (!TargetStatus.INIT_SUCCESS.equals(targetVmStatus)) {
logger.info("TargetManager.syncWithTarget() failed!");
Expand Down
2 changes: 2 additions & 0 deletions test/functional/cmdLineTests/criu/build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<property name="src" location="./src"/>
<property name="TestUtilities" location="../../TestUtilities/src"/>
<property name="build" location="./bin"/>
<property name="TestUtilitiesJ9" location="../../TestUtilitiesJ9/src" />

<target name="init">
<mkdir dir="${DEST}" />
Expand Down Expand Up @@ -93,6 +94,7 @@
<exclude name="${excludeTestMachineResourceChange}" />
<src path="${src}" />
<src path="${TestUtilities}" />
<src path="${TestUtilitiesJ9}" />
<classpath>
<pathelement location="${LIB_DIR}/testng.jar"/>
</classpath>
Expand Down
23 changes: 12 additions & 11 deletions test/functional/cmdLineTests/criu/cracScript.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,22 @@ echo "start running script";
# $1 is the TEST_ROOT
# $2 is the JAVA_COMMAND
# $3 is the JVM_OPTIONS
# $4 is the MAINCLASS
# $5 is the APP ARGS
# $6 is the NUM_CHECKPOINT
# $7 is the KEEP_CHECKPOINT
# $8 is the KEEP_TEST_OUTPUT
# $4 is the TESTNG
# $5 is the MAINCLASS
# $6 is the APP ARGS
# $7 is the NUM_CHECKPOINT
# $8 is the KEEP_CHECKPOINT
# $9 is the KEEP_TEST_OUTPUT

echo "export GLIBC_TUNABLES=glibc.cpu.hwcaps=-XSAVEC,-XSAVE,-AVX2,-ERMS,-AVX,-AVX_Fast_Unaligned_Load";
export GLIBC_TUNABLES=glibc.pthread.rseq=0:glibc.cpu.hwcaps=-XSAVEC,-XSAVE,-AVX2,-ERMS,-AVX,-AVX_Fast_Unaligned_Load
echo "export LD_BIND_NOT=on";
export LD_BIND_NOT=on
echo "$2 $3 -cp "$1/criu.jar" $4 $5 $6"
$2 $3 -cp "$1/criu.jar" $4 $5 $6 >testOutput 2>&1;
echo "$2 $3 -cp "$1/criu.jar:$4" $5 $6 $7"
$2 $3 -cp "$1/criu.jar:$4" $5 $6 $7 >testOutput 2>&1;

if [ "$7" != true ]; then
NUM_CHECKPOINT=$6
if [ "$8" != true ]; then
NUM_CHECKPOINT=$7
for ((i=0; i<$NUM_CHECKPOINT; i++)); do
sleep 2;
criu restore -D ./cpData --shell-job >criuOutput 2>&1;
Expand All @@ -50,8 +51,8 @@ fi

cat testOutput criuOutput;

if [ "$7" != true ]; then
if [ "$8" != true ]; then
if [ "$8" != true ]; then
if [ "$9" != true ]; then
rm -rf testOutput criuOutput
echo "Removed test output files"
fi
Expand Down
16 changes: 15 additions & 1 deletion test/functional/cmdLineTests/criu/criu_nonPortable_jdk_crac.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
<variable name="CRACCHECKPOINTTO" value="-XX:CRaCCheckpointTo=./cpData"/>

<test id="jdk.crac - create and restore once">
<command>bash $SCRIPPATH$ $TEST_RESROOT$ $JAVA_COMMAND$ "$JVM_OPTIONS$ $EXPORTS$ $CRACCHECKPOINTTO$" $MAINCLASS_TESTJDKCRAC$ unusedArgument 1 false false</command>
<command>bash $SCRIPPATH$ $TEST_RESROOT$ $JAVA_COMMAND$ "$JVM_OPTIONS$ $EXPORTS$ $CRACCHECKPOINTTO$" $TESTNG$ $MAINCLASS_TESTJDKCRAC$ testJDKCheckpoint 1 false false</command>
<output type="success" caseSensitive="no" regex="no">Killed</output>
<output type="required" caseSensitive="yes" regex="no">Pre-checkpoint</output>
<output type="success" caseSensitive="yes" regex="no">Post-checkpoint</output>
Expand All @@ -44,4 +44,18 @@
<output type="failure" caseSensitive="yes" regex="no">Could not dump the JVM processes, err=-70</output>
<output type="failure" caseSensitive="yes" regex="no">User requested Java dump using</output>
</test>

<test id="jdk.crac - jcmd request checkpoint via JDK.checkpoint">
<command>bash $SCRIPPATH$ $TEST_RESROOT$ $JAVA_COMMAND$ "$JVM_OPTIONS$ $EXPORTS$ $CRACCHECKPOINTTO$" $TESTNG$ $MAINCLASS_TESTJDKCRAC$ JDK.checkpoint 1 false false</command>
<output type="success" caseSensitive="no" regex="no">JVM checkpoint requested</output>
<output type="failure" caseSensitive="yes" regex="no">CRIU is not enabled</output>
<output type="failure" caseSensitive="yes" regex="no">Operation not permitted</output>
<!-- If CRIU can't acquire the original thread IDs, this test will fail. Nothing can be done about this failure. -->
<output type="success" caseSensitive="yes" regex="no">Thread pid mismatch</output>
<output type="success" caseSensitive="yes" regex="no">do not match expected</output>
<output type="success" caseSensitive="yes" regex="no">Unable to create a thread:</output>
<!-- In the past, the failure below was caused by an issue where CRIU can't be found on the PATH. -->
<output type="failure" caseSensitive="yes" regex="no">Could not dump the JVM processes, err=-70</output>
<output type="failure" caseSensitive="yes" regex="no">User requested Java dump using</output>
</test>
</suite>
2 changes: 1 addition & 1 deletion test/functional/cmdLineTests/criu/playlist.xml
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@
<command>
$(JAVA_COMMAND) $(CMDLINETESTER_JVM_OPTIONS) -Xdump \
-DSCRIPPATH=$(TEST_RESROOT)$(D)cracScript.sh -DTEST_RESROOT=$(TEST_RESROOT) \
-DJAVA_COMMAND=$(JAVA_COMMAND) -DJVM_OPTIONS=$(Q)$(JVM_OPTIONS)$(Q) \
-DJAVA_COMMAND=$(JAVA_COMMAND) -DJVM_OPTIONS=$(Q)$(JVM_OPTIONS)$(Q) -DTESTNG=$(TESTNG) \
-jar $(CMDLINETESTER_JAR) -config $(Q)$(TEST_RESROOT)$(D)criu_nonPortable_jdk_crac.xml$(Q) \
-explainExcludes -xids all,$(PLATFORM),$(VARIATION) -nonZeroExitWhenError; \
$(TEST_STATUS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,76 @@
*******************************************************************************/
package org.openj9.criu;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import openj9.internal.criu.InternalCRIUSupport;

public class TestJDKCRAC {
import org.openj9.test.attachAPI.AttachApiTest;
import org.openj9.test.attachAPI.TargetManager;
import org.openj9.test.attachAPI.TargetVM;
import org.openj9.test.util.StringUtilities;

import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;

public class TestJDKCRAC extends AttachApiTest {

private static final String JCMD_COMMAND = "jcmd";
private static final String ERROR_TARGET_NOT_LAUNCH = "target did not launch";
private static final String ERROR_EXPECTED_STRING_NOT_FOUND = "Expected string not found";
private static final String CRAC_CHECKPOINT_DIR = "-XX:CRaCCheckpointTo=./cpData";
private static final String TARGET_VM_CLASS = TargetVM.class.getCanonicalName();

public static void main(String args[]) throws Exception {
Path imagePath = Paths.get(InternalCRIUSupport.getCRaCCheckpointToDir());
CRIUTestUtils.deleteCheckpointDirectory(imagePath);
CRIUTestUtils.createCheckpointDirectory(imagePath);
public static void main(String[] args) throws Exception {
if (args.length == 0) {
throw new RuntimeException("Test name required");
} else {
Path imagePath = Paths.get(InternalCRIUSupport.getCRaCCheckpointToDir());
CRIUTestUtils.deleteCheckpointDirectory(imagePath);
CRIUTestUtils.createCheckpointDirectory(imagePath);

String test = args[0];
TestJDKCRAC testJDKCRaC = new TestJDKCRAC();
switch (test) {
case "testJDKCheckpoint":
testJDKCRaC.testJDKCheckpoint();
break;
case "JDK.checkpoint":
testJDKCRaC.testJcmdCheckpoint(test);
break;
default:
throw new RuntimeException("incorrect test name");
}
}
}

private void testJDKCheckpoint() throws Exception {
CRIUTestUtils.showThreadCurrentTime("Pre-checkpoint - jdk.crac.Core.checkpointRestore()");
jdk.crac.Core.checkpointRestore();
CRIUTestUtils.showThreadCurrentTime("Post-checkpoint - jdk.crac.Core.checkpointRestore()");
}

private void testJcmdCheckpoint(String command) throws IOException {
getJdkUtilityPath(JCMD_COMMAND);
TargetManager tgt = new TargetManager(TARGET_VM_CLASS, null, Collections.singletonList(CRAC_CHECKPOINT_DIR), Collections.emptyList());
tgt.syncWithTarget();
String targetId = tgt.targetId;
assertNotNull(targetId, ERROR_TARGET_NOT_LAUNCH);
List<String> argsTargetVM = new ArrayList<>();
argsTargetVM.add(targetId);
log("test " + command);
argsTargetVM.add(command);
List<String> jcmdOutput = runCommandAndLogOutput(argsTargetVM);
String expectedString = "JVM checkpoint requested";
log("Expected jcmd output string: " + expectedString);
Optional<String> searchResult = StringUtilities.searchSubstring(expectedString, jcmdOutput);
System.out.println("jcmdOutput = " + jcmdOutput);
assertTrue(searchResult.isPresent(), ERROR_EXPECTED_STRING_NOT_FOUND + " in jcmd output: " + expectedString);
}
}

0 comments on commit 5d03ee9

Please sign in to comment.