e555d54705
Reviewed-by: mgronlun, erikj
1017 lines
40 KiB
Java
1017 lines
40 KiB
Java
/*
|
|
* Copyright (c) 2018, 2022, 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 build.tools.jfr;
|
|
|
|
import java.io.BufferedOutputStream;
|
|
import java.io.Closeable;
|
|
import java.io.DataOutputStream;
|
|
import java.io.File;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.PrintStream;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Objects;
|
|
import java.util.StringJoiner;
|
|
import java.util.function.Predicate;
|
|
|
|
import javax.xml.XMLConstants;
|
|
import javax.xml.parsers.ParserConfigurationException;
|
|
import javax.xml.parsers.SAXParser;
|
|
import javax.xml.parsers.SAXParserFactory;
|
|
import javax.xml.validation.SchemaFactory;
|
|
|
|
import org.xml.sax.Attributes;
|
|
import org.xml.sax.SAXException;
|
|
import org.xml.sax.SAXParseException;
|
|
import org.xml.sax.helpers.DefaultHandler;
|
|
|
|
/**
|
|
* Purpose of this program is twofold:
|
|
*
|
|
* 1) Generate C++ classes to be used when writing native events for HotSpot.
|
|
*
|
|
* 2) Generate metadata (label, descriptions, field layout etc.) from XML
|
|
* (metadata.xml) into a binary format (metadata.bin) that can be read quickly
|
|
* during startup by the jdk.jfr module.
|
|
*
|
|
* INPUT FILES:
|
|
*
|
|
* - metadata.xml File that contains descriptions of events and types
|
|
* - metadata.xsd Schema that verifies that metadata.xml is legit XML
|
|
*
|
|
* OUTPUT FILES:
|
|
*
|
|
* MODE: headers
|
|
*
|
|
* - jfrEventIds.hpp List of IDs so events can be identified from native
|
|
* - jfrTypes.hpp List of IDs so types can be identified from native
|
|
* - jfrPeriodic.hpp Dispatch mechanism so Java can emit native periodic events
|
|
* - jfrEventControl.hpp Data structure for native event settings.
|
|
* - jfrEventClasses.hpp C++ event classes that can write data into native buffers
|
|
*
|
|
* MODE: metadata
|
|
*
|
|
* - metadata.bin Binary representation of the information in metadata.xml
|
|
*
|
|
*/
|
|
public class GenerateJfrFiles {
|
|
|
|
enum OutputMode {
|
|
headers, metadata
|
|
}
|
|
|
|
private static void printUsage(PrintStream out) {
|
|
out.println("Usage: java GenerateJfrFiles[.java]");
|
|
out.println(" --mode <headers|metadata>");
|
|
out.println(" --xml <path-to-metadata.xml> ");
|
|
out.println(" --xsd <path-to-metadata.xsd>");
|
|
out.println(" --output <output-file-or-directory>");
|
|
}
|
|
|
|
private static String consumeOption(String option, List<String> argList) throws Exception {
|
|
int index = argList.indexOf(option);
|
|
if (index >= 0 && index <= argList.size() - 2) {
|
|
String result = argList.get(index + 1);
|
|
argList.remove(index);
|
|
argList.remove(index);
|
|
return result;
|
|
}
|
|
throw new IllegalArgumentException("missing option " + option);
|
|
}
|
|
|
|
public static void main(String... args) throws Exception {
|
|
try {
|
|
List<String> argList = new ArrayList<>();
|
|
argList.addAll(Arrays.asList(args));
|
|
String mode = consumeOption("--mode", argList);
|
|
String output = consumeOption("--output", argList);
|
|
String xml = consumeOption("--xml", argList);
|
|
String xsd = consumeOption("--xsd", argList);
|
|
if (!argList.isEmpty()) {
|
|
throw new IllegalArgumentException("unknown option " + argList);
|
|
}
|
|
OutputMode outputMode = OutputMode.valueOf(mode);
|
|
File xmlFile = new File(xml);
|
|
File xsdFile = new File(xsd);
|
|
|
|
Metadata metadata = new Metadata(xmlFile, xsdFile);
|
|
metadata.verify();
|
|
metadata.wireUpTypes();
|
|
|
|
if (outputMode == OutputMode.headers) {
|
|
File outputDir = new File(output);
|
|
printJfrEventIdsHpp(metadata, new File(outputDir, "jfrEventIds.hpp"));
|
|
printJfrTypesHpp(metadata, new File(outputDir, "jfrTypes.hpp"));
|
|
printJfrPeriodicHpp(metadata, new File(outputDir, "jfrPeriodic.hpp"));
|
|
printJfrEventControlHpp(metadata, new File(outputDir, "jfrEventControl.hpp"));
|
|
printJfrEventClassesHpp(metadata, new File(outputDir, "jfrEventClasses.hpp"));
|
|
}
|
|
|
|
if (outputMode == OutputMode.metadata) {
|
|
File outputFile = new File(output);
|
|
try (var b = new DataOutputStream(
|
|
new BufferedOutputStream(
|
|
new FileOutputStream(outputFile)))) {
|
|
metadata.persist(b);
|
|
}
|
|
}
|
|
System.exit(0);
|
|
} catch (IllegalArgumentException iae) {
|
|
System.err.println();
|
|
System.err.println("GenerateJfrFiles: " + iae.getMessage());
|
|
System.err.println();
|
|
printUsage(System.err);
|
|
System.err.println();
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
System.exit(1);
|
|
}
|
|
|
|
static class XmlType {
|
|
final String name;
|
|
final String fieldType;
|
|
final String parameterType;
|
|
final String javaType;
|
|
final boolean unsigned;
|
|
final String contentType;
|
|
|
|
XmlType(String name, String fieldType, String parameterType, String javaType, String contentType,
|
|
boolean unsigned) {
|
|
this.name = name;
|
|
this.fieldType = fieldType;
|
|
this.parameterType = parameterType;
|
|
this.javaType = javaType;
|
|
this.unsigned = unsigned;
|
|
this.contentType = contentType;
|
|
}
|
|
}
|
|
|
|
static class XmlContentType {
|
|
final String name;
|
|
final String annotation;
|
|
|
|
XmlContentType(String name, String annotation) {
|
|
this.name = name;
|
|
this.annotation = annotation;
|
|
}
|
|
}
|
|
|
|
static class TypeElement {
|
|
List<FieldElement> fields = new ArrayList<>();
|
|
String name;
|
|
String javaType;
|
|
String label = "";
|
|
String description = "";
|
|
String category = "";
|
|
boolean thread;
|
|
boolean stackTrace;
|
|
boolean startTime;
|
|
String period = "";
|
|
boolean cutoff;
|
|
boolean throttle;
|
|
boolean experimental;
|
|
boolean internal;
|
|
long id;
|
|
boolean isEvent;
|
|
boolean isRelation;
|
|
boolean supportStruct = false;
|
|
String commitState;
|
|
public boolean primitive;
|
|
|
|
public void persist(DataOutputStream pos) throws IOException {
|
|
pos.writeInt(fields.size());
|
|
for (FieldElement field : fields) {
|
|
field.persist(pos);
|
|
}
|
|
pos.writeUTF(javaType);
|
|
pos.writeUTF(label);
|
|
pos.writeUTF(description);
|
|
pos.writeUTF(category);
|
|
pos.writeBoolean(thread);
|
|
pos.writeBoolean(stackTrace);
|
|
pos.writeBoolean(startTime);
|
|
pos.writeUTF(period);
|
|
pos.writeBoolean(cutoff);
|
|
pos.writeBoolean(throttle);
|
|
pos.writeBoolean(experimental);
|
|
pos.writeBoolean(internal);
|
|
pos.writeLong(id);
|
|
pos.writeBoolean(isEvent);
|
|
pos.writeBoolean(isRelation);
|
|
}
|
|
}
|
|
|
|
static class Metadata {
|
|
static class TypeCounter {
|
|
final long first;
|
|
long last = -1;
|
|
long count = 0;
|
|
long id = -1;
|
|
|
|
TypeCounter(long startId) {
|
|
this.first = startId;
|
|
}
|
|
|
|
long next() {
|
|
id = (id == -1) ? first : id + 1;
|
|
count++;
|
|
last = id;
|
|
return id;
|
|
}
|
|
}
|
|
|
|
static int RESERVED_EVENT_COUNT = 2;
|
|
final Map<String, TypeElement> types = new LinkedHashMap<>();
|
|
final Map<String, XmlType> xmlTypes = new LinkedHashMap<>();
|
|
final Map<String, XmlContentType> xmlContentTypes = new LinkedHashMap<>();
|
|
int lastEventId;
|
|
private TypeCounter eventCounter;
|
|
private TypeCounter typeCounter;
|
|
|
|
Metadata(File metadataXml, File metadataSchema)
|
|
throws ParserConfigurationException, SAXException, FileNotFoundException, IOException {
|
|
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
|
SAXParserFactory factory = SAXParserFactory.newInstance();
|
|
factory.setSchema(schemaFactory.newSchema(metadataSchema));
|
|
SAXParser sp = factory.newSAXParser();
|
|
sp.parse(metadataXml, new MetadataHandler(this));
|
|
}
|
|
|
|
public void persist(DataOutputStream pos) throws IOException {
|
|
pos.writeInt(types.values().size());
|
|
for (TypeElement t : types.values()) {
|
|
t.persist(pos);
|
|
}
|
|
}
|
|
|
|
List<TypeElement> getEvents() {
|
|
return getList(t -> t.isEvent);
|
|
}
|
|
|
|
List<TypeElement> getEventsAndStructs() {
|
|
return getList(t -> t.isEvent || t.supportStruct);
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
<T> List<T> getList(Predicate<? super TypeElement> pred) {
|
|
List<T> result = new ArrayList<>(types.size());
|
|
for (TypeElement t : types.values()) {
|
|
if (pred.test(t)) {
|
|
result.add((T) t);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
List<TypeElement> getPeriodicEvents() {
|
|
return getList(t -> t.isEvent && !t.period.isEmpty());
|
|
}
|
|
|
|
List<TypeElement> getTypes() {
|
|
return getList(t -> !t.isEvent);
|
|
}
|
|
|
|
List<TypeElement> getStructs() {
|
|
return getList(t -> !t.isEvent && t.supportStruct);
|
|
}
|
|
|
|
void verify() {
|
|
for (TypeElement t : types.values()) {
|
|
for (FieldElement f : t.fields) {
|
|
if (!xmlTypes.containsKey(f.typeName)) { // ignore primitives
|
|
if (!types.containsKey(f.typeName)) {
|
|
throw new IllegalStateException("Could not find definition of type '" + f.typeName
|
|
+ "' used by " + t.name + "#" + f.name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void wireUpTypes() {
|
|
// Add Java primitives
|
|
for (var t : xmlTypes.entrySet()) {
|
|
String name = t.getKey();
|
|
XmlType xmlType = t.getValue();
|
|
// Excludes Thread and Class
|
|
if (!types.containsKey(name)) {
|
|
// Excludes u8, u4, u2, u1, Ticks and Ticksspan
|
|
if (!xmlType.javaType.isEmpty() && !xmlType.unsigned) {
|
|
TypeElement te = new TypeElement();
|
|
te.name = name;
|
|
te.javaType = xmlType.javaType;
|
|
te.primitive = true;
|
|
types.put(te.name, te);
|
|
}
|
|
}
|
|
}
|
|
// Setup Java fully qualified names
|
|
for (TypeElement t : types.values()) {
|
|
if (t.isEvent) {
|
|
t.javaType = "jdk." + t.name;
|
|
} else {
|
|
XmlType xmlType = xmlTypes.get(t.name);
|
|
if (xmlType != null && !xmlType.javaType.isEmpty()) {
|
|
t.javaType = xmlType.javaType;
|
|
} else {
|
|
t.javaType = "jdk.types." + t.name;
|
|
}
|
|
}
|
|
}
|
|
// Setup content type, annotation, constant pool etc. for fields.
|
|
for (TypeElement t : types.values()) {
|
|
for (FieldElement f : t.fields) {
|
|
TypeElement type = types.get(f.typeName);
|
|
XmlType xmlType = xmlTypes.get(f.typeName);
|
|
if (type == null) {
|
|
if (xmlType == null) {
|
|
throw new IllegalStateException("Unknown type");
|
|
}
|
|
if (f.contentType.isEmpty()) {
|
|
f.contentType = xmlType.contentType;
|
|
}
|
|
String javaType = xmlType.javaType;
|
|
type = types.get(javaType);
|
|
Objects.requireNonNull(type);
|
|
}
|
|
if (type.primitive) {
|
|
f.constantPool = false;
|
|
}
|
|
|
|
if (xmlType != null) {
|
|
f.unsigned = xmlType.unsigned;
|
|
}
|
|
|
|
if (f.struct) {
|
|
f.constantPool = false;
|
|
type.supportStruct = true;
|
|
}
|
|
f.type = type;
|
|
XmlContentType xmlContentType = xmlContentTypes.get(f.contentType);
|
|
if (xmlContentType == null) {
|
|
f.annotations = "";
|
|
} else {
|
|
f.annotations = xmlContentType.annotation;
|
|
}
|
|
if (!f.relation.isEmpty()) {
|
|
f.relation = "jdk.types." + f.relation;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Low numbers for event so most of them
|
|
// can fit in one byte with compressed integers
|
|
eventCounter = new TypeCounter(RESERVED_EVENT_COUNT);
|
|
for (TypeElement t : getEvents()) {
|
|
t.id = eventCounter.next();
|
|
}
|
|
typeCounter = new TypeCounter(eventCounter.last + 1);
|
|
for (TypeElement t : getTypes()) {
|
|
t.id = typeCounter.next();
|
|
}
|
|
}
|
|
|
|
public String getName(long id) {
|
|
for (TypeElement t : types.values()) {
|
|
if (t.id == id) {
|
|
return t.name;
|
|
}
|
|
}
|
|
throw new IllegalStateException("Unexpected id " + id );
|
|
}
|
|
}
|
|
|
|
static class FieldElement {
|
|
final Metadata metadata;
|
|
TypeElement type;
|
|
String name;
|
|
String typeName;
|
|
boolean constantPool = true;
|
|
public String transition;
|
|
public String contentType;
|
|
private String label;
|
|
private String description;
|
|
private String relation;
|
|
private boolean experimental;
|
|
private boolean unsigned;
|
|
private boolean array;
|
|
private String annotations;
|
|
public boolean struct;
|
|
|
|
FieldElement(Metadata metadata) {
|
|
this.metadata = metadata;
|
|
}
|
|
|
|
public void persist(DataOutputStream pos) throws IOException {
|
|
pos.writeUTF(name);
|
|
pos.writeUTF(type.javaType);
|
|
pos.writeUTF(label);
|
|
pos.writeUTF(description);
|
|
pos.writeBoolean(constantPool);
|
|
pos.writeBoolean(array);
|
|
pos.writeBoolean(unsigned);
|
|
pos.writeUTF(annotations);
|
|
pos.writeUTF(transition);
|
|
pos.writeUTF(relation);
|
|
pos.writeBoolean(experimental);
|
|
}
|
|
|
|
String getParameterType() {
|
|
if (struct) {
|
|
return "const JfrStruct" + typeName + "&";
|
|
}
|
|
XmlType xmlType = metadata.xmlTypes.get(typeName);
|
|
if (xmlType != null) {
|
|
return xmlType.parameterType;
|
|
}
|
|
return type != null ? "u8" : typeName;
|
|
}
|
|
|
|
String getParameterName() {
|
|
return struct ? "value" : "new_value";
|
|
}
|
|
|
|
String getFieldType() {
|
|
if (struct) {
|
|
return "JfrStruct" + typeName;
|
|
}
|
|
XmlType xmlType = metadata.xmlTypes.get(typeName);
|
|
if (xmlType != null) {
|
|
return xmlType.fieldType;
|
|
}
|
|
return type != null ? "u8" : typeName;
|
|
}
|
|
}
|
|
|
|
static class MetadataHandler extends DefaultHandler {
|
|
final Metadata metadata;
|
|
FieldElement currentField;
|
|
TypeElement currentType;
|
|
|
|
MetadataHandler(Metadata metadata) {
|
|
this.metadata = metadata;
|
|
}
|
|
|
|
@Override
|
|
public void error(SAXParseException e) throws SAXException {
|
|
throw e;
|
|
}
|
|
|
|
@Override
|
|
public void startElement(String uri, String localName, String qName, Attributes attributes)
|
|
throws SAXException {
|
|
switch (qName) {
|
|
case "XmlContentType":
|
|
String n = attributes.getValue("name"); // mandatory
|
|
String a = attributes.getValue("annotation"); // mandatory
|
|
metadata.xmlContentTypes.put(n, new XmlContentType(n, a));
|
|
break;
|
|
case "XmlType":
|
|
String name = attributes.getValue("name"); // mandatory
|
|
String parameterType = attributes.getValue("parameterType"); // mandatory
|
|
String fieldType = attributes.getValue("fieldType"); // mandatory
|
|
String javaType = getString(attributes, "javaType");
|
|
String contentType = getString(attributes, "contentType");
|
|
boolean unsigned = getBoolean(attributes, "unsigned", false);
|
|
metadata.xmlTypes.put(name,
|
|
new XmlType(name, fieldType, parameterType, javaType, contentType, unsigned));
|
|
break;
|
|
case "Relation":
|
|
case "Type":
|
|
case "Event":
|
|
currentType = new TypeElement();
|
|
currentType.name = attributes.getValue("name"); // mandatory
|
|
currentType.label = getString(attributes, "label");
|
|
currentType.description = getString(attributes, "description");
|
|
currentType.category = getString(attributes, "category");
|
|
currentType.experimental = getBoolean(attributes, "experimental", false);
|
|
currentType.internal = getBoolean(attributes, "internal", false);
|
|
currentType.thread = getBoolean(attributes, "thread", false);
|
|
currentType.stackTrace = getBoolean(attributes, "stackTrace", false);
|
|
currentType.startTime = getBoolean(attributes, "startTime", true);
|
|
currentType.period = getString(attributes, "period");
|
|
currentType.cutoff = getBoolean(attributes, "cutoff", false);
|
|
currentType.throttle = getBoolean(attributes, "throttle", false);
|
|
currentType.commitState = getString(attributes, "commitState");
|
|
currentType.isEvent = "Event".equals(qName);
|
|
currentType.isRelation = "Relation".equals(qName);
|
|
break;
|
|
case "Field":
|
|
currentField = new FieldElement(metadata);
|
|
currentField.name = attributes.getValue("name"); // mandatory
|
|
currentField.typeName = attributes.getValue("type"); // mandatory
|
|
currentField.label = getString(attributes, "label");
|
|
currentField.description = getString(attributes, "description");
|
|
currentField.contentType = getString(attributes, "contentType");
|
|
currentField.struct = getBoolean(attributes, "struct", false);
|
|
currentField.array = getBoolean(attributes, "array", false);
|
|
currentField.transition = getString(attributes, "transition");
|
|
currentField.relation = getString(attributes, "relation");
|
|
currentField.experimental = getBoolean(attributes, "experimental", false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private static String getString(Attributes attributes, String name) {
|
|
String value = attributes.getValue(name);
|
|
return value != null ? value : "";
|
|
}
|
|
|
|
private static boolean getBoolean(Attributes attributes, String name, boolean defaultValue) {
|
|
String value = attributes.getValue(name);
|
|
return value == null ? defaultValue : Boolean.valueOf(value);
|
|
}
|
|
|
|
@Override
|
|
public void endElement(String uri, String localName, String qName) {
|
|
switch (qName) {
|
|
case "Relation":
|
|
case "Type":
|
|
case "Event":
|
|
metadata.types.put(currentType.name, currentType);
|
|
currentType = null;
|
|
break;
|
|
case "Field":
|
|
currentType.fields.add(currentField);
|
|
currentField = null;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static class Printer implements Closeable {
|
|
final PrintStream out;
|
|
|
|
Printer(File outputFile) throws FileNotFoundException {
|
|
out = new PrintStream(new BufferedOutputStream(new FileOutputStream(outputFile)));
|
|
write("/* AUTOMATICALLY GENERATED FILE - DO NOT EDIT */");
|
|
write("");
|
|
}
|
|
|
|
void write(String text) {
|
|
out.print(text);
|
|
out.print("\n"); // Don't use Windows line endings
|
|
}
|
|
|
|
@Override
|
|
public void close() throws IOException {
|
|
out.close();
|
|
}
|
|
}
|
|
|
|
private static void printJfrPeriodicHpp(Metadata metadata, File outputFile) throws Exception {
|
|
try (var out = new Printer(outputFile)) {
|
|
out.write("#ifndef JFRFILES_JFRPERIODICEVENTSET_HPP");
|
|
out.write("#define JFRFILES_JFRPERIODICEVENTSET_HPP");
|
|
out.write("");
|
|
out.write("#include \"utilities/macros.hpp\"");
|
|
out.write("#if INCLUDE_JFR");
|
|
out.write("#include \"jfrfiles/jfrEventIds.hpp\"");
|
|
out.write("#include \"memory/allocation.hpp\"");
|
|
out.write("");
|
|
out.write("enum PeriodicType {BEGIN_CHUNK, INTERVAL, END_CHUNK};");
|
|
out.write("");
|
|
out.write("class JfrPeriodicEventSet : public AllStatic {");
|
|
out.write(" public:");
|
|
out.write(" static void requestEvent(JfrEventId id, jlong timestamp, PeriodicType periodicType) {");
|
|
out.write(" _timestamp = Ticks(timestamp);");
|
|
out.write(" _type = periodicType;");
|
|
out.write(" switch(id) {");
|
|
out.write(" ");
|
|
for (TypeElement e : metadata.getPeriodicEvents()) {
|
|
out.write(" case Jfr" + e.name + "Event:");
|
|
out.write(" request" + e.name + "();");
|
|
out.write(" break;");
|
|
out.write(" ");
|
|
}
|
|
out.write(" default:");
|
|
out.write(" break;");
|
|
out.write(" }");
|
|
out.write(" }");
|
|
out.write("");
|
|
out.write(" private:");
|
|
out.write("");
|
|
for (TypeElement e : metadata.getPeriodicEvents()) {
|
|
out.write(" static void request" + e.name + "(void);");
|
|
out.write("");
|
|
}
|
|
out.write(" static Ticks timestamp(void);");
|
|
out.write(" static Ticks _timestamp;");
|
|
out.write(" static PeriodicType type(void);");
|
|
out.write(" static PeriodicType _type;");
|
|
out.write("};");
|
|
out.write("");
|
|
out.write("#endif // INCLUDE_JFR");
|
|
out.write("#endif // JFRFILES_JFRPERIODICEVENTSET_HPP");
|
|
}
|
|
}
|
|
|
|
private static void printJfrEventControlHpp(Metadata metadata, File outputFile) throws Exception {
|
|
try (var out = new Printer(outputFile)) {
|
|
out.write("#ifndef JFRFILES_JFR_NATIVE_EVENTSETTING_HPP");
|
|
out.write("#define JFRFILES_JFR_NATIVE_EVENTSETTING_HPP");
|
|
out.write("");
|
|
out.write("#include \"utilities/macros.hpp\"");
|
|
out.write("#if INCLUDE_JFR");
|
|
out.write("#include \"jfrfiles/jfrEventIds.hpp\"");
|
|
out.write("");
|
|
out.write("/**");
|
|
out.write(" * Event setting. We add some padding so we can use our");
|
|
out.write(" * event IDs as indexes into this.");
|
|
out.write(" */");
|
|
out.write("");
|
|
out.write("struct jfrNativeEventSetting {");
|
|
out.write(" jlong threshold_ticks;");
|
|
out.write(" jlong cutoff_ticks;");
|
|
out.write(" u1 stacktrace;");
|
|
out.write(" u1 enabled;");
|
|
out.write(" u1 large;");
|
|
out.write(" u1 pad[5]; // Because GCC on linux ia32 at least tries to pack this.");
|
|
out.write("};");
|
|
out.write("");
|
|
out.write("union JfrNativeSettings {");
|
|
out.write(" // Array version.");
|
|
out.write(" jfrNativeEventSetting bits[NUMBER_OF_EVENTS];");
|
|
out.write(" // Then, to make it easy to debug,");
|
|
out.write(" // add named struct members also.");
|
|
out.write(" struct {");
|
|
out.write(" jfrNativeEventSetting pad[NUMBER_OF_RESERVED_EVENTS];");
|
|
for (TypeElement t : metadata.getEventsAndStructs()) {
|
|
out.write(" jfrNativeEventSetting " + t.name + ";");
|
|
}
|
|
out.write(" } ev;");
|
|
out.write("};");
|
|
out.write("");
|
|
out.write("#endif // INCLUDE_JFR");
|
|
out.write("#endif // JFRFILES_JFR_NATIVE_EVENTSETTING_HPP");
|
|
}
|
|
}
|
|
|
|
private static void printJfrEventIdsHpp(Metadata metadata, File outputFile) throws Exception {
|
|
try (var out = new Printer(outputFile)) {
|
|
out.write("#ifndef JFRFILES_JFREVENTIDS_HPP");
|
|
out.write("#define JFRFILES_JFREVENTIDS_HPP");
|
|
out.write("");
|
|
out.write("#include \"utilities/macros.hpp\"");
|
|
out.write("#if INCLUDE_JFR");
|
|
out.write("");
|
|
out.write("enum JfrEventId {");
|
|
out.write(" JfrMetadataEvent = 0,");
|
|
out.write(" JfrCheckpointEvent = 1,");
|
|
for (TypeElement t : metadata.getEvents()) {
|
|
out.write(" " + jfrEventId(t.name) + " = " + t.id + ",");
|
|
}
|
|
out.write("};");
|
|
out.write("typedef enum JfrEventId JfrEventId;");
|
|
out.write("");
|
|
String first = metadata.getName(metadata.eventCounter.first);
|
|
String last = metadata.getName(metadata.eventCounter.last);
|
|
out.write("static const JfrEventId FIRST_EVENT_ID = " + jfrEventId(first) + ";");
|
|
out.write("static const JfrEventId LAST_EVENT_ID = " + jfrEventId(last) + ";");
|
|
out.write("static const int NUMBER_OF_EVENTS = " + metadata.eventCounter.count + ";");
|
|
out.write("static const int NUMBER_OF_RESERVED_EVENTS = " + Metadata.RESERVED_EVENT_COUNT + ";");
|
|
out.write("#endif // INCLUDE_JFR");
|
|
out.write("#endif // JFRFILES_JFREVENTIDS_HPP");
|
|
}
|
|
}
|
|
|
|
private static String jfrEventId(String name) {
|
|
return "Jfr" + name + "Event";
|
|
}
|
|
|
|
private static void printJfrTypesHpp(Metadata metadata, File outputFile) throws Exception {
|
|
try (var out = new Printer(outputFile)) {
|
|
out.write("#ifndef JFRFILES_JFRTYPES_HPP");
|
|
out.write("#define JFRFILES_JFRTYPES_HPP");
|
|
out.write("");
|
|
out.write("#include \"utilities/macros.hpp\"");
|
|
out.write("#if INCLUDE_JFR");
|
|
out.write("");
|
|
out.write("#include <string.h>");
|
|
out.write("#include \"memory/allocation.hpp\"");
|
|
out.write("");
|
|
out.write("enum JfrTypeId {");
|
|
for (TypeElement type : metadata.getTypes()) {
|
|
out.write(" " + jfrTypeId(type.name) + " = " + type.id + ",");
|
|
}
|
|
out.write("};");
|
|
out.write("");
|
|
String first = metadata.getName(metadata.typeCounter.first);
|
|
String last = metadata.getName(metadata.typeCounter.last);
|
|
out.write("static const JfrTypeId FIRST_TYPE_ID = " + jfrTypeId(first) + ";");
|
|
out.write("static const JfrTypeId LAST_TYPE_ID = " + jfrTypeId(last) + ";");
|
|
out.write("");
|
|
out.write("class JfrType : public AllStatic {");
|
|
out.write(" public:");
|
|
out.write(" static jlong name_to_id(const char* type_name) {");
|
|
|
|
Map<String, XmlType> javaTypes = new LinkedHashMap<>();
|
|
for (XmlType xmlType : metadata.xmlTypes.values()) {
|
|
if (!xmlType.javaType.isEmpty()) {
|
|
javaTypes.put(xmlType.javaType, xmlType);
|
|
}
|
|
}
|
|
for (XmlType xmlType : javaTypes.values()) {
|
|
String javaName = xmlType.javaType;
|
|
String typeName = xmlType.name.toUpperCase();
|
|
out.write(" if (strcmp(type_name, \"" + javaName + "\") == 0) {");
|
|
out.write(" return TYPE_" + typeName + ";");
|
|
out.write(" }");
|
|
}
|
|
out.write(" return -1;");
|
|
out.write(" }");
|
|
out.write("};");
|
|
out.write("");
|
|
out.write("#endif // INCLUDE_JFR");
|
|
out.write("#endif // JFRFILES_JFRTYPES_HPP");
|
|
}
|
|
}
|
|
|
|
private static String jfrTypeId(String name) {
|
|
return "TYPE_" + name.toUpperCase();
|
|
}
|
|
|
|
private static void printJfrEventClassesHpp(Metadata metadata, File outputFile) throws Exception {
|
|
try (var out = new Printer(outputFile)) {
|
|
out.write("#ifndef JFRFILES_JFREVENTCLASSES_HPP");
|
|
out.write("#define JFRFILES_JFREVENTCLASSES_HPP");
|
|
out.write("");
|
|
out.write("#include \"oops/klass.hpp\"");
|
|
out.write("#include \"jfrfiles/jfrTypes.hpp\"");
|
|
out.write("#include \"jfr/utilities/jfrTypes.hpp\"");
|
|
out.write("#include \"utilities/macros.hpp\"");
|
|
out.write("#include \"utilities/ticks.hpp\"");
|
|
out.write("#if INCLUDE_JFR");
|
|
out.write("#include \"jfr/recorder/service/jfrEvent.hpp\"");
|
|
out.write("/*");
|
|
out.write(" * Each event class has an assert member function verify() which is invoked");
|
|
out.write(" * just before the engine writes the event and its fields to the data stream.");
|
|
out.write(" * The purpose of verify() is to ensure that all fields in the event are initialized");
|
|
out.write(" * and set before attempting to commit.");
|
|
out.write(" *");
|
|
out.write(" * We enforce this requirement because events are generally stack allocated and therefore");
|
|
out.write(" * *not* initialized to default values. This prevents us from inadvertently committing");
|
|
out.write(" * uninitialized values to the data stream.");
|
|
out.write(" *");
|
|
out.write(" * The assert message contains both the index (zero based) as well as the name of the field.");
|
|
out.write(" */");
|
|
out.write("");
|
|
printTypes(out, metadata, false);
|
|
out.write("");
|
|
out.write("");
|
|
out.write("#else // !INCLUDE_JFR");
|
|
out.write("");
|
|
out.write("template <typename T>");
|
|
out.write("class JfrEvent {");
|
|
out.write(" public:");
|
|
out.write(" JfrEvent() {}");
|
|
out.write(" void set_starttime(const Ticks&) const {}");
|
|
out.write(" void set_endtime(const Ticks&) const {}");
|
|
out.write(" bool should_commit() const { return false; }");
|
|
out.write(" bool is_started() const { return false; }");
|
|
out.write(" static bool is_enabled() { return false; }");
|
|
out.write(" void commit() {}");
|
|
out.write("};");
|
|
out.write("");
|
|
printTypes(out, metadata, true);
|
|
out.write("");
|
|
out.write("");
|
|
out.write("#endif // INCLUDE_JFR");
|
|
out.write("#endif // JFRFILES_JFREVENTCLASSES_HPP");
|
|
}
|
|
}
|
|
|
|
private static void printTypes(Printer out, Metadata metadata, boolean empty) {
|
|
for (TypeElement t : metadata.getStructs()) {
|
|
printType(out, t, empty);
|
|
out.write("");
|
|
}
|
|
for (TypeElement e : metadata.getEvents()) {
|
|
printEvent(out, e, empty);
|
|
out.write("");
|
|
}
|
|
}
|
|
|
|
private static void printType(Printer out, TypeElement t, boolean empty) {
|
|
out.write("struct JfrStruct" + t.name);
|
|
out.write("{");
|
|
if (!empty) {
|
|
out.write(" private:");
|
|
for (FieldElement f : t.fields) {
|
|
printField(out, f);
|
|
}
|
|
out.write("");
|
|
}
|
|
out.write(" public:");
|
|
for (FieldElement f : t.fields) {
|
|
printTypeSetter(out, f, empty);
|
|
}
|
|
out.write("");
|
|
if (!empty) {
|
|
printWriteData(out, t);
|
|
}
|
|
out.write("};");
|
|
out.write("");
|
|
}
|
|
|
|
private static void printEvent(Printer out, TypeElement event, boolean empty) {
|
|
out.write("class Event" + event.name + " : public JfrEvent<Event" + event.name + ">");
|
|
out.write("{");
|
|
if (!empty) {
|
|
out.write(" private:");
|
|
for (FieldElement f : event.fields) {
|
|
printField(out, f);
|
|
}
|
|
out.write("");
|
|
}
|
|
out.write(" public:");
|
|
if (!empty) {
|
|
out.write(" static const bool hasThread = " + event.thread + ";");
|
|
out.write(" static const bool hasStackTrace = " + event.stackTrace + ";");
|
|
out.write(" static const bool isInstant = " + !event.startTime + ";");
|
|
out.write(" static const bool hasCutoff = " + event.cutoff + ";");
|
|
out.write(" static const bool hasThrottle = " + event.throttle + ";");
|
|
out.write(" static const bool isRequestable = " + !event.period.isEmpty() + ";");
|
|
out.write(" static const JfrEventId eventId = Jfr" + event.name + "Event;");
|
|
out.write("");
|
|
}
|
|
if (!empty) {
|
|
out.write(" Event" + event.name + "(EventStartTime timing=TIMED) : JfrEvent<Event" + event.name
|
|
+ ">(timing) {}");
|
|
} else {
|
|
out.write(" Event" + event.name + "(EventStartTime timing=TIMED) {}");
|
|
}
|
|
out.write("");
|
|
int index = 0;
|
|
for (FieldElement f : event.fields) {
|
|
out.write(" void set_" + f.name + "(" + f.getParameterType() + " " + f.getParameterName() + ") {");
|
|
if (!empty) {
|
|
out.write(" this->_" + f.name + " = " + f.getParameterName() + ";");
|
|
out.write(" DEBUG_ONLY(set_field_bit(" + index++ + "));");
|
|
}
|
|
out.write(" }");
|
|
}
|
|
out.write("");
|
|
if (!empty) {
|
|
printWriteData(out, event);
|
|
out.write("");
|
|
}
|
|
out.write(" using JfrEvent<Event" + event.name
|
|
+ ">::commit; // else commit() is hidden by overloaded versions in this class");
|
|
printConstructor2(out, event, empty);
|
|
printCommitMethod(out, event, empty);
|
|
if (!empty) {
|
|
printVerify(out, event.fields);
|
|
}
|
|
out.write("};");
|
|
}
|
|
|
|
private static void printWriteData(Printer out, TypeElement type) {
|
|
out.write(" template <typename Writer>");
|
|
out.write(" void writeData(Writer& w) {");
|
|
if (type.isEvent && type.internal) {
|
|
out.write(" JfrEventSetting::unhide_internal_types();");
|
|
}
|
|
for (FieldElement field : type.fields) {
|
|
if (field.struct) {
|
|
out.write(" _" + field.name + ".writeData(w);");
|
|
} else {
|
|
out.write(" w.write(_" + field.name + ");");
|
|
}
|
|
}
|
|
out.write(" }");
|
|
}
|
|
|
|
private static void printTypeSetter(Printer out, FieldElement field, boolean empty) {
|
|
if (!empty) {
|
|
out.write(" void set_" + field.name + "(" + field.getParameterType() + " new_value) { this->_" + field.name
|
|
+ " = new_value; }");
|
|
} else {
|
|
out.write(" void set_" + field.name + "(" + field.getParameterType() + " new_value) { }");
|
|
}
|
|
}
|
|
|
|
private static void printVerify(Printer out, List<FieldElement> fields) {
|
|
out.write("");
|
|
out.write("#ifdef ASSERT");
|
|
out.write(" void verify() const {");
|
|
int index = 0;
|
|
for (FieldElement f : fields) {
|
|
out.write(" assert(verify_field_bit(" + index++
|
|
+ "), \"Attempting to write an uninitialized event field: %s\", \"_" + f.name + "\");");
|
|
}
|
|
out.write(" }");
|
|
out.write("#endif");
|
|
}
|
|
|
|
private static void printCommitMethod(Printer out, TypeElement event, boolean empty) {
|
|
if (event.startTime) {
|
|
StringJoiner sj = new StringJoiner(",\n ");
|
|
for (FieldElement f : event.fields) {
|
|
sj.add(f.getParameterType() + " " + f.name);
|
|
}
|
|
out.write("");
|
|
out.write(" void commit(" + sj.toString() + ") {");
|
|
if (!empty) {
|
|
out.write(" if (should_commit()) {");
|
|
for (FieldElement f : event.fields) {
|
|
out.write(" set_" + f.name + "(" + f.name + ");");
|
|
}
|
|
out.write(" commit();");
|
|
out.write(" }");
|
|
}
|
|
out.write(" }");
|
|
}
|
|
|
|
// Avoid clash with static commit() method
|
|
if (event.fields.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
out.write("");
|
|
StringJoiner sj = new StringJoiner(",\n ");
|
|
if (event.startTime) {
|
|
sj.add("const Ticks& startTicks");
|
|
sj.add("const Ticks& endTicks");
|
|
}
|
|
for (FieldElement f : event.fields) {
|
|
sj.add(f.getParameterType() + " " + f.name);
|
|
}
|
|
out.write(" static void commit(" + sj.toString() + ") {");
|
|
if (!empty) {
|
|
out.write(" Event" + event.name + " me(UNTIMED);");
|
|
out.write("");
|
|
out.write(" if (me.should_commit()) {");
|
|
if (event.startTime) {
|
|
out.write(" me.set_starttime(startTicks);");
|
|
out.write(" me.set_endtime(endTicks);");
|
|
}
|
|
for (FieldElement f : event.fields) {
|
|
out.write(" me.set_" + f.name + "(" + f.name + ");");
|
|
}
|
|
out.write(" me.commit();");
|
|
out.write(" }");
|
|
}
|
|
out.write(" }");
|
|
}
|
|
|
|
private static void printConstructor2(Printer out, TypeElement event, boolean empty) {
|
|
if (!event.startTime) {
|
|
out.write("");
|
|
out.write("");
|
|
}
|
|
if (event.startTime) {
|
|
out.write("");
|
|
out.write(" Event" + event.name + "(");
|
|
StringJoiner sj = new StringJoiner(",\n ");
|
|
for (FieldElement f : event.fields) {
|
|
sj.add(f.getParameterType() + " " + f.name);
|
|
}
|
|
if (!empty) {
|
|
out.write(" " + sj.toString() + ") : JfrEvent<Event" + event.name + ">(TIMED) {");
|
|
out.write(" if (should_commit()) {");
|
|
for (FieldElement f : event.fields) {
|
|
out.write(" set_" + f.name + "(" + f.name + ");");
|
|
}
|
|
out.write(" }");
|
|
} else {
|
|
out.write(" " + sj.toString() + ") {");
|
|
}
|
|
out.write(" }");
|
|
}
|
|
}
|
|
|
|
private static void printField(Printer out, FieldElement field) {
|
|
out.write(" " + field.getFieldType() + " _" + field.name + ";");
|
|
}
|
|
}
|