From 8ca80eea1c287bc0b47172036c76985518f8c94e Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Thu, 30 Apr 2026 15:35:48 -0400 Subject: [PATCH 1/3] Debug port utils. --- .../trace/agent/test/utils/PortUtils.java | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/utils/PortUtils.java b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/utils/PortUtils.java index df7830a7506..b5789f72a18 100644 --- a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/utils/PortUtils.java +++ b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/utils/PortUtils.java @@ -1,10 +1,12 @@ package datadog.trace.agent.test.utils; import java.io.IOException; +import java.lang.reflect.Field; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; +import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -115,6 +117,18 @@ public static void waitForPortToOpen( final int port, final long timeout, final TimeUnit unit, final Process process) { final long startedAt = System.currentTimeMillis(); final long waitUntil = startedAt + unit.toMillis(timeout); + final long progressIntervalMillis = TimeUnit.SECONDS.toMillis(30); + long nextProgressAt = startedAt + progressIntervalMillis; + final long pid = tryGetPid(process); + + System.err.println( + "[PortUtils] Waiting up to " + + unit.toMillis(timeout) + + "ms for port " + + port + + " to open (process pid=" + + pid + + ")"); while (System.currentTimeMillis() < waitUntil) { try { @@ -138,10 +152,45 @@ public static void waitForPortToOpen( } if (isPortOpen(port)) { + System.err.println( + "[PortUtils] Port " + + port + + " opened after " + + (System.currentTimeMillis() - startedAt) + + "ms"); return; } + + long now = System.currentTimeMillis(); + if (now >= nextProgressAt) { + System.err.println( + "[PortUtils] Still waiting for port " + + port + + " (pid=" + + pid + + ") elapsed=" + + (now - startedAt) + + "ms alive=" + + process.isAlive()); + nextProgressAt = now + progressIntervalMillis; + } } + // Timeout: capture diagnostics before throwing so the next failure tells us what was hung. + System.err.println( + "[PortUtils] Timed out waiting for port " + + port + + " after " + + unit.toMillis(timeout) + + "ms; pid=" + + pid + + " alive=" + + process.isAlive()); + if (pid > 0) { + requestChildThreadDump(pid); + } + dumpTestJvmThreads(); + throw new RuntimeException( "Timed out waiting for port " + port @@ -151,6 +200,53 @@ public static void waitForPortToOpen( + System.currentTimeMillis()); } + private static long tryGetPid(Process process) { + try { + Field pidField = process.getClass().getDeclaredField("pid"); + pidField.setAccessible(true); + return pidField.getLong(process); + } catch (Throwable t) { + System.err.println("[PortUtils] Could not extract child pid: " + t); + return -1; + } + } + + private static void requestChildThreadDump(long pid) { + String osName = System.getProperty("os.name", "").toLowerCase(); + if (osName.startsWith("windows")) { + System.err.println("[PortUtils] Skipping SIGQUIT thread dump on Windows (pid=" + pid + ")"); + return; + } + try { + System.err.println( + "[PortUtils] Sending SIGQUIT (kill -3) to pid " + pid + " to trigger thread dump"); + Process kill = + new ProcessBuilder("kill", "-3", String.valueOf(pid)).redirectErrorStream(true).start(); + kill.waitFor(5, TimeUnit.SECONDS); + // HotSpot writes the dump to the child stderr (captured in the test process log). + // IBM J9 writes a javacore.* file (see -Xdump:directory, default /tmp in smoke tests). + // Give the JVM a moment to finish writing before we throw. + Thread.sleep(2000); + System.err.println("[PortUtils] SIGQUIT delivered; check child process log / javacore files"); + } catch (Throwable t) { + System.err.println("[PortUtils] Failed to send SIGQUIT to pid " + pid + ": " + t); + } + } + + private static void dumpTestJvmThreads() { + System.err.println("[PortUtils] === Test JVM thread dump ==="); + Map stacks = Thread.getAllStackTraces(); + for (Map.Entry entry : stacks.entrySet()) { + Thread t = entry.getKey(); + System.err.println("\"" + t.getName() + "\" id=" + t.getId() + " state=" + t.getState()); + for (StackTraceElement ste : entry.getValue()) { + System.err.println("\tat " + ste); + } + System.err.println(); + } + System.err.println("[PortUtils] === End of test JVM thread dump ==="); + } + public static void waitForPortToOpen(String host, int port, long timeout, TimeUnit unit) { waitForPort(host, port, timeout, unit, true); } From 694849f6bd7f9c88c56d4a0c9ef00a1755acfff9 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Fri, 1 May 2026 11:17:18 -0400 Subject: [PATCH 2/3] Trigger build --- .../groovy/datadog/smoketest/GradleLauncherSmokeTest.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dd-smoke-tests/gradle/src/test/groovy/datadog/smoketest/GradleLauncherSmokeTest.groovy b/dd-smoke-tests/gradle/src/test/groovy/datadog/smoketest/GradleLauncherSmokeTest.groovy index d8302d1cfe8..5c7833612cd 100644 --- a/dd-smoke-tests/gradle/src/test/groovy/datadog/smoketest/GradleLauncherSmokeTest.groovy +++ b/dd-smoke-tests/gradle/src/test/groovy/datadog/smoketest/GradleLauncherSmokeTest.groovy @@ -68,7 +68,7 @@ class GradleLauncherSmokeTest extends AbstractGradleTest { Thread.sleep(2000) // small delay for rapid retries on network issues } } - throw new AssertionError((Object) "Tried $GRADLE_WRAPPER_RETRIES times to execute gradle wrapper command and failed") + throw new AssertionError((Object) "Tried $GRADLE_WRAPPER_RETRIES times to execute gradle wrapper command and failed.") } private String whenRunningGradleLauncherWithJavaTracerInjected(String gradleDaemonCmdLineParams) { From f8747f0b8cff6317f046782b015e85f6fb5079c7 Mon Sep 17 00:00:00 2001 From: Alexey Kuznetsov Date: Fri, 1 May 2026 13:36:18 -0400 Subject: [PATCH 3/3] Suppress forbidden API --- .../groovy/datadog/trace/agent/test/utils/PortUtils.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/utils/PortUtils.java b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/utils/PortUtils.java index b5789f72a18..b0d51699f64 100644 --- a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/utils/PortUtils.java +++ b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/utils/PortUtils.java @@ -1,5 +1,6 @@ package datadog.trace.agent.test.utils; +import de.thetaphi.forbiddenapis.SuppressForbidden; import java.io.IOException; import java.lang.reflect.Field; import java.net.InetAddress; @@ -113,6 +114,7 @@ private static boolean isPortOpen(String host, int port) { } } + @SuppressForbidden public static void waitForPortToOpen( final int port, final long timeout, final TimeUnit unit, final Process process) { final long startedAt = System.currentTimeMillis(); @@ -200,6 +202,7 @@ public static void waitForPortToOpen( + System.currentTimeMillis()); } + @SuppressForbidden private static long tryGetPid(Process process) { try { Field pidField = process.getClass().getDeclaredField("pid"); @@ -211,6 +214,7 @@ private static long tryGetPid(Process process) { } } + @SuppressForbidden private static void requestChildThreadDump(long pid) { String osName = System.getProperty("os.name", "").toLowerCase(); if (osName.startsWith("windows")) { @@ -233,6 +237,7 @@ private static void requestChildThreadDump(long pid) { } } + @SuppressForbidden private static void dumpTestJvmThreads() { System.err.println("[PortUtils] === Test JVM thread dump ==="); Map stacks = Thread.getAllStackTraces();