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)