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.consumer; 27 28 import java.io.IOException; 29 import java.util.Collection; 30 import java.util.List; 31 32 import jdk.jfr.EventType; 33 import jdk.jfr.internal.LogLevel; 34 import jdk.jfr.internal.LogTag; 35 import jdk.jfr.internal.Logger; 36 import jdk.jfr.internal.MetadataDescriptor; 37 import jdk.jfr.internal.Type; 38 import jdk.jfr.internal.consumer.ChunkHeader; 39 import jdk.jfr.internal.consumer.RecordingInput; 40 41 /** 42 * Parses a chunk. 43 * 44 */ 45 final class ChunkParser { 46 private static final long CONSTANT_POOL_TYPE_ID = 1; 47 private final RecordingInput input; 48 private final LongMap<Parser> parsers; 49 private final ChunkHeader chunkHeader; 50 private final long absoluteChunkEnd; 51 private final MetadataDescriptor metadata; 52 private final LongMap<Type> typeMap; 53 private final TimeConverter timeConverter; 54 55 public ChunkParser(RecordingInput input) throws IOException { 56 this(new ChunkHeader(input)); 57 } 58 59 private ChunkParser(ChunkHeader header) throws IOException { 60 this.input = header.getInput(); 61 this.chunkHeader = header; 62 this.metadata = header.readMetadata(); 63 this.absoluteChunkEnd = header.getEnd(); 64 this.timeConverter = new TimeConverter(chunkHeader); 65 66 ParserFactory factory = new ParserFactory(metadata, timeConverter); 67 LongMap<ConstantMap> constantPools = factory.getConstantPools(); 68 parsers = factory.getParsers(); 69 typeMap = factory.getTypeMap(); 70 71 fillConstantPools(parsers, constantPools); 72 constantPools.forEach(ConstantMap::setIsResolving); 73 constantPools.forEach(ConstantMap::resolve); 74 constantPools.forEach(ConstantMap::setResolved); 75 76 input.position(chunkHeader.getEventStart()); 77 } 78 79 public RecordedEvent readEvent() throws IOException { 80 while (input.position() < absoluteChunkEnd) { 81 long pos = input.position(); 82 int size = input.readInt(); 83 if (size == 0) { 84 throw new IOException("Event can't have zero size"); 85 } 86 long typeId = input.readLong(); 87 if (typeId > CONSTANT_POOL_TYPE_ID) { // also skips metadata (id=0) 88 Parser ep = parsers.get(typeId); 89 if (ep instanceof EventParser) { 90 return (RecordedEvent) ep.parse(input); 91 } 92 } 93 input.position(pos + size); 94 } 95 return null; 96 } 97 98 private void fillConstantPools(LongMap<Parser> typeParser, LongMap<ConstantMap> constantPools) throws IOException { 99 long nextCP = chunkHeader.getAbsoluteChunkStart(); 100 long deltaToNext = chunkHeader.getConstantPoolPosition(); 101 while (deltaToNext != 0) { 102 nextCP += deltaToNext; 103 input.position(nextCP); 104 final long position = nextCP; 105 int size = input.readInt(); // size 106 long typeId = input.readLong(); 107 if (typeId != CONSTANT_POOL_TYPE_ID) { 108 throw new IOException("Expected check point event (id = 1) at position " + nextCP + ", but found type id = " + typeId); 109 } 110 input.readLong(); // timestamp 111 input.readLong(); // duration 112 deltaToNext = input.readLong(); 113 final long delta = deltaToNext; 114 boolean flush = input.readBoolean(); 115 int poolCount = input.readInt(); 116 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, () -> { 117 return "New constant pool: startPosition=" + position + 118 ", size=" + size + ", deltaToNext=" + delta + 119 ", flush=" + flush + ", poolCount=" + poolCount; 120 }); 121 122 for (int i = 0; i < poolCount; i++) { 123 long id = input.readLong(); // type id 124 ConstantMap pool = constantPools.get(id); 125 Type type = typeMap.get(id); 126 if (pool == null) { 127 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Found constant pool(" + id + ") that is never used"); 128 if (type == null) { 129 throw new IOException("Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + nextCP + ", " + nextCP + size + "]"); 130 } 131 pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName()); 132 constantPools.put(type.getId(), pool); 133 } 134 Parser parser = typeParser.get(id); 135 if (parser == null) { 136 throw new IOException("Could not find constant pool type with id = " + id); 137 } 138 try { 139 int count = input.readInt(); 140 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, () -> "Constant: " + getName(id) + "[" + count + "]"); 141 for (int j = 0; j < count; j++) { 142 long key = input.readLong(); 143 Object value = parser.parse(input); 144 pool.put(key, value); 145 } 146 } catch (Exception e) { 147 throw new IOException("Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + nextCP + ", " + nextCP + size + "]", e); 148 } 149 } 150 if (input.position() != nextCP + size) { 151 throw new IOException("Size of check point event doesn't match content"); 152 } 153 } 154 } 155 156 private String getName(long id) { 157 Type type = typeMap.get(id); 158 return type == null ? ("unknown(" + id +")") : type.getName(); 159 } 160 161 public Collection<Type> getTypes() { 162 return metadata.getTypes(); 163 } 164 165 public List<EventType> getEventTypes() { 166 return metadata.getEventTypes(); 167 } 168 169 public boolean isLastChunk() { 170 return chunkHeader.isLastChunk(); 171 } 172 173 public ChunkParser nextChunkParser() throws IOException { 174 return new ChunkParser(chunkHeader.nextHeader()); 175 } 176 }