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