1 /*
   2  * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 #include "precompiled.hpp"
  26 #include "jfr/dcmd/jfrDcmds.hpp"
  27 #include "jfr/recorder/service/jfrMemorySizer.hpp"
  28 #include "jfr/recorder/service/jfrOptionSet.hpp"
  29 #include "jfr/utilities/jfrAllocation.hpp"
  30 #include "logging/log.hpp"
  31 #include "memory/allocation.inline.hpp"
  32 #include "memory/resourceArea.hpp"
  33 #include "runtime/java.hpp"
  34 #include "runtime/thread.inline.hpp"
  35 #include "services/diagnosticArgument.hpp"
  36 #include "services/diagnosticFramework.hpp"
  37 #include "utilities/growableArray.hpp"
  38 #include "utilities/ostream.hpp"
  39 
  40 struct ObsoleteOption {
  41   const char* name;
  42   const char* message;
  43 };
  44 
  45 static const ObsoleteOption OBSOLETE_OPTIONS[] = {
  46   {"checkpointbuffersize", ""},
  47   {"maxsize",              "Use -XX:StartFlightRecording=maxsize=... instead."},
  48   {"maxage",               "Use -XX:StartFlightRecording=maxage=... instead."},
  49   {"settings",             "Use -XX:StartFlightRecording=settings=... instead."},
  50   {"defaultrecording",     "Use -XX:StartFlightRecording=disk=false to create an in-memory recording."},
  51   {"disk",                 "Use -XX:StartFlightRecording=disk=... instead."},
  52   {"dumponexit",           "Use -XX:StartFlightRecording=dumponexit=... instead."},
  53   {"dumponexitpath",       "Use -XX:StartFlightRecording=filename=... instead."},
  54   {"loglevel",             "Use -Xlog:jfr=... instead."}
  55 };
  56 
  57 jlong JfrOptionSet::max_chunk_size() {
  58   return _max_chunk_size;
  59 }
  60 
  61 void JfrOptionSet::set_max_chunk_size(jlong value) {
  62   _max_chunk_size = value;
  63 }
  64 
  65 jlong JfrOptionSet::global_buffer_size() {
  66   return _global_buffer_size;
  67 }
  68 
  69 void JfrOptionSet::set_global_buffer_size(jlong value) {
  70   _global_buffer_size = value;
  71 }
  72 
  73 jlong JfrOptionSet::thread_buffer_size() {
  74   return _thread_buffer_size;
  75 }
  76 
  77 void JfrOptionSet::set_thread_buffer_size(jlong value) {
  78   _thread_buffer_size = value;
  79 }
  80 
  81 jlong JfrOptionSet::memory_size() {
  82   return _memory_size;
  83 }
  84 
  85 void JfrOptionSet::set_memory_size(jlong value) {
  86   _memory_size = value;
  87 }
  88 
  89 jlong JfrOptionSet::num_global_buffers() {
  90   return _num_global_buffers;
  91 }
  92 
  93 void JfrOptionSet::set_num_global_buffers(jlong value) {
  94   _num_global_buffers = value;
  95 }
  96 
  97 jint JfrOptionSet::old_object_queue_size() {
  98   return (jint)_old_object_queue_size;
  99 }
 100 
 101 void JfrOptionSet::set_old_object_queue_size(jlong value) {
 102   _old_object_queue_size = value;
 103 }
 104 
 105 u4 JfrOptionSet::stackdepth() {
 106   return _stack_depth;
 107 }
 108 
 109 void JfrOptionSet::set_stackdepth(u4 depth) {
 110   if (depth < MIN_STACK_DEPTH) {
 111     _stack_depth = MIN_STACK_DEPTH;
 112   } else if (depth > MAX_STACK_DEPTH) {
 113     _stack_depth = MAX_STACK_DEPTH;
 114   } else {
 115     _stack_depth = depth;
 116   }
 117 }
 118 
 119 bool JfrOptionSet::sample_threads() {
 120   return _sample_threads == JNI_TRUE;
 121 }
 122 
 123 void JfrOptionSet::set_sample_threads(jboolean sample) {
 124   _sample_threads = sample;
 125 }
 126 
 127 bool JfrOptionSet::can_retransform() {
 128   return _retransform == JNI_TRUE;
 129 }
 130 
 131 void JfrOptionSet::set_retransform(jboolean value) {
 132   _retransform = value;
 133 }
 134 
 135 bool JfrOptionSet::sample_protection() {
 136   return _sample_protection == JNI_TRUE;
 137 }
 138 
 139 #ifdef ASSERT
 140 void JfrOptionSet::set_sample_protection(jboolean protection) {
 141   _sample_protection = protection;
 142 }
 143 #endif
 144 
 145 bool JfrOptionSet::compressed_integers() {
 146   // Set this to false for debugging purposes.
 147   return true;
 148 }
 149 
 150 bool JfrOptionSet::allow_retransforms() {
 151 #if INCLUDE_JVMTI
 152   return true;
 153 #else
 154   return false;
 155 #endif
 156 }
 157 
 158 bool JfrOptionSet::allow_event_retransforms() {
 159   return allow_retransforms() && (DumpSharedSpaces || can_retransform());
 160 }
 161 
 162 // default options for the dcmd parser
 163 const char* const default_repository = NULL;
 164 const char* const default_global_buffer_size = "512k";
 165 const char* const default_num_global_buffers = "20";
 166 const char* const default_memory_size = "10m";
 167 const char* const default_thread_buffer_size = "8k";
 168 const char* const default_max_chunk_size = "12m";
 169 const char* const default_sample_threads = "true";
 170 const char* const default_stack_depth = "64";
 171 const char* const default_retransform = "true";
 172 const char* const default_old_object_queue_size = "256";
 173 DEBUG_ONLY(const char* const default_sample_protection = "false";)
 174 
 175 // statics
 176 static DCmdArgument<char*> _dcmd_repository(
 177   "repository",
 178   "Flight recorder disk repository location",
 179   "STRING",
 180   false,
 181   default_repository);
 182 
 183 static DCmdArgument<MemorySizeArgument> _dcmd_threadbuffersize(
 184   "threadbuffersize",
 185   "Thread buffer size",
 186   "MEMORY SIZE",
 187   false,
 188   default_thread_buffer_size);
 189 
 190 static DCmdArgument<MemorySizeArgument> _dcmd_memorysize(
 191   "memorysize",
 192   "Size of memory to be used by Flight Recorder",
 193   "MEMORY SIZE",
 194   false,
 195   default_memory_size);
 196 
 197 static DCmdArgument<MemorySizeArgument> _dcmd_globalbuffersize(
 198   "globalbuffersize",
 199   "Global buffer size",
 200   "MEMORY SIZE",
 201   false,
 202   default_global_buffer_size);
 203 
 204 static DCmdArgument<jlong> _dcmd_numglobalbuffers(
 205   "numglobalbuffers",
 206   "Number of global buffers",
 207   "JULONG",
 208   false,
 209   default_num_global_buffers);
 210 
 211 static DCmdArgument<MemorySizeArgument> _dcmd_maxchunksize(
 212   "maxchunksize",
 213   "Maximum size of a single repository disk chunk",
 214   "MEMORY SIZE",
 215   false,
 216   default_max_chunk_size);
 217 
 218 static DCmdArgument<jlong> _dcmd_old_object_queue_size (
 219   "old-object-queue-size",
 220   "Maximum number of old objects to track",
 221   "JINT",
 222   false,
 223   default_old_object_queue_size);
 224 
 225 static DCmdArgument<bool> _dcmd_sample_threads(
 226   "samplethreads",
 227   "Thread sampling enable / disable (only sampling when event enabled and sampling enabled)",
 228   "BOOLEAN",
 229   false,
 230   default_sample_threads);
 231 
 232 #ifdef ASSERT
 233 static DCmdArgument<bool> _dcmd_sample_protection(
 234   "sampleprotection",
 235   "Safeguard for stackwalking while sampling threads (false by default)",
 236   "BOOLEAN",
 237   false,
 238   default_sample_protection);
 239 #endif
 240 
 241 static DCmdArgument<jlong> _dcmd_stackdepth(
 242   "stackdepth",
 243   "Stack depth for stacktraces (minimum 1, maximum 2048)",
 244   "JULONG",
 245   false,
 246   default_stack_depth);
 247 
 248 static DCmdArgument<bool> _dcmd_retransform(
 249   "retransform",
 250   "If event classes should be instrumented using JVMTI (by default true)",
 251   "BOOLEAN",
 252   true,
 253   default_retransform);
 254 
 255 static DCmdParser _parser;
 256 
 257 static void register_parser_options() {
 258   _parser.add_dcmd_option(&_dcmd_repository);
 259   _parser.add_dcmd_option(&_dcmd_threadbuffersize);
 260   _parser.add_dcmd_option(&_dcmd_memorysize);
 261   _parser.add_dcmd_option(&_dcmd_globalbuffersize);
 262   _parser.add_dcmd_option(&_dcmd_numglobalbuffers);
 263   _parser.add_dcmd_option(&_dcmd_maxchunksize);
 264   _parser.add_dcmd_option(&_dcmd_stackdepth);
 265   _parser.add_dcmd_option(&_dcmd_sample_threads);
 266   _parser.add_dcmd_option(&_dcmd_retransform);
 267   _parser.add_dcmd_option(&_dcmd_old_object_queue_size);
 268   DEBUG_ONLY(_parser.add_dcmd_option(&_dcmd_sample_protection);)
 269 }
 270 
 271 static bool parse_flight_recorder_options_internal(TRAPS) {
 272   if (FlightRecorderOptions == NULL) {
 273     return true;
 274   }
 275   const size_t length = strlen((const char*)FlightRecorderOptions);
 276   CmdLine cmdline((const char*)FlightRecorderOptions, length, true);
 277   _parser.parse(&cmdline, ',', THREAD);
 278   if (HAS_PENDING_EXCEPTION) {
 279     for (int index = 0; index < 9; index++) {
 280       ObsoleteOption option = OBSOLETE_OPTIONS[index];
 281       const char* p = strstr((const char*)FlightRecorderOptions, option.name);
 282       const size_t option_length = strlen(option.name);
 283       if (p != NULL && p[option_length] == '=') {
 284         log_error(arguments) ("-XX:FlightRecorderOptions=%s=... has been removed. %s", option.name, option.message);
 285         return false;
 286       }
 287     }
 288     ResourceMark rm(THREAD);
 289     oop message = java_lang_Throwable::message(PENDING_EXCEPTION);
 290     if (message != NULL) {
 291       const char* msg = java_lang_String::as_utf8_string(message);
 292       log_error(arguments) ("%s", msg);
 293     }
 294     CLEAR_PENDING_EXCEPTION;
 295     return false;
 296   }
 297   return true;
 298 }
 299 
 300 jlong JfrOptionSet::_max_chunk_size = 0;
 301 jlong JfrOptionSet::_global_buffer_size = 0;
 302 jlong JfrOptionSet::_thread_buffer_size = 0;
 303 jlong JfrOptionSet::_memory_size = 0;
 304 jlong JfrOptionSet::_num_global_buffers = 0;
 305 jlong JfrOptionSet::_old_object_queue_size = 0;
 306 u4 JfrOptionSet::_stack_depth = STACK_DEPTH_DEFAULT;
 307 jboolean JfrOptionSet::_sample_threads = JNI_TRUE;
 308 jboolean JfrOptionSet::_retransform = JNI_TRUE;
 309 #ifdef ASSERT
 310 jboolean JfrOptionSet::_sample_protection = JNI_FALSE;
 311 #else
 312 jboolean JfrOptionSet::_sample_protection = JNI_TRUE;
 313 #endif
 314 
 315 bool JfrOptionSet::initialize(Thread* thread) {
 316   register_parser_options();
 317   if (!parse_flight_recorder_options_internal(thread)) {
 318     return false;
 319   }
 320   if (_dcmd_retransform.is_set()) {
 321     set_retransform(_dcmd_retransform.value());
 322   }
 323   set_old_object_queue_size(_dcmd_old_object_queue_size.value());
 324   return adjust_memory_options();
 325 }
 326 
 327 bool JfrOptionSet::configure(TRAPS) {
 328   if (FlightRecorderOptions == NULL) {
 329     return true;
 330   }
 331   ResourceMark rm(THREAD);
 332   bufferedStream st;
 333   // delegate to DCmd execution
 334   JfrConfigureFlightRecorderDCmd configure(&st, false);
 335   configure._repository_path.set_is_set(_dcmd_repository.is_set());
 336   char* repo = _dcmd_repository.value();
 337   if (repo != NULL) {
 338     const size_t len = strlen(repo);
 339     char* repo_copy = JfrCHeapObj::new_array<char>(len + 1);
 340     if (NULL == repo_copy) {
 341       return false;
 342     }
 343     strncpy(repo_copy, repo, len + 1);
 344     configure._repository_path.set_value(repo_copy);
 345   }
 346 
 347   configure._stack_depth.set_is_set(_dcmd_stackdepth.is_set());
 348   configure._stack_depth.set_value(_dcmd_stackdepth.value());
 349 
 350   configure._thread_buffer_size.set_is_set(_dcmd_threadbuffersize.is_set());
 351   configure._thread_buffer_size.set_value(_dcmd_threadbuffersize.value());
 352 
 353   configure._global_buffer_count.set_is_set(_dcmd_numglobalbuffers.is_set());
 354   configure._global_buffer_count.set_value(_dcmd_numglobalbuffers.value());
 355 
 356   configure._global_buffer_size.set_is_set(_dcmd_globalbuffersize.is_set());
 357   configure._global_buffer_size.set_value(_dcmd_globalbuffersize.value());
 358 
 359   configure._max_chunk_size.set_is_set(_dcmd_maxchunksize.is_set());
 360   configure._max_chunk_size.set_value(_dcmd_maxchunksize.value());
 361 
 362   configure._memory_size.set_is_set(_dcmd_memorysize.is_set());
 363   configure._memory_size.set_value(_dcmd_memorysize.value());
 364 
 365   configure._sample_threads.set_is_set(_dcmd_sample_threads.is_set());
 366   configure._sample_threads.set_value(_dcmd_sample_threads.value());
 367 
 368   configure.execute(DCmd_Source_Internal, THREAD);
 369 
 370   if (HAS_PENDING_EXCEPTION) {
 371     java_lang_Throwable::print(PENDING_EXCEPTION, tty);
 372     CLEAR_PENDING_EXCEPTION;
 373     return false;
 374   }
 375   return true;
 376 }
 377 
 378 template <typename Argument>
 379 static julong divide_with_user_unit(Argument& memory_argument, julong value) {
 380   if (memory_argument.value()._size != memory_argument.value()._val) {
 381     switch (memory_argument.value()._multiplier) {
 382     case 'k': case 'K':
 383       return value / K;
 384     case 'm': case 'M':
 385       return value / M;
 386     case 'g': case 'G':
 387       return value / G;
 388     }
 389   }
 390   return value;
 391 }
 392 
 393 template <typename Argument>
 394 static void log_lower_than_min_value(Argument& memory_argument, julong min_value) {
 395   if (memory_argument.value()._size != memory_argument.value()._val) {
 396     // has multiplier
 397     log_error(arguments) (
 398       "This value is lower than the minimum size required " JULONG_FORMAT "%c",
 399       divide_with_user_unit(memory_argument, min_value),
 400       memory_argument.value()._multiplier);
 401     return;
 402   }
 403   log_error(arguments) (
 404     "This value is lower than the minimum size required " JULONG_FORMAT,
 405     divide_with_user_unit(memory_argument, min_value));
 406 }
 407 
 408 template <typename Argument>
 409 static void log_set_value(Argument& memory_argument) {
 410   if (memory_argument.value()._size != memory_argument.value()._val) {
 411     // has multiplier
 412     log_error(arguments) (
 413       "Value specified for option \"%s\" is " JULONG_FORMAT "%c",
 414       memory_argument.name(),
 415       memory_argument.value()._val,
 416       memory_argument.value()._multiplier);
 417     return;
 418   }
 419   log_error(arguments) (
 420     "Value specified for option \"%s\" is " JULONG_FORMAT,
 421     memory_argument.name(), memory_argument.value()._val);
 422 }
 423 
 424 template <typename MemoryArg>
 425 static void log_adjustments(MemoryArg& original_memory_size, julong new_memory_size, const char* msg) {
 426   log_trace(arguments) (
 427     "%s size (original) " JULONG_FORMAT " B (user defined: %s)",
 428     msg,
 429     original_memory_size.value()._size,
 430     original_memory_size.is_set() ? "true" : "false");
 431   log_trace(arguments) (
 432     "%s size (adjusted) " JULONG_FORMAT " B (modified: %s)",
 433     msg,
 434     new_memory_size,
 435     original_memory_size.value()._size != new_memory_size ? "true" : "false");
 436   log_trace(arguments) (
 437     "%s size (adjustment) %s" JULONG_FORMAT " B",
 438     msg,
 439     new_memory_size < original_memory_size.value()._size ? "-" : "+",
 440     new_memory_size < original_memory_size.value()._size ?
 441     original_memory_size.value()._size - new_memory_size :
 442     new_memory_size - original_memory_size.value()._size);
 443 }
 444 
 445 // All "triangular" options are explicitly set
 446 // check that they are congruent and not causing
 447 // an ambiguous situtation
 448 template <typename MemoryArg, typename NumberArg>
 449 static bool check_for_ambiguity(MemoryArg& memory_size, MemoryArg& global_buffer_size, NumberArg& num_global_buffers) {
 450   assert(memory_size.is_set(), "invariant");
 451   assert(global_buffer_size.is_set(), "invariant");
 452   assert(num_global_buffers.is_set(), "invariant");
 453   const julong calc_size = global_buffer_size.value()._size * (julong)num_global_buffers.value();
 454   if (calc_size != memory_size.value()._size) {
 455     // ambiguous
 456     log_set_value(global_buffer_size);
 457     log_error(arguments) (
 458       "Value specified for option \"%s\" is " JLONG_FORMAT,
 459       num_global_buffers.name(), num_global_buffers.value());
 460     log_set_value(memory_size);
 461     log_error(arguments) (
 462       "These values are causing an ambiguity when trying to determine how much memory to use");
 463     log_error(arguments) ("\"%s\" * \"%s\" do not equal \"%s\"",
 464       global_buffer_size.name(),
 465       num_global_buffers.name(),
 466       memory_size.name());
 467     log_error(arguments) (
 468       "Try to remove one of the involved options or make sure they are unambigous");
 469     return false;
 470   }
 471   return true;
 472 }
 473 
 474 template <typename Argument>
 475 static bool ensure_minimum_count(Argument& buffer_count_argument, jlong min_count) {
 476   if (buffer_count_argument.value() < min_count) {
 477     log_error(arguments) (
 478       "Value specified for option \"%s\" is " JLONG_FORMAT,
 479       buffer_count_argument.name(), buffer_count_argument.value());
 480     log_error(arguments) (
 481       "This value is lower than the minimum required number " JLONG_FORMAT,
 482       min_count);
 483     return false;
 484   }
 485   return true;
 486 }
 487 
 488 // global buffer size and num global buffers specified
 489 // ensure that particular combination to be ihigher than minimum memory size
 490 template <typename MemoryArg, typename NumberArg>
 491 static bool ensure_calculated_gteq(MemoryArg& global_buffer_size, NumberArg& num_global_buffers, julong min_value) {
 492   assert(global_buffer_size.is_set(), "invariant");
 493   assert(num_global_buffers.is_set(), "invariant");
 494   const julong calc_size = global_buffer_size.value()._size * (julong)num_global_buffers.value();
 495   if (calc_size < min_value) {
 496     log_set_value(global_buffer_size);
 497     log_error(arguments) (
 498       "Value specified for option \"%s\" is " JLONG_FORMAT,
 499       num_global_buffers.name(), num_global_buffers.value());
 500     log_error(arguments) ("\"%s\" * \"%s\" (" JULONG_FORMAT
 501       ") is lower than minimum memory size required " JULONG_FORMAT,
 502       global_buffer_size.name(),
 503       num_global_buffers.name(),
 504       calc_size,
 505       min_value);
 506     return false;
 507   }
 508   return true;
 509 }
 510 
 511 template <typename Argument>
 512 static bool ensure_first_gteq_second(Argument& first_argument, Argument& second_argument) {
 513   if (second_argument.value()._size > first_argument.value()._size) {
 514     log_set_value(first_argument);
 515     log_set_value(second_argument);
 516     log_error(arguments) (
 517       "The value for option \"%s\" should not be larger than the value specified for option \"%s\"",
 518       second_argument.name(), first_argument.name());
 519     return false;
 520   }
 521   return true;
 522 }
 523 
 524 static bool valid_memory_relations(const JfrMemoryOptions& options) {
 525   if (options.global_buffer_size_configured) {
 526     if (options.memory_size_configured) {
 527       if (!ensure_first_gteq_second(_dcmd_memorysize, _dcmd_globalbuffersize)) {
 528         return false;
 529       }
 530     }
 531     if (options.thread_buffer_size_configured) {
 532       if (!ensure_first_gteq_second(_dcmd_globalbuffersize, _dcmd_threadbuffersize)) {
 533         return false;
 534       }
 535     }
 536     if (options.buffer_count_configured) {
 537       if (!ensure_calculated_gteq(_dcmd_globalbuffersize, _dcmd_numglobalbuffers, MIN_MEMORY_SIZE)) {
 538         return false;
 539       }
 540     }
 541   }
 542   return true;
 543 }
 544 
 545 static void post_process_adjusted_memory_options(const JfrMemoryOptions& options) {
 546   assert(options.memory_size >= MIN_MEMORY_SIZE, "invariant");
 547   assert(options.global_buffer_size >= MIN_GLOBAL_BUFFER_SIZE, "invariant");
 548   assert(options.buffer_count >= MIN_BUFFER_COUNT, "invariant");
 549   assert(options.thread_buffer_size >= MIN_THREAD_BUFFER_SIZE, "invariant");
 550   log_adjustments(_dcmd_memorysize, options.memory_size, "Memory");
 551   log_adjustments(_dcmd_globalbuffersize, options.global_buffer_size, "Global buffer");
 552   log_adjustments(_dcmd_threadbuffersize, options.thread_buffer_size, "Thread local buffer");
 553   log_trace(arguments) ("Number of global buffers (original) " JLONG_FORMAT " (user defined: %s)",
 554     _dcmd_numglobalbuffers.value(),
 555     _dcmd_numglobalbuffers.is_set() ? "true" : "false");
 556   log_trace(arguments) ( "Number of global buffers (adjusted) " JULONG_FORMAT " (modified: %s)",
 557     options.buffer_count,
 558     _dcmd_numglobalbuffers.value() != (jlong)options.buffer_count ? "true" : "false");
 559   log_trace(arguments) ("Number of global buffers (adjustment) %s" JLONG_FORMAT,
 560     (jlong)options.buffer_count < _dcmd_numglobalbuffers.value() ? "" : "+",
 561     (jlong)options.buffer_count - _dcmd_numglobalbuffers.value());
 562 
 563   MemorySizeArgument adjusted_memory_size;
 564   adjusted_memory_size._val = divide_with_user_unit(_dcmd_memorysize, options.memory_size);
 565   adjusted_memory_size._multiplier = _dcmd_memorysize.value()._multiplier;
 566   adjusted_memory_size._size = options.memory_size;
 567 
 568   MemorySizeArgument adjusted_global_buffer_size;
 569   adjusted_global_buffer_size._val = divide_with_user_unit(_dcmd_globalbuffersize, options.global_buffer_size);
 570   adjusted_global_buffer_size._multiplier = _dcmd_globalbuffersize.value()._multiplier;
 571   adjusted_global_buffer_size._size = options.global_buffer_size;
 572 
 573   MemorySizeArgument adjusted_thread_buffer_size;
 574   adjusted_thread_buffer_size._val = divide_with_user_unit(_dcmd_threadbuffersize, options.thread_buffer_size);
 575   adjusted_thread_buffer_size._multiplier = _dcmd_threadbuffersize.value()._multiplier;
 576   adjusted_thread_buffer_size._size = options.thread_buffer_size;
 577 
 578   // store back to dcmd
 579   _dcmd_memorysize.set_value(adjusted_memory_size);
 580   _dcmd_memorysize.set_is_set(true);
 581   _dcmd_globalbuffersize.set_value(adjusted_global_buffer_size);
 582   _dcmd_globalbuffersize.set_is_set(true);
 583   _dcmd_numglobalbuffers.set_value((jlong)options.buffer_count);
 584   _dcmd_numglobalbuffers.set_is_set(true);
 585   _dcmd_threadbuffersize.set_value(adjusted_thread_buffer_size);
 586   _dcmd_threadbuffersize.set_is_set(true);
 587 }
 588 
 589 static void initialize_memory_options_from_dcmd(JfrMemoryOptions& options) {
 590   options.memory_size = _dcmd_memorysize.value()._size;
 591   options.global_buffer_size = MAX2<julong>(_dcmd_globalbuffersize.value()._size, (julong)os::vm_page_size());
 592   options.buffer_count = (julong)_dcmd_numglobalbuffers.value();
 593   options.thread_buffer_size = MAX2<julong>(_dcmd_threadbuffersize.value()._size, (julong)os::vm_page_size());
 594   // determine which options have been explicitly set
 595   options.memory_size_configured = _dcmd_memorysize.is_set();
 596   options.global_buffer_size_configured = _dcmd_globalbuffersize.is_set();
 597   options.buffer_count_configured = _dcmd_numglobalbuffers.is_set();
 598   options.thread_buffer_size_configured = _dcmd_threadbuffersize.is_set();
 599   assert(options.memory_size >= MIN_MEMORY_SIZE, "invariant");
 600   assert(options.global_buffer_size >= MIN_GLOBAL_BUFFER_SIZE, "invariant");
 601   assert(options.buffer_count >= MIN_BUFFER_COUNT, "invariant");
 602   assert(options.thread_buffer_size >= MIN_THREAD_BUFFER_SIZE, "invariant");
 603 }
 604 
 605 template <typename Argument>
 606 static bool ensure_gteq(Argument& memory_argument, const jlong value) {
 607   if ((jlong)memory_argument.value()._size < value) {
 608     log_set_value(memory_argument);
 609     log_lower_than_min_value(memory_argument, value);
 610     return false;
 611   }
 612   return true;
 613 }
 614 
 615 static bool ensure_valid_minimum_sizes() {
 616   // ensure valid minimum memory sizes
 617   if (_dcmd_memorysize.is_set()) {
 618     if (!ensure_gteq(_dcmd_memorysize, MIN_MEMORY_SIZE)) {
 619       return false;
 620     }
 621   }
 622   if (_dcmd_globalbuffersize.is_set()) {
 623     if (!ensure_gteq(_dcmd_globalbuffersize, MIN_GLOBAL_BUFFER_SIZE)) {
 624       return false;
 625     }
 626   }
 627   if (_dcmd_numglobalbuffers.is_set()) {
 628     if (!ensure_minimum_count(_dcmd_numglobalbuffers, MIN_BUFFER_COUNT)) {
 629       return false;
 630     }
 631   }
 632   if (_dcmd_threadbuffersize.is_set()) {
 633     if (!ensure_gteq(_dcmd_threadbuffersize, MIN_THREAD_BUFFER_SIZE)) {
 634       return false;
 635     }
 636   }
 637   return true;
 638 }
 639 
 640 /**
 641  * Starting with the initial set of memory values from the user,
 642  * sanitize, enforce min/max rules and adjust to a set of consistent options.
 643  *
 644  * Adjusted memory sizes will be page aligned.
 645  */
 646 bool JfrOptionSet::adjust_memory_options() {
 647   if (!ensure_valid_minimum_sizes()) {
 648     return false;
 649   }
 650   JfrMemoryOptions options;
 651   initialize_memory_options_from_dcmd(options);
 652   if (!valid_memory_relations(options)) {
 653     return false;
 654   }
 655   if (!JfrMemorySizer::adjust_options(&options)) {
 656     if (!check_for_ambiguity(_dcmd_memorysize, _dcmd_globalbuffersize, _dcmd_numglobalbuffers)) {
 657       return false;
 658     }
 659   }
 660   post_process_adjusted_memory_options(options);
 661   return true;
 662 }
 663 
 664 bool JfrOptionSet::parse_flight_recorder_option(const JavaVMOption** option, char* delimiter) {
 665   assert(option != NULL, "invariant");
 666   assert(delimiter != NULL, "invariant");
 667   assert((*option)->optionString != NULL, "invariant");
 668   assert(strncmp((*option)->optionString, "-XX:FlightRecorderOptions", 25) == 0, "invariant");
 669   if (*delimiter == '\0') {
 670     // -XX:FlightRecorderOptions without any delimiter and values
 671   } else {
 672     // -XX:FlightRecorderOptions[=|:]
 673     // set delimiter to '='
 674     *delimiter = '=';
 675   }
 676   return false;
 677 }
 678 
 679 static GrowableArray<const char*>* startup_recording_options_array = NULL;
 680 
 681 bool JfrOptionSet::parse_start_flight_recording_option(const JavaVMOption** option, char* delimiter) {
 682   assert(option != NULL, "invariant");
 683   assert(delimiter != NULL, "invariant");
 684   assert((*option)->optionString != NULL, "invariant");
 685   assert(strncmp((*option)->optionString, "-XX:StartFlightRecording", 24) == 0, "invariant");
 686   const char* value = NULL;
 687   if (*delimiter == '\0') {
 688     // -XX:StartFlightRecording without any delimiter and values
 689     // Add dummy value "dumponexit=false" so -XX:StartFlightRecording can be used without explicit values.
 690     // The existing option->optionString points to stack memory so no need to deallocate.
 691     const_cast<JavaVMOption*>(*option)->optionString = (char*)"-XX:StartFlightRecording=dumponexit=false";
 692     value = (*option)->optionString + 25;
 693   } else {
 694     // -XX:StartFlightRecording[=|:]
 695     // set delimiter to '='
 696     *delimiter = '=';
 697     value = delimiter + 1;
 698   }
 699   assert(value != NULL, "invariant");
 700   const size_t value_length = strlen(value);
 701 
 702   if (startup_recording_options_array == NULL) {
 703     startup_recording_options_array = new (ResourceObj::C_HEAP, mtTracing) GrowableArray<const char*>(8, true, mtTracing);
 704   }
 705   assert(startup_recording_options_array != NULL, "invariant");
 706   char* const startup_value = NEW_C_HEAP_ARRAY(char, value_length + 1, mtTracing);
 707   strncpy(startup_value, value, value_length + 1);
 708   assert(strncmp(startup_value, value, value_length) == 0, "invariant");
 709   startup_recording_options_array->append(startup_value);
 710   return false;
 711 }
 712 
 713 const GrowableArray<const char*>* JfrOptionSet::startup_recording_options() {
 714   return startup_recording_options_array;
 715 }
 716 
 717 void JfrOptionSet::release_startup_recording_options() {
 718   if (startup_recording_options_array != NULL) {
 719     const int length = startup_recording_options_array->length();
 720     for (int i = 0; i < length; ++i) {
 721       FREE_C_HEAP_ARRAY(char, startup_recording_options_array->at(i));
 722     }
 723     delete startup_recording_options_array;
 724     startup_recording_options_array = NULL;
 725   }
 726 }