--- old/test/TEST.groups 2015-06-16 18:36:43.073575618 +0300 +++ new/test/TEST.groups 2015-06-16 18:36:42.921575617 +0300 @@ -416,6 +416,7 @@ -runtime/SharedArchiveFile/DefaultUseWithClient.java \ -runtime/Thread/CancellableThreadTest.java \ -runtime/7158988/FieldMonitor.java \ + -runtime/CommandLine/OptionsValidation/TestOptionsWithRanges.java \ sanity/ \ testlibrary_tests/TestMutuallyExclusivePlatformPredicates.java --- /dev/null 2015-06-16 16:00:51.952800020 +0300 +++ new/test/runtime/CommandLine/OptionsValidation/TestJcmdOutput.java 2015-06-16 18:36:43.397575619 +0300 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test + * @summary Verify jcmd error message for out-of-range value and for + * value which is not allowed by constraint. Also check that + * jcmd does not print an error message to the target process output. + * @library /testlibrary + * @modules java.base/sun.misc + * java.management + * jdk.management + * @run main TestJcmdOutput + */ + +import jdk.test.lib.Asserts; +import jdk.test.lib.DynamicVMOption; +import jdk.test.lib.OutputAnalyzer; +import jdk.test.lib.ProcessTools; +import jdk.test.lib.dcmd.PidJcmdExecutor; + +public class TestJcmdOutput { + + /* Message printed by jcmd for value which is out-of-range */ + static final String JCMD_OUT_OF_RANGE_MESSAGE = "error: must have value in range"; + /* Message printed by jcmd for value which is not allowed by constraint */ + static final String JCMD_CONSTRAINT_MESSAGE = "value violates its flag's constraint"; + + public static void main(String[] args) throws Exception { + OutputAnalyzer output; + + System.out.println("Verify jcmd error message and that jcmd does not write errors to the target process output"); + output = new OutputAnalyzer((ProcessTools.createJavaProcessBuilder( + "-Dtest.jdk=" + System.getProperty("test.jdk"), + "-XX:MinHeapFreeRatio=20", "-XX:MaxHeapFreeRatio=80", runJcmd.class.getName())).start()); + + output.shouldHaveExitValue(0); + /* Verify that jcmd not print error message to the target process output */ + output.shouldNotContain(JCMD_OUT_OF_RANGE_MESSAGE); + output.shouldNotContain(JCMD_CONSTRAINT_MESSAGE); + } + + public static class runJcmd { + + public static void main(String[] args) throws Exception { + int minHeapFreeRatio = new Integer((new DynamicVMOption("MinHeapFreeRatio")).getValue()); + int maxHeapFreeRatio = new Integer((new DynamicVMOption("MaxHeapFreeRatio")).getValue()); + PidJcmdExecutor executor = new PidJcmdExecutor(); + + Asserts.assertGT(minHeapFreeRatio, 0, "MinHeapFreeRatio must be greater than 0"); + Asserts.assertLT(maxHeapFreeRatio, 100, "MaxHeapFreeRatio must be less than 100"); + + /* Check out-of-range values */ + executor.execute("VM.set_flag MinHeapFreeRatio -1", true).shouldContain(JCMD_OUT_OF_RANGE_MESSAGE); + executor.execute("VM.set_flag MaxHeapFreeRatio 101", true).shouldContain(JCMD_OUT_OF_RANGE_MESSAGE); + + /* Check values which not allowed by constraint */ + executor.execute( + String.format("VM.set_flag MinHeapFreeRatio %d", maxHeapFreeRatio + 1), true) + .shouldContain(JCMD_CONSTRAINT_MESSAGE); + executor.execute( + String.format("VM.set_flag MaxHeapFreeRatio %d", minHeapFreeRatio - 1), true) + .shouldContain(JCMD_CONSTRAINT_MESSAGE); + } + } +} --- /dev/null 2015-06-16 16:00:51.952800020 +0300 +++ new/test/runtime/CommandLine/OptionsValidation/TestOptionsWithRanges.java 2015-06-16 18:36:43.833575620 +0300 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test + * @summary Test VM Options with ranges + * @library /testlibrary /runtime/CommandLine/OptionsValidation/common + * @modules java.base/sun.misc + * java.management + * jdk.attach + * jdk.management/sun.tools.attach + * @run main/othervm/timeout=600 TestOptionsWithRanges + */ + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import jdk.test.lib.Asserts; +import optionsvalidation.JVMOption; +import optionsvalidation.JVMOptionsUtils; + +public class TestOptionsWithRanges { + + public static void main(String[] args) throws Exception { + int failedTests; + Map allOptionsAsMap = JVMOptionsUtils.getOptionsWithRangeAsMap(); + List allOptions; + + /* + * Remove CICompilerCount from testing because currently it can hang system + */ + allOptionsAsMap.remove("CICompilerCount"); + + allOptions = new ArrayList<>(allOptionsAsMap.values()); + + Asserts.assertGT(allOptions.size(), 0, "Options with ranges not found!"); + + System.out.println("Parsed " + allOptions.size() + " options with ranges. Start test!"); + + failedTests = JVMOptionsUtils.runCommandLineTests(allOptions); + + Asserts.assertEQ(failedTests, 0, + String.format("%d tests failed! %s", failedTests, JVMOptionsUtils.getMessageWithFailures())); + } +} --- /dev/null 2015-06-16 16:00:51.952800020 +0300 +++ new/test/runtime/CommandLine/OptionsValidation/TestOptionsWithRangesDynamic.java 2015-06-16 18:36:44.249575622 +0300 @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2015, 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. + */ + +/* + * @test + * @summary Test writeable VM Options with ranges. + * @library /testlibrary /runtime/CommandLine/OptionsValidation/common + * @modules java.base/sun.misc + * java.management + * jdk.attach + * jdk.management/sun.tools.attach + * @run main/othervm -XX:MinHeapFreeRatio=0 -XX:MaxHeapFreeRatio=100 TestOptionsWithRangesDynamic + */ + +import java.util.List; +import jdk.test.lib.Asserts; +import optionsvalidation.JVMOption; +import optionsvalidation.JVMOptionsUtils; + +public class TestOptionsWithRangesDynamic { + + public static void main(String[] args) throws Exception { + int failedTests; + List allWriteableOptions; + + /* Get only writeable options */ + allWriteableOptions = JVMOptionsUtils.getOptionsWithRange(origin -> (origin.contains("manageable") || origin.contains("rw"))); + + Asserts.assertGT(allWriteableOptions.size(), 0, "Options with ranges not found!"); + + System.out.println("Test " + allWriteableOptions.size() + " writeable options with ranges. Start test!"); + + failedTests = JVMOptionsUtils.runDynamicTests(allWriteableOptions); + + failedTests += JVMOptionsUtils.runJcmdTests(allWriteableOptions); + + failedTests += JVMOptionsUtils.runAttachTests(allWriteableOptions); + + Asserts.assertEQ(failedTests, 0, + String.format("%d tests failed! %s", failedTests, JVMOptionsUtils.getMessageWithFailures())); + } +} \ No newline at end of file --- /dev/null 2015-06-16 16:00:51.952800020 +0300 +++ new/test/runtime/CommandLine/OptionsValidation/common/optionsvalidation/DoubleJVMOption.java 2015-06-16 18:36:44.673575623 +0300 @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2015, 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 optionsvalidation; + +import java.util.ArrayList; +import java.util.List; + +public class DoubleJVMOption extends JVMOption { + + /** + * Additional double values to test + */ + private static final double ADDITIONAL_TEST_DOUBLE_NEGATIVE = -1.5; + private static final double ADDITIONAL_TEST_DOUBLE_ZERO = 0.0; + private static final double ADDITIONAL_TEST_DOUBLE_POSITIVE = 1.75; + + /** + * Mininum option value + */ + private double min; + /** + * Maximum option value + */ + private double max; + + /** + * Initialize double option with passed name + * + * @param name name of the option + */ + DoubleJVMOption(String name) { + this.name = name; + min = Double.MIN_VALUE; + max = Double.MAX_VALUE; + } + + /** + * Initialize double option with passed name, min and max values + * + * @param name name of the option + * @param min minimum value of the option + * @param max maximum value of the option + */ + public DoubleJVMOption(String name, double min, double max) { + this(name); + this.min = min; + this.max = max; + } + + /** + * Set new minimum option value + * + * @param min new minimum value + */ + @Override + void setMin(String min) { + this.min = new Double(min); + } + + /** + * Get string with minimum value of the option + * + * @return string with minimum value of the option + */ + @Override + String getMin() { + return formatValue(min); + } + + /** + * Set new maximum option value + * + * @param max new maximum value + */ + @Override + void setMax(String max) { + this.max = new Double(max); + } + + /** + * Get string with maximum value of the option + * + * @return string with maximum value of the option + */ + @Override + String getMax() { + return formatValue(max); + } + + private String formatValue(double value) { + return String.format("%f", value); + } + + /** + * Return list of strings with valid option values which used for testing + * using jcmd, attach and etc. + * + * @return list of strings which contain valid values for option + */ + @Override + protected List getValidValues() { + List validValues = new ArrayList<>(); + + validValues.add(formatValue(min)); + validValues.add(formatValue(max)); + + if ((Double.compare(min, ADDITIONAL_TEST_DOUBLE_NEGATIVE) < 0) + && (Double.compare(max, ADDITIONAL_TEST_DOUBLE_NEGATIVE) > 0)) { + validValues.add(formatValue(ADDITIONAL_TEST_DOUBLE_NEGATIVE)); + } + + if ((Double.compare(min, ADDITIONAL_TEST_DOUBLE_ZERO) < 0) + && (Double.compare(max, ADDITIONAL_TEST_DOUBLE_ZERO) > 0)) { + validValues.add(formatValue(ADDITIONAL_TEST_DOUBLE_ZERO)); + } + + if ((Double.compare(min, ADDITIONAL_TEST_DOUBLE_POSITIVE) < 0) + && (Double.compare(max, ADDITIONAL_TEST_DOUBLE_POSITIVE) > 0)) { + validValues.add(formatValue(ADDITIONAL_TEST_DOUBLE_POSITIVE)); + } + + return validValues; + } + + /** + * Return list of strings with invalid option values which used for testing + * using jcmd, attach and etc. + * + * @return list of strings which contain invalid values for option + */ + @Override + protected List getInvalidValues() { + List invalidValues = new ArrayList<>(); + + if (withRange) { + /* Return invalid values only for options which have defined range in VM */ + if (Double.compare(min, Double.MIN_VALUE) != 0) { + if ((Double.compare(min, 0.0) > 0) + && (Double.isNaN(min * 0.999) == false)) { + invalidValues.add(formatValue(min * 0.999)); + } else if ((Double.compare(min, 0.0) < 0) + && (Double.isNaN(min * 1.001) == false)) { + invalidValues.add(formatValue(min * 1.001)); + } + } + + if (Double.compare(max, Double.MAX_VALUE) != 0) { + if ((Double.compare(max, 0.0) > 0) + && (Double.isNaN(max * 1.001) == false)) { + invalidValues.add(formatValue(max * 1.001)); + } else if ((Double.compare(max, 0.0) < 0) + && (Double.isNaN(max * 0.999) == false)) { + invalidValues.add(formatValue(max * 0.999)); + } + } + } + + return invalidValues; + } + + /** + * Return expected error message for option with value "value" when it used + * on command line with passed value + * + * @param value option value + * @return expected error message + */ + @Override + protected String getErrorMessageCommandLine(String value) { + String errorMsg; + + if (withRange) { + /* Option have defined range in VM */ + errorMsg = "is outside the allowed range"; + } else { + errorMsg = ""; + } + + return errorMsg; + } +} --- /dev/null 2015-06-16 16:00:51.952800020 +0300 +++ new/test/runtime/CommandLine/OptionsValidation/common/optionsvalidation/IntJVMOption.java 2015-06-16 18:36:45.077575624 +0300 @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2015, 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 optionsvalidation; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import jdk.test.lib.Platform; + +public class IntJVMOption extends JVMOption { + + private static final BigInteger MIN_LONG; + private static final BigInteger MAX_LONG; + private static final BigInteger MAX_UNSIGNED_LONG; + private static final BigInteger MAX_UNSIGNED_LONG_64; + private static final BigInteger MINUS_ONE = new BigInteger("-1"); + private static final BigInteger TWO = new BigInteger("2"); + private static final BigInteger MIN_4_BYTE_INT = new BigInteger("-2147483648"); + private static final BigInteger MAX_4_BYTE_INT = new BigInteger("2147483647"); + private static final BigInteger MAX_4_BYTE_INT_PLUS_ONE = new BigInteger("2147483648"); + private static final BigInteger MAX_4_BYTE_UNSIGNED_INT = new BigInteger("4294967295"); + private static final BigInteger MAX_4_BYTE_UNSIGNED_INT_PLUS_ONE = new BigInteger("4294967296"); + + /** + * Mininum option value + */ + private BigInteger min; + + /** + * Maximum option value + */ + private BigInteger max; + + /** + * Option type: intx, uintx, size_t or uint64_t + */ + private String type; + + /** + * Is this value signed or unsigned + */ + private boolean unsigned; + + /** + * Is this 32 bit type + */ + private boolean is32Bit = false; + + /** + * Is this value 64 bit unsigned + */ + private boolean uint64 = false; + + static { + if (Platform.is32bit()) { + MIN_LONG = new BigInteger(String.valueOf(Integer.MIN_VALUE)); + MAX_LONG = new BigInteger(String.valueOf(Integer.MAX_VALUE)); + MAX_UNSIGNED_LONG = MAX_LONG.multiply(TWO).add(BigInteger.ONE); + } else { + MIN_LONG = new BigInteger(String.valueOf(Long.MIN_VALUE)); + MAX_LONG = new BigInteger(String.valueOf(Long.MAX_VALUE)); + MAX_UNSIGNED_LONG = MAX_LONG.multiply(TWO).add(BigInteger.ONE); + } + + MAX_UNSIGNED_LONG_64 = (new BigInteger(String.valueOf(Long.MAX_VALUE))) + .multiply(TWO).add(BigInteger.ONE); + } + + private IntJVMOption() { + type = ""; + } + + /** + * Initialize new integer option with given type. Type can be: INTX - + * integer signed option UINTX - unsigned integer option UINT64_T - unsigned + * 64 bit integer option + * + * @param name name of the option + * @param type type of the option + */ + IntJVMOption(String name, String type) { + this.name = name; + this.type = type; + + switch (type) { + case "uint64_t": + unsigned = true; + uint64 = true; + max = MAX_UNSIGNED_LONG_64; + break; + case "uintx": + case "size_t": + unsigned = true; + max = MAX_UNSIGNED_LONG; + break; + case "uint": + unsigned = true; + is32Bit = true; + max = MAX_4_BYTE_UNSIGNED_INT; + break; + case "int": + min = MIN_4_BYTE_INT; + max = MAX_4_BYTE_INT; + is32Bit = true; + break; + default: + min = MIN_LONG; + max = MAX_LONG; + break; + } + + if (unsigned) { + min = BigInteger.ZERO; + } + } + + /** + * Initialize integer option with passed name, min and max values. Min and + * max are string because they can be very big, bigger than long. + * + * @param name name of the option + * @param min minimum value of the option + * @param max maximum value of the option + */ + public IntJVMOption(String name, String min, String max) { + this(); + this.name = name; + this.min = new BigInteger(min); + this.max = new BigInteger(max); + } + + /** + * Set new minimum option value + * + * @param min new minimum value + */ + @Override + void setMin(String min) { + this.min = new BigInteger(min); + } + + /** + * Get string with minimum value of the option + * + * @return string with minimum value of the option + */ + @Override + String getMin() { + return min.toString(); + } + + /** + * Set new maximum option value + * + * @param max new maximum value + */ + @Override + void setMax(String max) { + this.max = new BigInteger(max); + } + + /** + * Get string with maximum value of the option + * + * @return string with maximum value of the option + */ + @Override + String getMax() { + return max.toString(); + } + + /** + * Return list of strings with valid option values which used for testing + * using jcmd, attach and etc. + * + * @return list of strings which contain valid values for option + */ + @Override + protected List getValidValues() { + List validValues = new ArrayList<>(); + + validValues.add(min.toString()); + validValues.add(max.toString()); + + if ((min.compareTo(MINUS_ONE) == -1) && (max.compareTo(MINUS_ONE) == 1)) { + /* + * Add -1 as valid value if min is less than -1 and max is greater than -1 + */ + validValues.add("-1"); + } + + if ((min.compareTo(BigInteger.ZERO) == -1) && (max.compareTo(BigInteger.ZERO) == 1)) { + /* + * Add 0 as valid value if min is less than 0 and max is greater than 0 + */ + validValues.add("0"); + } + if ((min.compareTo(BigInteger.ONE) == -1) && (max.compareTo(BigInteger.ONE) == 1)) { + /* + * Add 1 as valid value if min is less than 1 and max is greater than 1 + */ + validValues.add("1"); + } + + if (max.compareTo(MAX_4_BYTE_INT_PLUS_ONE) == 1) { + /* + * Check for overflow when flag is assigned to the + * 4 byte int variable + */ + validValues.add(MAX_4_BYTE_INT_PLUS_ONE.toString()); + } + + if (max.compareTo(MAX_4_BYTE_UNSIGNED_INT_PLUS_ONE) == 1) { + /* + * Check for overflow when flag is assigned to the + * 4 byte unsigned int variable + */ + validValues.add(MAX_4_BYTE_UNSIGNED_INT_PLUS_ONE.toString()); + } + + return validValues; + } + + /** + * Return list of strings with invalid option values which used for testing + * using jcmd, attach and etc. + * + * @return list of strings which contain invalid values for option + */ + @Override + protected List getInvalidValues() { + List invalidValues = new ArrayList<>(); + + if (withRange) { + /* Return invalid values only for options which have defined range in VM */ + if ((is32Bit && min.compareTo(MIN_4_BYTE_INT) != 0) + || (!is32Bit && min.compareTo(MIN_LONG) != 0)) { + invalidValues.add(min.subtract(BigInteger.ONE).toString()); + } + + if (!unsigned + && ((is32Bit && (max.compareTo(MAX_4_BYTE_INT) != 0)) + || (!is32Bit && (max.compareTo(MAX_LONG) != 0)))) { + invalidValues.add(max.add(BigInteger.ONE).toString()); + } + + if (unsigned + && ((is32Bit && (max.compareTo(MAX_4_BYTE_UNSIGNED_INT) != 0)) + || (!is32Bit && !uint64 && (max.compareTo(MAX_UNSIGNED_LONG) != 0)) + || (uint64 && (max.compareTo(MAX_UNSIGNED_LONG_64) != 0)))) { + invalidValues.add(max.add(BigInteger.ONE).toString()); + } + } + + return invalidValues; + } + + /** + * Return expected error message for option with value "value" when it used + * on command line with passed value + * + * @param value Option value + * @return expected error message + */ + @Override + protected String getErrorMessageCommandLine(String value) { + String errorMsg; + + if (withRange) { + /* Option have defined range in VM */ + if (unsigned && ((new BigInteger(value)).compareTo(BigInteger.ZERO) < 0)) { + /* + * Special case for unsigned options with lower range equal to 0. If + * passed value is negative then error will be caught earlier for + * such options. Thus use different error message. + */ + errorMsg = String.format("Improperly specified VM option '%s=%s'", name, value); + } else { + errorMsg = String.format("%s %s=%s is outside the allowed range [ %s ... %s ]", + type, name, value, min.toString(), max.toString()); + } + } else { + errorMsg = ""; + } + + return errorMsg; + } +} --- /dev/null 2015-06-16 16:00:51.952800020 +0300 +++ new/test/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOption.java 2015-06-16 18:36:45.501575626 +0300 @@ -0,0 +1,474 @@ +/* + * Copyright (c) 2015, 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 optionsvalidation; + +import com.sun.tools.attach.VirtualMachine; +import com.sun.tools.attach.AttachOperationFailedException; +import java.util.ArrayList; +import java.util.List; +import jdk.test.lib.DynamicVMOption; +import jdk.test.lib.OutputAnalyzer; +import jdk.test.lib.ProcessTools; +import jdk.test.lib.dcmd.CommandExecutor; +import jdk.test.lib.dcmd.JMXExecutor; +import sun.tools.attach.HotSpotVirtualMachine; + +import static optionsvalidation.JVMOptionsUtils.failedMessage; +import static optionsvalidation.JVMOptionsUtils.printOutputContent; + +public abstract class JVMOption { + + /** + * Executor for JCMD + */ + private final static CommandExecutor executor = new JMXExecutor(); + + /** + * Name of the tested parameter + */ + protected String name; + + /** + * Range is defined for option inside VM + */ + protected boolean withRange; + + /** + * Prepend string which added before testing option to the command line + */ + private final List prepend; + private final StringBuilder prependString; + + protected JVMOption() { + this.prepend = new ArrayList<>(); + prependString = new StringBuilder(); + withRange = false; + } + + /** + * Create JVM Option with given type and name. + * + * @param type type: "intx", "size_t", "uintx", "uint64_t" or "double" + * @param name name of the option + * @return created JVMOption + */ + static JVMOption createVMOption(String type, String name) { + JVMOption parameter; + + switch (type) { + case "int": + case "intx": + case "size_t": + case "uint": + case "uintx": + case "uint64_t": + parameter = new IntJVMOption(name, type); + break; + case "double": + parameter = new DoubleJVMOption(name); + break; + default: + throw new Error("Expected only \"int\", \"intx\", \"size_t\", " + + "\"uint\", \"uintx\", \"uint64_t\", or \"double\" " + + "option types! Got " + type + " type!"); + } + + return parameter; + } + + /** + * Add passed options to the prepend options of the option. Prepend options + * will be added before testing option to the command line. + * + * @param options array of prepend options + */ + public final void addPrepend(String... options) { + String toAdd; + + for (String option : options) { + if (option.startsWith("-")) { + toAdd = option; + } else { + /* Add "-" before parameter name */ + toAdd = "-" + option; + + } + prepend.add(toAdd); + prependString.append(toAdd).append(" "); + } + } + + /** + * Get name of the option + * + * @return name of the option + */ + final String getName() { + return name; + } + + /** + * Mark this option as option which range is defined inside VM + */ + final void optionWithRange() { + withRange = true; + } + + /** + * Set new minimum option value + * + * @param min new minimum value + */ + abstract void setMin(String min); + + /** + * Get string with minimum value of the option + * + * @return string with minimum value of the option + */ + abstract String getMin(); + + /** + * Set new maximum option value + * + * @param max new maximum value + */ + abstract void setMax(String min); + + /** + * Get string with maximum value of the option + * + * @return string with maximum value of the option + */ + abstract String getMax(); + + /** + * Return list of strings with valid option values which used for testing + * using jcmd, attach and etc. + * + * @return list of strings which contain valid values for option + */ + protected abstract List getValidValues(); + + /** + * Return list of strings with invalid option values which used for testing + * using jcmd, attach and etc. + * + * @return list of strings which contain invalid values for option + */ + protected abstract List getInvalidValues(); + + /** + * Return expected error message for option with value "value" when it used + * on command line with passed value + * + * @param value option value + * @return expected error message + */ + protected abstract String getErrorMessageCommandLine(String value); + + /** + * Testing writeable option using DynamicVMOption isValidValue and + * isInvalidValue methods + * + * @return number of failed tests + */ + public int testDynamic() { + DynamicVMOption option = new DynamicVMOption(name); + int failedTests = 0; + String origValue; + + if (option.isWriteable()) { + + System.out.println("Testing " + name + " option dynamically by DynamicVMOption"); + + origValue = option.getValue(); + + for (String value : getValidValues()) { + if (!option.isValidValue(value)) { + failedMessage(String.format("Option %s: Valid value \"%s\" is invalid", name, value)); + failedTests++; + } + } + + for (String value : getInvalidValues()) { + if (option.isValidValue(value)) { + failedMessage(String.format("Option %s: Invalid value \"%s\" is valid", name, value)); + failedTests++; + } + } + + option.setValue(origValue); + } + + return failedTests; + } + + /** + * Testing writeable option using Jcmd + * + * @return number of failed tests + */ + public int testJcmd() { + DynamicVMOption option = new DynamicVMOption(name); + int failedTests = 0; + OutputAnalyzer out; + String origValue; + + if (option.isWriteable()) { + + System.out.println("Testing " + name + " option dynamically by jcmd"); + + origValue = option.getValue(); + + for (String value : getValidValues()) { + out = executor.execute(String.format("VM.set_flag %s %s", name, value), true); + + if (out.getOutput().contains(name + " error")) { + failedMessage(String.format("Option %s: Can not change " + + "option to valid value \"%s\" via jcmd", name, value)); + printOutputContent(out); + failedTests++; + } + } + + for (String value : getInvalidValues()) { + out = executor.execute(String.format("VM.set_flag %s %s", name, value), true); + + if (!out.getOutput().contains(name + " error")) { + failedMessage(String.format("Option %s: Error not reported for " + + "option when it chagned to invalid value \"%s\" via jcmd", name, value)); + printOutputContent(out); + failedTests++; + } + } + + option.setValue(origValue); + } + + return failedTests; + } + + private boolean setFlagAttach(HotSpotVirtualMachine vm, String flagName, String flagValue) throws Exception { + boolean result; + + try { + vm.setFlag(flagName, flagValue); + result = true; + } catch (AttachOperationFailedException e) { + result = false; + } + + return result; + } + + /** + * Testing writeable option using attach method + * + * @return number of failed tests + * @throws Exception if an error occurred while attaching to the target JVM + */ + public int testAttach() throws Exception { + DynamicVMOption option = new DynamicVMOption(name); + int failedTests = 0; + String origValue; + + if (option.isWriteable()) { + + System.out.println("Testing " + name + " option dynamically via attach"); + + origValue = option.getValue(); + + HotSpotVirtualMachine vm = (HotSpotVirtualMachine) VirtualMachine.attach(String.valueOf(ProcessTools.getProcessId())); + + for (String value : getValidValues()) { + if (!setFlagAttach(vm, name, value)) { + failedMessage(String.format("Option %s: Can not change option to valid value \"%s\" via attach", name, value)); + failedTests++; + } + } + + for (String value : getInvalidValues()) { + if (setFlagAttach(vm, name, value)) { + failedMessage(String.format("Option %s: Option changed to invalid value \"%s\" via attach", name, value)); + failedTests++; + } + } + + vm.detach(); + + option.setValue(origValue); + } + + return failedTests; + } + + /** + * Run java with passed parameter and check the result depending on the + * 'valid' parameter + * + * @param param tested parameter passed to the JVM + * @param valid indicates whether the JVM should fail or not + * @return true - if test passed + * @throws Exception if java process can not be started + */ + private boolean runJavaWithParam(String optionValue, boolean valid) throws Exception { + int exitCode; + boolean result = true; + String value = optionValue.substring(optionValue.lastIndexOf("=") + 1); + String fullOptionString = prependString.toString() + optionValue; + List runJava = new ArrayList<>(); + OutputAnalyzer out; + + runJava.addAll(prepend); + runJava.add(optionValue); + runJava.add(JVMOptionsUtils.class.getName()); + + out = new OutputAnalyzer(ProcessTools.createJavaProcessBuilder(true, runJava.toArray(new String[0])).start()); + + exitCode = out.getExitValue(); + + if (out.getOutput().contains("A fatal error has been detected by the Java Runtime Environment")) { + /* Always consider "fatal error" in output as fail */ + failedMessage(name, fullOptionString, valid, "JVM output reports a fatal error. JVM exited with code " + exitCode + "!"); + printOutputContent(out); + result = false; + } else if (valid == true) { + if ((exitCode != 0) && (exitCode != 1)) { + failedMessage(name, fullOptionString, valid, "JVM exited with unexpected error code = " + exitCode); + printOutputContent(out); + result = false; + } else if ((exitCode == 1) && (out.getOutput().isEmpty() == true)) { + failedMessage(name, fullOptionString, valid, "JVM exited with error(exitcode == 1)" + + ", but with empty stdout and stderr. Description of error is needed!"); + result = false; + } else if (out.getOutput().contains("is outside the allowed range")) { + failedMessage(name, fullOptionString, valid, "JVM output contains \"is outside the allowed range\""); + printOutputContent(out); + result = false; + } + } else { + // valid == false + if (exitCode == 0) { + failedMessage(name, fullOptionString, valid, "JVM successfully exit"); + result = false; + } else if (exitCode != 1) { + failedMessage(name, fullOptionString, valid, "JVM exited with code " + + exitCode + " which not equal to 1"); + result = false; + } else if (!out.getOutput().contains(getErrorMessageCommandLine(value))) { + failedMessage(name, fullOptionString, valid, "JVM output does not contain " + + "expected output \"" + getErrorMessageCommandLine(value) + "\""); + printOutputContent(out); + result = false; + } + } + + System.out.println(""); + + return result; + } + + /** + * Construct option string with passed value + * + * @param value parameter value + * @return string containing option with passed value + */ + private String constructOption(String value) { + return "-XX:" + name + "=" + value; + } + + /** + * Return list of strings which contain options with valid values which can + * be used for testing on command line + * + * @return list of strings which contain options with valid values + */ + private List getValidCommandLineOptions() { + List validParameters = new ArrayList<>(); + + for (String value : getValidValues()) { + validParameters.add(constructOption(value)); + } + + return validParameters; + } + + /** + * Return list of strings which contain options with invalid values which + * can be used for testing on command line + * + * @return list of strings which contain options with invalid values + */ + private List getInvalidCommandLineOptions() { + List invalidParameters = new ArrayList<>(); + + for (String value : getInvalidValues()) { + invalidParameters.add(constructOption(value)); + } + + return invalidParameters; + } + + /** + * Perform test of the parameter. Call java with valid option values and + * with invalid option values. + * + * @return number of failed tests + * @throws Exception if java process can not be started + */ + public int testCommandLine() throws Exception { + ProcessBuilder pb; + int failed = 0; + List optionValuesList; + + optionValuesList = getValidCommandLineOptions(); + + if (optionValuesList.isEmpty() != true) { + System.out.println("Testing valid " + name + " values."); + for (String optionValid : optionValuesList) { + if (runJavaWithParam(optionValid, true) == false) { + failed++; + } + } + } + + optionValuesList = getInvalidCommandLineOptions(); + + if (optionValuesList.isEmpty() != true) { + System.out.println("Testing invalid " + name + " values."); + + for (String optionInvalid : optionValuesList) { + if (runJavaWithParam(optionInvalid, false) == false) { + failed++; + } + } + } + + /* return number of failed tests for this option */ + return failed; + } + +} --- /dev/null 2015-06-16 16:00:51.952800020 +0300 +++ new/test/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOptionsUtils.java 2015-06-16 18:36:45.925575627 +0300 @@ -0,0 +1,428 @@ +/* + * Copyright (c) 2015, 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 optionsvalidation; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.HashMap; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.function.Predicate; +import jdk.test.lib.OutputAnalyzer; +import jdk.test.lib.ProcessTools; + +public class JVMOptionsUtils { + + /* Java option which print options with ranges */ + private static final String PRINT_FLAGS_RANGES = "-XX:+PrintFlagsRanges"; + + /* StringBuilder to accumulate failed message */ + private static final StringBuilder finalFailedMessage = new StringBuilder(); + + /** + * Add dependency for option depending on it's name. E.g. enable G1 GC for + * G1 options or add prepend options to not hit constraints. + * + * @param option option + */ + private static void addNameDependency(JVMOption option) { + String name = option.getName(); + + if (name.startsWith("G1")) { + option.addPrepend("-XX:+UseG1GC"); + } + + if (name.startsWith("CMS")) { + option.addPrepend("-XX:+UseConcMarkSweepGC"); + } + + switch (name) { + case "MinHeapFreeRatio": + option.addPrepend("-XX:MaxHeapFreeRatio=100"); + break; + case "MaxHeapFreeRatio": + option.addPrepend("-XX:MinHeapFreeRatio=0"); + break; + case "MinMetaspaceFreeRatio": + option.addPrepend("-XX:MaxMetaspaceFreeRatio=100"); + break; + case "MaxMetaspaceFreeRatio": + option.addPrepend("-XX:MinMetaspaceFreeRatio=0"); + break; + case "CMSOldPLABMin": + option.addPrepend("-XX:CMSOldPLABMax=" + option.getMax()); + break; + case "CMSOldPLABMax": + option.addPrepend("-XX:CMSOldPLABMin=" + option.getMin()); + break; + case "CMSPrecleanNumerator": + option.addPrepend("-XX:CMSPrecleanDenominator=" + option.getMax()); + break; + case "CMSPrecleanDenominator": + option.addPrepend("-XX:CMSPrecleanNumerator=" + ((new Integer(option.getMin())) - 1)); + break; + case "InitialTenuringThreshold": + option.addPrepend("-XX:MaxTenuringThreshold=" + option.getMax()); + break; + default: + /* Do nothing */ + break; + } + + } + + /** + * Add dependency for option depending on it's type. E.g. run the JVM in + * compilation mode for compiler options. + * + * @param option option + * @param type type of the option + */ + private static void addTypeDependency(JVMOption option, String type) { + if (type.contains("C1") || type.contains("C2")) { + /* Run in compiler mode for compiler flags */ + option.addPrepend("-Xcomp"); + } + } + + /** + * Parse JVM Options. Get input from "inputReader". Parse using + * "-XX:+PrintFlagsRanges" output format. + * + * @param inputReader input data for parsing + * @param withRanges true if needed options with defined ranges inside JVM + * @param acceptOrigin predicate for option origins. Origins can be + * "product", "diagnostic" etc. Accept option only if acceptOrigin evaluates + * to true. + * @return map from option name to the JVMOption object + * @throws IOException if an error occurred while reading the data + */ + private static Map getJVMOptions(Reader inputReader, + boolean withRanges, Predicate acceptOrigin) throws IOException { + BufferedReader reader = new BufferedReader(inputReader); + String type; + String line; + String token; + String name; + StringTokenizer st; + JVMOption option; + Map allOptions = new HashMap<>(); + + // Skip first line + line = reader.readLine(); + + while ((line = reader.readLine()) != null) { + /* + * Parse option from following line: + * [ ... ] {} + */ + st = new StringTokenizer(line); + + type = st.nextToken(); + + name = st.nextToken(); + + option = JVMOption.createVMOption(type, name); + + /* Skip '[' */ + token = st.nextToken(); + + /* Read min range or "..." if range is absent */ + token = st.nextToken(); + + if (token.equals("...") == false) { + if (!withRanges) { + /* + * Option have range, but asked for options without + * ranges => skip it + */ + continue; + } + + /* Mark this option as option which range is defined in VM */ + option.optionWithRange(); + + option.setMin(token); + + /* Read "..." and skip it */ + token = st.nextToken(); + + /* Get max value */ + token = st.nextToken(); + option.setMax(token); + } else if (withRanges) { + /* + * Option not have range, but asked for options with + * ranges => skip it + */ + continue; + } + + /* Skip ']' */ + token = st.nextToken(); + + /* Read origin of the option */ + token = st.nextToken(); + + while (st.hasMoreTokens()) { + token += st.nextToken(); + }; + token = token.substring(1, token.indexOf("}")); + + if (acceptOrigin.test(token)) { + addTypeDependency(option, token); + addNameDependency(option); + + allOptions.put(name, option); + } + } + + return allOptions; + } + + static void failedMessage(String optionName, String value, boolean valid, String message) { + String temp; + + if (valid) { + temp = "valid"; + } else { + temp = "invalid"; + } + + failedMessage(String.format("Error processing option %s with %s value '%s'! %s", + optionName, temp, value, message)); + } + + static void failedMessage(String message) { + System.err.println("TEST FAILED: " + message); + finalFailedMessage.append(String.format("(%s)%n", message)); + } + + static void printOutputContent(OutputAnalyzer output) { + System.err.println(String.format("stdout content[%s]", output.getStdout())); + System.err.println(String.format("stderr content[%s]%n", output.getStderr())); + } + + /** + * Return string with accumulated failure messages + * + * @return string with accumulated failure messages + */ + public static String getMessageWithFailures() { + return finalFailedMessage.toString(); + } + + /** + * Run command line tests for options passed in the list + * + * @param options list of options to test + * @return number of failed tests + * @throws Exception if java process can not be started + */ + public static int runCommandLineTests(List options) throws Exception { + int failed = 0; + + for (JVMOption option : options) { + failed += option.testCommandLine(); + } + + return failed; + } + + /** + * Test passed options using DynamicVMOption isValidValue and isInvalidValue + * methods. Only tests writeable options. + * + * @param options list of options to test + * @return number of failed tests + */ + public static int runDynamicTests(List options) { + int failed = 0; + + for (JVMOption option : options) { + failed += option.testDynamic(); + } + + return failed; + } + + /** + * Test passed options using Jcmd. Only tests writeable options. + * + * @param options list of options to test + * @return number of failed tests + */ + public static int runJcmdTests(List options) { + int failed = 0; + + for (JVMOption option : options) { + failed += option.testJcmd(); + } + + return failed; + } + + /** + * Test passed option using attach method. Only tests writeable options. + * + * @param options list of options to test + * @return number of failed tests + * @throws Exception if an error occurred while attaching to the target JVM + */ + public static int runAttachTests(List options) throws Exception { + int failed = 0; + + for (JVMOption option : options) { + failed += option.testAttach(); + } + + return failed; + } + + /** + * Get JVM options as map. Can return options with defined ranges or options + * without range depending on "withRanges" argument. "acceptOrigin" + * predicate can be used to filter option origin. + * + * @param withRanges true if needed options with defined ranges inside JVM + * @param acceptOrigin predicate for option origins. Origins can be + * "product", "diagnostic" etc. Accept option only if acceptOrigin evaluates + * to true. + * @param additionalArgs additional arguments to the Java process which ran + * with "-XX:+PrintFlagsRanges" + * @return map from option name to the JVMOption object + * @throws Exception if a new process can not be created or an error + * occurred while reading the data + */ + public static Map getOptionsAsMap(boolean withRanges, Predicate acceptOrigin, + String... additionalArgs) throws Exception { + Map result; + Process p; + List runJava = new ArrayList<>(); + + if (additionalArgs.length > 0) { + runJava.addAll(Arrays.asList(additionalArgs)); + } + runJava.add(PRINT_FLAGS_RANGES); + runJava.add("-version"); + + p = ProcessTools.createJavaProcessBuilder(true, runJava.toArray(new String[0])).start(); + + result = getJVMOptions(new InputStreamReader(p.getInputStream()), withRanges, acceptOrigin); + + p.waitFor(); + + return result; + } + + /** + * Get JVM options as list. Can return options with defined ranges or + * options without range depending on "withRanges" argument. "acceptOrigin" + * predicate can be used to filter option origin. + * + * @param withRanges true if needed options with defined ranges inside JVM + * @param acceptOrigin predicate for option origins. Origins can be + * "product", "diagnostic" etc. Accept option only if acceptOrigin evaluates + * to true. + * @param additionalArgs additional arguments to the Java process which ran + * with "-XX:+PrintFlagsRanges" + * @return List of options + * @throws Exception if a new process can not be created or an error + * occurred while reading the data + */ + public static List getOptions(boolean withRanges, Predicate acceptOrigin, + String... additionalArgs) throws Exception { + return new ArrayList<>(getOptionsAsMap(withRanges, acceptOrigin, additionalArgs).values()); + } + + /** + * Get JVM options with ranges as list. "acceptOrigin" predicate can be used + * to filter option origin. + * + * @param acceptOrigin predicate for option origins. Origins can be + * "product", "diagnostic" etc. Accept option only if acceptOrigin evaluates + * to true. + * @param additionalArgs additional arguments to the Java process which ran + * with "-XX:+PrintFlagsRanges" + * @return List of options + * @throws Exception if a new process can not be created or an error + * occurred while reading the data + */ + public static List getOptionsWithRange(Predicate acceptOrigin, String... additionalArgs) throws Exception { + return getOptions(true, acceptOrigin, additionalArgs); + } + + /** + * Get JVM options with ranges as list. + * + * @param additionalArgs additional arguments to the Java process which ran + * with "-XX:+PrintFlagsRanges" + * @return list of options + * @throws Exception if a new process can not be created or an error + * occurred while reading the data + */ + public static List getOptionsWithRange(String... additionalArgs) throws Exception { + return getOptionsWithRange(origin -> true, additionalArgs); + } + + /** + * Get JVM options with range as map. "acceptOrigin" predicate can be used + * to filter option origin. + * + * @param acceptOrigin predicate for option origins. Origins can be + * "product", "diagnostic" etc. Accept option only if acceptOrigin evaluates + * to true. + * @param additionalArgs additional arguments to the Java process which ran + * with "-XX:+PrintFlagsRanges" + * @return Map from option name to the JVMOption object + * @throws Exception if a new process can not be created or an error + * occurred while reading the data + */ + public static Map getOptionsWithRangeAsMap(Predicate acceptOrigin, String... additionalArgs) throws Exception { + return getOptionsAsMap(true, acceptOrigin, additionalArgs); + } + + /** + * Get JVM options with range as map + * + * @param additionalArgs additional arguments to the Java process which ran + * with "-XX:+PrintFlagsRanges" + * @return map from option name to the JVMOption object + * @throws Exception if a new process can not be created or an error + * occurred while reading the data + */ + public static Map getOptionsWithRangeAsMap(String... additionalArgs) throws Exception { + return getOptionsWithRangeAsMap(origin -> true, additionalArgs); + } + + /* Simple method to test that java start-up. Used for testing options. */ + public static void main(String[] args) { + System.out.print("Java start-up!"); + } +}