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 }