8246436: JFR: Avoid parsing metadata.xml during startup
Reviewed-by: mgronlun, erikj, redestad
This commit is contained in:
parent
927a7287b7
commit
3576a88369
@ -36,6 +36,24 @@ TARGETS :=
|
||||
# Hook to include the corresponding custom file, if present.
|
||||
$(eval $(call IncludeCustomExtension, hotspot/CompileTools.gmk))
|
||||
|
||||
################################################################################
|
||||
# Build tools needed for the JFR source code generation
|
||||
|
||||
HOTSPOT_TOOLS_SRCDIR := $(TOPDIR)/make/src/classes
|
||||
|
||||
HOTSPOT_TOOLS_OUTPUTDIR := $(BUILDTOOLS_OUTPUTDIR)/buildtools/hotspot_tools_classes
|
||||
|
||||
$(eval $(call SetupJavaCompilation, BUILD_TOOLS_HOTSPOT, \
|
||||
COMPILER := bootjdk, \
|
||||
SRC := $(HOTSPOT_TOOLS_SRCDIR), \
|
||||
BIN := $(HOTSPOT_TOOLS_OUTPUTDIR), \
|
||||
))
|
||||
|
||||
TARGETS += $(BUILD_TOOLS_HOTSPOT)
|
||||
|
||||
|
||||
################################################################################
|
||||
# Graal build tools
|
||||
ifeq ($(INCLUDE_GRAAL), true)
|
||||
VM_CI_SRC_DIR := $(TOPDIR)/src/jdk.internal.vm.ci/share/classes
|
||||
|
||||
|
@ -804,7 +804,7 @@ else
|
||||
|
||||
# Declare dependencies between hotspot-<variant>* targets
|
||||
$(foreach v, $(JVM_VARIANTS), \
|
||||
$(eval hotspot-$v-gensrc: java.base-copy) \
|
||||
$(eval hotspot-$v-gensrc: java.base-copy buildtools-hotspot) \
|
||||
$(eval hotspot-$v-libs: hotspot-$v-gensrc java.base-copy) \
|
||||
)
|
||||
|
||||
|
47
make/ToolsHotspot.gmk
Normal file
47
make/ToolsHotspot.gmk
Normal file
@ -0,0 +1,47 @@
|
||||
#
|
||||
# Copyright (c) 2020, 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.
|
||||
#
|
||||
|
||||
ifndef _TOOLS_HOTSPOT_GMK
|
||||
_TOOLS_HOTSPOT_GMK := 1
|
||||
|
||||
include JavaCompilation.gmk
|
||||
|
||||
HOTSPOT_TOOLS_OUTPUTDIR := $(BUILDTOOLS_OUTPUTDIR)/buildtools/hotspot_tools_classes
|
||||
|
||||
################################################################################
|
||||
# To avoid reevaluating the compilation setup for the tools each time this file
|
||||
# is included, the actual compilation is handled by CompileToolsHotspot.gmk. The
|
||||
# following trick is used to be able to declare a dependency on the built tools.
|
||||
BUILD_TOOLS_HOTSPOT := $(call SetupJavaCompilationCompileTarget, \
|
||||
BUILD_TOOLS_HOTSPOT, $(HOTSPOT_TOOLS_OUTPUTDIR))
|
||||
|
||||
################################################################################
|
||||
|
||||
TOOL_JFR_GEN := $(JAVA_SMALL) -cp $(HOTSPOT_TOOLS_OUTPUTDIR) \
|
||||
build.tools.jfr.GenerateJfrFiles
|
||||
|
||||
##########################################################################################
|
||||
|
||||
endif # _TOOLS_HOTSPOT_GMK
|
@ -23,40 +23,24 @@
|
||||
# questions.
|
||||
#
|
||||
|
||||
################################################################################
|
||||
# Build tools needed for the JFR source code generation
|
||||
|
||||
JFR_TOOLS_SRCDIR := $(TOPDIR)/make/src/classes
|
||||
JFR_TOOLS_OUTPUTDIR := $(JVM_VARIANT_OUTPUTDIR)/buildtools/tools_classes
|
||||
|
||||
$(eval $(call SetupJavaCompilation, BUILD_JFR_TOOLS, \
|
||||
COMPILER := bootjdk, \
|
||||
SMALL_JAVA := false, \
|
||||
SRC := $(JFR_TOOLS_SRCDIR), \
|
||||
BIN := $(JFR_TOOLS_OUTPUTDIR), \
|
||||
DISABLED_WARNINGS := try, \
|
||||
))
|
||||
|
||||
TARGETS += $(BUILD_JFR_TOOLS)
|
||||
|
||||
################################################################################
|
||||
# Setup make rules for JFR gensrc file generation.
|
||||
#
|
||||
TOOL_JFR_GEN := $(JAVA_SMALL) -cp $(JFR_TOOLS_OUTPUTDIR) build.tools.jfr.GenerateJfrFiles
|
||||
|
||||
JFR_OUTPUTDIR := $(JVM_VARIANT_OUTPUTDIR)/gensrc/jfrfiles
|
||||
include $(TOPDIR)/make/ToolsHotspot.gmk
|
||||
|
||||
JFR_SRCDIR := $(TOPDIR)/src/hotspot/share/jfr/metadata
|
||||
JFR_OUTPUTDIR := $(JVM_VARIANT_OUTPUTDIR)/gensrc/jfrfiles
|
||||
|
||||
# Changing these will trigger a rebuild of generated jfr files.
|
||||
METADATA_XML := $(JFR_SRCDIR)/metadata.xml
|
||||
METADATA_XSD := $(JFR_SRCDIR)/metadata.xsd
|
||||
|
||||
$(eval $(call SetupExecute, jfr_gen, \
|
||||
$(eval $(call SetupExecute, jfr_gen_headers, \
|
||||
INFO := Generating JFR header files, \
|
||||
DEPS := $(METADATA_XML) $(METADATA_XSD) $(BUILD_JFR_TOOLS), \
|
||||
DEPS := $(METADATA_XML) $(METADATA_XSD) $(BUILD_TOOLS_HOTSPOT), \
|
||||
OUTPUT_DIR := $(JFR_OUTPUTDIR), \
|
||||
COMMAND := $(TOOL_JFR_GEN) $(METADATA_XML) $(METADATA_XSD) $(JFR_OUTPUTDIR), \
|
||||
COMMAND := $(TOOL_JFR_GEN) --mode headers --xml $(METADATA_XML) --xsd $(METADATA_XSD) --output $(JFR_OUTPUTDIR), \
|
||||
))
|
||||
|
||||
JFR_FILES := $(jfr_gen_TARGET)
|
||||
TARGETS += $(JFR_FILES)
|
||||
TARGETS += $(jfr_gen_headers)
|
||||
|
@ -27,14 +27,6 @@ include CopyCommon.gmk
|
||||
|
||||
################################################################################
|
||||
|
||||
$(eval $(call SetupCopyFiles, COPY_JFR_METADATA, \
|
||||
SRC := $(TOPDIR)/src/hotspot/share/jfr/metadata, \
|
||||
DEST := $(JDK_OUTPUTDIR)/modules/jdk.jfr/jdk/jfr/internal/types, \
|
||||
FILES := metadata.xml \
|
||||
))
|
||||
|
||||
TARGETS += $(COPY_JFR_METADATA)
|
||||
|
||||
JFR_CONF_DIR := $(TOPDIR)/src/jdk.jfr/share/conf/jfr
|
||||
$(eval $(call SetupCopyFiles, COPY_JFR_CONF, \
|
||||
DEST := $(LIB_DST_DIR)/jfr, \
|
||||
|
48
make/modules/jdk.jfr/Gendata.gmk
Normal file
48
make/modules/jdk.jfr/Gendata.gmk
Normal file
@ -0,0 +1,48 @@
|
||||
#
|
||||
# Copyright (c) 2020, 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.
|
||||
#
|
||||
|
||||
################################################################################
|
||||
|
||||
include ToolsHotspot.gmk
|
||||
include Execute.gmk
|
||||
|
||||
JFR_SRCDIR := $(TOPDIR)/src/hotspot/share/jfr/metadata
|
||||
JFR_DATA_OUTPUTDIR := $(OUTPUTDIR)/jdk/modules/jdk.jfr/jdk/jfr/internal/types
|
||||
JFR_DATA_OUTPUTFILE := $(JFR_DATA_OUTPUTDIR)/metadata.bin
|
||||
JFR_DATA_SUPPORTDIR := $(SUPPORT_OUTPUTDIR)/gendata/jdk.jfr
|
||||
|
||||
# Changing these will trigger a rebuild of generated jfr files.
|
||||
METADATA_XML := $(JFR_SRCDIR)/metadata.xml
|
||||
METADATA_XSD := $(JFR_SRCDIR)/metadata.xsd
|
||||
|
||||
$(eval $(call SetupExecute, jfr_gen_metadata, \
|
||||
INFO := Generating JFR metadata, \
|
||||
DEPS := $(METADATA_XML) $(METADATA_XSD) $(BUILD_TOOLS_HOTSPOT), \
|
||||
OUTPUT_FILE := $(JFR_DATA_OUTPUTFILE), \
|
||||
SUPPORT_DIR := $(JFR_DATA_SUPPORTDIR), \
|
||||
COMMAND := $(TOOL_JFR_GEN) --mode metadata --xml $(METADATA_XML) --xsd $(METADATA_XSD) --output $(JFR_DATA_OUTPUTFILE), \
|
||||
))
|
||||
|
||||
TARGETS += $(jfr_gen_metadata)
|
@ -1,16 +1,19 @@
|
||||
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.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@ -25,95 +28,108 @@ 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 {
|
||||
|
||||
public static void main(String... args) throws Exception {
|
||||
if (args.length != 3) {
|
||||
System.err.println("Incorrect number of command line arguments.");
|
||||
System.err.println("Usage:");
|
||||
System.err.println("java GenerateJfrFiles[.java] <path-to-metadata.xml> <path-to-metadata.xsd> <output-directory>");
|
||||
System.exit(1);
|
||||
enum OutputMode {
|
||||
headers, metadata
|
||||
}
|
||||
try {
|
||||
File metadataXml = new File(args[0]);
|
||||
File metadataSchema = new File(args[1]);
|
||||
File outputDirectory = new File(args[2]);
|
||||
|
||||
Metadata metadata = new Metadata(metadataXml, metadataSchema);
|
||||
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();
|
||||
|
||||
TypeCounter typeCounter = new TypeCounter();
|
||||
printJfrEventIdsHpp(metadata, typeCounter, outputDirectory);
|
||||
printJfrTypesHpp(metadata, typeCounter, outputDirectory);
|
||||
printJfrPeriodicHpp(metadata, outputDirectory);
|
||||
printJfrEventControlHpp(metadata, typeCounter, outputDirectory);
|
||||
printJfrEventClassesHpp(metadata, outputDirectory);
|
||||
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 TypeCounter {
|
||||
final static long RESERVED_EVENT_COUNT = 2;
|
||||
long typeId = -1;
|
||||
long eventId = -1;
|
||||
long eventCount = 0;
|
||||
String firstTypeName;
|
||||
String lastTypeName;
|
||||
String firstEventName;
|
||||
String lastEventname;
|
||||
|
||||
public long nextEventId(String name) {
|
||||
eventCount++;
|
||||
if (eventId == -1) {
|
||||
eventId = firstEventId();
|
||||
firstEventName = lastEventname = name;
|
||||
return eventId;
|
||||
}
|
||||
lastEventname = name;
|
||||
return ++eventId;
|
||||
}
|
||||
|
||||
public long nextTypeId(String typeName) {
|
||||
if (typeId == -1) {
|
||||
lastTypeName = firstTypeName = typeName;
|
||||
typeId = lastEventId();
|
||||
}
|
||||
lastTypeName = typeName;
|
||||
return ++typeId;
|
||||
}
|
||||
|
||||
public long firstEventId() {
|
||||
return RESERVED_EVENT_COUNT;
|
||||
}
|
||||
|
||||
public long lastEventId() {
|
||||
return eventId == -1 ? firstEventId() : eventId;
|
||||
}
|
||||
|
||||
public long eventCount() {
|
||||
return eventCount;
|
||||
}
|
||||
|
||||
public String firstTypeName() {
|
||||
return firstTypeName;
|
||||
}
|
||||
|
||||
public String lastTypeName() {
|
||||
return lastTypeName;
|
||||
}
|
||||
|
||||
public String firstEventName() {
|
||||
return firstEventName;
|
||||
}
|
||||
|
||||
public String lastEventName() {
|
||||
return lastEventname;
|
||||
}
|
||||
}
|
||||
|
||||
static class XmlType {
|
||||
final String name;
|
||||
@ -121,28 +137,99 @@ public class GenerateJfrFiles {
|
||||
final String parameterType;
|
||||
final String javaType;
|
||||
final boolean unsigned;
|
||||
final String contentType;
|
||||
|
||||
XmlType(String name, String fieldType, String parameterType, String javaType, boolean unsigned) {
|
||||
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 fieldType;
|
||||
String parameterType;
|
||||
boolean supportStruct;
|
||||
String javaType;
|
||||
String label = "";
|
||||
String description = "";
|
||||
String category = "";
|
||||
boolean thread;
|
||||
boolean stackTrace;
|
||||
boolean startTime;
|
||||
String period = "";
|
||||
boolean cutoff;
|
||||
boolean experimental;
|
||||
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(experimental);
|
||||
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<>();
|
||||
Metadata(File metadataXml, File metadataSchema) throws ParserConfigurationException, SAXException, FileNotFoundException, IOException {
|
||||
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));
|
||||
@ -150,16 +237,19 @@ public class GenerateJfrFiles {
|
||||
sp.parse(metadataXml, new MetadataHandler(this));
|
||||
}
|
||||
|
||||
List<EventElement> getEvents() {
|
||||
return getList(t -> t.getClass() == EventElement.class);
|
||||
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.getClass() == EventElement.class || t.supportStruct);
|
||||
}
|
||||
|
||||
List<TypeElement> getTypesAndStructs() {
|
||||
return getList(t -> t.getClass() == TypeElement.class || t.supportStruct);
|
||||
return getList(t -> t.isEvent || t.supportStruct);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@ -173,16 +263,16 @@ public class GenerateJfrFiles {
|
||||
return result;
|
||||
}
|
||||
|
||||
List<EventElement> getPeriodicEvents() {
|
||||
return getList(t -> t.getClass() == EventElement.class && ((EventElement) t).periodic);
|
||||
List<TypeElement> getPeriodicEvents() {
|
||||
return getList(t -> t.isEvent && !t.period.isEmpty());
|
||||
}
|
||||
|
||||
List<TypeElement> getTypes() {
|
||||
return getList(t -> t.getClass() == TypeElement.class);
|
||||
return getList(t -> !t.isEvent);
|
||||
}
|
||||
|
||||
List<TypeElement> getStructs() {
|
||||
return getList(t -> t.getClass() == TypeElement.class && t.supportStruct);
|
||||
return getList(t -> !t.isEvent && t.supportStruct);
|
||||
}
|
||||
|
||||
void verify() {
|
||||
@ -190,7 +280,8 @@ public class GenerateJfrFiles {
|
||||
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);
|
||||
throw new IllegalStateException("Could not find definition of type '" + f.typeName
|
||||
+ "' used by " + t.name + "#" + f.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -198,26 +289,96 @@ public class GenerateJfrFiles {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class EventElement extends TypeElement {
|
||||
String representation;
|
||||
boolean thread;
|
||||
boolean stackTrace;
|
||||
boolean startTime;
|
||||
boolean periodic;
|
||||
boolean cutoff;
|
||||
String commitState;
|
||||
// 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 {
|
||||
@ -225,12 +386,36 @@ public class GenerateJfrFiles {
|
||||
TypeElement type;
|
||||
String name;
|
||||
String typeName;
|
||||
boolean struct;
|
||||
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 + "&";
|
||||
@ -262,50 +447,75 @@ public class GenerateJfrFiles {
|
||||
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 {
|
||||
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");
|
||||
String parameterType = attributes.getValue("parameterType");
|
||||
String fieldType = attributes.getValue("fieldType");
|
||||
String javaType = attributes.getValue("javaType");
|
||||
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, unsigned));
|
||||
metadata.xmlTypes.put(name,
|
||||
new XmlType(name, fieldType, parameterType, javaType, contentType, unsigned));
|
||||
break;
|
||||
case "Relation":
|
||||
case "Type":
|
||||
currentType = new TypeElement();
|
||||
currentType.name = attributes.getValue("name");
|
||||
break;
|
||||
case "Event":
|
||||
EventElement eventType = new EventElement();
|
||||
eventType.name = attributes.getValue("name");
|
||||
eventType.thread = getBoolean(attributes, "thread", false);
|
||||
eventType.stackTrace = getBoolean(attributes, "stackTrace", false);
|
||||
eventType.startTime = getBoolean(attributes, "startTime", true);
|
||||
eventType.periodic = attributes.getValue("period") != null;
|
||||
eventType.cutoff = getBoolean(attributes, "cutoff", false);
|
||||
eventType.commitState = attributes.getValue("commitState");
|
||||
currentType = eventType;
|
||||
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.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.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.name = attributes.getValue("name");
|
||||
currentField.typeName = attributes.getValue("type");
|
||||
currentField.array = getBoolean(attributes, "array", false);
|
||||
currentField.transition = getString(attributes, "transition");
|
||||
currentField.relation = getString(attributes, "relation");
|
||||
currentField.experimental = getBoolean(attributes, "experimental", false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean getBoolean(Attributes attributes, String name, boolean defaultValue) {
|
||||
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);
|
||||
}
|
||||
@ -327,10 +537,11 @@ public class GenerateJfrFiles {
|
||||
}
|
||||
}
|
||||
|
||||
static class Printer implements AutoCloseable {
|
||||
static class Printer implements Closeable {
|
||||
final PrintStream out;
|
||||
Printer(File outputDirectory, String filename) throws FileNotFoundException {
|
||||
out = new PrintStream(new BufferedOutputStream(new FileOutputStream(new File(outputDirectory, filename))));
|
||||
|
||||
Printer(File outputFile) throws FileNotFoundException {
|
||||
out = new PrintStream(new BufferedOutputStream(new FileOutputStream(outputFile)));
|
||||
write("/* AUTOMATICALLY GENERATED FILE - DO NOT EDIT */");
|
||||
write("");
|
||||
}
|
||||
@ -341,13 +552,13 @@ public class GenerateJfrFiles {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
public void close() throws IOException {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static void printJfrPeriodicHpp(Metadata metadata, File outputDirectory) throws Exception {
|
||||
try (Printer out = new Printer(outputDirectory, "jfrPeriodic.hpp")) {
|
||||
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("");
|
||||
@ -361,7 +572,7 @@ public class GenerateJfrFiles {
|
||||
out.write(" static void requestEvent(JfrEventId id) {");
|
||||
out.write(" switch(id) {");
|
||||
out.write(" ");
|
||||
for (EventElement e : metadata.getPeriodicEvents()) {
|
||||
for (TypeElement e : metadata.getPeriodicEvents()) {
|
||||
out.write(" case Jfr" + e.name + "Event:");
|
||||
out.write(" request" + e.name + "();");
|
||||
out.write(" break;");
|
||||
@ -374,7 +585,7 @@ public class GenerateJfrFiles {
|
||||
out.write("");
|
||||
out.write(" private:");
|
||||
out.write("");
|
||||
for (EventElement e : metadata.getPeriodicEvents()) {
|
||||
for (TypeElement e : metadata.getPeriodicEvents()) {
|
||||
out.write(" static void request" + e.name + "(void);");
|
||||
out.write("");
|
||||
}
|
||||
@ -385,8 +596,8 @@ public class GenerateJfrFiles {
|
||||
}
|
||||
}
|
||||
|
||||
private static void printJfrEventControlHpp(Metadata metadata, TypeCounter typeCounter, File outputDirectory) throws Exception {
|
||||
try (Printer out = new Printer(outputDirectory, "jfrEventControl.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("");
|
||||
@ -426,8 +637,8 @@ public class GenerateJfrFiles {
|
||||
}
|
||||
}
|
||||
|
||||
private static void printJfrEventIdsHpp(Metadata metadata, TypeCounter typeCounter, File outputDirectory) throws Exception {
|
||||
try (Printer out = new Printer(outputDirectory, "jfrEventIds.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("");
|
||||
@ -438,23 +649,28 @@ public class GenerateJfrFiles {
|
||||
out.write(" JfrMetadataEvent = 0,");
|
||||
out.write(" JfrCheckpointEvent = 1,");
|
||||
for (TypeElement t : metadata.getEvents()) {
|
||||
String name = "Jfr" + t.name +"Event";
|
||||
out.write(" " + name + " = " + typeCounter.nextEventId(name) + ",");
|
||||
out.write(" " + jfrEventId(t.name) + " = " + t.id + ",");
|
||||
}
|
||||
out.write("};");
|
||||
out.write("typedef enum JfrEventId JfrEventId;");
|
||||
out.write("");
|
||||
out.write("static const JfrEventId FIRST_EVENT_ID = " + typeCounter.firstEventName() + ";");
|
||||
out.write("static const JfrEventId LAST_EVENT_ID = " + typeCounter.lastEventName() + ";");
|
||||
out.write("static const int NUMBER_OF_EVENTS = " + typeCounter.eventCount() + ";");
|
||||
out.write("static const int NUMBER_OF_RESERVED_EVENTS = " + TypeCounter.RESERVED_EVENT_COUNT + ";");
|
||||
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 void printJfrTypesHpp(Metadata metadata, TypeCounter typeCounter, File outputDirectory) throws Exception {
|
||||
try (Printer out = new Printer(outputDirectory, "jfrTypes.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("");
|
||||
@ -465,36 +681,27 @@ public class GenerateJfrFiles {
|
||||
out.write("#include \"memory/allocation.hpp\"");
|
||||
out.write("");
|
||||
out.write("enum JfrTypeId {");
|
||||
Map<String, XmlType> javaTypes = new LinkedHashMap<>();
|
||||
for (var t : metadata.xmlTypes.entrySet()) {
|
||||
String name = t.getKey();
|
||||
XmlType xmlType = t.getValue();
|
||||
if (xmlType.javaType != null && !xmlType.unsigned) {
|
||||
String typeName = "TYPE_" + name.toUpperCase();
|
||||
long typeId = typeCounter.nextTypeId(typeName);
|
||||
out.write(" " + typeName + " = " + typeId + ",");
|
||||
javaTypes.put(name, xmlType);
|
||||
}
|
||||
}
|
||||
for (TypeElement type : metadata.getTypes()) {
|
||||
String name = type.name;
|
||||
if (!javaTypes.containsKey(name)) {
|
||||
String typeName = "TYPE_" + name.toUpperCase();
|
||||
long typeId = typeCounter.nextTypeId(typeName);
|
||||
out.write(" " + typeName + " = " + typeId + ",");
|
||||
}
|
||||
out.write(" " + jfrTypeId(type.name) + " = " + type.id + ",");
|
||||
}
|
||||
out.write("};");
|
||||
out.write("");
|
||||
out.write("static const JfrTypeId FIRST_TYPE_ID = " + typeCounter.firstTypeName() + ";");
|
||||
out.write("static const JfrTypeId LAST_TYPE_ID = " + typeCounter.lastTypeName() + ";");
|
||||
|
||||
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) {");
|
||||
for (Entry<String, XmlType> m : javaTypes.entrySet()) {
|
||||
XmlType xmlType = m.getValue();
|
||||
|
||||
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) {");
|
||||
@ -508,11 +715,14 @@ public class GenerateJfrFiles {
|
||||
out.write("#endif // INCLUDE_JFR");
|
||||
out.write("#endif // JFRFILES_JFRTYPES_HPP");
|
||||
}
|
||||
;
|
||||
}
|
||||
|
||||
private static void printJfrEventClassesHpp(Metadata metadata, File outputDirectory) throws Exception {
|
||||
try (Printer out = new Printer(outputDirectory, "jfrEventClasses.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("");
|
||||
@ -566,7 +776,7 @@ public class GenerateJfrFiles {
|
||||
printType(out, t, empty);
|
||||
out.write("");
|
||||
}
|
||||
for (EventElement e : metadata.getEvents()) {
|
||||
for (TypeElement e : metadata.getEvents()) {
|
||||
printEvent(out, e, empty);
|
||||
out.write("");
|
||||
}
|
||||
@ -588,13 +798,13 @@ public class GenerateJfrFiles {
|
||||
}
|
||||
out.write("");
|
||||
if (!empty) {
|
||||
printWriteData(out, t.fields, null);
|
||||
printWriteData(out, t);
|
||||
}
|
||||
out.write("};");
|
||||
out.write("");
|
||||
}
|
||||
|
||||
private static void printEvent(Printer out, EventElement event, boolean empty) {
|
||||
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) {
|
||||
@ -610,12 +820,13 @@ public class GenerateJfrFiles {
|
||||
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 isRequestable = " + event.periodic + ";");
|
||||
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) {}");
|
||||
out.write(" Event" + event.name + "(EventStartTime timing=TIMED) : JfrEvent<Event" + event.name
|
||||
+ ">(timing) {}");
|
||||
} else {
|
||||
out.write(" Event" + event.name + "(EventStartTime timing=TIMED) {}");
|
||||
}
|
||||
@ -631,10 +842,11 @@ public class GenerateJfrFiles {
|
||||
}
|
||||
out.write("");
|
||||
if (!empty) {
|
||||
printWriteData(out, event.fields, event.commitState);
|
||||
printWriteData(out, event);
|
||||
out.write("");
|
||||
}
|
||||
out.write(" using JfrEvent<Event" + event.name + ">::commit; // else commit() is hidden by overloaded versions in this class");
|
||||
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) {
|
||||
@ -643,14 +855,14 @@ public class GenerateJfrFiles {
|
||||
out.write("};");
|
||||
}
|
||||
|
||||
private static void printWriteData(Printer out, List<FieldElement> fields, String commitState) {
|
||||
private static void printWriteData(Printer out, TypeElement type) {
|
||||
out.write(" template <typename Writer>");
|
||||
out.write(" void writeData(Writer& w) {");
|
||||
if (("_thread_in_native").equals(commitState)) {
|
||||
if (("_thread_in_native").equals(type.commitState)) {
|
||||
out.write(" // explicit epoch synchronization check");
|
||||
out.write(" JfrEpochSynchronization sync;");
|
||||
}
|
||||
for (FieldElement field : fields) {
|
||||
for (FieldElement field : type.fields) {
|
||||
if (field.struct) {
|
||||
out.write(" _" + field.name + ".writeData(w);");
|
||||
} else {
|
||||
@ -662,7 +874,8 @@ public class GenerateJfrFiles {
|
||||
|
||||
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; }");
|
||||
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) { }");
|
||||
}
|
||||
@ -674,13 +887,14 @@ public class GenerateJfrFiles {
|
||||
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(" 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, EventElement event, boolean empty) {
|
||||
private static void printCommitMethod(Printer out, TypeElement event, boolean empty) {
|
||||
if (event.startTime) {
|
||||
StringJoiner sj = new StringJoiner(",\n ");
|
||||
for (FieldElement f : event.fields) {
|
||||
@ -731,7 +945,7 @@ public class GenerateJfrFiles {
|
||||
out.write(" }");
|
||||
}
|
||||
|
||||
private static void printConstructor2(Printer out, EventElement event, boolean empty) {
|
||||
private static void printConstructor2(Printer out, TypeElement event, boolean empty) {
|
||||
if (!event.startTime) {
|
||||
out.write("");
|
||||
out.write("");
|
||||
|
@ -1,418 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2020, 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;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.internal.org.xml.sax.Attributes;
|
||||
import jdk.internal.org.xml.sax.EntityResolver;
|
||||
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;
|
||||
import jdk.jfr.AnnotationElement;
|
||||
import jdk.jfr.Category;
|
||||
import jdk.jfr.Description;
|
||||
import jdk.jfr.Enabled;
|
||||
import jdk.jfr.Experimental;
|
||||
import jdk.jfr.Label;
|
||||
import jdk.jfr.Period;
|
||||
import jdk.jfr.Relational;
|
||||
import jdk.jfr.StackTrace;
|
||||
import jdk.jfr.Threshold;
|
||||
import jdk.jfr.TransitionFrom;
|
||||
import jdk.jfr.TransitionTo;
|
||||
import jdk.jfr.Unsigned;
|
||||
|
||||
final class MetadataHandler extends DefaultHandler implements EntityResolver {
|
||||
|
||||
// Metadata and Checkpoint event
|
||||
private final long RESERVED_EVENT_COUNT = 2;
|
||||
|
||||
static class TypeElement {
|
||||
List<FieldElement> fields = new ArrayList<>();
|
||||
String name;
|
||||
String label;
|
||||
String description;
|
||||
String category;
|
||||
String superType;
|
||||
String period;
|
||||
boolean thread;
|
||||
boolean startTime;
|
||||
boolean stackTrace;
|
||||
boolean cutoff;
|
||||
boolean isEvent;
|
||||
boolean isRelation;
|
||||
boolean experimental;
|
||||
boolean valueType;
|
||||
}
|
||||
|
||||
static class FieldElement {
|
||||
TypeElement referenceType;
|
||||
String name;
|
||||
String label;
|
||||
String description;
|
||||
String contentType;
|
||||
String typeName;
|
||||
String transition;
|
||||
String relation;
|
||||
boolean struct;
|
||||
boolean array;
|
||||
boolean experimental;
|
||||
boolean unsigned;
|
||||
}
|
||||
|
||||
static class XmlType {
|
||||
String name;
|
||||
String javaType;
|
||||
String contentType;
|
||||
boolean unsigned;
|
||||
}
|
||||
|
||||
final Map<String, TypeElement> types = new LinkedHashMap<>(200);
|
||||
final Map<String, XmlType> xmlTypes = new LinkedHashMap<>(20);
|
||||
final Map<String, List<AnnotationElement>> xmlContentTypes = new LinkedHashMap<>(20);
|
||||
FieldElement currentField;
|
||||
TypeElement currentType;
|
||||
long eventCount;
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
|
||||
switch (qName) {
|
||||
case "XmlType":
|
||||
XmlType xmlType = new XmlType();
|
||||
xmlType.name = attributes.getValue("name");
|
||||
xmlType.javaType = attributes.getValue("javaType");
|
||||
xmlType.contentType = attributes.getValue("contentType");
|
||||
xmlType.unsigned = Boolean.valueOf(attributes.getValue("unsigned"));
|
||||
xmlTypes.put(xmlType.name, xmlType);
|
||||
break;
|
||||
case "Relation":
|
||||
case "Type":
|
||||
case "Event":
|
||||
currentType = new TypeElement();
|
||||
currentType.name = attributes.getValue("name");
|
||||
currentType.label = attributes.getValue("label");
|
||||
currentType.description = attributes.getValue("description");
|
||||
currentType.category = attributes.getValue("category");
|
||||
currentType.thread = getBoolean(attributes, "thread", false);
|
||||
currentType.stackTrace = getBoolean(attributes, "stackTrace", false);
|
||||
currentType.startTime = getBoolean(attributes, "startTime", true);
|
||||
currentType.period = attributes.getValue("period");
|
||||
currentType.cutoff = getBoolean(attributes, "cutoff", false);
|
||||
currentType.experimental = getBoolean(attributes, "experimental", false);
|
||||
currentType.isEvent = qName.equals("Event");
|
||||
currentType.isRelation = qName.equals("Relation");
|
||||
break;
|
||||
case "Field":
|
||||
currentField = new FieldElement();
|
||||
currentField.struct = getBoolean(attributes, "struct", false);
|
||||
currentField.array = getBoolean(attributes, "array", false);
|
||||
currentField.name = attributes.getValue("name");
|
||||
currentField.label = attributes.getValue("label");
|
||||
currentField.typeName = attributes.getValue("type");
|
||||
currentField.description = attributes.getValue("description");
|
||||
currentField.experimental = getBoolean(attributes, "experimental", false);
|
||||
currentField.contentType = attributes.getValue("contentType");
|
||||
currentField.relation = attributes.getValue("relation");
|
||||
currentField.transition = attributes.getValue("transition");
|
||||
break;
|
||||
case "XmlContentType":
|
||||
String name = attributes.getValue("name");
|
||||
String annotation = attributes.getValue("annotation");
|
||||
xmlContentTypes.put(name, createAnnotationElements(annotation));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private List<AnnotationElement> createAnnotationElements(String annotation) throws InternalError {
|
||||
String[] annotations = annotation.split(",");
|
||||
List<AnnotationElement> annotationElements = new ArrayList<>();
|
||||
for (String a : annotations) {
|
||||
a = a.trim();
|
||||
int leftParenthesis = a.indexOf("(");
|
||||
if (leftParenthesis == -1) {
|
||||
annotationElements.add(new AnnotationElement(createAnnotationClass(a)));
|
||||
} else {
|
||||
int rightParenthesis = a.lastIndexOf(")");
|
||||
if (rightParenthesis == -1) {
|
||||
throw new InternalError("Expected closing parenthesis for 'XMLContentType'");
|
||||
}
|
||||
String value = a.substring(leftParenthesis + 1, rightParenthesis);
|
||||
String type = a.substring(0, leftParenthesis);
|
||||
annotationElements.add(new AnnotationElement(createAnnotationClass(type), value));
|
||||
}
|
||||
}
|
||||
return annotationElements;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Class<? extends Annotation> createAnnotationClass(String type) {
|
||||
try {
|
||||
if (!type.startsWith("jdk.jfr.")) {
|
||||
throw new IllegalStateException("Incorrect type " + type + ". Annotation class must be located in jdk.jfr package.");
|
||||
}
|
||||
Class<?> c = Class.forName(type, true, null);
|
||||
return (Class<? extends Annotation>) c;
|
||||
} catch (ClassNotFoundException cne) {
|
||||
throw new IllegalStateException(cne);
|
||||
}
|
||||
}
|
||||
|
||||
private 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 "Type":
|
||||
case "Event":
|
||||
case "Relation":
|
||||
types.put(currentType.name, currentType);
|
||||
if (currentType.isEvent) {
|
||||
eventCount++;
|
||||
}
|
||||
currentType = null;
|
||||
break;
|
||||
case "Field":
|
||||
currentType.fields.add(currentField);
|
||||
currentField = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Type> createTypes() throws IOException {
|
||||
SAXParser parser = new SAXParserImpl();
|
||||
MetadataHandler t = new MetadataHandler();
|
||||
try (InputStream is = new BufferedInputStream(SecuritySupport.getResourceAsStream("/jdk/jfr/internal/types/metadata.xml"))) {
|
||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.DEBUG, "Parsing metadata.xml");
|
||||
try {
|
||||
parser.parse(is, t);
|
||||
return t.buildTypes();
|
||||
} catch (Exception e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<Type> buildTypes() {
|
||||
removeXMLConvenience();
|
||||
Map<String, Type> typeMap = buildTypeMap();
|
||||
Map<String, AnnotationElement> relationMap = buildRelationMap(typeMap);
|
||||
addFields(typeMap, relationMap);
|
||||
return trimTypes(typeMap);
|
||||
}
|
||||
|
||||
private Map<String, AnnotationElement> buildRelationMap(Map<String, Type> typeMap) {
|
||||
Map<String, AnnotationElement> relationMap = new HashMap<>();
|
||||
for (TypeElement t : types.values()) {
|
||||
if (t.isRelation) {
|
||||
Type relationType = typeMap.get(t.name);
|
||||
AnnotationElement ae = PrivateAccess.getInstance().newAnnotation(relationType, Collections.emptyList(), true);
|
||||
relationMap.put(t.name, ae);
|
||||
}
|
||||
}
|
||||
return relationMap;
|
||||
}
|
||||
|
||||
private List<Type> trimTypes(Map<String, Type> lookup) {
|
||||
List<Type> trimmedTypes = new ArrayList<>(lookup.size());
|
||||
for (Type t : lookup.values()) {
|
||||
t.trimFields();
|
||||
trimmedTypes.add(t);
|
||||
}
|
||||
return trimmedTypes;
|
||||
}
|
||||
|
||||
private void addFields(Map<String, Type> lookup, Map<String, AnnotationElement> relationMap) {
|
||||
for (TypeElement te : types.values()) {
|
||||
Type type = lookup.get(te.name);
|
||||
if (te.isEvent) {
|
||||
boolean periodic = te.period!= null;
|
||||
TypeLibrary.addImplicitFields(type, periodic, te.startTime && !periodic, te.thread, te.stackTrace && !periodic, te.cutoff);
|
||||
}
|
||||
for (FieldElement f : te.fields) {
|
||||
Type fieldType = Type.getKnownType(f.typeName);
|
||||
if (fieldType == null) {
|
||||
fieldType = Objects.requireNonNull(lookup.get(f.referenceType.name));
|
||||
}
|
||||
List<AnnotationElement> aes = new ArrayList<>();
|
||||
if (f.unsigned) {
|
||||
aes.add(new AnnotationElement(Unsigned.class));
|
||||
}
|
||||
if (f.contentType != null) {
|
||||
aes.addAll(Objects.requireNonNull(xmlContentTypes.get(f.contentType)));
|
||||
}
|
||||
if (f.relation != null) {
|
||||
String relationTypeName = Type.TYPES_PREFIX + f.relation;
|
||||
AnnotationElement t = relationMap.get(relationTypeName);
|
||||
aes.add(Objects.requireNonNull(t));
|
||||
}
|
||||
if (f.label != null) {
|
||||
aes.add(new AnnotationElement(Label.class, f.label));
|
||||
}
|
||||
if (f.experimental) {
|
||||
aes.add(new AnnotationElement(Experimental.class));
|
||||
}
|
||||
if (f.description != null) {
|
||||
aes.add(new AnnotationElement(Description.class, f.description));
|
||||
}
|
||||
if ("from".equals(f.transition)) {
|
||||
aes.add(new AnnotationElement(TransitionFrom.class));
|
||||
}
|
||||
if ("to".equals(f.transition)) {
|
||||
aes.add(new AnnotationElement(TransitionTo.class));
|
||||
}
|
||||
boolean constantPool = !f.struct && f.referenceType != null;
|
||||
type.add(PrivateAccess.getInstance().newValueDescriptor(f.name, fieldType, aes, f.array ? 1 : 0, constantPool, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Type> buildTypeMap() {
|
||||
Map<String, Type> typeMap = new HashMap<>();
|
||||
Map<String, Type> knownTypeMap = new HashMap<>();
|
||||
for (Type kt :Type.getKnownTypes()) {
|
||||
typeMap.put(kt.getName(), kt);
|
||||
knownTypeMap.put(kt.getName(), kt);
|
||||
}
|
||||
long eventTypeId = RESERVED_EVENT_COUNT;
|
||||
long typeId = RESERVED_EVENT_COUNT + eventCount + knownTypeMap.size();
|
||||
for (TypeElement t : types.values()) {
|
||||
List<AnnotationElement> aes = new ArrayList<>();
|
||||
if (t.category != null) {
|
||||
aes.add(new AnnotationElement(Category.class, buildCategoryArray(t.category)));
|
||||
}
|
||||
if (t.label != null) {
|
||||
aes.add(new AnnotationElement(Label.class, t.label));
|
||||
}
|
||||
if (t.description != null) {
|
||||
aes.add(new AnnotationElement(Description.class, t.description));
|
||||
}
|
||||
if (t.isEvent) {
|
||||
if (t.period != null) {
|
||||
aes.add(new AnnotationElement(Period.class, t.period));
|
||||
} else {
|
||||
if (t.startTime) {
|
||||
aes.add(new AnnotationElement(Threshold.class, "0 ns"));
|
||||
}
|
||||
if (t.stackTrace) {
|
||||
aes.add(new AnnotationElement(StackTrace.class, true));
|
||||
}
|
||||
}
|
||||
if (t.cutoff) {
|
||||
aes.add(new AnnotationElement(Cutoff.class, Cutoff.INFINITY));
|
||||
}
|
||||
}
|
||||
if (t.experimental) {
|
||||
aes.add(new AnnotationElement(Experimental.class));
|
||||
}
|
||||
Type type;
|
||||
if (t.isEvent) {
|
||||
aes.add(new AnnotationElement(Enabled.class, false));
|
||||
type = new PlatformEventType(t.name, eventTypeId++, false, true);
|
||||
} else {
|
||||
if (knownTypeMap.containsKey(t.name)) {
|
||||
type = knownTypeMap.get(t.name);
|
||||
} else {
|
||||
if (t.isRelation) {
|
||||
type = new Type(t.name, Type.SUPER_TYPE_ANNOTATION, typeId++);
|
||||
aes.add(new AnnotationElement(Relational.class));
|
||||
} else {
|
||||
type = new Type(t.name, null, typeId++);
|
||||
}
|
||||
}
|
||||
}
|
||||
type.setAnnotations(aes);
|
||||
typeMap.put(t.name, type);
|
||||
}
|
||||
return typeMap;
|
||||
}
|
||||
|
||||
private String[] buildCategoryArray(String category) {
|
||||
List<String> categories = new ArrayList<>();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (char c : category.toCharArray()) {
|
||||
if (c == ',') {
|
||||
categories.add(sb.toString().trim());
|
||||
sb.setLength(0);
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
categories.add(sb.toString().trim());
|
||||
return categories.toArray(new String[0]);
|
||||
}
|
||||
|
||||
private void removeXMLConvenience() {
|
||||
for (TypeElement t : types.values()) {
|
||||
XmlType xmlType = xmlTypes.get(t.name);
|
||||
if (xmlType != null && xmlType.javaType != null) {
|
||||
t.name = xmlType.javaType; // known type, i.e primitive
|
||||
} else {
|
||||
if (t.isEvent) {
|
||||
t.name = Type.EVENT_NAME_PREFIX + t.name;
|
||||
} else {
|
||||
t.name = Type.TYPES_PREFIX + t.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (TypeElement t : types.values()) {
|
||||
for (FieldElement f : t.fields) {
|
||||
f.referenceType = types.get(f.typeName);
|
||||
XmlType xmlType = xmlTypes.get(f.typeName);
|
||||
if (xmlType != null) {
|
||||
if (xmlType.javaType != null) {
|
||||
f.typeName = xmlType.javaType;
|
||||
}
|
||||
if (xmlType.contentType != null) {
|
||||
f.contentType = xmlType.contentType;
|
||||
}
|
||||
if (xmlType.unsigned) {
|
||||
f.unsigned = true;
|
||||
}
|
||||
}
|
||||
if (f.struct && f.referenceType != null) {
|
||||
f.referenceType.valueType = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
339
src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataLoader.java
Normal file
339
src/jdk.jfr/share/classes/jdk/jfr/internal/MetadataLoader.java
Normal file
@ -0,0 +1,339 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2020, 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;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.jfr.AnnotationElement;
|
||||
import jdk.jfr.Category;
|
||||
import jdk.jfr.Description;
|
||||
import jdk.jfr.Enabled;
|
||||
import jdk.jfr.Experimental;
|
||||
import jdk.jfr.Label;
|
||||
import jdk.jfr.Period;
|
||||
import jdk.jfr.Relational;
|
||||
import jdk.jfr.StackTrace;
|
||||
import jdk.jfr.Threshold;
|
||||
import jdk.jfr.TransitionFrom;
|
||||
import jdk.jfr.TransitionTo;
|
||||
import jdk.jfr.Unsigned;
|
||||
|
||||
public final class MetadataLoader {
|
||||
|
||||
// Caching to reduce allocation pressure and heap usage
|
||||
private final AnnotationElement RELATIONAL = new AnnotationElement(Relational.class);
|
||||
private final AnnotationElement ENABLED = new AnnotationElement(Enabled.class, false);
|
||||
private final AnnotationElement THRESHOLD = new AnnotationElement(Threshold.class, "0 ns");
|
||||
private final AnnotationElement STACK_TRACE = new AnnotationElement(StackTrace.class, true);
|
||||
private final AnnotationElement TRANSITION_TO = new AnnotationElement(TransitionTo.class);
|
||||
private final AnnotationElement TRANSITION_FROM = new AnnotationElement(TransitionFrom.class);
|
||||
private final AnnotationElement EXPERIMENTAL = new AnnotationElement(Experimental.class);
|
||||
private final AnnotationElement UNSIGNED = new AnnotationElement(Unsigned.class);
|
||||
private final List<Object> SMALL_TEMP_LIST = new ArrayList<>();
|
||||
private final Type LABEL_TYPE = TypeLibrary.createAnnotationType(Label.class);
|
||||
private final Type DESCRIPTION_TYPE = TypeLibrary.createAnnotationType(Description.class);
|
||||
private final Type CATEGORY_TYPE = TypeLibrary.createAnnotationType(Category.class);
|
||||
private final Type PERIOD_TYPE = TypeLibrary.createAnnotationType(Period.class);
|
||||
|
||||
// <Event>, <Type> and <Relation>
|
||||
private final static class TypeElement {
|
||||
private final List<FieldElement> fields;
|
||||
private final String name;
|
||||
private final String label;
|
||||
private final String description;
|
||||
private final String category;
|
||||
private final String period;
|
||||
private final boolean thread;
|
||||
private final boolean startTime;
|
||||
private final boolean stackTrace;
|
||||
private final boolean cutoff;
|
||||
private final boolean isEvent;
|
||||
private final boolean isRelation;
|
||||
private final boolean experimental;
|
||||
private final long id;
|
||||
|
||||
public TypeElement(DataInputStream dis) throws IOException {
|
||||
int fieldCount = dis.readInt();
|
||||
fields = new ArrayList<>(fieldCount);
|
||||
for (int i = 0; i < fieldCount; i++) {
|
||||
fields.add(new FieldElement(dis));
|
||||
}
|
||||
name = dis.readUTF();
|
||||
label = dis.readUTF();
|
||||
description = dis.readUTF();
|
||||
category = dis.readUTF();
|
||||
thread = dis.readBoolean();
|
||||
stackTrace = dis.readBoolean();
|
||||
startTime = dis.readBoolean();
|
||||
period = dis.readUTF();
|
||||
cutoff = dis.readBoolean();
|
||||
experimental = dis.readBoolean();
|
||||
id = dis.readLong();
|
||||
isEvent = dis.readBoolean();
|
||||
isRelation = dis.readBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
// <Field>
|
||||
private static class FieldElement {
|
||||
private final String name;
|
||||
private final String label;
|
||||
private final String description;
|
||||
private final String typeName;
|
||||
private final String annotations;
|
||||
private final String transition;
|
||||
private final String relation;
|
||||
private final boolean constantPool;
|
||||
private final boolean array;
|
||||
private final boolean experimental;
|
||||
private final boolean unsigned;
|
||||
|
||||
public FieldElement(DataInputStream dis) throws IOException {
|
||||
name = dis.readUTF();
|
||||
typeName = dis.readUTF();
|
||||
label = dis.readUTF();
|
||||
description = dis.readUTF();
|
||||
constantPool = dis.readBoolean();
|
||||
array = dis.readBoolean();
|
||||
unsigned = dis.readBoolean();
|
||||
annotations = dis.readUTF();
|
||||
transition = dis.readUTF();
|
||||
relation = dis.readUTF();
|
||||
experimental = dis.readBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
private final List<TypeElement> types;
|
||||
private final Map<String, List<AnnotationElement>> anotationElements = new HashMap<>(20);
|
||||
private final Map<String, AnnotationElement> categories = new HashMap<>();
|
||||
|
||||
MetadataLoader(DataInputStream dis) throws IOException {
|
||||
SMALL_TEMP_LIST.add(this); // add any object to expand list
|
||||
int typeCount = dis.readInt();
|
||||
types = new ArrayList<>(typeCount);
|
||||
for (int i = 0; i < typeCount; i++) {
|
||||
types.add(new TypeElement(dis));
|
||||
}
|
||||
}
|
||||
|
||||
private List<AnnotationElement> createAnnotationElements(String annotation) throws InternalError {
|
||||
String[] annotations = annotation.split(",");
|
||||
List<AnnotationElement> annotationElements = new ArrayList<>();
|
||||
for (String a : annotations) {
|
||||
a = a.trim();
|
||||
int leftParenthesis = a.indexOf("(");
|
||||
if (leftParenthesis == -1) {
|
||||
annotationElements.add(new AnnotationElement(createAnnotationClass(a)));
|
||||
} else {
|
||||
int rightParenthesis = a.lastIndexOf(")");
|
||||
if (rightParenthesis == -1) {
|
||||
throw new InternalError("Expected closing parenthesis for 'XMLContentType'");
|
||||
}
|
||||
String value = a.substring(leftParenthesis + 1, rightParenthesis);
|
||||
String type = a.substring(0, leftParenthesis);
|
||||
annotationElements.add(new AnnotationElement(createAnnotationClass(type), value));
|
||||
}
|
||||
}
|
||||
return annotationElements;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Class<? extends Annotation> createAnnotationClass(String type) {
|
||||
try {
|
||||
if (!type.startsWith("jdk.jfr.")) {
|
||||
throw new IllegalStateException("Incorrect type " + type + ". Annotation class must be located in jdk.jfr package.");
|
||||
}
|
||||
Class<?> c = Class.forName(type, true, null);
|
||||
return (Class<? extends Annotation>) c;
|
||||
} catch (ClassNotFoundException cne) {
|
||||
throw new IllegalStateException(cne);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Type> createTypes() throws IOException {
|
||||
try (DataInputStream dis = new DataInputStream(
|
||||
SecuritySupport.getResourceAsStream("/jdk/jfr/internal/types/metadata.bin"))) {
|
||||
MetadataLoader ml = new MetadataLoader(dis);
|
||||
return ml.buildTypes();
|
||||
} catch (Exception e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Type> buildTypes() {
|
||||
Map<String, Type> typeMap = buildTypeMap();
|
||||
Map<String, AnnotationElement> relationMap = buildRelationMap(typeMap);
|
||||
addFields(typeMap, relationMap);
|
||||
return new ArrayList<>(typeMap.values());
|
||||
}
|
||||
|
||||
private Map<String, AnnotationElement> buildRelationMap(Map<String, Type> typeMap) {
|
||||
Map<String, AnnotationElement> relationMap = new HashMap<>(20);
|
||||
for (TypeElement t : types) {
|
||||
if (t.isRelation) {
|
||||
Type relationType = typeMap.get(t.name);
|
||||
AnnotationElement ae = PrivateAccess.getInstance().newAnnotation(relationType, Collections.emptyList(), true);
|
||||
relationMap.put(t.name, ae);
|
||||
}
|
||||
}
|
||||
return relationMap;
|
||||
}
|
||||
|
||||
private void addFields(Map<String, Type> lookup, Map<String, AnnotationElement> relationMap) {
|
||||
for (TypeElement te : types) {
|
||||
Type type = lookup.get(te.name);
|
||||
if (te.isEvent) {
|
||||
boolean periodic = !te.period.isEmpty();
|
||||
TypeLibrary.addImplicitFields(type, periodic, te.startTime && !periodic, te.thread, te.stackTrace && !periodic, te.cutoff);
|
||||
}
|
||||
for (FieldElement f : te.fields) {
|
||||
Type fieldType = Type.getKnownType(f.typeName);
|
||||
if (fieldType == null) {
|
||||
fieldType = Objects.requireNonNull(lookup.get(f.typeName));
|
||||
}
|
||||
List<AnnotationElement> aes = new ArrayList<>();
|
||||
if (f.unsigned) {
|
||||
aes.add(UNSIGNED);
|
||||
}
|
||||
if (!f.annotations.isEmpty()) {
|
||||
var ae = anotationElements.get(f.annotations);
|
||||
if (ae == null) {
|
||||
ae = createAnnotationElements(f.annotations);
|
||||
anotationElements.put(f.annotations, ae);
|
||||
}
|
||||
aes.addAll(ae);
|
||||
}
|
||||
if (!f.relation.isEmpty()) {
|
||||
AnnotationElement t = relationMap.get(f.relation);
|
||||
aes.add(Objects.requireNonNull(t));
|
||||
}
|
||||
if (!f.label.isEmpty()) {
|
||||
aes.add(newAnnotation(LABEL_TYPE, f.label));
|
||||
}
|
||||
if (f.experimental) {
|
||||
aes.add(EXPERIMENTAL);
|
||||
}
|
||||
if (!f.description.isEmpty()) {
|
||||
aes.add(newAnnotation(DESCRIPTION_TYPE, f.description));
|
||||
}
|
||||
if ("from".equals(f.transition)) {
|
||||
aes.add(TRANSITION_FROM);
|
||||
}
|
||||
if ("to".equals(f.transition)) {
|
||||
aes.add(TRANSITION_TO);
|
||||
}
|
||||
type.add(PrivateAccess.getInstance().newValueDescriptor(f.name, fieldType, aes, f.array ? 1 : 0, f.constantPool, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private AnnotationElement newAnnotation(Type type, Object value) {
|
||||
SMALL_TEMP_LIST.set(0, value);
|
||||
return PrivateAccess.getInstance().newAnnotation(type, SMALL_TEMP_LIST, true);
|
||||
}
|
||||
|
||||
private Map<String, Type> buildTypeMap() {
|
||||
Map<String, Type> typeMap = new HashMap<>(2 * types.size());
|
||||
Map<String, Type> knownTypeMap = new HashMap<>(20);
|
||||
for (Type kt : Type.getKnownTypes()) {
|
||||
typeMap.put(kt.getName(), kt);
|
||||
knownTypeMap.put(kt.getName(), kt);
|
||||
}
|
||||
for (TypeElement t : types) {
|
||||
List<AnnotationElement> aes = new ArrayList<>();
|
||||
if (!t.category.isEmpty()) {
|
||||
AnnotationElement cat = categories.get(t.category);
|
||||
if (cat == null) {
|
||||
String[] segments = buildCategorySegments(t.category);
|
||||
cat = newAnnotation(CATEGORY_TYPE, segments);
|
||||
categories.put(t.category, cat);
|
||||
}
|
||||
aes.add(cat);
|
||||
}
|
||||
if (!t.label.isEmpty()) {
|
||||
aes.add(newAnnotation(LABEL_TYPE, t.label));
|
||||
}
|
||||
if (!t.description.isEmpty()) {
|
||||
aes.add(newAnnotation(DESCRIPTION_TYPE, t.description));
|
||||
}
|
||||
if (t.isEvent) {
|
||||
if (!t.period.isEmpty()) {
|
||||
aes.add(newAnnotation(PERIOD_TYPE, t.period));
|
||||
} else {
|
||||
if (t.startTime) {
|
||||
aes.add(THRESHOLD);
|
||||
}
|
||||
if (t.stackTrace) {
|
||||
aes.add(STACK_TRACE);
|
||||
}
|
||||
}
|
||||
if (t.cutoff) {
|
||||
aes.add(new AnnotationElement(Cutoff.class, Cutoff.INFINITY));
|
||||
}
|
||||
}
|
||||
if (t.experimental) {
|
||||
aes.add(EXPERIMENTAL);
|
||||
}
|
||||
Type type;
|
||||
if (t.isEvent) {
|
||||
aes.add(ENABLED);
|
||||
type = new PlatformEventType(t.name, t.id, false, true);
|
||||
} else {
|
||||
type = knownTypeMap.get(t.name);
|
||||
if (type == null) {
|
||||
if (t.isRelation) {
|
||||
type = new Type(t.name, Type.SUPER_TYPE_ANNOTATION, t.id);
|
||||
aes.add(RELATIONAL);
|
||||
} else {
|
||||
type = new Type(t.name, null, t.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
type.setAnnotations(aes);
|
||||
typeMap.put(t.name, type);
|
||||
}
|
||||
return typeMap;
|
||||
}
|
||||
|
||||
private String[] buildCategorySegments(String category) {
|
||||
String[] segments = category.split(",");
|
||||
for (int i = 0; i < segments.length; i++) {
|
||||
segments[i] = segments[i].trim();
|
||||
}
|
||||
return segments;
|
||||
}
|
||||
}
|
@ -26,6 +26,7 @@
|
||||
package jdk.jfr.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.reflect.Field;
|
||||
@ -56,10 +57,12 @@ import jdk.jfr.SettingDescriptor;
|
||||
import jdk.jfr.Timespan;
|
||||
import jdk.jfr.Timestamp;
|
||||
import jdk.jfr.ValueDescriptor;
|
||||
import jdk.jfr.internal.tool.PrettyWriter;
|
||||
|
||||
public final class TypeLibrary {
|
||||
|
||||
private static TypeLibrary instance;
|
||||
private static boolean implicitFieldTypes;
|
||||
private static final Map<Long, Type> types = new LinkedHashMap<>(100);
|
||||
static final ValueDescriptor DURATION_FIELD = createDurationField();
|
||||
static final ValueDescriptor THREAD_FIELD = createThreadField();
|
||||
@ -108,7 +111,7 @@ public final class TypeLibrary {
|
||||
if (instance == null) {
|
||||
List<Type> jvmTypes;
|
||||
try {
|
||||
jvmTypes = MetadataHandler.createTypes();
|
||||
jvmTypes = MetadataLoader.createTypes();
|
||||
Collections.sort(jvmTypes, (a,b) -> Long.compare(a.getId(), b.getId()));
|
||||
} catch (IOException e) {
|
||||
throw new Error("JFR: Could not read metadata");
|
||||
@ -315,10 +318,13 @@ public final class TypeLibrary {
|
||||
|
||||
// By convention all events have these fields.
|
||||
static void addImplicitFields(Type type, boolean requestable, boolean hasDuration, boolean hasThread, boolean hasStackTrace, boolean hasCutoff) {
|
||||
if (!implicitFieldTypes) {
|
||||
createAnnotationType(Timespan.class);
|
||||
createAnnotationType(Timestamp.class);
|
||||
createAnnotationType(Label.class);
|
||||
defineType(long.class, null,false);
|
||||
defineType(long.class, null, false);
|
||||
implicitFieldTypes = true;
|
||||
}
|
||||
addFields(type, requestable, hasDuration, hasThread, hasStackTrace, hasCutoff);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user