Path: blob/master/runtime/criusupport/criusupport.cpp
5985 views
/*******************************************************************************1* Copyright (c) 2021, 2022 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception20*******************************************************************************/21#if defined(LINUX)22#include <criu/criu.h>23#include <fcntl.h>24#include <errno.h>25#endif /* defined(LINUX) */2627#include "criusupport.hpp"2829#include "jni.h"30#include "j9.h"31#include "j9jclnls.h"32#include "ut_j9criu.h"33#include "omrlinkedlist.h"34#include "omrthread.h"3536extern "C" {3738#define STRING_BUFFER_SIZE 2563940static void41setupJNIFieldIDs(JNIEnv *env)42{43J9VMThread *currentThread = (J9VMThread*)env;44J9JavaVM *vm = currentThread->javaVM;45J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;46jclass criuJVMCheckpointExceptionClass = NULL;47jclass criuSystemCheckpointExceptionClass = NULL;48jclass criuRestoreExceptionClass = NULL;4950criuJVMCheckpointExceptionClass = env->FindClass("org/eclipse/openj9/criu/JVMCheckpointException");51Assert_CRIU_notNull(criuJVMCheckpointExceptionClass);52vm->criuJVMCheckpointExceptionClass = (jclass) env->NewGlobalRef(criuJVMCheckpointExceptionClass);5354vm->criuJVMCheckpointExceptionInit = env->GetMethodID(criuJVMCheckpointExceptionClass, "<init>", "(Ljava/lang/String;I)V");55Assert_CRIU_notNull(vm->criuJVMCheckpointExceptionInit);5657criuSystemCheckpointExceptionClass = env->FindClass("org/eclipse/openj9/criu/SystemCheckpointException");58Assert_CRIU_notNull(criuSystemCheckpointExceptionClass);59vm->criuSystemCheckpointExceptionClass = (jclass) env->NewGlobalRef(criuSystemCheckpointExceptionClass);6061vm->criuSystemCheckpointExceptionInit = env->GetMethodID(criuSystemCheckpointExceptionClass, "<init>", "(Ljava/lang/String;I)V");62Assert_CRIU_notNull(vm->criuSystemCheckpointExceptionInit);6364criuRestoreExceptionClass = env->FindClass("org/eclipse/openj9/criu/RestoreException");65Assert_CRIU_notNull(criuRestoreExceptionClass);66vm->criuRestoreExceptionClass = (jclass) env->NewGlobalRef(criuRestoreExceptionClass);6768vm->criuRestoreExceptionInit = env->GetMethodID(criuRestoreExceptionClass, "<init>", "(Ljava/lang/String;I)V");69Assert_CRIU_notNull(vm->criuRestoreExceptionInit);7071if (NULL == vm->criuJVMCheckpointExceptionClass72|| NULL == vm->criuSystemCheckpointExceptionClass73|| NULL == vm->criuRestoreExceptionClass74) {75vmFuncs->internalEnterVMFromJNI(currentThread);76vmFuncs->setNativeOutOfMemoryError(currentThread, 0, 0);77vmFuncs->internalExitVMToJNI(currentThread);78}79}8081jboolean JNICALL82Java_org_eclipse_openj9_criu_CRIUSupport_isCRIUSupportEnabledImpl(JNIEnv *env, jclass unused)83{84J9VMThread *currentThread = (J9VMThread *) env;85J9JavaVM *vm = currentThread->javaVM;86jboolean res = JNI_FALSE;8788UT_MODULE_LOADED(J9_UTINTERFACE_FROM_VM(vm));89if (vm->internalVMFunctions->isCRIUSupportEnabled(currentThread)) {90#if defined(LINUX)91if (0 == criu_init_opts()) {92res = JNI_TRUE;93}94#endif /* defined(LINUX) */95}96setupJNIFieldIDs(env);9798return res;99}100101jboolean JNICALL102Java_org_eclipse_openj9_criu_CRIUSupport_isCheckpointAllowed(JNIEnv *env, jclass unused)103{104J9VMThread *currentThread = (J9VMThread *) env;105jboolean res = JNI_FALSE;106107if (currentThread->javaVM->internalVMFunctions->isCheckpointAllowed(currentThread)) {108res = JNI_TRUE;109}110111return res;112}113114#define J9_NATIVE_STRING_NO_ERROR 0115#define J9_NATIVE_STRING_OUT_OF_MEMORY (-1)116#define J9_NATIVE_STRING_FAIL_TO_CONVERT (-2)117118/**119* Converts the given java string to native representation. The nativeString parameter should point120* to a buffer with its size specified by nativeStringBufSize. If the java string length exceeds the buffer121* size, nativeString will be set to allocated memory.122*123* @note If successful, the caller is responsible for freeing any memory allocated and stored in nativeString.124*125* @param[in] currentThread current thread126* @param[in] javaString java string object127* @param[out] nativeString the native representation of the java string128* @param[in] nativeStringBufSize size of the nativeString buffer129*130* @return return code indicating success, allocation failure, or string conversion failure131*/132static IDATA133getNativeString(J9VMThread *currentThread, j9object_t javaString, char **nativeString, IDATA nativeStringBufSize)134{135J9JavaVM *vm = currentThread->javaVM;136J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;137char mutf8StringBuf[STRING_BUFFER_SIZE];138char *mutf8String = NULL;139UDATA mutf8StringSize = 0;140IDATA requiredConvertedStringSize = 0;141char *localNativeString = *nativeString;142IDATA res = J9_NATIVE_STRING_NO_ERROR;143PORT_ACCESS_FROM_VMC(currentThread);144OMRPORT_ACCESS_FROM_J9PORT(PORTLIB);145146mutf8String = vmFuncs->copyStringToUTF8WithMemAlloc(currentThread, javaString, J9_STR_NULL_TERMINATE_RESULT, "", 0, mutf8StringBuf, STRING_BUFFER_SIZE, &mutf8StringSize);147if (NULL == mutf8String) {148res = J9_NATIVE_STRING_OUT_OF_MEMORY;149goto free;150}151152/* get the required size */153requiredConvertedStringSize = omrstr_convert(J9STR_CODE_MUTF8, J9STR_CODE_PLATFORM_RAW,154mutf8String,155mutf8StringSize,156localNativeString,1570);158159if (requiredConvertedStringSize < 0) {160Trc_CRIU_getNativeString_getStringSizeFail(currentThread, mutf8String, mutf8StringSize);161res = J9_NATIVE_STRING_FAIL_TO_CONVERT;162goto free;163}164165/* Add 1 for NUL terminator */166requiredConvertedStringSize += 1;167168if (requiredConvertedStringSize > nativeStringBufSize) {169localNativeString = (char*) j9mem_allocate_memory(requiredConvertedStringSize, OMRMEM_CATEGORY_VM);170if (NULL == localNativeString) {171res = J9_NATIVE_STRING_OUT_OF_MEMORY;172goto free;173}174}175176localNativeString[requiredConvertedStringSize - 1] = '\0';177178/* convert the string */179requiredConvertedStringSize = omrstr_convert(J9STR_CODE_MUTF8, J9STR_CODE_PLATFORM_RAW,180mutf8String,181mutf8StringSize,182localNativeString,183requiredConvertedStringSize);184185if (requiredConvertedStringSize < 0) {186Trc_CRIU_getNativeString_convertFail(currentThread, mutf8String, mutf8StringSize, requiredConvertedStringSize);187res = J9_NATIVE_STRING_FAIL_TO_CONVERT;188goto free;189}190191free:192if (mutf8String != mutf8StringBuf) {193j9mem_free_memory(mutf8String);194}195if (localNativeString != *nativeString) {196if (J9_NATIVE_STRING_NO_ERROR == res) {197*nativeString = localNativeString;198} else {199j9mem_free_memory(localNativeString);200localNativeString = NULL;201}202}203204return res;205}206207/**208* Caller must first acquire exclusive VMAccess209*/210static void211toggleSuspendOnJavaThreads(J9VMThread *currentThread, BOOLEAN suspend)212{213J9JavaVM *vm = currentThread->javaVM;214J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;215UDATA javaThreads = J9THREAD_CATEGORY_RESOURCE_MONITOR_THREAD | J9THREAD_CATEGORY_APPLICATION_THREAD;216J9VMThread *walkThread = J9_LINKED_LIST_START_DO(vm->mainThread);217218Assert_CRIU_true(J9_XACCESS_EXCLUSIVE == vm->exclusiveAccessState);219220while (NULL != walkThread) {221if (J9_ARE_ANY_BITS_SET(javaThreads, omrthread_get_category(walkThread->osThread))222&& (currentThread != walkThread)223) {224if (suspend) {225vmFuncs->setHaltFlag(walkThread, J9_PUBLIC_FLAGS_HALT_THREAD_FOR_CHECKPOINT);226} else {227vmFuncs->clearHaltFlag(walkThread, J9_PUBLIC_FLAGS_HALT_THREAD_FOR_CHECKPOINT);228}229}230walkThread = J9_LINKED_LIST_NEXT_DO(vm->mainThread, walkThread);231}232}233234void JNICALL235Java_org_eclipse_openj9_criu_CRIUSupport_checkpointJVMImpl(JNIEnv *env,236jclass unused,237jstring imagesDir,238jboolean leaveRunning,239jboolean shellJob,240jboolean extUnixSupport,241jint logLevel,242jstring logFile,243jboolean fileLocks,244jstring workDir,245jboolean tcpEstablished,246jboolean autoDedup,247jboolean trackMemory)248{249J9VMThread *currentThread = (J9VMThread*)env;250J9JavaVM *vm = currentThread->javaVM;251J9InternalVMFunctions *vmFuncs = vm->internalVMFunctions;252253jclass currentExceptionClass = NULL;254char *exceptionMsg = NULL;255const char *nlsMsgFormat = NULL;256UDATA msgCharLength = 0;257IDATA systemReturnCode = 0;258PORT_ACCESS_FROM_VMC(currentThread);259260Trc_CRIU_checkpointJVMImpl_Entry(currentThread);261if (vmFuncs->isCheckpointAllowed(currentThread)) {262#if defined(LINUX)263j9object_t cpDir = NULL;264j9object_t log = NULL;265j9object_t wrkDir = NULL;266IDATA dirFD = 0;267IDATA workDirFD = 0;268char directoryBuf[STRING_BUFFER_SIZE];269char *directoryChars = directoryBuf;270char logFileBuf[STRING_BUFFER_SIZE];271char *logFileChars = logFileBuf;272char workDirBuf[STRING_BUFFER_SIZE];273char *workDirChars = workDirBuf;274BOOLEAN isAfterCheckpoint = FALSE;275276vmFuncs->internalEnterVMFromJNI(currentThread);277278Assert_CRIU_notNull(imagesDir);279cpDir = J9_JNI_UNWRAP_REFERENCE(imagesDir);280systemReturnCode = getNativeString(currentThread, cpDir, &directoryChars, STRING_BUFFER_SIZE);281switch (systemReturnCode) {282case J9_NATIVE_STRING_NO_ERROR:283break;284case J9_NATIVE_STRING_OUT_OF_MEMORY:285vmFuncs->setNativeOutOfMemoryError(currentThread, 0, 0);286goto freeDir;287case J9_NATIVE_STRING_FAIL_TO_CONVERT:288currentExceptionClass = vm->criuJVMCheckpointExceptionClass;289nlsMsgFormat = j9nls_lookup_message(J9NLS_DO_NOT_PRINT_MESSAGE_TAG | J9NLS_DO_NOT_APPEND_NEWLINE, J9NLS_JCL_CRIU_FAILED_TO_CONVERT_JAVA_STRING, NULL);290goto freeDir;291}292293if (NULL != logFile) {294log = J9_JNI_UNWRAP_REFERENCE(logFile);295systemReturnCode = getNativeString(currentThread, log, &logFileChars, STRING_BUFFER_SIZE);296switch (systemReturnCode) {297case J9_NATIVE_STRING_NO_ERROR:298break;299case J9_NATIVE_STRING_OUT_OF_MEMORY:300vmFuncs->setNativeOutOfMemoryError(currentThread, 0, 0);301goto freeLog;302case J9_NATIVE_STRING_FAIL_TO_CONVERT:303currentExceptionClass = vm->criuJVMCheckpointExceptionClass;304nlsMsgFormat = j9nls_lookup_message(J9NLS_DO_NOT_PRINT_MESSAGE_TAG | J9NLS_DO_NOT_APPEND_NEWLINE, J9NLS_JCL_CRIU_FAILED_TO_CONVERT_JAVA_STRING, NULL);305goto freeLog;306}307}308309if (NULL != workDir) {310wrkDir = J9_JNI_UNWRAP_REFERENCE(workDir);311systemReturnCode = getNativeString(currentThread, wrkDir, &workDirChars, STRING_BUFFER_SIZE);312switch (systemReturnCode) {313case J9_NATIVE_STRING_NO_ERROR:314break;315case J9_NATIVE_STRING_OUT_OF_MEMORY:316vmFuncs->setNativeOutOfMemoryError(currentThread, 0, 0);317goto freeWorkDir;318case J9_NATIVE_STRING_FAIL_TO_CONVERT:319currentExceptionClass = vm->criuJVMCheckpointExceptionClass;320nlsMsgFormat = j9nls_lookup_message(J9NLS_DO_NOT_PRINT_MESSAGE_TAG | J9NLS_DO_NOT_APPEND_NEWLINE, J9NLS_JCL_CRIU_FAILED_TO_CONVERT_JAVA_STRING, NULL);321goto freeWorkDir;322}323}324325dirFD = open(directoryChars, O_DIRECTORY);326if (dirFD < 0) {327systemReturnCode = errno;328currentExceptionClass = vm->criuJVMCheckpointExceptionClass;329nlsMsgFormat = j9nls_lookup_message(J9NLS_DO_NOT_PRINT_MESSAGE_TAG | J9NLS_DO_NOT_APPEND_NEWLINE, J9NLS_JCL_CRIU_FAILED_TO_OPEN_DIR, NULL);330goto freeWorkDir;331}332333if (NULL != workDir) {334workDirFD = open(workDirChars, O_DIRECTORY);335if (workDirFD < 0) {336systemReturnCode = errno;337currentExceptionClass = vm->criuJVMCheckpointExceptionClass;338nlsMsgFormat = j9nls_lookup_message(J9NLS_DO_NOT_PRINT_MESSAGE_TAG | J9NLS_DO_NOT_APPEND_NEWLINE, J9NLS_JCL_CRIU_FAILED_TO_OPEN_WORK_DIR, NULL);339goto closeDirFD;340}341}342343systemReturnCode = criu_init_opts();344if (0 != systemReturnCode) {345currentExceptionClass = vm->criuSystemCheckpointExceptionClass;346nlsMsgFormat = j9nls_lookup_message(J9NLS_DO_NOT_PRINT_MESSAGE_TAG | J9NLS_DO_NOT_APPEND_NEWLINE, J9NLS_JCL_CRIU_INIT_FAILED, NULL);347goto closeWorkDirFD;348}349350criu_set_images_dir_fd(dirFD);351criu_set_shell_job(JNI_FALSE != shellJob);352if (logLevel > 0) {353criu_set_log_level((int)logLevel);354}355if (NULL != logFile) {356criu_set_log_file(logFileChars);357}358criu_set_leave_running(JNI_FALSE != leaveRunning);359criu_set_ext_unix_sk(JNI_FALSE != extUnixSupport);360criu_set_file_locks(JNI_FALSE != fileLocks);361criu_set_tcp_established(JNI_FALSE != tcpEstablished);362criu_set_auto_dedup(JNI_FALSE != autoDedup);363criu_set_track_mem(JNI_FALSE != trackMemory);364365if (NULL != workDir) {366criu_set_work_dir_fd(workDirFD);367}368369vmFuncs->acquireExclusiveVMAccess(currentThread);370371toggleSuspendOnJavaThreads(currentThread, TRUE);372373vmFuncs->releaseExclusiveVMAccess(currentThread);374375if (FALSE == vmFuncs->jvmCheckpointHooks(currentThread)) {376/* throw the pending exception */377goto wakeJavaThreads;378}379380vmFuncs->acquireExclusiveVMAccess(currentThread);381382/* Run internal checkpoint hooks, after iterating heap objects */383if (FALSE == vmFuncs->runInternalJVMCheckpointHooks(currentThread)) {384currentExceptionClass = vm->criuSystemCheckpointExceptionClass;385nlsMsgFormat = j9nls_lookup_message(J9NLS_DO_NOT_PRINT_MESSAGE_TAG | J9NLS_DO_NOT_APPEND_NEWLINE,386J9NLS_JCL_CRIU_FAILED_TO_RUN_INTERNAL_CHECKPOINT_HOOKS, NULL);387goto wakeJavaThreadsWithExclusiveVMAccess;388}389390Trc_CRIU_before_checkpoint(currentThread);391systemReturnCode = criu_dump();392Trc_CRIU_after_checkpoint(currentThread);393if (systemReturnCode < 0) {394currentExceptionClass = vm->criuSystemCheckpointExceptionClass;395nlsMsgFormat = j9nls_lookup_message(J9NLS_DO_NOT_PRINT_MESSAGE_TAG | J9NLS_DO_NOT_APPEND_NEWLINE, J9NLS_JCL_CRIU_DUMP_FAILED, NULL);396goto wakeJavaThreadsWithExclusiveVMAccess;397}398399/* We can only end up here if the CRIU restore was successful */400isAfterCheckpoint = TRUE;401402/* Run internal restore hooks, and cleanup */403if (FALSE == vmFuncs->runInternalJVMRestoreHooks(currentThread)) {404currentExceptionClass = vm->criuRestoreExceptionClass;405nlsMsgFormat = j9nls_lookup_message(J9NLS_DO_NOT_PRINT_MESSAGE_TAG | J9NLS_DO_NOT_APPEND_NEWLINE,406J9NLS_JCL_CRIU_FAILED_TO_RUN_INTERNAL_RESTORE_HOOKS, NULL);407goto wakeJavaThreadsWithExclusiveVMAccess;408}409410vmFuncs->releaseExclusiveVMAccess(currentThread);411412if (FALSE == vmFuncs->jvmRestoreHooks(currentThread)) {413/* throw the pending exception */414goto wakeJavaThreads;415}416417wakeJavaThreads:418vmFuncs->acquireExclusiveVMAccess(currentThread);419420wakeJavaThreadsWithExclusiveVMAccess:421toggleSuspendOnJavaThreads(currentThread, FALSE);422423vmFuncs->releaseExclusiveVMAccess(currentThread);424closeWorkDirFD:425if ((0 != close(workDirFD)) && (NULL == currentExceptionClass)) {426systemReturnCode = errno;427if (isAfterCheckpoint) {428currentExceptionClass = vm->criuRestoreExceptionClass;429} else {430currentExceptionClass = vm->criuSystemCheckpointExceptionClass;431}432nlsMsgFormat = j9nls_lookup_message(J9NLS_DO_NOT_PRINT_MESSAGE_TAG | J9NLS_DO_NOT_APPEND_NEWLINE, J9NLS_JCL_CRIU_FAILED_TO_CLOSE_WORK_DIR, NULL);433}434closeDirFD:435if ((0 != close(dirFD)) && (NULL == currentExceptionClass)) {436systemReturnCode = errno;437if (isAfterCheckpoint) {438currentExceptionClass = vm->criuRestoreExceptionClass;439} else {440currentExceptionClass = vm->criuSystemCheckpointExceptionClass;441}442nlsMsgFormat = j9nls_lookup_message(J9NLS_DO_NOT_PRINT_MESSAGE_TAG | J9NLS_DO_NOT_APPEND_NEWLINE, J9NLS_JCL_CRIU_FAILED_TO_CLOSE_DIR, NULL);443}444freeWorkDir:445if (workDirBuf != workDirChars) {446j9mem_free_memory(workDirChars);447}448freeLog:449if (logFileBuf != logFileChars) {450j9mem_free_memory(logFileChars);451}452freeDir:453if (directoryBuf != directoryChars) {454j9mem_free_memory(directoryChars);455}456457vmFuncs->internalExitVMToJNI(currentThread);458#endif /* defined(LINUX) */459}460461/*462* Pending exceptions will be set by the JVM hooks, these exception will take precedence.463*/464if ((NULL != currentExceptionClass) && (NULL == currentThread->currentException)) {465msgCharLength = j9str_printf(PORTLIB, NULL, 0, nlsMsgFormat, systemReturnCode);466exceptionMsg = (char*) j9mem_allocate_memory(msgCharLength, J9MEM_CATEGORY_VM);467468j9str_printf(PORTLIB, exceptionMsg, msgCharLength, nlsMsgFormat, systemReturnCode);469470jmethodID init = NULL;471if (vm->criuJVMCheckpointExceptionClass == currentExceptionClass) {472init = vm->criuJVMCheckpointExceptionInit;473} else if (vm->criuSystemCheckpointExceptionClass == currentExceptionClass) {474init = vm->criuSystemCheckpointExceptionInit;475} else {476init = vm->criuRestoreExceptionInit;477}478jstring jExceptionMsg = env->NewStringUTF(exceptionMsg);479480if (JNI_FALSE == env->ExceptionCheck()) {481jobject exception = env->NewObject(currentExceptionClass, init, jExceptionMsg, (jint)systemReturnCode);482if (NULL != exception) {483env->Throw((jthrowable)exception);484}485}486487if (NULL != exceptionMsg) {488j9mem_free_memory(exceptionMsg);489}490}491492Trc_CRIU_checkpointJVMImpl_Exit(currentThread);493}494495} /* extern "C" */496497498