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
41struct ObsoleteOption {
42 const char* name;
43 const char* message;
44};
45
46static 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
58jlong JfrOptionSet::max_chunk_size() {
59 return _max_chunk_size;
60}
61
62void JfrOptionSet::set_max_chunk_size(jlong value) {
63 _max_chunk_size = value;
64}
65
66jlong JfrOptionSet::global_buffer_size() {
67 return _global_buffer_size;
68}
69
70void JfrOptionSet::set_global_buffer_size(jlong value) {
71 _global_buffer_size = value;
72}
73
74jlong JfrOptionSet::thread_buffer_size() {
75 return _thread_buffer_size;
76}
77
78void JfrOptionSet::set_thread_buffer_size(jlong value) {
79 _thread_buffer_size = value;
80}
81
82jlong JfrOptionSet::memory_size() {
83 return _memory_size;
84}
85
86void JfrOptionSet::set_memory_size(jlong value) {
87 _memory_size = value;
88}
89
90jlong JfrOptionSet::num_global_buffers() {
91 return _num_global_buffers;
92}
93
94void JfrOptionSet::set_num_global_buffers(jlong value) {
95 _num_global_buffers = value;
96}
97
98jint JfrOptionSet::old_object_queue_size() {
99 return (jint)_old_object_queue_size;
100}
101
102void JfrOptionSet::set_old_object_queue_size(jlong value) {
103 _old_object_queue_size = value;
104}
105
106u4 JfrOptionSet::stackdepth() {
107 return _stack_depth;
108}
109
110void 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
120bool JfrOptionSet::sample_threads() {
121 return _sample_threads == JNI_TRUE;
122}
123
124void JfrOptionSet::set_sample_threads(jboolean sample) {
125 _sample_threads = sample;
126}
127
128bool JfrOptionSet::can_retransform() {
129 return _retransform == JNI_TRUE;
130}
131
132void JfrOptionSet::set_retransform(jboolean value) {
133 _retransform = value;
134}
135
136bool JfrOptionSet::sample_protection() {
137 return _sample_protection == JNI_TRUE;
138}
139
140#ifdef ASSERT
141void JfrOptionSet::set_sample_protection(jboolean protection) {
142 _sample_protection = protection;
143}
144#endif
145
146bool JfrOptionSet::compressed_integers() {
147 // Set this to false for debugging purposes.
148 return true;
149}
150
151bool JfrOptionSet::allow_retransforms() {
152#if INCLUDE_JVMTI
153 return true;
154#else
155 return false;
156#endif
157}
158
159bool JfrOptionSet::allow_event_retransforms() {
160 return allow_retransforms() && (DumpSharedSpaces || can_retransform());
161}
162
163// default options for the dcmd parser
164const char* const default_repository = NULL;
165const char* const default_global_buffer_size = "512k";
166const char* const default_num_global_buffers = "20";
167const char* const default_memory_size = "10m";
168const char* const default_thread_buffer_size = "8k";
169const char* const default_max_chunk_size = "12m";
170const char* const default_sample_threads = "true";
171const char* const default_stack_depth = "64";
172const char* const default_retransform = "true";
173const char* const default_old_object_queue_size = "256";
174DEBUG_ONLY(const char* const default_sample_protection = "false";)
175
176// statics
177static DCmdArgument<char*> _dcmd_repository(
178 "repository",
179 "Flight recorder disk repository location",
180 "STRING",
181 false,
182 default_repository);
183
184static DCmdArgument<MemorySizeArgument> _dcmd_threadbuffersize(
185 "threadbuffersize",
186 "Thread buffer size",
187 "MEMORY SIZE",
188 false,
189 default_thread_buffer_size);
190
191static 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
198static DCmdArgument<MemorySizeArgument> _dcmd_globalbuffersize(
199 "globalbuffersize",
200 "Global buffer size",
201 "MEMORY SIZE",
202 false,
203 default_global_buffer_size);
204
205static DCmdArgument<jlong> _dcmd_numglobalbuffers(
206 "numglobalbuffers",
207 "Number of global buffers",
208 "JULONG",
209 false,
210 default_num_global_buffers);
211
212static 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
219static 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
226static 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
234static 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
242static DCmdArgument<jlong> _dcmd_stackdepth(
243 "stackdepth",
244 "Stack depth for stacktraces (minimum 1, maximum 2048)",
245 "JULONG",
246 false,
247 default_stack_depth);
248
249static 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
256static DCmdParser _parser;
257
258static 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
272static 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
301jlong JfrOptionSet::_max_chunk_size = 0;
302jlong JfrOptionSet::_global_buffer_size = 0;
303jlong JfrOptionSet::_thread_buffer_size = 0;
304jlong JfrOptionSet::_memory_size = 0;
305jlong JfrOptionSet::_num_global_buffers = 0;
306jlong JfrOptionSet::_old_object_queue_size = 0;
307u4 JfrOptionSet::_stack_depth = STACK_DEPTH_DEFAULT;
308jboolean JfrOptionSet::_sample_threads = JNI_TRUE;
309jboolean JfrOptionSet::_retransform = JNI_TRUE;
310#ifdef ASSERT
311jboolean JfrOptionSet::_sample_protection = JNI_FALSE;
312#else
313jboolean JfrOptionSet::_sample_protection = JNI_TRUE;
314#endif
315
316bool 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
328bool 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
379template <typename Argument>
380static 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
394template <typename Argument>
395static 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
409template <typename Argument>
410static 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
425template <typename MemoryArg>
426static 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
449template <typename MemoryArg, typename NumberArg>
450static 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
475template <typename Argument>
476static 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
491template <typename MemoryArg, typename NumberArg>
492static 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
512template <typename Argument>
513static 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
525static 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
546static 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
590static 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
606template <typename Argument>
607static 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
616static 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 */
647bool 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
665bool 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
680static GrowableArray<const char*>* startup_recording_options_array = NULL;
681
682bool 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
714const GrowableArray<const char*>* JfrOptionSet::startup_recording_options() {
715 return startup_recording_options_array;
716}
717
718void 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}
728