8260862: JFR: New configure command for the jfr tool

Reviewed-by: mgronlun
This commit is contained in:
Erik Gahlin 2021-03-25 20:43:16 +00:00
parent 259319669c
commit 4e708e58dc
43 changed files with 3227 additions and 274 deletions

View File

@ -6,29 +6,19 @@
<xsd:complexType name="configurationType">
<xsd:sequence>
<xsd:element minOccurs="0" maxOccurs="unbounded" name="producer" type="producerType" />
</xsd:sequence>
<xsd:attribute use="required" name="version" type="xsd:decimal" />
<xsd:attribute use="required" name="name" type="xsd:string" />
<xsd:attribute use="optional" name="description" type="xsd:string" default="" />
<xsd:attribute use="optional" name="provider" type="xsd:string" default="" />
</xsd:complexType>
<xsd:complexType name="producerType">
<xsd:sequence>
<xsd:element minOccurs="0" maxOccurs="1" name="control" type="controlType" />
<xsd:element minOccurs="0" maxOccurs="unbounded" name="event" type="eventType" />
</xsd:sequence>
<xsd:attribute use="required" name="uri" type="xsd:anyURI" />
<xsd:attribute use="optional" name="label" type="xsd:string" default="" />
<xsd:attribute use="required" name="version" type="xsd:decimal" />
<xsd:attribute use="required" name="label" type="xsd:string" />
<xsd:attribute use="optional" name="description" type="xsd:string" default="" />
<xsd:attribute use="optional" name="provider" type="xsd:string" default="" />
</xsd:complexType>
<xsd:complexType name="eventType">
<xsd:sequence>
<xsd:element minOccurs="0" maxOccurs="unbounded" name="setting" type="settingType" />
</xsd:sequence>
<xsd:attribute use="required" name="path" type="xsd:string" />
<xsd:attribute use="required" name="name" type="xsd:string" />
<xsd:attribute use="optional" name="label" type="xsd:string" />
<xsd:attribute use="optional" name="description" type="xsd:string" />
</xsd:complexType>

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
public final class AbortException extends Exception {
private static final long serialVersionUID = -2501519883611363246L;
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
record Constraint(Class<? extends XmlElement> type, int min, int max) {
public static Constraint any(Class<? extends XmlElement> type) {
return new Constraint(type, 0, Integer.MAX_VALUE);
}
public static Constraint atLeast(Class<? extends XmlElement> type, int min) {
return new Constraint(type, min, Integer.MAX_VALUE);
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
// Interface for elements that can control a setting,
// <condition>, <selection>, <text> and <flag>
interface ControlElement {
String getName();
}

View File

@ -0,0 +1,206 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.Charset;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import jdk.jfr.internal.SecuritySupport.SafePath;
// Holds the structure of a .jfc file similar to an XML DOM.
public final class JFCModel {
private final Map<String, List<ControlElement>> controls = new LinkedHashMap<>();
private final XmlConfiguration configuration;
public JFCModel(SafePath file) throws ParseException, IOException {
this.configuration = createConfiguration(file);
this.configuration.validate();
addControls();
wireConditions();
wireSettings();
}
public JFCModel(List<SafePath> files) throws IOException, ParseException {
this.configuration = new XmlConfiguration();
this.configuration.setAttribute("version", "2.0");
for (SafePath file : files) {
JFCModel model = new JFCModel(file);
for (var entry : model.controls.entrySet()) {
String name = entry.getKey();
// Fail-fast checks that prevents an ambiguous file to be written later
if (controls.containsKey(name)) {
throw new ParseException("Control with '" + name + "' is declared in multiple files", 0);
}
controls.put(name, entry.getValue());
}
for (XmlElement child : model.configuration.getChildren()) {
this.configuration.addChild(child);
}
}
}
public void setLabel(String label) {
configuration.setAttribute("label", label);
}
public void configure(String name, String value) {
for (XmlInput i : getInputs()) {
if (i.getName().equals(name)) {
i.configure(value);
return;
}
}
boolean add = name.startsWith("+");
if (add) {
name = name.substring(1);
}
int index = name.indexOf("#");
if (index < 1 || index == name.length() - 1) {
throw new IllegalArgumentException("Option '" + name + "' doesn't exist in configuration");
}
XmlEvent event = configuration.getEvent(name.substring(0, index), add);
String settingName = name.substring(index + 1);
XmlSetting setting = event.getSetting(settingName, add);
if (settingName.equals("period") || settingName.equals("threshold")) {
try {
value = Utilities.parseTimespan(value);
} catch (IllegalArgumentException iae) {
// OK, no validation to allow forward compatibility.
}
}
setting.setContent(value);
}
public void configure(UserInterface ui) throws AbortException {
for (XmlInput input : getInputs()) {
input.configure(ui);
}
}
public List<XmlInput> getInputs() {
List<XmlInput> inputs = new ArrayList<>();
for (XmlControl control : configuration.getControls()) {
inputs.addAll(control.getInputs());
}
return inputs;
}
public XmlConfiguration getConfiguration() {
return configuration;
}
public void saveToFile(SafePath path) throws IOException {
try (PrintWriter p = new PrintWriter(path.toFile(), Charset.forName("UTF-8"))) {
PrettyPrinter pp = new PrettyPrinter(p);
pp.print(configuration);
if (p.checkError()) {
throw new IOException("Error writing " + path);
}
}
}
private List<ControlElement> getControlElements(String name) {
return controls.getOrDefault(name, Collections.emptyList());
}
private void addControls() {
for (var controls : configuration.getControls()) {
for (var control : controls.getControlElements()) {
add(control);
}
}
}
private void wireConditions() {
for (XmlControl control : configuration.getControls()) {
for (XmlCondition condition : control.getConditions()) {
for (XmlElement element : condition.getChildren()) {
wireExpression(condition, element);
}
}
}
}
private void wireExpression(XmlElement parent, XmlElement element) {
element.addListener(parent);
if (element instanceof XmlTest test) {
wireTest(test);
}
if (element instanceof XmlExpression expression) {
for (XmlExpression child : expression.getExpressions()) {
wireExpression(expression, child);
}
}
}
private void wireTest(XmlTest test) {
String name = test.getName();
for (ControlElement ce : getControlElements(name)) {
XmlElement control = (XmlElement) ce;
control.addListener(test);
}
}
private void wireSettings() {
for (XmlEvent event : configuration.getEvents()) {
for (XmlSetting setting : event.getSettings()) {
var controlName = setting.getControl();
if (controlName.isPresent()) {
List<ControlElement> controls = getControlElements(controlName.get());
if (controls.isEmpty()) {
System.out.println("Warning! Setting '" + setting.getFullName() + "' refers to missing control '" + controlName.get() + "'");
}
for (ControlElement ce : controls) {
XmlElement control = (XmlElement) ce;
control.addListener(setting);
}
}
}
}
}
private void add(ControlElement control) {
controls.computeIfAbsent(control.getName(), x -> new ArrayList<>()).add(control);
}
private XmlConfiguration createConfiguration(SafePath file) throws ParseException, IOException {
if (file.toString().equals("none")) {
XmlConfiguration configuration = new XmlConfiguration();
configuration.setAttribute("version", "2.0");
configuration.setAttribute("label", "None");
return configuration;
}
return Parser.parse(file.toPath());
}
}

View File

@ -0,0 +1,106 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.text.ParseException;
import java.util.ArrayDeque;
import java.util.Deque;
import jdk.internal.org.xml.sax.Attributes;
import jdk.internal.org.xml.sax.InputSource;
import jdk.internal.org.xml.sax.SAXException;
import jdk.internal.org.xml.sax.helpers.DefaultHandler;
import jdk.internal.util.xml.SAXParser;
import jdk.internal.util.xml.impl.SAXParserImpl;
final class Parser {
static XmlConfiguration parse(Path path) throws ParseException, IOException {
try (FileReader r = new FileReader(path.toFile(), Charset.forName("UTF-8"))) {
SAXParser saxParser = new SAXParserImpl();
ConfigurationHandler handler = new ConfigurationHandler();
saxParser.parse(new InputSource(r), handler);
return handler.configuration;
} catch (SAXException sp) {
ParseException pe = new ParseException(sp.getMessage(), -1);
pe.initCause(sp);
throw pe;
}
}
private static final class ConfigurationHandler extends DefaultHandler {
private final Deque<XmlElement> stack = new ArrayDeque<>();
private final StringBuilder buffer = new StringBuilder();
private XmlConfiguration configuration;
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (configuration == null) {
if (!qName.equalsIgnoreCase("configuration")) {
throw new SAXException("Expected root element to be named 'configuration'");
}
configuration = new XmlConfiguration();
addAttributes(configuration, attributes);
stack.push(configuration);
return;
}
XmlElement current = stack.peek();
XmlElement child = current.createChild(qName);
addAttributes(child, attributes);
stack.push(child);
}
@Override
public void characters(char ch[], int start, int length) throws SAXException {
buffer.append(ch, start, length);
}
@Override
public void endElement(String uri, String localName, String qName) {
String content = buffer.toString().strip();
if (!content.isEmpty()) {
stack.peek().setContent(content);
buffer.setLength(0);
}
XmlElement current = stack.peek();
if (current.getElementName().equalsIgnoreCase(qName)) {
stack.pop();
} else {
throw new IllegalStateException("Unexpected <" + qName + "/>");
}
}
private void addAttributes(XmlElement element, Attributes attributes) {
for (int i = 0; i < attributes.getLength(); i++) {
element.setAttribute(attributes.getQName(i), attributes.getValue(i));
}
}
}
}

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
import java.io.PrintWriter;
import java.util.Map;
final class PrettyPrinter {
private final PrintWriter out;
PrettyPrinter(PrintWriter out) {
this.out = out;
}
void print(XmlConfiguration configuration) {
printHeader();
prettyPrint("", configuration);
}
private void printHeader() {
out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
}
private void prettyPrint(String indent, XmlElement element) {
printComment(indent, element);
String elementName = element.getElementName();
out.print(indent + '<' + elementName);
printAttributes(element.getAttributes());
if (element.getChildren().isEmpty() && !element.hasContent()) {
out.println("/>");
return;
}
out.print('>');
out.print(Utilities.escapeAll(element.getContent().trim()));
if (element.getChildren().isEmpty()) {
out.println("</" + elementName + '>');
return;
}
out.println();
boolean first = true;
for (XmlElement child : element.getChildren()) {
if (first && child.isEntity()) {
out.println();
}
prettyPrint(indent + " ", child);
if (child.isEntity()) {
out.println();
}
first = false;
}
out.println(indent + "</" + elementName + '>');
}
private void printComment(String indent, XmlElement element) {
String comment = element.comment();
if (!comment.isEmpty()) {
String text = comment.indent(indent.length());
out.println(indent + "<!--");
out.println(text.replace("\n", System.lineSeparator()));
out.println(indent + "-->");
}
}
private void printAttributes(Map<String, String> attributes) {
for (var entry : attributes.entrySet()) {
out.print(' ');
out.print(entry.getKey());
out.print("=\"");
out.print(Utilities.escapeAll(entry.getValue()));
out.print('\"');
}
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
record Result(String value) {
public static final Result FALSE = new Result("false");
public static final Result TRUE = new Result("true");
public static final Result NULL = new Result(null);
public boolean isTrue() {
return "true".equalsIgnoreCase(value);
}
public boolean isNull() {
return value == null;
}
public boolean isFalse() {
return "false".equalsIgnoreCase(value);
}
public static Result of(String value) {
if ("true".equalsIgnoreCase(value)) {
return TRUE;
}
if ("false".equalsIgnoreCase(value)) {
return FALSE;
}
return new Result(value);
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
import java.util.Map;
import java.util.TreeMap;
public final class SettingsLog {
private static final Map<String, String> settings = new TreeMap<>();
private static boolean enabled;
public static void enable() {
enabled = true;
}
public static void flush() {
if (!settings.isEmpty()) {
System.out.println();
System.out.println("Setting:");
for (var s : settings.entrySet()) {
System.out.println("\"" + s.getKey() + "=" + s.getValue() + "\"");
}
settings.clear();
}
}
static void log(XmlSetting setting, String value) {
if (enabled) {
settings.put(setting.getFullName(), value);
}
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public final class UserInterface {
public void println() {
System.out.println();
}
public void println(String text) {
System.out.println(text);
}
public String readLine() throws AbortException {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = br.readLine();
if (line == null || line.equalsIgnoreCase("Q")) {
println();
throw new AbortException();
}
return line;
} catch (IOException e) {
throw new Error("Unable to read input", e);
}
}
}

View File

@ -0,0 +1,126 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
import java.util.StringJoiner;
public final class Utilities {
private static final String[] UNITS = new String[] {
"ns", "us", "ns", "ms", "s", "m", "h", "d" // order matters
};
static XmlElement instantiate(Class<? extends XmlElement> type) {
try {
return type.getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw new InternalError("Unable to instantiate " + type, e);
}
}
static String elementName(Class<? extends XmlElement> type) {
String name = type.getSimpleName();
if (name.startsWith("Xml") && name.length() > 3) {
return name.substring(3).toLowerCase();
}
throw new InternalError("Unexpected class " + type);
}
static String escapeAll(String text) {
StringBuilder s = new StringBuilder();
for (int i = 0; i < text.length(); i++) {
addCharacter(s, text.charAt(i));
}
return s.toString();
}
private static void addCharacter(StringBuilder s, char c) {
if (c == 34) {
s.append("&quot;");
return;
}
if (c == 38) {
s.append("&amp;");
return;
}
if (c == 39) {
s.append("&apos;");
return;
}
if (c == 60) {
s.append("&lt;");
return;
}
if (c == 62) {
s.append("&gt;");
return;
}
if (c > 0x7F) {
s.append("&#");
s.append((int) c);
s.append(';');
return;
}
s.append(c);
}
static void checkValid(String value, Object... valid) {
StringJoiner sj = new StringJoiner(", ");
for (Object v : valid) {
if (v.equals(value)) {
return;
}
sj.add("'" + v + "'");
}
String msg = "Incorrect value '" + value + "'. Valid values are " + sj.toString() + ".";
int index = msg.lastIndexOf(",");
if (index != -1) {
msg = msg.substring(0, index) + " and" + msg.substring(index + 1);
}
throw new IllegalArgumentException(msg);
}
static String parseTimespan(String s) {
StringBuilder sb = new StringBuilder();
try {
for (String unit : UNITS) {
if (s.endsWith(unit)) {
return parseForUnit(s, unit);
}
}
Long.parseLong(s);
sb.append("Timespan '" + s + "' is missing unit.");
} catch (NumberFormatException nfe) {
sb.append("'" + s + "' is not a valid timespan." + System.lineSeparator());
sb.append("Should be numeric value followed by a unit, i.e. 20 ms.");
}
sb.append(" Valid units are ns, us, ms, s, m, h and d.");
throw new IllegalArgumentException(sb.toString());
}
private static String parseForUnit(String s, String unit) {
String number = s.trim().substring(0, s.length() - unit.length());
return Long.parseLong(number.trim()) + " " + unit;
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
// Corresponds to <and>
final class XmlAnd extends XmlExpression {
@Override
boolean isEntity() {
return false;
}
@Override
protected Result evaluate() {
Result result = Result.NULL;
for (XmlElement e : getProducers()) {
Result r = e.evaluate();
if (r.isFalse()) {
return Result.FALSE;
}
if (r.isTrue()) {
result = Result.TRUE;
}
}
return result;
}
}

View File

@ -0,0 +1,74 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
import java.text.ParseException;
import java.util.List;
import java.util.Optional;
// Corresponds <condition>
final class XmlCondition extends XmlExpression implements ControlElement {
@Override
public String getName() {
return attribute("name");
}
public Result getTrueValue() {
return Result.of(attribute("true"));
}
public Result getFalseValue() {
return Result.of(attribute("false"));
}
@Override
protected void validateChildConstraints() throws ParseException {
if (getExpressions().size() > 1) {
throw new ParseException("Expected <condition> to not have more than one child", -1);
}
}
@Override
protected List<String> attributes() {
return List.of("name");
}
@Override
protected Result evaluate() {
Optional<String> trueValue = optional("true");
Optional<String> falseValue = optional("false");
for (XmlElement producer : getProducers()) {
Result r = producer.evaluate();
if (trueValue.isPresent() && r.isTrue()) {
return getTrueValue();
}
if (falseValue.isPresent() && r.isFalse()) {
return getFalseValue();
}
}
return Result.NULL;
}
}

View File

@ -0,0 +1,102 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
import java.text.ParseException;
import java.util.List;
import java.util.Optional;
// Corresponds to <configuration>
final class XmlConfiguration extends XmlElement {
public List<XmlEvent> getEvents() {
return elements(XmlEvent.class);
}
public Optional<String> getDescription() {
return optional("description");
}
public Optional<String> getLabel() {
return optional("label");
}
public Optional<String> getProvider() {
return optional("provider");
}
public Optional<String> getVersion() {
return optional("version");
}
public List<XmlControl> getControls() {
return elements(XmlControl.class);
}
@Override
String comment() {
return """
Recommended way to edit .jfc files is to use the configure command of
the 'jfr' tool, i.e. jfr configure, or JDK Mission Control
see Window -> Flight Recorder Template Manager
""";
}
XmlEvent getEvent(String eventName, boolean add) {
for (XmlEvent event : getEvents()) {
if (eventName.equals(event.getName())) {
return event;
}
}
if (!add) {
throw new IllegalArgumentException("Could not find event '" + eventName + "'");
}
XmlEvent event = new XmlEvent();
event.setAttribute("name", eventName);
addChild(event);
return event;
}
@Override
protected List<String> attributes() {
return List.of("version", "label");
}
@Override
protected void validateAttributes() throws ParseException {
super.validateAttributes();
if (!attribute("version").equals("2.0")) {
throw new ParseException("Only .jfc files of version 2.0 is supported", -1);
}
}
@Override
protected List<Constraint> constraints() {
return List.of(
Constraint.any(XmlEvent.class),
Constraint.any(XmlControl.class)
);
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
import java.util.List;
// Corresponds to <control>
final class XmlControl extends XmlElement {
public List<XmlInput> getInputs() {
return elements(XmlInput.class);
}
public List<XmlCondition> getConditions() {
return elements(XmlCondition.class);
}
// Returns list of all <selection>, <condition>, <flag> and <text>
public List<ControlElement> getControlElements() {
return elements(ControlElement.class);
}
@Override
String comment() {
return """
Contents of the control element is not read by the JVM, it's used
by JDK Mission Control and the 'jfr'-tool to change settings that
carry the control attribute.
""";
}
@Override
protected List<Constraint> constraints() {
return List.of(
Constraint.any(XmlCondition.class),
Constraint.any(XmlText.class),
Constraint.any(XmlSelection.class),
Constraint.any(XmlFlag.class)
);
}
}

View File

@ -0,0 +1,227 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
// Base class for XML-elements
class XmlElement {
private final Map<String, String> attributes = new HashMap<>();
private final List<XmlElement> elements = new ArrayList<>();
private final List<XmlElement> listeners = new ArrayList<>(2);
private final List<XmlElement> producers = new ArrayList<>(2);
private final String elementName;
private XmlElement parent;
private String content = "";
XmlElement() {
this.elementName = Utilities.elementName(this.getClass());
}
private XmlElement(String elementName) {
this.elementName = elementName;
}
final String getElementName() {
return elementName;
}
final boolean hasContent() {
return content != null && !content.isEmpty();
}
final Map<String, String> getAttributes() {
return attributes;
}
final void validate() throws ParseException {
validateAttributes();
validateChildConstraints();
validateChildren();
}
final void setAttribute(String key, String value) {
Objects.requireNonNull(key);
Objects.requireNonNull(key);
attributes.put(key, value);
}
final XmlElement createChild(String name) {
XmlElement child = instantiate(name);
child.parent = this;
addChild(child);
return child;
}
private XmlElement instantiate(String name) {
for (var e : constraints()) {
String elementName = Utilities.elementName(e.type());
if (elementName.equalsIgnoreCase(name)) {
return Utilities.instantiate(e.type());
}
}
return new XmlElement(name);
}
final void addChild(XmlElement element) {
elements.add(element);
}
final List<XmlElement> getChildren() {
return elements;
}
void setContent(String content) {
this.content = content;
}
final String getContent() {
return content;
}
final void addListener(XmlElement listener) {
listeners.add(listener);
listener.addProducer(this);
}
// If the element should be surrounded with an empty
// line when printed to file, for example, <event> but not <setting>
boolean isEntity() {
return true;
}
String comment() {
return "";
}
protected final void notifyListeners() {
onChange();
SettingsLog.flush();
}
protected void onChange() {
for (XmlElement listener : listeners) {
listener.onChange();
}
}
protected final XmlElement getParent() {
return parent;
}
protected List<Constraint> constraints() {
return List.of();
}
protected List<String> attributes() {
return List.of();
}
protected final List<XmlElement> getProducers() {
return producers;
}
protected final Optional<String> optional(String name) {
return Optional.ofNullable(attributes.get(name));
}
protected final String attribute(String name) {
return attributes.get(name);
}
@SuppressWarnings("unchecked")
protected final <T> List<T> elements(Class<T> type) {
List<T> result = new ArrayList<>();
for (XmlElement e : elements) {
if (type.isAssignableFrom(e.getClass())) {
result.add((T) e);
}
}
return result;
}
protected Result evaluate() {
if (producers.isEmpty()) {
throw new Error("No producer evaluate for " + getClass());
}
if (producers.size() != 1) {
throw new Error("Unsure how to evaluate multiple producers " + getClass());
}
return producers.get(0).evaluate();
}
protected void validateAttributes() throws ParseException {
for (String key : attributes()) {
if (!attributes.containsKey(key)) {
throw new ParseException("Missing mandatory attribute '" + key + "'", 0);
}
}
}
private void validateChildren() throws ParseException {
for (XmlElement child : elements) {
child.validate();
}
}
protected void validateChildConstraints() throws ParseException {
for (Constraint c : constraints()) {
validateConstraint(c);
}
}
private final void validateConstraint(Constraint c) throws ParseException {
int count = count(c.type());
if (count < c.min()) {
String elementName = Utilities.elementName(c.type());
throw new ParseException("Missing mandatory element <" + elementName + ">", 0);
}
if (count > c.max()) {
String elementName = Utilities.elementName(c.type());
throw new ParseException("Too many elements of type <" + elementName + ">", 0);
}
}
private void addProducer(XmlElement producer) {
producers.add(producer);
}
private int count(Class<? extends XmlElement> type) {
int count = 0;
for (XmlElement element : getChildren()) {
if (type.isAssignableFrom(element.getClass())) {
count++;
}
}
return count;
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
import java.util.List;
// Corresponds to <event>
final class XmlEvent extends XmlElement {
public String getName() {
return attribute("name");
}
public List<XmlSetting> getSettings() {
return elements(XmlSetting.class);
}
XmlSetting getSetting(String settingName, boolean add) {
for (XmlSetting setting : getSettings()) {
if (settingName.equals(setting.getName())) {
return setting;
}
}
if (!add) {
String msg = "Could not find setting '" + settingName;
msg += "' for event '" + getName() + "'";
throw new IllegalArgumentException(msg);
}
XmlSetting setting = new XmlSetting();
setting.setAttribute("name", settingName);
addChild(setting);
return setting;
}
@Override
protected List<String> attributes() {
return List.of("name");
}
@Override
protected List<Constraint> constraints() {
return List.of(Constraint.any(XmlSetting.class));
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
import java.text.ParseException;
import java.util.List;
// Base class for <condition>, <or>, <not>, <and> and <test>
abstract class XmlExpression extends XmlElement {
public final List<XmlExpression> getExpressions() {
return elements(XmlExpression.class);
}
@Override
protected List<Constraint> constraints() {
return List.of(
Constraint.any(XmlOr.class),
Constraint.any(XmlAnd.class),
Constraint.any(XmlTest.class),
Constraint.any(XmlNot.class)
);
}
@Override
protected void validateChildConstraints() throws ParseException {
if (getExpressions().size() < 2) {
throw new ParseException("Expected + <" + getElementName() + "> to have at least two children", 0);
}
}
}

View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
// Corresponds to <flag>
final class XmlFlag extends XmlInput {
@Override
public String getOptionSyntax() {
return getName() + "=<true|false>";
}
@Override
public void configure(String value) {
Utilities.checkValid(value, "true", "false");
setContent(value);
notifyListeners();
}
@Override
public void configure(UserInterface ui) throws AbortException {
Result defaultValue = Result.of(getContent());
ui.println();
ui.println(getLabel() + " [Y/N]: " + (defaultValue.isTrue() ? "Yes" : "No") + " (default)");
while (!read(ui, defaultValue)) {
;
}
}
@Override
protected Result evaluate() {
return Result.of(getContent());
}
private boolean read(UserInterface ui, Result defaultValue) throws AbortException {
String line = ui.readLine();
if (line.isBlank()) {
ui.println("Using default: " + (defaultValue.isTrue() ? "Yes" : "No"));
return true;
}
if (line.equalsIgnoreCase("Y") || line.equalsIgnoreCase("N")) {
boolean value = line.equalsIgnoreCase("Y");
ui.println("Using: " + (value ? "Yes" : "No"));
configure(String.valueOf(value));
return true;
}
ui.println("Not a valid choice.");
return false;
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
import java.util.List;
import java.util.Optional;
// Base class for elements that the user can interact with,
// <selection>, <text> and <flag>
public abstract class XmlInput extends XmlElement implements ControlElement {
public abstract String getOptionSyntax();
abstract void configure(UserInterface ui) throws AbortException;
abstract void configure(String value);
public final Optional<String> getContentType() {
return optional("contentType");
}
@Override
public final String getName() {
return attribute("name");
}
public final String getLabel() {
return attribute("label");
}
@Override
protected List<String> attributes() {
return List.of("name", "label");
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
import java.text.ParseException;
import java.util.List;
// Corresponds to <not>
final class XmlNot extends XmlExpression {
@Override
boolean isEntity() {
return false;
}
@Override
protected void validateChildConstraints() throws ParseException {
if (getExpressions().size() != 1) {
throw new ParseException("Expected <not> to have a single child", 0);
}
}
@Override
protected Result evaluate() {
List<XmlElement> producers = getProducers();
if (!producers.isEmpty()) {
Result r = producers.get(0).evaluate();
if (!r.isNull()) {
return r.isTrue() ? Result.FALSE : Result.TRUE;
}
}
return Result.NULL;
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
import java.util.List;
// Corresponds to <option>
final class XmlOption extends XmlElement {
public String getLabel() {
return attribute("label");
}
public String getName() {
return attribute("name");
}
@Override
boolean isEntity() {
return false;
}
@Override
protected List<String> attributes() {
return List.of("label", "name");
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
// Corresponds to <or>
final class XmlOr extends XmlExpression {
@Override
boolean isEntity() {
return false;
}
@Override
protected Result evaluate() {
Result result = Result.NULL;
for (XmlElement e : getProducers()) {
Result r = e.evaluate();
if (r.isFalse()) {
result = Result.FALSE;
}
if (r.isTrue()) {
return Result.TRUE;
}
}
return result;
}
}

View File

@ -0,0 +1,118 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
import java.util.List;
import java.util.StringJoiner;
// Corresponds to <selection>
final class XmlSelection extends XmlInput {
@Override
public String getOptionSyntax() {
StringJoiner sj = new StringJoiner("|", "<", ">");
for (XmlOption option : getOptions()) {
sj.add(option.getName());
}
return getName() + "=" + sj.toString();
}
@Override
public void configure(String value) {
var valid = getOptions().stream().map(XmlOption::getName);
Utilities.checkValid(value, valid.toArray());
setAttribute("default", value);
notifyListeners();
}
@Override
public void configure(UserInterface ui) throws AbortException {
XmlOption selected = getSelected();
if (selected == null) {
return;
}
String label = getLabel();
ui.println();
ui.println(label + ": " + selected.getLabel() + " (default)");
int index = 1;
List<XmlOption> options = getOptions();
for (XmlOption s : options) {
ui.println(index + ". " + s.getLabel());
index++;
}
while (true) {
String line = ui.readLine();
if (line.isBlank()) {
ui.println("Using default: " + selected.getLabel());
return;
}
try {
int s = Integer.parseInt(line) - 1;
if (s >= 0 && s < options.size()) {
XmlOption o = options.get(s);
ui.println("Using: " + o.getLabel());
configure(o.getName());
return;
}
ui.println("Not in range.");
} catch (NumberFormatException nfe) {
ui.println("Must enter a number, or ENTER to select default.");
}
}
}
@Override
protected List<String> attributes() {
return List.of("name", "label", "default");
}
public String getDefault() {
return attribute("default");
}
public List<XmlOption> getOptions() {
return elements(XmlOption.class);
}
@Override
protected List<Constraint> constraints() {
return List.of(Constraint.atLeast(XmlOption.class, 1));
}
@Override
protected Result evaluate() {
return Result.of(getSelected().getContent());
}
private XmlOption getSelected() {
List<XmlOption> options = getOptions();
for (XmlOption optionElement : options) {
if (getDefault().equals(optionElement.getName())) {
return optionElement;
}
}
return options.isEmpty() ? null : options.get(0);
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
import java.util.List;
import java.util.Optional;
// Corresponds to <setting>
final class XmlSetting extends XmlElement {
@Override
public boolean isEntity() {
return false;
}
@Override
protected List<String> attributes() {
return List.of("name");
}
public String getName() {
return attribute("name");
}
public Optional<String> getControl() {
return optional("control");
}
@Override
public void onChange() {
String value = evaluate().value();
if (value != null) {
setContent(value);
}
}
@Override
final void setContent(String value) {
super.setContent(value);
if (getParent() instanceof XmlEvent event) {
SettingsLog.log(this, value);
}
}
@Override
protected Result evaluate() {
for (XmlElement producer : getProducers()) {
Result result = producer.evaluate();
if (!result.isNull()) {
return result;
}
}
return Result.NULL;
}
public String getFullName() {
if (getParent() instanceof XmlEvent event) {
return event.getName() + "#" + getName();
}
return "unknown";
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
import java.text.ParseException;
import java.util.List;
// Corresponds to <test>
final class XmlTest extends XmlExpression {
public String getName() {
return attribute("name");
}
public String getOperator() {
return attribute("operator");
}
public String getValue() {
return attribute("value");
}
@Override
boolean isEntity() {
return false;
}
@Override
protected List<String> attributes() {
return List.of("name", "operator", "value");
}
@Override
protected void validateChildConstraints() throws ParseException {
if (!getExpressions().isEmpty()) {
throw new ParseException("Expected <test> to not have child elements", 0);
}
}
@Override
protected void validateAttributes() throws ParseException {
super.validateAttributes();
if (!getOperator().equalsIgnoreCase("equal")) {
throw new ParseException("Unknown operator '" + getOperator() + "', only supported is 'equal'", 0);
}
}
@Override
protected Result evaluate() {
Result ret = Result.NULL;
List<XmlElement> producers = getProducers();
if (!producers.isEmpty()) {
XmlElement producer = producers.get(0);
Result r = producer.evaluate();
if (!r.isNull()) {
ret = getValue().equals(r.value()) ? Result.TRUE : Result.FALSE;
}
}
return ret;
}
}

View File

@ -0,0 +1,85 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.jfc.model;
// Corresponds to <text>
final class XmlText extends XmlInput {
@Override
public String getOptionSyntax() {
StringBuilder sb = new StringBuilder();
sb.append(getName());
sb.append("=<");
sb.append(getContentType().orElse("text"));
sb.append(">");
return sb.toString();
}
@Override
public void configure(String value) {
if (isTimespan()) {
value = Utilities.parseTimespan(value);
}
setContent(value);
notifyListeners();
}
@Override
public void configure(UserInterface ui) throws AbortException {
ui.println();
ui.println(getLabel() + ": " + getContent() + " (default)");
while (!readInput(ui)) {
;
}
}
@Override
protected Result evaluate() {
return Result.of(getContent());
}
private boolean readInput(UserInterface ui) throws AbortException {
String line = ui.readLine();
if (line.isBlank()) {
ui.println("Using default: " + getContent());
return true;
}
if (isTimespan()) {
try {
line = Utilities.parseTimespan(line);
} catch (IllegalArgumentException iae) {
ui.println(iae.getMessage());
return false;
}
}
ui.println("Using: " + line);
configure(line);
return true;
}
private boolean isTimespan() {
return getContentType().orElse("text").equals("timespan");
}
}

View File

@ -70,7 +70,7 @@ final class Assemble extends Command {
Path file = Paths.get(options.pop());
ensureFileDoesNotExist(file);
ensureJFRFile(file);
ensureFileExtension(file, ".jfr");
try (FileOutputStream fos = new FileOutputStream(file.toFile())) {
List<Path> files = listJFRFiles(repository);

View File

@ -47,13 +47,14 @@ import java.util.function.Predicate;
import jdk.jfr.EventType;
abstract class Command {
public final static String title = "Tool for working with Flight Recorder files (.jfr)";
public final static String title = "Tool for working with Flight Recorder files";
private final static Command HELP = new Help();
private final static List<Command> COMMANDS = createCommands();
private static List<Command> createCommands() {
List<Command> commands = new ArrayList<>();
commands.add(new Print());
commands.add(new Configure());
commands.add(new Metadata());
commands.add(new Summary());
commands.add(new Assemble());
@ -155,6 +156,14 @@ abstract class Command {
public void displayOptionUsage(PrintStream stream) {
}
protected boolean acceptSwitch(Deque<String> options, String expected) throws UserSyntaxException {
if (!options.isEmpty() && options.peek().equals(expected)) {
options.remove();
return true;
}
return false;
}
protected boolean acceptOption(Deque<String> options, String expected) throws UserSyntaxException {
if (expected.equals(options.peek())) {
if (options.size() < 2) {
@ -233,7 +242,7 @@ abstract class Command {
try {
Path path = Paths.get(file).toAbsolutePath();
ensureAccess(path);
ensureJFRFile(path);
ensureFileExtension(path, ".jfr");
return path;
} catch (IOError ioe) {
throw new UserDataException("i/o error reading file '" + file + "', " + ioe.getMessage());
@ -266,9 +275,9 @@ abstract class Command {
return file;
}
final protected void ensureJFRFile(Path path) throws UserDataException {
if (!path.toString().endsWith(".jfr")) {
throw new UserDataException("filename must end with '.jfr'");
final protected void ensureFileExtension(Path path, String extension) throws UserDataException {
if (!path.toString().endsWith(extension)) {
throw new UserDataException("filename must end with '" + extension + "'");
}
}
@ -413,4 +422,4 @@ abstract class Command {
Map<X, Boolean> cache = new HashMap<>();
return t -> cache.computeIfAbsent(cacheFunction.apply(t), x -> filter.test(t));
}
}
}

View File

@ -0,0 +1,301 @@
/*
* Copyright (c) 2021, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.internal.tool;
import java.io.FileNotFoundException;
import java.io.IOError;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import jdk.jfr.internal.SecuritySupport;
import jdk.jfr.internal.SecuritySupport.SafePath;
import jdk.jfr.internal.jfc.JFC;
import jdk.jfr.internal.jfc.model.AbortException;
import jdk.jfr.internal.jfc.model.JFCModel;
import jdk.jfr.internal.jfc.model.SettingsLog;
import jdk.jfr.internal.jfc.model.UserInterface;
import jdk.jfr.internal.jfc.model.XmlInput;
final class Configure extends Command {
private final List<SafePath> inputFiles = new ArrayList<>();
@Override
public List<String> getOptionSyntax() {
List<String> list = new ArrayList<>();
list.add("[--interactive] [--verbose]");
list.add("[--input <files>] [--output <file>]");
list.add("[option=value]* [event-setting=value]*");
return list;
}
@Override
protected String getTitle() {
return "Configures a .jfc file";
}
@Override
public String getName() {
return "configure";
}
@Override
public String getDescription() {
return "Creates a custom .jfc-file for event configuration";
}
@Override
public void displayOptionUsage(PrintStream stream) {
stream.println(" --interactive Interactive mode where the configuration is");
stream.println(" determined by a set of questions.");
stream.println();
stream.println(" --verbose Displays the modified settings.");
stream.println();
stream.println(" --input <files> A comma-separated list of .jfc files from which");
stream.println(" the new configuration is based. If no file is");
stream.println(" specified, the default file in the JDK is used");
stream.println(" (default.jfc). If 'none' is specified, the new");
stream.println(" configuration starts empty.");
stream.println();
stream.println(" --ouput <file> The filename of the generated output file. If not");
stream.println(" specified, the filename custom.jfc will be used.");
stream.println();
stream.println(" option=value The option value to modify. For available options,");
stream.println(" see listed input files below.");
stream.println();
stream.println(" event-setting=value The event setting value to modify. Use the form:");
stream.println(" <event-name>#<setting-name>=<value>");
stream.println(" To add a new event setting, prefix the event name");
stream.println(" with '+'.");
stream.println();
stream.println("The whitespace delimiter can be omitted for timespan values, i.e. 20ms. For");
stream.println("more information about the settings syntax, see Javadoc of the jdk.jfr package.");
ensureInputFiles();
for (SafePath path : inputFiles) {
try {
String name = path.toPath().getFileName().toString();
displayParameters(stream, path, name);
} catch (InvalidPathException | ParseException | IOException e) {
stream.println("Unable read options for " + path + " " + e.getMessage());
}
}
stream.println();
stream.println("To run interactive configuration wizard:");
stream.println();
stream.println(" jfr configure --interactive");
stream.println();
stream.println("Example usage:");
stream.println();
stream.println(" jfr configure gc=high method-profiling=high --output high.jfc");
stream.println();
stream.println(" jfr configure jdk.JavaMonitorEnter#threshold=1ms --output locks.jfc");
stream.println();
stream.println(" jfr configure +HelloWorld#enabled=true +HelloWorld#stackTrace=true");
stream.println();
stream.println(" jfr configure --input default.jfc,third-party.jfc --output unified.jfc");
stream.println();
stream.println(" jfr configure --input none +Hello#enabled=true --output minimal.jfc");
}
private void displayParameters(PrintStream stream, SafePath path, String name) throws ParseException, IOException {
JFCModel parameters = new JFCModel(path);
stream.println();
stream.println("Options for " + name + ":");
stream.println();
for (XmlInput input : parameters.getInputs()) {
stream.println(" " + input.getOptionSyntax());
stream.println();
}
}
@Override
public void execute(Deque<String> options) throws UserSyntaxException, UserDataException {
boolean interactive = false;
boolean log = false;
SafePath output = null;
Map<String, String> keyValues = new LinkedHashMap<>();
int optionCount = options.size();
while (optionCount > 0) {
if (acceptSwitch(options, "--interactive")) {
interactive = true;
}
if (acceptSwitch(options, "--verbose")) {
log = true;
}
if (acceptOption(options, "--input")) {
String value = options.pop();
inputFiles.addAll(makeSafePathList(value));
}
if (acceptOption(options, "--output")) {
if (output != null) {
throw new UserDataException("only one output file can be specified");
}
String value = options.pop();
output = makeJFCPath(value);
}
if (acceptKeyValue(options)) {
String value = options.pop();
var keyValue = value.split("=");
keyValues.put(keyValue[0], keyValue[1]);
}
if (optionCount == options.size()) {
// No progress
throw new UserSyntaxException("unknown option " + options.peek());
}
optionCount = options.size();
}
if (!interactive && output == null && keyValues.isEmpty()) {
throw new UserSyntaxException("missing argument");
}
ensureInputFiles();
configure(interactive, log, output, keyValues);
}
private boolean acceptKeyValue(Deque<String> options) {
if (!options.isEmpty()) {
String keyValue = options.peek();
int index = keyValue.indexOf("=");
return index > 0 && index < keyValue.length() - 1;
}
return false;
}
private void configure(boolean interactive, boolean log, SafePath output, Map<String, String> options) throws UserDataException {
try {
if (output == null) {
output = new SafePath(Path.of("custom.jfc"));
}
UserInterface ui = new UserInterface();
JFCModel model = new JFCModel(inputFiles);
model.setLabel("Custom");
if (log) {
SettingsLog.enable();
}
for (var option : options.entrySet()) {
model.configure(option.getKey(), option.getValue());
}
SettingsLog.flush();
try {
if (interactive) {
int q = model.getInputs().size() + 1;
ui.println("============== .jfc Configuration Wizard ============");
ui.println("This wizard will generate a JFR configuration file by");
ui.println("asking " + q + " questions. Press ENTER to use the default");
ui.println("value, or type Q to abort the wizard.");
model.configure(ui);
output = filename(ui, output);
}
} catch (AbortException e) {
ui.println("Abort.");
return;
}
model.saveToFile(output);
ui.println("Configuration written successfully to:");
ui.println(output.toPath().toAbsolutePath().toString());
} catch (IllegalArgumentException iae) {
throw new UserDataException(iae.getMessage());
} catch (FileNotFoundException ffe) {
throw new UserDataException("could not find file: " + ffe.getMessage());
} catch (IOException ioe) {
throw new UserDataException("i/o error: " + ioe.getMessage());
} catch (ParseException pe) {
pe.printStackTrace();
throw new UserDataException("parsing error: " + pe.getMessage());
}
}
private List<SafePath> makeSafePathList(String value) {
List<SafePath> paths = new ArrayList<>();
for (String name : value.split(",")) {
paths.add(makeSafePath(name));
}
return paths;
}
private SafePath makeSafePath(String path) {
for (SafePath predefined : SecuritySupport.getPredefinedJFCFiles()) {
try {
String name = JFC.nameFromPath(predefined.toPath());
if (name.equals(path) || (name + ".jfc").equals(path)) {
return predefined;
}
} catch (IOException e) {
throw new InternalError("Error in predefined .jfc file", e);
}
}
return new SafePath(path);
}
private void ensureInputFiles() throws InternalError {
if (inputFiles.isEmpty()) {
for (SafePath predefined : SecuritySupport.getPredefinedJFCFiles()) {
if (predefined.toString().endsWith("default.jfc")) {
inputFiles.add(predefined);
}
}
}
}
private static SafePath filename(UserInterface ui, SafePath file) throws AbortException {
ui.println();
ui.println("Filename: " + file + " (default)");
while (true) {
String line = ui.readLine();
try {
if (line.isBlank()) {
return file;
}
if (line.endsWith(".jfc")) {
return new SafePath(line);
}
ui.println("Filename must end with .jfc.");
} catch (InvalidPathException ipe) {
ui.println("Not a valid filename. " + ipe.getMessage());
}
}
}
private SafePath makeJFCPath(String file) throws UserDataException, UserSyntaxException {
if (file.startsWith("--")) {
throw new UserSyntaxException("missing file");
}
try {
Path path = Path.of(file).toAbsolutePath();
ensureFileExtension(path, ".jfc");
return new SafePath(path);
} catch (IOError ioe) {
throw new UserDataException("i/o error reading file '" + file + "', " + ioe.getMessage());
} catch (InvalidPathException ipe) {
throw new UserDataException("invalid path '" + file + "'");
}
}
}

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Recommended way to edit .jfc files is to use Java Mission Control,
see Window -> Flight Recorder Template Manager.
Recommended way to edit .jfc files is to use the configure command of
the 'jfr' tool, i.e. jfr configure, or JDK Mission Control
see Window -> Flight Recorder Template Manager
-->
<configuration version="2.0" label="Continuous" description="Low overhead configuration safe for continuous use in production environments, typically less than 1 % overhead." provider="Oracle">
@ -64,31 +64,31 @@
<event name="jdk.ThreadSleep">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="synchronization-threshold">20 ms</setting>
<setting name="threshold" control="locking-threshold">20 ms</setting>
</event>
<event name="jdk.ThreadPark">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="synchronization-threshold">20 ms</setting>
<setting name="threshold" control="locking-threshold">20 ms</setting>
</event>
<event name="jdk.JavaMonitorEnter">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="synchronization-threshold">20 ms</setting>
<setting name="threshold" control="locking-threshold">20 ms</setting>
</event>
<event name="jdk.JavaMonitorWait">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="synchronization-threshold">20 ms</setting>
<setting name="threshold" control="locking-threshold">20 ms</setting>
</event>
<event name="jdk.JavaMonitorInflate">
<setting name="enabled">false</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="synchronization-threshold">20 ms</setting>
<setting name="threshold" control="locking-threshold">20 ms</setting>
</event>
<event name="jdk.SyncOnValueBasedClass">
@ -120,13 +120,13 @@
</event>
<event name="jdk.ClassLoad">
<setting name="enabled" control="class-loading-enabled">false</setting>
<setting name="enabled" control="class-loading">false</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold">0 ms</setting>
</event>
<event name="jdk.ClassDefine">
<setting name="enabled" control="class-loading-enabled">false</setting>
<setting name="enabled" control="class-loading">false</setting>
<setting name="stackTrace">true</setting>
</event>
@ -143,11 +143,11 @@
</event>
<event name="jdk.ClassRedefinition">
<setting name="enabled" control="class-loading-enabled">true</setting>
<setting name="enabled" control="class-loading">true</setting>
</event>
<event name="jdk.ClassUnload">
<setting name="enabled" control="class-loading-enabled">false</setting>
<setting name="enabled" control="class-loading">false</setting>
</event>
<event name="jdk.JVMInformation">
@ -207,7 +207,7 @@
<event name="jdk.ThreadDump">
<setting name="enabled" control="thread-dump-enabled">true</setting>
<setting name="period" control="thread-dump-interval">everyChunk</setting>
<setting name="period" control="thread-dump">everyChunk</setting>
</event>
<event name="jdk.IntFlag">
@ -274,7 +274,7 @@
</event>
<event name="jdk.ObjectCount">
<setting name="enabled" control="heap-statistics-enabled">false</setting>
<setting name="enabled" control="gc-enabled-all">false</setting>
<setting name="period">everyChunk</setting>
</event>
@ -382,22 +382,22 @@
</event>
<event name="jdk.GCPhasePauseLevel3">
<setting name="enabled" control="gc-enabled-all">false</setting>
<setting name="enabled" control="gc-enabled-high">false</setting>
<setting name="threshold">0 ms</setting>
</event>
<event name="jdk.GCPhasePauseLevel4">
<setting name="enabled" control="gc-enabled-all">false</setting>
<setting name="enabled" control="gc-enabled-high">false</setting>
<setting name="threshold">0 ms</setting>
</event>
<event name="jdk.GCPhaseConcurrent">
<setting name="enabled" control="gc-enabled-all">true</setting>
<setting name="enabled" control="gc-enabled-high">true</setting>
<setting name="threshold">0 ms</setting>
</event>
<event name="jdk.GCPhaseConcurrentLevel1">
<setting name="enabled" control="gc-enabled-all">true</setting>
<setting name="enabled" control="gc-enabled-high">true</setting>
<setting name="threshold">0 ms</setting>
</event>
@ -443,11 +443,11 @@
</event>
<event name="jdk.PromoteObjectInNewPLAB">
<setting name="enabled" control="promotion-enabled">false</setting>
<setting name="enabled" control="gc-enabled-high">false</setting>
</event>
<event name="jdk.PromoteObjectOutsidePLAB">
<setting name="enabled" control="promotion-enabled">false</setting>
<setting name="enabled" control="gc-enabled-high">false</setting>
</event>
<event name="jdk.ConcurrentModeFailure">
@ -455,7 +455,7 @@
</event>
<event name="jdk.AllocationRequiringGC">
<setting name="enabled" control="gc-enabled-all">false</setting>
<setting name="enabled" control="gc-enabled-high">false</setting>
<setting name="stackTrace">true</setting>
</event>
@ -464,27 +464,27 @@
</event>
<event name="jdk.G1HeapRegionInformation">
<setting name="enabled" control="gc-enabled-all">false</setting>
<setting name="enabled" control="gc-enabled-high">false</setting>
<setting name="period">everyChunk</setting>
</event>
<event name="jdk.G1HeapRegionTypeChange">
<setting name="enabled" control="gc-enabled-all">false</setting>
<setting name="enabled" control="gc-enabled-high">false</setting>
</event>
<event name="jdk.ShenandoahHeapRegionInformation">
<setting name="enabled" control="gc-enabled-all">false</setting>
<setting name="enabled" control="gc-enabled-high">false</setting>
<setting name="period">everyChunk</setting>
</event>
<event name="jdk.ShenandoahHeapRegionStateChange">
<setting name="enabled" control="gc-enabled-all">false</setting>
<setting name="enabled" control="gc-enabled-high">false</setting>
</event>
<event name="jdk.OldObjectSample">
<setting name="enabled" control="memory-leak-detection-enabled">true</setting>
<setting name="stackTrace" control="memory-leak-detection-stack-trace">false</setting>
<setting name="cutoff" control="memory-leak-detection-cutoff">0 ns</setting>
<setting name="enabled" control="old-objects-enabled">true</setting>
<setting name="stackTrace" control="old-objects-stack-trace">false</setting>
<setting name="cutoff" control="old-objects-cutoff">0 ns</setting>
</event>
<event name="jdk.CompilerConfiguration">
@ -605,18 +605,18 @@
</event>
<event name="jdk.ObjectAllocationInNewTLAB">
<setting name="enabled">false</setting>
<setting name="enabled" control="gc-enabled-high">false</setting>
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.ObjectAllocationOutsideTLAB">
<setting name="enabled">false</setting>
<setting name="enabled" control="gc-enabled-high">false</setting>
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.ObjectAllocationSample">
<setting name="enabled" control="enable-object-allocation">true</setting>
<setting name="throttle" control="object-allocation-rate">150/s</setting>
<setting name="enabled" control="object-allocation-enabled">true</setting>
<setting name="throttle" control="allocation-profiling">150/s</setting>
<setting name="stackTrace">true</setting>
</event>
@ -638,31 +638,31 @@
<event name="jdk.FileForce">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="file-io-threshold">20 ms</setting>
<setting name="threshold" control="file-threshold">20 ms</setting>
</event>
<event name="jdk.FileRead">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="file-io-threshold">20 ms</setting>
<setting name="threshold" control="file-threshold">20 ms</setting>
</event>
<event name="jdk.FileWrite">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="file-io-threshold">20 ms</setting>
<setting name="threshold" control="file-threshold">20 ms</setting>
</event>
<event name="jdk.SocketRead">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="socket-io-threshold">20 ms</setting>
<setting name="threshold" control="socket-threshold">20 ms</setting>
</event>
<event name="jdk.SocketWrite">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="socket-io-threshold">20 ms</setting>
<setting name="threshold" control="socket-threshold">20 ms</setting>
</event>
<event name="jdk.Deserialization">
@ -819,67 +819,63 @@
<!--
Contents of the control element is not read by the JVM, it's used
by Java Mission Control to change settings that carry the control attribute.
-->
<!--
Contents of the control element is not read by the JVM, it's used
by JDK Mission Control and the 'jfr' tool to change settings that
carry the control attribute.
-->
<control>
<selection name="gc-level" default="detailed" label="Garbage Collector">
<selection name="gc" default="normal" label="Garbage Collector">
<option label="Off" name="off">off</option>
<option label="Normal" name="detailed">normal</option>
<option label="All" name="all">all</option>
<option label="Normal" name="normal">normal</option>
<option label="Detailed" name="detailed">detailed</option>
<option label="High, incl. TLABs/PLABs (may cause many events)" name="high">high</option>
<option label="All, incl. Heap Statistics (may cause long GCs)" name="all">all</option>
</selection>
<condition name="gc-enabled-normal" true="true" false="false">
<or>
<test name="gc-level" operator="equal" value="normal"/>
<test name="gc-level" operator="equal" value="all"/>
<test name="gc" operator="equal" value="normal"/>
<test name="gc" operator="equal" value="detailed"/>
<test name="gc" operator="equal" value="high"/>
<test name="gc" operator="equal" value="all"/>
</or>
</condition>
<condition name="gc-enabled-detailed" true="true" false="false">
<or>
<test name="gc" operator="equal" value="detailed"/>
<test name="gc" operator="equal" value="high"/>
<test name="gc" operator="equal" value="all"/>
</or>
</condition>
<condition name="gc-enabled-high" true="true" false="false">
<or>
<test name="gc" operator="equal" value="high"/>
<test name="gc" operator="equal" value="all"/>
</or>
</condition>
<condition name="gc-enabled-all" true="true" false="false">
<test name="gc-level" operator="equal" value="all"/>
<test name="gc" operator="equal" value="all"/>
</condition>
<selection name="memory-profiling" default="low" label="Memory Profiling">
<option label="Off" name="off">off</option>
<option label="Object Allocation" name="low">low</option>
<option label="Object Allocation and Promotion" name="medium">medium</option>
<option label="All, including Heap Statistics (May cause long full GCs)" name="all">all</option>
<selection name="allocation-profiling" default="low" label="Allocation Profiling">
<option label="Off" name="off">0/s</option>
<option label="Low" name="low">150/s</option>
<option label="Medium" name="medium">300/s</option>
<option label="High" name="high">1000/s</option>
<option label="Maximum" name="maximum">1000000000/s</option>
</selection>
<condition name="memory-profiling-enabled-low" true="true" false="false">
<test name="memory-profiling" operator="equal" value="low"/>
</condition>
<condition name="object-allocation-enabled" true="true" false="false">
<or>
<test name="memory-profiling" operator="equal" value="low"/>
<test name="memory-profiling" operator="equal" value="medium"/>
<test name="memory-profiling" operator="equal" value="all"/>
</or>
<not>
<test name="allocation-profiling" operator="equal" value="off"/>
</not>
</condition>
<condition name="object-allocation-rate" true="300/s" false="150/s">
<or>
<test name="memory-profiling" operator="equal" value="medium"/>
<test name="memory-profiling" operator="equal" value="all"/>
</or>
</condition>
<condition name="promotion-enabled" true="true" false="false">
<or>
<test name="memory-profiling" operator="equal" value="medium"/>
<test name="memory-profiling" operator="equal" value="all"/>
</or>
</condition>
<condition name="heap-statistics-enabled" true="true" false="false">
<test name="memory-profiling" operator="equal" value="all"/>
</condition>
<selection name="compiler-level" default="normal" label="Compiler">
<selection name="compiler" default="normal" label="Compiler">
<option label="Off" name="off">off</option>
<option label="Normal" name="normal">normal</option>
<option label="Detailed" name="detailed">detailed</option>
@ -887,96 +883,96 @@
</selection>
<condition name="compiler-enabled" true="false" false="true">
<test name="compiler-level" operator="equal" value="off"/>
<test name="compiler" operator="equal" value="off"/>
</condition>
<condition name="compiler-enabled-failure" true="true" false="false">
<or>
<test name="compiler-level" operator="equal" value="detailed"/>
<test name="compiler-level" operator="equal" value="all"/>
<test name="compiler" operator="equal" value="detailed"/>
<test name="compiler" operator="equal" value="all"/>
</or>
</condition>
<condition name="compiler-sweeper-threshold" true="0 ms" false="100 ms">
<test name="compiler-level" operator="equal" value="all"/>
<test name="compiler" operator="equal" value="all"/>
</condition>
<condition name="compiler-compilation-threshold" true="1000 ms">
<test name="compiler-level" operator="equal" value="normal"/>
<test name="compiler" operator="equal" value="normal"/>
</condition>
<condition name="compiler-compilation-threshold" true="100 ms">
<test name="compiler-level" operator="equal" value="detailed"/>
<test name="compiler" operator="equal" value="detailed"/>
</condition>
<condition name="compiler-compilation-threshold" true="0 ms">
<test name="compiler-level" operator="equal" value="all"/>
<test name="compiler" operator="equal" value="all"/>
</condition>
<condition name="compiler-phase-threshold" true="60 s">
<test name="compiler-level" operator="equal" value="normal"/>
<test name="compiler" operator="equal" value="normal"/>
</condition>
<condition name="compiler-phase-threshold" true="10 s">
<test name="compiler-level" operator="equal" value="detailed"/>
<test name="compiler" operator="equal" value="detailed"/>
</condition>
<condition name="compiler-phase-threshold" true="0 s">
<test name="compiler-level" operator="equal" value="all"/>
<test name="compiler" operator="equal" value="all"/>
</condition>
<selection name="method-sampling-interval" default="normal" label="Method Sampling">
<selection name="method-profiling" default="normal" label="Method Profiling">
<option label="Off" name="off">off</option>
<option label="Normal" name="normal">normal</option>
<option label="High" name="high">high</option>
<option label="Ludicrous (High Overhead)" name="ludicrous">ludicrous</option>
<option label="Maximum (High Overhead)" name="max">max</option>
</selection>
<condition name="method-sampling-java-interval" true="999 d">
<test name="method-sampling-interval" operator="equal" value="off"/>
<test name="method-profiling" operator="equal" value="off"/>
</condition>
<condition name="method-sampling-java-interval" true="20 ms">
<test name="method-sampling-interval" operator="equal" value="normal"/>
<test name="method-profiling" operator="equal" value="normal"/>
</condition>
<condition name="method-sampling-java-interval" true="10 ms">
<test name="method-sampling-interval" operator="equal" value="high"/>
<test name="method-profiling" operator="equal" value="high"/>
</condition>
<condition name="method-sampling-java-interval" true="1 ms">
<test name="method-sampling-interval" operator="equal" value="ludicrous"/>
<test name="method-profiling" operator="equal" value="max"/>
</condition>
<condition name="method-sampling-native-interval" true="999 d">
<test name="method-sampling-interval" operator="equal" value="off"/>
<test name="method-profiling" operator="equal" value="off"/>
</condition>
<condition name="method-sampling-native-interval" true="20 ms">
<or>
<test name="method-sampling-interval" operator="equal" value="normal"/>
<test name="method-sampling-interval" operator="equal" value="high"/>
<test name="method-sampling-interval" operator="equal" value="ludicrous"/>
<test name="method-profiling" operator="equal" value="normal"/>
<test name="method-profiling" operator="equal" value="high"/>
<test name="method-profiling" operator="equal" value="max"/>
</or>
</condition>
<condition name="method-sampling-enabled" true="false" false="true">
<test name="method-sampling-interval" operator="equal" value="off"/>
<test name="method-profiling" operator="equal" value="off"/>
</condition>
<selection name="thread-dump-interval" default="normal" label="Thread Dump">
<selection name="thread-dump" default="once" label="Thread Dump">
<option label="Off" name="off">999 d</option>
<option label="At least Once" name="normal">everyChunk</option>
<option label="Every 60 s" name="everyMinute">60 s</option>
<option label="Every 10 s" name="everyTenSecond">10 s</option>
<option label="Every 1 s" name="everySecond">1 s</option>
<option label="At least Once" name="once">everyChunk</option>
<option label="Every 60 s" name="60s">60 s</option>
<option label="Every 10 s" name="10s">10 s</option>
<option label="Every 1 s" name="1s">1 s</option>
</selection>
<condition name="thread-dump-enabled" true="false" false="true">
<test name="thread-dump-interval" operator="equal" value="999 d"/>
<test name="thread-dump" operator="equal" value="999 d"/>
</condition>
<selection name="exception-level" default="errors" label="Exceptions">
<selection name="exceptions" default="errors" label="Exceptions">
<option label="Off" name="off">off</option>
<option label="Errors Only" name="errors">errors</option>
<option label="All Exceptions, including Errors" name="all">all</option>
@ -984,44 +980,44 @@
<condition name="enable-errors" true="true" false="false">
<or>
<test name="exception-level" operator="equal" value="errors"/>
<test name="exception-level" operator="equal" value="all"/>
<test name="exceptions" operator="equal" value="errors"/>
<test name="exceptions" operator="equal" value="all"/>
</or>
</condition>
<condition name="enable-exceptions" true="true" false="false">
<test name="exception-level" operator="equal" value="all"/>
<test name="exceptions" operator="equal" value="all"/>
</condition>
<selection name="memory-leak-detection" default="minimal" label="Memory Leak Detection">
<selection name="memory-leaks" default="types" label="Memory Leak Detection">
<option label="Off" name="off">off</option>
<option label="Object Types" name="minimal">minimal</option>
<option label="Object Types + Allocation Stack Traces" name="medium">medium</option>
<option label="Object Types + Allocation Stack Traces + Path to GC Root" name="full">full</option>
<option label="Object Types" name="types">types</option>
<option label="Object Types + Allocation Stack Traces" name="stack-traces">stack-traces</option>
<option label="Object Types + Allocation Stack Traces + Path to GC Root" name="gc-roots">gc-roots</option>
</selection>
<condition name="memory-leak-detection-enabled" true="false" false="true">
<test name="memory-leak-detection" operator="equal" value="off"/>
<condition name="old-objects-enabled" true="false" false="true">
<test name="memory-leaks" operator="equal" value="off"/>
</condition>
<condition name="memory-leak-detection-stack-trace" true="true" false="false">
<condition name="old-objects-stack-trace" true="true" false="false">
<or>
<test name="memory-leak-detection" operator="equal" value="medium"/>
<test name="memory-leak-detection" operator="equal" value="full"/>
<test name="memory-leaks" operator="equal" value="stack-traces"/>
<test name="memory-leaks" operator="equal" value="gc-roots"/>
</or>
</condition>
<condition name="memory-leak-detection-cutoff" true="1 h" false="0 ns">
<test name="memory-leak-detection" operator="equal" value="full"/>
<condition name="old-objects-cutoff" true="1 h" false="0 ns">
<test name="memory-leaks" operator="equal" value="gc-roots"/>
</condition>
<text name="synchronization-threshold" label="Synchronization Threshold" contentType="timespan" minimum="0 s">20 ms</text>
<text name="locking-threshold" label="Locking Threshold" contentType="timespan" minimum="0 s">20 ms</text>
<text name="file-io-threshold" label="File I/O Threshold" contentType="timespan" minimum="0 s">20 ms</text>
<text name="file-threshold" label="File I/O Threshold" contentType="timespan" minimum="0 s">20 ms</text>
<text name="socket-io-threshold" label="Socket I/O Threshold" contentType="timespan" minimum="0 s">20 ms</text>
<text name="socket-threshold" label="Socket I/O Threshold" contentType="timespan" minimum="0 s">20 ms</text>
<flag name="class-loading-enabled" label="Class Loading">false</flag>
<flag name="class-loading" label="Class Loading">false</flag>
</control>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Recommended way to edit .jfc files is to use Java Mission Control,
see Window -> Flight Recorder Template Manager.
Recommended way to edit .jfc files is to use the configure command of
the 'jfr' tool, i.e. jfr configure, or JDK Mission Control
see Window -> Flight Recorder Template Manager
-->
<configuration version="2.0" label="Profiling" description="Low overhead configuration for profiling, typically around 2 % overhead." provider="Oracle">
@ -64,31 +64,31 @@
<event name="jdk.ThreadSleep">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="synchronization-threshold">10 ms</setting>
<setting name="threshold" control="locking-threshold">10 ms</setting>
</event>
<event name="jdk.ThreadPark">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="synchronization-threshold">10 ms</setting>
<setting name="threshold" control="locking-threshold">10 ms</setting>
</event>
<event name="jdk.JavaMonitorEnter">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="synchronization-threshold">10 ms</setting>
<setting name="threshold" control="locking-threshold">10 ms</setting>
</event>
<event name="jdk.JavaMonitorWait">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="synchronization-threshold">10 ms</setting>
<setting name="threshold" control="locking-threshold">10 ms</setting>
</event>
<event name="jdk.JavaMonitorInflate">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="synchronization-threshold">10 ms</setting>
<setting name="threshold" control="locking-threshold">10 ms</setting>
</event>
<event name="jdk.SyncOnValueBasedClass">
@ -120,13 +120,13 @@
</event>
<event name="jdk.ClassLoad">
<setting name="enabled" control="class-loading-enabled">false</setting>
<setting name="enabled" control="class-loading">false</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold">0 ms</setting>
</event>
<event name="jdk.ClassDefine">
<setting name="enabled" control="class-loading-enabled">false</setting>
<setting name="enabled" control="class-loading">false</setting>
<setting name="stackTrace">true</setting>
</event>
@ -143,11 +143,11 @@
</event>
<event name="jdk.ClassRedefinition">
<setting name="enabled" control="class-loading-enabled">true</setting>
<setting name="enabled" control="class-loading">true</setting>
</event>
<event name="jdk.ClassUnload">
<setting name="enabled" control="class-loading-enabled">false</setting>
<setting name="enabled" control="class-loading">false</setting>
</event>
<event name="jdk.JVMInformation">
@ -207,7 +207,7 @@
<event name="jdk.ThreadDump">
<setting name="enabled" control="thread-dump-enabled">true</setting>
<setting name="period" control="thread-dump-interval">60 s</setting>
<setting name="period" control="thread-dump">60 s</setting>
</event>
<event name="jdk.IntFlag">
@ -274,7 +274,7 @@
</event>
<event name="jdk.ObjectCount">
<setting name="enabled" control="heap-statistics-enabled">false</setting>
<setting name="enabled" control="gc-enabled-all">false</setting>
<setting name="period">everyChunk</setting>
</event>
@ -382,22 +382,22 @@
</event>
<event name="jdk.GCPhasePauseLevel3">
<setting name="enabled" control="gc-enabled-all">false</setting>
<setting name="enabled" control="gc-enabled-high">false</setting>
<setting name="threshold">0 ms</setting>
</event>
<event name="jdk.GCPhasePauseLevel4">
<setting name="enabled" control="gc-enabled-all">false</setting>
<setting name="enabled" control="gc-enabled-high">false</setting>
<setting name="threshold">0 ms</setting>
</event>
<event name="jdk.GCPhaseConcurrent">
<setting name="enabled" control="gc-enabled-all">true</setting>
<setting name="enabled" control="gc-enabled-high">true</setting>
<setting name="threshold">0 ms</setting>
</event>
<event name="jdk.GCPhaseConcurrentLevel1">
<setting name="enabled" control="gc-enabled-all">true</setting>
<setting name="enabled" control="gc-enabled-high">true</setting>
<setting name="threshold">0 ms</setting>
</event>
@ -443,11 +443,11 @@
</event>
<event name="jdk.PromoteObjectInNewPLAB">
<setting name="enabled" control="promotion-enabled">true</setting>
<setting name="enabled" control="gc-enabled-high">true</setting>
</event>
<event name="jdk.PromoteObjectOutsidePLAB">
<setting name="enabled" control="promotion-enabled">true</setting>
<setting name="enabled" control="gc-enabled-high">true</setting>
</event>
<event name="jdk.ConcurrentModeFailure">
@ -455,7 +455,7 @@
</event>
<event name="jdk.AllocationRequiringGC">
<setting name="enabled" control="gc-enabled-all">false</setting>
<setting name="enabled" control="gc-enabled-high">false</setting>
<setting name="stackTrace">true</setting>
</event>
@ -464,27 +464,27 @@
</event>
<event name="jdk.G1HeapRegionInformation">
<setting name="enabled" control="gc-enabled-all">false</setting>
<setting name="enabled" control="gc-enabled-high">false</setting>
<setting name="period">everyChunk</setting>
</event>
<event name="jdk.G1HeapRegionTypeChange">
<setting name="enabled" control="gc-enabled-all">false</setting>
<setting name="enabled" control="gc-enabled-high">false</setting>
</event>
<event name="jdk.ShenandoahHeapRegionInformation">
<setting name="enabled" control="gc-enabled-all">false</setting>
<setting name="enabled" control="gc-enabled-high">false</setting>
<setting name="period">everyChunk</setting>
</event>
<event name="jdk.ShenandoahHeapRegionStateChange">
<setting name="enabled" control="gc-enabled-all">false</setting>
<setting name="enabled" control="gc-enabled-high">false</setting>
</event>
<event name="jdk.OldObjectSample">
<setting name="enabled" control="memory-leak-detection-enabled">true</setting>
<setting name="stackTrace" control="memory-leak-detection-stack-trace">true</setting>
<setting name="cutoff" control="memory-leak-detection-cutoff">0 ns</setting>
<setting name="enabled" control="old-objects-enabled">true</setting>
<setting name="stackTrace" control="old-objects-stack-trace">true</setting>
<setting name="cutoff" control="old-objects-cutoff">0 ns</setting>
</event>
<event name="jdk.CompilerConfiguration">
@ -605,18 +605,18 @@
</event>
<event name="jdk.ObjectAllocationInNewTLAB">
<setting name="enabled">false</setting>
<setting name="enabled" control="gc-enabled-high">false</setting>
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.ObjectAllocationOutsideTLAB">
<setting name="enabled">false</setting>
<setting name="enabled" control="gc-enabled-high">false</setting>
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.ObjectAllocationSample">
<setting name="enabled" control="enable-object-allocation">true</setting>
<setting name="throttle" control="object-allocation-rate">300/s</setting>
<setting name="enabled" control="object-allocation-enabled">true</setting>
<setting name="throttle" control="allocation-profiling">300/s</setting>
<setting name="stackTrace">true</setting>
</event>
@ -638,31 +638,31 @@
<event name="jdk.FileForce">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="file-io-threshold">10 ms</setting>
<setting name="threshold" control="file-threshold">10 ms</setting>
</event>
<event name="jdk.FileRead">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="file-io-threshold">10 ms</setting>
<setting name="threshold" control="file-threshold">10 ms</setting>
</event>
<event name="jdk.FileWrite">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="file-io-threshold">10 ms</setting>
<setting name="threshold" control="file-threshold">10 ms</setting>
</event>
<event name="jdk.SocketRead">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="socket-io-threshold">10 ms</setting>
<setting name="threshold" control="socket-threshold">10 ms</setting>
</event>
<event name="jdk.SocketWrite">
<setting name="enabled">true</setting>
<setting name="stackTrace">true</setting>
<setting name="threshold" control="socket-io-threshold">10 ms</setting>
<setting name="threshold" control="socket-threshold">10 ms</setting>
</event>
<event name="jdk.Deserialization">
@ -819,66 +819,63 @@
<!--
Contents of the control element is not read by the JVM, it's used
by Java Mission Control to change settings that carry the control attribute.
-->
<!--
Contents of the control element is not read by the JVM, it's used
by JDK Mission Control and the 'jfr' tool to change settings that
carry the control attribute.
-->
<control>
<selection name="gc-level" default="detailed" label="Garbage Collector">
<selection name="gc" default="detailed" label="Garbage Collector">
<option label="Off" name="off">off</option>
<option label="Normal" name="detailed">normal</option>
<option label="All" name="all">all</option>
<option label="Normal" name="normal">normal</option>
<option label="Detailed" name="detailed">detailed</option>
<option label="High, incl. TLABs/PLABs (may cause many events)" name="high">high</option>
<option label="All, incl. Heap Statistics (may cause long GCs)" name="all">all</option>
</selection>
<condition name="gc-enabled-normal" true="true" false="false">
<or>
<test name="gc-level" operator="equal" value="normal"/>
<test name="gc-level" operator="equal" value="all"/>
<test name="gc" operator="equal" value="normal"/>
<test name="gc" operator="equal" value="detailed"/>
<test name="gc" operator="equal" value="high"/>
<test name="gc" operator="equal" value="all"/>
</or>
</condition>
<condition name="gc-enabled-detailed" true="true" false="false">
<or>
<test name="gc" operator="equal" value="detailed"/>
<test name="gc" operator="equal" value="high"/>
<test name="gc" operator="equal" value="all"/>
</or>
</condition>
<condition name="gc-enabled-high" true="true" false="false">
<or>
<test name="gc" operator="equal" value="high"/>
<test name="gc" operator="equal" value="all"/>
</or>
</condition>
<condition name="gc-enabled-all" true="true" false="false">
<test name="gc-level" operator="equal" value="all"/>
<test name="gc" operator="equal" value="all"/>
</condition>
<selection name="memory-profiling" default="low" label="Memory Profiling">
<option label="Off" name="off">off</option>
<option label="Object Allocation" name="low">low</option>
<option label="Object Allocation and Promotion" name="medium">medium</option>
<option label="All, including Heap Statistics (May cause long full GCs)" name="all">all</option>
<selection name="allocation-profiling" default="medium" label="Allocation Profiling">
<option label="Off" name="off">0/s</option>
<option label="Low" name="low">150/s</option>
<option label="Medium" name="medium">300/s</option>
<option label="High" name="high">1000/s</option>
<option label="Maximum" name="maximum">1000000000/s</option>
</selection>
<condition name="memory-profiling-enabled-low" true="true" false="false">
<test name="memory-profiling" operator="equal" value="low"/>
</condition>
<condition name="object-allocation-enabled" true="true" false="false">
<or>
<test name="memory-profiling" operator="equal" value="low"/>
<test name="memory-profiling" operator="equal" value="medium"/>
<test name="memory-profiling" operator="equal" value="all"/>
</or>
<not>
<test name="allocation-profiling" operator="equal" value="off"/>
</not>
</condition>
<condition name="object-allocation-rate" true="300/s" false="150/s">
<or>
<test name="memory-profiling" operator="equal" value="medium"/>
<test name="memory-profiling" operator="equal" value="all"/>
</or>
</condition>
<condition name="promotion-enabled" true="true" false="false">
<or>
<test name="memory-profiling" operator="equal" value="medium"/>
<test name="memory-profiling" operator="equal" value="all"/>
</or>
</condition>
<condition name="heap-statistics-enabled" true="true" false="false">
<test name="memory-profiling" operator="equal" value="all"/>
</condition>
<selection name="compiler-level" default="detailed" label="Compiler">
<selection name="compiler" default="detailed" label="Compiler">
<option label="Off" name="off">off</option>
<option label="Normal" name="normal">normal</option>
<option label="Detailed" name="detailed">detailed</option>
@ -886,96 +883,96 @@
</selection>
<condition name="compiler-enabled" true="false" false="true">
<test name="compiler-level" operator="equal" value="off"/>
<test name="compiler" operator="equal" value="off"/>
</condition>
<condition name="compiler-enabled-failure" true="true" false="false">
<or>
<test name="compiler-level" operator="equal" value="detailed"/>
<test name="compiler-level" operator="equal" value="all"/>
<test name="compiler" operator="equal" value="detailed"/>
<test name="compiler" operator="equal" value="all"/>
</or>
</condition>
<condition name="compiler-sweeper-threshold" true="0 ms" false="100 ms">
<test name="compiler-level" operator="equal" value="all"/>
<test name="compiler" operator="equal" value="all"/>
</condition>
<condition name="compiler-compilation-threshold" true="1000 ms">
<test name="compiler-level" operator="equal" value="normal"/>
<test name="compiler" operator="equal" value="normal"/>
</condition>
<condition name="compiler-compilation-threshold" true="100 ms">
<test name="compiler-level" operator="equal" value="detailed"/>
<test name="compiler" operator="equal" value="detailed"/>
</condition>
<condition name="compiler-compilation-threshold" true="0 ms">
<test name="compiler-level" operator="equal" value="all"/>
<test name="compiler" operator="equal" value="all"/>
</condition>
<condition name="compiler-phase-threshold" true="60 s">
<test name="compiler-level" operator="equal" value="normal"/>
<test name="compiler" operator="equal" value="normal"/>
</condition>
<condition name="compiler-phase-threshold" true="10 s">
<test name="compiler-level" operator="equal" value="detailed"/>
<test name="compiler" operator="equal" value="detailed"/>
</condition>
<condition name="compiler-phase-threshold" true="0 s">
<test name="compiler-level" operator="equal" value="all"/>
<test name="compiler" operator="equal" value="all"/>
</condition>
<selection name="method-sampling-interval" default="normal" label="Method Sampling">
<selection name="method-profiling" default="high" label="Method Profiling">
<option label="Off" name="off">off</option>
<option label="Normal" name="normal">normal</option>
<option label="High" name="high">high</option>
<option label="Ludicrous (High Overhead)" name="ludicrous">ludicrous</option>
<option label="Maximum (High Overhead)" name="max">max</option>
</selection>
<condition name="method-sampling-java-interval" true="999 d">
<test name="method-sampling-interval" operator="equal" value="off"/>
<test name="method-profiling" operator="equal" value="off"/>
</condition>
<condition name="method-sampling-java-interval" true="20 ms">
<test name="method-sampling-interval" operator="equal" value="normal"/>
<test name="method-profiling" operator="equal" value="normal"/>
</condition>
<condition name="method-sampling-java-interval" true="10 ms">
<test name="method-sampling-interval" operator="equal" value="high"/>
<test name="method-profiling" operator="equal" value="high"/>
</condition>
<condition name="method-sampling-java-interval" true="1 ms">
<test name="method-sampling-interval" operator="equal" value="ludicrous"/>
<test name="method-profiling" operator="equal" value="max"/>
</condition>
<condition name="method-sampling-native-interval" true="999 d">
<test name="method-sampling-interval" operator="equal" value="off"/>
<test name="method-profiling" operator="equal" value="off"/>
</condition>
<condition name="method-sampling-native-interval" true="20 ms">
<or>
<test name="method-sampling-interval" operator="equal" value="normal"/>
<test name="method-sampling-interval" operator="equal" value="high"/>
<test name="method-sampling-interval" operator="equal" value="ludicrous"/>
<test name="method-profiling" operator="equal" value="normal"/>
<test name="method-profiling" operator="equal" value="high"/>
<test name="method-profiling" operator="equal" value="max"/>
</or>
</condition>
<condition name="method-sampling-enabled" true="false" false="true">
<test name="method-sampling-interval" operator="equal" value="off"/>
<test name="method-profiling" operator="equal" value="off"/>
</condition>
<selection name="thread-dump-interval" default="everyMinute" label="Thread Dump">
<selection name="thread-dump" default="once" label="Thread Dump">
<option label="Off" name="off">999 d</option>
<option label="At least Once" name="normal">everyChunk</option>
<option label="Every 60 s" name="everyMinute">60 s</option>
<option label="Every 10 s" name="everyTenSecond">10 s</option>
<option label="Every 1 s" name="everySecond">1 s</option>
<option label="At least Once" name="once">everyChunk</option>
<option label="Every 60 s" name="60s">60 s</option>
<option label="Every 10 s" name="10s">10 s</option>
<option label="Every 1 s" name="1s">1 s</option>
</selection>
<condition name="thread-dump-enabled" true="false" false="true">
<test name="thread-dump-interval" operator="equal" value="999 d"/>
<test name="thread-dump" operator="equal" value="999 d"/>
</condition>
<selection name="exception-level" default="errors" label="Exceptions">
<selection name="exceptions" default="errors" label="Exceptions">
<option label="Off" name="off">off</option>
<option label="Errors Only" name="errors">errors</option>
<option label="All Exceptions, including Errors" name="all">all</option>
@ -983,44 +980,44 @@
<condition name="enable-errors" true="true" false="false">
<or>
<test name="exception-level" operator="equal" value="errors"/>
<test name="exception-level" operator="equal" value="all"/>
<test name="exceptions" operator="equal" value="errors"/>
<test name="exceptions" operator="equal" value="all"/>
</or>
</condition>
<condition name="enable-exceptions" true="true" false="false">
<test name="exception-level" operator="equal" value="all"/>
<test name="exceptions" operator="equal" value="all"/>
</condition>
<selection name="memory-leak-detection" default="medium" label="Memory Leak Detection">
<selection name="memory-leaks" default="stack-traces" label="Memory Leak Detection">
<option label="Off" name="off">off</option>
<option label="Object Types" name="minimal">minimal</option>
<option label="Object Types + Allocation Stack Traces" name="medium">medium</option>
<option label="Object Types + Allocation Stack Traces + Path to GC Root" name="full">full</option>
<option label="Object Types" name="types">types</option>
<option label="Object Types + Allocation Stack Traces" name="stack-traces">stack-traces</option>
<option label="Object Types + Allocation Stack Traces + Path to GC Root" name="gc-roots">gc-roots</option>
</selection>
<condition name="memory-leak-detection-enabled" true="false" false="true">
<test name="memory-leak-detection" operator="equal" value="off"/>
<condition name="old-objects-enabled" true="false" false="true">
<test name="memory-leaks" operator="equal" value="off"/>
</condition>
<condition name="memory-leak-detection-stack-trace" true="true" false="false">
<condition name="old-objects-stack-trace" true="true" false="false">
<or>
<test name="memory-leak-detection" operator="equal" value="medium"/>
<test name="memory-leak-detection" operator="equal" value="full"/>
<test name="memory-leaks" operator="equal" value="stack-traces"/>
<test name="memory-leaks" operator="equal" value="gc-roots"/>
</or>
</condition>
<condition name="memory-leak-detection-cutoff" true="1 h" false="0 ns">
<test name="memory-leak-detection" operator="equal" value="full"/>
<condition name="old-objects-cutoff" true="1 h" false="0 ns">
<test name="memory-leaks" operator="equal" value="gc-roots"/>
</condition>
<text name="synchronization-threshold" label="Synchronization Threshold" contentType="timespan" minimum="0 s">10 ms</text>
<text name="locking-threshold" label="Locking Threshold" contentType="timespan" minimum="0 s">10 ms</text>
<text name="file-io-threshold" label="File I/O Threshold" contentType="timespan" minimum="0 s">10 ms</text>
<text name="file-threshold" label="File I/O Threshold" contentType="timespan" minimum="0 s">10 ms</text>
<text name="socket-io-threshold" label="Socket I/O Threshold" contentType="timespan" minimum="0 s">10 ms</text>
<text name="socket-threshold" label="Socket I/O Threshold" contentType="timespan" minimum="0 s">10 ms</text>
<flag name="class-loading-enabled" label="Class Loading">false</flag>
<flag name="class-loading" label="Class Loading">false</flag>
</control>

View File

@ -0,0 +1,362 @@
/*
* Copyright (c) 2021, 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.tool;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.ParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jdk.jfr.Configuration;
import jdk.test.lib.process.OutputAnalyzer;
/**
* @test
* @summary Test jfr configure
* @key jfr
* @requires vm.hasJFR
* @library /test/lib /test/jdk
* @run main/othervm jdk.jfr.tool.TestConfigure
*/
public class TestConfigure {
private static final String DIR = System.getProperty("test.src", ".");
public static void main(String... args) throws Throwable {
testSelection();
testText();
testFlag();
testCondition();
testAnd();
testOr();
testNone();
testMissingControl();
testDefault();
testCopyPredefined();
testSuperflouos();
testModify();
testAdding();
testUnification();
testTimespan();
testVerbose();
}
private static void testVerbose() throws Throwable {
var input = newInputFile("flag.jfc");
var output = newOutputFile("verbose-1.jfc");
var result = jfrConfigure("--input", input, "--verbose", "mammal=true", "--output", output);
result.shouldContain("com.example.Lion#enabled=true");
result.shouldContain("com.example.Tiger#enabled=true");
output = newOutputFile("verbose-2.jfc");
result = jfrConfigure("--input", input, "--verbose", "+com.example.Albatross#enabled=true", "--output", output);
result.shouldContain("com.example.Albatross#enabled=true");
}
private static void testTimespan() throws Throwable {
var input = newInputFile("timespan.jfc");
var output = newOutputFile("quoted-timespan.jfc");
jfrConfigure("--input", input, "value=20 s","--output", output);
var outputSetting = readSettings(output);
var expected = readSettings(input);
expected.put("com.example.Tiger#threshold", "20 s");
expected.put("com.example.Lion#period", "20 s");
aseertEqual(outputSetting, expected);
output = newOutputFile("compact-timespan.jfc");
jfrConfigure("--input", input, "value=13s","--output", output);
outputSetting = readSettings(output);
expected = readSettings(input);
expected.put("com.example.Tiger#threshold", "13 s");
expected.put("com.example.Lion#period", "13 s");
aseertEqual(outputSetting, expected);
output = newOutputFile("threshold-period-timespan.jfc");
jfrConfigure("--input", input,
"com.example.Tiger#threshold=2s",
"com.example.Lion#period=3s",
"--output", output);
outputSetting = readSettings(output);
expected = readSettings(input);
expected.put("com.example.Tiger#threshold", "2 s");
expected.put("com.example.Lion#period", "3 s");
aseertEqual(outputSetting, expected);
}
private static void testUnification() throws Throwable {
var input1 = newInputFile("or.jfc");
var input2 = newInputFile("and.jfc");
var output = newOutputFile("combined.jfc");
jfrConfigure("--input", input1 + "," + input2, "--output", output);
var input1Setting = readSettings(input1);
var input2Setting = readSettings(input2);
var outputSetting = readSettings(output);
Map<String, String> expected = new HashMap<>();
expected.putAll(input1Setting);
expected.putAll(input2Setting);
aseertEqual(outputSetting, expected);
}
private static void testAdding() throws Throwable {
var input = newInputFile("plain.jfc");
var output = newOutputFile("test-adding-succeed-1.jfc");
var result = jfrConfigure("--input", input, "+com.example.Tiger#legs=4", "--output", output);
result.shouldNotContain("Could not find");
var outputSetting = readSettings(output);
var expected = readSettings(input);
expected.put("com.example.Tiger#legs", "4");
aseertEqual(outputSetting, expected);
output = newOutputFile("test-adding-succeed-2.jfc");
result = jfrConfigure("--input", input, "+com.example.Foo#bar=baz", "--output", output);
result.shouldNotContain("Could not find");
outputSetting = readSettings(output);
expected = readSettings(input);
expected.put("com.example.Foo#bar", "baz");
aseertEqual(outputSetting, expected);
}
private static void testModify() throws Throwable {
var input = newInputFile("plain.jfc");
var output = newOutputFile("test-modify-fail-1.jfc");
var result = jfrConfigure("--input", input, "com.example.Zebra#stackTrace=true", "--output", output);
result.shouldContain("Could not find event 'com.example.Zebra'");
output = newOutputFile("test-modify-fail-2.jfc");
result = jfrConfigure("--input", input, "com.example.Tiger#foo=true", "--output", output);
result.shouldContain("Could not find setting 'foo' for event 'com.example.Tiger'");
output = newOutputFile("test-modify-succeed.jfc");
result = jfrConfigure("--input", input, "com.example.Tiger#enabled=true", "--output", output);
result.shouldNotContain("Could not find");
var outputSetting = readSettings(output);
var expected = readSettings(input);
expected.put("com.example.Tiger#enabled", "true");
aseertEqual(outputSetting, expected);
}
// JMC may add attributes or elements, make sure some random elements/attributes survive
private static void testSuperflouos() throws Throwable {
var output = newOutputFile("test-superfluous.jfc");
var input = newInputFile("superfluous.jfc");
jfrConfigure("--input", input, "--output", output);
String content = Files.readString(Path.of(output));
for (String t : List.of("legs=\"4\"", "radio", "red", "</radio>", "option")) {
if (!content.contains(t)) {
throw new Exception("Expected superfluous element '" + t + "' or attribute to survive");
}
}
}
private static void testMissingControl() throws Throwable {
var output = newOutputFile("missed.jfc");
var input = newInputFile("missing.jfc");
var result = jfrConfigure("--input", input, "--output", output);
result.shouldContain("Warning! Setting 'com.example.Tiger#enabled' refers to missing control 'tigre'");
}
private static void testDefault() throws Throwable {
var output = newOutputFile("fresh.jfc");
var result = jfrConfigure("--output", output);
result.shouldNotContain("Warning"); // checks dangling control reference in default.jfc
var outputSetting = readSettings(output);
aseertEqual(outputSetting, Configuration.getConfiguration("default").getSettings());
}
private static void testCopyPredefined() throws Throwable {
var output = newOutputFile("new.jfc");
var result = jfrConfigure("--input", "profile", "--output", output);
result.shouldNotContain("Warning"); // checks missing control reference in profile.jfc
var outputSetting = readSettings(output);
aseertEqual(outputSetting, Configuration.getConfiguration("profile").getSettings());
}
private static void testNone() throws Throwable {
var output = newOutputFile("new.jfc");
jfrConfigure("--input", "none", "--output", output);
var outputSetting = readSettings(output);
aseertEqual(outputSetting, Map.of());
}
private static void testOr() throws Throwable {
var output = newOutputFile("test-or-true.jfc");
var input = newInputFile("or.jfc");
jfrConfigure("--input", input, "month=May", "--output", output);
var outputSetting = readSettings(output);
var expected = readSettings(input);
expected.put("season.Spring#enabled", "true");
aseertEqual(outputSetting, expected);
output = newOutputFile("test-or-false.jfc");
jfrConfigure("--input", input, "month=September", "--output", output);
outputSetting = readSettings(output);
expected = readSettings(input);
expected.put("season.Spring#enabled", "false");
aseertEqual(outputSetting, expected);
}
private static void testAnd() throws Throwable {
var output = newOutputFile("test-and-true.jfc");
var input = newInputFile("and.jfc");
jfrConfigure("--input", input,
"closure=true",
"identity=true",
"associativity=true",
"inverse=true",
"--output", output);
var outputSetting = readSettings(output);
var expected = readSettings(input);
expected.put("algebra.Group#enabled", "true");
aseertEqual(outputSetting, expected);
output = newOutputFile("test-and-false.jfc");
jfrConfigure("--input", input,
"closure=true",
"identity=true",
"associativity=true",
"inverse=false",
"--output", output);
outputSetting = readSettings(output);
expected = readSettings(input);
expected.put("algebra.Group#enabled", "false");
aseertEqual(outputSetting, expected);
}
private static void testCondition() throws Throwable {
var output = newOutputFile("test-condition-1.jfc");
var input = newInputFile("condition.jfc");
jfrConfigure("--input", input, "variable=activate", "--output", output);
var outputSetting = readSettings(output);
var expected = readSettings(input);
expected.put("com.example.Tiger#period", "1 s");
expected.put("com.example.Lion#period", "3 s");
aseertEqual(outputSetting, expected);
output = newOutputFile("test-condition-2.jfc");
jfrConfigure("--input", input, "variable=whatever", "--output", output);
outputSetting = readSettings(output);
expected = readSettings(input);
expected.put("com.example.Lion#period", "5 s");
expected.put("com.example.Zebra#period", "7 s");
aseertEqual(outputSetting, expected);
}
private static void testFlag() throws Throwable {
var output = newOutputFile("test-flag.jfc");
var input = newInputFile("flag.jfc");
jfrConfigure("--input", input, "mammal=true", "--output", output);
var outputSetting = readSettings(output);
var expected = readSettings(input);
expected.put("com.example.Tiger#enabled", "true");
expected.put("com.example.Lion#enabled", "true");
aseertEqual(outputSetting, expected);
}
private static void testText() throws Throwable {
var output = newOutputFile("test-text.jfc");
var input = newInputFile("text.jfc");
jfrConfigure("--input", input, "animal-threshold=3s", "--output", output);
var outputSetting = readSettings(output);
var expected = readSettings(input);
expected.put("com.example.Tiger#threshold", "3 s");
expected.put("com.example.Lion#threshold", "3 s");
aseertEqual(outputSetting, expected);
}
private static void testSelection() throws Throwable {
var output = newOutputFile("test-selection.jfc");
var input = newInputFile("selection.jfc");
jfrConfigure("--input", input, "animal=medium", "--output", output);
var outputSetting = readSettings(output);
var expected = readSettings(input);
expected.put("com.example.Tiger#threshold", "10 s");
expected.put("com.example.Lion#threshold", "10 s");
aseertEqual(outputSetting, expected);
}
private static String newInputFile(String filename) {
return Path.of(DIR, "configure", filename).toAbsolutePath().toString();
}
private static Map<String, String> readSettings(String text) throws IOException, ParseException {
return Configuration.create(Path.of(text)).getSettings();
}
private static OutputAnalyzer jfrConfigure(String... args) throws Throwable {
String[] all = new String[args.length + 1];
all[0] = "configure";
for (int i = 0; i < args.length; i++) {
all[i + 1] = args[i];
}
OutputAnalyzer o = ExecuteHelper.jfr(all);
System.out.println(o.getOutput());
return o;
}
private static String newOutputFile(String filename) {
return Path.of(DIR, System.currentTimeMillis() + filename).toAbsolutePath().toString();
}
private static void aseertEqual(Map<String, String> output, Map<String, String> expected) throws Exception {
if (!output.equals(expected)) {
System.out.println("Output:");
for (var e : output.entrySet()) {
System.out.println("\"" + e.getKey() + "=" + e.getValue() + "\"");
}
System.out.println("Expected:");
for (var e : expected.entrySet()) {
System.out.println("\"" + e.getKey() + "=" + e.getValue() + "\"");
}
throw new Exception("Mismatch between output and expected");
}
}
}

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration version="2.0" label="Test and">
<event name="algebra.Group">
<setting name="enabled" control="group">unknown</setting>
</event>
<control>
<flag name="closure" label="Close">false</flag>
<flag name="identity" label="Identity">false</flag>
<flag name="associativity" label="Associativity">false</flag>
<flag name="inverse" label="Inverse">false</flag>
<condition name="group" true="true" false="false">
<and>
<test name="closure" operator="equal" value="true"/>
<test name="identity" operator="equal" value="true"/>
<test name="associativity" operator="equal" value="true"/>
<test name="inverse" operator="equal" value="true"/>
</and>
</condition>
</control>
</configuration>

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration version="2.0" label="Test condition">
<event name="com.example.Tiger">
<setting name="period" control="tiger">0 s</setting>
</event>
<event name="com.example.Lion">
<setting name="period" control="lion">0 s</setting>
</event>
<event name="com.example.Zebra">
<setting name="period" control="zebra">0 s</setting>
</event>
<control>
<text name="variable" label="Variable">disabled</text>
<condition name="tiger" true="1 s">
<test name="variable" operator="equal" value="activate"/>
</condition>
<condition name="lion" true="3 s" false="5 s">
<test name="variable" operator="equal" value="activate"/>
</condition>
<condition name="zebra" false="7 s">
<test name="variable" operator="equal" value="activate"/>
</condition>
</control>
</configuration>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration version="2.0" label="Test flag">
<event name="com.example.Tiger">
<setting name="enabled" control="mammal">false</setting>
<setting name="stackTrace">false</setting>
</event>
<event name="com.example.Lion">
<setting name="enabled" control="mammal">false</setting>
</event>
<event name="com.example.Albatross">
<setting name="enabled">false</setting>
</event>
<control>
<flag name="mammal" label="Mammal">false</flag>
</control>
</configuration>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration version="2.0" label="Test missing reference">
<event name="com.example.Tiger">
<setting name="enabled" control="tigre">false</setting>
</event>
<event name="com.example.Cactus">
<setting name="threshold">0 s</setting>
</event>
<control>
<selection name="tiger" label="Tiger" default="true">
<option name="true" label="True">true</option>
<option name="false" label="False">false</option>
</selection>
</control>
</configuration>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration version="2.0" label="Test or">
<event name="season.Spring">
<setting name="enabled" control="spring">unknown</setting>
</event>
<control>
<text name="month" label="Month">January</text>
<condition name="spring" true="true" false="false">
<or>
<test name="month" operator="equal" value="March"/>
<test name="month" operator="equal" value="April"/>
<test name="month" operator="equal" value="May"/>
</or>
</condition>
</control>
</configuration>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration version="2.0" label="Test plain">
<event name="com.example.Tiger">
<setting name="enabled">false</setting>
<setting name="stackTrace">false</setting>
</event>
<event name="com.example.Lion">
<setting name="enabled">false</setting>
<setting name="stackTrace">true</setting>
</event>
</configuration>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration version="2.0" label="Test selection">
<event name="com.example.Tiger">
<setting name="enabled">false</setting>
<setting name="threshold" control="animal">0 s</setting>
</event>
<event name="com.example.Lion">
<setting name="enabled">true</setting>
<setting name="threshold" control="animal">0 s</setting>
</event>
<event name="com.example.Cactus">
<setting name="threshold">0 s</setting>
</event>
<control>
<selection name="animal" label="Animal" default="off">
<option name="off" label="Off">0 s</option>
<option name="low" label="Low">1 s</option>
<option name="medium" label="Medium">10 s</option>
<option name="high" label="High">100 s</option>
</selection>
</control>
</configuration>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration version="2.0" label="Test superfluous elements and attributes">
<event legs="4" name="com.example.Tiger">
<setting name="enabled" control="animal">false</setting>
</event>
<control>
<flag name="animal" label="Animal">true</flag>
<radio name="color" label="Color" default="green">
<option name="red" label="Red">red</option>
<option name="green" label="Green">green</option>
<option name="blue" label="Blue">blue</option>
</radio>
</control>
</configuration>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration version="2.0" label="Test text">
<event name="com.example.Tiger">
<setting name="enabled">false</setting>
<setting name="threshold" control="animal-threshold">0 s</setting>
</event>
<event name="com.example.Lion">
<setting name="enabled">true</setting>
<setting name="threshold" control="animal-threshold">0 s</setting>
</event>
<event name="com.example.Cactus">
<setting name="legs">0 s</setting>
</event>
<control>
<text name="animal-threshold" contentType="timespan" label="Threshold">0 s</text>
</control>
</configuration>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration version="2.0" label="Test timespan">
<event name="com.example.Tiger">
<setting name="enabled">false</setting>
<setting name="threshold" control="value">0 s</setting>
</event>
<event name="com.example.Lion">
<setting name="enabled">true</setting>
<setting name="period" control="value">0 s</setting>
</event>
<control>
<text name="value" label="Value" contentType="timespan">0 s</text>
</control>
</configuration>