8376174: [IR Framework] Refactor Test VM socket communication

This commit is contained in:
Christian Hagedorn 2026-01-26 10:35:17 +01:00
parent bbae38e510
commit 828124da4e
14 changed files with 440 additions and 207 deletions

View File

@ -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) {

View File

@ -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<String> cmds;
private String hotspotPidFileName;
private String commandLine;
private OutputAnalyzer oa;
private String applicableIRRules;
private final TestVMData testVmData;
public TestVMProcess(List<String> additionalFlags, Class<?> testClass, Set<Class<?>> 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)) {

View File

@ -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();

View File

@ -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;
}
}
}

View File

@ -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<JavaMessages> {
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);
}
}
}

View File

@ -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;
}
}

View File

@ -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<String> socketTask;
private final int serverSocketPort;
private final ServerSocket serverSocket;
private boolean receivedStdOut = false;
private boolean running;
private final ExecutorService executor;
private Future<JavaMessages> 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<String> 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.
* <p>
* 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;
}
}

View File

@ -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'.");

View File

@ -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());
}
}

View File

@ -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<Long, String> 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()));
}
}

View File

@ -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());
}
}

View File

@ -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]";
}

View File

@ -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);
}
}
}
}

View File

@ -69,8 +69,7 @@ public class TestPhaseIRMatching {
List<String> 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<Failure> expectedFails = new ExpectedFailsBuilder().build(testClass);
List<Failure> foundFailures = new FailureBuilder().build(result);