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 }