| /* |
| * 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 throttle; |
| 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(); |
| throttle = 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.throttle) { |
| aes.add(new AnnotationElement(Throttle.class, Throttle.DEFAULT)); |
| } |
| } |
| 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; |
| } |
| } |