25
25
26
26
/*
27
27
* ===========================================================================
28
- * (c) Copyright IBM Corp. 2022, 2023 All Rights Reserved
28
+ * (c) Copyright IBM Corp. 2022, 2024 All Rights Reserved
29
29
* ===========================================================================
30
30
*/
31
31
61
61
62
62
#include "java.h"
63
63
#include "jni.h"
64
+ #include "j9cfg.h"
65
+ #if defined(J9VM_OPT_CRAC_SUPPORT )
66
+ #include <ctype.h>
67
+ #include <sys/wait.h>
68
+ #endif /* defined(J9VM_OPT_CRAC_SUPPORT) */
64
69
65
70
/*
66
71
* A NOTE TO DEVELOPERS: For performance reasons it is important that
@@ -543,6 +548,315 @@ invokeInstanceMainWithoutArgs(JNIEnv *env, jclass mainClass) {
543
548
return 1 ; // method was invoked
544
549
}
545
550
551
+ #if defined(J9VM_OPT_CRAC_SUPPORT )
552
+ /**
553
+ * Get the value of a command line option in the array of command line arguments.
554
+ * @param[in] optionName The name of the command line option to search for
555
+ * @param[in] argc The number of command line arguments
556
+ * @param[in] argv The array of command line arguments
557
+ * @param[out] error A pointer to an integer for the error code
558
+ * @return A pointer to the value of the command line option if found, NULL otherwise
559
+ */
560
+ static const char *
561
+ getCommandLineOptionValue (const char * optionName , int argc , char * * argv , int * error )
562
+ {
563
+ const char * value = NULL ;
564
+ int i = 0 ;
565
+ size_t optionNameLength = strlen (optionName );
566
+ jboolean optionNameFound = JNI_FALSE ;
567
+ * error = 0 ;
568
+ for (i = 0 ; i < argc ; i ++ ) {
569
+ const char * arg = argv [i ];
570
+ if (0 == strncmp (arg , optionName , optionNameLength )) {
571
+ const char * equals = arg + optionNameLength ;
572
+ if (('=' == * equals ) || ('\0' == * equals )) {
573
+ optionNameFound = JNI_TRUE ;
574
+ value = equals ;
575
+ if ('=' == * value ) {
576
+ value += 1 ;
577
+ }
578
+ if ('\0' == * value ) {
579
+ value = NULL ;
580
+ }
581
+ break ;
582
+ }
583
+ }
584
+ }
585
+ if (!optionNameFound ) {
586
+ * error = -1 ;
587
+ }
588
+ return value ;
589
+ }
590
+
591
+ /**
592
+ * Get the checkpoint directory from command line options.
593
+ * @param[in] argc The number of command line arguments
594
+ * @param[in] argv The array of command line arguments
595
+ * @param[in/out] error A pointer to an integer for the error code
596
+ * @return A pointer to the checkpoint directory if found, NULL otherwise
597
+ */
598
+ static const char *
599
+ getCheckpointDirectory (int argc , char * * argv , int * error )
600
+ {
601
+ const char * checkpointDirectory = NULL ;
602
+ const char * checkpointDirectoryPropertyValue = getCommandLineOptionValue ("-XX:CRaCRestoreFrom" , argc , argv , error );
603
+ if (0 == * error ) {
604
+ if (NULL != checkpointDirectoryPropertyValue ) {
605
+ checkpointDirectory = checkpointDirectoryPropertyValue ;
606
+ } else {
607
+ JLI_ReportErrorMessage ("The value of the command line option -XX:CRaCRestoreFrom was not found." );
608
+ * error = -2 ;
609
+ }
610
+ }
611
+ return checkpointDirectory ;
612
+ }
613
+
614
+ /**
615
+ * Get the log level specified in the command line arguments.
616
+ * @param[in] argc The number of command line arguments
617
+ * @param[in] argv The array of command line arguments
618
+ * @param[in/out] error A pointer to an integer for the error code
619
+ * @return The log level integer if successful, default of 2 otherwise
620
+ */
621
+ int
622
+ getLogLevel (int argc , char * * argv , int * error )
623
+ {
624
+ int logLevelValue = 2 ; /* default */
625
+ const char * logLevelPropertyValue = NULL ;
626
+ logLevelPropertyValue = getCommandLineOptionValue ("-Dopenj9.internal.criu.logLevel" , argc , argv , error );
627
+ if (0 == * error ) {
628
+ const char * c = logLevelPropertyValue ;
629
+ if (NULL == c ) {
630
+ goto done ;
631
+ }
632
+ for (; '\0' != * c ; c ++ ) {
633
+ if (!isdigit (* c )) {
634
+ goto setLogLevelOptionValueNotValidError ;
635
+ }
636
+ }
637
+ logLevelValue = atoi (logLevelPropertyValue );
638
+ if ((0 <= logLevelValue ) && (logLevelValue <= 4 )) {
639
+ goto done ;
640
+ } else {
641
+ goto setLogLevelOptionValueNotValidError ;
642
+ }
643
+ } else if (-1 == * error ) {
644
+ goto done ;
645
+ }
646
+ setLogLevelOptionValueNotValidError :
647
+ JLI_ReportErrorMessage ("The value of the command line option is not valid, option=-Dopenj9.internal.criu.logLevel, value=%s." , logLevelPropertyValue );
648
+ * error = -2 ;
649
+ done :
650
+ return logLevelValue ;
651
+ }
652
+
653
+ /**
654
+ * Check if the unprivileged mode is specified in the command line arguments.
655
+ * @param[in] argc The number of command line arguments
656
+ * @param[in] argv The array of command line arguments
657
+ * @param[in/out] error A pointer to an integer for the error code
658
+ * @return true if the unprivileged mode option is specified in the command line arguments, false otherwise
659
+ */
660
+ static jboolean
661
+ isUnprivilegedModeOn (int argc , char * * argv , int * error )
662
+ {
663
+ jboolean isUnprivilegedModeOn = JNI_FALSE ;
664
+ const char * unprivilegedModePropertyValue = getCommandLineOptionValue ("-Dopenj9.internal.criu.unprivilegedMode" , argc , argv , error );
665
+ if (0 == * error ) {
666
+ if (NULL == unprivilegedModePropertyValue ) {
667
+ isUnprivilegedModeOn = JNI_TRUE ;
668
+ } else {
669
+ JLI_ReportErrorMessage ("The value of the command line option is not expected, option=-Dopenj9.internal.criu.unprivilegedMode, value=%s." , unprivilegedModePropertyValue );
670
+ * error = -2 ;
671
+ }
672
+ }
673
+ return isUnprivilegedModeOn ;
674
+ }
675
+
676
+ /**
677
+ * Get the log file specified in the command line arguments.
678
+ * @param[in] argc The number of command line arguments
679
+ * @param[in] argv The array of command line arguments
680
+ * @param[in/out] error A pointer to an integer for the error code
681
+ * @return A pointer to the log file string if successful, NULL otherwise
682
+ */
683
+ static const char *
684
+ getLogFile (int argc , char * * argv , int * error )
685
+ {
686
+ const char * logFile = NULL ;
687
+ const char * logFilePropertyValue = getCommandLineOptionValue ("-Dopenj9.internal.criu.logFile" , argc , argv , error );
688
+ if (0 == * error ) {
689
+ if (NULL != logFilePropertyValue ) {
690
+ logFile = logFilePropertyValue ;
691
+ } else {
692
+ JLI_ReportErrorMessage ("The value of the command line option -Dopenj9.internal.criu.logFile was not found." );
693
+ * error = -2 ;
694
+ }
695
+ }
696
+ return logFile ;
697
+ }
698
+
699
+ /**
700
+ * Restore the system state from a checkpoint using the CRIU tool.
701
+ * @param[in] checkpointDirectory The directory containing the checkpoint data
702
+ * @param[in] logLevel The log level for CRIU logging
703
+ * @param[in] unprivilegedModeOn Indicates whether the unprivileged mode option is on
704
+ * @param[in] logFile The log file option for CRIU
705
+ * @return 0 if the execution of the 'criu restore' command succeeds, -1 otherwise
706
+ */
707
+ static int
708
+ restoreFromCheckpoint (const char * checkpointDirectory , int logLevel , jboolean unprivilegedModeOn , const char * logFile )
709
+ {
710
+ int restoreStatus = 0 ;
711
+ int length = -1 ;
712
+ char * logLevelOption = NULL ;
713
+ char * logFileOption = NULL ;
714
+ int argc = 0 ;
715
+ const char * argv [9 ] = { NULL };
716
+ argv [argc ++ ] = "criu" ;
717
+ argv [argc ++ ] = "restore" ;
718
+ argv [argc ++ ] = "-D" ;
719
+ argv [argc ++ ] = checkpointDirectory ;
720
+ length = snprintf (NULL , 0 , "-v%d" , logLevel );
721
+ if (length < 0 ) {
722
+ char logLevelString [2 ] = { 0 };
723
+ sprintf (logLevelString , "%d" , logLevel );
724
+ JLI_ReportErrorMessage ("Failed to calculate the length of the command option, value=%s, format=%s." , logLevelString , "-v%d" );
725
+ restoreStatus = -1 ;
726
+ goto done ;
727
+ }
728
+ logLevelOption = (char * )JLI_MemAlloc (length + 1 );
729
+ if (NULL == logLevelOption ) {
730
+ char logLevelString [2 ] = { 0 };
731
+ sprintf (logLevelString , "%d" , logLevel );
732
+ JLI_ReportErrorMessage ("Failed to allocate memory for the command option, value=%s, format=%s." , logLevelString , "-v%d" );
733
+ restoreStatus = -1 ;
734
+ goto done ;
735
+ }
736
+ if (snprintf (logLevelOption , length + 1 , "-v%d" , logLevel ) < 0 ) {
737
+ char logLevelString [2 ] = { 0 };
738
+ sprintf (logLevelString , "%d" , logLevel );
739
+ JLI_ReportErrorMessage ("Failed to allocate memory for the command option, value=%s, format=%s." , logLevelString , "-v%d" );
740
+ restoreStatus = -1 ;
741
+ goto freeLogLevelOption ;
742
+ }
743
+ argv [argc ++ ] = logLevelOption ;
744
+ argv [argc ++ ] = "--shell-job" ;
745
+ if (unprivilegedModeOn ) {
746
+ argv [argc ++ ] = "--unprivileged" ;
747
+ }
748
+ if (NULL != logFile ) {
749
+ length = snprintf (NULL , 0 , "--log-file=%s" , logFile );
750
+ if (length < 0 ) {
751
+ JLI_ReportErrorMessage ("Failed to calculate the length of the command option, value=%s, format=%s." , logFile , "--log-file=%s" );
752
+ restoreStatus = -1 ;
753
+ goto freeLogLevelOption ;
754
+ }
755
+ logFileOption = (char * )JLI_MemAlloc (length + 1 );
756
+ if (NULL == logFileOption ) {
757
+ JLI_ReportErrorMessage ("Failed to allocate memory for the command option, value=%s, format=%s." , logFile , "--log-file=%s" );
758
+ restoreStatus = -1 ;
759
+ goto freeLogLevelOption ;
760
+ }
761
+ if (snprintf (logFileOption , length + 1 , "--log-file=%s" , logFile ) < 0 ) {
762
+ JLI_ReportErrorMessage ("Failed to allocate memory for the command option, value=%s, format=%s." , logFile , "--log-file=%s" );
763
+ restoreStatus = -1 ;
764
+ goto freeLogFileOption ;
765
+ }
766
+ argv [argc ++ ] = logFileOption ;
767
+ }
768
+ argv [argc ] = NULL ;
769
+ execvp (argv [0 ], (char * const * )argv );
770
+ /* If execvp returns, there was an error. */
771
+ restoreStatus = -1 ;
772
+ freeLogFileOption :
773
+ if (logFileOption != NULL ) {
774
+ JLI_MemFree ((void * )logFileOption );
775
+ logFileOption = NULL ;
776
+ }
777
+ freeLogLevelOption :
778
+ if (logLevelOption != NULL ) {
779
+ JLI_MemFree ((void * )logLevelOption );
780
+ logLevelOption = NULL ;
781
+ }
782
+ done :
783
+ return restoreStatus ;
784
+ }
785
+
786
+ /**
787
+ * Handle the restoration of the system state from a checkpoint.
788
+ * @param[in] argc The number of command line arguments
789
+ * @param[in] argv The array of command line arguments
790
+ */
791
+ static void
792
+ handleCRaCRestore (int argc , char * * argv )
793
+ {
794
+ const char * checkpointDirectory = NULL ;
795
+ int error = 0 ;
796
+ int parentProcessExitStatus = EXIT_SUCCESS ;
797
+ pid_t childProcessPid = 0 ;
798
+ int logLevel = 0 ;
799
+ int childProcessExitStatus = EXIT_SUCCESS ;
800
+ jboolean unprivilegedModeOn = JNI_FALSE ;
801
+ const char * logFile = NULL ;
802
+ int childProcessPidStatus = 0 ;
803
+ int childProcessPidExitStatus = 0 ;
804
+ checkpointDirectory = getCheckpointDirectory (argc , argv , & error );
805
+ if (-1 == error ) {
806
+ return ;
807
+ }
808
+ if (-2 == error ) {
809
+ JLI_ReportErrorMessage ("Failed to get the CRIU checkpoint directory, error=%d." , error );
810
+ parentProcessExitStatus = EXIT_FAILURE ;
811
+ goto doneParentProcess ;
812
+ }
813
+ /*
814
+ * The if block will be invoked by the child process,
815
+ * and the else block will be invoked by the parent process.
816
+ */
817
+ childProcessPid = fork ();
818
+ if (0 == childProcessPid ) {
819
+ logLevel = getLogLevel (argc , argv , & error );
820
+ if (-2 == error ) {
821
+ JLI_ReportErrorMessage ("Failed to get the CRIU log level, error=%d." , error );
822
+ childProcessExitStatus = EXIT_FAILURE ;
823
+ goto doneChildProcess ;
824
+ }
825
+ unprivilegedModeOn = isUnprivilegedModeOn (argc , argv , & error );
826
+ if (-2 == error ) {
827
+ JLI_ReportErrorMessage ("Failed to get the CRIU unprivileged mode, error=%d." , error );
828
+ childProcessExitStatus = EXIT_FAILURE ;
829
+ goto doneChildProcess ;
830
+ }
831
+ logFile = getLogFile (argc , argv , & error );
832
+ if (-2 == error ) {
833
+ JLI_ReportErrorMessage ("Failed to get the CRIU log file, error=%d." , error );
834
+ childProcessExitStatus = EXIT_FAILURE ;
835
+ goto doneChildProcess ;
836
+ }
837
+ childProcessExitStatus = restoreFromCheckpoint (checkpointDirectory , logLevel , unprivilegedModeOn , logFile );
838
+ doneChildProcess :
839
+ exit (childProcessExitStatus );
840
+ } else {
841
+ waitpid (childProcessPid , & childProcessPidStatus , 0 );
842
+ if (WIFEXITED (childProcessPidStatus )) {
843
+ childProcessPidExitStatus = WEXITSTATUS (childProcessPidStatus );
844
+ if (EXIT_SUCCESS == childProcessPidExitStatus ) {
845
+ JLI_ReportMessage ("Completed restore with -XX:CRaCRestoreFrom=PATH." );
846
+ } else {
847
+ JLI_ReportErrorMessage ("Failed to restore from checkpoint, error=%d." , childProcessPidExitStatus );
848
+ parentProcessExitStatus = EXIT_FAILURE ;
849
+ }
850
+ } else {
851
+ JLI_ReportErrorMessage ("The CRIU restore child process failed." );
852
+ parentProcessExitStatus = EXIT_FAILURE ;
853
+ }
854
+ }
855
+ doneParentProcess :
856
+ exit (parentProcessExitStatus );
857
+ }
858
+ #endif /* defined(J9VM_OPT_CRAC_SUPPORT) */
859
+
546
860
int
547
861
JavaMain (void * _args )
548
862
{
@@ -568,6 +882,10 @@ JavaMain(void* _args)
568
882
569
883
RegisterThread ();
570
884
885
+ #if defined(J9VM_OPT_CRAC_SUPPORT )
886
+ handleCRaCRestore (argc , argv );
887
+ #endif /* defined(J9VM_OPT_CRAC_SUPPORT) */
888
+
571
889
/* Initialize the virtual machine */
572
890
start = CurrentTimeMicros ();
573
891
if (!InitializeJVM (& vm , & env , & ifn )) {
0 commit comments