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