1 /* 2 * Copyright (c) 2016, 2019, 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.consumer; 27 28 import java.io.IOException; 29 import java.util.ArrayList; 30 import java.util.List; 31 32 import jdk.jfr.EventType; 33 import jdk.jfr.ValueDescriptor; 34 import jdk.jfr.internal.LongMap; 35 import jdk.jfr.internal.MetadataDescriptor; 36 import jdk.jfr.internal.PrivateAccess; 37 import jdk.jfr.internal.Type; 38 import jdk.jfr.internal.consumer.Parser; 39 import jdk.jfr.internal.consumer.RecordingInput; 40 41 /** 42 * Class that create parsers suitable for reading events and constant pools 43 */ 44 final class ParserFactory { 45 private final LongMap<Parser> parsers = new LongMap<>(); 46 private final TimeConverter timeConverter; 47 private final LongMap<Type> types = new LongMap<>(); 48 private final LongMap<ConstantLookup> constantLookups; 49 ParserFactory(MetadataDescriptor metadata, LongMap<ConstantLookup> constantLookups, TimeConverter timeConverter)50 public ParserFactory(MetadataDescriptor metadata, LongMap<ConstantLookup> constantLookups, TimeConverter timeConverter) throws IOException { 51 this.constantLookups = constantLookups; 52 this.timeConverter = timeConverter; 53 for (Type t : metadata.getTypes()) { 54 types.put(t.getId(), t); 55 } 56 // Add to separate list 57 // so createCompositeParser can throw 58 // IOException outside lambda 59 List<Type> typeList = new ArrayList<>(); 60 types.forEach(typeList::add); 61 for (Type t : typeList) { 62 if (!t.getFields().isEmpty()) { // Avoid primitives 63 CompositeParser cp = createCompositeParser(t, false); 64 if (t.isSimpleType()) { // Reduce to nested parser 65 parsers.put(t.getId(), cp.parsers[0]); 66 } 67 } 68 } 69 // Override event types with event parsers 70 for (EventType t : metadata.getEventTypes()) { 71 parsers.put(t.getId(), createEventParser(t)); 72 } 73 } 74 getParsers()75 public LongMap<Parser> getParsers() { 76 return parsers; 77 } 78 getTypeMap()79 public LongMap<Type> getTypeMap() { 80 return types; 81 } 82 createEventParser(EventType eventType)83 private EventParser createEventParser(EventType eventType) throws IOException { 84 List<Parser> parsers = new ArrayList<Parser>(); 85 for (ValueDescriptor f : eventType.getFields()) { 86 parsers.add(createParser(f, true)); 87 } 88 return new EventParser(timeConverter, eventType, parsers.toArray(new Parser[0])); 89 } 90 createParser(ValueDescriptor v, boolean event)91 private Parser createParser(ValueDescriptor v, boolean event) throws IOException { 92 boolean constantPool = PrivateAccess.getInstance().isConstantPool(v); 93 if (v.isArray()) { 94 Type valueType = PrivateAccess.getInstance().getType(v); 95 ValueDescriptor element = PrivateAccess.getInstance().newValueDescriptor(v.getName(), valueType, v.getAnnotationElements(), 0, constantPool, null); 96 return new ArrayParser(createParser(element, event)); 97 } 98 long id = v.getTypeId(); 99 Type type = types.get(id); 100 if (type == null) { 101 throw new IOException("Type '" + v.getTypeName() + "' is not defined"); 102 } 103 if (constantPool) { 104 ConstantLookup lookup = constantLookups.get(id); 105 if (lookup == null) { 106 ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName()); 107 lookup = new ConstantLookup(pool, type); 108 constantLookups.put(id, lookup); 109 } 110 if (event) { 111 return new EventValueConstantParser(lookup); 112 } 113 return new ConstantValueParser(lookup); 114 } 115 Parser parser = parsers.get(id); 116 if (parser == null) { 117 if (!v.getFields().isEmpty()) { 118 return createCompositeParser(type, event); 119 } else { 120 return registerParserType(type, createPrimitiveParser(type, constantPool)); 121 } 122 } 123 return parser; 124 } 125 createPrimitiveParser(Type type, boolean event)126 private Parser createPrimitiveParser(Type type, boolean event) throws IOException { 127 switch (type.getName()) { 128 case "int": 129 return new IntegerParser(); 130 case "long": 131 return new LongParser(); 132 case "float": 133 return new FloatParser(); 134 case "double": 135 return new DoubleParser(); 136 case "char": 137 return new CharacterParser(); 138 case "boolean": 139 return new BooleanParser(); 140 case "short": 141 return new ShortParser(); 142 case "byte": 143 return new ByteParser(); 144 case "java.lang.String": 145 ConstantMap pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName()); 146 ConstantLookup lookup = new ConstantLookup(pool, type); 147 constantLookups.put(type.getId(), lookup); 148 return new StringParser(lookup, event); 149 default: 150 throw new IOException("Unknown primitive type " + type.getName()); 151 } 152 } 153 registerParserType(Type t, Parser parser)154 private Parser registerParserType(Type t, Parser parser) { 155 Parser p = parsers.get(t.getId()); 156 // check if parser exists (known type) 157 if (p != null) { 158 return p; 159 } 160 parsers.put(t.getId(), parser); 161 return parser; 162 } 163 createCompositeParser(Type type, boolean event)164 private CompositeParser createCompositeParser(Type type, boolean event) throws IOException { 165 List<ValueDescriptor> vds = type.getFields(); 166 Parser[] parsers = new Parser[vds.size()]; 167 CompositeParser composite = new CompositeParser(parsers); 168 // need to pre-register so recursive types can be handled 169 registerParserType(type, composite); 170 171 int index = 0; 172 for (ValueDescriptor vd : vds) { 173 parsers[index++] = createParser(vd, event); 174 } 175 return composite; 176 } 177 178 private static final class BooleanParser extends Parser { 179 @Override parse(RecordingInput input)180 public Object parse(RecordingInput input) throws IOException { 181 return input.readBoolean() ? Boolean.TRUE : Boolean.FALSE; 182 } 183 184 @Override skip(RecordingInput input)185 public void skip(RecordingInput input) throws IOException { 186 input.skipBytes(1); 187 } 188 } 189 190 private static final class ByteParser extends Parser { 191 @Override parse(RecordingInput input)192 public Object parse(RecordingInput input) throws IOException { 193 return Byte.valueOf(input.readByte()); 194 } 195 196 @Override skip(RecordingInput input)197 public void skip(RecordingInput input) throws IOException { 198 input.skipBytes(1); 199 } 200 } 201 202 private static final class LongParser extends Parser { 203 private Object lastLongObject = Long.valueOf(0); 204 private long last = 0; 205 206 @Override parse(RecordingInput input)207 public Object parse(RecordingInput input) throws IOException { 208 long l = input.readLong(); 209 if (l == last) { 210 return lastLongObject; 211 } 212 last = l; 213 lastLongObject = Long.valueOf(l); 214 return lastLongObject; 215 } 216 217 @Override skip(RecordingInput input)218 public void skip(RecordingInput input) throws IOException { 219 input.readLong(); 220 } 221 } 222 223 private static final class IntegerParser extends Parser { 224 private Integer lastIntegergObject = Integer.valueOf(0); 225 private int last = 0; 226 227 @Override parse(RecordingInput input)228 public Object parse(RecordingInput input) throws IOException { 229 int i = input.readInt(); 230 if (i != last) { 231 last = i; 232 lastIntegergObject = Integer.valueOf(i); 233 } 234 return lastIntegergObject; 235 } 236 237 @Override skip(RecordingInput input)238 public void skip(RecordingInput input) throws IOException { 239 input.readInt(); 240 } 241 } 242 243 private static final class ShortParser extends Parser { 244 @Override parse(RecordingInput input)245 public Object parse(RecordingInput input) throws IOException { 246 return Short.valueOf(input.readShort()); 247 } 248 249 @Override skip(RecordingInput input)250 public void skip(RecordingInput input) throws IOException { 251 input.readShort(); 252 } 253 } 254 255 private static final class CharacterParser extends Parser { 256 @Override parse(RecordingInput input)257 public Object parse(RecordingInput input) throws IOException { 258 return Character.valueOf(input.readChar()); 259 } 260 261 @Override skip(RecordingInput input)262 public void skip(RecordingInput input) throws IOException { 263 input.readChar(); 264 } 265 } 266 267 private static final class FloatParser extends Parser { 268 @Override parse(RecordingInput input)269 public Object parse(RecordingInput input) throws IOException { 270 return Float.valueOf(input.readFloat()); 271 } 272 273 @Override skip(RecordingInput input)274 public void skip(RecordingInput input) throws IOException { 275 input.skipBytes(Float.SIZE); 276 } 277 } 278 279 private static final class DoubleParser extends Parser { 280 @Override parse(RecordingInput input)281 public Object parse(RecordingInput input) throws IOException { 282 return Double.valueOf(input.readDouble()); 283 } 284 285 @Override skip(RecordingInput input)286 public void skip(RecordingInput input) throws IOException { 287 input.skipBytes(Double.SIZE); 288 } 289 } 290 291 private final static class ArrayParser extends Parser { 292 private final Parser elementParser; 293 ArrayParser(Parser elementParser)294 public ArrayParser(Parser elementParser) { 295 this.elementParser = elementParser; 296 } 297 298 @Override parse(RecordingInput input)299 public Object parse(RecordingInput input) throws IOException { 300 final int size = input.readInt(); 301 final Object[] array = new Object[size]; 302 for (int i = 0; i < size; i++) { 303 array[i] = elementParser.parse(input); 304 } 305 return array; 306 } 307 308 @Override skip(RecordingInput input)309 public void skip(RecordingInput input) throws IOException { 310 final int size = input.readInt(); 311 for (int i = 0; i < size; i++) { 312 elementParser.skip(input); 313 } 314 } 315 } 316 317 private final static class CompositeParser extends Parser { 318 private final Parser[] parsers; 319 CompositeParser(Parser[] valueParsers)320 public CompositeParser(Parser[] valueParsers) { 321 this.parsers = valueParsers; 322 } 323 324 @Override parse(RecordingInput input)325 public Object parse(RecordingInput input) throws IOException { 326 final Object[] values = new Object[parsers.length]; 327 for (int i = 0; i < values.length; i++) { 328 values[i] = parsers[i].parse(input); 329 } 330 return values; 331 } 332 333 @Override skip(RecordingInput input)334 public void skip(RecordingInput input) throws IOException { 335 for (int i = 0; i < parsers.length; i++) { 336 parsers[i].skip(input); 337 } 338 } 339 } 340 341 private static final class EventValueConstantParser extends Parser { 342 private final ConstantLookup lookup; 343 private Object lastValue = 0; 344 private long lastKey = -1; EventValueConstantParser(ConstantLookup lookup)345 EventValueConstantParser(ConstantLookup lookup) { 346 this.lookup = lookup; 347 } 348 349 @Override parse(RecordingInput input)350 public Object parse(RecordingInput input) throws IOException { 351 long key = input.readLong(); 352 if (key == lastKey) { 353 return lastValue; 354 } 355 lastKey = key; 356 lastValue = lookup.getCurrentResolved(key); 357 return lastValue; 358 } 359 360 @Override skip(RecordingInput input)361 public void skip(RecordingInput input) throws IOException { 362 input.readLong(); 363 } 364 } 365 366 private static final class ConstantValueParser extends Parser { 367 private final ConstantLookup lookup; ConstantValueParser(ConstantLookup lookup)368 ConstantValueParser(ConstantLookup lookup) { 369 this.lookup = lookup; 370 } 371 372 @Override parse(RecordingInput input)373 public Object parse(RecordingInput input) throws IOException { 374 return lookup.getCurrent(input.readLong()); 375 } 376 377 @Override skip(RecordingInput input)378 public void skip(RecordingInput input) throws IOException { 379 input.readLong(); 380 } 381 } 382 } 383