1 /*
   2  * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.jfr.internal;
  27 
  28 import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_CONSTANT_POOL;
  29 import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_DIMENSION;
  30 import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_ID;
  31 import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_NAME;
  32 import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_SIMPLE_TYPE;
  33 import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_SUPER_TYPE;
  34 import static jdk.jfr.internal.MetadataDescriptor.ATTRIBUTE_TYPE_ID;
  35 import static jdk.jfr.internal.MetadataDescriptor.ELEMENT_ANNOTATION;
  36 import static jdk.jfr.internal.MetadataDescriptor.ELEMENT_FIELD;
  37 import static jdk.jfr.internal.MetadataDescriptor.ELEMENT_SETTING;
  38 import static jdk.jfr.internal.MetadataDescriptor.ELEMENT_TYPE;
  39 
  40 import java.io.DataInput;
  41 import java.io.IOException;
  42 import java.util.ArrayList;
  43 import java.util.Collections;
  44 import java.util.HashMap;
  45 import java.util.List;
  46 import java.util.Map;
  47 
  48 import jdk.jfr.AnnotationElement;
  49 import jdk.jfr.SettingDescriptor;
  50 import jdk.jfr.ValueDescriptor;
  51 import jdk.jfr.internal.MetadataDescriptor.Element;
  52 
  53 /**
  54  * Parses metadata.
  55  *
  56  */
  57 final class MetadataReader {
  58 
  59     private final DataInput input;
  60     private final List<String> pool;
  61     private final MetadataDescriptor descriptor;
  62     private final Map<Long, Type> types = new HashMap<>();
  63 
  64     public MetadataReader(DataInput input) throws IOException {
  65         this.input = input;
  66         int size = input.readInt();
  67         this.pool = new ArrayList<>(size);
  68         for (int i = 0; i < size; i++) {
  69             this.pool.add(input.readUTF());
  70         }
  71         descriptor = new MetadataDescriptor();
  72         Element root = createElement();
  73         Element metadata = root.elements("metadata").get(0);
  74         declareTypes(metadata);
  75         defineTypes(metadata);
  76         annotateTypes(metadata);
  77         buildEvenTypes();
  78         Element time = root.elements("region").get(0);
  79         descriptor.gmtOffset = time.attribute(MetadataDescriptor.ATTRIBUTE_GMT_OFFSET, 1);
  80         descriptor.locale = time.attribute(MetadataDescriptor.ATTRIBUTE_LOCALE, "");
  81         descriptor.root = root;
  82         if (LogTag.JFR_SYSTEM_PARSER.shouldLog(LogLevel.TRACE.level)) {
  83              List<Type> ts = new ArrayList<>(types.values());
  84              Collections.sort(ts, (x,y) -> x.getName().compareTo(y.getName()));
  85              for (Type t : ts) {
  86                  t.log("Found", LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE);
  87              }
  88         }
  89     }
  90 
  91     private String readString() throws IOException {
  92         return pool.get(readInt());
  93     }
  94 
  95     private int readInt() throws IOException {
  96         return input.readInt();
  97     }
  98 
  99     private Element createElement() throws IOException {
 100         String name = readString();
 101         Element e = new Element(name);
 102         int attributeCount = readInt();
 103         for (int i = 0; i < attributeCount; i++) {
 104             e.addAttribute(readString(), readString());
 105         }
 106         int childrenCount = readInt();
 107         for (int i = 0; i < childrenCount; i++) {
 108             e.add(createElement());
 109         }
 110         return e;
 111     }
 112 
 113     private void annotateTypes(Element metadata) throws IOException {
 114         for (Element typeElement : metadata.elements(ELEMENT_TYPE)) {
 115             Type type = getType(ATTRIBUTE_ID, typeElement);
 116             ArrayList<AnnotationElement> aes = new ArrayList<>();
 117             for (Element annotationElement : typeElement.elements(ELEMENT_ANNOTATION)) {
 118                 aes.add(makeAnnotation(annotationElement));
 119             }
 120             aes.trimToSize();
 121             type.setAnnotations(aes);
 122 
 123             int index = 0;
 124             if (type instanceof PlatformEventType) {
 125                 List<SettingDescriptor> settings = ((PlatformEventType) type).getAllSettings();
 126                 for (Element settingElement : typeElement.elements(ELEMENT_SETTING)) {
 127                     ArrayList<AnnotationElement> annotations = new ArrayList<>();
 128                     for (Element annotationElement : settingElement.elements(ELEMENT_ANNOTATION)) {
 129                         annotations.add(makeAnnotation(annotationElement));
 130                     }
 131                     annotations.trimToSize();
 132                     PrivateAccess.getInstance().setAnnotations(settings.get(index), annotations);
 133                     index++;
 134                 }
 135             }
 136             index = 0;
 137             List<ValueDescriptor> fields = type.getFields();
 138             for (Element fieldElement : typeElement.elements(ELEMENT_FIELD)) {
 139                 ArrayList<AnnotationElement> annotations = new ArrayList<>();
 140                 for (Element annotationElement : fieldElement.elements(ELEMENT_ANNOTATION)) {
 141                     annotations.add(makeAnnotation(annotationElement));
 142                 }
 143                 annotations.trimToSize();
 144                 PrivateAccess.getInstance().setAnnotations(fields.get(index), annotations);
 145                 index++;
 146             }
 147         }
 148     }
 149 
 150     private AnnotationElement makeAnnotation(Element annotationElement) throws IOException {
 151         Type annotationType = getType(ATTRIBUTE_TYPE_ID, annotationElement);
 152         List<Object> values = new ArrayList<>();
 153         for (ValueDescriptor v : annotationType.getFields()) {
 154             if (v.isArray()) {
 155                 List<Object> list = new ArrayList<>();
 156                 int index = 0;
 157                 while (true) {
 158                     String text = annotationElement.attribute(v.getName() + "-" + index);
 159                     if (text == null) {
 160                         break;
 161                     }
 162                     list.add(objectify(v.getTypeName(), text));
 163                     index++;
 164                 }
 165                 Object object = Utils.makePrimitiveArray(v.getTypeName(), list);
 166                 if (object == null) {
 167                     throw new IOException("Unsupported type " + list + " in array");
 168                 }
 169                 values.add(object);
 170             } else {
 171                 String text = annotationElement.attribute(v.getName());
 172                 values.add(objectify(v.getTypeName(), text));
 173             }
 174         }
 175         return PrivateAccess.getInstance().newAnnotation(annotationType, values, false);
 176     }
 177 
 178     private Object objectify(String typeName, String text) throws IOException {
 179         try {
 180             switch (typeName) {
 181             case "int":
 182                 return Integer.valueOf(text);
 183             case "long":
 184                 return Long.valueOf(text);
 185             case "double":
 186                 return Double.valueOf(text);
 187             case "float":
 188                 return Float.valueOf(text);
 189             case "short":
 190                 return Short.valueOf(text);
 191             case "char":
 192                 if (text.length() != 1) {
 193                     throw new IOException("Unexpected size of char");
 194                 }
 195                 return text.charAt(0);
 196             case "byte":
 197                 return Byte.valueOf(text);
 198             case "boolean":
 199                 return Boolean.valueOf(text);
 200             case "java.lang.String":
 201                 return text;
 202             }
 203         } catch (IllegalArgumentException iae) {
 204             throw new IOException("Could not parse text representation of " + typeName);
 205         }
 206         throw new IOException("Unsupported type for annotation " + typeName);
 207     }
 208 
 209     private Type getType(String attribute, Element element) {
 210         long id = element.longValue(attribute);
 211         Type type = types.get(id);
 212         if (type == null) {
 213             String name = element.attribute("type");
 214             throw new IllegalStateException("Type '" + id + "' is not defined for " + name);
 215         }
 216         return type;
 217     }
 218 
 219     private void buildEvenTypes() {
 220         for (Type type : descriptor.types) {
 221             if (type instanceof PlatformEventType) {
 222                 descriptor.eventTypes.add(PrivateAccess.getInstance().newEventType((PlatformEventType) type));
 223             }
 224         }
 225     }
 226 
 227     private void defineTypes(Element metadata) {
 228         for (Element typeElement : metadata.elements(ELEMENT_TYPE)) {
 229             long id = typeElement.attribute(ATTRIBUTE_ID, -1);
 230             Type t = types.get(id);
 231             for (Element fieldElement : typeElement.elements(ELEMENT_SETTING)) {
 232                 String name = fieldElement.attribute(ATTRIBUTE_NAME);
 233                 String defaultValue = fieldElement.attribute(ATTRIBUTE_NAME);
 234                 Type settingType = getType(ATTRIBUTE_TYPE_ID, fieldElement);
 235                 PlatformEventType eventType = (PlatformEventType) t;
 236                 eventType.add(PrivateAccess.getInstance().newSettingDescriptor(settingType, name, defaultValue, new ArrayList<>(2)));
 237             }
 238             for (Element fieldElement : typeElement.elements(ELEMENT_FIELD)) {
 239                 String name = fieldElement.attribute(ATTRIBUTE_NAME);
 240                 Type fieldType = getType(ATTRIBUTE_TYPE_ID, fieldElement);
 241                 long dimension = fieldElement.attribute(ATTRIBUTE_DIMENSION, 0);
 242                 boolean constantPool = fieldElement.attribute(ATTRIBUTE_CONSTANT_POOL) != null;
 243                 // Add annotation later, because they may refer to undefined
 244                 // types at this stage
 245                 t.add(PrivateAccess.getInstance().newValueDescriptor(name, fieldType, new ArrayList<>(), (int) dimension, constantPool, null));
 246             }
 247             t.trimFields();
 248         }
 249     }
 250 
 251     private void declareTypes(Element metadata) {
 252         for (Element typeElement : metadata.elements(ELEMENT_TYPE)) {
 253             String typeName = typeElement.attribute(ATTRIBUTE_NAME);
 254             String superType = typeElement.attribute(ATTRIBUTE_SUPER_TYPE);
 255             boolean simpleType = typeElement.attribute(ATTRIBUTE_SIMPLE_TYPE) != null;
 256             long id = typeElement.attribute(ATTRIBUTE_ID, -1);
 257             Type t;
 258             if (Type.SUPER_TYPE_EVENT.equals(superType)) {
 259                 t = new PlatformEventType(typeName, id, false, false);
 260             } else {
 261                 t = new Type(typeName, superType, id, false, simpleType);
 262             }
 263             types.put(id, t);
 264             descriptor.types.add(t);
 265         }
 266     }
 267 
 268     public MetadataDescriptor getDescriptor() {
 269         return descriptor;
 270     }
 271 }