mirror of
https://github.com/openjdk/jdk.git
synced 2026-03-28 08:39:56 +00:00
466 lines
22 KiB
Java
466 lines
22 KiB
Java
/*
|
|
* Copyright Amazon.com Inc. 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.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* @test
|
|
* @bug 8313713
|
|
* @summary Test -XX:CompileCommand=exclude and compileonly with different compilation levels,
|
|
* monitoring compilation events in VM -XX:+PrintCompilation and -XX:+PrintTieredEvents output
|
|
* @requires vm.compMode != "Xint" & vm.flavor == "server"
|
|
* & (vm.opt.TieredStopAtLevel == 4 | vm.opt.TieredStopAtLevel == null)
|
|
* & (vm.opt.CompilationMode == "normal" | vm.opt.CompilationMode == null)
|
|
* @library /test/lib
|
|
* @run main ${test.main.class} runner
|
|
*/
|
|
|
|
package compiler.compilercontrol.commands;
|
|
|
|
import jdk.test.lib.Asserts;
|
|
import jdk.test.lib.management.InputArguments;
|
|
import jdk.test.lib.process.ProcessTools;
|
|
|
|
import java.io.BufferedReader;
|
|
import java.io.BufferedWriter;
|
|
import java.io.FileWriter;
|
|
import java.io.IOException;
|
|
import java.io.InputStreamReader;
|
|
import java.lang.reflect.Method;
|
|
import java.util.Arrays;
|
|
import java.util.Collections;
|
|
import java.util.HashSet;
|
|
import java.util.LinkedList;
|
|
import java.util.Set;
|
|
import java.util.concurrent.CountDownLatch;
|
|
import java.util.concurrent.ExecutionException;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
import java.util.concurrent.locks.LockSupport;
|
|
import java.util.function.BooleanSupplier;
|
|
import java.util.regex.Matcher;
|
|
import java.util.regex.Pattern;
|
|
|
|
public class CompileLevelPrintTest {
|
|
static final Method TEST_METHOD;
|
|
|
|
static {
|
|
try {
|
|
TEST_METHOD = Testee.class.getDeclaredMethod("compiledMethod", new Class[] {int.class});
|
|
} catch (NoSuchMethodException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
static final String TEST_METHOD_NAME_DOT = TEST_METHOD.getDeclaringClass().getName().replace('.', '/')
|
|
+ "." + TEST_METHOD.getName();
|
|
static final String TEST_METHOD_NAME_DBL_COLON = TEST_METHOD.getDeclaringClass().getName()
|
|
+ "::" + TEST_METHOD.getName();
|
|
static final String TEST_METHOD_SIGNATURE = TEST_METHOD_NAME_DBL_COLON + "(";
|
|
|
|
static final String TESTEE_WAITING_FOR_START_CMD = "==> waiting for start command";
|
|
|
|
static final String START_CMD = "start";
|
|
static final String STOP_CMD = "stop";
|
|
|
|
static final boolean DEBUG_OUTPUT = false;
|
|
|
|
static int TIMEOUT_SEC = 30;
|
|
|
|
static class TesteeState {
|
|
final CountDownLatch waitingForStartTest = new CountDownLatch(1);
|
|
final AtomicInteger compiler1QueueSize = new AtomicInteger();
|
|
final AtomicInteger compiler2QueueSize = new AtomicInteger(0);
|
|
final Set<String> compileCommandsReported = Collections.synchronizedSet(new HashSet<>());
|
|
volatile Set<String> testMethodCompiledAtLevel = Collections.synchronizedSet(new HashSet<>());
|
|
final Set<String> testMethodExcludedAtLevel = Collections.synchronizedSet(new HashSet<>());
|
|
volatile Set<String> testMethodPrintedAtLevel = Collections.synchronizedSet(new HashSet<>());
|
|
volatile boolean testMethodMDOPrinted = false;
|
|
}
|
|
|
|
public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {
|
|
// Use the same launcher to avoid double launch cost compared to multiple jtreg @test annotations
|
|
if (args.length > 0 && "runner".equals(args[0])) {
|
|
if (Arrays.asList(InputArguments.getVmInputArgs()).contains("-Xcomp")) {
|
|
TIMEOUT_SEC *= 3;
|
|
}
|
|
|
|
if (Arrays.asList(InputArguments.getVmInputArgs()).contains("-XX:-TieredCompilation")) {
|
|
// If we have -XX:-TieredCompilation, we check only for C2 compilation
|
|
// A space is printed instead of compile level
|
|
Runner.run("compileonly", "1", "1", "1", Set.of(), Set.of("4"), true, false);
|
|
Runner.run("compileonly", "2", "1", "1", Set.of(), Set.of("4"), true, false);
|
|
Runner.run("compileonly", "4", "1", "1", Set.of(), Set.of("4"), true, false);
|
|
Runner.run("compileonly", "8", "8", "4", Set.of(" "), Set.of(), true, false);
|
|
Runner.run("compileonly", "12", "12", "4", Set.of(" "), Set.of(), true, false);
|
|
|
|
Runner.run("exclude", "14", "1", "1", Set.of(), Set.of(), false, false);
|
|
Runner.run("exclude", "13", "2", "2", Set.of(), Set.of(), false, false);
|
|
Runner.run("exclude", "11", "4", "3", Set.of(), Set.of(), false, false);
|
|
Runner.run("exclude", "7", "8", "4", Set.of(" "), Set.of(), true, false);
|
|
} else {
|
|
// -XX:+TieredCompilation
|
|
Runner.run("compileonly", "1", "1", "1", Set.of("1"), Set.of(), false, true);
|
|
Runner.run("compileonly", "2", "2", "2", Set.of("2"), Set.of(), true, true);
|
|
Runner.run("compileonly", "4", "4", "3", Set.of("3"), Set.of(), true, true);
|
|
Runner.run("compileonly", "8", "8", "4", Set.of("4"), Set.of("3"), true, true);
|
|
Runner.run("compileonly", "12", "12", "4", Set.of("3", "4"), Set.of(), true, true);
|
|
|
|
Runner.run("exclude", "14", "1", "1", Set.of("1"), Set.of(), false, true);
|
|
Runner.run("exclude", "13", "2", "2", Set.of("2"), Set.of(), true, true);
|
|
Runner.run("exclude", "11", "4", "3", Set.of("3"), Set.of(), true, true);
|
|
Runner.run("exclude", "7", "8", "4", Set.of("4"), Set.of("3"), true, true);
|
|
}
|
|
} else {
|
|
Testee.run();
|
|
}
|
|
}
|
|
|
|
static class Runner {
|
|
private static final int LAST_N_LINES_COUNT = 5;
|
|
|
|
private static final Pattern reCompileCommand = Pattern.compile(
|
|
"CompileCommand: (.*)");
|
|
private static final Pattern reTieredEvent = Pattern.compile(
|
|
"[0-9.]+: \\[(call|loop|compile|force-compile|remove-from-queue|update-in-queue|reprofile|make-not-entrant) "
|
|
+ "level=\\d \\[([^]]+)] @-?\\d+ queues=(\\d+),(\\d+).*]");
|
|
private static final Pattern reCompilation = Pattern.compile(
|
|
"(\\d+) (C1|C2|no compiler): *(\\d+) ([ %][ s][ !][ b][ n]) ([-0-4 ]) +([^ ]+).*");
|
|
private static final Pattern reExcludeCompile = Pattern.compile(
|
|
"made not compilable on level (\\d) +([^ ]+) .* excluded by CompileCommand");
|
|
private static final Pattern reCompiledMethod = Pattern.compile(
|
|
".*\\n-{35} Assembly -{35}\\n(?:\\[[0-9.]+s]\\[warning]\\[os] Loading hsdis library failed\\n)?\\nCompiled method \\((?:c1|c2)\\) (\\d+) (C1|C2): *"
|
|
+ "(\\d+) ([ %][ s][ !][ b][ n]) ([-0-4 ]) +([^ ]+) \\(\\d+ bytes\\)", Pattern.DOTALL);
|
|
private static final Pattern reMethodData = Pattern.compile(
|
|
".*-{72}\\nstatic ([^\\n]+)\\n *interpreter_invocation_count: *\\d+\\n *invocation_counter: *\\d+", Pattern.DOTALL);
|
|
|
|
public static void run(String compileCmd,
|
|
String cmdCompLevel,
|
|
String printCmdCompLevel,
|
|
String tieredStopAtLevel,
|
|
Set<String> expectedCompLevel,
|
|
Set<String> expectExcludedAtLevels,
|
|
boolean expectMDOPrinted, boolean tieredCompilation)
|
|
throws IOException, InterruptedException {
|
|
|
|
System.out.println("\n########> Testing " + compileCmd + " " + cmdCompLevel);
|
|
|
|
ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(
|
|
"-XX:+UnlockDiagnosticVMOptions",
|
|
"-XX:+PrintCompilation",
|
|
"-XX:+CIPrintCompilerName",
|
|
"-XX:+PrintTieredEvents",
|
|
"-XX:" + (tieredCompilation ? "+" : "-") + "TieredCompilation",
|
|
"-XX:TieredStopAtLevel=" + tieredStopAtLevel,
|
|
"-XX:CompileCommand=" + compileCmd + "," + TEST_METHOD_NAME_DBL_COLON + "," + cmdCompLevel,
|
|
"-XX:CompileCommand=print," + TEST_METHOD_NAME_DBL_COLON + "," + printCmdCompLevel,
|
|
CompileLevelPrintTest.class.getName());
|
|
|
|
try (Process process = pb.start();
|
|
BufferedWriter processInput = process.outputWriter();
|
|
BufferedReader processOutput = process.inputReader();
|
|
BufferedReader processErrOut = process.errorReader()) {
|
|
long startNanos = System.nanoTime();
|
|
try {
|
|
IO.println("##> Testee PID: " + process.pid());
|
|
TesteeState testeeState = new TesteeState();
|
|
|
|
Thread stdoutParser = startDaemonThread(() ->
|
|
testeeOutputMonitor(processOutput, testeeState, "testee-" + process.pid() + ".out"));
|
|
Thread stderrParser = startDaemonThread(() ->
|
|
testeeErrorOutputMonitor(processErrOut, testeeState, "testee-" + process.pid() + ".err"));
|
|
|
|
System.out.println("##> Waiting for testee to get ready for the start command");
|
|
if (!testeeState.waitingForStartTest.await(TIMEOUT_SEC, TimeUnit.SECONDS)) {
|
|
throw new RuntimeException("No start signal from testee");
|
|
}
|
|
|
|
Asserts.assertTrue(waitUntil(() -> !process.isAlive()
|
|
|| (testeeState.compiler1QueueSize.get() < 5
|
|
&& testeeState.compiler2QueueSize.get() < 5)),
|
|
"Compiler queue is still not empty");
|
|
Asserts.assertTrue(testeeState.compileCommandsReported.contains(
|
|
compileCmd + " " + TEST_METHOD_NAME_DOT + " intx " + compileCmd + " = " + cmdCompLevel),
|
|
"'CompileCommand: " + compileCmd + "...' was not printed");
|
|
Asserts.assertTrue(testeeState.compileCommandsReported.contains(
|
|
"print " + TEST_METHOD_NAME_DOT + " intx print = " + printCmdCompLevel),
|
|
"'CompileCommand: print ...' was not printed");
|
|
|
|
System.out.println("##> Order testee to start");
|
|
processInput.write(START_CMD); processInput.newLine(); processInput.flush();
|
|
|
|
waitUntil(() -> !process.isAlive()
|
|
|| (expectedCompLevel.equals(testeeState.testMethodCompiledAtLevel)
|
|
&& expectedCompLevel.equals(testeeState.testMethodPrintedAtLevel)
|
|
&& expectExcludedAtLevels.equals(testeeState.testMethodExcludedAtLevel)));
|
|
|
|
Asserts.assertEquals(expectedCompLevel, testeeState.testMethodCompiledAtLevel,
|
|
"Test method was not compiled at required level (" + expectedCompLevel + ")");
|
|
Asserts.assertEquals(expectedCompLevel, testeeState.testMethodPrintedAtLevel,
|
|
"Test method assembly was not printed at required level (" + expectedCompLevel + ")");
|
|
Asserts.assertEquals(expectExcludedAtLevels, testeeState.testMethodExcludedAtLevel,
|
|
"Test method compilation was not excluded at required levels (" + expectExcludedAtLevels + ")");
|
|
|
|
System.out.println("##> Test method compiled, now stop");
|
|
processInput.write(STOP_CMD); processInput.newLine(); processInput.flush();
|
|
processInput.close();
|
|
|
|
Asserts.assertEquals(0, process.waitFor());
|
|
stdoutParser.join();
|
|
stderrParser.join();
|
|
|
|
Asserts.assertEquals(expectMDOPrinted, testeeState.testMethodMDOPrinted,
|
|
"Test method MDO was" + (expectMDOPrinted ? " NOT" : "") + " printed");
|
|
|
|
IO.println("########> Test passed");
|
|
} catch (Exception ex) {
|
|
IO.println("########> Test failed");
|
|
ex.printStackTrace();
|
|
process.destroyForcibly();
|
|
throw ex;
|
|
} finally {
|
|
IO.println("########> Elapsed " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos) + " ms");
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void testeeOutputMonitor(BufferedReader testeeOutput, TesteeState testeeState, String fileName) {
|
|
try (BufferedWriter outWriter = new BufferedWriter(new FileWriter(fileName))) {
|
|
String line;
|
|
LinkedList<String> lastNLines = new LinkedList<>();
|
|
|
|
while ((line = testeeOutput.readLine()) != null) {
|
|
outWriter.write(line);
|
|
outWriter.newLine();
|
|
|
|
line = line.trim();
|
|
|
|
lastNLines.addLast(line);
|
|
while (lastNLines.size() > LAST_N_LINES_COUNT) {
|
|
lastNLines.removeFirst();
|
|
}
|
|
String lastNLinesStr = String.join("\n", lastNLines);
|
|
|
|
Matcher matcher;
|
|
String msg = "";
|
|
|
|
if ((matcher = reCompileCommand.matcher(line)).matches()) {
|
|
testeeState.compileCommandsReported.add(matcher.group(1));
|
|
|
|
msg = "Compile command reported: " + matcher.group(1);
|
|
|
|
} else if ((matcher = reTieredEvent.matcher(line)).matches()) {
|
|
testeeState.compiler1QueueSize.set(Integer.parseInt(matcher.group(3)));
|
|
testeeState.compiler2QueueSize.set(Integer.parseInt(matcher.group(4)));
|
|
|
|
} else if ((matcher = reCompilation.matcher(line)).matches()) {
|
|
if ("C1".equalsIgnoreCase(matcher.group(2))) {
|
|
testeeState.compiler1QueueSize.decrementAndGet();
|
|
} else {
|
|
testeeState.compiler2QueueSize.decrementAndGet();
|
|
}
|
|
|
|
if (matcher.group(6).contains(TEST_METHOD_NAME_DBL_COLON)) {
|
|
testeeState.testMethodCompiledAtLevel.add(matcher.group(5));
|
|
|
|
msg = "Test method compiled:"
|
|
+ " compiler=" + matcher.group(2)
|
|
+ " level=" + matcher.group(5)
|
|
+ " compilation#=" + matcher.group(3)
|
|
+ " flags=" + matcher.group(4).trim()
|
|
+ " name=" + matcher.group(6);
|
|
}
|
|
} else if ((matcher = reCompiledMethod.matcher(lastNLinesStr)).matches()) {
|
|
if (matcher.group(6).contains(TEST_METHOD_NAME_DBL_COLON)) {
|
|
testeeState.testMethodPrintedAtLevel.add(matcher.group(5));
|
|
|
|
msg = "Test method assembly printed:"
|
|
+ " compiler=" + matcher.group(2)
|
|
+ " level=" + matcher.group(5)
|
|
+ " compilation#=" + matcher.group(3)
|
|
+ " flags=" + matcher.group(4).trim()
|
|
+ " name=" + matcher.group(6);
|
|
}
|
|
} else if ((matcher = reExcludeCompile.matcher(line)).matches()) {
|
|
if (matcher.group(2).contains(TEST_METHOD_NAME_DBL_COLON)) {
|
|
testeeState.testMethodExcludedAtLevel.add(matcher.group(1));
|
|
|
|
msg = "Test method not compilable:"
|
|
+ " level=" + matcher.group(1)
|
|
+ " name=" + matcher.group(2);
|
|
}
|
|
} else if ((matcher = reMethodData.matcher(lastNLinesStr)).matches()) {
|
|
if (matcher.group(1).contains(TEST_METHOD_SIGNATURE)) {
|
|
testeeState.testMethodMDOPrinted = true;
|
|
|
|
msg = "Test method data:"
|
|
+ " name=" + matcher.group(1);
|
|
}
|
|
} else if (DEBUG_OUTPUT) {
|
|
System.out.println("Did not parse stdout: " + line);
|
|
}
|
|
|
|
if (!msg.isEmpty()) {
|
|
msg = "##> " + msg;
|
|
System.out.println(msg);
|
|
outWriter.write(msg);
|
|
outWriter.newLine();
|
|
}
|
|
}
|
|
} catch (Exception ex) {
|
|
ex.printStackTrace();
|
|
}
|
|
}
|
|
|
|
private static void testeeErrorOutputMonitor(BufferedReader testeeErrorOutput, TesteeState testeeState, String fileName) {
|
|
try (BufferedWriter outWriter = new BufferedWriter(new FileWriter(fileName))) {
|
|
String line;
|
|
while ((line = testeeErrorOutput.readLine()) != null) {
|
|
outWriter.write(line);
|
|
outWriter.newLine();
|
|
|
|
line = line.trim();
|
|
|
|
if (TESTEE_WAITING_FOR_START_CMD.equals(line)) {
|
|
System.out.println("##> Testee is waiting for start command");
|
|
testeeState.waitingForStartTest.countDown();
|
|
} else if (line.startsWith("==>")) {
|
|
System.out.println(line);
|
|
} else if (line.startsWith("Exception in thread ") || line.startsWith("at ")) {
|
|
System.out.println("==>" + line);
|
|
} else if (DEBUG_OUTPUT) {
|
|
System.out.println("Did not parse stderr: " + line);
|
|
}
|
|
}
|
|
} catch (Exception ex) {
|
|
ex.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
|
|
static class Testee {
|
|
private static final CountDownLatch startCmd = new CountDownLatch(1);
|
|
private static final CountDownLatch stopCmd = new CountDownLatch(1);
|
|
|
|
static void run() throws IOException, InterruptedException {
|
|
System.err.println("==> entering testee()");
|
|
|
|
try {
|
|
startDaemonThread(Testee::inputMonitor);
|
|
|
|
if (stopCmd.getCount() == 0) {
|
|
return;
|
|
}
|
|
|
|
// Print 3 times, since the output can be intermixed with
|
|
System.err.println(TESTEE_WAITING_FOR_START_CMD);
|
|
if (!startCmd.await(TIMEOUT_SEC + 1, TimeUnit.SECONDS)) {
|
|
System.err.println("==> 'start' command was not given in stdin");
|
|
return;
|
|
}
|
|
|
|
if (stopCmd.getCount() == 0) {
|
|
return;
|
|
}
|
|
|
|
System.err.println("==> starting test");
|
|
runTestCode();
|
|
} finally {
|
|
System.err.println("==> exiting testee()");
|
|
}
|
|
}
|
|
|
|
private static void inputMonitor() {
|
|
try (BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in))) {
|
|
String line;
|
|
while ((line = stdin.readLine()) != null) {
|
|
line = line.trim();
|
|
System.err.println("==> STDIN: " + line);
|
|
|
|
switch (line) {
|
|
case START_CMD:
|
|
startCmd.countDown();
|
|
break;
|
|
|
|
case STOP_CMD:
|
|
stopCmd.countDown();
|
|
break;
|
|
|
|
default:
|
|
System.err.println("==> ERROR: unknown command");
|
|
}
|
|
}
|
|
} catch (Exception ex) {
|
|
ex.printStackTrace();
|
|
}
|
|
}
|
|
|
|
// For Tier4 600 invocation of this method with avg 25 loops for each should be enough
|
|
// to trigger Tier4CompilationThreshold=15000
|
|
private static void compiledMethod(final int a) {
|
|
int r = 0;
|
|
for (int i = 0; i < a % 50; i++) {
|
|
r ^= i;
|
|
}
|
|
if (r == 42) {
|
|
System.err.println("MAGIC!");
|
|
}
|
|
}
|
|
|
|
private static boolean longLoop() {
|
|
// To trigger compilation, 100-200 should be enough for C1 and 600-700 for C2
|
|
for (int i = 0; i < 10000; i++) {
|
|
compiledMethod(i);
|
|
if ((i & 0xf) == 0 && stopCmd.getCount() == 0) {
|
|
System.err.println("==> compiledMethod() has been compiled at iteration " + i);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static void runTestCode() {
|
|
for (int i = 0; i < 100; i++) {
|
|
if (longLoop()) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static Thread startDaemonThread(Runnable code) {
|
|
Thread t = new Thread(code);
|
|
t.setDaemon(true);
|
|
t.start();
|
|
return t;
|
|
}
|
|
|
|
static boolean waitUntil(BooleanSupplier condition) {
|
|
for (int maxWait = TIMEOUT_SEC * 5; maxWait > 0; --maxWait) {
|
|
if (condition.getAsBoolean()) {
|
|
return true;
|
|
}
|
|
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(200));
|
|
}
|
|
return false;
|
|
}
|
|
}
|