Skip to content

Commit

Permalink
8339366: [jittester] Make it possible to generate tests without execu…
Browse files Browse the repository at this point in the history
…tion

Reviewed-by: lmesnik
  • Loading branch information
Evgeny Nikitin authored and lmesnik committed Sep 9, 2024
1 parent 6b5958d commit 559fc71
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 80 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2024, 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 @@ -23,13 +23,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,57 +32,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) {
ProductionLimiter.resetTimer();
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 @@ -109,7 +51,9 @@ private static List<TestsGenerator> getTestGenerators() {
}

public static void main(String[] args) {
initializeTestGenerator(args);
ProductionParams.initializeFromCmdline(args);
IRTreeGenerator.initializeWithProductionParams();

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 @@ -121,15 +65,15 @@ public static void main(String[] args) {
try {
System.out.print("[" + LocalTime.now() + "] |");
String name = "Test_" + counter;
Pair<IRNode, IRNode> irTree = generateIRTree(name);
var test = IRTreeGenerator.generateIRTree(name);
System.out.printf(" %8d |", counter);
long maxWaitTime = TimeUnit.MINUTES.toMillis(MINUTES_TO_WAIT);
double generationTime = System.currentTimeMillis() - start;
System.out.printf(" %8.0f |", generationTime);
start = System.currentTimeMillis();
Thread generatorThread = new Thread(() -> {
for (TestsGenerator generator : generators) {
generator.accept(irTree.first, irTree.second);
generator.accept(test);
}
});
generatorThread.start();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2024, 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 @@ -47,16 +47,17 @@ class ByteCodeGenerator extends TestsGenerator {
}

@Override
public void accept(IRNode mainClass, IRNode privateClasses) {
generateClassFiles(mainClass, privateClasses);
generateSeparateJtregHeader(mainClass);
public void accept(IRTreeGenerator.Test test) {
IRNode mainClass = test.mainClass();
generateClassFiles(mainClass, test.privateClasses());
generateSeparateJtregHeader(test.seed(), mainClass);
compilePrinter();
generateGoldenOut(mainClass.getName());
}

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

private void generateClassFiles(IRNode mainClass, IRNode privateClasses) {
Expand Down Expand Up @@ -94,4 +95,17 @@ private void writeFile(String fileName, byte[] bytecode) {
ex.printStackTrace();
}
}

public static void main(String[] args) throws Exception {
ProductionParams.initializeFromCmdline(args);
IRTreeGenerator.initializeWithProductionParams();

ByteCodeGenerator generator = new ByteCodeGenerator();

for (String mainClass : ProductionParams.mainClassNames.value()) {
var test = IRTreeGenerator.generateIRTree(mainClass);
generator.generateClassFiles(test.mainClass(), test.privateClasses());
generator.generateSeparateJtregHeader(test.seed(), test.mainClass());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright (c) 2024, 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 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.PseudoRandom;

/**
* Generates IR trees for fuzzy test classes.
*/
public class IRTreeGenerator {

/**
* Generated Test - main and private classes trees along with random seed used for generation.
*/
public record Test (long seed, IRNode mainClass, IRNode privateClasses) {};

/**
* Generates IR trees for main and private classes.
*
* @param name main class name
* @return a pair (main class; private classes)
*/
public static Test generateIRTree(String name) {
long seed = PseudoRandom.getCurrentSeed();
ProductionLimiter.resetTimer();
//NB: SymbolTable is a widely-used singleton, hence all the locking.
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 Test(seed, mainClass, privateClasses);
}

/**
* Initializes the generator from ProductionParams static class.
*/
public static void initializeWithProductionParams() {
TypesParser.parseTypesAndMethods(ProductionParams.classesFile.value(),
ProductionParams.excludeMethodsFile.value());
if (ProductionParams.specificSeed.isSet()) {
PseudoRandom.setCurrentSeed(ProductionParams.specificSeed.value());
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,20 @@ public class JavaCodeGenerator extends TestsGenerator {
}

@Override
public void accept(IRNode mainClass, IRNode privateClasses) {
public void accept(IRTreeGenerator.Test test) {
IRNode mainClass = test.mainClass();
String mainClassName = mainClass.getName();
generateSources(mainClass, privateClasses);
generateSources(test.seed(), mainClass, test.privateClasses());
compilePrinter();
compileJavaFile(mainClassName);
generateGoldenOut(mainClassName);
}

private void generateSources(IRNode mainClass, IRNode privateClasses) {
private void generateSources(long seed, IRNode mainClass, IRNode privateClasses) {
String mainClassName = mainClass.getName();
StringBuilder code = new StringBuilder();
JavaCodeVisitor vis = new JavaCodeVisitor();
code.append(getJtregHeader(mainClassName));
code.append(getJtregHeader(mainClassName, seed));
if (privateClasses != null) {
code.append(privateClasses.accept(vis));
}
Expand All @@ -79,7 +80,19 @@ private void compileJavaFile(String mainClassName) {
}
}

private static String[] generatePrerunAction(String mainClassName) {
protected static String[] generatePrerunAction(String mainClassName) {
return new String[] {"@compile " + mainClassName + ".java"};
}

public static void main(String[] args) throws Exception {
ProductionParams.initializeFromCmdline(args);
IRTreeGenerator.initializeWithProductionParams();

JavaCodeGenerator generator = new JavaCodeGenerator();

for (String mainClass : ProductionParams.mainClassNames.value()) {
var test = IRTreeGenerator.generateIRTree(mainClass);
generator.generateSources(test.seed(), test.mainClass(), test.privateClasses());
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 2024, 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 @@ -23,11 +23,15 @@

package jdk.test.lib.jittester;

import java.util.List;

import jdk.test.lib.jittester.utils.OptionResolver;
import jdk.test.lib.jittester.utils.OptionResolver.Option;
import jdk.test.lib.jittester.utils.PseudoRandom;

public class ProductionParams {

public static Option<List<String>> mainClassNames = null;
public static Option<Integer> productionLimit = null;
public static Option<Integer> productionLimitSeconds = null;
public static Option<Integer> dataMemberLimit = null;
Expand Down Expand Up @@ -72,6 +76,7 @@ public class ProductionParams {
// workaraound: to reduce chance throwing ArrayIndexOutOfBoundsException
public static Option<Integer> chanceExpressionIndex = null;
public static Option<String> testbaseDir = null;
public static Option<String> tempDir = null;
public static Option<Integer> numberOfTests = null;
public static Option<String> seed = null;
public static Option<Long> specificSeed = null;
Expand All @@ -81,6 +86,7 @@ public class ProductionParams {
public static Option<String> generatorsFactories = null;

public static void register(OptionResolver optionResolver) {
mainClassNames = optionResolver.addRepeatingOption('k', "main-class", "", "Main class name");
productionLimit = optionResolver.addIntegerOption('l', "production-limit", 100, "Limit on steps in the production of an expression");
productionLimitSeconds = optionResolver.addIntegerOption("production-limit-seconds", 600, "Limit the time a test generation may take");
dataMemberLimit = optionResolver.addIntegerOption('v', "data-member-limit", 10, "Upper limit on data members");
Expand Down Expand Up @@ -124,6 +130,7 @@ public static void register(OptionResolver optionResolver) {
enableFinalizers = optionResolver.addBooleanOption("enable-finalizers", "Enable finalizers (for stress testing)");
chanceExpressionIndex = optionResolver.addIntegerOption("chance-expression-index", 0, "A non negative decimal integer used to restrict chane of generating expression in array index while creating or accessing by index");
testbaseDir = optionResolver.addStringOption("testbase-dir", ".", "Testbase dir");
tempDir = optionResolver.addStringOption("temp-dir", ".", "Temp dir path");
numberOfTests = optionResolver.addIntegerOption('n', "number-of-tests", 0, "Number of test classes to generate");
seed = optionResolver.addStringOption("seed", "", "Random seed");
specificSeed = optionResolver.addLongOption('z', "specificSeed", 0L, "A seed to be set for specific test generation(regular seed still needed for initialization)");
Expand All @@ -132,4 +139,18 @@ public static void register(OptionResolver optionResolver) {
generators = optionResolver.addStringOption("generators", "", "Comma-separated list of generator names");
generatorsFactories = optionResolver.addStringOption("generatorsFactories", "", "Comma-separated list of generators factories class names");
}

/**
* Initializes from the given command-line args
*
* @param args command-line arguments to use for initialization
*/
public static void initializeFromCmdline(String[] args) {
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(args, propertyFileOpt);
PseudoRandom.reset(ProductionParams.seed.value());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,12 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
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 abstract class TestsGenerator implements BiConsumer<IRNode, IRNode> {
public abstract class TestsGenerator implements Consumer<IRTreeGenerator.Test> {
private static final int DEFAULT_JTREG_TIMEOUT = 120;
protected static final String JAVA_BIN = getJavaPath();
protected static final String JAVAC = Paths.get(JAVA_BIN, "javac").toString();
Expand Down Expand Up @@ -121,9 +120,9 @@ protected static void ensureExisting(Path path) {
}
}

protected String getJtregHeader(String mainClassName) {
protected String getJtregHeader(String mainClassName, long seed) {
String synopsis = "seed = '" + ProductionParams.seed.value() + "'"
+ ", specificSeed = '" + PseudoRandom.getCurrentSeed() + "'";
+ ", specificSeed = '" + seed + "'";
StringBuilder header = new StringBuilder();
header.append("/*\n * @test\n * @summary ")
.append(synopsis)
Expand Down
Loading

1 comment on commit 559fc71

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

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

Please sign in to comment.