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
72
77
73
78
#define USE_STDERR JNI_TRUE /* we usually print to stderr */
74
79
#define USE_STDOUT JNI_FALSE
80
+ #if defined(J9VM_OPT_CRAC_SUPPORT )
81
+ #define EQUALS_SIGN '='
82
+ #define NULL_TERMINATOR '\0'
83
+ #define OPTION_VALUE_MEMORY_ALLOCATION_ERROR_MESSAGE "Failed to allocate memory for the value of the command line option %s."
84
+ #define OPTION_VALUE_NOT_FOUND_ERROR_MESSAGE "The value of the command line option %s was not found."
85
+ #define LOG_LEVEL_LENGTH_CALCULATATION_ERROR_MESSAGE "Failed to calculate the length of the CRIU log level string."
86
+ #define LOG_LEVEL_MEMORY_ALLOCATION_ERROR_MESSAGE "Failed to allocate memory for the CRIU log level."
87
+ #define OPTION_VALUE_NOT_VALID_ERROR_MESSAGE "The value of the command line option is not valid, option=%s, value=%s."
88
+ #define OPTION_VALUE_NOT_EXPECTED_ERROR_MESSAGE "The value of the command line option is not expected, option=%s, value=%s."
89
+ #define LOG_FILE_LENGTH_CALCULATION_ERROR_MESSAGE "Failed to calculate the length of the CRIU log file string."
90
+ #define LOG_FILE_MEMORY_ALLOCATION_ERROR_MESSAGE "Failed to allocate memory for the CRIU log file option."
91
+ #define CHECKPOINT_DIRECTORY_ERROR_MESSAGE "Failed to get the CRIU checkpoint directory, error=%d."
92
+ #define LOG_LEVEL_ERROR_MESSAGE "Failed to get the CRIU log level, error=%d."
93
+ #define UNPRIVILEGED_MODE_ERROR_MESSAGE "Failed to get the CRIU unprivileged mode, error=%d."
94
+ #define LOG_FILE_ERROR_MESSAGE "Failed to get the CRIU log file, error=%d."
95
+ #define RESTORE_FROM_CHECKPOINT_ERROR_MESSAGE "Failed to restore from checkpoint."
96
+ #define RESTORE_CHILD_PROCESS_FAILED_ERROR_MESSAGE "The CRIU restore child process failed."
97
+ #define MEMORY_ALLOCATION_ERROR 1
98
+ #define OPTION_NAME_NOT_FOUND_ERROR 2
99
+ #define OPTION_VALUE_NOT_FOUND_ERROR 3
100
+ #define OPTION_VALUE_NOT_VALID_ERROR 4
101
+ #define OPTION_VALUE_NOT_EXPECTED_ERROR 5
102
+ #define CRAC_RESTORE_FROM_OPTION_NAME "-XX:CRaCRestoreFrom"
103
+ #define LOG_LEVEL_OPTION_NAME "-Dopenj9.internal.criu.logLevel"
104
+ #define LOG_LEVEL_OPTION_FORMAT "-v%d"
105
+ #define UNPRIVILEGED_MODE_OPTION_NAME "-Dopenj9.internal.criu.unprivilegedMode"
106
+ #define LOG_FILE_OPTION_NAME "-Dopenj9.internal.criu.logFile"
107
+ #define LOG_FILE_OPTION_FORMAT "--log-file=%s"
108
+ #define CRIU_RESTORE_MIN_ARGS 6
109
+ #define CRIU_RESTORE_MAX_ARGS 9
110
+ #define CRIU_COMMAND "criu"
111
+ #define CRIU_RESTORE_OPTION "restore"
112
+ #define CRIU_CHECKPOINT_DIRECTORY_OPTION "-D"
113
+ #define CRIU_DEFAULT_LOG_LEVEL_OPTION "-v2"
114
+ #define CRIU_UNPRIVILEGED_MODE_OPTION "--unprivileged"
115
+ #define CRIU_SHELL_JOB_OPTION "--shell-job"
116
+ #define EXECVP_ERROR -1
117
+ #define EXECVP_SUCCESS 0
118
+ #define WAIT_INDEFINITELY 0
119
+ #endif /* defined(J9VM_OPT_CRAC_SUPPORT) */
75
120
76
121
static jboolean printVersion = JNI_FALSE ; /* print and exit */
77
122
static jboolean showVersion = JNI_FALSE ; /* print but continue */
@@ -534,6 +579,316 @@ invokeInstanceMainWithoutArgs(JNIEnv *env, jclass mainClass) {
534
579
return 1 ;
535
580
}
536
581
582
+ #if defined(J9VM_OPT_CRAC_SUPPORT )
583
+ /**
584
+ * Get the value of a command line option in the array of command line arguments.
585
+ * @param[in] optionName The name of the command line option to search for
586
+ * @param[in] argc The number of command line arguments
587
+ * @param[in] argv The array of command line arguments
588
+ * @param[out] error A pointer to an integer for the error code
589
+ * @return A pointer to the value of the command line option if found, NULL otherwise
590
+ */
591
+ static char * getCommandLineOptionValue (const char * optionName , int argc , char * * argv , int * error ) {
592
+ char * arg = NULL ;
593
+ unsigned int optionNameLength = strlen (optionName );
594
+ char * equals = NULL ;
595
+ jboolean optionNameFound = JNI_FALSE ;
596
+ char * value = NULL ;
597
+ * error = 0 ;
598
+ for (int i = 0 ; i < argc ; i ++ ) {
599
+ arg = argv [i ];
600
+ if (0 == strncmp (arg , optionName , optionNameLength )) {
601
+ equals = arg + optionNameLength ;
602
+ if ((EQUALS_SIGN == * equals ) || (NULL_TERMINATOR == * equals )) {
603
+ optionNameFound = JNI_TRUE ;
604
+ if (EQUALS_SIGN == * equals ) {
605
+ equals ++ ;
606
+ }
607
+ if (NULL_TERMINATOR != * equals ) {
608
+ value = strdup (equals );
609
+ if (NULL == value ) {
610
+ JLI_ReportErrorMessage (OPTION_VALUE_MEMORY_ALLOCATION_ERROR_MESSAGE , optionName );
611
+ * error = MEMORY_ALLOCATION_ERROR ;
612
+ }
613
+ }
614
+ break ;
615
+ }
616
+ }
617
+ }
618
+ if (!optionNameFound ) {
619
+ * error = OPTION_NAME_NOT_FOUND_ERROR ;
620
+ }
621
+ return value ;
622
+ }
623
+
624
+ /**
625
+ * Free the memory allocated for a command line option value.
626
+ * @param[in/out] value A pointer to a pointer to the command line option value
627
+ */
628
+ static void freeCommandLineOptionValue (char * * value ) {
629
+ if ((NULL != value ) && (NULL != * value )) {
630
+ JLI_MemFree (* value );
631
+ * value = NULL ;
632
+ }
633
+ }
634
+
635
+ /**
636
+ * Check if an error occurred while retrieving a command line option value.
637
+ * @param[in] error The error code of the result of obtaining the command line option value
638
+ * @return true if an error occurred, false otherwise
639
+ */
640
+ static jboolean hasGetCommandLineOptionValueFoundError (int error ) {
641
+ return 0 != error ;
642
+ }
643
+
644
+ /**
645
+ * Get the checkpoint directory from command line options.
646
+ * @param[in] argc The number of command line arguments
647
+ * @param[in] argv The array of command line arguments
648
+ * @param[in/out] error A pointer to an integer for the error code
649
+ * @return A pointer to the checkpoint directory if found, NULL otherwise
650
+ */
651
+ static char * getCheckpointDirectory (int argc , char * * argv , int * error ) {
652
+ char * checkpointDirectory = NULL ;
653
+ char * checkpointDirectoryPropertyValue = getCommandLineOptionValue (CRAC_RESTORE_FROM_OPTION_NAME , argc , argv , error );
654
+ if (!hasGetCommandLineOptionValueFoundError (* error )) {
655
+ if (NULL != checkpointDirectoryPropertyValue ) {
656
+ checkpointDirectory = checkpointDirectoryPropertyValue ;
657
+ } else {
658
+ JLI_ReportErrorMessage (OPTION_VALUE_NOT_FOUND_ERROR_MESSAGE , CRAC_RESTORE_FROM_OPTION_NAME );
659
+ * error = OPTION_VALUE_NOT_FOUND_ERROR ;
660
+ }
661
+ }
662
+ return checkpointDirectory ;
663
+ }
664
+
665
+ /**
666
+ * Check if a command line option is found with an error condition.
667
+ * @param[in] error The error code of the result of obtaining the command line option
668
+ * @return true if the command line option is found and an error occurred, false otherwise
669
+ */
670
+ static jboolean isCommandLineOptionFoundWithError (int error ) {
671
+ return (OPTION_NAME_NOT_FOUND_ERROR != error ) && hasGetCommandLineOptionValueFoundError (error );
672
+ }
673
+
674
+ /**
675
+ * Get the log level specified in the command line arguments.
676
+ * @param[in] argc The number of command line arguments
677
+ * @param[in] argv The array of command line arguments
678
+ * @param[in/out] error A pointer to an integer for the error code
679
+ * @return A pointer to the log level string if successful, NULL otherwise
680
+ */
681
+ static char * getLogLevel (int argc , char * * argv , int * error ) {
682
+ char * logLevel = NULL ;
683
+ char * logLevelPropertyValue = NULL ;
684
+ int logLevelValue = 0 ;
685
+ int logLevelLength = 0 ;
686
+ logLevelPropertyValue = getCommandLineOptionValue (LOG_LEVEL_OPTION_NAME , argc , argv , error );
687
+ if (!isCommandLineOptionFoundWithError (* error )) {
688
+ if (NULL != logLevelPropertyValue ) {
689
+ for (const char * c = logLevelPropertyValue ; NULL_TERMINATOR != * c ; c ++ ) {
690
+ if (!isdigit (* c )) {
691
+ goto setLogLevelOptionValueNotValidError ;
692
+ }
693
+ }
694
+ logLevelValue = atoi (logLevelPropertyValue );
695
+ if ((logLevelValue >= 0 ) && (logLevelValue <= 4 )) {
696
+ logLevelLength = snprintf (NULL , 0 , LOG_LEVEL_OPTION_FORMAT , logLevelValue );
697
+ if (logLevelLength < 0 ) {
698
+ JLI_ReportErrorMessage (LOG_LEVEL_LENGTH_CALCULATATION_ERROR_MESSAGE );
699
+ * error = MEMORY_ALLOCATION_ERROR ;
700
+ goto freeLogLevelPropertyValue ;
701
+ }
702
+ logLevel = (char * )JLI_MemAlloc (logLevelLength + 1 );
703
+ if (NULL == logLevel ) {
704
+ JLI_ReportErrorMessage (LOG_LEVEL_MEMORY_ALLOCATION_ERROR_MESSAGE );
705
+ * error = MEMORY_ALLOCATION_ERROR ;
706
+ goto freeLogLevelPropertyValue ;
707
+ }
708
+ snprintf (logLevel , logLevelLength + 1 , LOG_LEVEL_OPTION_FORMAT , logLevelValue );
709
+ goto freeLogLevelPropertyValue ;
710
+ } else {
711
+ goto setLogLevelOptionValueNotValidError ;
712
+ }
713
+ } else {
714
+ goto returnGetLogLevel ;
715
+ }
716
+ }
717
+ setLogLevelOptionValueNotValidError :
718
+ JLI_ReportErrorMessage (OPTION_VALUE_NOT_VALID_ERROR_MESSAGE , LOG_LEVEL_OPTION_NAME , logLevelPropertyValue );
719
+ * error = OPTION_VALUE_NOT_VALID_ERROR ;
720
+ freeLogLevelPropertyValue :
721
+ freeCommandLineOptionValue (& logLevelPropertyValue );
722
+ returnGetLogLevel :
723
+ return logLevel ;
724
+ }
725
+
726
+ /**
727
+ * Check if the unprivileged mode option is specified in the command line arguments.
728
+ * @param[in] argc The number of command line arguments
729
+ * @param[in] argv The array of command line arguments
730
+ * @param[in/out] error A pointer to an integer for the error code
731
+ * @return true if the unprivileged mode option is specified in the command line arguments, false otherwise
732
+ */
733
+ static jboolean isUnprivilegedModeOn (int argc , char * * argv , int * error ) {
734
+ jboolean isUnprivilegedModeOn = JNI_FALSE ;
735
+ char * unprivilegedModePropertyValue = NULL ;
736
+ unprivilegedModePropertyValue = getCommandLineOptionValue (UNPRIVILEGED_MODE_OPTION_NAME , argc , argv , error );
737
+ if (!hasGetCommandLineOptionValueFoundError (* error )) {
738
+ if (NULL == unprivilegedModePropertyValue ) {
739
+ isUnprivilegedModeOn = JNI_TRUE ;
740
+ } else {
741
+ JLI_ReportErrorMessage (OPTION_VALUE_NOT_EXPECTED_ERROR_MESSAGE , UNPRIVILEGED_MODE_OPTION_NAME , unprivilegedModePropertyValue );
742
+ * error = OPTION_VALUE_NOT_EXPECTED_ERROR ;
743
+ freeCommandLineOptionValue (& unprivilegedModePropertyValue );
744
+ }
745
+ }
746
+ return isUnprivilegedModeOn ;
747
+ }
748
+
749
+ /**
750
+ * Get the log file option specified in the command line arguments.
751
+ * @param[in] argc The number of command line arguments
752
+ * @param[in] argv The array of command line arguments
753
+ * @param[in/out] error A pointer to an integer for the error code
754
+ * @return A pointer to the log file string if successful, NULL otherwise
755
+ */
756
+ static char * getLogFile (int argc , char * * argv , int * error ) {
757
+ char * logFile = NULL ;
758
+ char * logFilePropertyValue = NULL ;
759
+ int logFileLength = 0 ;
760
+ logFilePropertyValue = getCommandLineOptionValue (LOG_FILE_OPTION_NAME , argc , argv , error );
761
+ if (!hasGetCommandLineOptionValueFoundError (* error )) {
762
+ if (NULL != logFilePropertyValue ) {
763
+ logFileLength = snprintf (NULL , 0 , LOG_FILE_OPTION_FORMAT , logFilePropertyValue ) + 1 ;
764
+ if (logFileLength < 0 ) {
765
+ JLI_ReportErrorMessage (LOG_FILE_LENGTH_CALCULATION_ERROR_MESSAGE );
766
+ * error = MEMORY_ALLOCATION_ERROR ;
767
+ goto freeLogFilePropertyValue ;
768
+ }
769
+ logFile = (char * )JLI_MemAlloc (logFileLength );
770
+ if (NULL == logFile ) {
771
+ JLI_ReportErrorMessage (LOG_FILE_MEMORY_ALLOCATION_ERROR_MESSAGE );
772
+ * error = MEMORY_ALLOCATION_ERROR ;
773
+ } else {
774
+ snprintf (logFile , logFileLength , LOG_FILE_OPTION_FORMAT , logFilePropertyValue );
775
+ }
776
+ goto freeLogFilePropertyValue ;
777
+ } else {
778
+ JLI_ReportErrorMessage (OPTION_VALUE_NOT_FOUND_ERROR_MESSAGE , LOG_FILE_OPTION_NAME );
779
+ * error = OPTION_VALUE_NOT_FOUND_ERROR ;
780
+ goto returnGetLogFile ;
781
+ }
782
+ }
783
+ freeLogFilePropertyValue :
784
+ freeCommandLineOptionValue (& logFilePropertyValue );
785
+ returnGetLogFile :
786
+ return logFile ;
787
+ }
788
+
789
+ /**
790
+ * Restore the system state from a checkpoint using the CRIU tool.
791
+ * @param[in] checkpointDirectory The directory containing the checkpoint data
792
+ * @param[in] logLevel The log level for CRIU logging
793
+ * @param[in] unprivilegedModeOn Indicates whether the unprivileged mode option is on
794
+ * @param[in] logFile The log file option for CRIU
795
+ * @return EXECVP_SUCCESS if the execution of the 'criu restore' command succeeds, EXECVP_ERROR otherwise
796
+ */
797
+ static int restoreFromCheckpoint (char * checkpointDirectory , char * logLevel , jboolean unprivilegedModeOn , char * logFile ) {
798
+ int argc = CRIU_RESTORE_MIN_ARGS ;
799
+ char * argv [CRIU_RESTORE_MAX_ARGS ] = { NULL };
800
+ argv [0 ] = CRIU_COMMAND ;
801
+ argv [1 ] = CRIU_RESTORE_OPTION ;
802
+ argv [2 ] = CRIU_CHECKPOINT_DIRECTORY_OPTION ;
803
+ argv [3 ] = checkpointDirectory ;
804
+ argv [4 ] = (NULL == logLevel ) ? CRIU_DEFAULT_LOG_LEVEL_OPTION : logLevel ;
805
+ argv [5 ] = CRIU_SHELL_JOB_OPTION ;
806
+ if (unprivilegedModeOn ) {
807
+ argv [argc ++ ] = CRIU_UNPRIVILEGED_MODE_OPTION ;
808
+ }
809
+ if (NULL != logFile ) {
810
+ argv [argc ++ ] = logFile ;
811
+ }
812
+ argv [argc ] = NULL ;
813
+ if (EXECVP_ERROR == execvp (argv [0 ], argv )) {
814
+ return EXECVP_ERROR ;
815
+ }
816
+ return EXECVP_SUCCESS ;
817
+ }
818
+
819
+ /**
820
+ * Handle the restoration of the system state from a checkpoint.
821
+ * @param[in] argc The number of command line arguments
822
+ * @param[in] argv The array of command line arguments
823
+ */
824
+ static void handleCRaCRestore (int argc , char * * argv ) {
825
+ char * checkpointDirectory = NULL ;
826
+ int error = 0 ;
827
+ int parentProcessExitStatus = EXIT_SUCCESS ;
828
+ pid_t childProcessPid = 0 ;
829
+ char * logLevel = NULL ;
830
+ int childProcessExitStatus = EXIT_SUCCESS ;
831
+ jboolean unprivilegedModeOn = JNI_FALSE ;
832
+ char * logFile = NULL ;
833
+ int childProcessPidStatus = 0 ;
834
+ checkpointDirectory = getCheckpointDirectory (argc , argv , & error );
835
+ if (OPTION_NAME_NOT_FOUND_ERROR == error ) {
836
+ return ;
837
+ }
838
+ if (hasGetCommandLineOptionValueFoundError (error )) {
839
+ JLI_ReportErrorMessage (CHECKPOINT_DIRECTORY_ERROR_MESSAGE , error );
840
+ parentProcessExitStatus = EXIT_FAILURE ;
841
+ goto exitParentProcessHandleCRaCRestore ;
842
+ }
843
+ /*
844
+ * The if block will be invoked by the child process,
845
+ * and the else block will be invoked by the parent process.
846
+ */
847
+ childProcessPid = fork ();
848
+ if (0 == childProcessPid ) {
849
+ logLevel = getLogLevel (argc , argv , & error );
850
+ if (isCommandLineOptionFoundWithError (error )) {
851
+ JLI_ReportErrorMessage (LOG_LEVEL_ERROR_MESSAGE , error );
852
+ childProcessExitStatus = EXIT_FAILURE ;
853
+ goto exitChildProcessHandleCRaCRestore ;
854
+ }
855
+ unprivilegedModeOn = isUnprivilegedModeOn (argc , argv , & error );
856
+ if (isCommandLineOptionFoundWithError (error )) {
857
+ JLI_ReportErrorMessage (UNPRIVILEGED_MODE_ERROR_MESSAGE , error );
858
+ childProcessExitStatus = EXIT_FAILURE ;
859
+ goto freeLogLevel ;
860
+ }
861
+ logFile = getLogFile (argc , argv , & error );
862
+ if (isCommandLineOptionFoundWithError (error )) {
863
+ JLI_ReportErrorMessage (LOG_FILE_ERROR_MESSAGE , error );
864
+ childProcessExitStatus = EXIT_FAILURE ;
865
+ goto freeLogLevel ;
866
+ }
867
+ childProcessExitStatus = restoreFromCheckpoint (checkpointDirectory , logLevel , unprivilegedModeOn , logFile );
868
+ freeLogFile :
869
+ freeCommandLineOptionValue (& logFile );
870
+ freeLogLevel :
871
+ freeCommandLineOptionValue (& logLevel );
872
+ exitChildProcessHandleCRaCRestore :
873
+ exit (childProcessExitStatus );
874
+ } else {
875
+ waitpid (childProcessPid , & childProcessPidStatus , WAIT_INDEFINITELY );
876
+ if (WIFEXITED (childProcessPidStatus )) {
877
+ if (EXIT_SUCCESS != WEXITSTATUS (childProcessPidStatus )) {
878
+ JLI_ReportErrorMessage (RESTORE_FROM_CHECKPOINT_ERROR_MESSAGE );
879
+ parentProcessExitStatus = EXIT_FAILURE ;
880
+ }
881
+ } else {
882
+ JLI_ReportErrorMessage (RESTORE_CHILD_PROCESS_FAILED_ERROR_MESSAGE );
883
+ parentProcessExitStatus = EXIT_FAILURE ;
884
+ }
885
+ freeCommandLineOptionValue (& checkpointDirectory );
886
+ }
887
+ exitParentProcessHandleCRaCRestore :
888
+ exit (parentProcessExitStatus );
889
+ }
890
+ #endif /* defined(J9VM_OPT_CRAC_SUPPORT) */
891
+
537
892
int
538
893
JavaMain (void * _args )
539
894
{
@@ -554,6 +909,10 @@ JavaMain(void* _args)
554
909
555
910
RegisterThread ();
556
911
912
+ #if defined(J9VM_OPT_CRAC_SUPPORT )
913
+ handleCRaCRestore (argc , argv );
914
+ #endif /* defined(J9VM_OPT_CRAC_SUPPORT) */
915
+
557
916
/* Initialize the virtual machine */
558
917
start = CurrentTimeMicros ();
559
918
if (!InitializeJVM (& vm , & env , & ifn )) {
0 commit comments