Hi All, 

I've attached the code to resolve JDK-8190187

Could a committer please take the fix (amended code attached, and 
available on request) and: 

1) Complete the CSR process for the new JNI Return code. 
2) Commit the changes that contain the Return code.

And, optionally, 3) Perform 1 and 2 for the jdwp agent changes as well, so 
it can use this new functionality.



Best Regards

Adam Farley
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 @@
     </rationale>
     <p/>
     The return value from <code>Agent_OnLoad</code> or
-    <code>Agent_OnLoad_&lt;agent-lib-name&gt;</code> is used to indicate an 
error.
-    Any value other than zero indicates an error and causes termination of the 
VM.
+    <code>Agent_OnLoad_&lt;agent-lib-name&gt;</code> 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.
   </intro>
 
   <intro id="onattach" label="Agent Start-Up (Live phase)">
@@ -14811,6 +14813,12 @@
        - The function may return NULL in the start phase if the
          can_generate_early_vmstart capability is enabled.
   </change>
+  <change date="14 February 2018" version="11.0.0">
+      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.      
+  </change>
 </changehistory>
 
 </specification>
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)

Reply via email to