src/jdk.jfr/share/classes/jdk/jfr/internal/consumer/ChunkHeader.java
changeset 58863 c16ac7a2eba4
parent 52850 f527b24990d7
child 59226 a0f39cc47387
equal deleted inserted replaced
58861:2c3cc4b01880 58863:c16ac7a2eba4
     1 /*
     1 /*
     2  * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
     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.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     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
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     7  * published by the Free Software Foundation.  Oracle designates this
    23  * questions.
    23  * questions.
    24  */
    24  */
    25 
    25 
    26 package jdk.jfr.internal.consumer;
    26 package jdk.jfr.internal.consumer;
    27 
    27 
    28 import java.io.DataInput;
       
    29 import java.io.IOException;
    28 import java.io.IOException;
    30 
    29 
    31 import jdk.jfr.internal.LogLevel;
    30 import jdk.jfr.internal.LogLevel;
    32 import jdk.jfr.internal.LogTag;
    31 import jdk.jfr.internal.LogTag;
    33 import jdk.jfr.internal.Logger;
    32 import jdk.jfr.internal.Logger;
    34 import jdk.jfr.internal.MetadataDescriptor;
    33 import jdk.jfr.internal.MetadataDescriptor;
       
    34 import jdk.jfr.internal.Utils;
    35 
    35 
    36 public final class ChunkHeader {
    36 public final class ChunkHeader {
       
    37     private static final long HEADER_SIZE = 68;
       
    38     private static final byte UPDATING_CHUNK_HEADER = (byte) 255;
       
    39     private static final long CHUNK_SIZE_POSITION = 8;
       
    40     private static final long DURATION_NANOS_POSITION = 40;
       
    41     private static final long FILE_STATE_POSITION = 64;
    37     private static final long METADATA_TYPE_ID = 0;
    42     private static final long METADATA_TYPE_ID = 0;
    38     private static final byte[] FILE_MAGIC = { 'F', 'L', 'R', '\0' };
    43     private static final byte[] FILE_MAGIC = { 'F', 'L', 'R', '\0' };
    39 
    44 
    40     private final short major;
    45     private final short major;
    41     private final short minor;
    46     private final short minor;
    42     private final long chunkSize;
       
    43     private final long chunkStartTicks;
    47     private final long chunkStartTicks;
    44     private final long ticksPerSecond;
    48     private final long ticksPerSecond;
    45     private final long chunkStartNanos;
    49     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;
    50     private final long absoluteChunkStart;
    51     private final boolean lastChunk;
       
    52     private final RecordingInput input;
    51     private final RecordingInput input;
    53     private final long durationNanos;
       
    54     private final long id;
    52     private final long id;
    55     private long constantPoolPosition;
    53     private long absoluteEventStart;
       
    54     private long chunkSize = 0;
       
    55     private long constantPoolPosition = 0;
       
    56     private long metadataPosition = 0;
       
    57     private long durationNanos;
       
    58     private long absoluteChunkEnd;
       
    59     private boolean isFinished;
       
    60     private boolean finished;
    56 
    61 
    57     public ChunkHeader(RecordingInput input) throws IOException {
    62     public ChunkHeader(RecordingInput input) throws IOException {
    58         this(input, 0, 0);
    63         this(input, 0, 0);
    59     }
    64     }
    60 
    65 
    61     private ChunkHeader(RecordingInput input, long absoluteChunkStart, long id) throws IOException {
    66     private ChunkHeader(RecordingInput input, long absoluteChunkStart, long id) throws IOException {
       
    67         this.absoluteChunkStart = absoluteChunkStart;
       
    68         this.absoluteEventStart = absoluteChunkStart + HEADER_SIZE;
       
    69         if (input.getFileSize() < HEADER_SIZE) {
       
    70             throw new IOException("Not a complete Chunk header");
       
    71         }
       
    72         input.setValidSize(absoluteChunkStart + HEADER_SIZE);
    62         input.position(absoluteChunkStart);
    73         input.position(absoluteChunkStart);
    63         if (input.position() >= input.size()) {
    74         if (input.position() >= input.size()) {
    64             throw new IOException("Chunk contains no data");
    75            throw new IOException("Chunk contains no data");
    65         }
    76         }
    66         verifyMagic(input);
    77         verifyMagic(input);
    67         this.input = input;
    78         this.input = input;
    68         this.id = id;
    79         this.id = id;
    69         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk " + id);
    80         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: " + id);
       
    81         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: file=" + input.getFilename());
    70         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startPosition=" + absoluteChunkStart);
    82         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startPosition=" + absoluteChunkStart);
    71         major = input.readRawShort();
    83         major = input.readRawShort();
    72         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: major=" + major);
    84         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: major=" + major);
    73         minor = input.readRawShort();
    85         minor = input.readRawShort();
    74         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: minor=" + minor);
    86         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: minor=" + minor);
    75         if (major != 1 && major != 2) {
    87         if (major != 1 && major != 2) {
    76             throw new IOException("File version " + major + "." + minor + ". Only Flight Recorder files of version 1.x and 2.x can be read by this JDK.");
    88             throw new IOException("File version " + major + "." + minor + ". Only Flight Recorder files of version 1.x and 2.x can be read by this JDK.");
    77         }
    89         }
    78         chunkSize = input.readRawLong();
    90         input.readRawLong(); // chunk size
    79         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: chunkSize=" + chunkSize);
    91         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: chunkSize=" + chunkSize);
    80         this.constantPoolPosition = input.readRawLong();
    92         input.readRawLong(); // constant pool position
    81         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: constantPoolPosition=" + constantPoolPosition);
    93         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: constantPoolPosition=" + constantPoolPosition);
    82         metadataPosition = input.readRawLong();
    94         input.readRawLong(); // metadata position
    83         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: metadataPosition=" + metadataPosition);
    95         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: metadataPosition=" + metadataPosition);
    84         chunkStartNanos = input.readRawLong(); // nanos since epoch
    96         chunkStartNanos = input.readRawLong(); // nanos since epoch
    85         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startNanos=" + chunkStartNanos);
    97         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startNanos=" + chunkStartNanos);
    86         durationNanos = input.readRawLong(); // duration nanos, not used
    98         durationNanos = input.readRawLong(); // duration nanos, not used
    87         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: durationNanos=" + durationNanos);
    99         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: durationNanos=" + durationNanos);
    89         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startTicks=" + chunkStartTicks);
   101         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startTicks=" + chunkStartTicks);
    90         ticksPerSecond = input.readRawLong();
   102         ticksPerSecond = input.readRawLong();
    91         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: ticksPerSecond=" + ticksPerSecond);
   103         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: ticksPerSecond=" + ticksPerSecond);
    92         input.readRawInt(); // features, not used
   104         input.readRawInt(); // features, not used
    93 
   105 
    94         // set up boundaries
   106         refresh();
    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);
   107         input.position(absoluteEventStart);
       
   108     }
       
   109 
       
   110     void refresh() throws IOException {
       
   111         while (true) {
       
   112             byte fileState1;
       
   113             input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION);
       
   114             while ((fileState1 = input.readPhysicalByte()) == UPDATING_CHUNK_HEADER) {
       
   115                 Utils.takeNap(1);
       
   116                 input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION);
       
   117             }
       
   118             input.positionPhysical(absoluteChunkStart + CHUNK_SIZE_POSITION);
       
   119             long chunkSize = input.readPhysicalLong();
       
   120             long constantPoolPosition = input.readPhysicalLong();
       
   121             long metadataPosition = input.readPhysicalLong();
       
   122             input.positionPhysical(absoluteChunkStart + DURATION_NANOS_POSITION);
       
   123             long durationNanos = input.readPhysicalLong();
       
   124             input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION);
       
   125             byte fileState2 =  input.readPhysicalByte();
       
   126             if (fileState1 == fileState2) { // valid header
       
   127                 finished = fileState1 == 0;
       
   128                 if (metadataPosition != 0) {
       
   129                     Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Setting input size to " + (absoluteChunkStart + chunkSize));
       
   130                     if (finished) {
       
   131                         // This assumes that the whole recording
       
   132                         // is finished if the first chunk is.
       
   133                         // This is a limitation we may want to
       
   134                         // remove, but greatly improves performance as
       
   135                         // data can be read across chunk boundaries
       
   136                         // of multi-chunk files and only once.
       
   137                         input.setValidSize(input.getFileSize());
       
   138                     } else {
       
   139                         input.setValidSize(absoluteChunkStart + chunkSize);
       
   140                     }
       
   141                     this.chunkSize = chunkSize;
       
   142                     Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: chunkSize=" + chunkSize);
       
   143                     this.constantPoolPosition = constantPoolPosition;
       
   144                     Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: constantPoolPosition=" + constantPoolPosition);
       
   145                     this.metadataPosition = metadataPosition;
       
   146                     Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: metadataPosition=" + metadataPosition);
       
   147                     this.durationNanos = durationNanos;
       
   148                     Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: durationNanos =" + durationNanos);
       
   149                     isFinished = fileState2 == 0;
       
   150                     Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: generation=" + fileState2);
       
   151                     Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: finished=" + isFinished);
       
   152                     Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: fileSize=" + input.size());
       
   153                     absoluteChunkEnd = absoluteChunkStart + chunkSize;
       
   154                     return;
       
   155                 }
       
   156             }
       
   157         }
       
   158     }
       
   159 
       
   160     public void awaitFinished() throws IOException {
       
   161         if (finished) {
       
   162             return;
       
   163         }
       
   164         long pos = input.position();
       
   165         try {
       
   166             input.positionPhysical(absoluteChunkStart + FILE_STATE_POSITION);
       
   167             while (true) {
       
   168                 byte filestate = input.readPhysicalByte();
       
   169                 if (filestate == 0) {
       
   170                     finished = true;
       
   171                     return;
       
   172                 }
       
   173                 Utils.takeNap(1);
       
   174             }
       
   175         } finally {
       
   176             input.position(pos);
       
   177         }
       
   178     }
       
   179 
       
   180     public boolean isLastChunk() throws IOException {
       
   181         awaitFinished();
       
   182         // streaming files only have one chunk
       
   183         return input.getFileSize() == absoluteChunkEnd;
       
   184    }
       
   185 
       
   186     public boolean isFinished() throws IOException {
       
   187         return isFinished;
   102     }
   188     }
   103 
   189 
   104     public ChunkHeader nextHeader() throws IOException {
   190     public ChunkHeader nextHeader() throws IOException {
   105         return new ChunkHeader(input, absoluteChunkEnd, id + 1);
   191         return new ChunkHeader(input, absoluteChunkEnd, id + 1);
   106     }
   192     }
   107 
       
   108     public MetadataDescriptor readMetadata() throws IOException {
   193     public MetadataDescriptor readMetadata() throws IOException {
       
   194         return readMetadata(null);
       
   195     }
       
   196 
       
   197     public MetadataDescriptor readMetadata(MetadataDescriptor previous) throws IOException {
   109         input.position(absoluteChunkStart + metadataPosition);
   198         input.position(absoluteChunkStart + metadataPosition);
   110         input.readInt(); // size
   199         input.readInt(); // size
   111         long id = input.readLong(); // event type id
   200         long id = input.readLong(); // event type id
   112         if (id != METADATA_TYPE_ID) {
   201         if (id != METADATA_TYPE_ID) {
   113             throw new IOException("Expected metadata event. Type id=" + id + ", should have been " + METADATA_TYPE_ID);
   202             throw new IOException("Expected metadata event. Type id=" + id + ", should have been " + METADATA_TYPE_ID);
   114         }
   203         }
   115         input.readLong(); // start time
   204         input.readLong(); // start time
   116         input.readLong(); // duration
   205         input.readLong(); // duration
   117         long metadataId = input.readLong();
   206         long metadataId = input.readLong();
   118         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "Metadata id=" + metadataId);
   207         if (previous != null && metadataId == previous.metadataId) {
   119         // No need to read if metadataId == lastMetadataId, but we
   208             return previous;
   120         // do it for verification purposes.
   209         }
   121         return MetadataDescriptor.read(input);
   210         Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "New metadata id = " + metadataId);
   122     }
   211         MetadataDescriptor m =  MetadataDescriptor.read(input);
   123 
   212         m.metadataId = metadataId;
   124     public boolean isLastChunk() {
   213         return m;
   125         return lastChunk;
   214     }
   126     }
   215 
   127 
   216 
   128     public short getMajor() {
   217     public short getMajor() {
   129         return major;
   218         return major;
   130     }
   219     }
   131 
   220 
   135 
   224 
   136     public long getAbsoluteChunkStart() {
   225     public long getAbsoluteChunkStart() {
   137         return absoluteChunkStart;
   226         return absoluteChunkStart;
   138     }
   227     }
   139 
   228 
       
   229     public long getAbsoluteEventStart() {
       
   230         return absoluteEventStart;
       
   231     }
   140     public long getConstantPoolPosition() {
   232     public long getConstantPoolPosition() {
   141         return constantPoolPosition;
   233         return constantPoolPosition;
   142     }
   234     }
   143 
   235 
       
   236     public long getMetataPosition() {
       
   237         return metadataPosition;
       
   238     }
   144     public long getStartTicks() {
   239     public long getStartTicks() {
   145         return chunkStartTicks;
   240         return chunkStartTicks;
   146     }
   241     }
       
   242     public long getChunkSize() {
       
   243         return chunkSize;
       
   244     }
   147 
   245 
   148     public double getTicksPerSecond() {
   246     public double getTicksPerSecond() {
   149         return ticksPerSecond;
   247         return ticksPerSecond;
   150     }
   248     }
   151 
   249 
   167 
   265 
   168     public RecordingInput getInput() {
   266     public RecordingInput getInput() {
   169         return input;
   267         return input;
   170     }
   268     }
   171 
   269 
   172     private static void verifyMagic(DataInput input) throws IOException {
   270     private static void verifyMagic(RecordingInput input) throws IOException {
   173         for (byte c : FILE_MAGIC) {
   271         for (byte c : FILE_MAGIC) {
   174             if (input.readByte() != c) {
   272             if (input.readByte() != c) {
   175                 throw new IOException("Not a Flight Recorder file");
   273                 throw new IOException("Not a Flight Recorder file");
   176             }
   274             }
   177         }
   275         }
   179 
   277 
   180     public long getEventStart() {
   278     public long getEventStart() {
   181         return absoluteEventStart;
   279         return absoluteEventStart;
   182     }
   280     }
   183 
   281 
       
   282     static long headerSize() {
       
   283         return HEADER_SIZE;
       
   284     }
   184 }
   285 }