diff --git a/src/hotspot/share/prims/jni.cpp b/src/hotspot/share/prims/jni.cpp --- a/src/hotspot/share/prims/jni.cpp +++ b/src/hotspot/share/prims/jni.cpp @@ -3909,7 +3909,7 @@ bool can_try_again = true; result = Threads::create_vm((JavaVMInitArgs*) args, &can_try_again); - if (result == JNI_OK) { + if ((result == JNI_OK) || (result == JNI_SILENT_EXIT)) { JavaThread *thread = JavaThread::current(); assert(!thread->has_pending_exception(), "should have returned not OK"); /* thread is thread_in_vm here */ diff --git a/src/hotspot/share/prims/jvmti.xml b/src/hotspot/share/prims/jvmti.xml --- a/src/hotspot/share/prims/jvmti.xml +++ b/src/hotspot/share/prims/jvmti.xml @@ -631,8 +631,10 @@

The return value from Agent_OnLoad or - Agent_OnLoad_<agent-lib-name> is used to indicate an error. - Any value other than zero indicates an error and causes termination of the VM. + Agent_OnLoad_<agent-lib-name> is used to indicate whether + the agent successfully initialised or not. JNI_OK indicates success, JNI_SILENT_EXIT + indicates the agent did not fully initialize but that no error occurred, and any + other value indicates an error. Non-JNI_OK return codes cause termination of the VM. @@ -14811,6 +14813,12 @@ - The function may return NULL in the start phase if the can_generate_early_vmstart capability is enabled. + + Added JNI_SILENT_EXIT return code for early exits without errors. + java.c's ParseArguments function now sets pret value to 2 for a NULL pwhat. Addendums: + - This allows us to clearly identify when no class was set, but no other error has occurred. + - This is undone in java.c's ContinueInNewThread method, so the surface behaviour does not change. + diff --git a/src/hotspot/share/runtime/thread.cpp b/src/hotspot/share/runtime/thread.cpp --- a/src/hotspot/share/runtime/thread.cpp +++ b/src/hotspot/share/runtime/thread.cpp @@ -3637,6 +3637,9 @@ } jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { + /* This gets returned at the end of the method. */ + /* It allows us to complete vm initialisation and still return an error code if we want. */ + jint jniReturnCode = JNI_OK; extern void JDK_Version_init(); // Preinitialize version info. @@ -3725,7 +3728,9 @@ // Launch -agentlib/-agentpath and converted -Xrun agents if (Arguments::init_agents_at_startup()) { - create_vm_init_agents(); + if (create_vm_init_agents() == JNI_SILENT_EXIT) { + jniReturnCode = JNI_SILENT_EXIT; + } } // Initialize Threads state @@ -3991,7 +3996,7 @@ ShouldNotReachHere(); } - return JNI_OK; + return jniReturnCode; } // type for the Agent_OnLoad and JVM_OnLoad entry points @@ -4110,9 +4115,10 @@ // Create agents for -agentlib: -agentpath: and converted -Xrun // Invokes Agent_OnLoad // Called very early -- before JavaThreads exist -void Threads::create_vm_init_agents() { +jint Threads::create_vm_init_agents() { extern struct JavaVM_ main_vm; AgentLibrary* agent; + jint jniReturnCode = JNI_OK; JvmtiExport::enter_onload_phase(); @@ -4123,13 +4129,18 @@ // Invoke the Agent_OnLoad function jint err = (*on_load_entry)(&main_vm, agent->options(), NULL); if (err != JNI_OK) { - vm_exit_during_initialization("agent library failed to init", agent->name()); + if (err == JNI_SILENT_EXIT) { + jniReturnCode = JNI_SILENT_EXIT; + } else { + vm_exit_during_initialization("agent library failed to init", agent->name()); + } } } else { vm_exit_during_initialization("Could not find Agent_OnLoad function in the agent library", agent->name()); } } JvmtiExport::enter_primordial_phase(); + return jniReturnCode; } extern "C" { diff --git a/src/hotspot/share/runtime/thread.hpp b/src/hotspot/share/runtime/thread.hpp --- a/src/hotspot/share/runtime/thread.hpp +++ b/src/hotspot/share/runtime/thread.hpp @@ -2153,7 +2153,7 @@ static jint create_vm(JavaVMInitArgs* args, bool* canTryAgain); static void convert_vm_init_libraries_to_agents(); static void create_vm_init_libraries(); - static void create_vm_init_agents(); + static jint create_vm_init_agents(); static void shutdown_vm_agents(); static bool destroy_vm(); // Supported VM versions via JNI diff --git a/src/java.base/share/native/include/jni.h b/src/java.base/share/native/include/jni.h --- a/src/java.base/share/native/include/jni.h +++ b/src/java.base/share/native/include/jni.h @@ -164,6 +164,7 @@ #define JNI_ENOMEM (-4) /* not enough memory */ #define JNI_EEXIST (-5) /* VM already created */ #define JNI_EINVAL (-6) /* invalid arguments */ +#define JNI_SILENT_EXIT (-7) /* vm shut down with no error */ /* * used in ReleaseScalarArrayElements diff --git a/src/java.base/share/native/libjli/java.c b/src/java.base/share/native/libjli/java.c --- a/src/java.base/share/native/libjli/java.c +++ b/src/java.base/share/native/libjli/java.c @@ -75,6 +75,7 @@ static jboolean listModules = JNI_FALSE; static char *describeModule = NULL; static jboolean validateModules = JNI_FALSE; +static jboolean silentExit = JNI_FALSE; static const char *_program_name; static const char *_launcher_name; @@ -413,6 +414,12 @@ exit(1); } + // Exit without error if the vm has returned JNI_SILENT_EXIT. + if (silentExit) { + CHECK_EXCEPTION_LEAVE(1); + LEAVE(); + } + if (showSettings != NULL) { ShowSettings(env, showSettings); CHECK_EXCEPTION_LEAVE(1); @@ -1426,7 +1433,9 @@ if (*pwhat == NULL) { /* LM_UNKNOWN okay for options that exit */ if (!listModules && !describeModule && !validateModules) { - *pret = 1; + if (*pret != 1) { + *pret = 2; + } } } else if (mode == LM_UNKNOWN) { /* default to LM_CLASS if -m, -jar and -cp options are @@ -1477,7 +1486,10 @@ r = ifn->CreateJavaVM(pvm, (void **)penv, &args); JLI_MemFree(options); - return r == JNI_OK; + if (r == JNI_SILENT_EXIT) { + silentExit = JNI_TRUE; + } + return ((r == JNI_OK) || (r == JNI_SILENT_EXIT)); } static jclass helperClass = NULL; @@ -2314,8 +2326,17 @@ rslt = ContinueInNewThread0(JavaMain, threadStackSize, (void*)&args); /* If the caller has deemed there is an error we * simply return that, otherwise we return the value of - * the callee + * the callee. The sole exception is if ret is 2, rslt + * is 0, and silentExit is true. This indicates no class + * was specified, no error happened when starting the vm, + * and one of the options used triggered a silent exit. */ + if (ret == 2) { + if ((silentExit == JNI_TRUE) && (rslt == 0)) { + return 0; + } + ret = 1; + } return (ret != 0) ? ret : rslt; } } diff --git a/src/jdk.jdwp.agent/share/native/libjdwp/debugInit.c b/src/jdk.jdwp.agent/share/native/libjdwp/debugInit.c --- a/src/jdk.jdwp.agent/share/native/libjdwp/debugInit.c +++ b/src/jdk.jdwp.agent/share/native/libjdwp/debugInit.c @@ -102,6 +102,7 @@ static void initialize(JNIEnv *env, jthread thread, EventIndex triggering_ei); static jboolean parseOptions(char *str); +static jboolean isHelp(char* options); /* * Phase 1: Initial load. @@ -204,6 +205,11 @@ jint jvmtiCompileTimeMinorVersion; jint jvmtiCompileTimeMicroVersion; + /* If we were sent a help option, we only need to print usage and return a JNI_SILENT_EXIT rc */ + if (isHelp(options)) { + return JNI_SILENT_EXIT; + } + /* See if it's already loaded */ if ( gdata!=NULL && gdata->isLoaded==JNI_TRUE ) { ERROR_MESSAGE(("Cannot load this JVM TI agent twice, check your java command line for duplicate jdwp options.")); @@ -384,7 +390,10 @@ DEF_Agent_OnUnload(JavaVM *vm) { - gdata->isLoaded = JNI_FALSE; + //If gdata was not initialised, we can't set isLoaded to false here. + if (gdata!=NULL) { + gdata->isLoaded = JNI_FALSE; + } /* Cleanup, but make sure VM is alive before using JNI, and * make sure JVMTI environment is ok before deallocating @@ -1022,9 +1031,10 @@ } /* Check for "help" BEFORE we add any environmental settings */ - if ((strcmp(options, "help")) == 0) { + /* Agent_OnLoad should have already handled this. Including here for completeness. */ + if (isHelp(options)) { printUsage(); - forceExit(0); /* Kill entire process, no core dump wanted */ + return JNI_FALSE; /* If this is a help option, we should shut down now. */ } /* These buffers are never freed */ @@ -1295,6 +1305,24 @@ return JNI_FALSE; } +/* Returns true if the options contains "help" */ +/* Useful when you want to identify a help option without parsing the entire set of options. */ +static jboolean +isHelp(char* options) +{ + /* Options being NULL will end up being an error. */ + if (options == NULL) { + options = ""; + } + + /* Check for "help" */ + if ((strcmp(options, "help")) == 0) { + printUsage(); + return JNI_TRUE; + } + return JNI_FALSE; +} + /* All normal exit doors lead here */ void debugInit_exit(jvmtiError error, const char *msg)