From e801770c68f0915de21076c22690cd4155329bb5 Mon Sep 17 00:00:00 2001 From: Tim Prinzing Date: Tue, 26 Mar 2024 19:33:44 -0500 Subject: [PATCH 01/11] 8310996: Add JFR event for connect operations Initial implementation for SocketChannel. --- .../internal/event/SocketConnectEvent.java | 137 ++++++++++++++++++ .../classes/sun/nio/ch/SocketChannelImpl.java | 19 ++- .../jdk/jfr/events/SocketConnectEvent.java | 53 +++++++ .../jdk/jfr/internal/MirrorEvents.java | 4 +- .../jfr/internal/instrument/JDKEvents.java | 5 +- 5 files changed, 212 insertions(+), 6 deletions(-) create mode 100644 src/java.base/share/classes/jdk/internal/event/SocketConnectEvent.java create mode 100644 src/jdk.jfr/share/classes/jdk/jfr/events/SocketConnectEvent.java diff --git a/src/java.base/share/classes/jdk/internal/event/SocketConnectEvent.java b/src/java.base/share/classes/jdk/internal/event/SocketConnectEvent.java new file mode 100644 index 0000000000000..8138df4cb0fd8 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/event/SocketConnectEvent.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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 connect operations. This event is mirrored in + * {@code jdk.jfr.events.SocketConnectEvent } 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, boolean)} method + * must be the same as the order of the fields. + */ +public class SocketConnectEvent 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 boolean connected; + + /** + * Actually commit an event. The implementation 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 connection + * @param address remote address of the connection + * @param port remote port of the connection + * @param connected is the connection connected + */ + public static void commit(long start, long duration, String host, String address, int port, boolean connected) { + // 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 event is 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 #emit(long, long, boolean, SocketAddress)}. + * + * @param start the start time + * @param connected is the connection connected + * @param remote the address of the remote socket + */ + public static void offer(long start, boolean connected, SocketAddress remote) { + long duration = timestamp() - start; + if (shouldCommit(duration)) { + emit(start, duration, connected, remote); + } + } + + /** + * Helper method to perform a common task of getting event data ready and + * then emitting the event by calling + * {@link #commit(long, long, String, String, int, boolean)}. + * + * @param start the start time + * @param duration the duration + * @param connected is the connection connected + * @param remote the address of the remote socket + */ + public static void emit(long start, long duration, boolean connected, SocketAddress remote) { + if (remote instanceof InetSocketAddress isa) { + commit(start, duration, isa.getHostString(), isa.getAddress().getHostAddress(), isa.getPort(), connected); + } else if (remote instanceof UnixDomainSocketAddress udsa) { + String path = "[" + udsa.getPath().toString() + "]"; + commit(start, duration, "Unix domain socket", path, 0, connected); + } + } +} 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 6c65a964a65f8..dcb84a147949e 100644 --- a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -59,6 +59,7 @@ import static java.net.StandardProtocolFamily.INET6; import static java.net.StandardProtocolFamily.UNIX; +import jdk.internal.event.SocketConnectEvent; import jdk.internal.event.SocketReadEvent; import jdk.internal.event.SocketWriteEvent; import sun.net.ConnectionResetException; @@ -947,9 +948,7 @@ private SocketAddress checkRemote(SocketAddress sa) { } } - @Override - public boolean connect(SocketAddress remote) throws IOException { - SocketAddress sa = checkRemote(remote); + private boolean implConnect(SocketAddress sa) throws IOException { try { readLock.lock(); try { @@ -994,6 +993,18 @@ public boolean connect(SocketAddress remote) throws IOException { } } + @Override + public boolean connect(SocketAddress remote) throws IOException { + SocketAddress sa = checkRemote(remote); + if (!SocketConnectEvent.enabled()) { + return implConnect(sa); + } + long start = SocketConnectEvent.timestamp(); + boolean connected = implConnect(sa); + SocketConnectEvent.offer(start, connected, sa); + return connected; + } + /** * Marks the beginning of a finishConnect operation that might block. * diff --git a/src/jdk.jfr/share/classes/jdk/jfr/events/SocketConnectEvent.java b/src/jdk.jfr/share/classes/jdk/jfr/events/SocketConnectEvent.java new file mode 100644 index 0000000000000..5426613063f58 --- /dev/null +++ b/src/jdk.jfr/share/classes/jdk/jfr/events/SocketConnectEvent.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.internal.MirrorEvent; +import jdk.jfr.internal.Type; + +@Name(Type.EVENT_NAME_PREFIX + "SocketConnect") +@Label("Socket Connect") +@Category("Java Application") +@Description("Connecting a socket") +@MirrorEvent(className = "jdk.internal.event.SocketConnectEvent") +public class SocketConnectEvent extends AbstractJDKEvent { + + @Label("Remote Host") + public String host; + + @Label("Remote Address") + public String address; + + @Label("Remote Port") + public int port; + + @Label("Connected") + public boolean connected; +} diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/MirrorEvents.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/MirrorEvents.java index 7c74bb41f4acd..08565f12adbec 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/MirrorEvents.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MirrorEvents.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,6 +36,7 @@ import jdk.jfr.events.SecurityPropertyModificationEvent; import jdk.jfr.events.SecurityProviderServiceEvent; import jdk.jfr.events.SerializationMisdeclarationEvent; +import jdk.jfr.events.SocketConnectEvent; import jdk.jfr.events.SocketReadEvent; import jdk.jfr.events.SocketWriteEvent; import jdk.jfr.events.TLSHandshakeEvent; @@ -54,6 +55,7 @@ public final class MirrorEvents { SecurityPropertyModificationEvent.class, SecurityProviderServiceEvent.class, SerializationMisdeclarationEvent.class, + SocketConnectEvent.class, SocketReadEvent.class, SocketWriteEvent.class, ThreadSleepEvent.class, 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 10ab2094d5ba0..e30efa09f59d6 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,6 +44,7 @@ import jdk.jfr.events.FileReadEvent; import jdk.jfr.events.FileWriteEvent; import jdk.jfr.events.InitialSecurityPropertyEvent; +import jdk.jfr.events.SocketConnectEvent; import jdk.jfr.events.SocketReadEvent; import jdk.jfr.events.SocketWriteEvent; @@ -62,6 +63,7 @@ public final class JDKEvents { FileForceEvent.class, FileReadEvent.class, FileWriteEvent.class, + SocketConnectEvent.class, SocketReadEvent.class, SocketWriteEvent.class, ActiveSettingEvent.class, @@ -76,6 +78,7 @@ public final class JDKEvents { jdk.internal.event.SecurityPropertyModificationEvent.class, jdk.internal.event.SecurityProviderServiceEvent.class, jdk.internal.event.SerializationMisdeclarationEvent.class, + jdk.internal.event.SocketConnectEvent.class, jdk.internal.event.SocketReadEvent.class, jdk.internal.event.SocketWriteEvent.class, jdk.internal.event.ThreadSleepEvent.class, From 9e1cce5423ab30f163357dfe54a8899ec92ed28e Mon Sep 17 00:00:00 2001 From: Tim Prinzing Date: Tue, 8 Oct 2024 10:05:45 -0500 Subject: [PATCH 02/11] added tests and support for sockets. --- .../share/classes/sun/nio/ch/NioSocketImpl.java | 12 ++++++++++-- src/jdk.jfr/share/conf/jfr/default.jfc | 6 ++++++ src/jdk.jfr/share/conf/jfr/profile.jfc | 6 ++++++ test/jdk/jdk/jfr/event/io/IOEvent.java | 11 ++++++++--- .../jdk/jfr/event/io/TestSocketChannelEvents.java | 4 +++- test/jdk/jdk/jfr/event/io/TestSocketEvents.java | 4 +++- test/lib/jdk/test/lib/jfr/EventNames.java | 3 ++- 7 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java index b9885a36fa896..3270ae45e065c 100644 --- a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -53,6 +53,7 @@ import jdk.internal.access.JavaIOFileDescriptorAccess; import jdk.internal.access.SharedSecrets; +import jdk.internal.event.SocketConnectEvent; import jdk.internal.ref.CleanerFactory; import sun.net.ConnectionResetException; import sun.net.NetHooks; @@ -573,10 +574,11 @@ protected void connect(SocketAddress remote, int millis) throws IOException { int port = isa.getPort(); ReentrantLock connectLock = readLock; + boolean connected = false; + long start = SocketConnectEvent.timestamp(); try { connectLock.lock(); try { - boolean connected = false; FileDescriptor fd = beginConnect(address, port); try { configureNonBlockingIfNeeded(fd, millis > 0); @@ -614,6 +616,12 @@ protected void connect(SocketAddress remote, int millis) throws IOException { throw SocketExceptions.of(ioe, isa); } } + if (SocketConnectEvent.enabled()) { + long duration = SocketConnectEvent.timestamp() - start; + if (SocketConnectEvent.shouldCommit(duration)) { + SocketConnectEvent.commit(start, duration, isa.getHostString(), address.getHostAddress(), port, connected); + } + } } @Override diff --git a/src/jdk.jfr/share/conf/jfr/default.jfc b/src/jdk.jfr/share/conf/jfr/default.jfc index 1ea936ae20d5d..52167d391ab74 100644 --- a/src/jdk.jfr/share/conf/jfr/default.jfc +++ b/src/jdk.jfr/share/conf/jfr/default.jfc @@ -744,6 +744,12 @@ 20 ms + + true + true + 20 ms + + true true diff --git a/src/jdk.jfr/share/conf/jfr/profile.jfc b/src/jdk.jfr/share/conf/jfr/profile.jfc index 0b018d33058ce..6ee14fe5b4344 100644 --- a/src/jdk.jfr/share/conf/jfr/profile.jfc +++ b/src/jdk.jfr/share/conf/jfr/profile.jfc @@ -744,6 +744,12 @@ 10 ms + + true + true + 10 ms + + true true diff --git a/test/jdk/jdk/jfr/event/io/IOEvent.java b/test/jdk/jdk/jfr/event/io/IOEvent.java index a8392e4d35a20..c71c188d11173 100644 --- a/test/jdk/jdk/jfr/event/io/IOEvent.java +++ b/test/jdk/jdk/jfr/event/io/IOEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -73,13 +73,14 @@ public int hashCode() { public static final String EVENT_FILE_FORCE = EventNames.FileForce; public static final String EVENT_FILE_READ = EventNames.FileRead; public static final String EVENT_FILE_WRITE = EventNames.FileWrite; + public static final String EVENT_SOCKET_CONNECT = EventNames.SocketConnect; public static final String EVENT_SOCKET_READ = EventNames.SocketRead; public static final String EVENT_SOCKET_WRITE = EventNames.SocketWrite; - public enum EventType { UnknownEvent, FileForce, FileRead, FileWrite, SocketRead, SocketWrite } + public enum EventType { UnknownEvent, FileForce, FileRead, FileWrite, SocketConnect, SocketRead, SocketWrite } private static final String[] eventPaths = { - EVENT_UNKNOWN, EVENT_FILE_FORCE, EVENT_FILE_READ, EVENT_FILE_WRITE, EVENT_SOCKET_READ, EVENT_SOCKET_WRITE + EVENT_UNKNOWN, EVENT_FILE_FORCE, EVENT_FILE_READ, EVENT_FILE_WRITE, EVENT_SOCKET_CONNECT, EVENT_SOCKET_READ, EVENT_SOCKET_WRITE }; public static boolean isWriteEvent(EventType eventType) { @@ -94,6 +95,10 @@ public static boolean isFileEvent(EventType eventType) { return (eventType == EventType.FileForce || eventType == EventType.FileWrite || eventType == EventType.FileRead); } + public static IOEvent createSocketConnectEvent(Socket s) { + return new IOEvent(Thread.currentThread().getName(), EventType.SocketConnect, 0, getAddress(s), false); + } + public static IOEvent createSocketWriteEvent(long size, Socket s) { if (size < 0) { size = 0; diff --git a/test/jdk/jdk/jfr/event/io/TestSocketChannelEvents.java b/test/jdk/jdk/jfr/event/io/TestSocketChannelEvents.java index fc045e55caa6e..24e9b8daedce1 100644 --- a/test/jdk/jdk/jfr/event/io/TestSocketChannelEvents.java +++ b/test/jdk/jdk/jfr/event/io/TestSocketChannelEvents.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -66,6 +66,7 @@ public static void main(String[] args) throws Throwable { public void test() throws Throwable { try (Recording recording = new Recording()) { try (ServerSocketChannel ssc = ServerSocketChannel.open()) { + recording.enable(IOEvent.EVENT_SOCKET_CONNECT).withThreshold(Duration.ofMillis(0)); recording.enable(IOEvent.EVENT_SOCKET_READ).withThreshold(Duration.ofMillis(0)); recording.enable(IOEvent.EVENT_SOCKET_WRITE).withThreshold(Duration.ofMillis(0)); recording.start(); @@ -102,6 +103,7 @@ public void xrun() throws IOException { readerThread.start(); try (SocketChannel sc = SocketChannel.open(ssc.getLocalAddress())) { + addExpectedEvent(IOEvent.createSocketConnectEvent(sc.socket())); ByteBuffer bufA = ByteBuffer.allocateDirect(bufSizeA); ByteBuffer bufB = ByteBuffer.allocateDirect(bufSizeB); for (int i = 0; i < bufSizeA; ++i) { diff --git a/test/jdk/jdk/jfr/event/io/TestSocketEvents.java b/test/jdk/jdk/jfr/event/io/TestSocketEvents.java index d73c5010cf7d0..9b65b25757341 100644 --- a/test/jdk/jdk/jfr/event/io/TestSocketEvents.java +++ b/test/jdk/jdk/jfr/event/io/TestSocketEvents.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -68,6 +68,7 @@ public static void main(String[] args) throws Throwable { private void test() throws Throwable { try (Recording recording = new Recording()) { try (ServerSocket ss = new ServerSocket()) { + recording.enable(IOEvent.EVENT_SOCKET_CONNECT).withThreshold(Duration.ofMillis(0)); recording.enable(IOEvent.EVENT_SOCKET_READ).withThreshold(Duration.ofMillis(0)); recording.enable(IOEvent.EVENT_SOCKET_WRITE).withThreshold(Duration.ofMillis(0)); recording.start(); @@ -104,6 +105,7 @@ public void xrun() throws IOException { try (Socket s = new Socket()) { s.connect(ss.getLocalSocketAddress()); + addExpectedEvent(IOEvent.createSocketConnectEvent(s)); try (OutputStream os = s.getOutputStream()) { os.write(writeInt); addExpectedEvent(IOEvent.createSocketWriteEvent(1, s)); diff --git a/test/lib/jdk/test/lib/jfr/EventNames.java b/test/lib/jdk/test/lib/jfr/EventNames.java index 3799af54804a1..0c3ac9ce54175 100644 --- a/test/lib/jdk/test/lib/jfr/EventNames.java +++ b/test/lib/jdk/test/lib/jfr/EventNames.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -195,6 +195,7 @@ 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 SocketConnect = PREFIX + "SocketConnect"; public static final String SocketRead = PREFIX + "SocketRead"; public static final String SocketWrite = PREFIX + "SocketWrite"; public static final String ExceptionStatistics = PREFIX + "ExceptionStatistics"; From 06a31206622584c55e100f22d7111072111cbd14 Mon Sep 17 00:00:00 2001 From: Tim Prinzing Date: Tue, 8 Oct 2024 18:59:09 -0500 Subject: [PATCH 03/11] fix merge --- .../share/classes/jdk/jfr/events/SocketConnectEvent.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/jdk.jfr/share/classes/jdk/jfr/events/SocketConnectEvent.java b/src/jdk.jfr/share/classes/jdk/jfr/events/SocketConnectEvent.java index 5426613063f58..1a2388cf6819f 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/events/SocketConnectEvent.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/events/SocketConnectEvent.java @@ -36,8 +36,7 @@ @Label("Socket Connect") @Category("Java Application") @Description("Connecting a socket") -@MirrorEvent(className = "jdk.internal.event.SocketConnectEvent") -public class SocketConnectEvent extends AbstractJDKEvent { +public class SocketConnectEvent extends MirrorEvent { @Label("Remote Host") public String host; From 05227c9dac541bf6c63d039a00c290e948bc9997 Mon Sep 17 00:00:00 2001 From: Tim Prinzing Date: Sun, 13 Oct 2024 17:55:08 -0500 Subject: [PATCH 04/11] fix default settings --- src/jdk.jfr/share/conf/jfr/default.jfc | 2 +- src/jdk.jfr/share/conf/jfr/profile.jfc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jdk.jfr/share/conf/jfr/default.jfc b/src/jdk.jfr/share/conf/jfr/default.jfc index 65049a76d209a..49e35a5b3af8e 100644 --- a/src/jdk.jfr/share/conf/jfr/default.jfc +++ b/src/jdk.jfr/share/conf/jfr/default.jfc @@ -737,7 +737,7 @@ true true - 20 ms + 20 ms diff --git a/src/jdk.jfr/share/conf/jfr/profile.jfc b/src/jdk.jfr/share/conf/jfr/profile.jfc index a63ae63a6f520..5f8981922311c 100644 --- a/src/jdk.jfr/share/conf/jfr/profile.jfc +++ b/src/jdk.jfr/share/conf/jfr/profile.jfc @@ -737,7 +737,7 @@ true true - 10 ms + 10 ms From 7113bd7505701648f73084f15a62ed9111b6d79a Mon Sep 17 00:00:00 2001 From: Tim Prinzing Date: Mon, 4 Nov 2024 21:16:47 -0600 Subject: [PATCH 05/11] Added support for connection failure and non-blocking connections. If an exception is thrown while attempting a connection, the message from the exception is stored in the event. The start time of the initial connect call is stored and used when a finishConnect call is successful or an exception is thrown. --- .../internal/event/SocketConnectEvent.java | 58 ++++++++++++------- .../classes/sun/nio/ch/NioSocketImpl.java | 13 +++-- .../classes/sun/nio/ch/SocketChannelImpl.java | 49 ++++++++++------ .../jdk/jfr/events/SocketConnectEvent.java | 4 +- 4 files changed, 77 insertions(+), 47 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/event/SocketConnectEvent.java b/src/java.base/share/classes/jdk/internal/event/SocketConnectEvent.java index 8138df4cb0fd8..e16c360e3b1dc 100644 --- a/src/java.base/share/classes/jdk/internal/event/SocketConnectEvent.java +++ b/src/java.base/share/classes/jdk/internal/event/SocketConnectEvent.java @@ -24,6 +24,7 @@ */ package jdk.internal.event; +import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.UnixDomainSocketAddress; @@ -43,7 +44,7 @@ public class SocketConnectEvent extends Event { public String host; public String address; public int port; - public boolean connected; + public String exceptionMessage; /** * Actually commit an event. The implementation is generated automatically. @@ -55,9 +56,9 @@ public class SocketConnectEvent extends Event { * @param host remote host of the connection * @param address remote address of the connection * @param port remote port of the connection - * @param connected is the connection connected + * @param exceptionMessage message from exception if failed */ - public static void commit(long start, long duration, String host, String address, int port, boolean connected) { + public static void commit(long start, long duration, String host, String address, int port, String exceptionMessage) { // Generated by JFR } @@ -97,41 +98,58 @@ public static long timestamp() { return 0L; } + static String exceptionToMessage(IOException ex) { + if (ex == null) { + return null; + } + String m = ex.getMessage(); + return m != null ? m : ex.toString(); + } + /** * 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 #emit(long, long, boolean, SocketAddress)}. + * {@link #commit(long, long, String, String, int, String)}. * * @param start the start time - * @param connected is the connection connected * @param remote the address of the remote socket + * @param thrown the exception thrown or null if succeeded */ - public static void offer(long start, boolean connected, SocketAddress remote) { + public static void offer(long start, SocketAddress remote, IOException thrown) { long duration = timestamp() - start; if (shouldCommit(duration)) { - emit(start, duration, connected, remote); + String msg = exceptionToMessage(thrown); + if (remote instanceof InetSocketAddress isa) { + commit(start, duration, isa.getHostString(), isa.getAddress().getHostAddress(), isa.getPort(), msg); + } else if (remote instanceof UnixDomainSocketAddress udsa) { + String path = "[" + udsa.getPath().toString() + "]"; + commit(start, duration, "Unix domain socket", path, 0, msg); + } } } /** - * Helper method to perform a common task of getting event data ready and - * then emitting the event by calling - * {@link #commit(long, long, String, String, int, boolean)}. + * 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 + * {@code commit(long, long, String, String, int, long)} * - * @param start the start time - * @param duration the duration - * @param connected is the connection connected - * @param remote the address of the remote socket + * @param start timestamp of the start of the operation + * @param host remote host of the connection + * @param address remote address of the connection + * @param port remote port of the connection + * @param thrown the exception thrown or null if succeeded */ - public static void emit(long start, long duration, boolean connected, SocketAddress remote) { - if (remote instanceof InetSocketAddress isa) { - commit(start, duration, isa.getHostString(), isa.getAddress().getHostAddress(), isa.getPort(), connected); - } else if (remote instanceof UnixDomainSocketAddress udsa) { - String path = "[" + udsa.getPath().toString() + "]"; - commit(start, duration, "Unix domain socket", path, 0, connected); + public static void offer(long start, String host, String address, int port, IOException thrown) { + long duration = timestamp() - start; + if (shouldCommit(duration)) { + commit(start, duration, host, address, port, exceptionToMessage(thrown)); } } + } diff --git a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java index 3270ae45e065c..5a1731888bfbf 100644 --- a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java @@ -576,6 +576,7 @@ protected void connect(SocketAddress remote, int millis) throws IOException { ReentrantLock connectLock = readLock; boolean connected = false; long start = SocketConnectEvent.timestamp(); + IOException thrown = null; try { connectLock.lock(); try { @@ -610,16 +611,16 @@ protected void connect(SocketAddress remote, int millis) throws IOException { } } catch (IOException ioe) { close(); + thrown = ioe; if (ioe instanceof InterruptedIOException) { throw ioe; } else { - throw SocketExceptions.of(ioe, isa); + thrown = SocketExceptions.of(ioe, isa); + throw thrown; } - } - if (SocketConnectEvent.enabled()) { - long duration = SocketConnectEvent.timestamp() - start; - if (SocketConnectEvent.shouldCommit(duration)) { - SocketConnectEvent.commit(start, duration, isa.getHostString(), address.getHostAddress(), port, connected); + } finally { + if (SocketConnectEvent.enabled()) { + SocketConnectEvent.offer(start, isa.getHostString(), address.getHostAddress(), port, thrown); } } } 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 f0be3a46b081e..1e69794b961d2 100644 --- a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java @@ -133,6 +133,9 @@ class SocketChannelImpl // preserve the semantics of blocking operations. private volatile boolean forcedNonBlocking; + // timestamp for socket connect event + private long connectStartTimestamp; + // -- End of fields protected by stateLock SocketChannelImpl(SelectorProvider sp) throws IOException { @@ -948,15 +951,20 @@ private SocketAddress checkRemote(SocketAddress sa) { } } - private boolean implConnect(SocketAddress sa) throws IOException { + @Override + public boolean connect(SocketAddress remote) throws IOException { + boolean connected = false; + IOException thrown = null; + long start = SocketConnectEvent.timestamp(); + SocketAddress sa = checkRemote(remote); try { readLock.lock(); try { writeLock.lock(); try { + connectStartTimestamp = start; ensureOpen(); boolean blocking = isBlocking(); - boolean connected = false; try { beginConnect(blocking, sa); configureSocketNonBlockingIfVirtualThread(); @@ -990,20 +998,13 @@ private boolean implConnect(SocketAddress sa) throws IOException { } catch (IOException ioe) { // connect failed, close the channel close(); - throw SocketExceptions.of(ioe, sa); - } - } - - @Override - public boolean connect(SocketAddress remote) throws IOException { - SocketAddress sa = checkRemote(remote); - if (!SocketConnectEvent.enabled()) { - return implConnect(sa); + thrown = SocketExceptions.of(ioe, sa); + throw thrown; + } finally { + if (SocketConnectEvent.enabled() && (connected || thrown != null)) { + SocketConnectEvent.offer(start, sa, thrown); + } } - long start = SocketConnectEvent.timestamp(); - boolean connected = implConnect(sa); - SocketConnectEvent.offer(start, connected, sa); - return connected; } /** @@ -1056,18 +1057,23 @@ private void endFinishConnect(boolean blocking, boolean completed) @Override public boolean finishConnect() throws IOException { + boolean connected = false; + IOException thrown = null; + long start = 0; try { readLock.lock(); + start = connectStartTimestamp; try { writeLock.lock(); try { // no-op if already connected - if (isConnected()) - return true; + if (isConnected()) { + connected = true; + return connected; + } ensureOpen(); boolean blocking = isBlocking(); - boolean connected = false; try { beginFinishConnect(blocking); boolean polled = Net.pollConnectNow(fd); @@ -1092,7 +1098,12 @@ public boolean finishConnect() throws IOException { } catch (IOException ioe) { // connect failed, close the channel close(); - throw SocketExceptions.of(ioe, remoteAddress); + thrown = SocketExceptions.of(ioe, remoteAddress); + throw thrown; + } finally { + if (SocketConnectEvent.enabled() && (connected || thrown != null)) { + SocketConnectEvent.offer(start, remoteAddress, thrown); + } } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/events/SocketConnectEvent.java b/src/jdk.jfr/share/classes/jdk/jfr/events/SocketConnectEvent.java index 1a2388cf6819f..48f1e64b059d0 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/events/SocketConnectEvent.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/events/SocketConnectEvent.java @@ -47,6 +47,6 @@ public class SocketConnectEvent extends MirrorEvent { @Label("Remote Port") public int port; - @Label("Connected") - public boolean connected; + @Label("Exception Message") + public String exceptionMessage; } From a8898ffc1dab3b1c8e4c6a47263f3582806c5372 Mon Sep 17 00:00:00 2001 From: Tim Prinzing Date: Tue, 5 Nov 2024 10:43:01 -0600 Subject: [PATCH 06/11] improved exception names and handling --- .../internal/event/SocketConnectEvent.java | 7 +++--- .../classes/sun/nio/ch/NioSocketImpl.java | 22 +++++++++---------- .../classes/sun/nio/ch/SocketChannelImpl.java | 20 ++++++++--------- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/event/SocketConnectEvent.java b/src/java.base/share/classes/jdk/internal/event/SocketConnectEvent.java index e16c360e3b1dc..f9c1376aede54 100644 --- a/src/java.base/share/classes/jdk/internal/event/SocketConnectEvent.java +++ b/src/java.base/share/classes/jdk/internal/event/SocketConnectEvent.java @@ -25,6 +25,7 @@ package jdk.internal.event; import java.io.IOException; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.UnixDomainSocketAddress; @@ -34,7 +35,7 @@ * {@code jdk.jfr.events.SocketConnectEvent } 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, boolean)} method + * {@link #commit(long, long, String, String, int, String)} method * must be the same as the order of the fields. */ public class SocketConnectEvent extends Event { @@ -145,10 +146,10 @@ public static void offer(long start, SocketAddress remote, IOException thrown) { * @param port remote port of the connection * @param thrown the exception thrown or null if succeeded */ - public static void offer(long start, String host, String address, int port, IOException thrown) { + public static void offer(long start, String host, InetAddress address, int port, IOException thrown) { long duration = timestamp() - start; if (shouldCommit(duration)) { - commit(start, duration, host, address, port, exceptionToMessage(thrown)); + commit(start, duration, host, address.getHostAddress(), port, exceptionToMessage(thrown)); } } diff --git a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java index 5a1731888bfbf..e83538217ac3f 100644 --- a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java @@ -573,13 +573,13 @@ protected void connect(SocketAddress remote, int millis) throws IOException { address = InetAddress.getLocalHost(); int port = isa.getPort(); - ReentrantLock connectLock = readLock; - boolean connected = false; long start = SocketConnectEvent.timestamp(); - IOException thrown = null; + IOException connectEx = null; + ReentrantLock connectLock = readLock; try { connectLock.lock(); try { + boolean connected = false; FileDescriptor fd = beginConnect(address, port); try { configureNonBlockingIfNeeded(fd, millis > 0); @@ -611,18 +611,18 @@ protected void connect(SocketAddress remote, int millis) throws IOException { } } catch (IOException ioe) { close(); - thrown = ioe; if (ioe instanceof InterruptedIOException) { - throw ioe; + connectEx = ioe; } else { - thrown = SocketExceptions.of(ioe, isa); - throw thrown; - } - } finally { - if (SocketConnectEvent.enabled()) { - SocketConnectEvent.offer(start, isa.getHostString(), address.getHostAddress(), port, thrown); + connectEx = SocketExceptions.of(ioe, isa); } } + if (SocketConnectEvent.enabled()) { + SocketConnectEvent.offer(start, isa.getHostString(), address, port, connectEx); + } + if (connectEx != null) { + throw connectEx; + } } @Override 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 1e69794b961d2..8b3004fcdd381 100644 --- a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java @@ -954,7 +954,7 @@ private SocketAddress checkRemote(SocketAddress sa) { @Override public boolean connect(SocketAddress remote) throws IOException { boolean connected = false; - IOException thrown = null; + IOException connectEx = null; long start = SocketConnectEvent.timestamp(); SocketAddress sa = checkRemote(remote); try { @@ -998,11 +998,11 @@ public boolean connect(SocketAddress remote) throws IOException { } catch (IOException ioe) { // connect failed, close the channel close(); - thrown = SocketExceptions.of(ioe, sa); - throw thrown; + connectEx = SocketExceptions.of(ioe, sa); + throw connectEx; } finally { - if (SocketConnectEvent.enabled() && (connected || thrown != null)) { - SocketConnectEvent.offer(start, sa, thrown); + if (SocketConnectEvent.enabled() && (connected || connectEx != null)) { + SocketConnectEvent.offer(start, sa, connectEx); } } } @@ -1058,7 +1058,7 @@ private void endFinishConnect(boolean blocking, boolean completed) @Override public boolean finishConnect() throws IOException { boolean connected = false; - IOException thrown = null; + IOException connectEx = null; long start = 0; try { readLock.lock(); @@ -1098,11 +1098,11 @@ public boolean finishConnect() throws IOException { } catch (IOException ioe) { // connect failed, close the channel close(); - thrown = SocketExceptions.of(ioe, remoteAddress); - throw thrown; + connectEx = SocketExceptions.of(ioe, remoteAddress); + throw connectEx; } finally { - if (SocketConnectEvent.enabled() && (connected || thrown != null)) { - SocketConnectEvent.offer(start, remoteAddress, thrown); + if (SocketConnectEvent.enabled() && (connected || connectEx != null)) { + SocketConnectEvent.offer(start, remoteAddress, connectEx); } } } From ce9d39e2a48072b1db8d456e90aa15821324ef7d Mon Sep 17 00:00:00 2001 From: Tim Prinzing Date: Tue, 5 Nov 2024 18:12:49 -0600 Subject: [PATCH 07/11] suggested changes --- .../classes/sun/nio/ch/NioSocketImpl.java | 8 +- .../classes/sun/nio/ch/SocketChannelImpl.java | 80 +++++++++++++------ 2 files changed, 61 insertions(+), 27 deletions(-) diff --git a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java index e83538217ac3f..345153b548097 100644 --- a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java @@ -573,8 +573,9 @@ protected void connect(SocketAddress remote, int millis) throws IOException { address = InetAddress.getLocalHost(); int port = isa.getPort(); - long start = SocketConnectEvent.timestamp(); + long connectStart = 0L; IOException connectEx = null; + ReentrantLock connectLock = readLock; try { connectLock.lock(); @@ -583,6 +584,7 @@ protected void connect(SocketAddress remote, int millis) throws IOException { FileDescriptor fd = beginConnect(address, port); try { configureNonBlockingIfNeeded(fd, millis > 0); + connectStart = SocketConnectEvent.timestamp(); int n = Net.connect(fd, address, port); if (n > 0) { // connection established @@ -617,8 +619,8 @@ protected void connect(SocketAddress remote, int millis) throws IOException { connectEx = SocketExceptions.of(ioe, isa); } } - if (SocketConnectEvent.enabled()) { - SocketConnectEvent.offer(start, isa.getHostString(), address, port, connectEx); + if (connectStart != 0L && SocketConnectEvent.enabled()) { + SocketConnectEvent.offer(connectStart, isa.getHostString(), address, port, connectEx); } if (connectEx != null) { throw connectEx; 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 8b3004fcdd381..3576d7510bc7b 100644 --- a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java @@ -133,8 +133,9 @@ class SocketChannelImpl // preserve the semantics of blocking operations. private volatile boolean forcedNonBlocking; - // timestamp for socket connect event - private long connectStartTimestamp; + // JFR support, start time of non-blocking connect + private long nonBlockingConnectStart; + // -- End of fields protected by stateLock @@ -860,7 +861,7 @@ public boolean isConnectionPending() { /** * Marks the beginning of a connect operation that might block. * @param blocking true if configured blocking - * @param isa the remote address + * @param sa the remote socket address * @throws ClosedChannelException if the channel is closed * @throws AlreadyConnectedException if already connected * @throws ConnectionPendingException is a connection is pending @@ -953,21 +954,23 @@ private SocketAddress checkRemote(SocketAddress sa) { @Override public boolean connect(SocketAddress remote) throws IOException { + SocketAddress sa = checkRemote(remote); + boolean connected = false; + long connectStart = 0L; IOException connectEx = null; - long start = SocketConnectEvent.timestamp(); - SocketAddress sa = checkRemote(remote); + try { readLock.lock(); try { writeLock.lock(); try { - connectStartTimestamp = start; ensureOpen(); boolean blocking = isBlocking(); try { beginConnect(blocking, sa); configureSocketNonBlockingIfVirtualThread(); + connectStart = SocketConnectEvent.timestamp(); int n; if (isUnixSocket()) { n = UnixDomainSockets.connect(fd, sa); @@ -984,11 +987,13 @@ public boolean connect(SocketAddress remote) throws IOException { polled = Net.pollConnectNow(fd); } connected = polled && isOpen(); + } else { + // non-blocking and not connected + this.nonBlockingConnectStart = connectStart; } } finally { endConnect(blocking, connected); } - return connected; } finally { writeLock.unlock(); } @@ -999,11 +1004,20 @@ public boolean connect(SocketAddress remote) throws IOException { // connect failed, close the channel close(); connectEx = SocketExceptions.of(ioe, sa); + } + + // record JFR event + if (connectStart != 0L + && SocketConnectEvent.enabled() + && (connected || connectEx != null)) { + SocketConnectEvent.offer(connectStart, sa, connectEx); + } + + if (connectEx == null) { + return connected; + } else { + assert !connected; throw connectEx; - } finally { - if (SocketConnectEvent.enabled() && (connected || connectEx != null)) { - SocketConnectEvent.offer(start, sa, connectEx); - } } } @@ -1057,25 +1071,22 @@ private void endFinishConnect(boolean blocking, boolean completed) @Override public boolean finishConnect() throws IOException { + long connectStart = 0L; boolean connected = false; IOException connectEx = null; - long start = 0; try { readLock.lock(); - start = connectStartTimestamp; try { writeLock.lock(); try { // no-op if already connected - if (isConnected()) { - connected = true; - return connected; - } - + if (isConnected()) + return true; ensureOpen(); boolean blocking = isBlocking(); try { beginFinishConnect(blocking); + connectStart = this.nonBlockingConnectStart; boolean polled = Net.pollConnectNow(fd); if (blocking) { while (!polled && isOpen()) { @@ -1088,7 +1099,6 @@ public boolean finishConnect() throws IOException { endFinishConnect(blocking, connected); } assert (blocking && connected) ^ !blocking; - return connected; } finally { writeLock.unlock(); } @@ -1099,11 +1109,20 @@ public boolean finishConnect() throws IOException { // connect failed, close the channel close(); connectEx = SocketExceptions.of(ioe, remoteAddress); + } + + // record JFR event + if (connectStart != 0L + && SocketConnectEvent.enabled() + && (connected || connectEx != null)) { + SocketConnectEvent.offer(connectStart, remoteAddress(), connectEx); + } + + if (connectEx != null) { + assert !connected; throw connectEx; - } finally { - if (SocketConnectEvent.enabled() && (connected || connectEx != null)) { - SocketConnectEvent.offer(start, remoteAddress, connectEx); - } + } else { + return connected; } } @@ -1327,6 +1346,9 @@ private boolean finishTimedConnect(long nanos) throws IOException { */ void blockingConnect(SocketAddress remote, long nanos) throws IOException { SocketAddress sa = checkRemote(remote); + + long connectStart = 0L; + IOException connectEx = null; try { readLock.lock(); try { @@ -1340,6 +1362,7 @@ void blockingConnect(SocketAddress remote, long nanos) throws IOException { // change socket to non-blocking lockedConfigureBlocking(false); try { + connectStart = SocketConnectEvent.timestamp(); int n; if (isUnixSocket()) { n = UnixDomainSockets.connect(fd, sa); @@ -1363,7 +1386,16 @@ void blockingConnect(SocketAddress remote, long nanos) throws IOException { } catch (IOException ioe) { // connect failed, close the channel close(); - throw SocketExceptions.of(ioe, sa); + connectEx = SocketExceptions.of(ioe, sa); + } + + // record JFR event + if (connectStart != 0L && SocketConnectEvent.enabled()) { + SocketConnectEvent.offer(connectStart, sa, connectEx); + } + + if (connectEx != null) { + throw connectEx; } } From 13f8157073e1202e5e120a8b5643a0a58d1a9724 Mon Sep 17 00:00:00 2001 From: Tim Prinzing Date: Wed, 20 Nov 2024 21:01:36 -0600 Subject: [PATCH 08/11] Added more tests for socket connect events. - SocketAdapter connect - SocketAdapter connect with exception - Socket connect with exception - SocketChannel connect with exception - SocketChannel non-blocking connect - SocketChannel non-blocking connect with exception --- .../classes/jdk/jfr/internal/JDKEvents.java | 2 +- test/jdk/jdk/jfr/event/io/IOHelper.java | 10 +- .../jfr/event/io/TestSocketAdapterEvents.java | 57 +++++++++--- .../jfr/event/io/TestSocketChannelEvents.java | 93 ++++++++++++++++++- .../jdk/jfr/event/io/TestSocketEvents.java | 30 ++++++ 5 files changed, 177 insertions(+), 15 deletions(-) diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/JDKEvents.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/JDKEvents.java index 7d4b38892a9cf..41c0fc333442e 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JDKEvents.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JDKEvents.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/test/jdk/jdk/jfr/event/io/IOHelper.java b/test/jdk/jdk/jfr/event/io/IOHelper.java index a8dbe940b03a3..27bc81a66d8a8 100644 --- a/test/jdk/jdk/jfr/event/io/IOHelper.java +++ b/test/jdk/jdk/jfr/event/io/IOHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ import static jdk.test.lib.Asserts.assertEquals; import static jdk.test.lib.Asserts.assertTrue; +import java.io.IOException; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -34,6 +35,7 @@ import jdk.jfr.event.io.IOEvent.EventType; import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; import jdk.test.lib.jfr.Events; @@ -130,4 +132,10 @@ private static void logEvents(List actualEvents, List expected } } + public static void checkConnectionEventException(RecordedEvent event, IOException ioe) { + Asserts.assertEquals(event.getEventType().getName(),IOEvent.EVENT_SOCKET_CONNECT); + Asserts.assertNotNull(ioe); + String eventMessage = event.getString("exceptionMessage"); + Asserts.assertEquals(eventMessage, ioe.getMessage()); + } } diff --git a/test/jdk/jdk/jfr/event/io/TestSocketAdapterEvents.java b/test/jdk/jdk/jfr/event/io/TestSocketAdapterEvents.java index 9ac57b839fb2c..40a6fac396cac 100644 --- a/test/jdk/jdk/jfr/event/io/TestSocketAdapterEvents.java +++ b/test/jdk/jdk/jfr/event/io/TestSocketAdapterEvents.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,7 +31,7 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; -import java.nio.ByteBuffer; +import java.net.SocketAddress; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.time.Duration; @@ -40,6 +40,7 @@ import jdk.jfr.Recording; import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; import jdk.test.lib.jfr.Events; import jdk.test.lib.thread.TestThread; import jdk.test.lib.thread.XRun; @@ -65,11 +66,13 @@ private synchronized void addExpectedEvent(IOEvent event) { public static void main(String[] args) throws Throwable { new TestSocketAdapterEvents().test(); + testConnectException(); } - public void test() throws Throwable { + private void test() throws Throwable { try (Recording recording = new Recording()) { try (ServerSocketChannel ssc = ServerSocketChannel.open()) { + recording.enable(IOEvent.EVENT_SOCKET_CONNECT).withThreshold(Duration.ofMillis(0)); recording.enable(IOEvent.EVENT_SOCKET_READ).withThreshold(Duration.ofMillis(0)); recording.enable(IOEvent.EVENT_SOCKET_WRITE).withThreshold(Duration.ofMillis(0)); recording.start(); @@ -106,15 +109,17 @@ public void xrun() throws IOException { }); readerThread.start(); - try (SocketChannel sc = SocketChannel.open(ssc.getLocalAddress()); - Socket s = sc.socket(); OutputStream os = s.getOutputStream()) { - - os.write(writeInt); - addExpectedEvent(IOEvent.createSocketWriteEvent(1, s)); - os.write(writeBuf, 0, 3); - addExpectedEvent(IOEvent.createSocketWriteEvent(3, s)); - os.write(writeBuf); - addExpectedEvent(IOEvent.createSocketWriteEvent(writeBuf.length, s)); + try (SocketChannel sc = SocketChannel.open(); Socket s = sc.socket()) { + s.connect(ssc.getLocalAddress()); + addExpectedEvent(IOEvent.createSocketConnectEvent(s)); + try (OutputStream os = s.getOutputStream()) { + os.write(writeInt); + addExpectedEvent(IOEvent.createSocketWriteEvent(1, s)); + os.write(writeBuf, 0, 3); + addExpectedEvent(IOEvent.createSocketWriteEvent(3, s)); + os.write(writeBuf); + addExpectedEvent(IOEvent.createSocketWriteEvent(writeBuf.length, s)); + } } readerThread.joinAndThrow(); @@ -124,4 +129,32 @@ public void xrun() throws IOException { } } } + + private static void testConnectException() throws Throwable { + try (Recording recording = new Recording()) { + try (ServerSocketChannel ssc = ServerSocketChannel.open()) { + recording.enable(IOEvent.EVENT_SOCKET_CONNECT).withThreshold(Duration.ofMillis(0)); + recording.start(); + + InetAddress lb = InetAddress.getLoopbackAddress(); + ssc.bind(new InetSocketAddress(lb, 0)); + SocketAddress addr = ssc.getLocalAddress(); + ssc.close(); + + IOException connectException = null; + try (SocketChannel sc = SocketChannel.open()) { + Socket s = sc.socket(); + s.connect(addr); + } catch (IOException ioe) { + // we expect this + connectException = ioe; + } + + recording.stop(); + List events = Events.fromRecording(recording); + Asserts.assertEquals(events.size(), 1); + IOHelper.checkConnectionEventException(events.get(0), connectException); + } + } + } } diff --git a/test/jdk/jdk/jfr/event/io/TestSocketChannelEvents.java b/test/jdk/jdk/jfr/event/io/TestSocketChannelEvents.java index 24e9b8daedce1..f30a9ad9695bd 100644 --- a/test/jdk/jdk/jfr/event/io/TestSocketChannelEvents.java +++ b/test/jdk/jdk/jfr/event/io/TestSocketChannelEvents.java @@ -28,6 +28,7 @@ import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; @@ -37,6 +38,7 @@ import jdk.jfr.Recording; import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; import jdk.test.lib.jfr.Events; import jdk.test.lib.thread.TestThread; import jdk.test.lib.thread.XRun; @@ -61,9 +63,12 @@ private synchronized void addExpectedEvent(IOEvent event) { public static void main(String[] args) throws Throwable { new TestSocketChannelEvents().test(); + new TestSocketChannelEvents().testNonBlockingConnect(); + testConnectException(); + testNonBlockingConnectException(); } - public void test() throws Throwable { + private void test() throws Throwable { try (Recording recording = new Recording()) { try (ServerSocketChannel ssc = ServerSocketChannel.open()) { recording.enable(IOEvent.EVENT_SOCKET_CONNECT).withThreshold(Duration.ofMillis(0)); @@ -132,4 +137,90 @@ public void xrun() throws IOException { } } } + + private void testNonBlockingConnect() throws Throwable { + try (Recording recording = new Recording()) { + try (ServerSocketChannel ssc = ServerSocketChannel.open()) { + recording.enable(IOEvent.EVENT_SOCKET_CONNECT).withThreshold(Duration.ofMillis(0)); + recording.start(); + + InetAddress lb = InetAddress.getLoopbackAddress(); + ssc.bind(new InetSocketAddress(lb, 0)); + SocketAddress addr = ssc.getLocalAddress(); + + try (SocketChannel sc = SocketChannel.open()) { + sc.configureBlocking(false); + sc.connect(addr); + try (SocketChannel serverSide = ssc.accept()) { + while (! sc.finishConnect()) { + Thread.sleep(1); + } + } + addExpectedEvent(IOEvent.createSocketConnectEvent(sc.socket())); + } + + recording.stop(); + List events = Events.fromRecording(recording); + IOHelper.verifyEquals(events, expectedEvents); + } + } + } + + private static void testConnectException() throws Throwable { + try (Recording recording = new Recording()) { + try (ServerSocketChannel ssc = ServerSocketChannel.open()) { + recording.enable(IOEvent.EVENT_SOCKET_CONNECT).withThreshold(Duration.ofMillis(0)); + recording.start(); + + InetAddress lb = InetAddress.getLoopbackAddress(); + ssc.bind(new InetSocketAddress(lb, 0)); + SocketAddress addr = ssc.getLocalAddress(); + ssc.close(); + + // try to connect, but the server will not accept + IOException connectException = null; + try (SocketChannel sc = SocketChannel.open(addr)) { + } catch (IOException ioe) { + // we expect this + connectException = ioe; + } + + recording.stop(); + List events = Events.fromRecording(recording); + Asserts.assertEquals(events.size(), 1); + IOHelper.checkConnectionEventException(events.get(0), connectException); + } + } + } + + private static void testNonBlockingConnectException() throws Throwable { + try (Recording recording = new Recording()) { + try (ServerSocketChannel ssc = ServerSocketChannel.open()) { + recording.enable(IOEvent.EVENT_SOCKET_CONNECT).withThreshold(Duration.ofMillis(0)); + recording.start(); + + InetAddress lb = InetAddress.getLoopbackAddress(); + ssc.bind(new InetSocketAddress(lb, 0)); + SocketAddress addr = ssc.getLocalAddress(); + ssc.close(); + + IOException connectException = null; + try (SocketChannel sc = SocketChannel.open()) { + sc.configureBlocking(false); + sc.connect(addr); + while (! sc.finishConnect()) { + Thread.sleep(1); + } + } catch (IOException ioe) { + // we expect this + connectException = ioe; + } + + recording.stop(); + List events = Events.fromRecording(recording); + Asserts.assertEquals(events.size(), 1); + IOHelper.checkConnectionEventException(events.get(0), connectException); + } + } + } } diff --git a/test/jdk/jdk/jfr/event/io/TestSocketEvents.java b/test/jdk/jdk/jfr/event/io/TestSocketEvents.java index 9b65b25757341..4fb382d23fe8d 100644 --- a/test/jdk/jdk/jfr/event/io/TestSocketEvents.java +++ b/test/jdk/jdk/jfr/event/io/TestSocketEvents.java @@ -32,12 +32,14 @@ import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; +import java.net.SocketAddress; import java.time.Duration; import java.util.ArrayList; import java.util.List; import jdk.jfr.Recording; import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.Asserts; import jdk.test.lib.jfr.Events; import jdk.test.lib.thread.TestThread; import jdk.test.lib.thread.XRun; @@ -63,6 +65,7 @@ private synchronized void addExpectedEvent(IOEvent event) { public static void main(String[] args) throws Throwable { new TestSocketEvents().test(); + testConnectException(); } private void test() throws Throwable { @@ -123,4 +126,31 @@ public void xrun() throws IOException { } } } + + private static void testConnectException() throws Throwable { + try (Recording recording = new Recording()) { + try (ServerSocket ss = new ServerSocket()) { + recording.enable(IOEvent.EVENT_SOCKET_CONNECT).withThreshold(Duration.ofMillis(0)); + recording.start(); + + InetAddress lb = InetAddress.getLoopbackAddress(); + ss.bind(new InetSocketAddress(lb, 0)); + SocketAddress addr = ss.getLocalSocketAddress(); + ss.close(); + + IOException connectException = null; + try (Socket s = new Socket()) { + s.connect(addr); + } catch (IOException ioe) { + // we expect this + connectException = ioe; + } + + recording.stop(); + List events = Events.fromRecording(recording); + Asserts.assertEquals(events.size(), 1); + IOHelper.checkConnectionEventException(events.get(0), connectException); + } + } + } } From f7b3be00e579a45945a3c7930e30e0ec695d9ab5 Mon Sep 17 00:00:00 2001 From: Tim Prinzing Date: Mon, 2 Dec 2024 18:25:30 -0600 Subject: [PATCH 09/11] split socket connect failure out to its own event. --- .../internal/event/SocketConnectEvent.java | 37 ++--- .../event/SocketConnectFailedEvent.java | 147 ++++++++++++++++++ .../classes/sun/nio/ch/NioSocketImpl.java | 13 +- .../classes/sun/nio/ch/SocketChannelImpl.java | 29 ++-- .../jdk/jfr/events/SocketConnectEvent.java | 5 +- .../jfr/events/SocketConnectFailedEvent.java | 52 +++++++ .../classes/jdk/jfr/internal/JDKEvents.java | 1 + .../jdk/jfr/internal/MirrorEvents.java | 2 + src/jdk.jfr/share/conf/jfr/default.jfc | 6 + src/jdk.jfr/share/conf/jfr/profile.jfc | 6 + test/jdk/jdk/jfr/event/io/IOEvent.java | 10 +- test/jdk/jdk/jfr/event/io/IOHelper.java | 6 +- .../jfr/event/io/TestSocketAdapterEvents.java | 21 ++- .../jfr/event/io/TestSocketChannelEvents.java | 36 ++--- .../jdk/jfr/event/io/TestSocketEvents.java | 21 ++- test/lib/jdk/test/lib/jfr/EventNames.java | 1 + 16 files changed, 306 insertions(+), 87 deletions(-) create mode 100644 src/java.base/share/classes/jdk/internal/event/SocketConnectFailedEvent.java create mode 100644 src/jdk.jfr/share/classes/jdk/jfr/events/SocketConnectFailedEvent.java diff --git a/src/java.base/share/classes/jdk/internal/event/SocketConnectEvent.java b/src/java.base/share/classes/jdk/internal/event/SocketConnectEvent.java index f9c1376aede54..c554349265839 100644 --- a/src/java.base/share/classes/jdk/internal/event/SocketConnectEvent.java +++ b/src/java.base/share/classes/jdk/internal/event/SocketConnectEvent.java @@ -24,19 +24,18 @@ */ package jdk.internal.event; -import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.UnixDomainSocketAddress; /** - * A JFR event for socket connect operations. This event is mirrored in - * {@code jdk.jfr.events.SocketConnectEvent } where the metadata for the event is + * A JFR event for when a socket connection is established. This event is mirrored in + * {@code jdk.jfr.events.SocketConnectEvent} 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, String)} method - * must be the same as the order of the fields. + * {@link #commit(long, long, String, String, int)} method must be the same as the + * order of the fields. */ public class SocketConnectEvent extends Event { @@ -45,7 +44,6 @@ public class SocketConnectEvent extends Event { public String host; public String address; public int port; - public String exceptionMessage; /** * Actually commit an event. The implementation is generated automatically. @@ -57,9 +55,8 @@ public class SocketConnectEvent extends Event { * @param host remote host of the connection * @param address remote address of the connection * @param port remote port of the connection - * @param exceptionMessage message from exception if failed */ - public static void commit(long start, long duration, String host, String address, int port, String exceptionMessage) { + public static void commit(long start, long duration, String host, String address, int port) { // Generated by JFR } @@ -99,35 +96,25 @@ public static long timestamp() { return 0L; } - static String exceptionToMessage(IOException ex) { - if (ex == null) { - return null; - } - String m = ex.getMessage(); - return m != null ? m : ex.toString(); - } - /** * 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, String)}. + * {@link #commit(long, long, String, String, int)}. * * @param start the start time * @param remote the address of the remote socket - * @param thrown the exception thrown or null if succeeded */ - public static void offer(long start, SocketAddress remote, IOException thrown) { + public static void offer(long start, SocketAddress remote) { long duration = timestamp() - start; if (shouldCommit(duration)) { - String msg = exceptionToMessage(thrown); if (remote instanceof InetSocketAddress isa) { - commit(start, duration, isa.getHostString(), isa.getAddress().getHostAddress(), isa.getPort(), msg); + commit(start, duration, isa.getHostString(), isa.getAddress().getHostAddress(), isa.getPort()); } else if (remote instanceof UnixDomainSocketAddress udsa) { String path = "[" + udsa.getPath().toString() + "]"; - commit(start, duration, "Unix domain socket", path, 0, msg); + commit(start, duration, "Unix domain socket", path, 0); } } } @@ -144,13 +131,11 @@ public static void offer(long start, SocketAddress remote, IOException thrown) { * @param host remote host of the connection * @param address remote address of the connection * @param port remote port of the connection - * @param thrown the exception thrown or null if succeeded */ - public static void offer(long start, String host, InetAddress address, int port, IOException thrown) { + public static void offer(long start, String host, InetAddress address, int port) { long duration = timestamp() - start; if (shouldCommit(duration)) { - commit(start, duration, host, address.getHostAddress(), port, exceptionToMessage(thrown)); + commit(start, duration, host, address.getHostAddress(), port); } } - } diff --git a/src/java.base/share/classes/jdk/internal/event/SocketConnectFailedEvent.java b/src/java.base/share/classes/jdk/internal/event/SocketConnectFailedEvent.java new file mode 100644 index 0000000000000..670942b25143d --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/event/SocketConnectFailedEvent.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.UnixDomainSocketAddress; + +/** + * A JFR event for when a socket connection cannot be established. This event is mirrored + * in {@code jdk.jfr.events.SocketConnectFailedEvent} 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, String)} method must be the same as + * the order of the fields. + */ +public class SocketConnectFailedEvent 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 String connectExceptionMessage; + + /** + * Actually commit an event. The implementation 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 connection + * @param address remote address of the connection + * @param port remote port of the connection + * @param connectEx the connect exception message + */ + public static void commit(long start, long duration, String host, String address, int port, String connectEx) { + // 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 event is 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, String)}. + * + * @param start the start time + * @param remote the address of the remote socket + * @param connectEx the I/O exception thrown + */ + public static void offer(long start, SocketAddress remote, IOException connectEx) { + long duration = timestamp() - start; + if (shouldCommit(duration)) { + String msg = connectEx.getMessage(); + if (remote instanceof InetSocketAddress isa) { + commit(start, duration, isa.getHostString(), isa.getAddress().getHostAddress(), isa.getPort(), msg); + } else if (remote instanceof UnixDomainSocketAddress udsa) { + String path = "[" + udsa.getPath().toString() + "]"; + commit(start, duration, "Unix domain socket", path, 0, msg); + } + } + } + + /** + * 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 + * {@code commit(long, long, String, String, int, long)} + * + * @param start timestamp of the start of the operation + * @param host remote host of the connection + * @param address remote address of the connection + * @param port remote port of the connection + * @param connectEx the I/O exception thrown + */ + public static void offer(long start, String host, InetAddress address, int port, IOException connectEx) { + long duration = timestamp() - start; + if (shouldCommit(duration)) { + commit(start, duration, host, address.getHostAddress(), port, connectEx.getMessage()); + } + } +} diff --git a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java index 345153b548097..032ae57e4b653 100644 --- a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java @@ -54,6 +54,7 @@ import jdk.internal.access.JavaIOFileDescriptorAccess; import jdk.internal.access.SharedSecrets; import jdk.internal.event.SocketConnectEvent; +import jdk.internal.event.SocketConnectFailedEvent; import jdk.internal.ref.CleanerFactory; import sun.net.ConnectionResetException; import sun.net.NetHooks; @@ -619,9 +620,17 @@ protected void connect(SocketAddress remote, int millis) throws IOException { connectEx = SocketExceptions.of(ioe, isa); } } - if (connectStart != 0L && SocketConnectEvent.enabled()) { - SocketConnectEvent.offer(connectStart, isa.getHostString(), address, port, connectEx); + + // record JFR event + if (connectStart != 0L) { + String hostname = isa.getHostString(); + if (connectEx == null && SocketConnectEvent.enabled()) { + SocketConnectEvent.offer(connectStart, hostname , address, port); + } else if (connectEx != null && SocketConnectFailedEvent.enabled()) { + SocketConnectFailedEvent.offer(connectStart, hostname, address, port, connectEx); + } } + if (connectEx != null) { throw connectEx; } 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 3576d7510bc7b..0ce68ac6fdaa0 100644 --- a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java +++ b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java @@ -60,6 +60,7 @@ import static java.net.StandardProtocolFamily.UNIX; import jdk.internal.event.SocketConnectEvent; +import jdk.internal.event.SocketConnectFailedEvent; import jdk.internal.event.SocketReadEvent; import jdk.internal.event.SocketWriteEvent; import sun.net.ConnectionResetException; @@ -1007,10 +1008,12 @@ public boolean connect(SocketAddress remote) throws IOException { } // record JFR event - if (connectStart != 0L - && SocketConnectEvent.enabled() - && (connected || connectEx != null)) { - SocketConnectEvent.offer(connectStart, sa, connectEx); + if (connectStart != 0L) { + if (connected && SocketConnectEvent.enabled()) { + SocketConnectEvent.offer(connectStart, sa); + } else if (connectEx != null && SocketConnectFailedEvent.enabled()) { + SocketConnectFailedEvent.offer(connectStart, sa, connectEx); + } } if (connectEx == null) { @@ -1112,10 +1115,12 @@ public boolean finishConnect() throws IOException { } // record JFR event - if (connectStart != 0L - && SocketConnectEvent.enabled() - && (connected || connectEx != null)) { - SocketConnectEvent.offer(connectStart, remoteAddress(), connectEx); + if (connectStart != 0L) { + if (connected && SocketConnectEvent.enabled()) { + SocketConnectEvent.offer(connectStart, remoteAddress()); + } else if (connectEx != null && SocketConnectFailedEvent.enabled()) { + SocketConnectFailedEvent.offer(connectStart, remoteAddress(), connectEx); + } } if (connectEx != null) { @@ -1390,8 +1395,12 @@ void blockingConnect(SocketAddress remote, long nanos) throws IOException { } // record JFR event - if (connectStart != 0L && SocketConnectEvent.enabled()) { - SocketConnectEvent.offer(connectStart, sa, connectEx); + if (connectStart != 0L) { + if (connectEx == null && SocketConnectEvent.enabled()) { + SocketConnectEvent.offer(connectStart, sa); + } else if (connectEx != null && SocketConnectFailedEvent.enabled()) { + SocketConnectFailedEvent.offer(connectStart, sa, connectEx); + } } if (connectEx != null) { diff --git a/src/jdk.jfr/share/classes/jdk/jfr/events/SocketConnectEvent.java b/src/jdk.jfr/share/classes/jdk/jfr/events/SocketConnectEvent.java index 48f1e64b059d0..d6058d1ececf6 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/events/SocketConnectEvent.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/events/SocketConnectEvent.java @@ -35,7 +35,7 @@ @Name(Type.EVENT_NAME_PREFIX + "SocketConnect") @Label("Socket Connect") @Category("Java Application") -@Description("Connecting a socket") +@Description("Socket connection established") public class SocketConnectEvent extends MirrorEvent { @Label("Remote Host") @@ -46,7 +46,4 @@ public class SocketConnectEvent extends MirrorEvent { @Label("Remote Port") public int port; - - @Label("Exception Message") - public String exceptionMessage; } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/events/SocketConnectFailedEvent.java b/src/jdk.jfr/share/classes/jdk/jfr/events/SocketConnectFailedEvent.java new file mode 100644 index 0000000000000..09b91d6cf39b2 --- /dev/null +++ b/src/jdk.jfr/share/classes/jdk/jfr/events/SocketConnectFailedEvent.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * 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.internal.MirrorEvent; +import jdk.jfr.internal.Type; + +@Name(Type.EVENT_NAME_PREFIX + "SocketConnectFailed") +@Label("Socket Connect Failed") +@Category("Java Application") +@Description("Socket connection could not be established") +public class SocketConnectFailedEvent extends MirrorEvent { + + @Label("Remote Host") + public String host; + + @Label("Remote Address") + public String address; + + @Label("Remote Port") + public int port; + + @Label("Connect Exception Message") + public String connectExceptionMessage; +} \ No newline at end of file diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/JDKEvents.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/JDKEvents.java index 41c0fc333442e..903c8eb172381 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/JDKEvents.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/JDKEvents.java @@ -63,6 +63,7 @@ public final class JDKEvents { jdk.internal.event.SecurityProviderServiceEvent.class, jdk.internal.event.SerializationMisdeclarationEvent.class, jdk.internal.event.SocketConnectEvent.class, + jdk.internal.event.SocketConnectFailedEvent.class, jdk.internal.event.SocketReadEvent.class, jdk.internal.event.SocketWriteEvent.class, jdk.internal.event.ThreadSleepEvent.class, diff --git a/src/jdk.jfr/share/classes/jdk/jfr/internal/MirrorEvents.java b/src/jdk.jfr/share/classes/jdk/jfr/internal/MirrorEvents.java index 2d1a1069f98a2..c94eba7cde041 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/internal/MirrorEvents.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/internal/MirrorEvents.java @@ -39,6 +39,7 @@ import jdk.jfr.events.SecurityProviderServiceEvent; import jdk.jfr.events.SerializationMisdeclarationEvent; import jdk.jfr.events.SocketConnectEvent; +import jdk.jfr.events.SocketConnectFailedEvent; import jdk.jfr.events.SocketReadEvent; import jdk.jfr.events.SocketWriteEvent; import jdk.jfr.events.TLSHandshakeEvent; @@ -68,6 +69,7 @@ final class MirrorEvents { register("jdk.internal.event.SecurityProviderServiceEvent", SecurityProviderServiceEvent.class); register("jdk.internal.event.SerializationMisdeclarationEvent", SerializationMisdeclarationEvent.class); register("jdk.internal.event.SocketConnectEvent", SocketConnectEvent.class); + register("jdk.internal.event.SocketConnectFailedEvent", SocketConnectFailedEvent.class); register("jdk.internal.event.SocketReadEvent", SocketReadEvent.class); register("jdk.internal.event.SocketWriteEvent", SocketWriteEvent.class); register("jdk.internal.event.ThreadSleepEvent", ThreadSleepEvent.class); diff --git a/src/jdk.jfr/share/conf/jfr/default.jfc b/src/jdk.jfr/share/conf/jfr/default.jfc index 49e35a5b3af8e..3c35a8b60f54a 100644 --- a/src/jdk.jfr/share/conf/jfr/default.jfc +++ b/src/jdk.jfr/share/conf/jfr/default.jfc @@ -740,6 +740,12 @@ 20 ms + + true + true + 20 ms + + true true diff --git a/src/jdk.jfr/share/conf/jfr/profile.jfc b/src/jdk.jfr/share/conf/jfr/profile.jfc index 5f8981922311c..fd29ee5660b03 100644 --- a/src/jdk.jfr/share/conf/jfr/profile.jfc +++ b/src/jdk.jfr/share/conf/jfr/profile.jfc @@ -740,6 +740,12 @@ 10 ms + + true + true + 10 ms + + true true diff --git a/test/jdk/jdk/jfr/event/io/IOEvent.java b/test/jdk/jdk/jfr/event/io/IOEvent.java index c71c188d11173..174bb238800e3 100644 --- a/test/jdk/jdk/jfr/event/io/IOEvent.java +++ b/test/jdk/jdk/jfr/event/io/IOEvent.java @@ -74,13 +74,14 @@ public int hashCode() { public static final String EVENT_FILE_READ = EventNames.FileRead; public static final String EVENT_FILE_WRITE = EventNames.FileWrite; public static final String EVENT_SOCKET_CONNECT = EventNames.SocketConnect; + public static final String EVENT_SOCKET_CONNECT_FAILED = EventNames.SocketConnectFailed; public static final String EVENT_SOCKET_READ = EventNames.SocketRead; public static final String EVENT_SOCKET_WRITE = EventNames.SocketWrite; - public enum EventType { UnknownEvent, FileForce, FileRead, FileWrite, SocketConnect, SocketRead, SocketWrite } + public enum EventType { UnknownEvent, FileForce, FileRead, FileWrite, SocketConnect, SocketConnectFailed, SocketRead, SocketWrite } private static final String[] eventPaths = { - EVENT_UNKNOWN, EVENT_FILE_FORCE, EVENT_FILE_READ, EVENT_FILE_WRITE, EVENT_SOCKET_CONNECT, EVENT_SOCKET_READ, EVENT_SOCKET_WRITE + EVENT_UNKNOWN, EVENT_FILE_FORCE, EVENT_FILE_READ, EVENT_FILE_WRITE, EVENT_SOCKET_CONNECT, EVENT_SOCKET_CONNECT_FAILED, EVENT_SOCKET_READ, EVENT_SOCKET_WRITE }; public static boolean isWriteEvent(EventType eventType) { @@ -99,6 +100,11 @@ public static IOEvent createSocketConnectEvent(Socket s) { return new IOEvent(Thread.currentThread().getName(), EventType.SocketConnect, 0, getAddress(s), false); } + + public static IOEvent createSocketConnectFailedEvent(Socket s) { + return new IOEvent(Thread.currentThread().getName(), EventType.SocketConnectFailed, 0, getAddress(s), false); + } + public static IOEvent createSocketWriteEvent(long size, Socket s) { if (size < 0) { size = 0; diff --git a/test/jdk/jdk/jfr/event/io/IOHelper.java b/test/jdk/jdk/jfr/event/io/IOHelper.java index 27bc81a66d8a8..b672a9d68d83d 100644 --- a/test/jdk/jdk/jfr/event/io/IOHelper.java +++ b/test/jdk/jdk/jfr/event/io/IOHelper.java @@ -132,10 +132,10 @@ private static void logEvents(List actualEvents, List expected } } - public static void checkConnectionEventException(RecordedEvent event, IOException ioe) { - Asserts.assertEquals(event.getEventType().getName(),IOEvent.EVENT_SOCKET_CONNECT); + public static void checkConnectEventException(RecordedEvent event, IOException ioe) { + Asserts.assertEquals(IOEvent.EVENT_SOCKET_CONNECT_FAILED, event.getEventType().getName()); Asserts.assertNotNull(ioe); - String eventMessage = event.getString("exceptionMessage"); + String eventMessage = event.getString("connectExceptionMessage"); Asserts.assertEquals(eventMessage, ioe.getMessage()); } } diff --git a/test/jdk/jdk/jfr/event/io/TestSocketAdapterEvents.java b/test/jdk/jdk/jfr/event/io/TestSocketAdapterEvents.java index 40a6fac396cac..cb77015af1269 100644 --- a/test/jdk/jdk/jfr/event/io/TestSocketAdapterEvents.java +++ b/test/jdk/jdk/jfr/event/io/TestSocketAdapterEvents.java @@ -34,7 +34,6 @@ import java.net.SocketAddress; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; -import java.time.Duration; import java.util.ArrayList; import java.util.List; @@ -72,9 +71,9 @@ public static void main(String[] args) throws Throwable { private void test() throws Throwable { try (Recording recording = new Recording()) { try (ServerSocketChannel ssc = ServerSocketChannel.open()) { - recording.enable(IOEvent.EVENT_SOCKET_CONNECT).withThreshold(Duration.ofMillis(0)); - recording.enable(IOEvent.EVENT_SOCKET_READ).withThreshold(Duration.ofMillis(0)); - recording.enable(IOEvent.EVENT_SOCKET_WRITE).withThreshold(Duration.ofMillis(0)); + recording.enable(IOEvent.EVENT_SOCKET_CONNECT); + recording.enable(IOEvent.EVENT_SOCKET_READ); + recording.enable(IOEvent.EVENT_SOCKET_WRITE); recording.start(); InetAddress lb = InetAddress.getLoopbackAddress(); @@ -88,21 +87,21 @@ public void xrun() throws IOException { InputStream is = s.getInputStream()) { int readInt = is.read(); - assertEquals(readInt, writeInt, "Wrong readInt"); + assertEquals(writeInt, readInt, "Wrong readInt"); addExpectedEvent(IOEvent.createSocketReadEvent(1, s)); int bytesRead = is.read(bs, 0, 3); - assertEquals(bytesRead, 3, "Wrong bytesRead partial buffer"); + assertEquals(3, bytesRead, "Wrong bytesRead partial buffer"); addExpectedEvent(IOEvent.createSocketReadEvent(bytesRead, s)); bytesRead = is.read(bs); - assertEquals(bytesRead, writeBuf.length, "Wrong bytesRead full buffer"); + assertEquals(writeBuf.length, bytesRead, "Wrong bytesRead full buffer"); addExpectedEvent(IOEvent.createSocketReadEvent(bytesRead, s)); // Try to read more, but writer have closed. Should // get EOF. readInt = is.read(); - assertEquals(readInt, -1, "Wrong readInt at EOF"); + assertEquals(-1, readInt, "Wrong readInt at EOF"); addExpectedEvent(IOEvent.createSocketReadEvent(-1, s)); } } @@ -133,7 +132,7 @@ public void xrun() throws IOException { private static void testConnectException() throws Throwable { try (Recording recording = new Recording()) { try (ServerSocketChannel ssc = ServerSocketChannel.open()) { - recording.enable(IOEvent.EVENT_SOCKET_CONNECT).withThreshold(Duration.ofMillis(0)); + recording.enable(IOEvent.EVENT_SOCKET_CONNECT_FAILED); recording.start(); InetAddress lb = InetAddress.getLoopbackAddress(); @@ -152,8 +151,8 @@ private static void testConnectException() throws Throwable { recording.stop(); List events = Events.fromRecording(recording); - Asserts.assertEquals(events.size(), 1); - IOHelper.checkConnectionEventException(events.get(0), connectException); + Asserts.assertEquals(1, events.size()); + IOHelper.checkConnectEventException(events.get(0), connectException); } } } diff --git a/test/jdk/jdk/jfr/event/io/TestSocketChannelEvents.java b/test/jdk/jdk/jfr/event/io/TestSocketChannelEvents.java index f30a9ad9695bd..f2bc147bfee18 100644 --- a/test/jdk/jdk/jfr/event/io/TestSocketChannelEvents.java +++ b/test/jdk/jdk/jfr/event/io/TestSocketChannelEvents.java @@ -32,7 +32,6 @@ import java.nio.ByteBuffer; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; -import java.time.Duration; import java.util.ArrayList; import java.util.List; @@ -71,9 +70,9 @@ public static void main(String[] args) throws Throwable { private void test() throws Throwable { try (Recording recording = new Recording()) { try (ServerSocketChannel ssc = ServerSocketChannel.open()) { - recording.enable(IOEvent.EVENT_SOCKET_CONNECT).withThreshold(Duration.ofMillis(0)); - recording.enable(IOEvent.EVENT_SOCKET_READ).withThreshold(Duration.ofMillis(0)); - recording.enable(IOEvent.EVENT_SOCKET_WRITE).withThreshold(Duration.ofMillis(0)); + recording.enable(IOEvent.EVENT_SOCKET_CONNECT); + recording.enable(IOEvent.EVENT_SOCKET_READ); + recording.enable(IOEvent.EVENT_SOCKET_WRITE); recording.start(); InetAddress lb = InetAddress.getLoopbackAddress(); @@ -86,13 +85,13 @@ public void xrun() throws IOException { ByteBuffer bufB = ByteBuffer.allocate(bufSizeB); try (SocketChannel sc = ssc.accept()) { int readSize = sc.read(bufA); - assertEquals(readSize, bufSizeA, "Wrong readSize bufA"); + assertEquals(bufSizeA, readSize, "Wrong readSize bufA"); addExpectedEvent(IOEvent.createSocketReadEvent(bufSizeA, sc.socket())); bufA.clear(); bufA.limit(1); readSize = (int) sc.read(new ByteBuffer[] { bufA, bufB }); - assertEquals(readSize, 1 + bufSizeB, "Wrong readSize 1+bufB"); + assertEquals(1 + bufSizeB, readSize, "Wrong readSize 1+bufB"); addExpectedEvent(IOEvent.createSocketReadEvent(readSize, sc.socket())); // We try to read, but client have closed. Should @@ -100,7 +99,7 @@ public void xrun() throws IOException { bufA.clear(); bufA.limit(1); readSize = sc.read(bufA); - assertEquals(readSize, -1, "Wrong readSize at EOF"); + assertEquals(-1, readSize, "Wrong readSize at EOF"); addExpectedEvent(IOEvent.createSocketReadEvent(-1, sc.socket())); } } @@ -126,7 +125,7 @@ public void xrun() throws IOException { bufA.clear(); bufA.limit(1); int bytesWritten = (int) sc.write(new ByteBuffer[] { bufA, bufB }); - assertEquals(bytesWritten, 1 + bufSizeB, "Wrong bytesWritten 1+bufB"); + assertEquals(1 + bufSizeB, bytesWritten, "Wrong bytesWritten 1+bufB"); addExpectedEvent(IOEvent.createSocketWriteEvent(bytesWritten, sc.socket())); } @@ -141,7 +140,7 @@ public void xrun() throws IOException { private void testNonBlockingConnect() throws Throwable { try (Recording recording = new Recording()) { try (ServerSocketChannel ssc = ServerSocketChannel.open()) { - recording.enable(IOEvent.EVENT_SOCKET_CONNECT).withThreshold(Duration.ofMillis(0)); + recording.enable(IOEvent.EVENT_SOCKET_CONNECT); recording.start(); InetAddress lb = InetAddress.getLoopbackAddress(); @@ -169,7 +168,7 @@ private void testNonBlockingConnect() throws Throwable { private static void testConnectException() throws Throwable { try (Recording recording = new Recording()) { try (ServerSocketChannel ssc = ServerSocketChannel.open()) { - recording.enable(IOEvent.EVENT_SOCKET_CONNECT).withThreshold(Duration.ofMillis(0)); + recording.enable(IOEvent.EVENT_SOCKET_CONNECT_FAILED); recording.start(); InetAddress lb = InetAddress.getLoopbackAddress(); @@ -187,8 +186,8 @@ private static void testConnectException() throws Throwable { recording.stop(); List events = Events.fromRecording(recording); - Asserts.assertEquals(events.size(), 1); - IOHelper.checkConnectionEventException(events.get(0), connectException); + Asserts.assertEquals(1, events.size()); + IOHelper.checkConnectEventException(events.get(0), connectException); } } } @@ -196,7 +195,7 @@ private static void testConnectException() throws Throwable { private static void testNonBlockingConnectException() throws Throwable { try (Recording recording = new Recording()) { try (ServerSocketChannel ssc = ServerSocketChannel.open()) { - recording.enable(IOEvent.EVENT_SOCKET_CONNECT).withThreshold(Duration.ofMillis(0)); + recording.enable(IOEvent.EVENT_SOCKET_CONNECT_FAILED); recording.start(); InetAddress lb = InetAddress.getLoopbackAddress(); @@ -207,9 +206,10 @@ private static void testNonBlockingConnectException() throws Throwable { IOException connectException = null; try (SocketChannel sc = SocketChannel.open()) { sc.configureBlocking(false); - sc.connect(addr); - while (! sc.finishConnect()) { - Thread.sleep(1); + boolean connected = sc.connect(addr); + while (!connected) { + Thread.sleep(10); + connected = sc.finishConnect(); } } catch (IOException ioe) { // we expect this @@ -218,8 +218,8 @@ private static void testNonBlockingConnectException() throws Throwable { recording.stop(); List events = Events.fromRecording(recording); - Asserts.assertEquals(events.size(), 1); - IOHelper.checkConnectionEventException(events.get(0), connectException); + Asserts.assertEquals(1, events.size()); + IOHelper.checkConnectEventException(events.get(0), connectException); } } } diff --git a/test/jdk/jdk/jfr/event/io/TestSocketEvents.java b/test/jdk/jdk/jfr/event/io/TestSocketEvents.java index 4fb382d23fe8d..1acb432936ac5 100644 --- a/test/jdk/jdk/jfr/event/io/TestSocketEvents.java +++ b/test/jdk/jdk/jfr/event/io/TestSocketEvents.java @@ -33,7 +33,6 @@ import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; -import java.time.Duration; import java.util.ArrayList; import java.util.List; @@ -71,9 +70,9 @@ public static void main(String[] args) throws Throwable { private void test() throws Throwable { try (Recording recording = new Recording()) { try (ServerSocket ss = new ServerSocket()) { - recording.enable(IOEvent.EVENT_SOCKET_CONNECT).withThreshold(Duration.ofMillis(0)); - recording.enable(IOEvent.EVENT_SOCKET_READ).withThreshold(Duration.ofMillis(0)); - recording.enable(IOEvent.EVENT_SOCKET_WRITE).withThreshold(Duration.ofMillis(0)); + recording.enable(IOEvent.EVENT_SOCKET_CONNECT); + recording.enable(IOEvent.EVENT_SOCKET_READ); + recording.enable(IOEvent.EVENT_SOCKET_WRITE); recording.start(); InetAddress lb = InetAddress.getLoopbackAddress(); @@ -85,21 +84,21 @@ public void xrun() throws IOException { byte[] bs = new byte[4]; try (Socket s = ss.accept(); InputStream is = s.getInputStream()) { int readInt = is.read(); - assertEquals(readInt, writeInt, "Wrong readInt"); + assertEquals(writeInt, readInt, "Wrong readInt"); addExpectedEvent(IOEvent.createSocketReadEvent(1, s)); int bytesRead = is.read(bs, 0, 3); - assertEquals(bytesRead, 3, "Wrong bytesRead partial buffer"); + assertEquals(3, bytesRead, "Wrong bytesRead partial buffer"); addExpectedEvent(IOEvent.createSocketReadEvent(bytesRead, s)); bytesRead = is.read(bs); - assertEquals(bytesRead, writeBuf.length, "Wrong bytesRead full buffer"); + assertEquals(writeBuf.length, bytesRead, "Wrong bytesRead full buffer"); addExpectedEvent(IOEvent.createSocketReadEvent(bytesRead, s)); // Try to read more, but writer have closed. Should // get EOF. readInt = is.read(); - assertEquals(readInt, -1, "Wrong readInt at EOF"); + assertEquals(-1, readInt, "Wrong readInt at EOF"); addExpectedEvent(IOEvent.createSocketReadEvent(-1, s)); } } @@ -130,7 +129,7 @@ public void xrun() throws IOException { private static void testConnectException() throws Throwable { try (Recording recording = new Recording()) { try (ServerSocket ss = new ServerSocket()) { - recording.enable(IOEvent.EVENT_SOCKET_CONNECT).withThreshold(Duration.ofMillis(0)); + recording.enable(IOEvent.EVENT_SOCKET_CONNECT_FAILED); recording.start(); InetAddress lb = InetAddress.getLoopbackAddress(); @@ -148,8 +147,8 @@ private static void testConnectException() throws Throwable { recording.stop(); List events = Events.fromRecording(recording); - Asserts.assertEquals(events.size(), 1); - IOHelper.checkConnectionEventException(events.get(0), connectException); + Asserts.assertEquals(1, events.size()); + IOHelper.checkConnectEventException(events.get(0), connectException); } } } diff --git a/test/lib/jdk/test/lib/jfr/EventNames.java b/test/lib/jdk/test/lib/jfr/EventNames.java index 913b59761ce37..daebf2b6466c3 100644 --- a/test/lib/jdk/test/lib/jfr/EventNames.java +++ b/test/lib/jdk/test/lib/jfr/EventNames.java @@ -194,6 +194,7 @@ public class EventNames { public static final String FileRead = PREFIX + "FileRead"; public static final String FileWrite = PREFIX + "FileWrite"; public static final String SocketConnect = PREFIX + "SocketConnect"; + public static final String SocketConnectFailed = PREFIX + "SocketConnectFailed"; public static final String SocketRead = PREFIX + "SocketRead"; public static final String SocketWrite = PREFIX + "SocketWrite"; public static final String ExceptionStatistics = PREFIX + "ExceptionStatistics"; From 5e55ffc6769ac8bab841522886385e2ba1ed8c34 Mon Sep 17 00:00:00 2001 From: Tim Prinzing Date: Tue, 10 Dec 2024 15:33:12 -0600 Subject: [PATCH 10/11] requests fixes - Use IOException.toString() instead of getMessage() in case it's empty - Attempts to test connect exceptions may fail due to unexpected successful connect. Tests quit with uncompleted status if the connect is successful and are retried a small number of times until the test can be performed properly. If the retries are exceeded an exception is generated indicating the test can't be setup properly. --- .../event/SocketConnectFailedEvent.java | 4 +- .../jfr/events/SocketConnectFailedEvent.java | 2 +- test/jdk/jdk/jfr/event/io/IOHelper.java | 2 +- .../jfr/event/io/TestSocketAdapterEvents.java | 13 +++++- .../jfr/event/io/TestSocketChannelEvents.java | 44 ++++++++++++++----- .../jdk/jfr/event/io/TestSocketEvents.java | 13 +++++- 6 files changed, 59 insertions(+), 19 deletions(-) diff --git a/src/java.base/share/classes/jdk/internal/event/SocketConnectFailedEvent.java b/src/java.base/share/classes/jdk/internal/event/SocketConnectFailedEvent.java index 670942b25143d..af9508ab1d502 100644 --- a/src/java.base/share/classes/jdk/internal/event/SocketConnectFailedEvent.java +++ b/src/java.base/share/classes/jdk/internal/event/SocketConnectFailedEvent.java @@ -114,7 +114,7 @@ public static long timestamp() { public static void offer(long start, SocketAddress remote, IOException connectEx) { long duration = timestamp() - start; if (shouldCommit(duration)) { - String msg = connectEx.getMessage(); + String msg = connectEx.toString(); if (remote instanceof InetSocketAddress isa) { commit(start, duration, isa.getHostString(), isa.getAddress().getHostAddress(), isa.getPort(), msg); } else if (remote instanceof UnixDomainSocketAddress udsa) { @@ -141,7 +141,7 @@ public static void offer(long start, SocketAddress remote, IOException connectEx public static void offer(long start, String host, InetAddress address, int port, IOException connectEx) { long duration = timestamp() - start; if (shouldCommit(duration)) { - commit(start, duration, host, address.getHostAddress(), port, connectEx.getMessage()); + commit(start, duration, host, address.getHostAddress(), port, connectEx.toString()); } } } diff --git a/src/jdk.jfr/share/classes/jdk/jfr/events/SocketConnectFailedEvent.java b/src/jdk.jfr/share/classes/jdk/jfr/events/SocketConnectFailedEvent.java index 09b91d6cf39b2..751e9193b2d7f 100644 --- a/src/jdk.jfr/share/classes/jdk/jfr/events/SocketConnectFailedEvent.java +++ b/src/jdk.jfr/share/classes/jdk/jfr/events/SocketConnectFailedEvent.java @@ -49,4 +49,4 @@ public class SocketConnectFailedEvent extends MirrorEvent { @Label("Connect Exception Message") public String connectExceptionMessage; -} \ No newline at end of file +} diff --git a/test/jdk/jdk/jfr/event/io/IOHelper.java b/test/jdk/jdk/jfr/event/io/IOHelper.java index b672a9d68d83d..930e5ec2d778c 100644 --- a/test/jdk/jdk/jfr/event/io/IOHelper.java +++ b/test/jdk/jdk/jfr/event/io/IOHelper.java @@ -136,6 +136,6 @@ public static void checkConnectEventException(RecordedEvent event, IOException i Asserts.assertEquals(IOEvent.EVENT_SOCKET_CONNECT_FAILED, event.getEventType().getName()); Asserts.assertNotNull(ioe); String eventMessage = event.getString("connectExceptionMessage"); - Asserts.assertEquals(eventMessage, ioe.getMessage()); + Asserts.assertEquals(eventMessage, ioe.toString()); } } diff --git a/test/jdk/jdk/jfr/event/io/TestSocketAdapterEvents.java b/test/jdk/jdk/jfr/event/io/TestSocketAdapterEvents.java index cb77015af1269..e487f854826a9 100644 --- a/test/jdk/jdk/jfr/event/io/TestSocketAdapterEvents.java +++ b/test/jdk/jdk/jfr/event/io/TestSocketAdapterEvents.java @@ -56,6 +56,7 @@ public class TestSocketAdapterEvents { private static final int writeInt = 'A'; private static final byte[] writeBuf = { 'B', 'C', 'D', 'E' }; + private static final int MAX_ATTEMPTS = 5; private List expectedEvents = new ArrayList<>(); @@ -65,7 +66,12 @@ private synchronized void addExpectedEvent(IOEvent event) { public static void main(String[] args) throws Throwable { new TestSocketAdapterEvents().test(); - testConnectException(); + boolean completed = false; + for (int ntries = 0; (completed == false) && (ntries < MAX_ATTEMPTS); ++ntries) { + completed = testConnectException(); + } + if (! completed) + throw new Exception("Unable to setup connect exception"); } private void test() throws Throwable { @@ -129,7 +135,7 @@ public void xrun() throws IOException { } } - private static void testConnectException() throws Throwable { + private static boolean testConnectException() throws Throwable { try (Recording recording = new Recording()) { try (ServerSocketChannel ssc = ServerSocketChannel.open()) { recording.enable(IOEvent.EVENT_SOCKET_CONNECT_FAILED); @@ -144,6 +150,8 @@ private static void testConnectException() throws Throwable { try (SocketChannel sc = SocketChannel.open()) { Socket s = sc.socket(); s.connect(addr); + // unexpected, abandon the test + return false; } catch (IOException ioe) { // we expect this connectException = ioe; @@ -153,6 +161,7 @@ private static void testConnectException() throws Throwable { List events = Events.fromRecording(recording); Asserts.assertEquals(1, events.size()); IOHelper.checkConnectEventException(events.get(0), connectException); + return true; } } } diff --git a/test/jdk/jdk/jfr/event/io/TestSocketChannelEvents.java b/test/jdk/jdk/jfr/event/io/TestSocketChannelEvents.java index f2bc147bfee18..02ed154aaaafe 100644 --- a/test/jdk/jdk/jfr/event/io/TestSocketChannelEvents.java +++ b/test/jdk/jdk/jfr/event/io/TestSocketChannelEvents.java @@ -53,6 +53,7 @@ public class TestSocketChannelEvents { private static final int bufSizeA = 10; private static final int bufSizeB = 20; + private static final int MAX_ATTEMPTS = 5; private List expectedEvents = new ArrayList<>(); @@ -63,8 +64,21 @@ private synchronized void addExpectedEvent(IOEvent event) { public static void main(String[] args) throws Throwable { new TestSocketChannelEvents().test(); new TestSocketChannelEvents().testNonBlockingConnect(); - testConnectException(); - testNonBlockingConnectException(); + + boolean completed = false; + for (int ntries = 0; (completed == false) && (ntries < MAX_ATTEMPTS); ++ntries) { + completed = testConnectException(); + } + if (! completed) + throw new Exception("Unable to setup connect exception"); + + completed = false; + for (int ntries = 0; (completed == false) && (ntries < MAX_ATTEMPTS); ++ntries) { + completed = testNonBlockingConnectException(); + } + if (! completed) + throw new Exception("Unable to setup non-blocking connect exception"); + } private void test() throws Throwable { @@ -165,7 +179,7 @@ private void testNonBlockingConnect() throws Throwable { } } - private static void testConnectException() throws Throwable { + private static boolean testConnectException() throws Throwable { try (Recording recording = new Recording()) { try (ServerSocketChannel ssc = ServerSocketChannel.open()) { recording.enable(IOEvent.EVENT_SOCKET_CONNECT_FAILED); @@ -179,6 +193,8 @@ private static void testConnectException() throws Throwable { // try to connect, but the server will not accept IOException connectException = null; try (SocketChannel sc = SocketChannel.open(addr)) { + // unexpected connect, abandon the test + return false; } catch (IOException ioe) { // we expect this connectException = ioe; @@ -188,11 +204,12 @@ private static void testConnectException() throws Throwable { List events = Events.fromRecording(recording); Asserts.assertEquals(1, events.size()); IOHelper.checkConnectEventException(events.get(0), connectException); + return true; } } } - private static void testNonBlockingConnectException() throws Throwable { + private static boolean testNonBlockingConnectException() throws Throwable { try (Recording recording = new Recording()) { try (ServerSocketChannel ssc = ServerSocketChannel.open()) { recording.enable(IOEvent.EVENT_SOCKET_CONNECT_FAILED); @@ -206,20 +223,25 @@ private static void testNonBlockingConnectException() throws Throwable { IOException connectException = null; try (SocketChannel sc = SocketChannel.open()) { sc.configureBlocking(false); - boolean connected = sc.connect(addr); - while (!connected) { - Thread.sleep(10); - connected = sc.finishConnect(); + try { + boolean connected = sc.connect(addr); + while (!connected) { + Thread.sleep(10); + connected = sc.finishConnect(); + } + // unexpected connect, abandon the test + return false; + } catch (IOException ioe) { + // we expect this + connectException = ioe; } - } catch (IOException ioe) { - // we expect this - connectException = ioe; } recording.stop(); List events = Events.fromRecording(recording); Asserts.assertEquals(1, events.size()); IOHelper.checkConnectEventException(events.get(0), connectException); + return true; } } } diff --git a/test/jdk/jdk/jfr/event/io/TestSocketEvents.java b/test/jdk/jdk/jfr/event/io/TestSocketEvents.java index 1acb432936ac5..c90e625130cbf 100644 --- a/test/jdk/jdk/jfr/event/io/TestSocketEvents.java +++ b/test/jdk/jdk/jfr/event/io/TestSocketEvents.java @@ -55,6 +55,7 @@ public class TestSocketEvents { private static final int writeInt = 'A'; private static final byte[] writeBuf = { 'B', 'C', 'D', 'E' }; + private static final int MAX_ATTEMPTS = 5; private List expectedEvents = new ArrayList<>(); @@ -64,7 +65,12 @@ private synchronized void addExpectedEvent(IOEvent event) { public static void main(String[] args) throws Throwable { new TestSocketEvents().test(); - testConnectException(); + boolean completed = false; + for (int ntries = 0; (completed == false) && (ntries < MAX_ATTEMPTS); ++ntries) { + completed = testConnectException(); + } + if (! completed) + throw new Exception("Unable to setup connect exception"); } private void test() throws Throwable { @@ -126,7 +132,7 @@ public void xrun() throws IOException { } } - private static void testConnectException() throws Throwable { + private static boolean testConnectException() throws Throwable { try (Recording recording = new Recording()) { try (ServerSocket ss = new ServerSocket()) { recording.enable(IOEvent.EVENT_SOCKET_CONNECT_FAILED); @@ -140,6 +146,8 @@ private static void testConnectException() throws Throwable { IOException connectException = null; try (Socket s = new Socket()) { s.connect(addr); + // unexpected, abandon the test + return false; } catch (IOException ioe) { // we expect this connectException = ioe; @@ -149,6 +157,7 @@ private static void testConnectException() throws Throwable { List events = Events.fromRecording(recording); Asserts.assertEquals(1, events.size()); IOHelper.checkConnectEventException(events.get(0), connectException); + return true; } } } From 81cea489604f01d6015405aa5a49baf06934e790 Mon Sep 17 00:00:00 2001 From: Tim Prinzing Date: Mon, 16 Dec 2024 14:14:13 -0600 Subject: [PATCH 11/11] Refactored connect exception tests to a single IOHelper method that takes a function argument that is excpected to produce the appropriate exception. IANA reserved ports in the range 225 to 241 are used to attempt to produce a connect exception. --- test/jdk/jdk/jfr/event/io/IOHelper.java | 60 +++++++++++- .../jfr/event/io/TestSocketAdapterEvents.java | 45 ++------- .../jfr/event/io/TestSocketChannelEvents.java | 96 ++++--------------- .../jdk/jfr/event/io/TestSocketEvents.java | 43 ++------- 4 files changed, 92 insertions(+), 152 deletions(-) diff --git a/test/jdk/jdk/jfr/event/io/IOHelper.java b/test/jdk/jdk/jfr/event/io/IOHelper.java index 930e5ec2d778c..bfa666857b40d 100644 --- a/test/jdk/jdk/jfr/event/io/IOHelper.java +++ b/test/jdk/jdk/jfr/event/io/IOHelper.java @@ -27,11 +27,15 @@ import static jdk.test.lib.Asserts.assertTrue; import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; +import jdk.jfr.Recording; import jdk.jfr.event.io.IOEvent.EventType; import jdk.jfr.consumer.RecordedEvent; @@ -132,10 +136,56 @@ private static void logEvents(List actualEvents, List expected } } - public static void checkConnectEventException(RecordedEvent event, IOException ioe) { - Asserts.assertEquals(IOEvent.EVENT_SOCKET_CONNECT_FAILED, event.getEventType().getName()); - Asserts.assertNotNull(ioe); - String eventMessage = event.getString("connectExceptionMessage"); - Asserts.assertEquals(eventMessage, ioe.toString()); + public interface ConnectExceptionMaker { + /** + * Implementation should attempt to connect to the given address, which + * should cause an exception to be generated. + * @return the exception generated, or null if the connection was + * unexpectedly successful. + * @throws Throwable if something else failed + */ + IOException generateConnectException(SocketAddress addr) throws Throwable; + } + + /** + * Attempt to test JFR events for an exception condition while attempting to connect + * a socket. The given function attempts to make the connection which we would like to + * fail so we can inspect the associated JFR event. To do this a range of IANA reserved + * ports are used which it is expected will be unused. + * + * @param func an implementation of a connection attempt + * @throws Throwable + */ + public static void testConnectException(ConnectExceptionMaker func) throws Throwable { + InetAddress lb = InetAddress.getLoopbackAddress(); + boolean completed = false; + for (int port = 225; (completed == false) && (port <= 241); ++port) { + completed = testConnectExceptionOnPort(new InetSocketAddress(lb, port), func); + } + if (! completed) + throw new Exception("Unable to setup connect exception"); + } + + private static boolean testConnectExceptionOnPort(SocketAddress addr, ConnectExceptionMaker func) throws Throwable { + try (Recording recording = new Recording()) { + recording.enable(IOEvent.EVENT_SOCKET_CONNECT_FAILED); + recording.start(); + + // try to connect to a port we expect to be unused + // to generate an exception + IOException connectException = func.generateConnectException(addr); + if (connectException == null) + return false; + + recording.stop(); + List events = Events.fromRecording(recording); + Asserts.assertEquals(1, events.size()); + RecordedEvent event = events.get(0); + Asserts.assertEquals(IOEvent.EVENT_SOCKET_CONNECT_FAILED, event.getEventType().getName()); + Asserts.assertNotNull(connectException); + String eventMessage = event.getString("connectExceptionMessage"); + Asserts.assertEquals(eventMessage, connectException.toString()); + return true; + } } } diff --git a/test/jdk/jdk/jfr/event/io/TestSocketAdapterEvents.java b/test/jdk/jdk/jfr/event/io/TestSocketAdapterEvents.java index e487f854826a9..7eef7734444dd 100644 --- a/test/jdk/jdk/jfr/event/io/TestSocketAdapterEvents.java +++ b/test/jdk/jdk/jfr/event/io/TestSocketAdapterEvents.java @@ -39,7 +39,6 @@ import jdk.jfr.Recording; import jdk.jfr.consumer.RecordedEvent; -import jdk.test.lib.Asserts; import jdk.test.lib.jfr.Events; import jdk.test.lib.thread.TestThread; import jdk.test.lib.thread.XRun; @@ -56,7 +55,6 @@ public class TestSocketAdapterEvents { private static final int writeInt = 'A'; private static final byte[] writeBuf = { 'B', 'C', 'D', 'E' }; - private static final int MAX_ATTEMPTS = 5; private List expectedEvents = new ArrayList<>(); @@ -66,12 +64,7 @@ private synchronized void addExpectedEvent(IOEvent event) { public static void main(String[] args) throws Throwable { new TestSocketAdapterEvents().test(); - boolean completed = false; - for (int ntries = 0; (completed == false) && (ntries < MAX_ATTEMPTS); ++ntries) { - completed = testConnectException(); - } - if (! completed) - throw new Exception("Unable to setup connect exception"); + IOHelper.testConnectException(TestSocketAdapterEvents::makeConnectException); } private void test() throws Throwable { @@ -135,34 +128,14 @@ public void xrun() throws IOException { } } - private static boolean testConnectException() throws Throwable { - try (Recording recording = new Recording()) { - try (ServerSocketChannel ssc = ServerSocketChannel.open()) { - recording.enable(IOEvent.EVENT_SOCKET_CONNECT_FAILED); - recording.start(); - - InetAddress lb = InetAddress.getLoopbackAddress(); - ssc.bind(new InetSocketAddress(lb, 0)); - SocketAddress addr = ssc.getLocalAddress(); - ssc.close(); - - IOException connectException = null; - try (SocketChannel sc = SocketChannel.open()) { - Socket s = sc.socket(); - s.connect(addr); - // unexpected, abandon the test - return false; - } catch (IOException ioe) { - // we expect this - connectException = ioe; - } - - recording.stop(); - List events = Events.fromRecording(recording); - Asserts.assertEquals(1, events.size()); - IOHelper.checkConnectEventException(events.get(0), connectException); - return true; - } + private static IOException makeConnectException(SocketAddress addr) throws Throwable { + IOException connectException = null; + try (SocketChannel sc = SocketChannel.open()) { + Socket s = sc.socket(); + s.connect(addr); + } catch (IOException ioe) { + connectException = ioe; } + return connectException; } } diff --git a/test/jdk/jdk/jfr/event/io/TestSocketChannelEvents.java b/test/jdk/jdk/jfr/event/io/TestSocketChannelEvents.java index 02ed154aaaafe..9455487d404c8 100644 --- a/test/jdk/jdk/jfr/event/io/TestSocketChannelEvents.java +++ b/test/jdk/jdk/jfr/event/io/TestSocketChannelEvents.java @@ -37,7 +37,6 @@ import jdk.jfr.Recording; import jdk.jfr.consumer.RecordedEvent; -import jdk.test.lib.Asserts; import jdk.test.lib.jfr.Events; import jdk.test.lib.thread.TestThread; import jdk.test.lib.thread.XRun; @@ -53,7 +52,6 @@ public class TestSocketChannelEvents { private static final int bufSizeA = 10; private static final int bufSizeB = 20; - private static final int MAX_ATTEMPTS = 5; private List expectedEvents = new ArrayList<>(); @@ -64,21 +62,8 @@ private synchronized void addExpectedEvent(IOEvent event) { public static void main(String[] args) throws Throwable { new TestSocketChannelEvents().test(); new TestSocketChannelEvents().testNonBlockingConnect(); - - boolean completed = false; - for (int ntries = 0; (completed == false) && (ntries < MAX_ATTEMPTS); ++ntries) { - completed = testConnectException(); - } - if (! completed) - throw new Exception("Unable to setup connect exception"); - - completed = false; - for (int ntries = 0; (completed == false) && (ntries < MAX_ATTEMPTS); ++ntries) { - completed = testNonBlockingConnectException(); - } - if (! completed) - throw new Exception("Unable to setup non-blocking connect exception"); - + IOHelper.testConnectException(TestSocketChannelEvents::makeBlockingConnectException); + IOHelper.testConnectException(TestSocketChannelEvents::makeNonBlockingConnectException); } private void test() throws Throwable { @@ -179,70 +164,29 @@ private void testNonBlockingConnect() throws Throwable { } } - private static boolean testConnectException() throws Throwable { - try (Recording recording = new Recording()) { - try (ServerSocketChannel ssc = ServerSocketChannel.open()) { - recording.enable(IOEvent.EVENT_SOCKET_CONNECT_FAILED); - recording.start(); - - InetAddress lb = InetAddress.getLoopbackAddress(); - ssc.bind(new InetSocketAddress(lb, 0)); - SocketAddress addr = ssc.getLocalAddress(); - ssc.close(); - - // try to connect, but the server will not accept - IOException connectException = null; - try (SocketChannel sc = SocketChannel.open(addr)) { - // unexpected connect, abandon the test - return false; - } catch (IOException ioe) { - // we expect this - connectException = ioe; - } - - recording.stop(); - List events = Events.fromRecording(recording); - Asserts.assertEquals(1, events.size()); - IOHelper.checkConnectEventException(events.get(0), connectException); - return true; - } + private static IOException makeBlockingConnectException(SocketAddress addr) throws Throwable { + IOException connectException = null; + try (SocketChannel sc = SocketChannel.open(addr)) { + } catch (IOException ioe) { + connectException = ioe; } + return connectException; } - private static boolean testNonBlockingConnectException() throws Throwable { - try (Recording recording = new Recording()) { - try (ServerSocketChannel ssc = ServerSocketChannel.open()) { - recording.enable(IOEvent.EVENT_SOCKET_CONNECT_FAILED); - recording.start(); - - InetAddress lb = InetAddress.getLoopbackAddress(); - ssc.bind(new InetSocketAddress(lb, 0)); - SocketAddress addr = ssc.getLocalAddress(); - ssc.close(); - - IOException connectException = null; - try (SocketChannel sc = SocketChannel.open()) { - sc.configureBlocking(false); - try { - boolean connected = sc.connect(addr); - while (!connected) { - Thread.sleep(10); - connected = sc.finishConnect(); - } - // unexpected connect, abandon the test - return false; - } catch (IOException ioe) { - // we expect this - connectException = ioe; - } + private static IOException makeNonBlockingConnectException(SocketAddress addr) throws Throwable { + IOException connectException = null; + try (SocketChannel sc = SocketChannel.open()) { + sc.configureBlocking(false); + try { + boolean connected = sc.connect(addr); + while (!connected) { + Thread.sleep(10); + connected = sc.finishConnect(); } - - recording.stop(); - List events = Events.fromRecording(recording); - Asserts.assertEquals(1, events.size()); - IOHelper.checkConnectEventException(events.get(0), connectException); - return true; + } catch (IOException ioe) { + connectException = ioe; } } + return connectException; } } diff --git a/test/jdk/jdk/jfr/event/io/TestSocketEvents.java b/test/jdk/jdk/jfr/event/io/TestSocketEvents.java index c90e625130cbf..3bbe8b6cd8164 100644 --- a/test/jdk/jdk/jfr/event/io/TestSocketEvents.java +++ b/test/jdk/jdk/jfr/event/io/TestSocketEvents.java @@ -38,7 +38,6 @@ import jdk.jfr.Recording; import jdk.jfr.consumer.RecordedEvent; -import jdk.test.lib.Asserts; import jdk.test.lib.jfr.Events; import jdk.test.lib.thread.TestThread; import jdk.test.lib.thread.XRun; @@ -55,7 +54,6 @@ public class TestSocketEvents { private static final int writeInt = 'A'; private static final byte[] writeBuf = { 'B', 'C', 'D', 'E' }; - private static final int MAX_ATTEMPTS = 5; private List expectedEvents = new ArrayList<>(); @@ -65,12 +63,7 @@ private synchronized void addExpectedEvent(IOEvent event) { public static void main(String[] args) throws Throwable { new TestSocketEvents().test(); - boolean completed = false; - for (int ntries = 0; (completed == false) && (ntries < MAX_ATTEMPTS); ++ntries) { - completed = testConnectException(); - } - if (! completed) - throw new Exception("Unable to setup connect exception"); + IOHelper.testConnectException(TestSocketEvents::makeConnectException); } private void test() throws Throwable { @@ -132,33 +125,13 @@ public void xrun() throws IOException { } } - private static boolean testConnectException() throws Throwable { - try (Recording recording = new Recording()) { - try (ServerSocket ss = new ServerSocket()) { - recording.enable(IOEvent.EVENT_SOCKET_CONNECT_FAILED); - recording.start(); - - InetAddress lb = InetAddress.getLoopbackAddress(); - ss.bind(new InetSocketAddress(lb, 0)); - SocketAddress addr = ss.getLocalSocketAddress(); - ss.close(); - - IOException connectException = null; - try (Socket s = new Socket()) { - s.connect(addr); - // unexpected, abandon the test - return false; - } catch (IOException ioe) { - // we expect this - connectException = ioe; - } - - recording.stop(); - List events = Events.fromRecording(recording); - Asserts.assertEquals(1, events.size()); - IOHelper.checkConnectEventException(events.get(0), connectException); - return true; - } + private static IOException makeConnectException(SocketAddress addr) throws Throwable { + IOException connectException = null; + try (Socket s = new Socket()) { + s.connect(addr); + } catch (IOException ioe) { + connectException = ioe; } + return connectException; } }