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.DataInput; 29 import java.io.IOException; 30 31 import jdk.jfr.internal.LogLevel; 32 import jdk.jfr.internal.LogTag; 33 import jdk.jfr.internal.Logger; 34 import jdk.jfr.internal.MetadataDescriptor; 35 36 public final class ChunkHeader { 37 private static final long METADATA_TYPE_ID = 0; 38 private static final byte[] FILE_MAGIC = { 'F', 'L', 'R', '\0' }; 39 40 private final short major; 41 private final short minor; 42 private final long chunkSize; 43 private final long chunkStartTicks; 44 private final long ticksPerSecond; 45 private final long chunkStartNanos; 46 private final long metadataPosition; 47 // private final long absoluteInitialConstantPoolPosition; 48 private final long absoluteChunkEnd; 49 private final long absoluteEventStart; 50 private final long absoluteChunkStart; 51 private final boolean lastChunk; 52 private final RecordingInput input; 53 private final long durationNanos; 54 private final long id; 55 private long constantPoolPosition; 56 57 public ChunkHeader(RecordingInput input) throws IOException { 58 this(input, 0, 0); 59 } 60 61 private ChunkHeader(RecordingInput input, long absoluteChunkStart, long id) throws IOException { 62 input.position(absoluteChunkStart); 63 if (input.position() >= input.size()) { 64 throw new IOException("Chunk contains no data"); 65 } 66 verifyMagic(input); 67 this.input = input; 68 this.id = id; 69 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk " + id); 70 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startPosition=" + absoluteChunkStart); 71 major = input.readRawShort(); 72 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: major=" + major); 73 minor = input.readRawShort(); 74 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: minor=" + minor); 75 if (major != 1) { 76 throw new IOException("File version " + major + "." + minor + ". Only Flight Recorder files of version 1.x can be read by this JDK."); 77 } 78 chunkSize = input.readRawLong(); 79 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: chunkSize=" + chunkSize); 80 this.constantPoolPosition = input.readRawLong(); 81 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: constantPoolPosition=" + constantPoolPosition); 82 metadataPosition = input.readRawLong(); 83 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: metadataPosition=" + metadataPosition); 84 chunkStartNanos = input.readRawLong(); // nanos since epoch 85 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startNanos=" + chunkStartNanos); 86 durationNanos = input.readRawLong(); // duration nanos, not used 87 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: durationNanos=" + durationNanos); 88 chunkStartTicks = input.readRawLong(); 89 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startTicks=" + chunkStartTicks); 90 ticksPerSecond = input.readRawLong(); 91 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: ticksPerSecond=" + ticksPerSecond); 92 input.readRawInt(); // features, not used 93 94 // set up boundaries 95 this.absoluteChunkStart = absoluteChunkStart; 96 absoluteChunkEnd = absoluteChunkStart + chunkSize; 97 lastChunk = input.size() == absoluteChunkEnd; 98 absoluteEventStart = input.position(); 99 100 // read metadata 101 input.position(absoluteEventStart); 102 } 103 104 public ChunkHeader nextHeader() throws IOException { 105 return new ChunkHeader(input, absoluteChunkEnd, id + 1); 106 } 107 108 public MetadataDescriptor readMetadata() throws IOException { 109 input.position(absoluteChunkStart + metadataPosition); 110 input.readInt(); // size 111 long id = input.readLong(); // event type id 112 if (id != METADATA_TYPE_ID) { 113 throw new IOException("Expected metadata event. Type id=" + id + ", should have been " + METADATA_TYPE_ID); 114 } 115 input.readLong(); // start time 116 input.readLong(); // duration 117 long metadataId = input.readLong(); 118 Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "Metadata id=" + metadataId); 119 // No need to read if metadataId == lastMetadataId, but we 120 // do it for verification purposes. 121 return MetadataDescriptor.read(input); 122 } 123 124 public boolean isLastChunk() { 125 return lastChunk; 126 } 127 128 public short getMajor() { 129 return major; 130 } 131 132 public short getMinor() { 133 return minor; 134 } 135 136 public long getAbsoluteChunkStart() { 137 return absoluteChunkStart; 138 } 139 140 public long getConstantPoolPosition() { 141 return constantPoolPosition; 142 } 143 144 public long getStartTicks() { 145 return chunkStartTicks; 146 } 147 148 public double getTicksPerSecond() { 149 return ticksPerSecond; 150 } 151 152 public long getStartNanos() { 153 return chunkStartNanos; 154 } 155 156 public long getEnd() { 157 return absoluteChunkEnd; 158 } 159 160 public long getSize() { 161 return chunkSize; 162 } 163 164 public long getDuration() { 165 return durationNanos; 166 } 167 168 public RecordingInput getInput() { 169 return input; 170 } 171 172 private static void verifyMagic(DataInput input) throws IOException { 173 for (byte c : FILE_MAGIC) { 174 if (input.readByte() != c) { 175 throw new IOException("Not a Flight Recorder file"); 176 } 177 } 178 } 179 180 public long getEventStart() { 181 return absoluteEventStart; 182 } 183 184 }