Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8302267: [jittester] Improve separation of test generation and execution #12527

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2023, 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
Expand All @@ -24,12 +24,6 @@
package jdk.test.lib.jittester;

import jdk.test.lib.util.Pair;
import jdk.test.lib.jittester.factories.IRNodeBuilder;
import jdk.test.lib.jittester.types.TypeKlass;
import jdk.test.lib.jittester.utils.FixedTrees;
import jdk.test.lib.jittester.utils.OptionResolver;
import jdk.test.lib.jittester.utils.OptionResolver.Option;
import jdk.test.lib.jittester.utils.PseudoRandom;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -39,56 +33,6 @@
public class Automatic {
public static final int MINUTES_TO_WAIT = Integer.getInteger("jdk.test.lib.jittester", 3);

private static Pair<IRNode, IRNode> generateIRTree(String name) {
SymbolTable.removeAll();
TypeList.removeAll();

IRNodeBuilder builder = new IRNodeBuilder()
.setPrefix(name)
.setName(name)
.setLevel(0);

Long complexityLimit = ProductionParams.complexityLimit.value();
IRNode privateClasses = null;
if (!ProductionParams.disableClasses.value()) {
long privateClassComlexity = (long) (complexityLimit * PseudoRandom.random());
try {
privateClasses = builder.setComplexityLimit(privateClassComlexity)
.getClassDefinitionBlockFactory()
.produce();
} catch (ProductionFailedException ex) {
ex.printStackTrace(System.out);
}
}
long mainClassComplexity = (long) (complexityLimit * PseudoRandom.random());
IRNode mainClass = null;
try {
mainClass = builder.setComplexityLimit(mainClassComplexity)
.getMainKlassFactory()
.produce();
TypeKlass aClass = new TypeKlass(name);
mainClass.getChild(1).addChild(FixedTrees.generateMainOrExecuteMethod(aClass, true));
mainClass.getChild(1).addChild(FixedTrees.generateMainOrExecuteMethod(aClass, false));
} catch (ProductionFailedException ex) {
ex.printStackTrace(System.out);
}
return new Pair<>(mainClass, privateClasses);
}

private static void initializeTestGenerator(String[] params) {
OptionResolver parser = new OptionResolver();
Option<String> propertyFileOpt = parser.addStringOption('p', "property-file",
"conf/default.properties", "File to read properties from");
ProductionParams.register(parser);
parser.parse(params, propertyFileOpt);
PseudoRandom.reset(ProductionParams.seed.value());
TypesParser.parseTypesAndMethods(ProductionParams.classesFile.value(),
ProductionParams.excludeMethodsFile.value());
if (ProductionParams.specificSeed.isSet()) {
PseudoRandom.setCurrentSeed(ProductionParams.specificSeed.value());
}
}

private static List<TestsGenerator> getTestGenerators() {
List<TestsGenerator> result = new ArrayList<>();
Class<?> factoryClass;
Expand All @@ -108,7 +52,7 @@ private static List<TestsGenerator> getTestGenerators() {
}

public static void main(String[] args) {
initializeTestGenerator(args);
IRTreeGenerator.initializeFromCmdlineArgs(args);
int counter = 0;
System.out.printf("Generating %d tests...%n", ProductionParams.numberOfTests.value());
System.out.printf(" %13s | %8s | %8s | %8s |%n", "start time", "count", "generat",
Expand All @@ -119,7 +63,7 @@ public static void main(String[] args) {
double start = System.currentTimeMillis();
System.out.print("[" + LocalTime.now() + "] |");
String name = "Test_" + counter;
Pair<IRNode, IRNode> irTree = generateIRTree(name);
Pair<IRNode, IRNode> irTree = IRTreeGenerator.generateIRTree(name);
System.out.printf(" %8d |", counter);
long maxWaitTime = TimeUnit.MINUTES.toMillis(MINUTES_TO_WAIT);
double generationTime = System.currentTimeMillis() - start;
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2023, 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
Expand Down Expand Up @@ -30,20 +30,22 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.function.Function;

/**
* Generates class files from IRTree
*/
class ByteCodeGenerator extends TestsGenerator {
private static final String DEFAULT_SUFFIX = "bytecode_tests";
public class ByteCodeGenerator extends TestsGenerator {
public static final String DEFAULT_SUFFIX = "bytecode_tests";

ByteCodeGenerator() {
super(DEFAULT_SUFFIX, s -> new String[0], "-Xcomp");
protected final HeaderFormatter headerFormatter;

protected ByteCodeGenerator() {
this(new HeaderFormatter.Builder().build());
}

ByteCodeGenerator(String suffix, Function<String, String[]> preRunActions, String jtDriverOptions) {
super(suffix, preRunActions, jtDriverOptions);
protected ByteCodeGenerator(HeaderFormatter headerFormatter) {
super(DEFAULT_SUFFIX);
this.headerFormatter = headerFormatter;
}

@Override
Expand All @@ -56,10 +58,10 @@ public void accept(IRNode mainClass, IRNode privateClasses) {

private void generateSeparateJtregHeader(IRNode mainClass) {
String mainClassName = mainClass.getName();
writeFile(generatorDir, mainClassName + ".java", getJtregHeader(mainClassName));
writeFile(generatorDir, mainClassName + ".java", headerFormatter.getJtregHeader(mainClassName));
}

private void generateClassFiles(IRNode mainClass, IRNode privateClasses) {
protected void generateClassFiles(IRNode mainClass, IRNode privateClasses) {
String mainClassName = mainClass.getName();
ensureExisting(generatorDir);
try {
Expand Down
@@ -0,0 +1,100 @@
/*
* Copyright (c) 2023, 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 jdk.test.lib.jittester;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.List;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Stream;

import jdk.test.lib.Asserts;

/**
* Compares reference output to the one that's being verified.
*
* The class can tolerate some errors (like OutOfMemoryError),
* as their nature is unpredictable and ignore missing intrinsics in
* compiled code stack traces.
*/
public class ErrorTolerance {

// Put the most annoying intrinsics here
private static final List<Predicate<String>> PATTERNS = List.of(
Pattern.compile(".*at java.base.*misc.Unsafe.allocateUninitializedArray0.*").asPredicate()
);

private static final Predicate<String> OOME = Pattern.compile(
"Exception in thread \".*\" java.lang.OutOfMemoryError.*").asPredicate();
private static final Predicate<String> SOE = Pattern.compile(
".*StackOverFlowError.*").asPredicate();

private static boolean isIntrinsicCandidate(String line) {
return PATTERNS.stream()
.map(pattern -> pattern.test(line))
.reduce(false, (acc, match) -> acc | match);
}

public static void assertIsAcceptable(String message, Stream<String> gold, Stream<String> run) {
Iterator<String> goldIt = gold.iterator();
Iterator<String> runIt = run.iterator();

while (goldIt.hasNext() && runIt.hasNext()) {
String goldLine = goldIt.next();
String runLine = runIt.next();

// Skipping intrinsics in 'run'
while (isIntrinsicCandidate(goldLine) &&
!runLine.equals(goldLine) &&
goldIt.hasNext()) {
goldLine = goldIt.next();
}

if (!goldLine.equals(runLine)) {
String fullMessage = "While " + message + ":\n" +
"Expected: " + goldLine + "\n" +
"Actual : " + runLine + "\n";
Asserts.fail(fullMessage);
}
}
Asserts.assertEquals(goldIt.hasNext(), runIt.hasNext(), message + ": files are different");
}

public static void assertIsAcceptable(Path gold, Path run) {
String comparisonNames = "'" + gold + "' and '" + run + "'";
try {
assertIsAcceptable("comparing files " + comparisonNames,
Files.lines(gold), Files.lines(run));
} catch (IOException e) {
throw new Error("Could not compare files: '" + comparisonNames);
}
}

public static boolean isReliable(Stream<String> gold) {
return !gold.anyMatch(line -> OOME.test(line) || SOE.test(line));
}
}
@@ -0,0 +1,126 @@
/*
* Copyright (c) 2023, 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 jdk.test.lib.jittester;

import java.util.function.Function;
import java.util.stream.Collectors;

import jdk.test.lib.jittester.types.TypeKlass;
import jdk.test.lib.jittester.utils.PseudoRandom;

public final class HeaderFormatter {
public static final String DISABLE_WARNINGS = "-XX:-PrintWarnings";

private final Builder builder;

public static class Builder {
private Function<String, String[]> preRunActions = s -> new String[0];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private Function<String, String[]> preRunActions = s -> new String[0];
private Function<String, String[]> preRunActions = s -> new String[0];

private String jtDriverOptions = "-Xcomp";
private String libraries = "/ ../";
private boolean printJitTesterHierarchy = true;
private boolean buildPrinter = true;

public Builder preRunActions(Function<String, String[]> from) {
preRunActions = from;
return this;
}

public Builder jtDriverOptions(String from) {
jtDriverOptions = from;
return this;
}

public Builder libraries(String from) {
libraries = from;
return this;
}

public Builder printJitTesterHierarchy(boolean from) {
printJitTesterHierarchy = from;
return this;
}

public Builder buildPrinter(boolean from) {
buildPrinter = false;
return this;
}

public HeaderFormatter build() {
return new HeaderFormatter(this);
}
}

private HeaderFormatter(Builder builder) {
this.builder = builder;
}

public String getJtregHeader(String mainClassName) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public String getJtregHeader(String mainClassName) {
public String getJtregHeader(String mainClassName) {

String synopsis = "seed = '" + ProductionParams.seed.value() + "'"
+ ", specificSeed = '" + PseudoRandom.getCurrentSeed() + "'";
StringBuilder header = new StringBuilder();
header.append("/*\n * @test\n * @summary ")
.append(synopsis)
.append(" \n * @library " + builder.libraries + "\n");
header.append(" * @run build jdk.test.lib.jittester.jtreg.JitTesterDriver"
+ (builder.buildPrinter ? " jdk.test.lib.jittester.jtreg.Printer\n" : "\n"));
for (String action : builder.preRunActions.apply(mainClassName)) {
header.append(" * ")
.append(action)
.append("\n");
}
header.append(" * @run driver/timeout=180 jdk.test.lib.jittester.jtreg.JitTesterDriver ")
.append(DISABLE_WARNINGS)
.append(" ")
.append(builder.jtDriverOptions)
.append(" ")
.append(mainClassName)
.append("\n */\n\n");
if (ProductionParams.printHierarchy.value() && builder.printJitTesterHierarchy) {
header.append("/*\n")
.append(printHierarchy())
.append("*/\n");
}
return header.toString();
}

private static String printHierarchy() {
return TypeList.getAll()
.stream()
.filter(t -> t instanceof TypeKlass)
.map(t -> typeDescription((TypeKlass) t))
.collect(Collectors.joining("\n", "CLASS HIERARCHY:\n", "\n"));
}

private static String typeDescription(TypeKlass type) {
StringBuilder result = new StringBuilder();
String parents = type.getParentsNames().stream().collect(Collectors.joining(","));
result.append(type.isAbstract() ? "abstract " : "")
.append(type.isFinal() ? "final " : "")
.append(type.isInterface() ? "interface " : "class ")
.append(type.getName())
.append(parents.isEmpty() ? "" : ": " + parents);
return result.toString();
}

}