--- old/src/share/vm/runtime/arguments.cpp Fri Jun 19 12:52:20 2015 +++ new/src/share/vm/runtime/arguments.cpp Fri Jun 19 12:52:19 2015 @@ -56,6 +56,8 @@ // Note: This is a special bug reporting site for the JVM #define DEFAULT_VENDOR_URL_BUG "http://bugreport.java.com/bugreport/crash.jsp" #define DEFAULT_JAVA_LAUNCHER "generic" +#define N_MAX_OPTIONS 64 +#define OPTION_BUFFER_SIZE 1024 #define UNSUPPORTED_GC_OPTION(gc) \ do { \ @@ -112,7 +114,73 @@ SystemProperty *Arguments::_sun_boot_class_path = NULL; char* Arguments::_ext_dirs = NULL; +// change the 0 on next line to 1 to enable argument tracing +#if 0 +#define JVM_ARGS_ETRACE(x) x +#else +#define JVM_ARGS_ETRACE(x) +#endif +// Dump VM Option with a tag +void DumpOption(const JavaVMOption *option_p, + const int index, const char *tag) { + if (PrintVMOptions && Verbose) { + if (index >= 0) { + jio_fprintf(defaultStream::output_stream(), + "%s:[%d]='%s'\n", tag, index, option_p->optionString); + } + } +} + +// Dump VM Option with a tag +void DumpVMOption(const JavaVMInitArgs* args_p, + const int index, const char *tag) { + if (PrintVMOptions && Verbose) { + if (args_p->options != NULL) { + if ((index >= 0) && (index < args_p->nOptions)) { + JavaVMOption *option_p = args_p->options + index; + jio_fprintf(defaultStream::output_stream(), + "%s:[%d]='%s'\n", tag, index, option_p->optionString); + } + } + } +} + +// Dump VM Options with a tag +void DumpVMOptions(const JavaVMInitArgs* args_p, const char *tag) { + if (PrintVMOptions && Verbose) { + if (args_p->options != NULL) { + for (int index = 0; index < args_p->nOptions; index++) { + JavaVMOption *option_p = args_p->options + index; + jio_fprintf(defaultStream::output_stream(), + "%s:[%d]='%s'\n", tag, index, option_p->optionString); + } + } + } +} + +// Free memory associated with an options list +void FreeVMOptions(JavaVMInitArgs **args_pp, const JavaVMInitArgs *args_base_p) { + assert(args_pp != NULL, "args_pp must not be NULL"); + assert(*args_pp != NULL, "*args_pp must not be NULL"); + if (args_base_p == (*args_pp)) { + // Do not free memory we did not allocate + return; + } + assert((*args_pp)->options != NULL, "(*args_pp)->options must not be NULL"); + for (int index = 0; index < (*args_pp)->nOptions; index++) { + JavaVMOption *option_p = (*args_pp)->options + index; + if (option_p->optionString != NULL) { + os::free(option_p->optionString); + option_p->optionString = NULL; + } + } + os::free((void *)(*args_pp)->options); + (*args_pp)->options = NULL; + os::free((void *)(*args_pp)); + (*args_pp) = NULL; +} + // Check if head of 'option' matches 'name', and sets 'tail' to the remaining // part of the option string. static bool match_option(const JavaVMOption *option, const char* name, @@ -2644,6 +2712,7 @@ const char* tail; // iterate over arguments + DumpVMOptions(args, "Parse each"); for (int index = 0; index < args->nOptions; index++) { bool is_absolute_path = false; // for -agentpath vs -agentlib @@ -3277,8 +3346,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; } @@ -3698,22 +3769,561 @@ } #endif // PRODUCT -// Parse entry point called from JNI_CreateJavaVM +jint Arguments::alloc_JVM_options_list(const int needed_slots, + const int version, + struct JavaVMInitArgs **ret_args_head) { + assert(needed_slots > 0, "needed_slots must greater than zero"); + assert(ret_args_head != NULL, "ret_args_head must not be NULL"); -jint Arguments::parse(const JavaVMInitArgs* args) { + JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(), + "Allocating %d option slots\n", needed_slots)); + JavaVMInitArgs *new_args_head = + (JavaVMInitArgs *) os::malloc(sizeof (JavaVMInitArgs), mtInternal); + if (new_args_head == NULL) { + jio_fprintf(defaultStream::error_stream(), + "Could not allocate arguments list head\n"); + return JNI_ENOMEM; + } - // Remaining part of option string - const char* tail; + memset(new_args_head, 0, sizeof (struct JavaVMInitArgs)); - // If flag "-XX:Flags=flags-file" is used it will be the first option to be processed. + int bytes_needed = needed_slots * (sizeof (struct JavaVMOption)); + JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(), + "to allocate options list %d %d\n", + needed_slots, bytes_needed)); + + JavaVMOption *new_options_head = + (struct JavaVMOption *) os::malloc(bytes_needed, mtInternal); + + if (new_options_head == NULL) { + jio_fprintf(defaultStream::error_stream(), + "Could not allocate options list with %d entries" + " and %d bytes\n", + needed_slots, bytes_needed); + + os::free(new_args_head); + new_args_head = NULL; + + return JNI_ENOMEM; + } + + memset(new_options_head, 0, bytes_needed); + + JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(), + "allocation need for options list %d %d\n", + needed_slots, bytes_needed)); + + // Assemble the args object + new_args_head->options = new_options_head; + new_args_head->nOptions = 0; + new_args_head->version = version; + + // Return newly allocated but not populated options list + *ret_args_head = new_args_head; + + return JNI_OK; +} + +// Copy num_slots NULL separated JVM options from buffer to +// new_options[0] through new_options[num_slots - 1]. +// The byte_cnt includes null termination. +jint Arguments::copy_JVM_options_from_buf(const char *buffer, + const size_t byte_cnt, + const int num_slots, + int *cur_slot_p, + struct JavaVMOption *new_options) { + + assert(buffer != NULL, "buffer must not be NULL"); + assert(byte_cnt > 1, "byte_cnt must be greater than one"); + assert(num_slots > 0, "num_slots must be greater than zero"); + assert(cur_slot_p != NULL, "cur_slot_p must not be NULL"); + assert(new_options != NULL, "new_options must not be NULL"); + + JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(), + "Copying %d options from buffer\n", + num_slots)); + (*cur_slot_p) = 0; + const char *head = buffer; + const char *tail = buffer + byte_cnt - 1; + + JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(), + "Copying option %d %d %d\n", + byte_cnt, *cur_slot_p, num_slots)); + + while ((head < tail) && (*cur_slot_p < num_slots)) { + // Skip null character separator(s) + while ((head < tail) && (*head == '\0')) { + head++; + } + + if (head >= tail) { + break; + } + + // save a copy of the string in the options list + new_options[(*cur_slot_p)].optionString = + os::strdup_check_oom(head, mtInternal); + + if (new_options[(*cur_slot_p)].optionString == NULL) { + jio_fprintf(defaultStream::error_stream(), + "Could not copy file option string '%s'\n", head); + return JNI_ENOMEM; + } + + DumpOption(new_options, *cur_slot_p, "copy"); + const char *dummy; + if (match_option(&new_options[(*cur_slot_p)], + "-XX:VMOptionsFile=", &dummy)) { + jio_fprintf(defaultStream::error_stream(), + "VM options file is only supported on the command line\n"); + + (*cur_slot_p)++; // We do not need this slot, but count it on the + // list so the error handling can clean this up + return JNI_EINVAL; + } + + JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(), + "Copying option %d %d %s\n", + *cur_slot_p, + num_slots, + new_options[(*cur_slot_p)].optionString)); + (*cur_slot_p)++; // Done with current slot + + while ((head < tail) && (*head != '\0')) { + head++; + } + } + + return JNI_OK; +} + +// Parse the JVM options from file_name into ret_buff. The +// options in ret_buff are NULL separated in order to preserve +// any embedded white space that was quoted in file_name. +jint Arguments::parse_JVM_options_file(const char *file_name, + char **ret_buff, + size_t *ret_bytes, + int *ret_opt_cnt) { + + assert(file_name != NULL, "file_name must not be NULL."); + assert(ret_buff != NULL, "ret_buff must not be NULL."); + assert(ret_bytes != NULL, "ret_bytes must not be NULL."); + assert(ret_opt_cnt != NULL, "ret_opt_cnt must not be NULL."); + + // Default all return params to not return data + *ret_buff = NULL; + *ret_bytes = 0; + *ret_opt_cnt = 0; + + 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 = (char *)os::malloc(bytes_alloc, mtInternal); + + if (buf == NULL) { + 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 thead state transition + // and it is too early for that here + + int bytes_read = ::read(fd, (void *)buf, + (unsigned)bytes_alloc); + if (bytes_read < 0) { + os::free(buf); + buf = NULL; + os::close(fd); + 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 + os::free(buf); + buf = NULL; + os::close(fd); + return JNI_OK; + } + + // file is larger than OPTION_BUFFER_SIZE + if (bytes_read > bytes_alloc - 1) { + os::free(buf); + buf = NULL; + os::close(fd); + jio_fprintf(defaultStream::error_stream(), + "Options file '%s' is larger than %d bytes.\n", + file_name, bytes_alloc - 1); + return JNI_EINVAL; + } + + os::close(fd); + + // some pointers to help with parsing + char *buf_end = buf + bytes_read; + char *cur_token_head = buf; + char *wrt = buf; + char *rd = buf; + + // count tokens + int token_cnt = 0; + + JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(), + "Parse buf loop > '%s'\n", + buf)); + // parse all options + while (rd < buf_end) { + JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(), + "Parse buf loop white > >%s<\n", rd)); + // skip leading white space from the input string + while (rd < buf_end && iswhite(*rd)) { + rd++; + } + + if (rd >= buf_end) { + break; + } + + JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(), + "Parse next token > %s\n", rd)); + // Remember this is where we found the head of the token. + cur_token_head = wrt; + + // Tokens are strings of non white space characters separated + // by one or more white spaces. + while (rd < buf_end && !iswhite(*rd)) { + if (*rd == '\'' || *rd == '"') { // handle a quoted string + int quote = *rd; // matching quote to look for + rd++; // don't copy open quote + while (rd < buf_end && *rd != quote) { + // include everything (even spaces) + // up until the close quote + *wrt++ = *rd++; // copy to option string + } + + if (rd < buf_end) { + rd++; // don't copy close quote + } else { + // did not see closing quote + jio_fprintf(defaultStream::error_stream(), + "Unmatched quote in '%s'\n", file_name); + os::free(buf); + buf = NULL; + return JNI_EINVAL; + } + } else { + *wrt++ = *rd++; // copy to option string + } + } + + // steal a white space character and set it to NULL + *wrt++ = '\0'; + token_cnt++; + // We now have a complete token + JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(), + "Current parsed token > %s\n", + cur_token_head)); + + rd++; // Advance to next character + } + + JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(), + "Done parsing tokens > %d\n", + token_cnt)); + + if (token_cnt > N_MAX_OPTIONS) { + jio_fprintf(defaultStream::error_stream(), + "Options file has more than %d options.\n", + N_MAX_OPTIONS); + os::free(buf); + buf = NULL; + return JNI_EINVAL; + } + + // Pass the data the back to the caller + *ret_buff = buf; + *ret_opt_cnt = token_cnt; + *ret_bytes = (size_t)(wrt - buf); + + return JNI_OK; +} + +// Merge options from a JVM options file with the options provided +// by argsin and return the merge results via *argsout. If the +// '-XX:VMOptionsFile=...' option is not specified on the command +// line, then there is nothing to merge and *argsout is set to NULL. +jint Arguments::merge_JVM_options_file(const JavaVMInitArgs *argsin, + JavaVMInitArgs **argsout) { + + assert(argsin != NULL, "argsin should not be NULL"); + assert(argsout != NULL, "argsout should not be NULL"); + + JavaVMInitArgs *new_args_head = NULL; + char *options_file = NULL; + bool options_file_found = false; + + JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(), + "Found %d existing option(s)\n", + argsin->nOptions)); + + // Take a peak at options passed in, looking for an options file + int old_index = 0; + const char *tail = NULL; + while (old_index < argsin->nOptions) { + JavaVMOption *option = argsin->options + old_index; + + JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(), + "Found existing option %s\n", + option->optionString)); + + if (match_option(option, "-XX:VMOptionsFile=", &tail)) { + // Only one options file allowed on the command line. + if (options_file_found) { + jio_fprintf(defaultStream::error_stream(), + "Only one VM Options file is supported " + "on the command line\n"); + + *argsout = NULL; + os::free(options_file); + options_file = NULL; + return JNI_EINVAL; + } else { + options_file_found = true; + } + options_file = os::strdup_check_oom(tail, mtInternal); + if (options_file == NULL) { + jio_fprintf(defaultStream::error_stream(), + "Could not copy options file path\n"); + + return JNI_ENOMEM; + } + } + old_index++; + } + + // No option file specified so nothing more to do + if (options_file == NULL) { + *argsout = NULL; + return JNI_OK; + } + + DumpVMOptions(argsin, "Premerge"); + + char *buff; // Contents of the options file + size_t byte_cnt = 0; // bytes in buffer + int file_slots = 0; // # options found in file + + // We have requested an options file, so we need to read it + // and do a simple white space parse on it. + jint result = parse_JVM_options_file(options_file, + &buff, + &byte_cnt, + &file_slots); + if (result != JNI_OK) { + // the option file processing failed + jio_fprintf(defaultStream::error_stream(), + "Options file '%s', parsing error\n", + options_file); + + os::free(options_file); + options_file = NULL; + *argsout = NULL; + return result; + } + + JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(), + "Found %d bytes and %d options\n", + byte_cnt, file_slots)); + // no options but that is ok + if (file_slots == 0) { + os::free(options_file); + options_file = NULL; + *argsout = NULL; + return JNI_OK; + } + + // Combine cmd line slot count and the options file slots passed to us + // We need one less because the "-XX:VMOptionsFile" is not included + // in the new list. + int num_slots = argsin->nOptions + file_slots - 1; + + JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(), + "Allocate option list with %d slots, %d file," + " %d exist\n", + num_slots, file_slots, argsin->nOptions)); + + // Allocate a new option list + result = Arguments::alloc_JVM_options_list(num_slots, argsin->version, + &new_args_head); + if (result != JNI_OK) { + // the option file processing failed + jio_fprintf(defaultStream::error_stream(), + "Memory error in options processing\n"); + os::free(buff); + buff = NULL; + os::free(options_file); + options_file = NULL; + *argsout = NULL; + return result; + } + + JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(), + "merge option list > %d %d\n", + argsin->nOptions, num_slots)); + + old_index = 0; + while ((old_index < argsin->nOptions) && + (new_args_head->nOptions < num_slots)) { + + JavaVMOption *old_option_p = argsin->options + old_index; + JavaVMOption *new_option_p = new_args_head->options + + new_args_head->nOptions; + + JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(), + "Merge option %s old %d new %d\n", + old_option_p->optionString, + old_index, new_args_head->nOptions)); + + // Allow the VM option tracing to come on early. + if (match_option(old_option_p, "-XX:+PrintVMOptions")) { + PrintVMOptions = true; + } + + if (match_option(old_option_p, "-XX:VMOptionsFile=", &tail)) { + int file_slots_used = 0; + result = Arguments::copy_JVM_options_from_buf(buff, + byte_cnt, + file_slots, + &file_slots_used, + new_option_p); + // Update option count for success and failure. + // The initialized count is required to free the new args object + new_args_head->nOptions += file_slots_used; + + if (result != JNI_OK) { + // the option file processing failed + jio_fprintf(defaultStream::error_stream(), + "Options copy error '%s'\n", + options_file); + + os::free(buff); + buff = NULL; + os::free(options_file); + options_file = NULL; + FreeVMOptions(&new_args_head, argsin); + *argsout = NULL; + return result; + } + + old_index++; // Next slot in the input list + + JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(), + "old %d new %d file %d\n", + old_index, + new_args_head->nOptions, + file_slots)); + } else { + // We only increment the index if the option preexisted. + // The options file copying increments the index too + new_option_p->optionString = + os::strdup_check_oom(old_option_p->optionString, mtInternal); + + if (new_option_p->optionString == NULL) { + jio_fprintf(defaultStream::error_stream(), + "Could not allocate command option string '%s'\n", + old_option_p->optionString); + + FreeVMOptions(&new_args_head, argsin); + *argsout = NULL; + os::free(buff); + buff = NULL; + os::free(options_file); + options_file = NULL; + return JNI_ENOMEM; + } + + new_option_p->extraInfo = NULL; + + DumpOption(old_option_p, old_index, "old"); + DumpOption(new_option_p, new_args_head->nOptions, "new"); + + DumpVMOption(new_args_head, new_args_head->nOptions, "new"); + DumpVMOption(argsin, old_index, "old"); + old_index++; // Next slot in the input list + new_args_head->nOptions++; // Next slot in the new args list + } + + } + + os::free(buff); + buff = NULL; + os::free(options_file); + options_file = NULL; + + JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(), + "trace final num ops %d\n", + new_args_head->nOptions)); + + // We have a list of options that is ready to be returned to the caller. + *argsout = new_args_head; + DumpVMOptions(*argsout, "Postmerge"); + return JNI_OK; +} + +// Parse entry point called from JNI_CreateJavaVM + +jint Arguments::parse(const struct JavaVMInitArgs *argsin) { const char* hotspotrc = ".hotspotrc"; bool settings_file_specified = false; bool needs_hotspotrc_warning = false; + // If flag "-XX:Flags=flags-file" is used it will be the + // first option to be processed. const char* flags_file; int index; + struct JavaVMInitArgs *args = NULL; + + jint result = Arguments::merge_JVM_options_file(argsin, &args); + if (result != JNI_OK) { + // A more specific error message has been printed to the error stream + jio_fprintf(defaultStream::error_stream(), + "Options merge error, VM will exit\n"); + vm_exit(1); + } + + if (args == NULL) { + args = (JavaVMInitArgs *)argsin; + JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(), + "no file options merged\n")); + } else { + JVM_ARGS_ETRACE(jio_fprintf(defaultStream::error_stream(), + "file options merged\n")); + } + + DumpVMOptions(args, "Final Command Line"); + const char* tail = NULL; for (index = 0; index < args->nOptions; index++) { const JavaVMOption *option = args->options + index; + if (match_option(option, "-XX:VMOptionsFile=", &tail)) { + // -XX:VMOptionsFile= can only appear here if the option file + // is empty or if there are no options to process, ignore it. + continue; + } if (ArgumentsExt::process_options(option)) { continue; } @@ -3782,6 +4392,7 @@ // Parse specified settings file if (settings_file_specified) { if (!process_settings_file(flags_file, true, args->ignoreUnrecognized)) { + FreeVMOptions(&args, argsin); return JNI_EINVAL; } } else { @@ -3788,6 +4399,7 @@ #ifdef ASSERT // Parse default .hotspotrc settings file if (!process_settings_file(".hotspotrc", false, args->ignoreUnrecognized)) { + FreeVMOptions(&args, argsin); return JNI_EINVAL; } #else @@ -3808,11 +4420,14 @@ } // Parse JavaVMInitArgs structure passed in, as well as JAVA_TOOL_OPTIONS and _JAVA_OPTIONS - jint result = parse_vm_init_args(args); + result = parse_vm_init_args(args); if (result != JNI_OK) { + FreeVMOptions(&args, argsin); return result; } + FreeVMOptions(&args, argsin); + // Call get_shared_archive_path() here, after possible SharedArchiveFile option got parsed. SharedArchivePath = get_shared_archive_path(); if (SharedArchivePath == NULL) { --- old/src/share/vm/runtime/arguments.hpp Fri Jun 19 12:52:22 2015 +++ new/src/share/vm/runtime/arguments.hpp Fri Jun 19 12:52:21 2015 @@ -372,6 +372,20 @@ static bool process_argument(const char* arg, jboolean ignore_unrecognized, Flag::Flags origin); static void process_java_launcher_argument(const char*, void*); static void process_java_compiler_argument(char* arg); + + // Options file processing + static jint alloc_JVM_options_list(const int needed_slots, const int version, + struct JavaVMInitArgs **ret_args_head); + static jint copy_JVM_options_from_buf(const char *buffer, + const size_t buf_size, + const int num_slots, + int *num_slots_used, + struct JavaVMOption *new_options); + static jint merge_JVM_options_file(const struct JavaVMInitArgs *argsin, + struct JavaVMInitArgs **argsout); + static jint parse_JVM_options_file(const char *file_name, + char **buff, size_t *bytes, int *opt_cnt); + static jint parse_options_environment_variable(const char* name, SysClassPath* scp_p, bool* scp_assembly_required_p); static jint parse_java_tool_options_environment_variable(SysClassPath* scp_p, bool* scp_assembly_required_p); static jint parse_java_options_environment_variable(SysClassPath* scp_p, bool* scp_assembly_required_p); --- old/src/share/vm/utilities/globalDefinitions.hpp Fri Jun 19 12:52:24 2015 +++ new/src/share/vm/utilities/globalDefinitions.hpp Fri Jun 19 12:52:23 2015 @@ -1103,6 +1103,22 @@ //---------------------------------------------------------------------------------------------------- +// Utility functions for characters + +// isblank() in ctype.h only includes space and tab. +// iswhite() includes space, tab, new-line, vertical-tab, formfeed, +// carrage-return. This larger set of characters might be found in a +// text file. +#define iswhite(c) \ + ((c == ' ') || \ + (c == '\t') || \ + (c == '\n') || \ + (c == '\v') || \ + (c == '\f') || \ + (c == '\r')) + + +//---------------------------------------------------------------------------------------------------- // Utility functions for integers // Avoid use of global min/max macros which may cause unwanted double