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