diff --git a/src/jdk.jfr/share/classes/jdk/jfr/events/EventConfigurations.java b/src/jdk.jfr/share/classes/jdk/jfr/events/EventConfigurations.java index 383feaee73ef8..ace6c1d84b4b4 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/events/EventConfigurations.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/events/EventConfigurations.java @@ -31,6 +31,8 @@ public final class EventConfigurations { public static final EventConfiguration SOCKET_READ = JVMSupport.getConfiguration(SocketReadEvent.class); public static final EventConfiguration SOCKET_WRITE = JVMSupport.getConfiguration(SocketWriteEvent.class); public static final EventConfiguration FILE_READ = JVMSupport.getConfiguration(FileReadEvent.class); + public static final EventConfiguration FILE_WRITE_IO_STATISTICS = JVMSupport.getConfiguration(FileWriteIOStatisticsEvent.class); + public static final EventConfiguration FILE_READ_IO_STATISTICS = JVMSupport.getConfiguration(FileReadIOStatisticsEvent.class); public static final EventConfiguration FILE_WRITE = JVMSupport.getConfiguration(FileWriteEvent.class); public static final EventConfiguration FILE_FORCE = JVMSupport.getConfiguration(FileForceEvent.class); public static final EventConfiguration ERROR_THROWN = JVMSupport.getConfiguration(ErrorThrownEvent.class); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/events/FileReadIOStatisticsEvent.java b/src/jdk.jfr/share/classes/jdk/jfr/events/FileReadIOStatisticsEvent.java new file mode 100644 index 0000000000000..577869b640a7e --- /dev/null +++ b/src/jdk.jfr/share/classes/jdk/jfr/events/FileReadIOStatisticsEvent.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2023, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package jdk.jfr.events; + +import jdk.jfr.Category; +import jdk.jfr.Description; +import jdk.jfr.Label; +import jdk.jfr.Name; +import jdk.jfr.StackTrace; +import jdk.jfr.internal.Type; +import java.util.concurrent.atomic.AtomicLong; + +@Name(Type.EVENT_NAME_PREFIX + "FileReadIOStatistics") +@Label("FileReadIO Statistics") +@Category({ "Java Application", "Statistics" }) +@Description("Read Rate from the FileInputStream, FileChannelImpl, RandomAccessFile") +@StackTrace(false) +public final class FileReadIOStatisticsEvent extends AbstractJDKEvent { + + @Label("Read Rate (Bytes per Sec)") + public long readRate; + + @Label("Total Accumulated Read Bytes") + public long accRead; + +} \ No newline at end of file diff --git a/src/jdk.jfr/share/classes/jdk/jfr/events/FileWriteIOStatisticsEvent.java b/src/jdk.jfr/share/classes/jdk/jfr/events/FileWriteIOStatisticsEvent.java new file mode 100644 index 0000000000000..268009df0e5b5 --- /dev/null +++ b/src/jdk.jfr/share/classes/jdk/jfr/events/FileWriteIOStatisticsEvent.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2023, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package jdk.jfr.events; + +import jdk.jfr.Category; +import jdk.jfr.Description; +import jdk.jfr.Label; +import jdk.jfr.Name; +import jdk.jfr.StackTrace; +import jdk.jfr.internal.Type; +import java.util.concurrent.atomic.AtomicLong; + +@Name(Type.EVENT_NAME_PREFIX + "FileWriteIOStatistics") +@Label("FileWriteIO Statistics") +@Category({ "Java Application", "Statistics" }) +@Description("Write Rate from the FileOutputStream, FileChannelImpl, RandomAccessFile") +@StackTrace(false) +public final class FileWriteIOStatisticsEvent extends AbstractJDKEvent { + + @Label("Write Rate (Bytes per Sec)") + public long writeRate; + + @Label("Total Accumulated Write Bytes") + public long accWrite; + +} \ No newline at end of file diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/FileChannelImplInstrumentor.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/FileChannelImplInstrumentor.java index d0d8d9b4509e1..155f06f54d88f 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/FileChannelImplInstrumentor.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/FileChannelImplInstrumentor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2023, 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 @@ -66,18 +66,20 @@ public void force(boolean metaData) throws IOException { @JIInstrumentationMethod public int read(ByteBuffer dst) throws IOException { - EventConfiguration eventConfiguration = EventConfigurations.FILE_READ; - if (!eventConfiguration.isEnabled()) { + EventConfiguration fileReadEventConfiguration = EventConfigurations.FILE_READ; + EventConfiguration fileReadIOStatisticsEventConfiguration = EventConfigurations.FILE_READ_IO_STATISTICS; + if (!fileReadEventConfiguration.isEnabled() && !fileReadIOStatisticsEventConfiguration.isEnabled()) { return read(dst); } int bytesRead = 0; long start = 0; + long duration = 0; try { start = EventConfiguration.timestamp(); bytesRead = read(dst); } finally { - long duration = EventConfiguration.timestamp() - start; - if (eventConfiguration.shouldCommit(duration)) { + duration = EventConfiguration.timestamp() - start; + if (fileReadEventConfiguration.shouldCommit(duration)) { if (bytesRead < 0) { FileReadEvent.commit(start, duration, path, 0L, true); } else { @@ -85,47 +87,57 @@ public int read(ByteBuffer dst) throws IOException { } } } + if(fileReadIOStatisticsEventConfiguration.isEnabled()){ + FileIOStatistics.addTotalReadBytesForPeriod(((bytesRead < 0) ? 0 : bytesRead), duration); + } return bytesRead; } @JIInstrumentationMethod public int read(ByteBuffer dst, long position) throws IOException { - EventConfiguration eventConfiguration = EventConfigurations.FILE_READ; - if (!eventConfiguration.isEnabled()) { + EventConfiguration fileReadEventConfiguration = EventConfigurations.FILE_READ; + EventConfiguration fileReadIOStatisticsEventConfiguration = EventConfigurations.FILE_READ_IO_STATISTICS; + if (!fileReadEventConfiguration.isEnabled() && !fileReadIOStatisticsEventConfiguration.isEnabled()) { return read(dst, position); } int bytesRead = 0; long start = 0; + long duration = 0; try { start = EventConfiguration.timestamp(); bytesRead = read(dst, position); } finally { - long duration = EventConfiguration.timestamp() - start; - if (eventConfiguration.shouldCommit(duration)) { + duration = EventConfiguration.timestamp() - start; + if (fileReadEventConfiguration.shouldCommit(duration)) { if (bytesRead < 0) { FileReadEvent.commit(start, duration, path, 0L, true); } else { FileReadEvent.commit(start, duration, path, bytesRead, false); } } + } + if(fileReadIOStatisticsEventConfiguration.isEnabled()){ + FileIOStatistics.addTotalReadBytesForPeriod(((bytesRead < 0) ? 0 : bytesRead), duration); } return bytesRead; } @JIInstrumentationMethod public long read(ByteBuffer[] dsts, int offset, int length) throws IOException { - EventConfiguration eventConfiguration = EventConfigurations.FILE_READ; - if (!eventConfiguration.isEnabled()) { + EventConfiguration fileReadEventConfiguration = EventConfigurations.FILE_READ; + EventConfiguration fileReadIOStatisticsEventConfiguration = EventConfigurations.FILE_READ_IO_STATISTICS; + if (!fileReadEventConfiguration.isEnabled() && !fileReadIOStatisticsEventConfiguration.isEnabled()) { return read(dsts, offset, length); } long bytesRead = 0; long start = 0; + long duration = 0; try { start = EventConfiguration.timestamp(); bytesRead = read(dsts, offset, length); } finally { - long duration = EventConfiguration.timestamp() - start; - if (eventConfiguration.shouldCommit(duration)) { + duration = EventConfiguration.timestamp() - start; + if (fileReadEventConfiguration.shouldCommit(duration)) { if (bytesRead < 0) { FileReadEvent.commit(start, duration, path, 0L, true); } else { @@ -133,70 +145,88 @@ public long read(ByteBuffer[] dsts, int offset, int length) throws IOException { } } } + if(fileReadIOStatisticsEventConfiguration.isEnabled()){ + FileIOStatistics.addTotalReadBytesForPeriod(((bytesRead < 0) ? 0 : bytesRead), duration); + } return bytesRead; } @JIInstrumentationMethod public int write(ByteBuffer src) throws IOException { - EventConfiguration eventConfiguration = EventConfigurations.FILE_WRITE; - if (!eventConfiguration.isEnabled()) { + EventConfiguration fileWriteEventConfiguration = EventConfigurations.FILE_WRITE; + EventConfiguration fileWriteIOStatisticsEventConfiguration = EventConfigurations.FILE_WRITE_IO_STATISTICS; + if (!fileWriteEventConfiguration.isEnabled() && !fileWriteIOStatisticsEventConfiguration.isEnabled()) { return write(src); - } + } int bytesWritten = 0; long start = 0; + long duration = 0; try { start = EventConfiguration.timestamp(); bytesWritten = write(src); } finally { - long duration = EventConfiguration.timestamp() - start; - if (eventConfiguration.shouldCommit(duration)) { + duration = EventConfiguration.timestamp() - start; + if (fileWriteEventConfiguration.shouldCommit(duration)) { long bytes = bytesWritten > 0 ? bytesWritten : 0; FileWriteEvent.commit(start, duration, path, bytes); } } + if(fileWriteIOStatisticsEventConfiguration.isEnabled()){ + FileIOStatistics.addTotalWriteBytesForPeriod(((bytesWritten > 0) ? bytesWritten : 0), duration); + } return bytesWritten; } @JIInstrumentationMethod public int write(ByteBuffer src, long position) throws IOException { - EventConfiguration eventConfiguration = EventConfigurations.FILE_WRITE; - if (!eventConfiguration.isEnabled()) { + EventConfiguration fileWriteEventConfiguration = EventConfigurations.FILE_WRITE; + EventConfiguration fileWriteIOStatisticsEventConfiguration = EventConfigurations.FILE_WRITE_IO_STATISTICS; + if (!fileWriteEventConfiguration.isEnabled() && !fileWriteIOStatisticsEventConfiguration.isEnabled()) { return write(src, position); } - int bytesWritten = 0; long start = 0; + long duration = 0; try { start = EventConfiguration.timestamp(); bytesWritten = write(src, position); } finally { - long duration = EventConfiguration.timestamp() - start; - if (eventConfiguration.shouldCommit(duration)) { + duration = EventConfiguration.timestamp() - start; + if (fileWriteEventConfiguration.shouldCommit(duration)) { long bytes = bytesWritten > 0 ? bytesWritten : 0; FileWriteEvent.commit(start, duration, path, bytes); } } + if(fileWriteIOStatisticsEventConfiguration.isEnabled()){ + FileIOStatistics.addTotalWriteBytesForPeriod(((bytesWritten > 0) ? bytesWritten : 0), duration); + } + return bytesWritten; } @JIInstrumentationMethod public long write(ByteBuffer[] srcs, int offset, int length) throws IOException { - EventConfiguration eventConfiguration = EventConfigurations.FILE_WRITE; - if (!eventConfiguration.isEnabled()) { + EventConfiguration fileWriteEventConfiguration = EventConfigurations.FILE_WRITE; + EventConfiguration fileWriteIOStatisticsEventConfiguration = EventConfigurations.FILE_WRITE_IO_STATISTICS; + if (!fileWriteEventConfiguration.isEnabled() && !fileWriteIOStatisticsEventConfiguration.isEnabled()) { return write(srcs, offset, length); } long bytesWritten = 0; long start = 0; + long duration = 0; try { start = EventConfiguration.timestamp(); bytesWritten = write(srcs, offset, length); } finally { - long duration = EventConfiguration.timestamp() - start; - if (eventConfiguration.shouldCommit(duration)) { + duration = EventConfiguration.timestamp() - start; + if (fileWriteEventConfiguration.shouldCommit(duration)) { long bytes = bytesWritten > 0 ? bytesWritten : 0; FileWriteEvent.commit(start, duration, path, bytes); } } + if(fileWriteIOStatisticsEventConfiguration.isEnabled()){ + FileIOStatistics.addTotalWriteBytesForPeriod(((bytesWritten > 0) ? bytesWritten : 0), duration); + } return bytesWritten; } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/FileIOStatistics.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/FileIOStatistics.java new file mode 100644 index 0000000000000..da3e1df36fed7 --- /dev/null +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/FileIOStatistics.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2023, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +package jdk.jfr.internal.instrument; + +import java.util.concurrent.atomic.AtomicLong; + +public final class FileIOStatistics { + + private static AtomicLong totalReadBytesForPeriod = new AtomicLong(0); + private static AtomicLong totalReadDuration = new AtomicLong(0); + private static AtomicLong totalReadBytesForProcess = new AtomicLong(0); + private static AtomicLong totalWriteBytesForProcess = new AtomicLong(0); + private static AtomicLong totalWriteBytesForPeriod = new AtomicLong(0); + private static AtomicLong totalWriteDuration = new AtomicLong(0); + + public static long getTotalReadBytesForProcess() { + return totalReadBytesForProcess.get(); + } + + public static long getTotalReadDuration() { + return totalReadDuration.get(); + } + + public static long getTotalReadBytesForPeriod() { + return totalReadBytesForPeriod.get(); + } + + public static void addTotalReadBytesForPeriod(long bytesRead, long duration) { + totalReadDuration.addAndGet(duration); + totalReadBytesForProcess.addAndGet(bytesRead); + totalReadBytesForPeriod.addAndGet(bytesRead); + } + + /** + * Calculates and returns the read rate in bytes per second for the period mentioned in the jfc. + * + * This method computes the read rate by taking the total number of bytes read + * and the total duration of the read operation, then calculates the rate as + * bytes per second. If the interval is zero or negative, it returns 0. + * + * @return The read rate in bytes per second. + */ + public static long getAndResetReadRateForPeriod() { + long result = getTotalReadBytesForPeriod(); + long interval = getTotalReadDuration(); + totalReadBytesForPeriod.addAndGet(-result); + if (interval > 0) { + totalReadDuration.addAndGet(-interval); + double intervalInSec = (interval * 1.0 / 1_000_000_000); + long rRate = (long) (result / intervalInSec); + return rRate; + } + return 0; + } + + public static long getTotalWriteBytesForProcess() { + return totalWriteBytesForProcess.get(); + } + + public static long getTotalWriteDuration() { + return totalWriteDuration.get(); + } + + public static long getTotalWriteBytesForPeriod() { + return totalWriteBytesForPeriod.get(); + } + + public static long addTotalWriteBytesForPeriod(long bytesWritten, long duration) { + totalWriteDuration.addAndGet(duration); + totalWriteBytesForProcess.addAndGet(bytesWritten); + return totalWriteBytesForPeriod.addAndGet(bytesWritten); + } + + /** + * Calculates and returns the write rate in bytes per second for the period mentioned in the jfc. + * + * This method computes the write rate by taking the total number of bytes written + * and the total duration of the write operation, then calculates the rate as + * bytes per second. If the interval is zero or negative, it returns 0. + * + * @return The write rate in bytes per second. + */ + public static long getAndResetWriteRateForPeriod() { + long result = getTotalWriteBytesForPeriod(); + long interval = getTotalWriteDuration(); + totalWriteBytesForPeriod.addAndGet(-result); + if (interval > 0) { + totalWriteDuration.addAndGet(-interval); + double intervalInSec = (interval * 1.0 / 1_000_000_000); + long wRate = (long) (result / intervalInSec); + return wRate; + } + return 0; + } +} diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/FileInputStreamInstrumentor.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/FileInputStreamInstrumentor.java index 1d1ccb3bcb232..bcfcfdf6ec732 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/FileInputStreamInstrumentor.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/FileInputStreamInstrumentor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2023, 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 @@ -29,6 +29,7 @@ import jdk.jfr.events.EventConfigurations; import jdk.jfr.events.FileReadEvent; +import jdk.jfr.internal.instrument.FileIOStatistics; import jdk.jfr.internal.event.EventConfiguration; /** @@ -44,14 +45,16 @@ private FileInputStreamInstrumentor() { @JIInstrumentationMethod public int read() throws IOException { - EventConfiguration eventConfiguration = EventConfigurations.FILE_READ; - if (!eventConfiguration.isEnabled()) { + EventConfiguration fileReadEventConfiguration = EventConfigurations.FILE_READ; + EventConfiguration fileReadIOStatisticsEventConfiguration = EventConfigurations.FILE_READ_IO_STATISTICS; + if (!fileReadEventConfiguration.isEnabled() && !fileReadIOStatisticsEventConfiguration.isEnabled()) { return read(); } int result = 0; boolean endOfFile = false; long bytesRead = 0; long start = 0; + long duration = 0; try { start = EventConfiguration.timestamp(); result = read(); @@ -61,28 +64,35 @@ public int read() throws IOException { bytesRead = 1; } } finally { - long duration = EventConfiguration.timestamp() - start; - if (eventConfiguration.shouldCommit(duration)) { + duration = EventConfiguration.timestamp() - start; + if (fileReadEventConfiguration.shouldCommit(duration)) { FileReadEvent.commit(start, duration, path, bytesRead, endOfFile); } } + + if(fileReadIOStatisticsEventConfiguration.isEnabled()){ + FileIOStatistics.addTotalReadBytesForPeriod(bytesRead, duration); + } + return result; } @JIInstrumentationMethod public int read(byte b[]) throws IOException { - EventConfiguration eventConfiguration = EventConfigurations.FILE_READ; - if (!eventConfiguration.isEnabled()) { + EventConfiguration fileReadEventConfiguration = EventConfigurations.FILE_READ; + EventConfiguration fileReadIOStatisticsEventConfiguration = EventConfigurations.FILE_READ_IO_STATISTICS; + if (!fileReadEventConfiguration.isEnabled() && !fileReadIOStatisticsEventConfiguration.isEnabled()) { return read(b); } int bytesRead = 0; long start = 0; + long duration = 0; try { start = EventConfiguration.timestamp(); bytesRead = read(b); } finally { - long duration = EventConfiguration.timestamp() - start; - if (eventConfiguration.shouldCommit(duration)) { + duration = EventConfiguration.timestamp() - start; + if (fileReadEventConfiguration.shouldCommit(duration)) { if (bytesRead < 0) { FileReadEvent.commit(start, duration, path, 0L, true); } else { @@ -90,23 +100,30 @@ public int read(byte b[]) throws IOException { } } } + + if(fileReadIOStatisticsEventConfiguration.isEnabled()){ + FileIOStatistics.addTotalReadBytesForPeriod(((bytesRead < 0) ? 0 : bytesRead), duration); + } + return bytesRead; } @JIInstrumentationMethod public int read(byte b[], int off, int len) throws IOException { - EventConfiguration eventConfiguration = EventConfigurations.FILE_READ; - if (!eventConfiguration.isEnabled()) { + EventConfiguration fileReadEventConfiguration = EventConfigurations.FILE_READ; + EventConfiguration fileReadIOStatisticsEventConfiguration = EventConfigurations.FILE_READ_IO_STATISTICS; + if (!fileReadEventConfiguration.isEnabled() && !fileReadIOStatisticsEventConfiguration.isEnabled()) { return read(b, off, len); } int bytesRead = 0; long start = 0; + long duration = 0; try { start = EventConfiguration.timestamp(); bytesRead = read(b, off, len); } finally { - long duration = EventConfiguration.timestamp() - start; - if (eventConfiguration.shouldCommit(duration)) { + duration = EventConfiguration.timestamp() - start; + if (fileReadEventConfiguration.shouldCommit(duration)) { if (bytesRead < 0) { FileReadEvent.commit(start, duration, path, 0L, true); } else { @@ -114,6 +131,10 @@ public int read(byte b[], int off, int len) throws IOException { } } } + if(fileReadIOStatisticsEventConfiguration.isEnabled()){ + FileIOStatistics.addTotalReadBytesForPeriod(((bytesRead < 0) ? 0 : bytesRead), duration); + } + return bytesRead; } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/FileOutputStreamInstrumentor.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/FileOutputStreamInstrumentor.java index 2e9e750df9d8a..1660164103b84 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/FileOutputStreamInstrumentor.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/FileOutputStreamInstrumentor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2023, 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 @@ -44,64 +44,81 @@ private FileOutputStreamInstrumentor() { @JIInstrumentationMethod public void write(int b) throws IOException { - EventConfiguration eventConfiguration = EventConfigurations.FILE_WRITE; - if (!eventConfiguration.isEnabled()) { + EventConfiguration fileWriteEventConfiguration = EventConfigurations.FILE_WRITE; + EventConfiguration fileWriteIOStatisticsEventConfiguration = EventConfigurations.FILE_WRITE_IO_STATISTICS; + if (!fileWriteEventConfiguration.isEnabled() && !fileWriteIOStatisticsEventConfiguration.isEnabled()) { write(b); return; } long bytesWritten = 0; long start = 0; + long duration = 0; try { start = EventConfiguration.timestamp(); write(b); bytesWritten = 1; } finally { - long duration = EventConfiguration.timestamp() - start; - if (eventConfiguration.shouldCommit(duration)) { + duration = EventConfiguration.timestamp() - start; + if (fileWriteEventConfiguration.shouldCommit(duration)) { FileWriteEvent.commit(start, duration, path, bytesWritten); } } + + if (fileWriteIOStatisticsEventConfiguration.isEnabled()) { + FileIOStatistics.addTotalWriteBytesForPeriod(bytesWritten, duration); + } } @JIInstrumentationMethod public void write(byte b[]) throws IOException { - EventConfiguration eventConfiguration = EventConfigurations.FILE_WRITE; - if (!eventConfiguration.isEnabled()) { + EventConfiguration fileWriteEventConfiguration = EventConfigurations.FILE_WRITE; + EventConfiguration fileWriteIOStatisticsEventConfiguration = EventConfigurations.FILE_WRITE_IO_STATISTICS; + if (!fileWriteEventConfiguration.isEnabled() && !fileWriteIOStatisticsEventConfiguration.isEnabled()) { write(b); return; } long bytesWritten = 0; long start = 0; + long duration = 0; try { start = EventConfiguration.timestamp(); write(b); bytesWritten = b.length; } finally { - long duration = EventConfiguration.timestamp() - start; - if (eventConfiguration.shouldCommit(duration)) { + duration = EventConfiguration.timestamp() - start; + if (fileWriteEventConfiguration.shouldCommit(duration)) { FileWriteEvent.commit(start, duration, path, bytesWritten); } } + if (fileWriteIOStatisticsEventConfiguration.isEnabled()) { + FileIOStatistics.addTotalWriteBytesForPeriod(bytesWritten, duration); + } } @JIInstrumentationMethod public void write(byte b[], int off, int len) throws IOException { - EventConfiguration eventConfiguration = EventConfigurations.FILE_WRITE; - if (!eventConfiguration.isEnabled()) { + EventConfiguration fileWriteEventConfiguration = EventConfigurations.FILE_WRITE; + EventConfiguration fileWriteIOStatisticsEventConfiguration = EventConfigurations.FILE_WRITE_IO_STATISTICS; + if (!fileWriteEventConfiguration.isEnabled() && !fileWriteIOStatisticsEventConfiguration.isEnabled()) { write(b, off, len); return; } long bytesWritten = 0; long start = 0; + long duration = 0; try { start = EventConfiguration.timestamp(); write(b, off, len); bytesWritten = len; } finally { - long duration = EventConfiguration.timestamp() - start; - if (eventConfiguration.shouldCommit(duration)) { + duration = EventConfiguration.timestamp() - start; + if (fileWriteEventConfiguration.shouldCommit(duration)) { FileWriteEvent.commit(start, duration, path, bytesWritten); } } + if (fileWriteIOStatisticsEventConfiguration.isEnabled()) { + FileIOStatistics.addTotalWriteBytesForPeriod(bytesWritten, duration); + } + } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/JDKEvents.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/JDKEvents.java index 9405dfff8e0e7..9cf87eb387162 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/JDKEvents.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/JDKEvents.java @@ -25,6 +25,7 @@ package jdk.jfr.internal.instrument; +import java.time.Period; import java.util.ArrayList; import java.util.List; import java.util.Properties; @@ -45,6 +46,8 @@ import jdk.jfr.events.FileForceEvent; import jdk.jfr.events.FileReadEvent; import jdk.jfr.events.FileWriteEvent; +import jdk.jfr.events.FileReadIOStatisticsEvent; +import jdk.jfr.events.FileWriteIOStatisticsEvent; import jdk.jfr.events.DeserializationEvent; import jdk.jfr.events.InitialSecurityPropertyEvent; import jdk.jfr.events.ProcessStartEvent; @@ -89,6 +92,8 @@ public final class JDKEvents { FileForceEvent.class, FileReadEvent.class, FileWriteEvent.class, + FileReadIOStatisticsEvent.class, + FileWriteIOStatisticsEvent.class, SocketReadEvent.class, SocketWriteEvent.class, ExceptionThrownEvent.class, @@ -133,6 +138,8 @@ public final class JDKEvents { private static final Runnable emitContainerMemoryUsage = JDKEvents::emitContainerMemoryUsage; private static final Runnable emitContainerIOUsage = JDKEvents::emitContainerIOUsage; private static final Runnable emitInitialSecurityProperties = JDKEvents::emitInitialSecurityProperties; + private static final Runnable emitFileReadIOStatistics = JDKEvents::emitFileReadIOStatistics; + private static final Runnable emitFileWriteIOStatistics = JDKEvents::emitFileWriteIOStatistics; private static Metrics containerMetrics = null; private static boolean initializationTriggered; @@ -149,6 +156,8 @@ public static synchronized void initialize() { PeriodicEvents.addJDKEvent(ExceptionStatisticsEvent.class, emitExceptionStatistics); PeriodicEvents.addJDKEvent(DirectBufferStatisticsEvent.class, emitDirectBufferStatistics); PeriodicEvents.addJDKEvent(InitialSecurityPropertyEvent.class, emitInitialSecurityProperties); + PeriodicEvents.addJDKEvent(FileReadIOStatisticsEvent.class, emitFileReadIOStatistics); + PeriodicEvents.addJDKEvent(FileWriteIOStatisticsEvent.class, emitFileWriteIOStatistics); initializeContainerEvents(); initializationTriggered = true; @@ -265,6 +274,26 @@ private static void emitContainerCPUThrottling() { } } + private static void emitFileReadIOStatistics() { + FileReadIOStatisticsEvent t = new FileReadIOStatisticsEvent(); + if (FileIOStatistics.getTotalReadBytesForProcess() > 0) { + t.begin(); + t.accRead = FileIOStatistics.getTotalReadBytesForProcess(); + t.readRate = FileIOStatistics.getAndResetReadRateForPeriod(); + t.commit(); + } + } + + private static void emitFileWriteIOStatistics() { + FileWriteIOStatisticsEvent t = new FileWriteIOStatisticsEvent(); + if (FileIOStatistics.getTotalWriteBytesForProcess() > 0) { + t.begin(); + t.accWrite = FileIOStatistics.getTotalWriteBytesForProcess(); + t.writeRate = FileIOStatistics.getAndResetWriteRateForPeriod(); + t.commit(); + } + } + @SuppressWarnings("deprecation") public static byte[] retransformCallback(Class klass, byte[] oldBytes) throws Throwable { if (java.lang.Throwable.class == klass) { @@ -293,6 +322,8 @@ public static void remove() { PeriodicEvents.removeEvent(emitExceptionStatistics); PeriodicEvents.removeEvent(emitDirectBufferStatistics); PeriodicEvents.removeEvent(emitInitialSecurityProperties); + PeriodicEvents.removeEvent(emitFileReadIOStatistics); + PeriodicEvents.removeEvent(emitFileWriteIOStatistics); PeriodicEvents.removeEvent(emitContainerConfiguration); PeriodicEvents.removeEvent(emitContainerCPUUsage); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/RandomAccessFileInstrumentor.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/RandomAccessFileInstrumentor.java index 128859358d84b..d82158583106d 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/RandomAccessFileInstrumentor.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/RandomAccessFileInstrumentor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2023, 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 @@ -45,14 +45,16 @@ private RandomAccessFileInstrumentor() { @JIInstrumentationMethod public int read() throws IOException { - EventConfiguration eventConfiguration = EventConfigurations.FILE_READ; - if (!eventConfiguration.isEnabled()) { + EventConfiguration fileReadEventConfiguration = EventConfigurations.FILE_READ; + EventConfiguration fileReadIOStatisticsEventConfiguration = EventConfigurations.FILE_READ_IO_STATISTICS; + if (!fileReadEventConfiguration.isEnabled() && !fileReadIOStatisticsEventConfiguration.isEnabled()) { return read(); } int result = 0; long bytesRead = 0; boolean endOfFile = false; long start = 0; + long duration = 0; try { start = EventConfiguration.timestamp(); result = read(); @@ -62,28 +64,33 @@ public int read() throws IOException { bytesRead = 1; } } finally { - long duration = EventConfiguration.timestamp() - start; - if (eventConfiguration.shouldCommit(duration)) { + duration = EventConfiguration.timestamp() - start; + if (fileReadEventConfiguration.shouldCommit(duration)) { FileReadEvent.commit(start, duration, path, bytesRead, endOfFile); } } + if(fileReadIOStatisticsEventConfiguration.isEnabled()){ + FileIOStatistics.addTotalReadBytesForPeriod(((result < 0) ? 0 : bytesRead), duration); + } return result; } @JIInstrumentationMethod public int read(byte b[]) throws IOException { - EventConfiguration eventConfiguration = EventConfigurations.FILE_READ; - if (!eventConfiguration.isEnabled()) { + EventConfiguration fileReadEventConfiguration = EventConfigurations.FILE_READ; + EventConfiguration fileReadIOStatisticsEventConfiguration = EventConfigurations.FILE_READ_IO_STATISTICS; + if (!fileReadEventConfiguration.isEnabled() && !fileReadIOStatisticsEventConfiguration.isEnabled()) { return read(b); } int bytesRead = 0; long start = 0; + long duration = 0; try { start = EventConfiguration.timestamp(); bytesRead = read(b); } finally { - long duration = EventConfiguration.timestamp() - start; - if (eventConfiguration.shouldCommit(duration)) { + duration = EventConfiguration.timestamp() - start; + if (fileReadEventConfiguration.shouldCommit(duration)) { if (bytesRead < 0) { FileReadEvent.commit(start, duration, path, 0L, true); } else { @@ -91,23 +98,28 @@ public int read(byte b[]) throws IOException { } } } + if(fileReadIOStatisticsEventConfiguration.isEnabled()){ + FileIOStatistics.addTotalReadBytesForPeriod(((bytesRead < 0) ? 0 : bytesRead), duration); + } return bytesRead; } @JIInstrumentationMethod public int read(byte b[], int off, int len) throws IOException { - EventConfiguration eventConfiguration = EventConfigurations.FILE_READ; - if (!eventConfiguration.isEnabled()) { + EventConfiguration fileReadEventConfiguration = EventConfigurations.FILE_READ; + EventConfiguration fileReadIOStatisticsEventConfiguration = EventConfigurations.FILE_READ_IO_STATISTICS; + if (!fileReadEventConfiguration.isEnabled() && !fileReadIOStatisticsEventConfiguration.isEnabled()) { return read(b, off, len); } int bytesRead = 0; long start = 0; + long duration = 0; try { start = EventConfiguration.timestamp(); bytesRead = read(b, off, len); } finally { - long duration = EventConfiguration.timestamp() - start; - if (eventConfiguration.shouldCommit(duration)) { + duration = EventConfiguration.timestamp() - start; + if (fileReadEventConfiguration.shouldCommit(duration)) { if (bytesRead < 0) { FileReadEvent.commit(start, duration, path, 0L, true); } else { @@ -115,69 +127,87 @@ public int read(byte b[], int off, int len) throws IOException { } } } + if(fileReadIOStatisticsEventConfiguration.isEnabled()){ + FileIOStatistics.addTotalReadBytesForPeriod(((bytesRead < 0) ? 0 : bytesRead), duration); + } return bytesRead; } @JIInstrumentationMethod public void write(int b) throws IOException { - EventConfiguration eventConfiguration = EventConfigurations.FILE_WRITE; - if (!eventConfiguration.isEnabled()) { + EventConfiguration fileWriteEventConfiguration = EventConfigurations.FILE_WRITE; + EventConfiguration fileWriteIOStatisticsEventConfiguration = EventConfigurations.FILE_WRITE_IO_STATISTICS; + if (!fileWriteEventConfiguration.isEnabled() && !fileWriteIOStatisticsEventConfiguration.isEnabled()) { write(b); return; } long bytesWritten = 0; long start = 0; + long duration = 0; try { start = EventConfiguration.timestamp(); write(b); bytesWritten = 1; } finally { - long duration = EventConfiguration.timestamp() - start; - if (eventConfiguration.shouldCommit(duration)) { + duration = EventConfiguration.timestamp() - start; + if (fileWriteEventConfiguration.shouldCommit(duration)) { FileWriteEvent.commit(start, duration, path, bytesWritten); } } + if (fileWriteIOStatisticsEventConfiguration.isEnabled()) { + FileIOStatistics.addTotalWriteBytesForPeriod(bytesWritten, duration); + } } @JIInstrumentationMethod public void write(byte b[]) throws IOException { - EventConfiguration eventConfiguration = EventConfigurations.FILE_WRITE; - if (!eventConfiguration.isEnabled()) { + EventConfiguration fileWriteEventConfiguration = EventConfigurations.FILE_WRITE; + EventConfiguration fileWriteIOStatisticsEventConfiguration = EventConfigurations.FILE_WRITE_IO_STATISTICS; + if (!fileWriteEventConfiguration.isEnabled() && !fileWriteIOStatisticsEventConfiguration.isEnabled()) { write(b); return; } long bytesWritten = 0; long start = 0; + long duration = 0; try { start = EventConfiguration.timestamp(); write(b); bytesWritten = b.length; } finally { - long duration = EventConfiguration.timestamp() - start; - if (eventConfiguration.shouldCommit(duration)) { + duration = EventConfiguration.timestamp() - start; + if (fileWriteEventConfiguration.shouldCommit(duration)) { FileWriteEvent.commit(start, duration, path, bytesWritten); } } + if (fileWriteIOStatisticsEventConfiguration.isEnabled()) { + FileIOStatistics.addTotalWriteBytesForPeriod(bytesWritten, duration); + } } @JIInstrumentationMethod public void write(byte b[], int off, int len) throws IOException { - EventConfiguration eventConfiguration = EventConfigurations.FILE_WRITE; - if (!eventConfiguration.isEnabled()) { + EventConfiguration fileWriteEventConfiguration = EventConfigurations.FILE_WRITE; + EventConfiguration fileWriteIOStatisticsEventConfiguration = EventConfigurations.FILE_WRITE_IO_STATISTICS; + if (!fileWriteEventConfiguration.isEnabled() && !fileWriteIOStatisticsEventConfiguration.isEnabled()) { write(b, off, len); return; } long bytesWritten = 0; long start = 0; + long duration = 0; try { start = EventConfiguration.timestamp(); write(b, off, len); bytesWritten = len; } finally { - long duration = EventConfiguration.timestamp() - start; - if (eventConfiguration.shouldCommit(duration)) { + duration = EventConfiguration.timestamp() - start; + if (fileWriteEventConfiguration.shouldCommit(duration)) { FileWriteEvent.commit(start, duration, path, bytesWritten); } } + if (fileWriteIOStatisticsEventConfiguration.isEnabled()) { + FileIOStatistics.addTotalWriteBytesForPeriod(bytesWritten, duration); + } } -} +} \ No newline at end of file diff --git a/src/jdk.jfr/share/conf/jfr/default.jfc b/src/jdk.jfr/share/conf/jfr/default.jfc index 6b0aa1724c439..60e23d24777a2 100644 --- a/src/jdk.jfr/share/conf/jfr/default.jfc +++ b/src/jdk.jfr/share/conf/jfr/default.jfc @@ -734,6 +734,16 @@ 20 ms + + false + 10 s + + + + false + 10 s + + true true diff --git a/src/jdk.jfr/share/conf/jfr/profile.jfc b/src/jdk.jfr/share/conf/jfr/profile.jfc index 79ce390052a14..4c0f43f18a919 100644 --- a/src/jdk.jfr/share/conf/jfr/profile.jfc +++ b/src/jdk.jfr/share/conf/jfr/profile.jfc @@ -734,6 +734,16 @@ 10 ms + + false + 10 s + + + + false + 10 s + + true true diff --git a/test/hotspot/jtreg/runtime/cds/appcds/CDSandJFR.java b/test/hotspot/jtreg/runtime/cds/appcds/CDSandJFR.java index 065786686fa06..e15c97788affd 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/CDSandJFR.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/CDSandJFR.java @@ -41,6 +41,8 @@ public class CDSandJFR { "jdk/jfr/Event", "jdk/jfr/events/FileReadEvent", "jdk/jfr/events/FileWriteEvent", + "jdk.jfr/events/FileReadIOStatistics", + "jdk.jfr/events/FileWriteIOStatistics", "jdk/jfr/events/SocketReadEvent", "jdk/jfr/events/SocketWriteEvent", "jdk/jfr/events/ExceptionThrownEvent", diff --git a/test/jdk/jdk/jfr/api/recording/settings/TestSettingsAvailability.java b/test/jdk/jdk/jfr/api/recording/settings/TestSettingsAvailability.java index dd160881d2edd..f44f998f91cba 100644 --- a/test/jdk/jdk/jfr/api/recording/settings/TestSettingsAvailability.java +++ b/test/jdk/jdk/jfr/api/recording/settings/TestSettingsAvailability.java @@ -91,6 +91,8 @@ private static void testKnownSettings() throws Exception { testSetting(EventNames.JVMInformation, "enabled", "period"); testSetting(EventNames.FileRead, "enabled", "threshold", "stackTrace"); testSetting(EventNames.FileWrite, "enabled", "threshold","stackTrace"); + testSetting(EventNames.FileReadIOStatistics, "enabled", "period"); + testSetting(EventNames.FileWriteIOStatistics, "enabled", "period"); testSetting(EventNames.ExceptionStatistics, "enabled", "period"); testSetting(EventNames.SocketRead, "enabled", "threshold", "stackTrace"); testSetting(EventNames.SocketWrite, "enabled", "threshold", "stackTrace"); diff --git a/test/jdk/jdk/jfr/event/io/TestDisabledEvents.java b/test/jdk/jdk/jfr/event/io/TestDisabledEvents.java index 0b523b80523b8..57896cb106397 100644 --- a/test/jdk/jdk/jfr/event/io/TestDisabledEvents.java +++ b/test/jdk/jdk/jfr/event/io/TestDisabledEvents.java @@ -36,6 +36,7 @@ import jdk.jfr.Recording; import jdk.jfr.consumer.RecordedEvent; import jdk.test.lib.Utils; +import jdk.test.lib.jfr.EventNames; import jdk.test.lib.jfr.Events; /** @@ -57,7 +58,9 @@ public static void main(String[] args) throws Throwable { File tmp = Utils.createTempFile("TestDisabledEvents", ".tmp").toFile(); try (Recording recording = new Recording()) { recording.disable(IOEvent.EVENT_FILE_READ); - recording.disable(IOEvent.EVENT_FILE_WRITE); + recording.disable(IOEvent.EVENT_FILE_WRITE); + recording.disable(EventNames.FileReadIOStatistics); + recording.disable(EventNames.FileWriteIOStatistics); recording.start(); useRandomAccessFile(tmp); @@ -70,6 +73,8 @@ public static void main(String[] args) throws Throwable { System.out.println("Got eventName:" + eventName); assertNotEquals(eventName, IOEvent.EVENT_FILE_READ, "Got disabled read event"); assertNotEquals(eventName, IOEvent.EVENT_FILE_WRITE, "Got disabled write event"); + assertNotEquals(eventName, EventNames.FileReadIOStatistics, "Got disabled FileReadIOStatistics Event"); + assertNotEquals(eventName, EventNames.FileWriteIOStatistics, "Got disabled FileWriteIOStatistics Event"); } } } diff --git a/test/jdk/jdk/jfr/event/io/TestFileChannelEvents.java b/test/jdk/jdk/jfr/event/io/TestFileChannelEvents.java index 11ae0a12e4fb5..dc0114543a47e 100644 --- a/test/jdk/jdk/jfr/event/io/TestFileChannelEvents.java +++ b/test/jdk/jdk/jfr/event/io/TestFileChannelEvents.java @@ -36,6 +36,7 @@ import jdk.jfr.Recording; import jdk.jfr.consumer.RecordedEvent; import jdk.test.lib.Utils; +import jdk.test.lib.jfr.EventNames; import jdk.test.lib.jfr.Events; /** @@ -54,6 +55,8 @@ public static void main(String[] args) throws Throwable { recording.enable(IOEvent.EVENT_FILE_FORCE).withThreshold(Duration.ofMillis(0)); recording.enable(IOEvent.EVENT_FILE_READ).withThreshold(Duration.ofMillis(0)); recording.enable(IOEvent.EVENT_FILE_WRITE).withThreshold(Duration.ofMillis(0)); + recording.enable(EventNames.FileReadIOStatistics); + recording.enable(EventNames.FileWriteIOStatistics); recording.start(); ByteBuffer bufA = ByteBuffer.allocateDirect(10); @@ -115,6 +118,8 @@ public static void main(String[] args) throws Throwable { recording.stop(); List events = Events.fromRecording(recording); IOHelper.verifyEqualsInOrder(events, expectedEvents); + Events.hasEvent(events, EventNames.FileReadIOStatistics); + Events.hasEvent(events, EventNames.FileWriteIOStatistics); } } } diff --git a/test/jdk/jdk/jfr/event/io/TestFileIOStatisticsEvents.java b/test/jdk/jdk/jfr/event/io/TestFileIOStatisticsEvents.java new file mode 100644 index 0000000000000..afcb779e7758e --- /dev/null +++ b/test/jdk/jdk/jfr/event/io/TestFileIOStatisticsEvents.java @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2023, 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 + * 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. + */ + +package jdk.jfr.event.io; + +import static jdk.test.lib.Asserts.assertEquals; +import static jdk.test.lib.Asserts.assertNotEquals; +import static jdk.test.lib.Asserts.assertGreaterThanOrEqual; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.time.Duration; +import java.util.List; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; +import jdk.test.lib.Utils; + +/** + * @test + * @key jfr + * @requires vm.hasJFR + * @library /test/lib /test/jdk + * @run main/othervm jdk.jfr.event.io.TestFileIOStatisticsEvents + */ +public class TestFileIOStatisticsEvents { + private static final int writeInt = 'A'; + private static final byte[] writeBuf = { 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; + private static int accWriteExpected = 0; + private static int accReadExpected = 0; + public static void main(String[] args) throws Throwable { + Recording recording = new Recording(); + File tmp = Utils.createTempFile("TestFileIOStatistics", ".tmp").toFile(); + recording.enable(EventNames.FileReadIOStatistics); + recording.enable(EventNames.FileWriteIOStatistics); + + recording.start(); + useFileChannel(tmp); + useRandomAccessFile(tmp); + useFileStream(tmp); + recording.stop(); + + checkForDisabledFileEvents(recording); + checkForEnabledFileIOStatistics(recording); + } + + private static void useFileChannel(File tmp) throws Throwable { + tmp.delete(); + try (RandomAccessFile rf = new RandomAccessFile(tmp, "rw"); FileChannel ch = rf.getChannel()) { + final String bufContent = "0123456789"; + final int bufSize = bufContent.length(); + ByteBuffer writeBuf = ByteBuffer.allocateDirect(bufSize); + writeBuf.put(bufContent.getBytes()); + writeBuf.flip(); + int writeSize = ch.write(writeBuf); + accWriteExpected = accWriteExpected + writeSize; + + ch.position(0); + ByteBuffer readBuf = ByteBuffer.allocateDirect(bufSize); + int readSize = ch.read(readBuf); + accReadExpected = accReadExpected + readSize; + assertEquals(accWriteExpected, accReadExpected, "Total Read bytes != Total write bytes in the useFileChannel"); + } + } + + private static void useRandomAccessFile(File tmp) throws Throwable { + tmp.delete(); + try (RandomAccessFile ras = new RandomAccessFile(tmp, "rw")) { + ras.write(writeInt); + ras.write(writeBuf); + ras.seek(0); + accWriteExpected = accWriteExpected + writeBuf.length + 1; + int readInt = ras.read(); + byte[] readBuf = new byte[writeBuf.length]; + int readSize = ras.read(readBuf); + accReadExpected = accReadExpected + readSize + 1; + // Try to read more which should generate EOF. + readInt = ras.read(); + assertEquals(readInt, -1, "Wrong readInt after EOF"); + } + assertEquals(accWriteExpected, accReadExpected, "Total Read bytes != Total write bytes in the RandomAccessFile"); + } + + private static void useFileStream(File tmp) throws Throwable { + tmp.delete(); + try (FileOutputStream fos = new FileOutputStream(tmp)) { + fos.write(writeInt); + fos.write(writeBuf); + accWriteExpected = accWriteExpected + writeBuf.length + 1; + } + try(FileInputStream fis = new FileInputStream(tmp)){ + int readInt = fis.read(); + byte[] readBuf = new byte[writeBuf.length]; + int readSize = fis.read(readBuf); + accReadExpected = accReadExpected + readSize + 1; + readInt = fis.read(); + assertEquals(readInt, -1, "Wrong readInt after EOF"); + } + assertEquals(accWriteExpected, accReadExpected, "Total Read bytes != Total write bytes in the FileStream"); + + } + + private static void checkForDisabledFileEvents(Recording recording) throws Throwable { + for (RecordedEvent event : Events.fromRecording(recording)) { + final String eventName = event.getEventType().getName(); + System.out.println("Got eventName:" + eventName); + assertNotEquals(eventName, IOEvent.EVENT_FILE_READ, "Got disabled read event"); + assertNotEquals(eventName, IOEvent.EVENT_FILE_WRITE, "Got disabled write event"); + assertNotEquals(eventName, IOEvent.EVENT_FILE_FORCE, "Got disabled force event"); + } + } + + private static void checkForEnabledFileIOStatistics(Recording recording) throws Throwable { + boolean hasNonZeroReadRate = false; + boolean hasNonZeroWriteRate = false; + String accWrite = null; + String accRead = null; + + //JFR EventAttributes + final String WRITE_RATE = "writeRate"; + final String ACC_WRITE_BYTES = "accWrite"; + final String READ_RATE = "readRate"; + final String ACC_READ_BYTES = "accRead"; + + List events = Events.fromRecording(recording); + + Events.hasEvents(events); + Events.hasEvent(events, EventNames.FileReadIOStatistics); + Events.hasEvent(events, EventNames.FileWriteIOStatistics); + + for (RecordedEvent event : events) { + if (event.getEventType().getName().toString().equals(EventNames.FileWriteIOStatistics)) { + String writeRateVal = Events.assertField(event, WRITE_RATE).getValue().toString(); + accWrite = Events.assertField(event, ACC_WRITE_BYTES).getValue().toString(); + if (Double.parseDouble(writeRateVal) > 0) { + hasNonZeroWriteRate = true; + } + } else if (event.getEventType().getName().toString().equals(EventNames.FileReadIOStatistics)) { + String readRateVal = Events.assertField(event, READ_RATE).getValue().toString(); + accRead = Events.assertField(event, ACC_READ_BYTES).getValue().toString(); + if (Double.parseDouble(readRateVal) > 0) { + hasNonZeroReadRate = true; + } + } + } + assertEquals(hasNonZeroWriteRate, true); + assertEquals(hasNonZeroReadRate, true); + + assertGreaterThanOrEqual(Integer.parseInt(accRead), accReadExpected, "The accumulated read bytes:" + accRead + " should be equal to expected Read Bytes:" + accReadExpected); + assertGreaterThanOrEqual(Integer.parseInt(accWrite), accWriteExpected, + "The accumulated write bytes:" + accWrite + " should be equal or more that expected length:" + accWriteExpected); + } +} \ No newline at end of file diff --git a/test/jdk/jdk/jfr/event/io/TestFileStreamEvents.java b/test/jdk/jdk/jfr/event/io/TestFileStreamEvents.java index d557a8dfd7de2..1e11f44acb7e8 100644 --- a/test/jdk/jdk/jfr/event/io/TestFileStreamEvents.java +++ b/test/jdk/jdk/jfr/event/io/TestFileStreamEvents.java @@ -36,6 +36,7 @@ import jdk.jfr.consumer.RecordedEvent; import jdk.test.lib.Utils; import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.EventNames; /** * @test TestFileStreamEvents @@ -53,6 +54,8 @@ public static void main(String[] args) throws Throwable { try(FileOutputStream fos = new FileOutputStream(tmp); FileInputStream fis = new FileInputStream(tmp);) { recording.enable(IOEvent.EVENT_FILE_READ).withThreshold(Duration.ofMillis(0)); recording.enable(IOEvent.EVENT_FILE_WRITE).withThreshold(Duration.ofMillis(0)); + recording.enable(EventNames.FileReadIOStatistics); + recording.enable(EventNames.FileWriteIOStatistics); recording.start(); int writeByte = 47; @@ -88,6 +91,8 @@ public static void main(String[] args) throws Throwable { recording.stop(); List events = Events.fromRecording(recording); IOHelper.verifyEqualsInOrder(events, expectedEvents); + Events.hasEvent(events, EventNames.FileReadIOStatistics); + Events.hasEvent(events, EventNames.FileWriteIOStatistics); } } } diff --git a/test/jdk/jdk/jfr/event/io/TestRandomAccessFileEvents.java b/test/jdk/jdk/jfr/event/io/TestRandomAccessFileEvents.java index 47fbc62d08d20..cf968bf22ab67 100644 --- a/test/jdk/jdk/jfr/event/io/TestRandomAccessFileEvents.java +++ b/test/jdk/jdk/jfr/event/io/TestRandomAccessFileEvents.java @@ -35,6 +35,7 @@ import jdk.jfr.consumer.RecordedEvent; import jdk.test.lib.Utils; import jdk.test.lib.jfr.Events; +import jdk.test.lib.jfr.EventNames; /** * @test @@ -52,6 +53,8 @@ public static void main(String[] args) throws Throwable { recording.enable(IOEvent.EVENT_FILE_WRITE).withThreshold(Duration.ofMillis(0)); recording.enable(IOEvent.EVENT_FILE_READ).withThreshold(Duration.ofMillis(0)); + recording.enable(EventNames.FileReadIOStatistics); + recording.enable(EventNames.FileWriteIOStatistics); recording.start(); RandomAccessFile ras = new RandomAccessFile(tmp, "rw"); @@ -103,6 +106,8 @@ public static void main(String[] args) throws Throwable { recording.stop(); List events = Events.fromRecording(recording); IOHelper.verifyEqualsInOrder(events, expectedEvents); + Events.hasEvent(events, EventNames.FileReadIOStatistics); + Events.hasEvent(events, EventNames.FileWriteIOStatistics); } } diff --git a/test/lib/jdk/test/lib/jfr/EventNames.java b/test/lib/jdk/test/lib/jfr/EventNames.java index fd0dcc6895aab..96ac1758d7009 100644 --- a/test/lib/jdk/test/lib/jfr/EventNames.java +++ b/test/lib/jdk/test/lib/jfr/EventNames.java @@ -192,6 +192,8 @@ public class EventNames { public static final String FileForce = PREFIX + "FileForce"; public static final String FileRead = PREFIX + "FileRead"; public static final String FileWrite = PREFIX + "FileWrite"; + public static final String FileWriteIOStatistics = PREFIX + "FileWriteIOStatistics"; + public static final String FileReadIOStatistics = PREFIX + "FileReadIOStatistics"; public static final String SocketRead = PREFIX + "SocketRead"; public static final String SocketWrite = PREFIX + "SocketWrite"; public static final String ExceptionStatistics = PREFIX + "ExceptionStatistics";