- 
                Notifications
    You must be signed in to change notification settings 
- Fork 6.1k
8310996: Add JFR event for connect operations #21528
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e801770
              9e1cce5
              4f844ca
              06a3120
              05227c9
              7113bd7
              a8898ff
              ce9d39e
              13f8157
              f7b3be0
              a379609
              5e55ffc
              81cea48
              File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| /* | ||
| * 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.InetAddress; | ||
| import java.net.InetSocketAddress; | ||
| import java.net.SocketAddress; | ||
| import java.net.UnixDomainSocketAddress; | ||
|  | ||
| /** | ||
| * 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)} 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; | ||
|  | ||
| /** | ||
| * Actually commit an event. The implementation is generated automatically. | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assume "Actually" should be removed, this method commits an event. | ||
| * 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 | ||
| */ | ||
| public static void commit(long start, long duration, String host, String address, int port) { | ||
| // 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)}. | ||
| * | ||
| * @param start the start time | ||
| * @param remote the address of the remote socket | ||
| */ | ||
| public static void offer(long start, SocketAddress remote) { | ||
| long duration = timestamp() - start; | ||
| if (shouldCommit(duration)) { | ||
| if (remote instanceof InetSocketAddress isa) { | ||
| 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); | ||
| } | ||
| } | ||
| } | ||
|  | ||
| /** | ||
| * 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 | ||
| */ | ||
| 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); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -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.toString(); | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should the description for this field in the event be updated too? | ||
| 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.toString()); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -53,6 +53,8 @@ | |
|  | ||
| 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; | ||
|  | @@ -563,6 +565,9 @@ protected void connect(SocketAddress remote, int millis) throws IOException { | |
| address = InetAddress.getLocalHost(); | ||
| int port = isa.getPort(); | ||
|  | ||
| long connectStart = 0L; | ||
| IOException connectEx = null; | ||
|  | ||
| ReentrantLock connectLock = readLock; | ||
| try { | ||
| connectLock.lock(); | ||
|  | @@ -571,6 +576,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 | ||
|  | @@ -600,14 +606,28 @@ protected void connect(SocketAddress remote, int millis) throws IOException { | |
| } catch (IOException ioe) { | ||
| close(); | ||
| if (ioe instanceof SocketTimeoutException) { | ||
| throw ioe; | ||
| connectEx = ioe; | ||
| } else if (ioe instanceof InterruptedIOException) { | ||
| assert Thread.currentThread().isVirtual(); | ||
| throw new SocketException("Closed by interrupt"); | ||
| connectEx = new SocketException("Closed by interrupt"); | ||
| } else { | ||
| throw SocketExceptions.of(ioe, isa); | ||
| connectEx = SocketExceptions.of(ioe, isa); | ||
| } | ||
| } | ||
|  | ||
| // 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); | ||
| } | ||
| } | ||
| There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be possible to update the JBS or PR description to indicate if the intent is to record an event when the connection cannot be established? I'm asking because the change will only record an event when a connection is successfully established ("connected" is always true here). JFR will record exceptions already of course but I think for troubleshooting purposes, recording an event when "connect" hangs and eventually fails is very useful to have. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Capturing all calls even if they threw an exception does seem pretty useful. I'll update the JBS | ||
|  | ||
| if (connectEx != null) { | ||
| throw connectEx; | ||
| } | ||
| } | ||
|  | ||
| @Override | ||
|  | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In passing, there is a mix of "JFR" and "jfr" through-out, I assume you want "JFR" everywhere.