src/hotspot/share/jfr/recorder/service/jfrOptionSet.cpp
changeset 50113 caf115bb98ad
child 50664 857ce291c70c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/jfr/recorder/service/jfrOptionSet.cpp	Tue May 15 20:24:34 2018 +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/service/jfrMemorySizer.hpp"
+#include "jfr/recorder/service/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<char*> _dcmd_repository(
+  "repository",
+  "Flight recorder disk repository location",
+  "STRING",
+  false,
+  default_repository);
+
+static DCmdArgument<MemorySizeArgument> _dcmd_threadbuffersize(
+  "threadbuffersize",
+  "Thread buffer size",
+  "MEMORY SIZE",
+  false,
+  default_thread_buffer_size);
+
+static DCmdArgument<MemorySizeArgument> _dcmd_memorysize(
+  "memorysize",
+  "Size of memory to be used by Flight Recorder",
+  "MEMORY SIZE",
+  false,
+  default_memory_size);
+
+static DCmdArgument<MemorySizeArgument> _dcmd_globalbuffersize(
+  "globalbuffersize",
+  "Global buffer size",
+  "MEMORY SIZE",
+  false,
+  default_global_buffer_size);
+
+static DCmdArgument<jlong> _dcmd_numglobalbuffers(
+  "numglobalbuffers",
+  "Number of global buffers",
+  "JULONG",
+  false,
+  default_num_global_buffers);
+
+static DCmdArgument<MemorySizeArgument> _dcmd_maxchunksize(
+  "maxchunksize",
+  "Maximum size of a single repository disk chunk",
+  "MEMORY SIZE",
+  false,
+  default_max_chunk_size);
+
+static DCmdArgument<jlong> _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<bool> _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<bool> _dcmd_sample_protection(
+  "sampleprotection",
+  "Safeguard for stackwalking while sampling threads (false by default)",
+  "BOOLEAN",
+  false,
+  default_sample_protection);
+#endif
+
+static DCmdArgument<jlong> _dcmd_stackdepth(
+  "stackdepth",
+  "Stack depth for stacktraces (minimum 1, maximum 2048)",
+  "JULONG",
+  false,
+  default_stack_depth);
+
+static DCmdArgument<bool> _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<char>(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 <typename Argument>
+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 <typename Argument>
+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 <typename Argument>
+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 <typename MemoryArg>
+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 <typename MemoryArg, typename NumberArg>
+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 <typename Argument>
+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 <typename MemoryArg, typename NumberArg>
+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 <typename Argument>
+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<julong>(_dcmd_globalbuffersize.value()._size, (julong)os::vm_page_size());
+  options.buffer_count = (julong)_dcmd_numglobalbuffers.value();
+  options.thread_buffer_size = MAX2<julong>(_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 <typename Argument>
+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<char>(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_option(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<JavaVMOption*>(*option)->optionString = (char*)"-XX:StartFlightRecording=dumponexit=false";
+  } else {
+    *tail = '='; // ":" -> "="
+  }
+  return false;
+}
+
+bool JfrOptionSet::parse_flight_recorder_option(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;
+}
+