diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/throttling/JfrAdaptiveSampler.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/throttling/JfrAdaptiveSampler.java index 1216e57ab43d..bf83944e5643 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/throttling/JfrAdaptiveSampler.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/throttling/JfrAdaptiveSampler.java @@ -28,6 +28,7 @@ import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE; +import com.oracle.svm.core.util.BasedOnJDKFile; import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.headers.LibM; import com.oracle.svm.core.jdk.UninterruptibleUtils; @@ -65,6 +66,7 @@ abstract class JfrAdaptiveSampler { activeWindow = window0; } + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-23+8/src/hotspot/share/jfr/support/jfrAdaptiveSampler.cpp#L79-L89") @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) protected boolean sample(long timestampNs) { boolean expired = activeWindow.isExpired(timestampNs); @@ -100,6 +102,7 @@ private void rotate(JfrSamplerWindow expired) { @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) protected abstract JfrSamplerParams nextWindowParams(); + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-23+8/src/hotspot/share/jfr/support/jfrAdaptiveSampler.cpp#L145-L156") @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private void configure(JfrSamplerParams params, JfrSamplerWindow expired, JfrSamplerWindow next) { if (params.reconfigure) { @@ -116,11 +119,13 @@ private void configure(JfrSamplerParams params, JfrSamplerWindow expired, JfrSam next.initialize(params.windowDurationMs); } + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-23+8/src/hotspot/share/jfr/support/jfrAdaptiveSampler.cpp#L173-L175") @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static double computeEwmaAlphaCoefficient(long lookbackCount) { return lookbackCount <= 1 ? 1d : 1d / lookbackCount; } + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-23+8/src/hotspot/share/jfr/support/jfrAdaptiveSampler.cpp#L177-L182") @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static long computeAccumulatedDebtCarryLimit(long windowDurationMs) { if (windowDurationMs == 0 || windowDurationMs >= TimeUtils.millisPerSecond) { @@ -129,6 +134,7 @@ private static long computeAccumulatedDebtCarryLimit(long windowDurationMs) { return TimeUtils.millisPerSecond / windowDurationMs; } + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-23+8/src/hotspot/share/jfr/support/jfrAdaptiveSampler.cpp#L217-L229") @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private void setRate(JfrSamplerParams params, JfrSamplerWindow expired, JfrSamplerWindow next) { long sampleSize = projectSampleSize(params, expired); @@ -141,11 +147,13 @@ private void setRate(JfrSamplerParams params, JfrSamplerWindow expired, JfrSampl next.setProjectedPopulationSize(sampleSize * next.getSamplingInterval()); } + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-23+8/src/hotspot/share/jfr/support/jfrAdaptiveSampler.cpp#L236-L238") @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private long projectSampleSize(JfrSamplerParams params, JfrSamplerWindow expired) { return params.samplePointsPerWindow + amortizeDebt(expired); } + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-23+8/src/hotspot/share/jfr/support/jfrAdaptiveSampler.cpp#L259-L269") @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) protected long amortizeDebt(JfrSamplerWindow expired) { long accumulatedDebt = expired.getAccumulatedDebt(); @@ -158,6 +166,7 @@ protected long amortizeDebt(JfrSamplerWindow expired) { return -accumulatedDebt; } + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-23+8/src/hotspot/share/jfr/support/jfrAdaptiveSampler.cpp#L316-L325") @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private long deriveSamplingInterval(double sampleSize, JfrSamplerWindow expired) { assert sampleSize > 0; @@ -176,11 +185,13 @@ private double projectPopulationSize(JfrSamplerWindow expired) { return avgPopulationSize; } + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-23+8/src/hotspot/share/jfr/support/jfrAdaptiveSampler.cpp#L169-L171") @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static double exponentiallyWeightedMovingAverage(double currentMeasurement, double alpha, double prevEwma) { return alpha * currentMeasurement + (1 - alpha) * prevEwma; } + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-23+8/src/hotspot/share/jfr/support/jfrAdaptiveSampler.cpp#L304-L314") @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private long nextGeometric(double p) { double u = prng.nextUniform(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/throttling/JfrEventThrottler.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/throttling/JfrEventThrottler.java index edd65382fd3c..100d6450168d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/throttling/JfrEventThrottler.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/throttling/JfrEventThrottler.java @@ -27,6 +27,7 @@ import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE; +import com.oracle.svm.core.util.BasedOnJDKFile; import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.jfr.JfrTicks; import com.oracle.svm.core.thread.JavaSpinLockUtils; @@ -47,9 +48,11 @@ public class JfrEventThrottler extends JfrAdaptiveSampler { private static final long TEN_PER_1000_MS_IN_HOURS = 36000; private static final long DAY = 24 * HOUR; private static final long TEN_PER_1000_MS_IN_DAYS = 864000; - + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-23+8/src/hotspot/share/jfr/recorder/service/jfrEventThrottler.cpp#L105") // private static final long DEFAULT_WINDOW_LOOKBACK_COUNT = 25; + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-23+8/src/hotspot/share/jfr/recorder/service/jfrEventThrottler.cpp#L112") // private static final long LOW_RATE_UPPER_BOUND = 9; + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-23+8/src/hotspot/share/jfr/recorder/service/jfrEventThrottler.cpp#L113") // private static final long WINDOW_DIVISOR = 5; private static final JfrSamplerParams DISABLED_PARAMS = new JfrSamplerParams(); @@ -96,6 +99,7 @@ protected JfrSamplerParams nextWindowParams() { return disabled ? DISABLED_PARAMS : lastParams; } + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-23+8/src/hotspot/share/jfr/recorder/service/jfrEventThrottler.cpp#L200-L212") @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private void updateParams() { normalize(); @@ -110,6 +114,7 @@ private static boolean isDisabled(long eventSampleSize) { return eventSampleSize == Target_jdk_jfr_internal_settings_ThrottleSetting.OFF; } + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-23+8/src/hotspot/share/jfr/recorder/service/jfrEventThrottler.cpp#L170-L194") @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private void normalize() { if (periodMs == TimeUtils.millisPerSecond) { @@ -130,6 +135,7 @@ private void normalize() { } } + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-23+8/src/hotspot/share/jfr/recorder/service/jfrEventThrottler.cpp#L145-L165") @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static void setSamplePointsAndWindowDuration(JfrSamplerParams params, long sampleSize, long periodMs) { assert sampleSize != Target_jdk_jfr_internal_settings_ThrottleSetting.OFF; @@ -150,12 +156,14 @@ private static void setSamplePointsAndWindowDuration(JfrSamplerParams params, lo } } + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-23+8/src/hotspot/share/jfr/recorder/service/jfrEventThrottler.cpp#L134-L137") @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static void setLowRate(JfrSamplerParams params, long eventSampleSize, long periodMs) { params.samplePointsPerWindow = eventSampleSize; params.windowDurationMs = periodMs; } + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-23+8/src/hotspot/share/jfr/recorder/service/jfrEventThrottler.cpp#L122-L132") @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private static void setWindowLookback(JfrSamplerParams params) { if (params.windowDurationMs <= TimeUtils.millisPerSecond) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/throttling/JfrSamplerWindow.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/throttling/JfrSamplerWindow.java index 4ff5e7bdea90..bd046568e723 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/throttling/JfrSamplerWindow.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/throttling/JfrSamplerWindow.java @@ -27,6 +27,7 @@ import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE; +import com.oracle.svm.core.util.BasedOnJDKFile; import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.jdk.UninterruptibleUtils; import com.oracle.svm.core.jfr.JfrTicks; @@ -68,6 +69,7 @@ public void copyParams(JfrSamplerParams other) { this.params.initializeFrom(other); } + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-23+8/src/hotspot/share/jfr/support/jfrAdaptiveSampler.cpp#L104-L108") @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public boolean sample() { long ordinal = measuredPopulationSize.incrementAndGet(); @@ -99,21 +101,25 @@ public void setProjectedPopulationSize(long value) { projectedPopulationSize = value; } + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-23+8/src/hotspot/share/jfr/support/jfrAdaptiveSampler.cpp#L285-L287") @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) public long getAccumulatedDebt() { return projectedPopulationSize == 0 ? 0 : (params.samplePointsPerWindow - getMaxSampleSize()) + getDebt(); } + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-23+8/src/hotspot/share/jfr/support/jfrAdaptiveSampler.cpp#L289-L291") @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private long getDebt() { return projectedPopulationSize == 0 ? 0 : getSampleSize() - params.samplePointsPerWindow; } + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-23+8/src/hotspot/share/jfr/support/jfrAdaptiveSampler.cpp#L271-L273") @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private long getMaxSampleSize() { return projectedPopulationSize / samplingInterval; } + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-23+8/src/hotspot/share/jfr/support/jfrAdaptiveSampler.cpp#L276-L279") @Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true) private long getSampleSize() { long size = getPopulationSize(); diff --git a/substratevm/src/com.oracle.svm.processor/src/com/oracle/svm/processor/BasedOnJDKFileProcessor.java b/substratevm/src/com.oracle.svm.processor/src/com/oracle/svm/processor/BasedOnJDKFileProcessor.java index f05206c68f5f..04fc639695ee 100644 --- a/substratevm/src/com.oracle.svm.processor/src/com/oracle/svm/processor/BasedOnJDKFileProcessor.java +++ b/substratevm/src/com.oracle.svm.processor/src/com/oracle/svm/processor/BasedOnJDKFileProcessor.java @@ -73,7 +73,7 @@ public class BasedOnJDKFileProcessor extends AbstractProcessor { static final String ANNOTATION_CLASS_NAME = "com.oracle.svm.core.util.BasedOnJDKFile"; static final String ANNOTATION_LIST_CLASS_NAME = "com.oracle.svm.core.util.BasedOnJDKFile.List"; static final Pattern FILE_PATTERN = Pattern - .compile("^https://github.com/openjdk/jdk/blob/(?[^/]+)/(?[-_.A-Za-z0-9][-_./A-Za-z0-9]*)(#L(?[0-9]+)-L(?[0-9]+))?$"); + .compile("^https://github.com/openjdk/jdk/blob/(?[^/]+)/(?[-_.A-Za-z0-9][-_./A-Za-z0-9]*)(#L(?[0-9]+)(-L(?[0-9]+))?)?$"); static final String FILE_PATTERN_STR = "https://github.com/openjdk/jdk/blob//path/to/file.ext(#L[0-9]+-L[0-9]+)?"; public static final int FULL_FILE_LINE_MARKER = 0; diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestFlightRecorderEvents.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestFlightRecorderEvents.java new file mode 100644 index 000000000000..b3693fa9ff7d --- /dev/null +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestFlightRecorderEvents.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2024, Red Hat Inc. 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 com.oracle.svm.test.jfr; + +import com.oracle.svm.core.jfr.JfrEvent; +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import org.junit.Test; + +import java.time.Duration; +import java.util.List; + +import static org.junit.Assert.assertTrue; + +public class TestFlightRecorderEvents extends JfrRecordingTest { + private static final long THRESHOLD = 12345678; + private static final long MAX_SIZE = 33554432; + + @Test + public void test() throws Throwable { + String[] events = new String[]{"jdk.ActiveRecording", "jdk.ActiveSetting"}; + /* Set properties before recording is started so we can accurately validate them later. */ + Recording recording = prepareRecording(events, getDefaultConfiguration(), null, createTempJfrFile()); + recording.enable(JfrEvent.ThreadPark.getName()).withThreshold(Duration.ofNanos(THRESHOLD)); + recording.setMaxSize(MAX_SIZE); + recording.start(); + stopRecording(recording, TestFlightRecorderEvents::validateEvents); + } + + private static void validateEvents(List events) { + boolean foundActiveRecording = false; + boolean foundActiveSetting = false; + for (RecordedEvent e : events) { + if (e.getEventType().getName().equals("jdk.ActiveSetting") && + e.getLong("id") == JfrEvent.ThreadPark.getId() && + e.getString("name").equals("threshold") && + e.getString("value").equals(THRESHOLD + " ns")) { + foundActiveSetting = true; + } else if (e.getEventType().getName().equals("jdk.ActiveRecording") && + e.getLong("maxSize") == MAX_SIZE) { + foundActiveRecording = true; + } + } + assertTrue(foundActiveSetting); + assertTrue(foundActiveRecording); + } +} diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJdkContainerEvents.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJdkContainerEvents.java new file mode 100644 index 000000000000..06df5dc600d1 --- /dev/null +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestJdkContainerEvents.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2024, Red Hat Inc. 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 com.oracle.svm.test.jfr; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import org.junit.Test; + +import java.time.Duration; +import java.util.List; + +import static org.junit.Assert.assertTrue; + +public class TestJdkContainerEvents extends JfrRecordingTest { + private static final long PERIOD_MS = 10; + + @Test + public void test() throws Throwable { + String[] events = new String[]{"jdk.ContainerCPUThrottling", "jdk.ContainerCPUUsage", + "jdk.ContainerConfiguration", "jdk.ContainerIOUsage", "jdk.ContainerMemoryUsage"}; + + Recording recording = prepareRecording(events, getDefaultConfiguration(), null, createTempJfrFile()); + recording.enable("jdk.ContainerCPUThrottling").withPeriod(Duration.ofMillis(PERIOD_MS)); + recording.enable("jdk.ContainerCPUUsage").withPeriod(Duration.ofMillis(PERIOD_MS)); + recording.enable("jdk.ContainerIOUsage").withPeriod(Duration.ofMillis(PERIOD_MS)); + recording.enable("jdk.ContainerMemoryUsage").withPeriod(Duration.ofMillis(PERIOD_MS)); + + recording.start(); + + // Sleep so the periodic events can be emitted. + Thread.sleep(PERIOD_MS * 5); + + stopRecording(recording, TestJdkContainerEvents::validateEvents); + } + + private static void validateEvents(List events) { + boolean foundContainerCPUThrottling = false; + boolean foundContainerCPUUsage = false; + boolean foundContainerIOUsage = false; + boolean foundContainerMemoryUsage = false; + boolean foundContainerConfiguration = false; + for (RecordedEvent e : events) { + if (e.getEventType().getName().equals("jdk.ContainerCPUThrottling")) { + foundContainerCPUThrottling = true; + } else if (e.getEventType().getName().equals("jdk.ContainerCPUUsage") && + e.getLong("cpuTime") > 0 && + e.getLong("cpuSystemTime") > 0 && + e.getLong("cpuUserTime") > 0) { + foundContainerCPUUsage = true; + } else if (e.getEventType().getName().equals("jdk.ContainerIOUsage")) { + foundContainerIOUsage = true; + } else if (e.getEventType().getName().equals("jdk.ContainerMemoryUsage") && + e.getLong("memoryUsage") > 0 && + e.getLong("swapMemoryUsage") > 0) { + foundContainerMemoryUsage = true; + } else if (e.getEventType().getName().equals("jdk.ContainerConfiguration") && + e.getLong("effectiveCpuCount") > 0) { + /* + * It's also worth checking hostTotalMemory and hostTotalSwapMemory are > 0 or -1 + * after GR-52453 is integrated + */ + foundContainerConfiguration = true; + } + } + + assertTrue(foundContainerCPUThrottling); + assertTrue(foundContainerCPUUsage); + assertTrue(foundContainerIOUsage); + assertTrue(foundContainerMemoryUsage); + assertTrue(foundContainerConfiguration); + } +} diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestSocketChannelEvents.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestSocketChannelEvents.java new file mode 100644 index 000000000000..9de174ecc0a7 --- /dev/null +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestSocketChannelEvents.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2024, Red Hat Inc. 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 com.oracle.svm.test.jfr; + +import static org.junit.Assert.assertTrue; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import org.junit.Test; + +import java.io.IOException; + +import java.net.InetSocketAddress; + +import java.nio.ByteBuffer; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.List; + +public class TestSocketChannelEvents extends JfrRecordingTest { + public static final String MESSAGE = "hello server"; + public static final int DEFAULT_SIZE = 1024; + public static int PORT = 9876; + public static String HOST = "127.0.0.1"; + + @Test + public void test() throws Throwable { + String[] events = new String[]{"jdk.SocketRead", "jdk.SocketWrite"}; + Recording recording = startRecording(events); + + Thread serverThread = new Thread(() -> { + GreetServer server = new GreetServer(); + try { + server.start(PORT); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + + serverThread.start(); + GreetClient client = new GreetClient(); + client.startConnection(HOST, PORT); + client.sendMessage(MESSAGE); + serverThread.join(); + stopRecording(recording, TestSocketChannelEvents::validateEvents); + } + + private static void validateEvents(List events) { + boolean foundSocketRead = false; + boolean foundSocketWrite = false; + for (RecordedEvent e : events) { + if (e.getString("host").equals(HOST) && e.getEventType().getName().equals("jdk.SocketRead") && e.getLong("bytesRead") == MESSAGE.getBytes().length) { + foundSocketRead = true; + } else if (e.getString("host").equals(HOST) && e.getEventType().getName().equals("jdk.SocketWrite") && e.getLong("bytesWritten") == MESSAGE.getBytes().length && + e.getLong("port") == PORT) { + foundSocketWrite = true; + } + } + assertTrue(foundSocketRead); + assertTrue(foundSocketWrite); + } + + static class GreetClient { + SocketChannel socketChannel; + + public void startConnection(String ip, int port) throws InterruptedException { + try { + socketChannel = SocketChannel.open(new InetSocketAddress(ip, port)); + } catch (Exception e) { + // Keep trying until server begins accepting connections. + Thread.sleep(100); + startConnection(ip, port); + } + } + + public void sendMessage(String msg) throws IOException { + byte[] bytes = msg.getBytes(); + ByteBuffer buf = ByteBuffer.allocate(bytes.length); + buf.put(bytes); + buf.flip(); + while (buf.hasRemaining()) { + socketChannel.write(buf); + } + socketChannel.close(); + } + } + + static class GreetServer { + public void start(int port) throws IOException { + // Prepare server. + ServerSocketChannel server = ServerSocketChannel.open(); + server.socket().bind(new InetSocketAddress(port)); + + // Block waiting for connection from client. + SocketChannel client = server.accept(); + + // Read data from client. + ByteBuffer readBuf = ByteBuffer.allocate(DEFAULT_SIZE); + client.read(readBuf); + String message = new String(readBuf.array(), 0, readBuf.position()); + assertTrue(MESSAGE.equals(message)); + client.close(); + server.close(); + } + } +} diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestSocketEvents.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestSocketEvents.java new file mode 100644 index 000000000000..0d3d76564aa7 --- /dev/null +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/jfr/TestSocketEvents.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2024, Red Hat Inc. 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 com.oracle.svm.test.jfr; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import org.junit.Test; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.List; + +import static org.junit.Assert.assertTrue; + +public class TestSocketEvents extends JfrRecordingTest { + public static final String MESSAGE = "hello server"; + public static final int DEFAULT_SIZE = 1024; + public static int PORT = 9876; + public static String HOST = "127.0.0.1"; + + @Test + public void test() throws Throwable { + String[] events = new String[]{"jdk.SocketRead", "jdk.SocketWrite"}; + Recording recording = startRecording(events); + + Thread serverThread = new Thread(() -> { + GreetServer server = new GreetServer(); + try { + server.start(PORT); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + + serverThread.start(); + + GreetClient client = new GreetClient(); + client.startConnection(HOST, PORT); + client.sendMessage(MESSAGE); + serverThread.join(); + + stopRecording(recording, TestSocketEvents::validateEvents); + } + + private static void validateEvents(List events) { + boolean foundSocketRead = false; + boolean foundSocketWrite = false; + for (RecordedEvent e : events) { + if (e.getString("host").equals(HOST) && e.getEventType().getName().equals("jdk.SocketRead") && e.getLong("bytesRead") == MESSAGE.getBytes().length) { + foundSocketRead = true; + } else if (e.getString("host").equals(HOST) && e.getEventType().getName().equals("jdk.SocketWrite") && e.getLong("bytesWritten") == MESSAGE.getBytes().length && + e.getLong("port") == PORT) { + foundSocketWrite = true; + } + } + assertTrue(foundSocketRead); + assertTrue(foundSocketWrite); + } + + static class GreetClient { + private Socket clientSocket; + private PrintWriter out; + + public void startConnection(String ip, int port) throws IOException, InterruptedException { + try { + clientSocket = new Socket(ip, port); + } catch (Exception e) { + // Keep trying until server begins accepting connections. + Thread.sleep(100); + startConnection(ip, port); + } + out = new PrintWriter(clientSocket.getOutputStream(), true); + } + + public void sendMessage(String msg) throws IOException { + out.print(msg); + out.close(); + clientSocket.close(); + } + } + + static class GreetServer { + public void start(int port) throws IOException { + ServerSocket serverSocket = new ServerSocket(port); + Socket clientSocket = serverSocket.accept(); + + BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); + in.readLine(); + + in.close(); + clientSocket.close(); + serverSocket.close(); + } + } +}