Skip to content

Commit

Permalink
Merge pull request #15609 from ymanton/jit-checkpoint-nanotime
Browse files Browse the repository at this point in the history
Fix JIT System.nanoTime() in checkpoint mode
  • Loading branch information
tajila committed Aug 10, 2022
2 parents 0cca472 + d3cdc1b commit 8c885cb
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 18 deletions.
9 changes: 9 additions & 0 deletions runtime/compiler/env/VMJ9.cpp
Expand Up @@ -9351,6 +9351,15 @@ TR_J9VMBase::inSnapshotMode()
#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */
}

bool
TR_J9VMBase::isSnapshotModeEnabled()
{
#if defined(J9VM_OPT_CRIU_SUPPORT)
return getJ9JITConfig()->javaVM->internalVMFunctions->isCRIUSupportEnabled(vmThread());
#else /* defined(J9VM_OPT_CRIU_SUPPORT) */
return false;
#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */
}

// Native method bodies
//
Expand Down
15 changes: 15 additions & 0 deletions runtime/compiler/env/VMJ9.h
Expand Up @@ -1345,8 +1345,23 @@ class TR_J9VMBase : public TR_FrontEnd
virtual bool isStringCompressionEnabledVM();
virtual void *getInvokeExactThunkHelperAddress(TR::Compilation *comp, TR::SymbolReference *glueSymRef, TR::DataType dataType);

/**
* \brief Answers whether snapshots (checkpoints) that will include this compiled body
* (if/when it is successfully compiled) can be taken.
*
* \return True if snapshots can be taken, false if no snapshots that will include this
* body are allowed.
*/
bool inSnapshotMode();

/**
* \brief Answers whether checkpoint and restore mode is enabled (but not necessarily
* whether snapshots can be taken or if any restores have already occurred).
*
* \return True if checkpoint and restore mode is enabled, false otherwise.
*/
bool isSnapshotModeEnabled();

protected:

enum // _flags
Expand Down
24 changes: 11 additions & 13 deletions runtime/compiler/optimizer/J9Simplifier.cpp
Expand Up @@ -326,22 +326,20 @@ J9::Simplifier::simplifyiCallMethods(TR::Node * node, TR::Block * block)
TR::Node *
J9::Simplifier::simplifylCallMethods(TR::Node * node, TR::Block * block)
{
if (comp()->cg()->getSupportsCurrentTimeMaxPrecision())
TR::MethodSymbol * methodSymbol = node->getSymbol()->getMethodSymbol();
if (methodSymbol)
{
TR::MethodSymbol * methodSymbol = node->getSymbol()->getMethodSymbol();
if (methodSymbol)
if ((methodSymbol->getRecognizedMethod() == TR::java_lang_System_currentTimeMillis) &&
comp()->cg()->getSupportsMaxPrecisionMilliTime() &&
(methodSymbol->isJNI() || methodSymbol->isVMInternalNative() || methodSymbol->isJITInternalNative()))
{
if (comp()->cg()->getSupportsMaxPrecisionMilliTime() &&
(methodSymbol->getRecognizedMethod() == TR::java_lang_System_currentTimeMillis) &&
(methodSymbol->isJNI() || methodSymbol->isVMInternalNative() || methodSymbol->isJITInternalNative()))
{
node = convertCurrentTimeMillis(node, block);
}
else if (methodSymbol->getRecognizedMethod() == TR::java_lang_System_nanoTime &&
node = convertCurrentTimeMillis(node, block);
}
else if ((methodSymbol->getRecognizedMethod() == TR::java_lang_System_nanoTime) &&
comp()->cg()->getSupportsCurrentTimeMaxPrecision() &&
(methodSymbol->isJNI() || methodSymbol->isVMInternalNative() || methodSymbol->isJITInternalNative()))
{
node = convertNanoTime(node, block);
}
{
node = convertNanoTime(node, block);
}
}
else
Expand Down
27 changes: 26 additions & 1 deletion runtime/compiler/x/codegen/J9TreeEvaluator.cpp
Expand Up @@ -9255,6 +9255,17 @@ inlineNanoTime(
// result = reg + result
generateRegMemInstruction(TR::InstOpCode::LEA8RegMem, node, result, generateX86MemoryReference(reg, result, 0, cg), cg);

#if defined(J9VM_OPT_CRIU_SUPPORT)
if (fej9->isSnapshotModeEnabled())
{
// result = result - nanoTimeMonotonicClockDelta
TR::Register *vmThreadReg = cg->getVMThreadRegister();
generateRegMemInstruction(TR::InstOpCode::L8RegMem, node, reg, generateX86MemoryReference(vmThreadReg, offsetof(J9VMThread, javaVM), cg), cg);
generateRegMemInstruction(TR::InstOpCode::L8RegMem, node, reg, generateX86MemoryReference(reg, offsetof(J9JavaVM, portLibrary), cg), cg);
generateRegMemInstruction(TR::InstOpCode::SUB8RegMem, node, result, generateX86MemoryReference(reg, offsetof(J9PortLibrary, nanoTimeMonotonicClockDelta), cg), cg);
}
#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */

cg->stopUsingRegister(reg);

// Store the result to memory if necessary
Expand Down Expand Up @@ -9369,9 +9380,23 @@ inlineNanoTime(
generateRegRegInstruction(TR::InstOpCode::IMUL4AccReg, node, eaxReal, edxReal, dep1, cg);


// add the two parts then store it back
// add the two parts
generateRegRegInstruction(TR::InstOpCode::ADD4RegReg, node, eaxReal, reglow, cg);
generateRegImmInstruction(TR::InstOpCode::ADC4RegImm4, node, edxReal, 0x0, cg);

#if defined(J9VM_OPT_CRIU_SUPPORT)
if (fej9->isSnapshotModeEnabled())
{
// subtract nanoTimeMonotonicClockDelta from the result
TR::Register *vmThreadReg = cg->getVMThreadRegister();
generateRegMemInstruction(TR::InstOpCode::L4RegMem, node, regLow, generateX86MemoryReference(vmThreadReg, offsetof(J9VMThread, javaVM), cg), cg);
generateRegMemInstruction(TR::InstOpCode::L4RegMem, node, regLow, generateX86MemoryReference(regLow, offsetof(J9JavaVM, portLibrary), cg), cg);
generateRegMemInstruction(TR::InstOpCode::SUB4RegMem, node, eaxReal, generateX86MemoryReference(regLow, offsetof(J9PortLibrary, nanoTimeMonotonicClockDelta), cg), cg);
generateRegMemInstruction(TR::InstOpCode::SBB4RegMem, node, edxReal, generateX86MemoryReference(regLow, offsetof(J9PortLibrary, nanoTimeMonotonicClockDelta) + 4, cg), cg);
}
#endif /* defined(J9VM_OPT_CRIU_SUPPORT) */

// store it back
generateMemRegInstruction(TR::InstOpCode::S4MemReg, node, generateX86MemoryReference(resultAddress, 0, cg), eaxReal, cg);
generateMemRegInstruction(TR::InstOpCode::S4MemReg, node, generateX86MemoryReference(resultAddress, 4, cg), edxReal, cg);

Expand Down
10 changes: 6 additions & 4 deletions runtime/compiler/z/codegen/J9CodeGenerator.cpp
Expand Up @@ -165,9 +165,6 @@ J9::Z::CodeGenerator::initialize()
if (!disableMonitorCacheLookup)
comp->setOption(TR_EnableMonitorCacheLookup);

// Enable high-resolution timer
cg->setSupportsCurrentTimeMaxPrecision();

// Defect 109299 : PMR 14649,999,760 / CritSit AV8426
// Turn off use of hardware clock on zLinux for calculating currentTimeMillis() as user can adjust time on their system.
//
Expand All @@ -177,6 +174,12 @@ J9::Z::CodeGenerator::initialize()
if (comp->target().isZOS())
cg->setSupportsMaxPrecisionMilliTime();

// Enable high-resolution timer for System.nanoTime() unless we need to support checkpointing (i.e. snapshot mode), which requires
// that we adjust nanoTime() after restoring checkpoints. This adjustment is currently not implemented for the high res timer, hence
// we need to stick to the Java nanoTime() implementation.
if (!fej9->isSnapshotModeEnabled())
cg->setSupportsCurrentTimeMaxPrecision();

// Support BigDecimal Long Lookaside versioning optimizations.
if (!comp->getOption(TR_DisableBDLLVersioning))
cg->setSupportsBigDecimalLongLookasideVersioning();
Expand Down Expand Up @@ -4095,4 +4098,3 @@ J9::Z::CodeGenerator::supportsTrapsInTMRegion()
{
return self()->comp()->target().isZOS();
}

22 changes: 22 additions & 0 deletions test/functional/cmdLineTests/criu/criu_nonPortable.xml
Expand Up @@ -67,6 +67,28 @@
</test>
-->

<test id="Create CRIU checkpoint image and restore three times - testSystemNanoTimeJitPreCheckpointCompile">
<command>bash $SCRIPPATH$ $TEST_RESROOT$ $JAVA_COMMAND$ "$JVM_OPTIONS$" "$MAINCLASS_TIMECHANGE$ testSystemNanoTimeJitPreCheckpointCompile" 3 3 false</command>
<output type="success" caseSensitive="no" regex="no">Killed</output>
<output type="required" caseSensitive="yes" regex="no">System.nanoTime() before CRIU checkpoint:</output>
<output type="required" caseSensitive="yes" regex="no">PASSED: System.nanoTime() after CRIU restore:</output>
<output type="failure" caseSensitive="yes" regex="no">CRIU is not enabled</output>
<output type="failure" caseSensitive="yes" regex="no">Operation not permitted</output>
<output type="failure" caseSensitive="yes" regex="no">FAILED: System.nanoTime() after CRIU restore:</output>
<output type="required" caseSensitive="yes" regex="no">Error</output>
</test>

<test id="Create CRIU checkpoint image and restore three times - testSystemNanoTimeJitPostCheckpointCompile">
<command>bash $SCRIPPATH$ $TEST_RESROOT$ $JAVA_COMMAND$ "$JVM_OPTIONS$" "$MAINCLASS_TIMECHANGE$ testSystemNanoTimeJitPostCheckpointCompile" 3 3 false</command>
<output type="success" caseSensitive="no" regex="no">Killed</output>
<output type="required" caseSensitive="yes" regex="no">System.nanoTime() before CRIU checkpoint:</output>
<output type="required" caseSensitive="yes" regex="no">PASSED: System.nanoTime() after CRIU restore:</output>
<output type="failure" caseSensitive="yes" regex="no">CRIU is not enabled</output>
<output type="failure" caseSensitive="yes" regex="no">Operation not permitted</output>
<output type="failure" caseSensitive="yes" regex="no">FAILED: System.nanoTime() after CRIU restore:</output>
<output type="required" caseSensitive="yes" regex="no">Error</output>
</test>

<test id="Create CRIU checkpoint image and restore three times - testMillisDelayBeforeCheckpointDone">
<command>bash $SCRIPPATH$ $TEST_RESROOT$ $JAVA_COMMAND$ "$JVM_OPTIONS$" "$MAINCLASS_TIMECHANGE$ testMillisDelayBeforeCheckpointDone" 3 3 false</command>
<output type="success" caseSensitive="no" regex="no">Killed</output>
Expand Down
1 change: 1 addition & 0 deletions test/functional/cmdLineTests/criu/playlist.xml
Expand Up @@ -90,6 +90,7 @@
<variation>-Xjit:count=0 -XX:+CRIURestoreNonPortableMode</variation>
</variations>
<command>
TR_Options=$(Q)exclude={org/openj9/criu/TimeChangeTest.nanoTimeInt()J},dontInline={org/openj9/criu/TimeChangeTest.nanoTimeInt()J|org/openj9/criu/TimeChangeTest.nanoTimeJit()J},{org/openj9/criu/TimeChangeTest.nanoTimeJit()J}(count=1)$(Q) \
$(JAVA_COMMAND) $(JVM_OPTIONS) -Xdump \
-DSCRIPPATH=$(TEST_RESROOT)$(D)criuScript.sh -DTEST_RESROOT=$(TEST_RESROOT) \
-DJAVA_COMMAND=$(JAVA_COMMAND) -DJVM_OPTIONS=$(Q)$(JVM_OPTIONS)$(Q) \
Expand Down
Expand Up @@ -48,6 +48,10 @@ public static void main(String args[]) throws InterruptedException {
TimeChangeTest tct = new TimeChangeTest();
if ("testSystemNanoTime".equalsIgnoreCase(testName)) {
tct.testSystemNanoTime();
} else if ("testSystemNanoTimeJitPreCheckpointCompile".equalsIgnoreCase(testName)) {
tct.testSystemNanoTimeJitPreCheckpointCompile();
} else if ("testSystemNanoTimeJitPostCheckpointCompile".equalsIgnoreCase(testName)) {
tct.testSystemNanoTimeJitPostCheckpointCompile();
} else {
tct.test(testName);
}
Expand Down Expand Up @@ -96,6 +100,58 @@ public void testSystemNanoTime() {
}
}

public void testSystemNanoTimeJitPreCheckpointCompile() {
testSystemNanoTimeJitTestPreCheckpointPhase();
testSystemNanoTimeJitWarmupPhase();
CRIUTestUtils.checkPointJVM(imagePath, false);
testSystemNanoTimeJitTestPostCheckpointPhase();
}

public void testSystemNanoTimeJitPostCheckpointCompile() {
testSystemNanoTimeJitTestPreCheckpointPhase();
CRIUTestUtils.checkPointJVM(imagePath, false);
testSystemNanoTimeJitWarmupPhase();
testSystemNanoTimeJitTestPostCheckpointPhase();
}

private static void testSystemNanoTimeJitWarmupPhase() {
for (int i = 0; i < 100000; i++) {
nanoTimeJit();
}
}

private void testSystemNanoTimeJitTestPreCheckpointPhase() {
final long beforeCheckpoint = nanoTimeInt();
System.out.println("System.nanoTime() before CRIU checkpoint: " + beforeCheckpoint);
}

private void testSystemNanoTimeJitTestPostCheckpointPhase() {
final long afterRestoreJit = nanoTimeJit();
final long afterRestoreInt = nanoTimeInt();
if (afterRestoreJit <= afterRestoreInt) {
System.out.println("PASSED: System.nanoTime() after CRIU restore: interpreter nanotime = " + afterRestoreInt + ", JIT nanotime = " + afterRestoreJit);
} else {
System.out.println("FAILED: System.nanoTime() after CRIU restore: interpreter nanotime = " + afterRestoreInt + ", JIT nanotime = " + afterRestoreJit);
}
}

/*
* In order to test the JIT handling of System.nanoTime() these two methods,
* nanoTimeInt() and nanoTimeJit(), need special treatment.
* 1) nanoTimeInt() must not be compiled or inlined to ensure that when called it
* will invoke the non-JIT implementation of System.nanoTime().
* 2) nanoTimeJit() must not be inlined so that it can be warmed up effectively.
* The above is currently accomplished via Xjit exclude= and dontInline= options
* when the test is executed.
*/
private static long nanoTimeInt() {
return System.nanoTime();
}

private static long nanoTimeJit() {
return System.nanoTime();
}

private final TimerTask taskMillisDelayBeforeCheckpointDone = new TimerTask() {
public void run() {
final long endNanoTime = System.nanoTime();
Expand Down

0 comments on commit 8c885cb

Please sign in to comment.