/* * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package jdk.jfr.internal.consumer; import java.io.DataInput; import java.io.IOException; import jdk.jfr.internal.LogLevel; import jdk.jfr.internal.LogTag; import jdk.jfr.internal.Logger; import jdk.jfr.internal.MetadataDescriptor; public final class ChunkHeader { private static final long METADATA_TYPE_ID = 0; private static final byte[] FILE_MAGIC = { 'F', 'L', 'R', '\0' }; private final short major; private final short minor; private final long chunkSize; private final long chunkStartTicks; private final long ticksPerSecond; private final long chunkStartNanos; private final long metadataPosition; // private final long absoluteInitialConstantPoolPosition; private final long absoluteChunkEnd; private final long absoluteEventStart; private final long absoluteChunkStart; private final boolean lastChunk; private final RecordingInput input; private final long durationNanos; private final long id; private long constantPoolPosition; public ChunkHeader(RecordingInput input) throws IOException { this(input, 0, 0); } private ChunkHeader(RecordingInput input, long absoluteChunkStart, long id) throws IOException { input.position(absoluteChunkStart); if (input.position() >= input.size()) { throw new IOException("Chunk contains no data"); } verifyMagic(input); this.input = input; this.id = id; Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk " + id); Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startPosition=" + absoluteChunkStart); major = input.readRawShort(); Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: major=" + major); minor = input.readRawShort(); Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: minor=" + minor); if (major != 1) { throw new IOException("File version " + major + "." + minor + ". Only Flight Recorder files of version 1.x can be read by this JDK."); } chunkSize = input.readRawLong(); Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: chunkSize=" + chunkSize); this.constantPoolPosition = input.readRawLong(); Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: constantPoolPosition=" + constantPoolPosition); metadataPosition = input.readRawLong(); Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: metadataPosition=" + metadataPosition); chunkStartNanos = input.readRawLong(); // nanos since epoch Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startNanos=" + chunkStartNanos); durationNanos = input.readRawLong(); // duration nanos, not used Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: durationNanos=" + durationNanos); chunkStartTicks = input.readRawLong(); Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startTicks=" + chunkStartTicks); ticksPerSecond = input.readRawLong(); Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: ticksPerSecond=" + ticksPerSecond); input.readRawInt(); // features, not used // set up boundaries this.absoluteChunkStart = absoluteChunkStart; absoluteChunkEnd = absoluteChunkStart + chunkSize; lastChunk = input.size() == absoluteChunkEnd; absoluteEventStart = input.position(); // read metadata input.position(absoluteEventStart); } public ChunkHeader nextHeader() throws IOException { return new ChunkHeader(input, absoluteChunkEnd, id + 1); } public MetadataDescriptor readMetadata() throws IOException { input.position(absoluteChunkStart + metadataPosition); input.readInt(); // size long id = input.readLong(); // event type id if (id != METADATA_TYPE_ID) { throw new IOException("Expected metadata event. Type id=" + id + ", should have been " + METADATA_TYPE_ID); } input.readLong(); // start time input.readLong(); // duration long metadataId = input.readLong(); Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "Metadata id=" + metadataId); // No need to read if metadataId == lastMetadataId, but we // do it for verification purposes. return MetadataDescriptor.read(input); } public boolean isLastChunk() { return lastChunk; } public short getMajor() { return major; } public short getMinor() { return minor; } public long getAbsoluteChunkStart() { return absoluteChunkStart; } public long getConstantPoolPosition() { return constantPoolPosition; } public long getStartTicks() { return chunkStartTicks; } public double getTicksPerSecond() { return ticksPerSecond; } public long getStartNanos() { return chunkStartNanos; } public long getEnd() { return absoluteChunkEnd; } public long getSize() { return chunkSize; } public long getDuration() { return durationNanos; } public RecordingInput getInput() { return input; } private static void verifyMagic(DataInput input) throws IOException { for (byte c : FILE_MAGIC) { if (input.readByte() != c) { throw new IOException("Not a Flight Recorder file"); } } } public long getEventStart() { return absoluteEventStart; } }