| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
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 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 && major != 2) { | 
 | 
            throw new IOException("File version " + major + "." + minor + ". Only Flight Recorder files of version 1.x and 2.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();   | 
 | 
        Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Chunk: startNanos=" + chunkStartNanos);  | 
 | 
        durationNanos = input.readRawLong();   | 
 | 
        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();   | 
 | 
 | 
 | 
          | 
 | 
        this.absoluteChunkStart = absoluteChunkStart;  | 
 | 
        absoluteChunkEnd = absoluteChunkStart + chunkSize;  | 
 | 
        lastChunk = input.size() == absoluteChunkEnd;  | 
 | 
        absoluteEventStart = input.position();  | 
 | 
 | 
 | 
          | 
 | 
        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();   | 
 | 
        long id = input.readLong();   | 
 | 
        if (id != METADATA_TYPE_ID) { | 
 | 
            throw new IOException("Expected metadata event. Type id=" + id + ", should have been " + METADATA_TYPE_ID); | 
 | 
        }  | 
 | 
        input.readLong();   | 
 | 
        input.readLong();   | 
 | 
        long metadataId = input.readLong();  | 
 | 
        Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.TRACE, "Metadata id=" + metadataId);  | 
 | 
        // No need to read if metadataId == lastMetadataId, but we  | 
 | 
          | 
 | 
        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 getDurationNanos() { | 
 | 
        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;  | 
 | 
    }  | 
 | 
 | 
 | 
}  |