diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java index 137efd18136..debb025f449 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/TestFramework.java @@ -881,8 +881,7 @@ public class TestFramework { if (shouldVerifyIR) { try { TestClassParser testClassParser = new TestClassParser(testClass, allowNotCompilable); - Matchable testClassMatchable = testClassParser.parse(testVMProcess.getHotspotPidFileName(), - testVMProcess.getApplicableIRRules()); + Matchable testClassMatchable = testClassParser.parse(testVMProcess.testVmData()); IRMatcher matcher = new IRMatcher(testClassMatchable); matcher.match(); } catch (IRViolationException e) { diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java index 931d687b0bc..9a55db01fa0 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/TestVMProcess.java @@ -24,6 +24,7 @@ package compiler.lib.ir_framework.driver; import compiler.lib.ir_framework.TestFramework; +import compiler.lib.ir_framework.driver.network.TestVMData; import compiler.lib.ir_framework.shared.TestFrameworkException; import compiler.lib.ir_framework.shared.TestFrameworkSocket; import compiler.lib.ir_framework.shared.NoTestsRunException; @@ -58,10 +59,9 @@ public class TestVMProcess { private static String lastTestVMOutput = ""; private final ArrayList cmds; - private String hotspotPidFileName; private String commandLine; private OutputAnalyzer oa; - private String applicableIRRules; + private final TestVMData testVmData; public TestVMProcess(List additionalFlags, Class testClass, Set> helperClasses, int defaultWarmup, boolean allowNotCompilable, boolean testClassesOnBootClassPath) { @@ -72,20 +72,17 @@ public class TestVMProcess { allowNotCompilable, testClassesOnBootClassPath); start(); } - processSocketOutput(socket); checkTestVMExitCode(); + String hotspotPidFileName = String.format("hotspot_pid%d.log", oa.pid()); + testVmData = socket.testVmData(hotspotPidFileName, allowNotCompilable); } public String getCommandLine() { return commandLine; } - public String getApplicableIRRules() { - return applicableIRRules; - } - - public String getHotspotPidFileName() { - return hotspotPidFileName; + public TestVMData testVmData() { + return testVmData; } public static String getLastTestVMOutput() { @@ -172,55 +169,9 @@ public class TestVMProcess { process.command().add(1, "-DReproduce=true"); // Add after "/path/to/bin/java" in order to rerun the Test VM directly commandLine = "Command Line:" + System.lineSeparator() + String.join(" ", process.command()) + System.lineSeparator(); - hotspotPidFileName = String.format("hotspot_pid%d.log", oa.pid()); lastTestVMOutput = oa.getOutput(); } - /** - * Process the socket output: All prefixed lines are dumped to the standard output while the remaining lines - * represent the Applicable IR Rules used for IR matching later. - */ - private void processSocketOutput(TestFrameworkSocket socket) { - String output = socket.getOutput(); - if (socket.hasStdOut()) { - StringBuilder testListBuilder = new StringBuilder(); - StringBuilder messagesBuilder = new StringBuilder(); - StringBuilder nonStdOutBuilder = new StringBuilder(); - Scanner scanner = new Scanner(output); - while (scanner.hasNextLine()) { - String line = scanner.nextLine(); - if (line.startsWith(TestFrameworkSocket.STDOUT_PREFIX)) { - // Exclude [STDOUT] from message. - line = line.substring(TestFrameworkSocket.STDOUT_PREFIX.length()); - if (line.startsWith(TestFrameworkSocket.TESTLIST_TAG)) { - // Exclude [TESTLIST] from message for better formatting. - line = "> " + line.substring(TestFrameworkSocket.TESTLIST_TAG.length() + 1); - testListBuilder.append(line).append(System.lineSeparator()); - } else { - messagesBuilder.append(line).append(System.lineSeparator()); - } - } else { - nonStdOutBuilder.append(line).append(System.lineSeparator()); - } - } - System.out.println(); - if (!testListBuilder.isEmpty()) { - System.out.println("Run flag defined test list"); - System.out.println("--------------------------"); - System.out.println(testListBuilder); - System.out.println(); - } - if (!messagesBuilder.isEmpty()) { - System.out.println("Messages from Test VM"); - System.out.println("---------------------"); - System.out.println(messagesBuilder); - } - applicableIRRules = nonStdOutBuilder.toString(); - } else { - applicableIRRules = output; - } - } - private void checkTestVMExitCode() { final int exitCode = oa.getExitValue(); if (EXCLUDE_RANDOM || REPORT_STDOUT || (VERBOSE && exitCode == 0)) { diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestClassParser.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestClassParser.java index 2329b41afbe..6c899af2116 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestClassParser.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/irmatching/parser/TestClassParser.java @@ -30,6 +30,7 @@ import compiler.lib.ir_framework.driver.irmatching.irmethod.IRMethod; import compiler.lib.ir_framework.driver.irmatching.irmethod.IRMethodMatchable; import compiler.lib.ir_framework.driver.irmatching.parser.hotspot.HotSpotPidFileParser; import compiler.lib.ir_framework.driver.irmatching.parser.hotspot.LoggedMethods; +import compiler.lib.ir_framework.driver.network.TestVMData; import compiler.lib.ir_framework.shared.TestFormat; import java.util.SortedSet; @@ -53,13 +54,13 @@ public class TestClassParser { * Parse the Applicable IR Rules and hotspot_pid* file to create a collection of {@link IRMethod} objects. * Return a default/empty TestClass object if there are no applicable @IR rules in any method of the test class. */ - public Matchable parse(String hotspotPidFileName, String applicableIRRules) { + public Matchable parse(TestVMData testVmData) { ApplicableIRRulesParser applicableIRRulesParser = new ApplicableIRRulesParser(testClass); - TestMethods testMethods = applicableIRRulesParser.parse(applicableIRRules); - VMInfo vmInfo = VMInfoParser.parseVMInfo(applicableIRRules); + TestMethods testMethods = applicableIRRulesParser.parse(testVmData.applicableIRRules()); + VMInfo vmInfo = VMInfoParser.parseVMInfo(testVmData.applicableIRRules()); if (testMethods.hasTestMethods()) { HotSpotPidFileParser hotSpotPidFileParser = new HotSpotPidFileParser(testClass.getName(), testMethods); - LoggedMethods loggedMethods = hotSpotPidFileParser.parse(hotspotPidFileName); + LoggedMethods loggedMethods = hotSpotPidFileParser.parse(testVmData.hotspotPidFileName()); return createTestClass(testMethods, loggedMethods, vmInfo); } return new NonIRTestClass(); diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/TestVMData.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/TestVMData.java new file mode 100644 index 00000000000..daa6a590ddf --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/TestVMData.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.lib.ir_framework.driver.network; + +import compiler.lib.ir_framework.driver.irmatching.IRMatcher; +import compiler.lib.ir_framework.driver.network.testvm.java.JavaMessages; +import compiler.lib.ir_framework.shared.TestFrameworkSocket; +import compiler.lib.ir_framework.test.network.MessageTag; + +import java.util.Scanner; + +/** + * This class collects all the parsed data received over the {@link TestFrameworkSocket}. This data is required later + * in the {@link IRMatcher}. + */ +public class TestVMData { + private final boolean allowNotCompilable; + private final String hotspotPidFileName; + private final String applicableIRRules; + + public TestVMData(JavaMessages javaMessages, String hotspotPidFileName, boolean allowNotCompilable) { + this.applicableIRRules = processOutput(javaMessages); + this.hotspotPidFileName = hotspotPidFileName; + this.allowNotCompilable = allowNotCompilable; + } + + public String hotspotPidFileName() { + return hotspotPidFileName; + } + + public boolean allowNotCompilable() { + return allowNotCompilable; + } + + public String applicableIRRules() { + return applicableIRRules; + } + + /** + * Process the socket output: All prefixed lines are dumped to the standard output while the remaining lines + * represent the Applicable IR Rules used for IR matching later. + */ + private String processOutput(JavaMessages javaMessages) { + String output = javaMessages.output(); + if (javaMessages.hasStdOut()) { + StringBuilder testListBuilder = new StringBuilder(); + StringBuilder messagesBuilder = new StringBuilder(); + StringBuilder nonStdOutBuilder = new StringBuilder(); + Scanner scanner = new Scanner(output); + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + if (line.startsWith(MessageTag.STDOUT)) { + // Exclude [STDOUT] from message. + line = line.substring(MessageTag.STDOUT.length()); + if (line.startsWith(MessageTag.TEST_LIST)) { + // Exclude [TEST_LIST] from message for better formatting. + line = "> " + line.substring(MessageTag.TEST_LIST.length() + 1); + testListBuilder.append(line).append(System.lineSeparator()); + } else { + messagesBuilder.append(line).append(System.lineSeparator()); + } + } else { + nonStdOutBuilder.append(line).append(System.lineSeparator()); + } + } + System.out.println(); + if (!testListBuilder.isEmpty()) { + System.out.println("Run flag defined test list"); + System.out.println("--------------------------"); + System.out.println(testListBuilder); + System.out.println(); + } + if (!messagesBuilder.isEmpty()) { + System.out.println("Messages from Test VM"); + System.out.println("---------------------"); + System.out.println(messagesBuilder); + } + return nonStdOutBuilder.toString(); + } else { + return output; + } + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/TestVmMessageReader.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/TestVmMessageReader.java new file mode 100644 index 00000000000..b438794964d --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/TestVmMessageReader.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.lib.ir_framework.driver.network.testvm; + +import compiler.lib.ir_framework.driver.network.testvm.java.JavaMessages; +import compiler.lib.ir_framework.shared.TestFrameworkException; +import compiler.lib.ir_framework.shared.TestFrameworkSocket; +import compiler.lib.ir_framework.test.network.MessageTag; + +import java.io.BufferedReader; +import java.net.Socket; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; + +/** + * Dedicated reader for Test VM messages received by the {@link TestFrameworkSocket}. The reader is used as a task + * wrapped in a {@link Future}. The received messages are returned in a new {@link JavaMessages} wrapper. Once the + * Test VM is terminated, the client connection is closed and the parsed messages can be fetched with + * {@link Future#get()} which calls {@link #call()}. + */ +public class TestVmMessageReader implements Callable { + private final Socket socket; + private final BufferedReader reader; + private boolean receivedStdOut; + + public TestVmMessageReader(Socket socket, BufferedReader reader) { + this.socket = socket; + this.reader = reader; + this.receivedStdOut = false; + } + + @Override + public JavaMessages call() { + try (socket; reader) { + StringBuilder builder = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + builder.append(line).append(System.lineSeparator()); + if (line.startsWith(MessageTag.STDOUT)) { + receivedStdOut = true; + } + } + return new JavaMessages(builder.toString(), receivedStdOut); + } catch (Exception e) { + throw new TestFrameworkException("Error while reading Test VM socket messages", e); + } + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessages.java b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessages.java new file mode 100644 index 00000000000..8a285f46ca9 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/driver/network/testvm/java/JavaMessages.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.lib.ir_framework.driver.network.testvm.java; + +/** + * Class to collect all Java messages sent from the Test VM to the Driver VM. + */ +public class JavaMessages { + private final String output; + private final boolean receivedStdOut; + + public JavaMessages(String output, boolean receivedStdOut) { + this.output = output; + this.receivedStdOut = receivedStdOut; + } + + public String output() { + return output; + } + + public boolean hasStdOut() { + return receivedStdOut; + } +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/shared/TestFrameworkSocket.java b/test/hotspot/jtreg/compiler/lib/ir_framework/shared/TestFrameworkSocket.java index f10540ebc5b..77d560952f1 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/shared/TestFrameworkSocket.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/shared/TestFrameworkSocket.java @@ -24,40 +24,27 @@ package compiler.lib.ir_framework.shared; import compiler.lib.ir_framework.TestFramework; +import compiler.lib.ir_framework.driver.network.*; +import compiler.lib.ir_framework.driver.network.testvm.TestVmMessageReader; +import compiler.lib.ir_framework.driver.network.testvm.java.JavaMessages; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; -import java.io.PrintWriter; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.ServerSocket; -import java.net.Socket; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.FutureTask; +import java.net.*; +import java.util.concurrent.*; /** - * Dedicated socket to send data from the flag and Test VM back to the Driver VM. + * Dedicated Driver VM socket to receive data from the Test VM. Could either be received from Java and C2 code. */ public class TestFrameworkSocket implements AutoCloseable { - public static final String STDOUT_PREFIX = "[STDOUT]"; - public static final String TESTLIST_TAG = "[TESTLIST]"; - public static final String DEFAULT_REGEX_TAG = "[DEFAULT_REGEX]"; - public static final String PRINT_TIMES_TAG = "[PRINT_TIMES]"; - public static final String NOT_COMPILABLE_TAG = "[NOT_COMPILABLE]"; - - // Static fields used for Test VM only. private static final String SERVER_PORT_PROPERTY = "ir.framework.server.port"; - private static final int SERVER_PORT = Integer.getInteger(SERVER_PORT_PROPERTY, -1); - private static final boolean REPRODUCE = Boolean.getBoolean("Reproduce"); - private static Socket clientSocket = null; - private static PrintWriter clientWriter = null; - - private final String serverPortPropertyFlag; - private FutureTask socketTask; + private final int serverSocketPort; private final ServerSocket serverSocket; - private boolean receivedStdOut = false; + private boolean running; + private final ExecutorService executor; + private Future javaFuture; public TestFrameworkSocket() { try { @@ -66,140 +53,80 @@ public class TestFrameworkSocket implements AutoCloseable { } catch (IOException e) { throw new TestFrameworkException("Failed to create TestFramework server socket", e); } - int port = serverSocket.getLocalPort(); + serverSocketPort = serverSocket.getLocalPort(); + executor = Executors.newCachedThreadPool(); if (TestFramework.VERBOSE) { - System.out.println("TestFramework server socket uses port " + port); + System.out.println("TestFramework server socket uses port " + serverSocketPort); } - serverPortPropertyFlag = "-D" + SERVER_PORT_PROPERTY + "=" + port; start(); } public String getPortPropertyFlag() { - return serverPortPropertyFlag; + return "-D" + SERVER_PORT_PROPERTY + "=" + serverSocketPort; } private void start() { - socketTask = initSocketTask(); - Thread socketThread = new Thread(socketTask); - socketThread.start(); + running = true; + executor.submit(this::acceptLoop); } /** - * Waits for a client (created by flag or Test VM) to connect. Return the messages received from the client. + * Main loop to wait for new client connections and handling them upon connection request. */ - private FutureTask initSocketTask() { - return new FutureTask<>(() -> { - try (Socket clientSocket = serverSocket.accept(); - BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())) - ) { - StringBuilder builder = new StringBuilder(); - String next; - while ((next = in.readLine()) != null) { - builder.append(next).append(System.lineSeparator()); - if (next.startsWith(STDOUT_PREFIX)) { - receivedStdOut = true; - } - } - return builder.toString(); - } catch (IOException e) { + private void acceptLoop() { + while (running) { + try { + acceptNewClientConnection(); + } catch (TestFrameworkException e) { + running = false; + throw e; + } catch (Exception e) { + running = false; throw new TestFrameworkException("Server socket error", e); } - }); + } + } + + /** + * Accept new client connection and then submit a task accordingly to manage incoming message on that connection/socket. + */ + private void acceptNewClientConnection() throws IOException { + Socket client = serverSocket.accept(); + BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream())); + submitTask(client, reader); + } + + /** + * Submit dedicated tasks which are wrapped into {@link Future} objects. The tasks will read all messages sent + * over that connection. + */ + private void submitTask(Socket client, BufferedReader reader) { + javaFuture = executor.submit(new TestVmMessageReader(client, reader)); } @Override public void close() { try { + running = false; serverSocket.close(); } catch (IOException e) { throw new TestFrameworkException("Could not close socket", e); } + executor.shutdown(); } - /** - * Only called by Test VM to write to server socket. - */ - public static void write(String msg, String tag) { - write(msg, tag, false); + public TestVMData testVmData(String hotspotPidFileName, boolean allowNotCompilable) { + JavaMessages javaMessages = testVmMessages(); + return new TestVMData(javaMessages, hotspotPidFileName, allowNotCompilable); } - /** - * Only called by Test VM to write to server socket. - *

- * The Test VM is spawned by the main jtreg VM. The stdout of the Test VM is hidden - * unless the Verbose or ReportStdout flag is used. TestFrameworkSocket is used by the parent jtreg - * VM and the Test VM to communicate. By sending the prints through the TestFrameworkSocket with the - * parameter stdout set to true, the parent VM will print the received messages to its stdout, making it - * visible to the user. - */ - public static void write(String msg, String tag, boolean stdout) { - if (REPRODUCE) { - System.out.println("Debugging Test VM: Skip writing due to -DReproduce"); - return; - } - TestFramework.check(SERVER_PORT != -1, "Server port was not set correctly for flag and/or Test VM " - + "or method not called from flag or Test VM"); + private JavaMessages testVmMessages() { try { - // Keep the client socket open until the Test VM terminates (calls closeClientSocket before exiting main()). - if (clientSocket == null) { - clientSocket = new Socket(InetAddress.getLoopbackAddress(), SERVER_PORT); - clientWriter = new PrintWriter(clientSocket.getOutputStream(), true); - } - if (stdout) { - msg = STDOUT_PREFIX + tag + " " + msg; - } - clientWriter.println(msg); - } catch (Exception e) { - // When the Test VM is directly run, we should ignore all messages that would normally be sent to the - // Driver VM. - String failMsg = System.lineSeparator() + System.lineSeparator() + """ - ########################################################### - Did you directly run the Test VM (TestVM class) - to reproduce a bug? - => Append the flag -DReproduce=true and try again! - ########################################################### - """; - throw new TestRunException(failMsg, e); - } - if (TestFramework.VERBOSE) { - System.out.println("Written " + tag + " to socket:"); - System.out.println(msg); - } - } - - /** - * Closes (and flushes) the printer to the socket and the socket itself. Is called as last thing before exiting - * the main() method of the flag and the Test VM. - */ - public static void closeClientSocket() { - if (clientSocket != null) { - try { - clientWriter.close(); - clientSocket.close(); - } catch (IOException e) { - throw new RuntimeException("Could not close TestVM socket", e); - } - } - } - - /** - * Get the socket output of the Flag VM. - */ - public String getOutput() { - try { - return socketTask.get(); + return javaFuture.get(); } catch (ExecutionException e) { - // Thrown when socket task was not finished, yet (i.e. no client sent data) but socket was already closed. - return ""; + throw new TestFrameworkException("No test VM messages were received", e); } catch (Exception e) { - throw new TestFrameworkException("Could not read from socket task", e); + throw new TestFrameworkException("Error while fetching Test VM Future", e); } } - - /** - * Return whether Test VM sent messages to be put on stdout (starting with {@link ::STDOUT_PREFIX}). - */ - public boolean hasStdOut() { - return receivedStdOut; - } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/AbstractTest.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/AbstractTest.java index 4d227900e2e..152dcab273a 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/AbstractTest.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/AbstractTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, 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,14 +26,13 @@ package compiler.lib.ir_framework.test; import compiler.lib.ir_framework.*; import compiler.lib.ir_framework.shared.TestRun; import compiler.lib.ir_framework.shared.TestRunException; +import compiler.lib.ir_framework.test.network.TestVmSocket; import jdk.test.whitebox.WhiteBox; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import compiler.lib.ir_framework.shared.TestFrameworkSocket; - /** * Abstract super class for base, checked and custom run tests. */ @@ -118,7 +117,7 @@ abstract class AbstractTest { tryCompileMethod(test); } catch (MethodNotCompilableException e) { final Method testMethod = test.getTestMethod(); - TestFrameworkSocket.write("Method not compilable: " + testMethod, TestFrameworkSocket.NOT_COMPILABLE_TAG, true); + TestVmSocket.send("Method not compilable: " + testMethod); TestRun.check(test.isAllowNotCompilable(), "Method " + testMethod + " not compilable (anymore) at level " + test.getCompLevel() + ". Most likely, this is not expected, but if it is, you can use 'allowNotCompilable'."); diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java index 4fa1f8f3fe5..e42ac3be6aa 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/ApplicableIRRulesPrinter.java @@ -27,6 +27,8 @@ import compiler.lib.ir_framework.IR; import compiler.lib.ir_framework.IRNode; import compiler.lib.ir_framework.TestFramework; import compiler.lib.ir_framework.shared.*; +import compiler.lib.ir_framework.test.network.MessageTag; +import compiler.lib.ir_framework.test.network.TestVmSocket; import jdk.test.lib.Platform; import jdk.test.whitebox.WhiteBox; @@ -169,9 +171,8 @@ public class ApplicableIRRulesPrinter { } private void printDisableReason(String method, String reason, String[] apply, int ruleIndex, int ruleMax) { - TestFrameworkSocket.write("Disabling IR matching for rule " + ruleIndex + " of " + ruleMax + " in " + - method + ": " + reason + ": " + String.join(", ", apply), - "[ApplicableIRRules]", true); + TestVmSocket.send("Disabling IR matching for rule " + ruleIndex + " of " + ruleMax + " in " + method + ": " + + reason + ": " + String.join(", ", apply)); } private boolean shouldApplyIrRule(IR irAnno, String m, int ruleIndex, int ruleMax) { @@ -284,7 +285,7 @@ public class ApplicableIRRulesPrinter { IRNode.checkIRNodeSupported(s); } } catch (CheckedTestFrameworkException e) { - TestFrameworkSocket.write("Skip Rule " + ruleIndex + ": " + e.getMessage(), TestFrameworkSocket.DEFAULT_REGEX_TAG, true); + TestVmSocket.send("Skip Rule " + ruleIndex + ": " + e.getMessage()); return true; } return false; @@ -522,7 +523,7 @@ public class ApplicableIRRulesPrinter { public void emit() { output.append(END); - TestFrameworkSocket.write(output.toString(), "ApplicableIRRules"); + TestVmSocket.sendWithTag(MessageTag.APPLICABLE_IR_RULES, output.toString()); } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java index c2580e087f0..c7910c85390 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/TestVM.java @@ -26,6 +26,8 @@ package compiler.lib.ir_framework.test; import compiler.lib.ir_framework.*; import compiler.lib.ir_framework.Compiler; import compiler.lib.ir_framework.shared.*; +import compiler.lib.ir_framework.test.network.MessageTag; +import compiler.lib.ir_framework.test.network.TestVmSocket; import jdk.test.lib.Platform; import jdk.test.lib.Utils; import jdk.test.whitebox.WhiteBox; @@ -38,8 +40,6 @@ import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; -import static compiler.lib.ir_framework.shared.TestFrameworkSocket.PRINT_TIMES_TAG; - /** * This class' main method is called from {@link TestFramework} and represents the so-called "Test VM". The class is * the heart of the framework and is responsible for executing all the specified tests in the test class. It uses the @@ -159,6 +159,7 @@ public class TestVM { */ public static void main(String[] args) { try { + TestVmSocket.connect(); String testClassName = args[0]; System.out.println("TestVM main() called - about to run tests in class " + testClassName); Class testClass = getClassObject(testClassName, "test"); @@ -167,7 +168,7 @@ public class TestVM { framework.addHelperClasses(args); framework.start(); } finally { - TestFrameworkSocket.closeClientSocket(); + TestVmSocket.close(); } } @@ -859,7 +860,7 @@ public class TestVM { System.out.println("Run " + test.toString()); } if (testFilterPresent) { - TestFrameworkSocket.write("Run " + test.toString(), TestFrameworkSocket.TESTLIST_TAG, true); + TestVmSocket.send(MessageTag.TEST_LIST + "Run " + test.toString()); } try { test.run(); @@ -887,10 +888,9 @@ public class TestVM { // Print execution times if (PRINT_TIMES) { - TestFrameworkSocket.write("Test execution times:", PRINT_TIMES_TAG, true); + TestVmSocket.send(MessageTag.PRINT_TIMES + " Test execution times:"); for (Map.Entry entry : durations.entrySet()) { - TestFrameworkSocket.write(String.format("%-25s%15d ns%n", entry.getValue() + ":", entry.getKey()), - PRINT_TIMES_TAG, true); + TestVmSocket.send(MessageTag.PRINT_TIMES + String.format("%-25s%15d ns%n", entry.getValue() + ":", entry.getKey())); } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/VMInfoPrinter.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/VMInfoPrinter.java index 470569122dd..2227c7760d5 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/VMInfoPrinter.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/VMInfoPrinter.java @@ -23,7 +23,8 @@ package compiler.lib.ir_framework.test; -import compiler.lib.ir_framework.shared.TestFrameworkSocket; +import compiler.lib.ir_framework.test.network.MessageTag; +import compiler.lib.ir_framework.test.network.TestVmSocket; import jdk.test.whitebox.WhiteBox; /** @@ -65,6 +66,6 @@ public class VMInfoPrinter { .append(System.lineSeparator()); vmInfo.append(END_VM_INFO); - TestFrameworkSocket.write(vmInfo.toString(), "VMInfo"); + TestVmSocket.sendWithTag(MessageTag.VM_INFO, vmInfo.toString()); } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/network/MessageTag.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/network/MessageTag.java new file mode 100644 index 00000000000..2981f203e65 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/network/MessageTag.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.lib.ir_framework.test.network; + +public class MessageTag { + public static final String STDOUT = "[STDOUT]"; + public static final String TEST_LIST = "[TEST_LIST]"; + public static final String PRINT_TIMES = "[PRINT_TIMES]"; + public static final String VM_INFO = "[VM_INFO]"; + public static final String APPLICABLE_IR_RULES = "[APPLICABLE_IR_RULES]"; +} diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/network/TestVmSocket.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/network/TestVmSocket.java new file mode 100644 index 00000000000..c1065c84320 --- /dev/null +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/network/TestVmSocket.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.lib.ir_framework.test.network; + +import compiler.lib.ir_framework.TestFramework; +import compiler.lib.ir_framework.shared.TestRunException; + +import java.io.IOException; +import java.io.PrintWriter; +import java.net.InetAddress; +import java.net.Socket; + +public class TestVmSocket { + private static final boolean REPRODUCE = Boolean.getBoolean("Reproduce"); + private static final String SERVER_PORT_PROPERTY = "ir.framework.server.port"; + private static final int SERVER_PORT = Integer.getInteger(SERVER_PORT_PROPERTY, -1); + + private static Socket socket = null; + private static PrintWriter writer = null; + + /** + * Send a message to the Driver VM which is unconditionally shown in the Driver VM output. + */ + public static void send(String message) { + sendWithTag(MessageTag.STDOUT, message); + } + + /** + * Send a message to the Driver VM with a {@link MessageTag}. Not all messages are shown by default in the + * Driver VM output and require setting some property flags first like {@code -DPrintTimes=true}. + */ + public static void sendWithTag(String tag, String message) { + if (REPRODUCE) { + // Debugging Test VM: Skip writing due to -DReproduce; + return; + } + + TestFramework.check(socket != null, "must be connected"); + writer.println(tag + " " + message); + } + + public static void connect() { + if (REPRODUCE) { + // Debugging Test VM: Skip writing due to -DReproduce; + return; + } + + TestFramework.check(SERVER_PORT != -1, "Server port was not set correctly for flag and/or test VM " + + "or method not called from flag or test VM"); + + try { + // Keep the client socket open until the test VM terminates (calls closeClientSocket before exiting main()). + socket = new Socket(InetAddress.getLoopbackAddress(), SERVER_PORT); + writer = new PrintWriter(socket.getOutputStream(), true); + } catch (Exception e) { + // When the test VM is directly run, we should ignore all messages that would normally be sent to the + // driver VM. + String failMsg = System.lineSeparator() + System.lineSeparator() + """ + ########################################################### + Did you directly run the test VM (TestVM class) + to reproduce a bug? + => Append the flag -DReproduce=true and try again! + ########################################################### + """; + throw new TestRunException(failMsg, e); + } + + } + + /** + * Closes (and flushes) the printer to the socket and the socket itself. Is called as last thing before exiting + * the main() method of the flag and the test VM. + */ + public static void close() { + if (socket != null) { + writer.close(); + try { + socket.close(); + } catch (IOException e) { + throw new RuntimeException("Could not close TestVM socket", e); + } + } + } +} diff --git a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestPhaseIRMatching.java b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestPhaseIRMatching.java index 70e4c463c55..8bec7c03bfe 100644 --- a/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestPhaseIRMatching.java +++ b/test/hotspot/jtreg/testlibrary_tests/ir_framework/tests/TestPhaseIRMatching.java @@ -69,8 +69,7 @@ public class TestPhaseIRMatching { List testVMFlags = flagVMProcess.getTestVMFlags(); TestVMProcess testVMProcess = new TestVMProcess(testVMFlags, testClass, null, -1, false, false); TestClassParser testClassParser = new TestClassParser(testClass, false); - Matchable testClassMatchable = testClassParser.parse(testVMProcess.getHotspotPidFileName(), - testVMProcess.getApplicableIRRules()); + Matchable testClassMatchable = testClassParser.parse(testVMProcess.testVmData()); MatchResult result = testClassMatchable.match(); List expectedFails = new ExpectedFailsBuilder().build(testClass); List foundFailures = new FailureBuilder().build(result);