From 559fc711e03cf0086bea399ffb40cf294cbbb6e1 Mon Sep 17 00:00:00 2001 From: Evgeny Nikitin Date: Mon, 9 Sep 2024 19:55:45 +0000 Subject: [PATCH] 8339366: [jittester] Make it possible to generate tests without execution Reviewed-by: lmesnik --- .../src/jdk/test/lib/jittester/Automatic.java | 68 ++----------- .../test/lib/jittester/ByteCodeGenerator.java | 26 +++-- .../test/lib/jittester/IRTreeGenerator.java | 97 +++++++++++++++++++ .../test/lib/jittester/JavaCodeGenerator.java | 23 ++++- .../test/lib/jittester/ProductionParams.java | 23 ++++- .../test/lib/jittester/TestsGenerator.java | 9 +- .../lib/jittester/utils/OptionResolver.java | 22 ++++- 7 files changed, 188 insertions(+), 80 deletions(-) create mode 100644 test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/IRTreeGenerator.java diff --git a/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/Automatic.java b/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/Automatic.java index c74c01f1bf7..61ec487c7ff 100644 --- a/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/Automatic.java +++ b/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/Automatic.java @@ -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 @@ -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; @@ -39,57 +32,6 @@ import java.util.function.Function; public class Automatic { public static final int MINUTES_TO_WAIT = Integer.getInteger("jdk.test.lib.jittester", 3); - private static Pair 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 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 getTestGenerators() { List result = new ArrayList<>(); Class factoryClass; @@ -109,7 +51,9 @@ public class Automatic { } 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", @@ -121,7 +65,7 @@ public class Automatic { try { System.out.print("[" + LocalTime.now() + "] |"); String name = "Test_" + counter; - Pair 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; @@ -129,7 +73,7 @@ public class Automatic { start = System.currentTimeMillis(); Thread generatorThread = new Thread(() -> { for (TestsGenerator generator : generators) { - generator.accept(irTree.first, irTree.second); + generator.accept(test); } }); generatorThread.start(); diff --git a/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/ByteCodeGenerator.java b/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/ByteCodeGenerator.java index 87ca48c5acb..fac88a1833e 100644 --- a/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/ByteCodeGenerator.java +++ b/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/ByteCodeGenerator.java @@ -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 @@ -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) { @@ -94,4 +95,17 @@ class ByteCodeGenerator extends TestsGenerator { 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()); + } + } } diff --git a/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/IRTreeGenerator.java b/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/IRTreeGenerator.java new file mode 100644 index 00000000000..b748fadde59 --- /dev/null +++ b/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/IRTreeGenerator.java @@ -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()); + } + } + +} diff --git a/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/JavaCodeGenerator.java b/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/JavaCodeGenerator.java index 7ca940fe531..16d02f91d8e 100644 --- a/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/JavaCodeGenerator.java +++ b/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/JavaCodeGenerator.java @@ -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)); } @@ -79,7 +80,19 @@ public class JavaCodeGenerator extends TestsGenerator { } } - 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()); + } + } } diff --git a/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/ProductionParams.java b/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/ProductionParams.java index 69d489bf00d..afc526e81c7 100644 --- a/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/ProductionParams.java +++ b/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/ProductionParams.java @@ -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 @@ -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> mainClassNames = null; public static Option productionLimit = null; public static Option productionLimitSeconds = null; public static Option dataMemberLimit = null; @@ -72,6 +76,7 @@ public class ProductionParams { // workaraound: to reduce chance throwing ArrayIndexOutOfBoundsException public static Option chanceExpressionIndex = null; public static Option testbaseDir = null; + public static Option tempDir = null; public static Option numberOfTests = null; public static Option seed = null; public static Option specificSeed = null; @@ -81,6 +86,7 @@ public class ProductionParams { public static Option 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"); @@ -124,6 +130,7 @@ public class ProductionParams { 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)"); @@ -132,4 +139,18 @@ public class ProductionParams { 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 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()); + } } diff --git a/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/TestsGenerator.java b/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/TestsGenerator.java index 5d8ac107d6a..3eee8e5a021 100644 --- a/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/TestsGenerator.java +++ b/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/TestsGenerator.java @@ -30,13 +30,12 @@ import java.nio.file.Files; 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 { +public abstract class TestsGenerator implements Consumer { 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(); @@ -121,9 +120,9 @@ public abstract class TestsGenerator implements BiConsumer { } } - 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) diff --git a/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/utils/OptionResolver.java b/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/utils/OptionResolver.java index 09f7f20695c..87207628df3 100644 --- a/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/utils/OptionResolver.java +++ b/test/hotspot/jtreg/testlibrary/jittester/src/jdk/test/lib/jittester/utils/OptionResolver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 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 @@ -156,6 +156,12 @@ public class OptionResolver { return addBooleanOption(null, name, false, description); } + public Option> addRepeatingOption(Character key, String name, String defaultValue, String description) { + final Option> option = new RepeatingOption(key, name, defaultValue, description); + register(option); + return option; + } + private void register(Option option) { if (options.put("--" + option.longName, option) != null) { throw new RuntimeException("Option is already registered for key " + option.longName); @@ -264,6 +270,20 @@ public class OptionResolver { } } + private class RepeatingOption extends Option> { + List accumulated = new ArrayList(); + + RepeatingOption(Character s, String l, String v, String d) { + super(s, l, List.of(v), d); + } + + @Override + public List parseFromString(String arg) { + accumulated.add(arg); + return accumulated; + } + } + public Collection> getRegisteredOptions() { return Collections.unmodifiableSet(new LinkedHashSet<>(options.values())); }