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 java.io.IOException; 29 import java.io.InputStream; 30 import java.util.ArrayList; 31 import java.util.Collections; 32 import java.util.HashMap; 33 import java.util.LinkedHashMap; 34 import java.util.List; 35 import java.util.Map; 36 37 import jdk.internal.org.xml.sax.Attributes; 38 import jdk.internal.org.xml.sax.EntityResolver; 39 import jdk.internal.org.xml.sax.SAXException; 40 import jdk.internal.org.xml.sax.helpers.DefaultHandler; 41 import jdk.internal.util.xml.SAXParser; 42 import jdk.internal.util.xml.impl.SAXParserImpl; 43 import jdk.jfr.AnnotationElement; 44 import jdk.jfr.Category; 45 import jdk.jfr.Description; 46 import jdk.jfr.Enabled; 47 import jdk.jfr.Experimental; 48 import jdk.jfr.Label; 49 import jdk.jfr.MemoryAddress; 50 import jdk.jfr.DataAmount; 51 import jdk.jfr.Percentage; 52 import jdk.jfr.Period; 53 import jdk.jfr.Relational; 54 import jdk.jfr.StackTrace; 55 import jdk.jfr.Threshold; 56 import jdk.jfr.Timespan; 57 import jdk.jfr.Timestamp; 58 import jdk.jfr.TransitionFrom; 59 import jdk.jfr.TransitionTo; 60 import jdk.jfr.Unsigned; 61 import jdk.jfr.ValueDescriptor; 62 63 final class TraceHandler extends DefaultHandler implements EntityResolver { 64 65 private static class ValueDeclaration { 66 private final String typeName; 67 private final String field; 68 private final String label; 69 private final boolean experimental; 70 private final String description; 71 private final String relation; 72 private final int dimension; 73 private final Type type; 74 private final boolean transitionFrom; 75 private final boolean transitionTo; 76 77 public ValueDeclaration(Type type, int dimension, Attributes attributes) { 78 typeName = attributes.getValue(ATTRIBUTE_TYPE); 79 field = attributes.getValue(ATTRIBUTE_FIELD); 80 label = attributes.getValue(ATTRIBUTE_LABEL); 81 experimental = "true".equals(attributes.getValue(ATTRIBUTE_EXPERIMENTAL)); 82 description = attributes.getValue(ATTRIBUTE_DESCRIPTION); 83 String t = attributes.getValue(ATTRIBUTE_RELATION); 84 relation = "NOT_AVAILABLE".equals(t) ? null : t; 85 String transition = attributes.getValue(ATTRIBUTE_TRANSITION); 86 transitionFrom = "FROM".equals(transition); 87 transitionTo = "TO".equals(transition); 88 this.dimension = dimension; 89 this.type = type; 90 } 91 } 92 private static final Map<String, String> knownCategorySegments = new HashMap<>(); 93 static { 94 knownCategorySegments.put("os", "Operating System"); 95 knownCategorySegments.put("java", "Java Application"); 96 knownCategorySegments.put("vm", "Java Virtual Machine"); 97 knownCategorySegments.put("class", "Class Loading"); 98 knownCategorySegments.put("gc", "GC"); 99 knownCategorySegments.put("prof", "Profiling"); 100 knownCategorySegments.put("flight_recorder", "Flight Recorder"); 101 } 102 private static final String ELEMENT_CONTENT_TYPE = "content_type"; 103 private static final String ELEMENT_PRIMARY_TYPE = "primary_type"; 104 private static final String ELEMENT_EVENT = "event"; 105 private static final String ELEMENT_VALUE = "value"; 106 private static final String ELEMENT_STRUCT_VALUE ="structvalue"; 107 private static final String ELEMENT_STRUCT_TYPE = "struct_type"; 108 private static final String ELEMENT_STRUCT_ARRAY = "structarray"; 109 private static final String ELEMENT_STRUCT = "struct"; 110 private static final String ATTRIBUTE_LABEL = "label"; 111 private static final String ATTRIBUTE_EXPERIMENTAL = "experimental"; 112 private static final String ATTRIBUTE_TRANSITION = "transition"; 113 private static final String ATTRIBUTE_ID = "id"; 114 private static final String ATTRIBUTE_IS_CONSTANT = "is_constant"; 115 private static final String IS_REQUESTABLE = "is_requestable"; 116 private static final String ATTRIBUTE_IS_REQUESTABLE = IS_REQUESTABLE; 117 private static final String ATTRIBUTE_IS_INSTANT = "is_instant"; 118 private static final String ATTRIBUTE_CUTOFF = "cutoff"; 119 private static final String ATTRIBUTE_URI = "uri"; 120 private static final String ATTRIBUTE_CONTENTTYPE = "contenttype"; 121 private static final String ATTRIBUTE_FIELD = "field"; 122 private static final String ATTRIBUTE_TYPE = "type"; 123 private static final String ATTRIBUTE_DESCRIPTION = "description"; 124 private static final String ATTRIBUTE_HR_NAME = "hr_name"; 125 private static final String ATTRIBUTE_RELATION = "relation"; 126 private static final String ATTRIBUTE_DATATYPE = "datatype"; 127 private static final String ATTRIBUTE_SYMBOL = "symbol"; 128 private static final String ATTRIBUTE_BUILTIN_TYPE = "builtin_type"; 129 private static final String ATTRIBUTE_JVM_TYPE = "jvm_type"; 130 private static final String ATTRIBUTE_PATH = "path"; 131 private static final String ATTRIBUTE_VALUE_NONE = "NONE"; 132 private static final String ELEMENT_RELATION_DECL = "relation_decl"; 133 private static final String ATTRIBUTE_HAS_STACKTRACE = "has_stacktrace"; 134 private static final String ATTRIBUTE_HAS_THREAD = "has_thread"; 135 136 private final Map<String, Type> types = new LinkedHashMap<>(200); 137 private final Map<String, String> typedef = new HashMap<>(100); 138 private final Map<String, AnnotationElement> annotationTypes = new HashMap<>(); 139 private final Map<String, Type> unsignedTypes = new HashMap<>(); 140 private final List<ValueDeclaration> valueDeclarations = new ArrayList<>(1000); 141 142 private Type type; 143 private long structTypeId = 255; 144 private long jvmTypeId = 33; // content_type with jvm_type attribute, others hard wired 145 146 TraceHandler() { 147 148 // Content types handled using annotation in Java 149 annotationTypes.put("BYTES64", new AnnotationElement(DataAmount.class, DataAmount.BYTES)); 150 annotationTypes.put("BYTES", new AnnotationElement(DataAmount.class, DataAmount.BYTES)); 151 annotationTypes.put("MILLIS", new AnnotationElement(Timespan.class, Timespan.MILLISECONDS)); 152 annotationTypes.put("EPOCHMILLIS", new AnnotationElement(Timestamp.class, Timestamp.MILLISECONDS_SINCE_EPOCH)); 153 annotationTypes.put("NANOS", new AnnotationElement(Timespan.class, Timespan.NANOSECONDS)); 154 annotationTypes.put("TICKSPAN", new AnnotationElement(Timespan.class, Timespan.TICKS)); 155 annotationTypes.put("TICKS", new AnnotationElement(Timestamp.class, Timespan.TICKS)); 156 annotationTypes.put("ADDRESS", new AnnotationElement(MemoryAddress.class)); 157 annotationTypes.put("PERCENTAGE", new AnnotationElement(Percentage.class)); 158 159 // Add known unsigned types, and their counter part in Java 160 unsignedTypes.put("U8", Type.LONG); 161 unsignedTypes.put("U4", Type.INT); 162 unsignedTypes.put("U2", Type.SHORT); 163 unsignedTypes.put("U1", Type.BYTE); 164 165 // Map trace.xml primitive to Java type 166 typedef.put("U8", Type.LONG.getName()); 167 typedef.put("U4", Type.INT.getName()); 168 typedef.put("U2", Type.SHORT.getName()); 169 typedef.put("U1", Type.BYTE.getName()); 170 typedef.put("LONG", Type.LONG.getName()); 171 typedef.put("INT", Type.INT.getName()); 172 typedef.put("SHORT", Type.SHORT.getName()); 173 typedef.put("BYTE", Type.BYTE.getName()); 174 typedef.put("DOUBLE", Type.DOUBLE.getName()); 175 typedef.put("BOOLEAN", Type.BOOLEAN.getName()); 176 typedef.put("FLOAT", Type.FLOAT.getName()); 177 typedef.put("CHAR", Type.CHAR.getName()); 178 typedef.put("STRING", Type.STRING.getName()); 179 typedef.put("THREAD", Type.THREAD.getName()); 180 typedef.put("CLASS", Type.CLASS.getName()); 181 // Add known types 182 for (Type type : Type.getKnownTypes()) { 183 types.put(type.getName(), type); 184 } 185 } 186 187 @Override 188 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { 189 switch (qName) { 190 case ELEMENT_RELATION_DECL: 191 String relationalURI = attributes.getValue(ATTRIBUTE_URI); 192 String id = attributes.getValue(ATTRIBUTE_ID); 193 typedef.put(id, relationalURI); 194 break; 195 case ELEMENT_PRIMARY_TYPE: 196 addTypedef(attributes); 197 break; 198 case ELEMENT_STRUCT_ARRAY: 199 valueDeclarations.add(new ValueDeclaration(type, 1, attributes)); 200 break; 201 case ELEMENT_VALUE: 202 case ELEMENT_STRUCT_VALUE: 203 valueDeclarations.add(new ValueDeclaration(type, 0, attributes)); 204 break; 205 case ELEMENT_EVENT: 206 type = createType(attributes, true, structTypeId++, false); 207 boolean stackTrace = getBoolean(attributes, ATTRIBUTE_HAS_STACKTRACE); 208 boolean thread = getBoolean(attributes, (ATTRIBUTE_HAS_THREAD)); 209 boolean instant = getBoolean(attributes, (ATTRIBUTE_IS_INSTANT)); 210 boolean requestable = getBoolean(attributes, (ATTRIBUTE_IS_REQUESTABLE)); 211 boolean constant = getBoolean(attributes, (ATTRIBUTE_IS_CONSTANT)); 212 boolean duration = !requestable && !instant; 213 boolean experimental = getBoolean(attributes, ATTRIBUTE_EXPERIMENTAL); 214 boolean cutoff = getBoolean(attributes, ATTRIBUTE_CUTOFF); 215 TypeLibrary.addImplicitFields(type, requestable, duration, thread, stackTrace, cutoff); 216 ArrayList<AnnotationElement> aes = new ArrayList<>(); 217 aes.addAll(type.getAnnotationElements()); 218 if (requestable) { 219 String period = constant ? "endChunk" : "everyChunk"; 220 aes.add(new AnnotationElement(Period.class, period)); 221 } else { 222 if (!instant) { 223 aes.add(new AnnotationElement(Threshold.class, "0 ns")); 224 } 225 if (stackTrace) { 226 aes.add(new AnnotationElement(StackTrace.class, true)); 227 } 228 } 229 if (cutoff) { 230 aes.add(new AnnotationElement(Cutoff.class, Cutoff.INIFITY)); 231 } 232 if (experimental) { 233 aes.add(new AnnotationElement(Experimental.class)); 234 } 235 aes.add(new AnnotationElement(Enabled.class, false)); 236 aes.trimToSize(); 237 type.setAnnotations(aes); 238 break; 239 case ELEMENT_CONTENT_TYPE: 240 if (attributes.getValue(ATTRIBUTE_BUILTIN_TYPE) != null) { 241 type = createType(attributes, false, builtInId(attributes), true); 242 } else { 243 type = createType(attributes, false, jvmTypeId++, true); 244 } 245 break; 246 case ELEMENT_STRUCT_TYPE: 247 case ELEMENT_STRUCT: 248 type = createType(attributes, false, structTypeId++, false); 249 break; 250 } 251 } 252 253 private long builtInId(Attributes attributes) { 254 String t = attributes.getValue(ATTRIBUTE_ID); 255 if ("Thread".equals(t)) { 256 return Type.THREAD.getId(); 257 } 258 if ("STRING".equals(t)) { 259 return Type.STRING.getId(); 260 } 261 if ("Class".equals(t)) { 262 return Type.CLASS.getId(); 263 } 264 for (Type type : Type.getKnownTypes()) { 265 if (type.getName().equals(Type.ORACLE_TYPE_PREFIX + t)) { 266 return type.getId(); 267 } 268 } 269 throw new IllegalStateException("Built-in type " + t + " not defined in Java"); 270 } 271 272 private boolean getBoolean(Attributes attributes, String attribute) { 273 return "true".equals(attributes.getValue(attribute)); 274 } 275 276 @Override 277 public void endElement(String uri, String localName, String qName) { 278 switch (qName) { 279 case ELEMENT_CONTENT_TYPE: 280 case ELEMENT_EVENT: 281 case ELEMENT_STRUCT_TYPE: 282 case ELEMENT_STRUCT: 283 types.put(type.getName(), type); 284 type = null; 285 break; 286 } 287 } 288 289 private void addTypedef(Attributes attributes) { 290 String symbol = attributes.getValue(ATTRIBUTE_SYMBOL); 291 String contentType = attributes.getValue(ATTRIBUTE_CONTENTTYPE); 292 String dataType = attributes.getValue(ATTRIBUTE_DATATYPE); 293 if (unsignedTypes.containsKey(dataType)) { 294 unsignedTypes.put(symbol, unsignedTypes.get(dataType)); 295 } 296 if (annotationTypes.containsKey(contentType)) { 297 typedef.put(symbol, dataType); 298 return; 299 } 300 if (typedef.containsKey(symbol)) { 301 return; 302 } 303 typedef.put(symbol, ATTRIBUTE_VALUE_NONE.equals(contentType) ? dataType : contentType); 304 } 305 306 private void defineValues() { 307 for (ValueDeclaration decl : valueDeclarations) { 308 decl.type.add(createValueDescriptor(decl)); 309 } 310 } 311 312 private ValueDescriptor createValueDescriptor(ValueDeclaration declaration) { 313 Type referencedType = null; 314 String typeAlias = declaration.typeName; 315 while (referencedType == null && typeAlias != null) { 316 referencedType = types.get(typeAlias); 317 typeAlias = typedef.get(typeAlias); 318 } 319 if (referencedType == null) { 320 throw new InternalError("Trace files contains incompatible type definition"); 321 } 322 List<AnnotationElement> annos = new ArrayList<>(); 323 if (unsignedTypes.containsKey(declaration.typeName) && !referencedType.isConstantPool()) { 324 annos.add(new AnnotationElement(Unsigned.class)); 325 } 326 AnnotationElement contentType = annotationTypes.get(declaration.typeName); 327 if (contentType != null) { 328 annos.add(contentType); 329 } 330 if (declaration.relation != null) { 331 Type relationType = types.get(typedef.get(declaration.relation)); 332 if (relationType == null) { 333 String typeName = Type.ORACLE_TYPE_PREFIX + declaration.relation; 334 typedef.put(declaration.relation, typeName); 335 relationType = new Type(typeName, Type.SUPER_TYPE_ANNOTATION, structTypeId++); 336 relationType.setAnnotations(Collections.singletonList(new AnnotationElement(Relational.class))); 337 types.put(typeName, relationType); 338 } 339 annos.add(PrivateAccess.getInstance().newAnnotation(relationType, new ArrayList<>(), true)); 340 } 341 342 if (declaration.label != null) { 343 annos.add(new AnnotationElement(Label.class, declaration.label)); 344 } 345 346 if (declaration.experimental) { 347 annos.add(new AnnotationElement(Experimental.class)); 348 } 349 350 if (declaration.description != null) { 351 annos.add(new AnnotationElement(Description.class, declaration.description)); 352 } 353 if (declaration.transitionFrom) { 354 annos.add(new AnnotationElement(TransitionFrom.class)); 355 } 356 if (declaration.transitionTo) { 357 annos.add(new AnnotationElement(TransitionTo.class)); 358 } 359 360 return PrivateAccess.getInstance().newValueDescriptor(declaration.field, referencedType, annos, declaration.dimension, referencedType.isConstantPool(), null); 361 } 362 363 private Type createType(Attributes attributes, boolean eventType, long typeId, boolean contantPool) { 364 String labelAttribute = ATTRIBUTE_LABEL; 365 String id = attributes.getValue(ATTRIBUTE_ID); 366 String path = attributes.getValue(ATTRIBUTE_PATH); 367 String builtInType = attributes.getValue(ATTRIBUTE_BUILTIN_TYPE); 368 String jvmType = attributes.getValue(ATTRIBUTE_JVM_TYPE); 369 370 String typeName = makeTypeName(id, path); 371 Type t; 372 if (eventType) { 373 t = new PlatformEventType(typeName, typeId, false, true); 374 } else { 375 t = new Type(typeName, null, typeId, contantPool); 376 } 377 typedef.put(id, typeName); 378 if (contantPool) { 379 labelAttribute = ATTRIBUTE_HR_NAME; // not "label" for some reason? 380 if (builtInType != null) { 381 typedef.put(builtInType, typeName); 382 } 383 if (jvmType != null) { 384 typedef.put(jvmType, typeName); 385 } 386 } 387 ArrayList<AnnotationElement> aes = new ArrayList<>(); 388 if (path != null) { 389 aes.add(new AnnotationElement(Category.class, makeCategory(path))); 390 } 391 String label = attributes.getValue(labelAttribute); 392 if (label != null) { 393 aes.add(new AnnotationElement(Label.class, label)); 394 } 395 String description = attributes.getValue(ATTRIBUTE_DESCRIPTION); 396 if (description != null) { 397 aes.add(new AnnotationElement(Description.class, description)); 398 } 399 aes.trimToSize(); 400 t.setAnnotations(aes); 401 return t; 402 } 403 404 private String makeTypeName(String id, String path) { 405 if ("Thread".equals(id)) { 406 return Type.THREAD.getName(); 407 } 408 if ("Class".equals(id)) { 409 return Type.CLASS.getName(); 410 } 411 return path == null ? Type.ORACLE_TYPE_PREFIX + id : Type.ORACLE_EVENT_PREFIX + id; 412 } 413 414 private static String[] makeCategory(String path) { 415 List<String> categoryNames = new ArrayList<>(); 416 int index = 0; 417 int segmentEnd; 418 while ((segmentEnd = path.indexOf("/", index)) != -1) { 419 String pathSegment = path.substring(index, segmentEnd); 420 String known = knownCategorySegments.get(pathSegment); 421 if (known != null) { 422 categoryNames.add(known); 423 } else { 424 String categorySegment = humanifySegmentPath(pathSegment); 425 categoryNames.add(categorySegment); 426 knownCategorySegments.put(pathSegment, categorySegment); 427 } 428 index = segmentEnd + 1; 429 } 430 String[] result = new String[categoryNames.size()]; 431 for (int i = 0; i< result.length;i++) { 432 result[i] = categoryNames.get(i); 433 } 434 return result; 435 } 436 437 private static String humanifySegmentPath(String pathSegment) { 438 char[] chars = pathSegment.toCharArray(); 439 char lastCharacter = ' '; // signals new word 440 for (int i = 0; i < chars.length; i++) { 441 char c = pathSegment.charAt(i); 442 if (c == '_') { 443 chars[i] = ' '; 444 } 445 if (lastCharacter == ' ') { 446 chars[i] = Character.toUpperCase(c); 447 } 448 lastCharacter = c; 449 } 450 return new String(chars); 451 } 452 453 public static List<Type> createTypes() throws IOException { 454 String[] xmls = { "trace.xml", "tracerelationdecls.xml", "traceevents.xml", 455 "tracetypes.xml"}; 456 TraceHandler t = new TraceHandler(); 457 try { 458 SAXParser parser = new SAXParserImpl(); 459 for (String xml : xmls) { 460 Logger.log(LogTag.JFR_SYSTEM, LogLevel.DEBUG, () -> "Parsing " + xml); 461 parser.parse(createInputStream(xml), t); 462 } 463 t.defineValues(); 464 return new ArrayList<>(t.types.values()); 465 } catch (SAXException e) { 466 throw new IOException(e); 467 } 468 } 469 private static InputStream createInputStream(String name) throws IOException { 470 return SecuritySupport.getResourceAsStream("/jdk/jfr/internal/types/" + name); 471 } 472 }