1 /*
  2  * Copyright (c) 2012, 2026, 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 "classfile/javaClasses.inline.hpp"
 26 #include "classfile/vmSymbols.hpp"
 27 #include "jfr/dcmd/jfrDcmds.hpp"
 28 #include "jfr/jfr.hpp"
 29 #include "jfr/jni/jfrJavaSupport.hpp"
 30 #include "jfr/periodic/jfrRedactedEvents.hpp"
 31 #include "jfr/recorder/jfrRecorder.hpp"
 32 #include "jfr/recorder/service/jfrOptionSet.hpp"
 33 #include "jfr/support/jfrThreadLocal.hpp"
 34 #include "logging/log.hpp"
 35 #include "logging/logConfiguration.hpp"
 36 #include "logging/logMessage.hpp"
 37 #include "memory/arena.hpp"
 38 #include "memory/resourceArea.hpp"
 39 #include "oops/objArrayOop.inline.hpp"
 40 #include "oops/oop.inline.hpp"
 41 #include "oops/oopCast.inline.hpp"
 42 #include "oops/symbol.hpp"
 43 #include "runtime/handles.inline.hpp"
 44 #include "runtime/jniHandles.hpp"
 45 #include "services/diagnosticArgument.hpp"
 46 #include "services/diagnosticFramework.hpp"
 47 #include "utilities/globalDefinitions.hpp"
 48 
 49 
 50 bool register_jfr_dcmds() {
 51   uint32_t full_export = DCmd_Source_Internal | DCmd_Source_AttachAPI | DCmd_Source_MBean;
 52   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JfrCheckFlightRecordingDCmd>(full_export));
 53   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JfrDumpFlightRecordingDCmd>(full_export));
 54   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JfrStartFlightRecordingDCmd>(full_export));
 55   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JfrStopFlightRecordingDCmd>(full_export));
 56   // JFR.query Uncomment when developing new queries for the JFR.view command
 57   // DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JfrQueryFlightRecordingDCmd>(full_export, /*hidden=*/true));
 58   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JfrViewFlightRecordingDCmd>(full_export));
 59   DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<JfrConfigureFlightRecorderDCmd>(full_export));
 60   return true;
 61 }
 62 
 63 static bool is_disabled(outputStream* output) {
 64   if (Jfr::is_disabled()) {
 65     if (output != nullptr) {
 66       output->print_cr("Flight Recorder is disabled.\n");
 67     }
 68     return true;
 69   }
 70   return false;
 71 }
 72 
 73 static bool invalid_state(outputStream* out, TRAPS) {
 74   DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
 75   if (is_disabled(out)) {
 76     return true;
 77   }
 78   if (!JfrJavaSupport::is_jdk_jfr_module_available()) {
 79     JfrJavaSupport::load_jdk_jfr_module(THREAD);
 80     if (HAS_PENDING_EXCEPTION) {
 81       // Log exception here, but let is_jdk_jfr_module_available(out, THREAD)
 82       // handle output to the user.
 83       ResourceMark rm(THREAD);
 84       oop throwable = PENDING_EXCEPTION;
 85       assert(throwable != nullptr, "invariant");
 86       oop msg = java_lang_Throwable::message(throwable);
 87       if (msg != nullptr) {
 88         char* text = java_lang_String::as_utf8_string(msg);
 89         if (text != nullptr) {
 90           log_debug(jfr, startup)("Flight Recorder can not be enabled. %s", text);
 91         }
 92       }
 93       CLEAR_PENDING_EXCEPTION;
 94     }
 95   }
 96   return !JfrJavaSupport::is_jdk_jfr_module_available(out, THREAD);
 97 }
 98 
 99 static void handle_pending_exception(outputStream* output, bool startup, oop throwable) {
100   assert(throwable != nullptr, "invariant");
101 
102   oop msg = java_lang_Throwable::message(throwable);
103   if (msg == nullptr) {
104     return;
105   }
106   char* text = java_lang_String::as_utf8_string(msg);
107   if (text != nullptr) {
108     if (startup) {
109       log_error(jfr,startup)("%s", text);
110     } else {
111       output->print_cr("%s", text);
112     }
113   }
114 }
115 
116 static void print_message(outputStream* output, oop content, TRAPS) {
117   assert(content != nullptr, "invariant");
118   refArrayOop lines = oop_cast<refArrayOop>(content);
119   const int length = lines->length();
120   for (int i = 0; i < length; ++i) {
121     const char* text = JfrJavaSupport::c_str(lines->obj_at(i), THREAD);
122     if (text == nullptr) {
123       // An oome has been thrown and is pending.
124       break;
125     }
126     output->print_cr("%s", text);
127   }
128 }
129 
130 static void log(oop content, TRAPS) {
131   LogMessage(jfr,startup) msg;
132   assert(content != nullptr, "invariant");
133   refArrayOop lines = oop_cast<refArrayOop>(content);
134   const int length = lines->length();
135   for (int i = 0; i < length; ++i) {
136     const char* text = JfrJavaSupport::c_str(lines->obj_at(i), THREAD);
137     if (text == nullptr) {
138       // An oome has been thrown and is pending.
139       break;
140     }
141     msg.info("%s", text);
142   }
143 }
144 
145 static void handle_dcmd_result(outputStream* output,
146                                const oop result,
147                                const DCmdSource source,
148                                TRAPS) {
149   DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
150   assert(output != nullptr, "invariant");
151   ResourceMark rm(THREAD);
152   const bool startup = DCmd_Source_Internal == source;
153   if (HAS_PENDING_EXCEPTION) {
154     handle_pending_exception(output, startup, PENDING_EXCEPTION);
155     // Don't clear exception on startup, JVM should fail initialization.
156     if (!startup) {
157       CLEAR_PENDING_EXCEPTION;
158     }
159     return;
160   }
161 
162   assert(!HAS_PENDING_EXCEPTION, "invariant");
163 
164   if (startup) {
165     if (log_is_enabled(Warning, jfr, startup))  {
166       // if warning is set, assume user hasn't configured log level
167       // Log to Info and reset to Warning. This way user can disable
168       // default output by setting -Xlog:jfr+startup=error/off
169       LogConfiguration::configure_stdout(LogLevel::Info, true, LOG_TAGS(jfr, startup));
170       log(result, THREAD);
171       LogConfiguration::configure_stdout(LogLevel::Warning, true, LOG_TAGS(jfr, startup));
172     } else {
173       log(result, THREAD);
174     }
175   } else {
176       // Print output for jcmd or MXBean
177       print_message(output, result, THREAD);
178   }
179 }
180 
181 static oop construct_dcmd_instance(JfrJavaArguments* args, TRAPS) {
182   assert(args != nullptr, "invariant");
183   DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
184   assert(args->klass() != nullptr, "invariant");
185   args->set_name("<init>");
186   args->set_signature("()V");
187   JfrJavaSupport::new_object(args, CHECK_NULL);
188   return args->result()->get_oop();
189 }
190 
191 JfrDCmd::JfrDCmd(outputStream* output, bool heap, int num_arguments) : DCmd(output, heap), _args(nullptr), _num_arguments(num_arguments), _delimiter('\0') {}
192 
193 void JfrDCmd::invoke(JfrJavaArguments& method, TRAPS) const {
194   JavaValue constructor_result(T_OBJECT);
195   JfrJavaArguments constructor_args(&constructor_result);
196   constructor_args.set_klass(javaClass(), CHECK);
197 
198   HandleMark hm(THREAD);
199   JNIHandleMark jni_handle_management(THREAD);
200 
201   const oop dcmd = construct_dcmd_instance(&constructor_args, CHECK);
202 
203   Handle h_dcmd_instance(THREAD, dcmd);
204   assert(h_dcmd_instance.not_null(), "invariant");
205 
206   method.set_receiver(h_dcmd_instance);
207   JfrJavaSupport::call_virtual(&method, THREAD);
208 }
209 
210 void JfrDCmd::parse(CmdLine* line, char delim, TRAPS) {
211   _args = line->args_addr();
212   _delimiter = delim;
213   // Error checking done in execute.
214   // Will not matter from DCmdFactory perspective
215   // where parse and execute are called consecutively.
216 }
217 
218 void JfrDCmd::execute(DCmdSource source, TRAPS) {
219   if (invalid_state(output(), THREAD)) {
220     return;
221   }
222   if (source == DCmd_Source_Internal && _args != nullptr && strcmp(_args, "help") == 0) {
223      print_java_help("getStartupHelp");
224      vm_exit(0);
225   }
226 
227   static const char signature[] = "(Ljava/lang/String;Ljava/lang/String;C)[Ljava/lang/String;";
228   JavaValue result(T_OBJECT);
229   JfrJavaArguments execute(&result, javaClass(), "execute", signature, CHECK);
230   jstring argument = JfrJavaSupport::new_string(_args, CHECK);
231   jstring s = nullptr;
232   if (source == DCmd_Source_Internal) {
233     s = JfrJavaSupport::new_string("internal", CHECK);
234   }
235   if (source == DCmd_Source_MBean) {
236     s = JfrJavaSupport::new_string("mbean", CHECK);
237   }
238   if (source == DCmd_Source_AttachAPI) {
239     s = JfrJavaSupport::new_string("attach", CHECK);
240   }
241   execute.push_jobject(s);
242   execute.push_jobject(argument);
243   execute.push_int(_delimiter);
244   invoke(execute, THREAD);
245   handle_dcmd_result(output(), result.get_oop(), source, THREAD);
246 }
247 
248 void JfrDCmd::print_java_help(const char* get_help_method) const {
249   static const char signature[] = "()[Ljava/lang/String;";
250   JavaThread* thread = JavaThread::current();
251   JavaValue result(T_OBJECT);
252   JfrJavaArguments java_method(&result, javaClass(), get_help_method, signature, thread);
253   invoke(java_method, thread);
254   handle_dcmd_result(output(), result.get_oop(), DCmd_Source_MBean, thread);
255 }
256 
257 void JfrDCmd::print_help(const char* name) const {
258   print_java_help("getHelp");
259 }
260 
261 static void initialize_dummy_descriptors(GrowableArray<DCmdArgumentInfo*>* array) {
262   assert(array != nullptr, "invariant");
263   DCmdArgumentInfo * const dummy = new DCmdArgumentInfo(nullptr,
264                                                         nullptr,
265                                                         nullptr,
266                                                         nullptr,
267                                                         false,
268                                                         true, // a DcmdFramework "option"
269                                                         false);
270   for (int i = 0; i < array->capacity(); ++i) {
271     array->append(dummy);
272   }
273 }
274 
275 // Since the DcmdFramework does not support dynamically allocated strings,
276 // we keep them in a thread local arena. The arena is reset between invocations.
277 static THREAD_LOCAL Arena* dcmd_arena = nullptr;
278 
279 static void prepare_dcmd_string_arena(JavaThread* jt) {
280   dcmd_arena = JfrThreadLocal::dcmd_arena(jt);
281   assert(dcmd_arena != nullptr, "invariant");
282   dcmd_arena->destruct_contents(); // will grow on next allocation
283 }
284 
285 static char* dcmd_arena_allocate(size_t size) {
286   assert(dcmd_arena != nullptr, "invariant");
287   return (char*)dcmd_arena->Amalloc(size);
288 }
289 
290 static const char* get_as_dcmd_arena_string(oop string) {
291   char* str = nullptr;
292   const typeArrayOop value = java_lang_String::value(string);
293   if (value != nullptr) {
294     const size_t length = java_lang_String::utf8_length(string, value) + 1;
295     str = dcmd_arena_allocate(length);
296     assert(str != nullptr, "invariant");
297     java_lang_String::as_utf8_string(string, value, str, length);
298   }
299   return str;
300 }
301 
302 static const char* read_string_field(oop argument, const char* field_name, TRAPS) {
303   JavaValue result(T_OBJECT);
304   JfrJavaArguments args(&result);
305   args.set_klass(argument->klass());
306   args.set_name(field_name);
307   args.set_signature("Ljava/lang/String;");
308   args.set_receiver(argument);
309   JfrJavaSupport::get_field(&args, THREAD);
310   const oop string_oop = result.get_oop();
311   return string_oop != nullptr ? get_as_dcmd_arena_string(string_oop) : nullptr;
312 }
313 
314 static bool read_boolean_field(oop argument, const char* field_name, TRAPS) {
315   JavaValue result(T_BOOLEAN);
316   JfrJavaArguments args(&result);
317   args.set_klass(argument->klass());
318   args.set_name(field_name);
319   args.set_signature("Z");
320   args.set_receiver(argument);
321   JfrJavaSupport::get_field(&args, THREAD);
322   return (result.get_jint() & 1) == 1;
323 }
324 
325 static DCmdArgumentInfo* create_info(oop argument, TRAPS) {
326   return new DCmdArgumentInfo(
327     read_string_field(argument, "name", THREAD),
328     read_string_field(argument, "description", THREAD),
329     read_string_field(argument, "type", THREAD),
330     read_string_field(argument, "defaultValue", THREAD),
331     read_boolean_field(argument, "mandatory", THREAD),
332     read_boolean_field(argument, "option", THREAD),
333     read_boolean_field(argument, "allowMultiple", THREAD));
334 }
335 
336 GrowableArray<DCmdArgumentInfo*>* JfrDCmd::argument_info_array() const {
337   static const char signature[] = "()[Ljdk/jfr/internal/dcmd/Argument;";
338   JavaThread* thread = JavaThread::current();
339   GrowableArray<DCmdArgumentInfo*>* const array = new GrowableArray<DCmdArgumentInfo*>(_num_arguments);
340   JavaValue result(T_OBJECT);
341   JfrJavaArguments getArgumentInfos(&result, javaClass(), "getArgumentInfos", signature, thread);
342   invoke(getArgumentInfos, thread);
343   if (thread->has_pending_exception()) {
344     // Most likely an OOME, but the DCmdFramework is not the best place to handle it.
345     // We handle it locally by clearing the exception and returning an array with dummy descriptors.
346     // This lets the MBean server initialization routine complete successfully,
347     // but this particular command will have no argument descriptors exposed.
348     // Hence we postpone, or delegate, handling of OOME's to code that is better suited.
349     log_debug(jfr, system)("Exception in DCmd getArgumentInfos");
350     thread->clear_pending_exception();
351     initialize_dummy_descriptors(array);
352     assert(array->length() == _num_arguments, "invariant");
353     return array;
354   }
355   oop oop_args = result.get_oop();
356   assert(oop_args != nullptr, "invariant");
357   refArrayOop arguments = oop_cast<refArrayOop>(oop_args);
358   const int num_arguments = arguments->length();
359   assert(num_arguments == _num_arguments, "invariant");
360   prepare_dcmd_string_arena(thread);
361   for (int i = 0; i < num_arguments; ++i) {
362     DCmdArgumentInfo* const dai = create_info(arguments->obj_at(i), thread);
363     assert(dai != nullptr, "invariant");
364     array->append(dai);
365   }
366   return array;
367 }
368 
369 GrowableArray<const char*>* JfrDCmd::argument_name_array() const {
370   GrowableArray<DCmdArgumentInfo*>* argument_infos = argument_info_array();
371   GrowableArray<const char*>* array = new GrowableArray<const char*>(argument_infos->length());
372   for (int i = 0; i < argument_infos->length(); i++) {
373     array->append(argument_infos->at(i)->name());
374   }
375   return array;
376 }
377 
378 JfrConfigureFlightRecorderDCmd::JfrConfigureFlightRecorderDCmd(outputStream* output,
379                                                                bool heap) : DCmdWithParser(output, heap),
380   _repository_path("repositorypath", "Path to repository,.e.g \\\"My Repository\\\"", "STRING", false, nullptr),
381   _dump_path("dumppath", "Path to dump, e.g. \\\"My Dump path\\\"", "STRING", false, nullptr),
382   _stack_depth("stackdepth", "Stack depth", "JULONG", false, "64"),
383   _global_buffer_count("globalbuffercount", "Number of global buffers,", "JULONG", false, "20"),
384   _global_buffer_size("globalbuffersize", "Size of a global buffers,", "MEMORY SIZE", false, "512k"),
385   _thread_buffer_size("thread_buffer_size", "Size of a thread buffer", "MEMORY SIZE", false, "8k"),
386   _memory_size("memorysize", "Overall memory size, ", "MEMORY SIZE", false, "10m"),
387   _max_chunk_size("maxchunksize", "Size of an individual disk chunk", "MEMORY SIZE", false, "12m"),
388   _sample_threads("samplethreads", "Activate thread sampling", "BOOLEAN", false, "true"),
389   _preserve_repository("preserve-repository", "Preserve the disk repository after JVM exit", "BOOLEAN", false, "false"),
390   _verbose(true) {
391   _dcmdparser.add_dcmd_option(&_repository_path);
392   _dcmdparser.add_dcmd_option(&_dump_path);
393   _dcmdparser.add_dcmd_option(&_stack_depth);
394   _dcmdparser.add_dcmd_option(&_global_buffer_count);
395   _dcmdparser.add_dcmd_option(&_global_buffer_size);
396   _dcmdparser.add_dcmd_option(&_thread_buffer_size);
397   _dcmdparser.add_dcmd_option(&_memory_size);
398   _dcmdparser.add_dcmd_option(&_max_chunk_size);
399   _dcmdparser.add_dcmd_option(&_sample_threads);
400   _dcmdparser.add_dcmd_option(&_preserve_repository);
401 };
402 
403 void JfrConfigureFlightRecorderDCmd::print_help(const char* name) const {
404   print_help(output(), false);
405 }
406 
407 static void print_filters(outputStream* out, JfrRedactedEvents::StringArray* filters) {
408   for (int i = 0; i < filters->length(); i++) {
409     out->print_cr("                        %s", filters->at(i)->text());
410   }
411 }
412 
413 void JfrConfigureFlightRecorderDCmd::print_help(outputStream* out, bool startup) {
414               // 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890
415   if (startup) {
416     out->print_cr("Syntax : -XX:FlightRecorderOptions:[options]");
417     out->print_cr("");
418   }
419   out->print_cr("Options:");
420   out->print_cr("");
421   out->print_cr(              "  %-19s (Optional) Number of global buffers. This option is a legacy",
422                                        startup ? "numglobalbuffers": "globalbuffercount");
423   out->print_cr("                      option: change the memorysize parameter to alter the number of");
424   out->print_cr("                      global buffers. This value cannot be changed once JFR has been");
425   out->print_cr("                      initialized. (STRING, default determined by the value for");
426   out->print_cr("                      memorysize)");
427   out->print_cr("");
428   out->print_cr("  globalbuffersize    (Optional) Size of the global buffers, in bytes. This option is a");
429   out->print_cr("                      legacy option: change the memorysize parameter to alter the size");
430   out->print_cr("                      of the global buffers. This value cannot be changed once JFR has");
431   out->print_cr("                      been initialized. (STRING, default determined by the value for");
432   out->print_cr("                      memorysize)");
433   out->print_cr("");
434   out->print_cr("  maxchunksize        (Optional) Maximum size of an individual data chunk in bytes if");
435   out->print_cr("                      one of the following suffixes is not used: 'm' or 'M' for");
436   out->print_cr("                      megabytes OR 'g' or 'G' for gigabytes. This value cannot be");
437   out->print_cr("                      changed once JFR has been initialized. (STRING, 12M)");
438   out->print_cr("");
439   out->print_cr("  memorysize          (Optional) Overall memory size, in bytes if one of the following");
440   out->print_cr("                      suffixes is not used: 'm' or 'M' for megabytes OR 'g' or 'G' for");
441   out->print_cr("                      gigabytes. This value cannot be changed once JFR has been");
442   out->print_cr("                      initialized. (STRING, 10M)");
443   out->print_cr("");
444   out->print_cr(              "  %-19s (Optional) Path to the location where recordings are stored until",
445                                        startup ? "repository" : "repositorypath");
446   out->print_cr("                      they are written to a permanent file. (STRING, The default");
447   out->print_cr("                      location is the temporary directory for the operating system. On");
448   out->print_cr("                      Linux operating systems, the temporary directory is /tmp. On");
449   out->print_cr("                      Windows, the temporary directory is specified by the TMP");
450   out->print_cr("                      environment variable)");
451   out->print_cr("");
452   out->print_cr("  dumppath            (Optional) Path to the location where a recording file is written");
453   out->print_cr("                      in case the VM runs into a critical error, such as a system");
454   out->print_cr("                      crash. (STRING, The default location is the current directory)");
455   out->print_cr("");
456   out->print_cr("  stackdepth          (Optional) Stack depth for stack traces. Setting this value");
457   out->print_cr("                      greater than the default of 64 may cause a performance");
458   out->print_cr("                      degradation. This value cannot be changed once JFR has been");
459   out->print_cr("                      initialized. (LONG, 64)");
460   out->print_cr("");
461   out->print_cr(              "  %-19s (Optional) Local buffer size for each thread in bytes if one of",
462                                        startup ? "threadbuffersize" : "thread_buffer_size");
463   out->print_cr("                      the following suffixes is not used: 'k' or 'K' for kilobytes or");
464   out->print_cr("                      'm' or 'M' for megabytes. Overriding this parameter could reduce");
465   out->print_cr("                      performance and is not recommended. This value cannot be changed");
466   out->print_cr("                      once JFR has been initialized. (STRING, 8k)");
467   out->print_cr("");
468   out->print_cr("  preserve-repository (Optional) Preserve files stored in the disk repository after the");
469   out->print_cr("                      Java Virtual Machine has exited. (BOOLEAN, false)");
470   out->print_cr("");
471   if (startup) {
472     out->print_cr("  old-object-queue-size (Optional) Maximum number of old objects to track. By default,");
473     out->print_cr("                        the number of objects is set to 256. (LONG, 256)");
474     out->print_cr("");
475     out->print_cr("  redact-argument     (Optional) Replace command-line arguments that match a");
476     out->print_cr("                      semicolon-separated list of glob patterns, for example,");
477     out->print_cr("                      *secret*;password*. Matching is case-insensitive, and the");
478     out->print_cr("                      supported wildcards are '*' and '?'. To redact multiple arguments,");
479     out->print_cr("                      use a literal space (' ') as a separator. For example, to match");
480     out->print_cr("                      the two arguments --auth username:token, use the filter");
481     out->print_cr("                      --auth *:*. Filters containing spaces must be quoted as a single");
482     out->print_cr("                      command-line argument, for example,");
483     out->print_cr("                      -XX:FlightRecorderOptions:'redact-argument=--auth *:*'.");
484     out->print_cr("                      Arguments containing spaces might not be matched as expected.");
485     out->print_cr("                      The option redact-argument is best-effort and applies only to");
486     out->print_cr("                      command-line arguments in the jdk.JVMInformation event and to");
487     out->print_cr("                      the java.command system property in the jdk.InitialSystemProperty");
488     out->print_cr("                      event. Other events, such as jdk.ProcessStart (child processes),");
489     out->print_cr("                      are not redacted.");
490     out->print_cr("");
491     out->print_cr("                      If the redact-argument option is not specified, the following");
492     out->print_cr("                      filters are used by default:");
493     out->print_cr("");
494     print_filters(out, JfrRedactedEvents::argument_filters());
495     out->print_cr("");
496     out->print_cr("                      To load patterns from a file (one per line), use @<filename>.");
497     out->print_cr("                      To add to the default patterns instead of replacing them, prefix");
498     out->print_cr("                      the whole list with '+', for example, +*foo*;@redact.txt.");
499     out->print_cr("                      Use 'none' (lowercase) to disable all redaction filters for");
500     out->print_cr("                      command-line arguments. Redacted arguments will be replaced");
501     out->print_cr("                      with '[REDACTED]'. (STRING, default filters)");
502     out->print_cr("");
503     out->print_cr("  redact-key          (Optional) Replace the value of environment variables and system");
504     out->print_cr("                      properties whose key matches a semicolon-separated list of glob");
505     out->print_cr("                      patterns, for example, *password*;*token*. Matching is");
506     out->print_cr("                      case-insensitive, and the supported wildcards are '*' and '?'.");
507     out->print_cr("                      The option redact-key is best-effort and applies only to the");
508     out->print_cr("                      jdk.InitialSystemProperty, jdk.InitialEnvironmentVariable and");
509     out->print_cr("                      jdk.JVMInformation (-Dkey...) events. Other events, such as");
510     out->print_cr("                      jdk.InitialSecurityProperty, are not redacted.");
511     out->print_cr("");
512     out->print_cr("                      If the redact-key option is not specified, the");
513     out->print_cr("                      following filters are used by default:");
514     out->print_cr("");
515     print_filters(out, JfrRedactedEvents::key_filters());
516     out->print_cr("");
517     out->print_cr("                      To load patterns from a file (one per line), use @<filename>.");
518     out->print_cr("                      To add to the default patterns instead of replacing them, prefix");
519     out->print_cr("                      the whole list with '+', for example, +*cred*;@keys.txt.");
520     out->print_cr("                      Use 'none' (lowercase) to disable all redaction filters for key");
521     out->print_cr("                      matching. Redacted values will be replaced with '[REDACTED]'.");
522     out->print_cr("                      (STRING, default filters)");
523     out->print_cr("");
524     out->print_cr("Options must be specified using the <key>=<value> syntax. Multiple options are separated");
525     out->print_cr("with a comma.");
526     out->print_cr("");
527     out->print_cr("Example usage:");
528     out->print_cr("");
529     out->print_cr(" -XX:FlightRecorderOptions:repository=/temporary,stackdepth=256");
530     out->print_cr(" -XX:FlightRecorderOptions:'redact-key=+*confidential*;*private*,redact-argument=+https://*:*@*'");
531     out->print_cr(" -XX:FlightRecorderOptions:'redact-argument=+-*private *'");
532   } else {
533     out->print_cr("Options must be specified using the <key> or <key>=<value> syntax.");
534     out->print_cr("");
535     out->print_cr("Example usage:");
536     out->print_cr("");
537     out->print_cr(" $ jcmd <pid> JFR.configure");
538     out->print_cr(" $ jcmd <pid> JFR.configure repositorypath=/temporary");
539     out->print_cr(" $ jcmd <pid> JFR.configure stackdepth=256");
540     out->print_cr(" $ jcmd <pid> JFR.configure memorysize=100M");
541   }
542   out->print_cr("");
543 }
544 
545 void JfrConfigureFlightRecorderDCmd::execute(DCmdSource source, TRAPS) {
546   DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
547 
548   if (invalid_state(output(), THREAD)) {
549     return;
550   }
551 
552   HandleMark hm(THREAD);
553   JNIHandleMark jni_handle_management(THREAD);
554 
555   JavaValue result(T_OBJECT);
556   JfrJavaArguments constructor_args(&result);
557   constructor_args.set_klass("jdk/jfr/internal/dcmd/DCmdConfigure", CHECK);
558   const oop dcmd = construct_dcmd_instance(&constructor_args, CHECK);
559   Handle h_dcmd_instance(THREAD, dcmd);
560   assert(h_dcmd_instance.not_null(), "invariant");
561 
562   jstring repository_path = nullptr;
563   if (_repository_path.is_set() && _repository_path.value() != nullptr) {
564     repository_path = JfrJavaSupport::new_string(_repository_path.value(), CHECK);
565   }
566 
567   jstring dump_path = nullptr;
568   if (_dump_path.is_set() && _dump_path.value() != nullptr) {
569     dump_path = JfrJavaSupport::new_string(_dump_path.value(), CHECK);
570   }
571 
572   jobject stack_depth = nullptr;
573   jobject global_buffer_count = nullptr;
574   jobject global_buffer_size = nullptr;
575   jobject thread_buffer_size = nullptr;
576   jobject max_chunk_size = nullptr;
577   jobject memory_size = nullptr;
578   jobject preserve_repository = nullptr;
579 
580   if (!JfrRecorder::is_created()) {
581     if (_stack_depth.is_set()) {
582       stack_depth = JfrJavaSupport::new_java_lang_Integer((jint)_stack_depth.value(), CHECK);
583     }
584     if (_global_buffer_count.is_set()) {
585       global_buffer_count = JfrJavaSupport::new_java_lang_Long(_global_buffer_count.value(), CHECK);
586     }
587     if (_global_buffer_size.is_set()) {
588       global_buffer_size = JfrJavaSupport::new_java_lang_Long(_global_buffer_size.value()._size, CHECK);
589     }
590     if (_thread_buffer_size.is_set()) {
591       thread_buffer_size = JfrJavaSupport::new_java_lang_Long(_thread_buffer_size.value()._size, CHECK);
592     }
593     if (_max_chunk_size.is_set()) {
594       max_chunk_size = JfrJavaSupport::new_java_lang_Long(_max_chunk_size.value()._size, CHECK);
595     }
596     if (_memory_size.is_set()) {
597       memory_size = JfrJavaSupport::new_java_lang_Long(_memory_size.value()._size, CHECK);
598     }
599     if (_sample_threads.is_set()) {
600       bool startup = DCmd_Source_Internal == source;
601       if (startup) {
602         log_warning(jfr,startup)("%s", "Option samplethreads is deprecated. Use -XX:StartFlightRecording:method-profiling=<off|normal|high|max>");
603       } else {
604         output()->print_cr("%s", "Option samplethreads is deprecated. Use JFR.start method-profiling=<off|normal|high|max>");
605         output()->print_cr("");
606       }
607     }
608   }
609   if (_preserve_repository.is_set()) {
610     preserve_repository = JfrJavaSupport::new_java_lang_Boolean(_preserve_repository.value(), CHECK);
611   }
612 
613   static const char klass[] = "jdk/jfr/internal/dcmd/DCmdConfigure";
614   static const char method[] = "execute";
615   static const char signature[] = "(ZLjava/lang/String;Ljava/lang/String;Ljava/lang/Integer;"
616     "Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Long;Ljava/lang/Long;"
617     "Ljava/lang/Long;Ljava/lang/Boolean;)[Ljava/lang/String;";
618 
619   JfrJavaArguments execute_args(&result, klass, method, signature, CHECK);
620   execute_args.set_receiver(h_dcmd_instance);
621 
622   // params
623   execute_args.push_int(_verbose ? 1 : 0);
624   execute_args.push_jobject(repository_path);
625   execute_args.push_jobject(dump_path);
626   execute_args.push_jobject(stack_depth);
627   execute_args.push_jobject(global_buffer_count);
628   execute_args.push_jobject(global_buffer_size);
629   execute_args.push_jobject(thread_buffer_size);
630   execute_args.push_jobject(memory_size);
631   execute_args.push_jobject(max_chunk_size);
632   execute_args.push_jobject(preserve_repository);
633 
634   JfrJavaSupport::call_virtual(&execute_args, THREAD);
635   handle_dcmd_result(output(), result.get_oop(), source, THREAD);
636 }