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