From 3a352b82591eb522c24108de95e42a3d1e5ceb3a Mon Sep 17 00:00:00 2001 From: David Schlosnagle Date: Fri, 30 Aug 2024 12:36:33 +0000 Subject: [PATCH] 8339191: JFR: Bulk read support for ChunkInputStream Reviewed-by: egahlin --- .../jdk/jfr/internal/ChunkInputStream.java | 53 ++++++++++++-- .../TestChunkInputStreamBulkRead.java | 69 +++++++++++++++++++ 2 files changed, 115 insertions(+), 7 deletions(-) create mode 100644 test/jdk/jdk/jfr/api/consumer/TestChunkInputStreamBulkRead.java diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/ChunkInputStream.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/ChunkInputStream.java index 1ce7054916c..7cc399019e6 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/ChunkInputStream.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/ChunkInputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2024, 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 @@ -31,6 +31,7 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Objects; final class ChunkInputStream extends InputStream { private final Iterator chunks; @@ -85,10 +86,7 @@ final class ChunkInputStream extends InputStream { if (r != -1) { return r; } - stream.close(); - currentChunk.release(); - stream = null; - currentChunk = null; + closeStream(); } if (!nextStream()) { return -1; @@ -97,14 +95,55 @@ final class ChunkInputStream extends InputStream { } @Override - public void close() throws IOException { + public int read(byte[] buf, int off, int len) throws IOException { + Objects.checkFromIndexSize(off, len, buf.length); + if (len == 0) { + return 0; + } + + int totalRead = 0; + while (len > 0) { + if (stream == null) { + closeChunk(); + if (!nextStream()) { + return totalRead > 0 ? totalRead : -1; + } + } + int read = stream.read(buf, off, len); + if (read > -1) { + totalRead += read; + len -= read; + if (len == 0) { + return totalRead; + } + off += read; + } else { + closeStream(); + } + } + return totalRead; + } + + private void closeStream() throws IOException { if (stream != null) { stream.close(); stream = null; } - while (currentChunk != null) { + closeChunk(); + } + + private void closeChunk() { + if (currentChunk != null) { currentChunk.release(); currentChunk = null; + } + } + + @Override + public void close() throws IOException { + closeStream(); + while (currentChunk != null) { + closeChunk(); if (!nextChunk()) { return; } diff --git a/test/jdk/jdk/jfr/api/consumer/TestChunkInputStreamBulkRead.java b/test/jdk/jdk/jfr/api/consumer/TestChunkInputStreamBulkRead.java new file mode 100644 index 00000000000..15bf1b7707c --- /dev/null +++ b/test/jdk/jdk/jfr/api/consumer/TestChunkInputStreamBulkRead.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2024, Palantir Technologies, Inc. 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. + * + * 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. + */ + +/** + * @test TestChunkInputStreamBulkRead + * @key jfr + * @requires vm.hasJFR + * @library /test/lib + * @run main/othervm jdk.jfr.api.consumer.TestChunkInputStreamBulkRead + */ +package jdk.jfr.api.consumer; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import jdk.jfr.Recording; +import jdk.test.lib.Asserts; + +public class TestChunkInputStreamBulkRead { + + public static void main(String[] args) throws Exception { + try (Recording r = new Recording()) { + r.start(); + try (Recording s = new Recording()) { + s.start(); + s.stop(); + } + r.stop(); + try (InputStream stream = r.getStream(null, null); + ByteArrayOutputStream output = new ByteArrayOutputStream()) { + long read = stream.transferTo(output); + System.out.printf("Read %d bytes from JFR stream%n", read); + Asserts.assertEquals(r.getSize(), read); + + byte[] actual = output.toByteArray(); + Asserts.assertEqualsByteArray(r.getStream(null, null).readAllBytes(), actual); + + Path dumpFile = Paths.get("recording.jfr").toAbsolutePath().normalize(); + r.dump(dumpFile); + System.out.printf("Dumped recording to %s (%d bytes)%n", dumpFile, Files.size(dumpFile)); + Asserts.assertEqualsByteArray(Files.readAllBytes(dumpFile), actual); + } + } + } +}