From b275bdd9b55b567cfe60c389d5ef8b70615928f4 Mon Sep 17 00:00:00 2001 From: Tim Prinzing Date: Wed, 20 Sep 2023 12:34:08 +0000 Subject: [PATCH] 8308995: Update Network IO JFR events to be static mirror events Reviewed-by: egahlin, alanb --- make/test/BuildMicrobenchmark.gmk | 3 +- .../share/classes/java/net/Socket.java | 38 +++- .../jdk/internal/event/SocketReadEvent.java | 134 ++++++++++++ .../jdk/internal/event/SocketWriteEvent.java | 126 +++++++++++ .../classes/sun/nio/ch/SocketChannelImpl.java | 63 +++++- .../jdk/jfr/events/EventConfigurations.java | 2 - .../jdk/jfr/events/SocketReadEvent.java | 10 +- .../jdk/jfr/events/SocketWriteEvent.java | 10 +- .../jfr/internal/instrument/JDKEvents.java | 9 +- .../SocketChannelImplInstrumentor.java | 201 ------------------ .../SocketInputStreamInstrumentor.java | 76 ------- .../SocketOutputStreamInstrumentor.java | 75 ------- .../bench/java/net/SocketEventOverhead.java | 167 +++++++++++++++ 13 files changed, 527 insertions(+), 387 deletions(-) create mode 100644 src/java.base/share/classes/jdk/internal/event/SocketReadEvent.java create mode 100644 src/java.base/share/classes/jdk/internal/event/SocketWriteEvent.java delete mode 100644 src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/SocketChannelImplInstrumentor.java delete mode 100644 src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/SocketInputStreamInstrumentor.java delete mode 100644 src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/SocketOutputStreamInstrumentor.java create mode 100644 test/micro/org/openjdk/bench/java/net/SocketEventOverhead.java diff --git a/make/test/BuildMicrobenchmark.gmk b/make/test/BuildMicrobenchmark.gmk index e4d6ebc9a52..0369b4e0bb3 100644 --- a/make/test/BuildMicrobenchmark.gmk +++ b/make/test/BuildMicrobenchmark.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2018, 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 @@ -105,6 +105,7 @@ $(eval $(call SetupJavaCompilation, BUILD_JDK_MICROBENCHMARK, \ --add-exports java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED \ --add-exports java.base/jdk.internal.org.objectweb.asm.tree=ALL-UNNAMED \ --add-exports java.base/jdk.internal.vm=ALL-UNNAMED \ + --add-exports java.base/jdk.internal.event=ALL-UNNAMED \ --enable-preview, \ JAVA_FLAGS := --add-modules jdk.unsupported --limit-modules java.management \ --add-exports java.base/jdk.internal.vm=ALL-UNNAMED \ diff --git a/src/java.base/share/classes/java/net/Socket.java b/src/java.base/share/classes/java/net/Socket.java index 82a7a42badf..ada2073c408 100644 --- a/src/java.base/share/classes/java/net/Socket.java +++ b/src/java.base/share/classes/java/net/Socket.java @@ -25,6 +25,8 @@ package java.net; +import jdk.internal.event.SocketReadEvent; +import jdk.internal.event.SocketWriteEvent; import sun.security.util.SecurityConstants; import java.io.InputStream; @@ -1073,9 +1075,6 @@ public class Socket implements java.io.Closeable { /** * An InputStream that delegates read/available operations to an underlying * input stream. The close method is overridden to close the Socket. - * - * This class is instrumented by Java Flight Recorder (JFR) to get socket - * I/O events. */ private static class SocketInputStream extends InputStream { private final Socket parent; @@ -1092,6 +1091,16 @@ public class Socket implements java.io.Closeable { } @Override public int read(byte[] b, int off, int len) throws IOException { + if (!SocketReadEvent.enabled()) { + return implRead(b, off, len); + } + long start = SocketReadEvent.timestamp(); + int nbytes = implRead(b, off, len); + SocketReadEvent.offer(start, nbytes, parent.getRemoteSocketAddress(), getSoTimeout()); + return nbytes; + } + + private int implRead(byte[] b, int off, int len) throws IOException { try { return in.read(b, off, len); } catch (SocketTimeoutException e) { @@ -1105,6 +1114,16 @@ public class Socket implements java.io.Closeable { throw e; } } + + private int getSoTimeout() { + try { + return parent.getSoTimeout(); + } catch (SocketException e) { + // ignored - avoiding exceptions in jfr event data gathering + } + return 0; + } + @Override public int available() throws IOException { return in.available(); @@ -1169,9 +1188,6 @@ public class Socket implements java.io.Closeable { /** * An OutputStream that delegates write operations to an underlying output * stream. The close method is overridden to close the Socket. - * - * This class is instrumented by Java Flight Recorder (JFR) to get socket - * I/O events. */ private static class SocketOutputStream extends OutputStream { private final Socket parent; @@ -1187,6 +1203,16 @@ public class Socket implements java.io.Closeable { } @Override public void write(byte[] b, int off, int len) throws IOException { + if (!SocketWriteEvent.enabled()) { + implWrite(b, off, len); + return; + } + long start = SocketWriteEvent.timestamp(); + implWrite(b, off, len); + SocketWriteEvent.offer(start, len, parent.getRemoteSocketAddress()); + } + + private void implWrite(byte[] b, int off, int len) throws IOException { try { out.write(b, off, len); } catch (InterruptedIOException e) { diff --git a/src/java.base/share/classes/jdk/internal/event/SocketReadEvent.java b/src/java.base/share/classes/jdk/internal/event/SocketReadEvent.java new file mode 100644 index 00000000000..8f9b6ac5710 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/event/SocketReadEvent.java @@ -0,0 +1,134 @@ +/* + * 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.internal.event; + + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.UnixDomainSocketAddress; + +/** + * A JFR event for socket read operations. This event is mirrored in + * {@code jdk.jfr.events.SocketReadEvent } where the metadata for the event is + * provided with annotations. Some of the methods are replaced by generated + * methods when jfr is enabled. Note that the order of the arguments of the + * {@link #commit(long, long, String, String, int, long, long, boolean)} method + * must be the same as the order of the fields. + */ +public class SocketReadEvent extends Event { + + // THE ORDER OF THE FOLLOWING FIELDS IS IMPORTANT! + // The order must match the argument order of the generated commit method. + public String host; + public String address; + public int port; + public long timeout; + public long bytesRead; + public boolean endOfStream; + + /** + * Actually commit a socket read event. The implementation + * of this method is generated automatically if jfr is enabled. + * The order of the fields must be the same as the parameters in this method. + * {@code commit(..., String, String, int, long, long, boolean)} + * + * @param start timestamp of the start of the operation + * @param duration time in nanoseconds to complete the operation + * @param host remote host of the transfer + * @param address remote address of the transfer + * @param port remote port of the transfer + * @param timeout timeout setting for the read + * @param bytes number of bytes that were transferred + * @param endOfStream has the end of the stream been reached + */ + public static void commit(long start, long duration, String host, String address, int port, long timeout, long bytes, boolean endOfStream) { + // Generated by JFR + } + + /** + * Determine if an event should be emitted. The duration of the operation + * must exceed some threshold in order to commit the event. The implementation + * of this method is generated automatically if jfr is enabled. + * + * @param duration time in nanoseconds to complete the operation + * @return true if the event should be commited + */ + public static boolean shouldCommit(long duration) { + // Generated by JFR + return false; + } + + /** + * Determine if this kind of event is enabled. The implementation + * of this method is generated automatically if jfr is enabled. + * + * @return true if socket read events are enabled, false otherwise + */ + public static boolean enabled() { + // Generated by JFR + return false; + } + + /** + * Fetch the current timestamp in nanoseconds. This method is used + * to determine the start and end of an operation. The implementation + * of this method is generated automatically if jfr is enabled. + * + * @return the current timestamp value + */ + public static long timestamp() { + // Generated by JFR + return 0L; + } + + /** + * Helper method to offer the data needed to potentially commit an event. + * The duration of the operation is computed using the current + * timestamp and the given start time. If the duration is meets + * or exceeds the configured value (determined by calling the generated method + * {@link #shouldCommit(long)}), an event will be emitted by calling + * {@link #commit(long, long, String, String, int, long, long, boolean)}. + * + * @param start the start time + * @param nbytes how many bytes were transferred + * @param remote the address of the remote socket + * @param timeout maximum time to wait + */ + public static void offer(long start, long nbytes, SocketAddress remote, long timeout) { + long duration = timestamp() - start; + if (shouldCommit(duration)) { + boolean eof = nbytes < 0 ? true : false; + nbytes = nbytes < 0 ? 0 : nbytes; + if (remote instanceof InetSocketAddress isa) { + commit(start, duration, isa.getHostString(), isa.getAddress().getHostAddress(), isa.getPort(), timeout, nbytes, eof); + } else if (remote instanceof UnixDomainSocketAddress udsa) { + String path = "[" + udsa.getPath().toString() + "]"; + commit(start, duration, "Unix domain socket", path, 0, timeout, nbytes, eof); + } + } + } + +} diff --git a/src/java.base/share/classes/jdk/internal/event/SocketWriteEvent.java b/src/java.base/share/classes/jdk/internal/event/SocketWriteEvent.java new file mode 100644 index 00000000000..4fba69d3efe --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/event/SocketWriteEvent.java @@ -0,0 +1,126 @@ +/* + * 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.internal.event; + + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.UnixDomainSocketAddress; + +/** + * A JFR event for socket write operations. This event is mirrored in + * {@code jdk.jfr.events.SocketWriteEvent } where the metadata for the event is + * provided with annotations. Some of the methods are replaced by generated + * methods when jfr is enabled. Note that the order of the arguments of the + * {@link #commit(long, long, String, String, int, long)} method + * must be the same as the order of the fields. + */ +public class SocketWriteEvent extends Event { + + // THE ORDER OF THE FOLLOWING FIELDS IS IMPORTANT! + // The order must match the argument order of the generated commit method. + public String host; + public String address; + public int port; + public long bytesWritten; + + /** + * Actually commit a socket write event. This is generated automatically. + * The order of the fields must be the same as the parameters in this method. + * {@code commit(..., String, String, int, long)} + * + * @param start timestamp of the start of the operation + * @param duration time in nanoseconds to complete the operation + * @param host remote host of the transfer + * @param address remote address of the transfer + * @param port remote port of the transfer + * @param bytes number of bytes that were transferred + */ + public static void commit(long start, long duration, String host, String address, int port, long bytes) { + // Generated by JFR + } + + /** + * Determine if an event should be emitted. The duration of the operation + * must exceed some threshold in order to commit the event. The implementation + * of this method is generated automatically if jfr is enabled. + * + * @param duration time in nanoseconds to complete the operation + * @return true if the event should be commited + */ + public static boolean shouldCommit(long duration) { + // Generated by JFR + return false; + } + + /** + * Determine if this kind of event is enabled. The implementation + * of this method is generated automatically if jfr is enabled. + * + * @return true if socket write events are enabled, false otherwise + */ + public static boolean enabled() { + // Generated by JFR + return false; + } + + /** + * Fetch the current timestamp in nanoseconds. This method is used + * to determine the start and end of an operation. The implementation + * of this method is generated automatically if jfr is enabled. + * + * @return the current timestamp value + */ + public static long timestamp() { + // Generated by JFR + return 0L; + } + + /** + * Helper method to offer the data needed to potentially commit an event. + * The duration of the operation is computed using the current + * timestamp and the given start time. If the duration is meets + * or exceeds the configured value (determined by calling the generated method + * {@link #shouldCommit(long)}), an event will be emitted by calling + * {@link #commit(long, long, String, String, int, long)}. + * + * @param start the start time + * @param bytesWritten how many bytes were sent + * @param remote the address of the remote socket being written to + */ + public static void offer(long start, long bytesWritten, SocketAddress remote) { + long duration = timestamp() - start; + if (shouldCommit(duration)) { + long bytes = bytesWritten < 0 ? 0 : bytesWritten; + if (remote instanceof InetSocketAddress isa) { + commit(start, duration, isa.getHostString(), isa.getAddress().getHostAddress(), isa.getPort(), bytes); + } else if (remote instanceof UnixDomainSocketAddress udsa) { + String path = "[" + udsa.getPath().toString() + "]"; + commit(start, duration, "Unix domain socket", path, 0, bytes); + } + } + } +} diff --git a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java index e002e35ce8d..6c65a964a65 100644 --- a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java @@ -59,6 +59,8 @@ import static java.net.StandardProtocolFamily.INET; import static java.net.StandardProtocolFamily.INET6; import static java.net.StandardProtocolFamily.UNIX; +import jdk.internal.event.SocketReadEvent; +import jdk.internal.event.SocketWriteEvent; import sun.net.ConnectionResetException; import sun.net.NetHooks; import sun.net.ext.ExtendedSocketOptions; @@ -401,8 +403,7 @@ class SocketChannelImpl throw new SocketException("Connection reset"); } - @Override - public int read(ByteBuffer buf) throws IOException { + private int implRead(ByteBuffer buf) throws IOException { Objects.requireNonNull(buf); readLock.lock(); @@ -443,8 +444,7 @@ class SocketChannelImpl } } - @Override - public long read(ByteBuffer[] dsts, int offset, int length) + private long implRead(ByteBuffer[] dsts, int offset, int length) throws IOException { Objects.checkFromIndexSize(offset, length, dsts.length); @@ -487,6 +487,31 @@ class SocketChannelImpl } } + @Override + public int read(ByteBuffer buf) throws IOException { + if (!SocketReadEvent.enabled()) { + return implRead(buf); + } + long start = SocketReadEvent.timestamp(); + int nbytes = implRead(buf); + SocketReadEvent.offer(start, nbytes, remoteAddress(), 0); + return nbytes; + } + + + @Override + public long read(ByteBuffer[] dsts, int offset, int length) + throws IOException + { + if (!SocketReadEvent.enabled()) { + return implRead(dsts, offset, length); + } + long start = SocketReadEvent.timestamp(); + long nbytes = implRead(dsts, offset, length); + SocketReadEvent.offer(start, nbytes, remoteAddress(), 0); + return nbytes; + } + /** * Marks the beginning of a write operation that might block. * @@ -528,8 +553,7 @@ class SocketChannelImpl } } - @Override - public int write(ByteBuffer buf) throws IOException { + private int implWrite(ByteBuffer buf) throws IOException { Objects.requireNonNull(buf); writeLock.lock(); try { @@ -557,8 +581,7 @@ class SocketChannelImpl } } - @Override - public long write(ByteBuffer[] srcs, int offset, int length) + private long implWrite(ByteBuffer[] srcs, int offset, int length) throws IOException { Objects.checkFromIndexSize(offset, length, srcs.length); @@ -589,6 +612,30 @@ class SocketChannelImpl } } + @Override + public int write(ByteBuffer buf) throws IOException { + if (!SocketWriteEvent.enabled()) { + return implWrite(buf); + } + long start = SocketWriteEvent.timestamp(); + int nbytes = implWrite(buf); + SocketWriteEvent.offer(start, nbytes, remoteAddress()); + return nbytes; + } + + @Override + public long write(ByteBuffer[] srcs, int offset, int length) + throws IOException + { + if (!SocketWriteEvent.enabled()) { + return implWrite(srcs, offset, length); + } + long start = SocketWriteEvent.timestamp(); + long nbytes = implWrite(srcs, offset, length); + SocketWriteEvent.offer(start, nbytes, remoteAddress()); + return nbytes; + } + /** * Writes a byte of out of band data. */ 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 383feaee73e..8853c60ce45 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/events/EventConfigurations.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/events/EventConfigurations.java @@ -28,8 +28,6 @@ import jdk.jfr.internal.JVMSupport; import jdk.jfr.internal.event.EventConfiguration; 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 = JVMSupport.getConfiguration(FileWriteEvent.class); public static final EventConfiguration FILE_FORCE = JVMSupport.getConfiguration(FileForceEvent.class); diff --git a/src/jdk.jfr/share/classes/jdk/jfr/events/SocketReadEvent.java b/src/jdk.jfr/share/classes/jdk/jfr/events/SocketReadEvent.java index 8daf4726f67..7ef63765eec 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/events/SocketReadEvent.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/events/SocketReadEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -31,17 +31,16 @@ import jdk.jfr.Label; import jdk.jfr.DataAmount; import jdk.jfr.Name; import jdk.jfr.Timespan; +import jdk.jfr.internal.MirrorEvent; import jdk.jfr.internal.Type; @Name(Type.EVENT_NAME_PREFIX + "SocketRead") @Label("Socket Read") @Category("Java Application") @Description("Reading data from a socket") +@MirrorEvent(className = "jdk.internal.event.SocketReadEvent") public final class SocketReadEvent extends AbstractJDKEvent { - // The order of these fields must be the same as the parameters in - // commit(..., String, String, int, long, long, boolean) - @Label("Remote Host") public String host; @@ -64,7 +63,4 @@ public final class SocketReadEvent extends AbstractJDKEvent { @Description("If end of stream was reached") public boolean endOfStream; - public static void commit(long start, long duration, String host, String address, int port, long timeout, long byteRead, boolean endOfStream) { - // Generated - } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/events/SocketWriteEvent.java b/src/jdk.jfr/share/classes/jdk/jfr/events/SocketWriteEvent.java index 1ba40d29146..6e776b8871d 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/events/SocketWriteEvent.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/events/SocketWriteEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 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 @@ -30,17 +30,16 @@ import jdk.jfr.Description; import jdk.jfr.Label; import jdk.jfr.DataAmount; import jdk.jfr.Name; +import jdk.jfr.internal.MirrorEvent; import jdk.jfr.internal.Type; @Name(Type.EVENT_NAME_PREFIX + "SocketWrite") @Label("Socket Write") @Category("Java Application") @Description("Writing data to a socket") +@MirrorEvent(className = "jdk.internal.event.SocketWriteEvent") public final class SocketWriteEvent extends AbstractJDKEvent { - // The order of these fields must be the same as the parameters in - // commit(..., String, String, int, long) - @Label("Remote Host") public String host; @@ -55,7 +54,4 @@ public final class SocketWriteEvent extends AbstractJDKEvent { @DataAmount public long bytesWritten; - public static void commit(long start, long duration, String host, String address, int port, long bytes) { - // Generated - } } 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 9405dfff8e0..6a5981073c5 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 @@ -75,6 +75,8 @@ public final class JDKEvents { ProcessStartEvent.class, SecurityPropertyModificationEvent.class, SecurityProviderServiceEvent.class, + SocketReadEvent.class, + SocketWriteEvent.class, ThreadSleepEvent.class, TLSHandshakeEvent.class, VirtualThreadStartEvent.class, @@ -100,6 +102,8 @@ public final class JDKEvents { jdk.internal.event.ProcessStartEvent.class, jdk.internal.event.SecurityPropertyModificationEvent.class, jdk.internal.event.SecurityProviderServiceEvent.class, + jdk.internal.event.SocketReadEvent.class, + jdk.internal.event.SocketWriteEvent.class, jdk.internal.event.ThreadSleepEvent.class, jdk.internal.event.TLSHandshakeEvent.class, jdk.internal.event.VirtualThreadStartEvent.class, @@ -118,10 +122,7 @@ public final class JDKEvents { FileInputStreamInstrumentor.class, FileOutputStreamInstrumentor.class, RandomAccessFileInstrumentor.class, - FileChannelImplInstrumentor.class, - SocketInputStreamInstrumentor.class, - SocketOutputStreamInstrumentor.class, - SocketChannelImplInstrumentor.class + FileChannelImplInstrumentor.class }; private static final Class[] targetClasses = new Class[instrumentationClasses.length]; diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/SocketChannelImplInstrumentor.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/SocketChannelImplInstrumentor.java deleted file mode 100644 index b911ab1fd50..00000000000 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/SocketChannelImplInstrumentor.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (c) 2013, 2022, 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.io.IOException; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.net.UnixDomainSocketAddress; -import java.nio.ByteBuffer; -import jdk.jfr.events.EventConfigurations; -import jdk.jfr.events.SocketReadEvent; -import jdk.jfr.events.SocketWriteEvent; -import jdk.jfr.internal.event.EventConfiguration; - -/** - * See {@link JITracer} for an explanation of this code. - */ -@JIInstrumentationTarget("sun.nio.ch.SocketChannelImpl") -final class SocketChannelImplInstrumentor { - - private SocketChannelImplInstrumentor() { - } - - @JIInstrumentationMethod - public int read(ByteBuffer dst) throws IOException { - EventConfiguration eventConfiguration = EventConfigurations.SOCKET_READ; - if (!eventConfiguration.isEnabled()) { - return read(dst); - } - int bytesRead = 0; - long start = 0; - try { - start = EventConfiguration.timestamp();; - bytesRead = read(dst); - } finally { - long duration = EventConfiguration.timestamp() - start; - if (eventConfiguration.shouldCommit(duration)) { - SocketAddress remoteAddress = getRemoteAddress(); - if (remoteAddress instanceof InetSocketAddress isa) { - String hostString = isa.getAddress().toString(); - int delimiterIndex = hostString.lastIndexOf('/'); - - String host = hostString.substring(0, delimiterIndex); - String address = hostString.substring(delimiterIndex + 1); - int port = isa.getPort(); - if (bytesRead < 0) { - SocketReadEvent.commit(start, duration, host, address, port, 0, 0L, true); - } else { - SocketReadEvent.commit(start, duration, host, address, port, 0, bytesRead, false); - } - } else { - UnixDomainSocketAddress udsa = (UnixDomainSocketAddress) remoteAddress; - String path = "[" + udsa.getPath().toString() + "]"; - if (bytesRead < 0) { - SocketReadEvent.commit(start, duration, "Unix domain socket", path, 0, 0, 0L, true); - } else { - SocketReadEvent.commit(start, duration, "Unix domain socket", path, 0, 0, bytesRead, false); - } - } - } - } - return bytesRead; - } - - @JIInstrumentationMethod - public long read(ByteBuffer[] dsts, int offset, int length) throws IOException { - EventConfiguration eventConfiguration = EventConfigurations.SOCKET_READ; - if (!eventConfiguration.isEnabled()) { - return read(dsts, offset, length); - } - long bytesRead = 0; - long start = 0; - try { - start = EventConfiguration.timestamp(); - bytesRead = read(dsts, offset, length); - } finally { - long duration = EventConfiguration.timestamp() - start; - if (eventConfiguration.shouldCommit(duration)) { - SocketAddress remoteAddress = getRemoteAddress(); - if (remoteAddress instanceof InetSocketAddress isa) { - String hostString = isa.getAddress().toString(); - int delimiterIndex = hostString.lastIndexOf('/'); - - String host = hostString.substring(0, delimiterIndex); - String address = hostString.substring(delimiterIndex + 1); - int port = isa.getPort(); - if (bytesRead < 0) { - SocketReadEvent.commit(start, duration, host, address, port, 0, 0L, true); - } else { - SocketReadEvent.commit(start, duration, host, address, port, 0, bytesRead, false); - } - } else { - UnixDomainSocketAddress udsa = (UnixDomainSocketAddress) remoteAddress; - String path = "[" + udsa.getPath().toString() + "]"; - if (bytesRead < 0) { - SocketReadEvent.commit(start, duration, "Unix domain socket", path, 0, 0, 0L, true); - } else { - SocketReadEvent.commit(start, duration, "Unix domain socket", path, 0, 0, bytesRead, false); - } - } - } - } - return bytesRead; - } - - @JIInstrumentationMethod - public int write(ByteBuffer buf) throws IOException { - EventConfiguration eventConfiguration = EventConfigurations.SOCKET_WRITE; - if (!eventConfiguration.isEnabled()) { - return write(buf); - } - int bytesWritten = 0; - long start = 0; - try { - start = EventConfiguration.timestamp(); - bytesWritten = write(buf); - } finally { - long duration = EventConfiguration.timestamp() - start; - if (eventConfiguration.shouldCommit(duration)) { - long bytes = bytesWritten < 0 ? 0 : bytesWritten; - SocketAddress remoteAddress = getRemoteAddress(); - if (remoteAddress instanceof InetSocketAddress isa) { - String hostString = isa.getAddress().toString(); - int delimiterIndex = hostString.lastIndexOf('/'); - - String host = hostString.substring(0, delimiterIndex); - String address = hostString.substring(delimiterIndex + 1); - int port = isa.getPort(); - SocketWriteEvent.commit(start, duration, host, address, port, bytes); - } else { - UnixDomainSocketAddress udsa = (UnixDomainSocketAddress) remoteAddress; - String path = "[" + udsa.getPath().toString() + "]"; - SocketWriteEvent.commit(start, duration, "Unix domain socket", path, 0, bytes); - } - } - } - return bytesWritten; - } - - public SocketAddress getRemoteAddress() throws IOException { - // gets replaced by call to instrumented class - return null; - } - - @JIInstrumentationMethod - public long write(ByteBuffer[] srcs, int offset, int length) throws IOException { - EventConfiguration eventConfiguration = EventConfigurations.SOCKET_WRITE; - if (!eventConfiguration.isEnabled()) { - return write(srcs, offset, length); - } - long bytesWritten = 0; - long start = 0; - try { - start = EventConfiguration.timestamp(); - bytesWritten = write(srcs, offset, length); - } finally { - long duration = EventConfiguration.timestamp() - start; - if (eventConfiguration.shouldCommit(duration)) { - long bytes = bytesWritten < 0 ? 0 : bytesWritten; - SocketAddress remoteAddress = getRemoteAddress(); - if (remoteAddress instanceof InetSocketAddress isa) { - String hostString = isa.getAddress().toString(); - int delimiterIndex = hostString.lastIndexOf('/'); - - String host = hostString.substring(0, delimiterIndex); - String address = hostString.substring(delimiterIndex + 1); - int port = isa.getPort(); - SocketWriteEvent.commit(start, duration, host, address, port, bytes); - } else { - UnixDomainSocketAddress udsa = (UnixDomainSocketAddress) remoteAddress; - String path = "[" + udsa.getPath().toString() + "]"; - SocketWriteEvent.commit(start, duration, "Unix domain socket", path, 0, bytes); - } - } - } - return bytesWritten; - } -} diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/SocketInputStreamInstrumentor.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/SocketInputStreamInstrumentor.java deleted file mode 100644 index f0ced1df9be..00000000000 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/SocketInputStreamInstrumentor.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2013, 2022, 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.io.IOException; -import java.net.InetAddress; -import java.net.Socket; - -import jdk.jfr.events.EventConfigurations; -import jdk.jfr.events.SocketReadEvent; -import jdk.jfr.internal.event.EventConfiguration; - -/** - * See {@link JITracer} for an explanation of this code. - */ -@JIInstrumentationTarget("java.net.Socket$SocketInputStream") -final class SocketInputStreamInstrumentor { - - private SocketInputStreamInstrumentor() { - } - - @JIInstrumentationMethod - public int read(byte b[], int off, int length) throws IOException { - EventConfiguration eventConfiguration = EventConfigurations.SOCKET_READ; - if (!eventConfiguration.isEnabled()) { - return read(b, off, length); - } - int bytesRead = 0; - long start = 0; - try { - start = EventConfiguration.timestamp(); - bytesRead = read(b, off, length); - } finally { - long duration = EventConfiguration.timestamp() - start; - if (eventConfiguration.shouldCommit(duration)) { - InetAddress remote = parent.getInetAddress(); - String host = remote.getHostName(); - String address = remote.getHostAddress(); - int port = parent.getPort(); - int timeout = parent.getSoTimeout(); - if (bytesRead < 0) { - SocketReadEvent.commit(start, duration, host, address, port, timeout, 0L, true); - } else { - SocketReadEvent.commit(start, duration, host, address, port, timeout, bytesRead, false); - } - } - } - return bytesRead; - } - - // private field in java.net.Socket$SocketInputStream - private Socket parent; -} diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/SocketOutputStreamInstrumentor.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/SocketOutputStreamInstrumentor.java deleted file mode 100644 index d2752b2bbc1..00000000000 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/instrument/SocketOutputStreamInstrumentor.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2013, 2022, 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.io.IOException; -import java.net.InetAddress; -import java.net.Socket; - -import jdk.jfr.events.EventConfigurations; -import jdk.jfr.events.SocketWriteEvent; -import jdk.jfr.internal.event.EventConfiguration; - -/** - * See {@link JITracer} for an explanation of this code. - */ -@JIInstrumentationTarget("java.net.Socket$SocketOutputStream") -final class SocketOutputStreamInstrumentor { - - private SocketOutputStreamInstrumentor() { - } - - @JIInstrumentationMethod - public void write(byte b[], int off, int len) throws IOException { - EventConfiguration eventConfiguration = EventConfigurations.SOCKET_WRITE; - if (!eventConfiguration.isEnabled()) { - write(b, off, len); - return; - } - int bytesWritten = 0; - long start = 0; - try { - start = EventConfiguration.timestamp(); - write(b, off, len); - bytesWritten = len; - } finally { - long duration = EventConfiguration.timestamp() - start; - if (eventConfiguration.shouldCommit(duration)) { - InetAddress remote = parent.getInetAddress(); - SocketWriteEvent.commit( - start, - duration, - remote.getHostName(), - remote.getHostAddress(), - parent.getPort(), - bytesWritten); - } - } - } - - // private field in java.net.Socket$SocketOutputStream - private Socket parent; -} diff --git a/test/micro/org/openjdk/bench/java/net/SocketEventOverhead.java b/test/micro/org/openjdk/bench/java/net/SocketEventOverhead.java new file mode 100644 index 00000000000..e54b42cd6fe --- /dev/null +++ b/test/micro/org/openjdk/bench/java/net/SocketEventOverhead.java @@ -0,0 +1,167 @@ +/* + * 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 org.openjdk.bench.java.net; + +import jdk.internal.event.SocketReadEvent; +import jdk.internal.event.SocketWriteEvent; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.concurrent.TimeUnit; + +/** + * Test the overhead of the handling jfr events SocketReadEvent and + * SocketWriteEvent without the latencies of the actual I/O code. + */ +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS) +@State(Scope.Thread) +public class SocketEventOverhead { + + @Fork(value = 1, jvmArgsAppend = { + "--add-exports", + "java.base/jdk.internal.event=ALL-UNNAMED" }) + @Benchmark + public int socketWriteJFRDisabled(SkeletonFixture fixture) { + return fixture.write(); + } + + @Fork(value = 1, jvmArgsAppend = { + "--add-exports", + "java.base/jdk.internal.event=ALL-UNNAMED", + "-XX:StartFlightRecording:jdk.SocketWrite#enabled=false"}) + @Benchmark + public int socketWriteJFREnabledEventDisabled(SkeletonFixture fixture) { + return fixture.write(); + } + + @Fork(value = 1, jvmArgsAppend = { + "--add-exports", + "java.base/jdk.internal.event=ALL-UNNAMED", + "-XX:StartFlightRecording:jdk.SocketWrite#enabled=true,jdk.SocketWrite#threshold=1s"}) + @Benchmark + public int socketWriteJFREnabledEventNotEmitted(SkeletonFixture fixture) { + return fixture.write(); + } + + @Fork(value = 1, jvmArgsAppend = { + "--add-exports","java.base/jdk.internal.event=ALL-UNNAMED", + "-XX:StartFlightRecording:jdk.SocketWrite#enabled=true,jdk.SocketWrite#threshold=0ms,disk=false,jdk.SocketWrite#stackTrace=false"}) + @Benchmark + public int socketWriteJFREnabledEventEmitted(SkeletonFixture fixture) { + return fixture.write(); + } + + @Fork(value = 1, jvmArgsAppend = { + "--add-exports", + "java.base/jdk.internal.event=ALL-UNNAMED" }) + @Benchmark + public int socketReadJFRDisabled(SkeletonFixture fixture) { + return fixture.read(); + } + + @Fork(value = 1, jvmArgsAppend = { + "--add-exports", + "java.base/jdk.internal.event=ALL-UNNAMED", + "-XX:StartFlightRecording:jdk.SocketRead#enabled=false"}) + @Benchmark + public int socketReadJFREnabledEventDisabled(SkeletonFixture fixture) { + return fixture.read(); + } + + @Fork(value = 1, jvmArgsAppend = { + "--add-exports", + "java.base/jdk.internal.event=ALL-UNNAMED", + "-XX:StartFlightRecording:jdk.SocketRead#enabled=true,jdk.SocketRead#threshold=1s"}) + @Benchmark + public int socketReadJFREnabledEventNotEmitted(SkeletonFixture fixture) { + return fixture.read(); + } + + @Fork(value = 1, jvmArgsAppend = { + "--add-exports","java.base/jdk.internal.event=ALL-UNNAMED", + "-XX:StartFlightRecording:jdk.SocketRead#enabled=true,jdk.SocketRead#threshold=0ms,disk=false,jdk.SocketRead#stackTrace=false"}) + @Benchmark + public int socketReadJFREnabledEventEmitted(SkeletonFixture fixture) { + return fixture.read(); + } + + /** + * Fixture with fake read/write operations that have only the JFR event + * boilerplate code for managing jfr events. No actual transfer is done + * to eliminate the I/O portion and measure the overhead of JFR event + * handling in it's various states. + */ + @State(Scope.Thread) + public static class SkeletonFixture { + + private final InetSocketAddress remote = new InetSocketAddress("localhost",5000); + + public SocketAddress getRemoteAddress() { + return remote; + } + + public int write() { + if (! SocketWriteEvent.enabled()) { + return write0(); + } + int nbytes = 0; + long start = SocketWriteEvent.timestamp(); + try { + nbytes = write0(); + } finally { + SocketWriteEvent.offer(start, nbytes, getRemoteAddress()); + } + return nbytes; + } + + private int write0() { + return 1024; + } + + public int read() { + if (! SocketReadEvent.enabled()) { + return read0(); + } + int nbytes = 0; + long start = SocketReadEvent.timestamp(); + try { + nbytes = read0(); + } finally { + SocketReadEvent.offer(start, nbytes, getRemoteAddress(), 0); + } + return nbytes; + } + + private int read0() { + return 1024; + } + } +}