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 | } |
728 | |