hotspot/src/share/vm/runtime/arguments.cpp
changeset 32627 a48ad2501e47
parent 32582 56619bb8bcaa
parent 32621 cdab920f3b6c
child 32823 ca8fef4cd57b
child 33973 c6c0af1521f4
--- a/hotspot/src/share/vm/runtime/arguments.cpp	Fri Sep 04 12:47:57 2015 +0200
+++ b/hotspot/src/share/vm/runtime/arguments.cpp	Tue Sep 08 16:10:37 2015 +0200
@@ -982,53 +982,61 @@
 
 bool Arguments::add_property(const char* prop) {
   const char* eq = strchr(prop, '=');
-  char* key;
-  // ns must be static--its address may be stored in a SystemProperty object.
-  const static char ns[1] = {0};
-  char* value = (char *)ns;
-
-  size_t key_len = (eq == NULL) ? strlen(prop) : (eq - prop);
-  key = AllocateHeap(key_len + 1, mtInternal);
-  strncpy(key, prop, key_len);
-  key[key_len] = '\0';
-
-  if (eq != NULL) {
-    size_t value_len = strlen(prop) - key_len - 1;
-    value = AllocateHeap(value_len + 1, mtInternal);
-    strncpy(value, &prop[key_len + 1], value_len + 1);
+  const char* key;
+  const char* value = "";
+
+  if (eq == NULL) {
+    // property doesn't have a value, thus use passed string
+    key = prop;
+  } else {
+    // property have a value, thus extract it and save to the
+    // allocated string
+    size_t key_len = eq - prop;
+    char* tmp_key = AllocateHeap(key_len + 1, mtInternal);
+
+    strncpy(tmp_key, prop, key_len);
+    tmp_key[key_len] = '\0';
+    key = tmp_key;
+
+    value = &prop[key_len + 1];
   }
 
   if (strcmp(key, "java.compiler") == 0) {
     process_java_compiler_argument(value);
-    FreeHeap(key);
-    if (eq != NULL) {
-      FreeHeap(value);
-    }
-    return true;
-  } else if (strcmp(key, "sun.java.command") == 0) {
-    _java_command = value;
-
     // Record value in Arguments, but let it get passed to Java.
   } else if (strcmp(key, "sun.java.launcher.is_altjvm") == 0 ||
              strcmp(key, "sun.java.launcher.pid") == 0) {
     // sun.java.launcher.is_altjvm and sun.java.launcher.pid property are
     // private and are processed in process_sun_java_launcher_properties();
     // the sun.java.launcher property is passed on to the java application
-    FreeHeap(key);
-    if (eq != NULL) {
-      FreeHeap(value);
-    }
-    return true;
-  } else if (strcmp(key, "java.vendor.url.bug") == 0) {
-    // save it in _java_vendor_url_bug, so JVM fatal error handler can access
-    // its value without going through the property list or making a Java call.
-    _java_vendor_url_bug = value;
   } else if (strcmp(key, "sun.boot.library.path") == 0) {
     PropertyList_unique_add(&_system_properties, key, value, true);
-    return true;
-  }
-  // Create new property and add at the end of the list
-  PropertyList_unique_add(&_system_properties, key, value);
+  } else {
+    if (strcmp(key, "sun.java.command") == 0) {
+      if (_java_command != NULL) {
+        os::free(_java_command);
+      }
+      _java_command = os::strdup_check_oom(value, mtInternal);
+    } else if (strcmp(key, "java.vendor.url.bug") == 0) {
+      if (_java_vendor_url_bug != DEFAULT_VENDOR_URL_BUG) {
+        assert(_java_vendor_url_bug != NULL, "_java_vendor_url_bug is NULL");
+        os::free((void *)_java_vendor_url_bug);
+      }
+      // save it in _java_vendor_url_bug, so JVM fatal error handler can access
+      // its value without going through the property list or making a Java call.
+      _java_vendor_url_bug = os::strdup_check_oom(value, mtInternal);
+    }
+
+    // Create new property and add at the end of the list
+    PropertyList_unique_add(&_system_properties, key, value);
+  }
+
+  if (key != prop) {
+    // SystemProperty copy passed value, thus free previously allocated
+    // memory
+    FreeHeap((void *)key);
+  }
+
   return true;
 }
 
@@ -1045,7 +1053,7 @@
   // Ensure Agent_OnLoad has the correct initial values.
   // This may not be the final mode; mode may change later in onload phase.
   PropertyList_unique_add(&_system_properties, "java.vm.info",
-                          (char*)VM_Version::vm_info_string(), false);
+                          VM_Version::vm_info_string(), false);
 
   UseInterpreter             = true;
   UseCompiler                = true;
@@ -1582,9 +1590,6 @@
 #endif // _LP64
 #endif // !ZERO
 
-  // Set up runtime image flags.
-  set_runtime_image_flags();
-
   CodeCacheExtensions::set_ergonomics_flags();
 }
 
@@ -1839,16 +1844,6 @@
   }
 }
 
-  // Set up runtime image flags
-void Arguments::set_runtime_image_flags() {
-#ifdef _LP64
-  // Memory map image file by default on 64 bit machines.
-  if (FLAG_IS_DEFAULT(MemoryMapImage)) {
-    FLAG_SET_ERGO(bool, MemoryMapImage, true);
-  }
-#endif
-}
-
 // This must be called after ergonomics.
 void Arguments::set_bytecode_flags() {
   if (!RewriteBytecodes) {
@@ -1857,7 +1852,7 @@
 }
 
 // Aggressive optimization flags  -XX:+AggressiveOpts
-void Arguments::set_aggressive_opts_flags() {
+jint Arguments::set_aggressive_opts_flags() {
 #ifdef COMPILER2
   if (AggressiveUnboxing) {
     if (FLAG_IS_DEFAULT(EliminateAutoBox)) {
@@ -1884,7 +1879,9 @@
     // Feed the cache size setting into the JDK
     char buffer[1024];
     sprintf(buffer, "java.lang.Integer.IntegerCache.high=" INTX_FORMAT, AutoBoxCacheMax);
-    add_property(buffer);
+    if (!add_property(buffer)) {
+      return JNI_ENOMEM;
+    }
   }
   if (AggressiveOpts && FLAG_IS_DEFAULT(BiasedLockingStartupDelay)) {
     FLAG_SET_DEFAULT(BiasedLockingStartupDelay, 500);
@@ -1897,12 +1894,14 @@
 //      FLAG_SET_DEFAULT(EliminateZeroing, true);
 //    }
   }
+
+  return JNI_OK;
 }
 
 //===========================================================================================================
 // Parsing of java.compiler property
 
-void Arguments::process_java_compiler_argument(char* arg) {
+void Arguments::process_java_compiler_argument(const char* arg) {
   // For backwards compatibility, Djava.compiler=NONE or ""
   // causes us to switch to -Xint mode UNLESS -Xdebug
   // is also specified.
@@ -3129,8 +3128,10 @@
       jio_fprintf(defaultStream::output_stream(),
           "CreateMinidumpOnCrash is replaced by CreateCoredumpOnCrash: CreateCoredumpOnCrash is off\n");
     } else if (match_option(option, "-XX:", &tail)) { // -XX:xxxx
-      // Skip -XX:Flags= since that case has already been handled
-      if (strncmp(tail, "Flags=", strlen("Flags=")) != 0) {
+      // Skip -XX:Flags= and -XX:VMOptionsFile= since those cases have
+      // already been handled
+      if ((strncmp(tail, "Flags=", strlen("Flags=")) != 0) &&
+          (strncmp(tail, "VMOptionsFile=", strlen("VMOptionsFile=")) != 0)) {
         if (!process_argument(tail, args->ignoreUnrecognized, origin)) {
           return JNI_EINVAL;
         }
@@ -3382,6 +3383,7 @@
 class ScopedVMInitArgs : public StackObj {
  private:
   JavaVMInitArgs _args;
+  bool           _is_set;
 
  public:
   ScopedVMInitArgs() {
@@ -3389,6 +3391,7 @@
     _args.nOptions = 0;
     _args.options = NULL;
     _args.ignoreUnrecognized = false;
+    _is_set = false;
   }
 
   // Populates the JavaVMInitArgs object represented by this
@@ -3397,6 +3400,7 @@
   // returns anything other than JNI_OK, then this object is in a
   // partially constructed state, and should be abandoned.
   jint set_args(GrowableArray<JavaVMOption>* options) {
+    _is_set = true;
     JavaVMOption* options_arr = NEW_C_HEAP_ARRAY_RETURN_NULL(
         JavaVMOption, options->length(), mtInternal);
     if (options_arr == NULL) {
@@ -3420,6 +3424,7 @@
   }
 
   JavaVMInitArgs* get() { return &_args; }
+  bool is_set()         { return _is_set; }
 
   ~ScopedVMInitArgs() {
     if (_args.options == NULL) return;
@@ -3428,6 +3433,35 @@
     }
     FREE_C_HEAP_ARRAY(JavaVMOption, _args.options);
   }
+
+  // Insert options into this option list, to replace option at
+  // vm_options_file_pos (-XX:VMOptionsFile)
+  jint insert(const JavaVMInitArgs* args,
+              const JavaVMInitArgs* args_to_insert,
+              const int vm_options_file_pos) {
+    assert(_args.options == NULL, "shouldn't be set yet");
+    assert(args_to_insert->nOptions != 0, "there should be args to insert");
+    assert(vm_options_file_pos != -1, "vm_options_file_pos should be set");
+
+    int length = args->nOptions + args_to_insert->nOptions - 1;
+    GrowableArray<JavaVMOption> *options = new (ResourceObj::C_HEAP, mtInternal)
+              GrowableArray<JavaVMOption>(length, true);    // Construct new option array
+    for (int i = 0; i < args->nOptions; i++) {
+      if (i == vm_options_file_pos) {
+        // insert the new options starting at the same place as the
+        // -XX:VMOptionsFile option
+        for (int j = 0; j < args_to_insert->nOptions; j++) {
+          options->push(args_to_insert->options[j]);
+        }
+      } else {
+        options->push(args->options[i]);
+      }
+    }
+    // make into options array
+    jint result = set_args(options);
+    delete options;
+    return result;
+  }
 };
 
 jint Arguments::parse_java_options_environment_variable(ScopedVMInitArgs* args) {
@@ -3452,54 +3486,137 @@
     return JNI_ENOMEM;
   }
 
+  int retcode = parse_options_buffer(name, buffer, strlen(buffer), vm_args);
+
+  os::free(buffer);
+  return retcode;
+}
+
+const int OPTION_BUFFER_SIZE = 1024;
+
+jint Arguments::parse_vm_options_file(const char* file_name, ScopedVMInitArgs* vm_args) {
+  // read file into buffer
+  int fd = ::open(file_name, O_RDONLY);
+  if (fd < 0) {
+    jio_fprintf(defaultStream::error_stream(),
+                "Could not open options file '%s'\n",
+                file_name);
+    return JNI_ERR;
+  }
+
+  // '+ 1' for NULL termination even with max bytes
+  int bytes_alloc = OPTION_BUFFER_SIZE + 1;
+
+  char *buf = NEW_C_HEAP_ARRAY_RETURN_NULL(char, bytes_alloc, mtInternal);
+  if (NULL == buf) {
+    jio_fprintf(defaultStream::error_stream(),
+                "Could not allocate read buffer for options file parse\n");
+    os::close(fd);
+    return JNI_ENOMEM;
+  }
+
+  memset(buf, 0, (unsigned)bytes_alloc);
+
+  // Fill buffer
+  // Use ::read() instead of os::read because os::read()
+  // might do a thread state transition
+  // and it is too early for that here
+
+  int bytes_read = ::read(fd, (void *)buf, (unsigned)bytes_alloc);
+  os::close(fd);
+  if (bytes_read < 0) {
+    FREE_C_HEAP_ARRAY(char, buf);
+    jio_fprintf(defaultStream::error_stream(),
+                "Could not read options file '%s'\n", file_name);
+    return JNI_ERR;
+  }
+
+  if (bytes_read == 0) {
+    // tell caller there is no option data and that is ok
+    FREE_C_HEAP_ARRAY(char, buf);
+    return JNI_OK;
+  }
+
+  // file is larger than OPTION_BUFFER_SIZE
+  if (bytes_read > bytes_alloc - 1) {
+    FREE_C_HEAP_ARRAY(char, buf);
+    jio_fprintf(defaultStream::error_stream(),
+                "Options file '%s' is larger than %d bytes.\n",
+                file_name, bytes_alloc - 1);
+    return JNI_EINVAL;
+  }
+
+  int retcode = parse_options_buffer(file_name, buf, bytes_read, vm_args);
+
+  FREE_C_HEAP_ARRAY(char, buf);
+  return retcode;
+}
+
+jint Arguments::parse_options_buffer(const char* name, char* buffer, const size_t buf_len, ScopedVMInitArgs* vm_args) {
   GrowableArray<JavaVMOption> *options = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<JavaVMOption>(2, true);    // Construct option array
-  jio_fprintf(defaultStream::error_stream(),
-              "Picked up %s: %s\n", name, buffer);
-  char* rd = buffer;                        // pointer to the input string (rd)
-  while (true) {                            // repeat for all options in the input string
-    while (isspace(*rd)) rd++;              // skip whitespace
-    if (*rd == 0) break;                    // we re done when the input string is read completely
-
-    // The output, option string, overwrites the input string.
-    // Because of quoting, the pointer to the option string (wrt) may lag the pointer to
-    // input string (rd).
-    char* wrt = rd;
-
-    JavaVMOption option;
-    option.optionString = wrt;
-    options->append(option);                // Fill in option
-    while (*rd != 0 && !isspace(*rd)) {     // unquoted strings terminate with a space or NULL
+
+  // some pointers to help with parsing
+  char *buffer_end = buffer + buf_len;
+  char *opt_hd = buffer;
+  char *wrt = buffer;
+  char *rd = buffer;
+
+  // parse all options
+  while (rd < buffer_end) {
+    // skip leading white space from the input string
+    while (rd < buffer_end && isspace(*rd)) {
+      rd++;
+    }
+
+    if (rd >= buffer_end) {
+      break;
+    }
+
+    // Remember this is where we found the head of the token.
+    opt_hd = wrt;
+
+    // Tokens are strings of non white space characters separated
+    // by one or more white spaces.
+    while (rd < buffer_end && !isspace(*rd)) {
       if (*rd == '\'' || *rd == '"') {      // handle a quoted string
         int quote = *rd;                    // matching quote to look for
         rd++;                               // don't copy open quote
-        while (*rd != quote) {              // include everything (even spaces) up until quote
-          if (*rd == 0) {                   // string termination means unmatched string
-            jio_fprintf(defaultStream::error_stream(),
-                        "Unmatched quote in %s\n", name);
-            delete options;
-            os::free(buffer);
-            return JNI_ERR;
-          }
+        while (rd < buffer_end && *rd != quote) {
+                                            // include everything (even spaces)
+                                            // up until the close quote
           *wrt++ = *rd++;                   // copy to option string
         }
-        rd++;                               // don't copy close quote
+
+        if (rd < buffer_end) {
+          rd++;                             // don't copy close quote
+        } else {
+                                            // did not see closing quote
+          jio_fprintf(defaultStream::error_stream(),
+                      "Unmatched quote in %s\n", name);
+          delete options;
+          return JNI_ERR;
+        }
       } else {
         *wrt++ = *rd++;                     // copy to option string
       }
     }
-    if (*rd != 0) {
-      // In this case, the assignment to wrt below will make *rd nul,
-      // which will interfere with the next loop iteration.
-      rd++;
-    }
-    *wrt = 0;                               // Zero terminate option
+
+    // steal a white space character and set it to NULL
+    *wrt++ = '\0';
+    // We now have a complete token
+
+    JavaVMOption option;
+    option.optionString = opt_hd;
+
+    options->append(option);                // Fill in option
+
+    rd++;  // Advance to next character
   }
 
   // Fill out JavaVMInitArgs structure.
   jint status = vm_args->set_args(options);
 
   delete options;
-  os::free(buffer);
   return status;
 }
 
@@ -3581,12 +3698,44 @@
 
   return false;
 }
+
 #endif // PRODUCT
 
-static jint match_special_option_and_act(const JavaVMInitArgs* args,
-                                        char** flags_file) {
+jint Arguments::insert_vm_options_file(const JavaVMInitArgs* args,
+                                       char** flags_file,
+                                       char** vm_options_file,
+                                       const int vm_options_file_pos,
+                                       ScopedVMInitArgs *vm_options_file_args,
+                                       ScopedVMInitArgs* args_out) {
+  jint code = parse_vm_options_file(*vm_options_file, vm_options_file_args);
+  if (code != JNI_OK) {
+    return code;
+  }
+
+  // Now set global settings from the vm_option file, giving an error if
+  // it has VMOptionsFile in it
+  code = match_special_option_and_act(vm_options_file_args->get(), flags_file,
+                                      NULL, NULL, NULL);
+  if (code != JNI_OK) {
+    return code;
+  }
+
+  if (vm_options_file_args->get()->nOptions < 1) {
+    return 0;
+  }
+
+  return args_out->insert(args, vm_options_file_args->get(),
+                          vm_options_file_pos);
+}
+
+jint Arguments::match_special_option_and_act(const JavaVMInitArgs* args,
+                                             char ** flags_file,
+                                             char ** vm_options_file,
+                                             ScopedVMInitArgs* vm_options_file_args,
+                                             ScopedVMInitArgs* args_out) {
   // Remaining part of option string
   const char* tail;
+  int   vm_options_file_pos = -1;
 
   for (int index = 0; index < args->nOptions; index++) {
     const JavaVMOption* option = args->options + index;
@@ -3595,6 +3744,35 @@
     }
     if (match_option(option, "-XX:Flags=", &tail)) {
       *flags_file = (char *) tail;
+      if (*flags_file == NULL) {
+        jio_fprintf(defaultStream::error_stream(),
+                    "Cannot copy flags_file name.\n");
+        return JNI_ENOMEM;
+      }
+      continue;
+    }
+    if (match_option(option, "-XX:VMOptionsFile=", &tail)) {
+      if (vm_options_file != NULL) {
+        // The caller accepts -XX:VMOptionsFile
+        if (*vm_options_file != NULL) {
+          jio_fprintf(defaultStream::error_stream(),
+                      "Only one VM Options file is supported "
+                      "on the command line\n");
+          return JNI_EINVAL;
+        }
+
+        *vm_options_file = (char *) tail;
+        vm_options_file_pos = index;  // save position of -XX:VMOptionsFile
+        if (*vm_options_file == NULL) {
+          jio_fprintf(defaultStream::error_stream(),
+                      "Cannot copy vm_options_file name.\n");
+          return JNI_ENOMEM;
+        }
+      } else {
+        jio_fprintf(defaultStream::error_stream(),
+                    "VM options file is only supported on the command line\n");
+        return JNI_EINVAL;
+      }
       continue;
     }
     if (match_option(option, "-XX:+PrintVMOptions")) {
@@ -3648,6 +3826,12 @@
     }
 #endif
   }
+
+  // If there's a VMOptionsFile, parse that (also can set flags_file)
+  if ((vm_options_file != NULL) && (*vm_options_file != NULL)) {
+    return insert_vm_options_file(args, flags_file, vm_options_file,
+                                  vm_options_file_pos, vm_options_file_args, args_out);
+  }
   return JNI_OK;
 }
 
@@ -3672,10 +3856,15 @@
   // If flag "-XX:Flags=flags-file" is used it will be the first option to be processed.
   const char* hotspotrc = ".hotspotrc";
   char* flags_file = NULL;
+  char* vm_options_file = NULL;
   bool settings_file_specified = false;
   bool needs_hotspotrc_warning = false;
   ScopedVMInitArgs java_tool_options_args;
   ScopedVMInitArgs java_options_args;
+  ScopedVMInitArgs modified_cmd_line_args;
+  // Pass in vm_options_file_args to keep memory for flags_file from being
+  // deallocated if found in the vm options file.
+  ScopedVMInitArgs vm_options_file_args;
 
   jint code =
       parse_java_tool_options_environment_variable(&java_tool_options_args);
@@ -3688,18 +3877,27 @@
     return code;
   }
 
-  code =
-      match_special_option_and_act(java_tool_options_args.get(), &flags_file);
+  code = match_special_option_and_act(java_tool_options_args.get(),
+                                      &flags_file, NULL, NULL, NULL);
+  if (code != JNI_OK) {
+    return code;
+  }
+
+  code = match_special_option_and_act(args, &flags_file, &vm_options_file,
+                                      &vm_options_file_args,
+                                      &modified_cmd_line_args);
   if (code != JNI_OK) {
     return code;
   }
 
-  code = match_special_option_and_act(args, &flags_file);
-  if (code != JNI_OK) {
-    return code;
-  }
-
-  code = match_special_option_and_act(java_options_args.get(), &flags_file);
+
+  // The command line arguments have been modified to include VMOptionsFile arguments.
+  if (modified_cmd_line_args.is_set()) {
+    args = modified_cmd_line_args.get();
+  }
+
+  code = match_special_option_and_act(java_options_args.get(), &flags_file,
+                                      NULL, NULL, NULL);
   if (code != JNI_OK) {
     return code;
   }
@@ -3740,7 +3938,8 @@
 
   // Parse JavaVMInitArgs structure passed in, as well as JAVA_TOOL_OPTIONS and _JAVA_OPTIONS
   jint result = parse_vm_init_args(java_tool_options_args.get(),
-                                   java_options_args.get(), args);
+                                   java_options_args.get(),
+                                   args);   // command line arguments
 
   if (result != JNI_OK) {
     return result;
@@ -3869,7 +4068,10 @@
   set_bytecode_flags();
 
   // Set flags if Aggressive optimization flags (-XX:+AggressiveOpts) enabled
-  set_aggressive_opts_flags();
+  jint code = set_aggressive_opts_flags();
+  if (code != JNI_OK) {
+    return code;
+  }
 
   // Turn off biased locking for locking debug mode flags,
   // which are subtly different from each other but neither works with
@@ -4035,7 +4237,7 @@
   }
 }
 
-void Arguments::PropertyList_add(SystemProperty** plist, const char* k, char* v) {
+void Arguments::PropertyList_add(SystemProperty** plist, const char* k, const char* v) {
   if (plist == NULL)
     return;
 
@@ -4048,7 +4250,7 @@
 }
 
 // This add maintains unique property key in the list.
-void Arguments::PropertyList_unique_add(SystemProperty** plist, const char* k, char* v, jboolean append) {
+void Arguments::PropertyList_unique_add(SystemProperty** plist, const char* k, const char* v, jboolean append) {
   if (plist == NULL)
     return;