--- /dev/null 2017-11-09 09:38:01.297999907 +0100 +++ new/src/hotspot/share/jfr/recorder/access/jfrOptionSet.cpp 2018-04-09 14:55:30.808893780 +0200 @@ -0,0 +1,713 @@ +/* + * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "jfr/dcmd/jfrDcmds.hpp" +#include "jfr/recorder/access/jfrMemorySizer.hpp" +#include "jfr/recorder/access/jfrOptionSet.hpp" +#include "jfr/utilities/jfrAllocation.hpp" +#include "logging/log.hpp" +#include "memory/allocation.inline.hpp" +#include "memory/resourceArea.hpp" +#include "runtime/java.hpp" +#include "runtime/thread.inline.hpp" +#include "services/diagnosticArgument.hpp" +#include "services/diagnosticFramework.hpp" +#include "utilities/ostream.hpp" + +struct ObsoleteOption { + const char* name; + const char* message; +}; + +static const ObsoleteOption OBSOLETE_OPTIONS[] = { + {"checkpointbuffersize", ""}, + {"maxsize", "Use -XX:StartFlightRecording=maxsize=... instead."}, + {"maxage", "Use -XX:StartFlightRecording=maxage=... instead."}, + {"settings", "Use -XX:StartFlightRecording=settings=... instead."}, + {"defaultrecording", "Use -XX:StartFlightRecording=disk=false to create an in-memory recording."}, + {"disk", "Use -XX:StartFlightRecording=disk=... instead."}, + {"dumponexit", "Use -XX:StartFlightRecording=dumponexit=... instead."}, + {"dumponexitpath", "Use -XX:StartFlightRecording=filename=... instead."}, + {"loglevel", "Use -Xlog:jfr=... instead."} +}; + +jlong JfrOptionSet::max_chunk_size() { + return _max_chunk_size; +} + +void JfrOptionSet::set_max_chunk_size(jlong value) { + _max_chunk_size = value; +} + +jlong JfrOptionSet::global_buffer_size() { + return _global_buffer_size; +} + +void JfrOptionSet::set_global_buffer_size(jlong value) { + _global_buffer_size = value; +} + +jlong JfrOptionSet::thread_buffer_size() { + return _thread_buffer_size; +} + +void JfrOptionSet::set_thread_buffer_size(jlong value) { + _thread_buffer_size = value; +} + +jlong JfrOptionSet::memory_size() { + return _memory_size; +} + +void JfrOptionSet::set_memory_size(jlong value) { + _memory_size = value; +} + +jlong JfrOptionSet::num_global_buffers() { + return _num_global_buffers; +} + +void JfrOptionSet::set_num_global_buffers(jlong value) { + _num_global_buffers = value; +} + +jint JfrOptionSet::old_object_queue_size() { + return (jint)_old_object_queue_size; +} + +void JfrOptionSet::set_old_object_queue_size(jlong value) { + _old_object_queue_size = value; +} + +u4 JfrOptionSet::stackdepth() { + return _stack_depth; +} + +static const u4 STACK_DEPTH_DEFAULT = 64; +static const u4 MIN_STACK_DEPTH = 1; +static const u4 MAX_STACK_DEPTH = 2048; + +void JfrOptionSet::set_stackdepth(u4 depth) { + if (depth < MIN_STACK_DEPTH) { + _stack_depth = MIN_STACK_DEPTH; + } else if (depth > MAX_STACK_DEPTH) { + _stack_depth = MAX_STACK_DEPTH; + } else { + _stack_depth = depth; + } +} + +bool JfrOptionSet::sample_threads() { + return _sample_threads == JNI_TRUE; +} + +void JfrOptionSet::set_sample_threads(jboolean sample) { + _sample_threads = sample; +} + +bool JfrOptionSet::can_retransform() { + return _retransform == JNI_TRUE; +} + +void JfrOptionSet::set_retransform(jboolean value) { + _retransform = value; +} + +bool JfrOptionSet::sample_protection() { + return _sample_protection == JNI_TRUE; +} + +#ifdef ASSERT +void JfrOptionSet::set_sample_protection(jboolean protection) { + _sample_protection = protection; +} +#endif + +bool JfrOptionSet::compressed_integers() { + // Set this to false for debugging purposes. + return true; +} + +bool JfrOptionSet::allow_retransforms() { +#if INCLUDE_JVMTI + return true; +#else + return false; +#endif +} + +bool JfrOptionSet::allow_event_retransforms() { + return allow_retransforms() && (DumpSharedSpaces || can_retransform()); +} + +// default options for the dcmd parser +const char* const default_repository = NULL; +const char* const default_global_buffer_size = "512k"; +const char* const default_num_global_buffers = "20"; +const char* const default_memory_size = "10m"; +const char* const default_thread_buffer_size = "8k"; +const char* const default_max_chunk_size = "12m"; +const char* const default_sample_threads = "true"; +const char* const default_stack_depth = "64"; +const char* const default_retransform = "true"; +const char* const default_old_object_queue_size = "256"; +DEBUG_ONLY(const char* const default_sample_protection = "false";) + +// statics +static DCmdArgument _dcmd_repository( + "repository", + "Flight recorder disk repository location", + "STRING", + false, + default_repository); + +static DCmdArgument _dcmd_threadbuffersize( + "threadbuffersize", + "Thread buffer size", + "MEMORY SIZE", + false, + default_thread_buffer_size); + +static DCmdArgument _dcmd_memorysize( + "memorysize", + "Size of memory to be used by Flight Recorder", + "MEMORY SIZE", + false, + default_memory_size); + +static DCmdArgument _dcmd_globalbuffersize( + "globalbuffersize", + "Global buffer size", + "MEMORY SIZE", + false, + default_global_buffer_size); + +static DCmdArgument _dcmd_numglobalbuffers( + "numglobalbuffers", + "Number of global buffers", + "JULONG", + false, + default_num_global_buffers); + +static DCmdArgument _dcmd_maxchunksize( + "maxchunksize", + "Maximum size of a single repository disk chunk", + "MEMORY SIZE", + false, + default_max_chunk_size); + +static DCmdArgument _dcmd_old_object_queue_size ( + "old-object-queue-size", + "Maximum number of old objects to track", + "JINT", + false, + default_old_object_queue_size); + +static DCmdArgument _dcmd_sample_threads( + "samplethreads", + "Thread sampling enable / disable (only sampling when event enabled and sampling enabled)", + "BOOLEAN", + false, + default_sample_threads); + +#ifdef ASSERT +static DCmdArgument _dcmd_sample_protection( + "sampleprotection", + "Safeguard for stackwalking while sampling threads (false by default)", + "BOOLEAN", + false, + default_sample_protection); +#endif + +static DCmdArgument _dcmd_stackdepth( + "stackdepth", + "Stack depth for stacktraces (minimum 1, maximum 2048)", + "JULONG", + false, + default_stack_depth); + +static DCmdArgument _dcmd_retransform( + "retransform", + "If event classes should be instrumented using JVMTI (by default true)", + "BOOLEAN", + true, + default_retransform); + +static DCmdParser _parser; + +static void register_parser_options() { + _parser.add_dcmd_option(&_dcmd_repository); + _parser.add_dcmd_option(&_dcmd_threadbuffersize); + _parser.add_dcmd_option(&_dcmd_memorysize); + _parser.add_dcmd_option(&_dcmd_globalbuffersize); + _parser.add_dcmd_option(&_dcmd_numglobalbuffers); + _parser.add_dcmd_option(&_dcmd_maxchunksize); + _parser.add_dcmd_option(&_dcmd_stackdepth); + _parser.add_dcmd_option(&_dcmd_sample_threads); + _parser.add_dcmd_option(&_dcmd_retransform); + _parser.add_dcmd_option(&_dcmd_old_object_queue_size); + DEBUG_ONLY(_parser.add_dcmd_option(&_dcmd_sample_protection);) +} + +static bool parse_flight_recorder_options_internal(TRAPS) { + if (FlightRecorderOptions == NULL) { + return true; + } + const size_t length = strlen((const char*)FlightRecorderOptions); + CmdLine cmdline((const char*)FlightRecorderOptions, length, true); + _parser.parse(&cmdline, ',', THREAD); + if (HAS_PENDING_EXCEPTION) { + for (int index = 0; index < 9; index++) { + ObsoleteOption option = OBSOLETE_OPTIONS[index]; + const char* p = strstr((const char*)FlightRecorderOptions, option.name); + const size_t option_length = strlen(option.name); + if (p != NULL && p[option_length] == '=') { + log_error(arguments) ("-XX:FlightRecorderOptions=%s=... has been removed. %s", option.name, option.message); + return false; + } + } + ResourceMark rm(THREAD); + oop message = java_lang_Throwable::message(PENDING_EXCEPTION); + if (message != NULL) { + const char* msg = java_lang_String::as_utf8_string(message); + log_error(arguments) ("%s", msg); + } + CLEAR_PENDING_EXCEPTION; + return false; + } + return true; +} + +jlong JfrOptionSet::_max_chunk_size = 0; +jlong JfrOptionSet::_global_buffer_size = 0; +jlong JfrOptionSet::_thread_buffer_size = 0; +jlong JfrOptionSet::_memory_size = 0; +jlong JfrOptionSet::_num_global_buffers = 0; +jlong JfrOptionSet::_old_object_queue_size = 0; +u4 JfrOptionSet::_stack_depth = STACK_DEPTH_DEFAULT; +jboolean JfrOptionSet::_sample_threads = JNI_TRUE; +jboolean JfrOptionSet::_retransform = JNI_TRUE; +#ifdef ASSERT +jboolean JfrOptionSet::_sample_protection = JNI_FALSE; +#else +jboolean JfrOptionSet::_sample_protection = JNI_TRUE; +#endif + +bool JfrOptionSet::initialize(Thread* thread) { + register_parser_options(); + if (!parse_flight_recorder_options_internal(thread)) { + return false; + } + if (_dcmd_retransform.is_set()) { + set_retransform(_dcmd_retransform.value()); + } + set_old_object_queue_size(_dcmd_old_object_queue_size.value()); + return adjust_memory_options(); +} + +bool JfrOptionSet::configure(TRAPS) { + if (FlightRecorderOptions == NULL) { + return true; + } + ResourceMark rm(THREAD); + bufferedStream st; + // delegate to DCmd execution + JfrConfigureFlightRecorderDCmd configure(&st, false); + configure._repository_path.set_is_set(_dcmd_repository.is_set()); + char* repo = _dcmd_repository.value(); + if (repo != NULL) { + const size_t len = strlen(repo); + char* repo_copy = JfrCHeapObj::new_array(len + 1); + if (NULL == repo_copy) { + return false; + } + strncpy(repo_copy, repo, len + 1); + configure._repository_path.set_value(repo_copy); + } + + configure._stack_depth.set_is_set(_dcmd_stackdepth.is_set()); + configure._stack_depth.set_value(_dcmd_stackdepth.value()); + + configure._thread_buffer_size.set_is_set(_dcmd_threadbuffersize.is_set()); + configure._thread_buffer_size.set_value(_dcmd_threadbuffersize.value()._size); + + configure._global_buffer_count.set_is_set(_dcmd_numglobalbuffers.is_set()); + configure._global_buffer_count.set_value(_dcmd_numglobalbuffers.value()); + + configure._global_buffer_size.set_is_set(_dcmd_globalbuffersize.is_set()); + configure._global_buffer_size.set_value(_dcmd_globalbuffersize.value()._size); + + configure._max_chunk_size.set_is_set(_dcmd_maxchunksize.is_set()); + configure._max_chunk_size.set_value(_dcmd_maxchunksize.value()._size); + + configure._memory_size.set_is_set(_dcmd_memorysize.is_set()); + configure._memory_size.set_value(_dcmd_memorysize.value()._size); + + configure._sample_threads.set_is_set(_dcmd_sample_threads.is_set()); + configure._sample_threads.set_value(_dcmd_sample_threads.value()); + + configure.execute(DCmd_Source_Internal, THREAD); + + if (HAS_PENDING_EXCEPTION) { + java_lang_Throwable::print(PENDING_EXCEPTION, tty); + CLEAR_PENDING_EXCEPTION; + return false; + } + return true; +} + +template +static julong divide_with_user_unit(Argument& memory_argument, julong value) { + if (memory_argument.value()._size != memory_argument.value()._val) { + switch (memory_argument.value()._multiplier) { + case 'k': case 'K': + return value / K; + case 'm': case 'M': + return value / M; + case 'g': case 'G': + return value / G; + } + } + return value; +} + +template +static void log_lower_than_min_value(Argument& memory_argument, julong min_value) { + if (memory_argument.value()._size != memory_argument.value()._val) { + // has multiplier + log_error(arguments) ( + "This value is lower than the minimum size required " JULONG_FORMAT "%c", + divide_with_user_unit(memory_argument, min_value), + memory_argument.value()._multiplier); + return; + } + log_error(arguments) ( + "This value is lower than the minimum size required " JULONG_FORMAT, + divide_with_user_unit(memory_argument, min_value)); +} + +template +static void log_set_value(Argument& memory_argument) { + if (memory_argument.value()._size != memory_argument.value()._val) { + // has multiplier + log_error(arguments) ( + "Value specified for option \"%s\" is " JULONG_FORMAT "%c", + memory_argument.name(), + memory_argument.value()._val, + memory_argument.value()._multiplier); + return; + } + log_error(arguments) ( + "Value specified for option \"%s\" is " JULONG_FORMAT, + memory_argument.name(), memory_argument.value()._val); +} + +template +static void log_adjustments(MemoryArg& original_memory_size, julong new_memory_size, const char* msg) { + log_trace(arguments) ( + "%s size (original) " JULONG_FORMAT " B (user defined: %s)", + msg, + original_memory_size.value()._size, + original_memory_size.is_set() ? "true" : "false"); + log_trace(arguments) ( + "%s size (adjusted) " JULONG_FORMAT " B (modified: %s)", + msg, + new_memory_size, + original_memory_size.value()._size != new_memory_size ? "true" : "false"); + log_trace(arguments) ( + "%s size (adjustment) %s" JULONG_FORMAT " B", + msg, + new_memory_size < original_memory_size.value()._size ? "-" : "+", + new_memory_size < original_memory_size.value()._size ? + original_memory_size.value()._size - new_memory_size : + new_memory_size - original_memory_size.value()._size); +} + +// All "triangular" options are explicitly set +// check that they are congruent and not causing +// an ambiguous situtation +template +static bool check_for_ambiguity(MemoryArg& memory_size, MemoryArg& global_buffer_size, NumberArg& num_global_buffers) { + assert(memory_size.is_set(), "invariant"); + assert(global_buffer_size.is_set(), "invariant"); + assert(num_global_buffers.is_set(), "invariant"); + const julong calc_size = global_buffer_size.value()._size * (julong)num_global_buffers.value(); + if (calc_size != memory_size.value()._size) { + // ambiguous + log_set_value(global_buffer_size); + log_error(arguments) ( + "Value specified for option \"%s\" is " JLONG_FORMAT, + num_global_buffers.name(), num_global_buffers.value()); + log_set_value(memory_size); + log_error(arguments) ( + "These values are causing an ambiguity when trying to determine how much memory to use"); + log_error(arguments) ("\"%s\" * \"%s\" do not equal \"%s\"", + global_buffer_size.name(), + num_global_buffers.name(), + memory_size.name()); + log_error(arguments) ( + "Try to remove one of the involved options or make sure they are unambigous"); + return false; + } + return true; +} + +template +static bool ensure_minimum_count(Argument& buffer_count_argument, jlong min_count) { + if (buffer_count_argument.value() < min_count) { + log_error(arguments) ( + "Value specified for option \"%s\" is " JLONG_FORMAT, + buffer_count_argument.name(), buffer_count_argument.value()); + log_error(arguments) ( + "This value is lower than the minimum required number " JLONG_FORMAT, + min_count); + return false; + } + return true; +} + +// global buffer size and num global buffers specified +// ensure that particular combination to be ihigher than minimum memory size +template +static bool ensure_calculated_gteq(MemoryArg& global_buffer_size, NumberArg& num_global_buffers, julong min_value) { + assert(global_buffer_size.is_set(), "invariant"); + assert(num_global_buffers.is_set(), "invariant"); + const julong calc_size = global_buffer_size.value()._size * (julong)num_global_buffers.value(); + if (calc_size < min_value) { + log_set_value(global_buffer_size); + log_error(arguments) ( + "Value specified for option \"%s\" is " JLONG_FORMAT, + num_global_buffers.name(), num_global_buffers.value()); + log_error(arguments) ("\"%s\" * \"%s\" (" JULONG_FORMAT + ") is lower than minimum memory size required " JULONG_FORMAT, + global_buffer_size.name(), + num_global_buffers.name(), + calc_size, + min_value); + return false; + } + return true; +} + +template +static bool ensure_first_gteq_second(Argument& first_argument, Argument& second_argument) { + if (second_argument.value()._size > first_argument.value()._size) { + log_set_value(first_argument); + log_set_value(second_argument); + log_error(arguments) ( + "The value for option \"%s\" should not be larger than the value specified for option \"%s\"", + second_argument.name(), first_argument.name()); + return false; + } + return true; +} + +static bool valid_memory_relations(const JfrMemoryOptions& options) { + if (options.global_buffer_size_configured) { + if (options.memory_size_configured) { + if (!ensure_first_gteq_second(_dcmd_memorysize, _dcmd_globalbuffersize)) { + return false; + } + } + if (options.thread_buffer_size_configured) { + if (!ensure_first_gteq_second(_dcmd_globalbuffersize, _dcmd_threadbuffersize)) { + return false; + } + } + if (options.buffer_count_configured) { + if (!ensure_calculated_gteq(_dcmd_globalbuffersize, _dcmd_numglobalbuffers, MIN_MEMORY_SIZE)) { + return false; + } + } + } + return true; +} + +static void post_process_adjusted_memory_options(const JfrMemoryOptions& options) { + assert(options.memory_size >= MIN_MEMORY_SIZE, "invariant"); + assert(options.global_buffer_size >= MIN_GLOBAL_BUFFER_SIZE, "invariant"); + assert(options.buffer_count >= MIN_BUFFER_COUNT, "invariant"); + assert(options.thread_buffer_size >= MIN_THREAD_BUFFER_SIZE, "invariant"); + log_adjustments(_dcmd_memorysize, options.memory_size, "Memory"); + log_adjustments(_dcmd_globalbuffersize, options.global_buffer_size, "Global buffer"); + log_adjustments(_dcmd_threadbuffersize, options.thread_buffer_size, "Thread local buffer"); + log_trace(arguments) ("Number of global buffers (original) " JLONG_FORMAT " (user defined: %s)", + _dcmd_numglobalbuffers.value(), + _dcmd_numglobalbuffers.is_set() ? "true" : "false"); + log_trace(arguments) ( "Number of global buffers (adjusted) " JULONG_FORMAT " (modified: %s)", + options.buffer_count, + _dcmd_numglobalbuffers.value() != (jlong)options.buffer_count ? "true" : "false"); + log_trace(arguments) ("Number of global buffers (adjustment) %s" JLONG_FORMAT, + (jlong)options.buffer_count < _dcmd_numglobalbuffers.value() ? "" : "+", + (jlong)options.buffer_count - _dcmd_numglobalbuffers.value()); + + MemorySizeArgument adjusted_memory_size; + adjusted_memory_size._val = divide_with_user_unit(_dcmd_memorysize, options.memory_size); + adjusted_memory_size._multiplier = _dcmd_memorysize.value()._multiplier; + adjusted_memory_size._size = options.memory_size; + + MemorySizeArgument adjusted_global_buffer_size; + adjusted_global_buffer_size._val = divide_with_user_unit(_dcmd_globalbuffersize, options.global_buffer_size); + adjusted_global_buffer_size._multiplier = _dcmd_globalbuffersize.value()._multiplier; + adjusted_global_buffer_size._size = options.global_buffer_size; + + MemorySizeArgument adjusted_thread_buffer_size; + adjusted_thread_buffer_size._val = divide_with_user_unit(_dcmd_threadbuffersize, options.thread_buffer_size); + adjusted_thread_buffer_size._multiplier = _dcmd_threadbuffersize.value()._multiplier; + adjusted_thread_buffer_size._size = options.thread_buffer_size; + + // store back to dcmd + _dcmd_memorysize.set_value(adjusted_memory_size); + _dcmd_memorysize.set_is_set(true); + _dcmd_globalbuffersize.set_value(adjusted_global_buffer_size); + _dcmd_globalbuffersize.set_is_set(true); + _dcmd_numglobalbuffers.set_value((jlong)options.buffer_count); + _dcmd_numglobalbuffers.set_is_set(true); + _dcmd_threadbuffersize.set_value(adjusted_thread_buffer_size); + _dcmd_threadbuffersize.set_is_set(true); +} + +static void initialize_memory_options_from_dcmd(JfrMemoryOptions& options) { + options.memory_size = _dcmd_memorysize.value()._size; + options.global_buffer_size = MAX2(_dcmd_globalbuffersize.value()._size, (julong)os::vm_page_size()); + options.buffer_count = (julong)_dcmd_numglobalbuffers.value(); + options.thread_buffer_size = MAX2(_dcmd_threadbuffersize.value()._size, (julong)os::vm_page_size()); + // determine which options have been explicitly set + options.memory_size_configured = _dcmd_memorysize.is_set(); + options.global_buffer_size_configured = _dcmd_globalbuffersize.is_set(); + options.buffer_count_configured = _dcmd_numglobalbuffers.is_set(); + options.thread_buffer_size_configured = _dcmd_threadbuffersize.is_set(); + assert(options.memory_size >= MIN_MEMORY_SIZE, "invariant"); + assert(options.global_buffer_size >= MIN_GLOBAL_BUFFER_SIZE, "invariant"); + assert(options.buffer_count >= MIN_BUFFER_COUNT, "invariant"); + assert(options.thread_buffer_size >= MIN_THREAD_BUFFER_SIZE, "invariant"); +} + +template +static bool ensure_gteq(Argument& memory_argument, const jlong value) { + if ((jlong)memory_argument.value()._size < value) { + log_set_value(memory_argument); + log_lower_than_min_value(memory_argument, value); + return false; + } + return true; +} + +static bool ensure_valid_minimum_sizes() { + // ensure valid minimum memory sizes + if (_dcmd_memorysize.is_set()) { + if (!ensure_gteq(_dcmd_memorysize, MIN_MEMORY_SIZE)) { + return false; + } + } + if (_dcmd_globalbuffersize.is_set()) { + if (!ensure_gteq(_dcmd_globalbuffersize, MIN_GLOBAL_BUFFER_SIZE)) { + return false; + } + } + if (_dcmd_numglobalbuffers.is_set()) { + if (!ensure_minimum_count(_dcmd_numglobalbuffers, MIN_BUFFER_COUNT)) { + return false; + } + } + if (_dcmd_threadbuffersize.is_set()) { + if (!ensure_gteq(_dcmd_threadbuffersize, MIN_THREAD_BUFFER_SIZE)) { + return false; + } + } + return true; +} + +/** + * Starting with the initial set of memory values from the user, + * sanitize, enforce min/max rules and adjust to a set of consistent options. + * + * Adjusted memory sizes will be page aligned. + */ +bool JfrOptionSet::adjust_memory_options() { + if (!ensure_valid_minimum_sizes()) { + return false; + } + JfrMemoryOptions options; + initialize_memory_options_from_dcmd(options); + if (!valid_memory_relations(options)) { + return false; + } + if (!JfrMemorySizer::adjust_options(&options)) { + if (!check_for_ambiguity(_dcmd_memorysize, _dcmd_globalbuffersize, _dcmd_numglobalbuffers)) { + return false; + } + } + post_process_adjusted_memory_options(options); + return true; +} + +/* + +to support starting multiple startup recordings + +static const char* start_flight_recording_option_original = NULL; +static const char* flight_recorder_option_original = NULL; + +static void copy_option_string(const JavaVMOption* option, const char** addr) { + assert(option != NULL, "invariant"); + assert(option->optionString != NULL, "invariant"); + const size_t length = strlen(option->optionString); + *addr = JfrCHeapObj::new_array(length + 1); + assert(*addr != NULL, "invarinat"); + strncpy((char*)*addr, option->optionString, length + 1); + assert(strncmp(*addr, option->optionString, length + 1) == 0, "invariant"); +} + +copy_option_string(*option, &start_flight_recording_option_original); +copy_option_string(*option, &flight_recorder_option_original); +*/ + +bool JfrOptionSet::parse_start_flight_recording(const JavaVMOption** option, char* tail) { + assert(option != NULL, "invariant"); + assert(tail != NULL, "invariant"); + assert((*option)->optionString != NULL, "invariant"); + assert(strncmp((*option)->optionString, "-XX:StartFlightRecording", 24) == 0, "invariant"); + if (*tail == '\0') { + // Add dummy dumponexit=false so -XX:StartFlightRecording can be used without a parameter. + // The existing option->optionString points to stack memory so no need to deallocate. + const_cast(*option)->optionString = (char*)"-XX:StartFlightRecording=dumponexit=false"; + } else { + *tail = '='; // ":" -> "=" + } + return false; +} + +bool JfrOptionSet::parse_flight_recorder_options(const JavaVMOption** option, char* tail) { + assert(option != NULL, "invariant"); + assert(tail != NULL, "invariant"); + assert((*option)->optionString != NULL, "invariant"); + assert(strncmp((*option)->optionString, "-XX:FlightRecorderOptions", 25) == 0, "invariant"); + if (tail != NULL) { + *tail = '='; // ":" -> "=" + } + return false; +} +