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 ChunkParser(RecordingInput input)55 public ChunkParser(RecordingInput input) throws IOException { 56 this(new ChunkHeader(input)); 57 } 58 ChunkParser(ChunkHeader header)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, metadata.getGMTOffset()); 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 readEvent()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 fillConstantPools(LongMap<Parser> typeParser, LongMap<ConstantMap> constantPools)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 + ", size=" + size + ", deltaToNext=" + delta + ", flush=" + flush + ", poolCount=" + poolCount; 118 }); 119 120 for (int i = 0; i < poolCount; i++) { 121 long id = input.readLong(); // type id 122 ConstantMap pool = constantPools.get(id); 123 Type type = typeMap.get(id); 124 if (pool == null) { 125 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Found constant pool(" + id + ") that is never used"); 126 if (type == null) { 127 throw new IOException("Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + nextCP + ", " + nextCP + size + "]"); 128 } 129 pool = new ConstantMap(ObjectFactory.create(type, timeConverter), type.getName()); 130 constantPools.put(type.getId(), pool); 131 } 132 Parser parser = typeParser.get(id); 133 if (parser == null) { 134 throw new IOException("Could not find constant pool type with id = " + id); 135 } 136 try { 137 int count = input.readInt(); 138 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, () -> "Constant: " + getName(id) + "[" + count + "]"); 139 for (int j = 0; j < count; j++) { 140 long key = input.readLong(); 141 Object value = parser.parse(input); 142 pool.put(key, value); 143 } 144 } catch (Exception e) { 145 throw new IOException("Error parsing constant pool type " + getName(id) + " at position " + input.position() + " at check point between [" + nextCP + ", " + nextCP + size + "]", e); 146 } 147 } 148 if (input.position() != nextCP + size) { 149 throw new IOException("Size of check point event doesn't match content"); 150 } 151 } 152 } 153 getName(long id)154 private String getName(long id) { 155 Type type = typeMap.get(id); 156 return type == null ? ("unknown(" + id + ")") : type.getName(); 157 } 158 getTypes()159 public Collection<Type> getTypes() { 160 return metadata.getTypes(); 161 } 162 getEventTypes()163 public List<EventType> getEventTypes() { 164 return metadata.getEventTypes(); 165 } 166 isLastChunk()167 public boolean isLastChunk() { 168 return chunkHeader.isLastChunk(); 169 } 170 nextChunkParser()171 public ChunkParser nextChunkParser() throws IOException { 172 return new ChunkParser(chunkHeader.nextHeader()); 173 } 174 } 175