8326116: JFR: Add help option to -XX:StartFlightRecording
Reviewed-by: mgronlun
This commit is contained in:
parent
941bee197f
commit
f7c84137b5
@ -220,6 +220,11 @@ void JfrDCmd::execute(DCmdSource source, TRAPS) {
|
||||
if (invalid_state(output(), THREAD)) {
|
||||
return;
|
||||
}
|
||||
if (source == DCmd_Source_Internal && _args != nullptr && strcmp(_args, "help") == 0) {
|
||||
print_java_help("printStartupHelp");
|
||||
vm_exit(0);
|
||||
}
|
||||
|
||||
static const char signature[] = "(Ljava/lang/String;Ljava/lang/String;C)[Ljava/lang/String;";
|
||||
JavaValue result(T_OBJECT);
|
||||
JfrJavaArguments execute(&result, javaClass(), "execute", signature, CHECK);
|
||||
@ -241,15 +246,19 @@ void JfrDCmd::execute(DCmdSource source, TRAPS) {
|
||||
handle_dcmd_result(output(), result.get_oop(), source, THREAD);
|
||||
}
|
||||
|
||||
void JfrDCmd::print_help(const char* name) const {
|
||||
void JfrDCmd::print_java_help(const char* help_method) const {
|
||||
static const char signature[] = "()[Ljava/lang/String;";
|
||||
JavaThread* thread = JavaThread::current();
|
||||
JavaValue result(T_OBJECT);
|
||||
JfrJavaArguments printHelp(&result, javaClass(), "printHelp", signature, thread);
|
||||
JfrJavaArguments printHelp(&result, javaClass(), help_method, signature, thread);
|
||||
invoke(printHelp, thread);
|
||||
handle_dcmd_result(output(), result.get_oop(), DCmd_Source_MBean, thread);
|
||||
}
|
||||
|
||||
void JfrDCmd::print_help(const char* name) const {
|
||||
print_java_help("printHelp");
|
||||
}
|
||||
|
||||
static void initialize_dummy_descriptors(GrowableArray<DCmdArgumentInfo*>* array) {
|
||||
assert(array != nullptr, "invariant");
|
||||
DCmdArgumentInfo * const dummy = new DCmdArgumentInfo(nullptr,
|
||||
|
@ -37,6 +37,7 @@ class JfrDCmd : public DCmd {
|
||||
JfrDCmd(outputStream* output, bool heap, int num_arguments);
|
||||
virtual const char* javaClass() const = 0;
|
||||
void invoke(JfrJavaArguments& method, TRAPS) const;
|
||||
void print_java_help(const char* help_method) const;
|
||||
public:
|
||||
virtual void execute(DCmdSource source, TRAPS);
|
||||
virtual void print_help(const char* name) const;
|
||||
|
@ -55,6 +55,7 @@ import jdk.jfr.internal.jfc.JFC;
|
||||
import jdk.jfr.internal.jfc.model.JFCModel;
|
||||
import jdk.jfr.internal.jfc.model.JFCModelException;
|
||||
import jdk.jfr.internal.jfc.model.XmlInput;
|
||||
import jdk.jfr.internal.util.Utils;
|
||||
|
||||
/**
|
||||
* JFR.start
|
||||
@ -317,11 +318,35 @@ final class DCmdStart extends AbstractDCmd {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String[] printStartupHelp() {
|
||||
Map<String, String> parameters = Map.of(
|
||||
"$SYNTAX", "-XX:StartFlightRecording:[options]",
|
||||
"$SOURCE", "-XX:StartFlightRecording:",
|
||||
"$DELIMITER", ",",
|
||||
"$DELIMITER_NAME", "comma",
|
||||
"$DIRECTORY", exampleDirectory(),
|
||||
"$JFC_OPTIONS", jfcOptions()
|
||||
);
|
||||
return Utils.format(helpTemplate(), parameters).lines().toArray(String[]::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] printHelp() {
|
||||
Map<String, String> parameters = Map.of(
|
||||
"$SYNTAX", "JFR.start [options]",
|
||||
"$SOURCE", "$ jcmd <pid> JFR.start ",
|
||||
"$DELIMITER", " ",
|
||||
"$DELIMITER_NAME", "whitespace",
|
||||
"$DIRECTORY", exampleDirectory(),
|
||||
"$JFC_OPTIONS", jfcOptions()
|
||||
);
|
||||
return Utils.format(helpTemplate(), parameters).lines().toArray(String[]::new);
|
||||
}
|
||||
|
||||
private static String helpTemplate() {
|
||||
// 0123456789001234567890012345678900123456789001234567890012345678900123456789001234567890
|
||||
return """
|
||||
Syntax : JFR.start [options]
|
||||
Syntax : $SYNTAX
|
||||
|
||||
Options:
|
||||
|
||||
@ -352,8 +377,8 @@ final class DCmdStart extends AbstractDCmd {
|
||||
generated from the PID and the current date in the specified
|
||||
directory. (STRING, no default value)
|
||||
|
||||
Note: If a filename is given, '%%p' in the filename will be
|
||||
replaced by the PID, and '%%t' will be replaced by the time in
|
||||
Note: If a filename is given, '%p' in the filename will be
|
||||
replaced by the PID, and '%t' will be replaced by the time in
|
||||
'yyyy_MM_dd_HH_mm_ss' format.
|
||||
|
||||
maxage (Optional) Maximum time to keep the recorded data on disk. This
|
||||
@ -409,27 +434,28 @@ final class DCmdStart extends AbstractDCmd {
|
||||
take precedence. The whitespace character can be omitted for timespan values,
|
||||
i.e. 20s. For more information about the settings syntax, see Javadoc of the
|
||||
jdk.jfr package.
|
||||
%s
|
||||
Options must be specified using the <key> or <key>=<value> syntax.
|
||||
$JFC_OPTIONS
|
||||
Options must be specified using the <key> or <key>=<value> syntax. Multiple
|
||||
options are separated with a $DELIMITER_NAME.
|
||||
|
||||
Example usage:
|
||||
|
||||
$ jcmd <pid> JFR.start
|
||||
$ jcmd <pid> JFR.start filename=dump.jfr
|
||||
$ jcmd <pid> JFR.start filename=%s
|
||||
$ jcmd <pid> JFR.start dumponexit=true
|
||||
$ jcmd <pid> JFR.start maxage=1h maxsize=1000M
|
||||
$ jcmd <pid> JFR.start settings=profile
|
||||
$ jcmd <pid> JFR.start delay=5m settings=my.jfc
|
||||
$ jcmd <pid> JFR.start gc=high method-profiling=high
|
||||
$ jcmd <pid> JFR.start jdk.JavaMonitorEnter#threshold=1ms
|
||||
$ jcmd <pid> JFR.start +HelloWorld#enabled=true +HelloWorld#stackTrace=true
|
||||
$ jcmd <pid> JFR.start settings=user.jfc com.example.UserDefined#enabled=true
|
||||
$ jcmd <pid> JFR.start settings=none +Hello#enabled=true
|
||||
$SOURCE
|
||||
$SOURCEfilename=dump.jfr
|
||||
$SOURCEfilename=$DIRECTORY
|
||||
$SOURCEdumponexit=true
|
||||
$SOURCEmaxage=1h$DELIMITERmaxsize=1000M
|
||||
$SOURCEsettings=profile
|
||||
$SOURCEdelay=5m$DELIMITERsettings=my.jfc
|
||||
$SOURCEgc=high$DELIMITERmethod-profiling=high
|
||||
$SOURCEjdk.JavaMonitorEnter#threshold=1ms
|
||||
$SOURCE+HelloWorld#enabled=true$DELIMITER+HelloWorld#stackTrace=true
|
||||
$SOURCEsettings=user.jfc$DELIMITERcom.example.UserDefined#enabled=true
|
||||
$SOURCEsettings=none$DELIMITER+Hello#enabled=true
|
||||
|
||||
Note, if the default event settings are modified, overhead may exceed 1%%.
|
||||
Note, if the default event settings are modified, overhead may exceed 1%.
|
||||
|
||||
""".formatted(jfcOptions(), exampleDirectory()).lines().toArray(String[]::new);
|
||||
""";
|
||||
}
|
||||
|
||||
private static String jfcOptions() {
|
||||
|
@ -37,6 +37,7 @@ import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -445,4 +446,26 @@ public final class Utils {
|
||||
public static String makeSimpleName(String qualified) {
|
||||
return qualified.substring(qualified.lastIndexOf(".") + 1);
|
||||
}
|
||||
|
||||
public static String format(String template, Map<String, String> parameters) {
|
||||
StringBuilder sb = new StringBuilder(3 * template.length() / 2);
|
||||
List<String> keys = new ArrayList<>(parameters.keySet());
|
||||
// Sort so longest keys are checked first in case keys overlap.
|
||||
keys.sort((a, b) -> b.length() - a.length());
|
||||
for (int i = 0; i < template.length(); i++) {
|
||||
int index = i;
|
||||
for (int j = 0; j < keys.size(); j++) {
|
||||
String key = keys.get(j);
|
||||
if (template.startsWith(key, i)) {
|
||||
sb.append(parameters.get(key));
|
||||
i += key.length() - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == index) {
|
||||
sb.append(template.charAt(i));
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
47
test/jdk/jdk/jfr/startupargs/TestStartHelp.java
Normal file
47
test/jdk/jdk/jfr/startupargs/TestStartHelp.java
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.jfr.startupargs;
|
||||
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.test.lib.Asserts;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @key jfr
|
||||
* @requires vm.hasJFR
|
||||
* @library /test/lib /test/jdk
|
||||
* @run main jdk.jfr.startupargs.TestStartHelp
|
||||
*/
|
||||
public class TestStartHelp {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder("-XX:StartFlightRecording:help");
|
||||
OutputAnalyzer out = ProcessTools.executeProcess(pb);
|
||||
out.shouldContain("Syntax : -XX:StartFlightRecording:[options]");
|
||||
out.shouldContain("options are separated with a comma.");
|
||||
out.shouldHaveExitValue(0);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user