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