--- old/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/util/ChunkReader.java 2018-05-21 20:34:41.000000000 +0200 +++ new/core/org.openjdk.jmc.flightrecorder/src/main/java/org/openjdk/jmc/flightrecorder/util/ChunkReader.java 2018-05-21 20:34:40.000000000 +0200 @@ -71,9 +71,10 @@ * into memory as byte arrays by memory mapping the JFR file, finding the chunk boundaries with * a minimum of parsing, and then block-transferring the byte arrays. The transfers will be done * on {@link Iterator#next()}, and the resulting byte array will only be reachable for as long - * as it is referenced. The JFR file must not be zip or gzip compressed. Note that - * {@link Iterator#next()} can throw {@link IllegalArgumentException} if it encounters a - * corrupted chunk. + * as it is referenced. The JFR file must not be zip or gzip compressed. + *

+ * Note that {@link Iterator#next()} can throw {@link IllegalArgumentException} if it encounters + * a corrupted chunk. */ private static class ChunkIterator implements Iterator { int lastChunkOffset; @@ -131,15 +132,26 @@ throw new IllegalArgumentException("Corrupted chunk encountered! Aborting!"); //$NON-NLS-1$ } - // Skipping version, can add version validation here later. Note that we may - // need to update this to support JDK 7u all through 11. - int index = lastChunkOffset + INTEGER_SIZE + 2 * SHORT_SIZE; - // Setting index to metadata offset - index = lastChunkOffset + (int) buffer.getLong(index); - // Reading event size - int lastEventSize = buffer.getInt(index); - index += lastEventSize; - int size = index - lastChunkOffset; + int index = lastChunkOffset + INTEGER_SIZE; + short versionMSB = buffer.getShort(index); + // short versionLSB = buffer.getShort(index + SHORT_SIZE); + index += 2 * SHORT_SIZE; + int size = 0; + + if (versionMSB >= 1) { + // We have a JDK 9+ recording - chunk size can be directly read from header + size = (int) buffer.getLong(index); + index = lastChunkOffset + size; + } else { + // Got a pre JDK 9 recording. Need to find the metadata event index, read and + // add the size of the metadata event to find the chunk boundary + index = lastChunkOffset + (int) buffer.getLong(index); + // Reading the metadata event size + int lastEventSize = buffer.getInt(index); + index += lastEventSize; + size = index - lastChunkOffset; + } + // Read the chunk and return it byte[] result = new byte[size]; buffer.position(lastChunkOffset); buffer.get(result, 0, result.length); @@ -244,19 +256,31 @@ System.arraycopy(JFR_MAGIC_BYTES, 0, chunkHeader, 0, JFR_MAGIC_BYTES.length); // Read rest of chunk header readBytesFromStream(chunkHeader, JFR_MAGIC_BYTES.length, HEADER_SIZE - JFR_MAGIC_BYTES.length); - long metadataIndex = DataInputToolkit.readLong(chunkHeader, HEADER_SIZE - LONG_SIZE); - int eventReadSize = (int) (metadataIndex - HEADER_SIZE + INTEGER_SIZE); - byte[] chunkEvents = new byte[eventReadSize]; - readBytesFromStream(chunkEvents, 0, chunkEvents.length); - int metadataEventSize = DataInputToolkit.readInt(chunkEvents, eventReadSize - INTEGER_SIZE) - INTEGER_SIZE; - byte[] chunkMetadata = new byte[metadataEventSize]; - readBytesFromStream(chunkMetadata, 0, chunkMetadata.length); - - byte[] chunkTotal = new byte[chunkHeader.length + chunkEvents.length + chunkMetadata.length]; - System.arraycopy(chunkHeader, 0, chunkTotal, 0, chunkHeader.length); - System.arraycopy(chunkEvents, 0, chunkTotal, chunkHeader.length, chunkEvents.length); - System.arraycopy(chunkMetadata, 0, chunkTotal, chunkHeader.length + chunkEvents.length, - chunkMetadata.length); + short majorVersion = DataInputToolkit.readShort(chunkHeader, JFR_MAGIC_BYTES.length); + byte[] chunkTotal = null; + if (majorVersion >= 1) { + // JDK 9+ recording + long fullSize = DataInputToolkit.readLong(chunkHeader, HEADER_SIZE - LONG_SIZE); + int readSize = (int) fullSize - HEADER_SIZE; + chunkTotal = new byte[(int) fullSize]; + System.arraycopy(chunkHeader, 0, chunkTotal, 0, chunkHeader.length); + readBytesFromStream(chunkTotal, HEADER_SIZE, readSize); + } else { + long metadataIndex = DataInputToolkit.readLong(chunkHeader, HEADER_SIZE - LONG_SIZE); + int eventReadSize = (int) (metadataIndex - HEADER_SIZE + INTEGER_SIZE); + byte[] chunkEvents = new byte[eventReadSize]; + readBytesFromStream(chunkEvents, 0, chunkEvents.length); + int metadataEventSize = DataInputToolkit.readInt(chunkEvents, eventReadSize - INTEGER_SIZE) + - INTEGER_SIZE; + byte[] chunkMetadata = new byte[metadataEventSize]; + readBytesFromStream(chunkMetadata, 0, chunkMetadata.length); + + chunkTotal = new byte[chunkHeader.length + chunkEvents.length + chunkMetadata.length]; + System.arraycopy(chunkHeader, 0, chunkTotal, 0, chunkHeader.length); + System.arraycopy(chunkEvents, 0, chunkTotal, chunkHeader.length, chunkEvents.length); + System.arraycopy(chunkMetadata, 0, chunkTotal, chunkHeader.length + chunkEvents.length, + chunkMetadata.length); + } streamState = StreamState.NEXT_CHUNK; return chunkTotal; } @@ -291,6 +315,7 @@ * chunk */ public static Iterator readChunks(File jfrFile) throws IOException { + // We fall back to using a StreamChunkIterator if the file is compressed. if (IOToolkit.isCompressedFile(jfrFile)) { return new StreamChunkIterator(IOToolkit.openUncompressedStream(jfrFile)); } @@ -324,7 +349,17 @@ public static void main(String[] args) throws IOException { long nanoStart = System.nanoTime(); int chunkCount = 0, byteCount = 0; - Iterator iter = readChunks(new File(args[0])); + + if (args.length != 1) { + System.out.println("Usage: ChunkReader "); + System.exit(2); + } + File file = new File(args[0]); + if (!file.exists()) { + System.out.println("The file " + file.getAbsolutePath() + " does not exist. Exiting..."); + System.exit(3); + } + Iterator iter = readChunks(file); while (iter.hasNext()) { byte[] bytes = iter.next(); chunkCount += 1;