--- old/test/TEST.groups 2015-05-18 18:30:08.053418398 +0300 +++ new/test/TEST.groups 2015-05-18 18:30:07.905418391 +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-05-18 12:17:27.059041439 +0300 +++ new/test/runtime/CommandLine/OptionsValidation/TestJcmdOutput.java 2015-05-18 18:30:08.301418409 +0300 @@ -0,0 +1,84 @@ +/* + * 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 not allowd by constraint. Also check that + * jcmd not print error message to the target process output. + * @library /testlibrary + * @run main TestJcmdOutput + * @author dmitry.dmitriev@oracle.com + */ + +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 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-05-18 12:17:27.059041439 +0300 +++ new/test/runtime/CommandLine/OptionsValidation/TestOptionsWithRanges.java 2015-05-18 18:30:08.625418424 +0300 @@ -0,0 +1,61 @@ +/* + * 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 + * @run main/othervm/timeout=1200 TestOptionsWithRanges + * @author dmitry.dmitriev@oracle.com + */ +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, becayse 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-05-18 12:17:27.059041439 +0300 +++ new/test/runtime/CommandLine/OptionsValidation/TestOptionsWithRangesDynamic.java 2015-05-18 18:30:09.037418444 +0300 @@ -0,0 +1,59 @@ +/* + * 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 + * @run main/othervm -XX:MinHeapFreeRatio=0 -XX:MaxHeapFreeRatio=100 TestOptionsWithRangesDynamic + * @author dmitry.dmitriev@oracle.com + */ + +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("Parsed " + 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-05-18 12:17:27.059041439 +0300 +++ new/test/runtime/CommandLine/OptionsValidation/common/optionsvalidation/DoubleJVMOption.java 2015-05-18 18:30:09.497418465 +0300 @@ -0,0 +1,221 @@ +/* + * 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 { + + private final double MAX_DOUBLE = 18446744073709551616.000; + + /** + * Mininum option value + */ + private double min; + /** + * Maximum option value + */ + private double max; + + /** + * Default constructor. + */ + private DoubleJVMOption() { + min = Double.MIN_VALUE; + max = MAX_DOUBLE; + } + + /** + * Initialize double option with passed name + * + * @param name Name of the option + */ + DoubleJVMOption(String name) { + this(); + this.name = name; + } + + /** + * 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; + } + + /** + * 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; + } + + /** + * 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 String.format("%f", 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 String.format("%f", max); + } + + /** + * 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(String.format("%f", min)); + validValues.add(String.format("%f", max)); + + if (min == Double.MIN_VALUE && max == MAX_DOUBLE) { + validValues.add(String.format("%f", -1.5)); + validValues.add(String.format("%f", 0.0)); + validValues.add(String.format("%f", 0.85)); + } + + 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 (min != Double.MIN_VALUE) { + invalidValues.add(String.format("%f", min - 0.01)); + } + + if (max != MAX_DOUBLE) { + invalidValues.add(String.format("%f", max + 0.01)); + } + + return invalidValues; + } + + /** + * Return list of strings with parameter with valid values. Valid values + * are: min, max Following values are added to valid if min and max not + * defined: -1, 0, 1 Following value is added if min is equal to 0 and max + * is greater than 0: 1 + * + * @return List of String with parameters with valid values. + */ + @Override + protected List getValidCommandLineOptions() { + List validParameters = new ArrayList<>(); + + for (String value : getValidValues()) { + validParameters.add(constructOption(value)); + } + + return validParameters; + } + + /** + * Return list of strings with parameter with invalid values. Invalid values + * are(if min, max are defined): min - 1, max + 1 + * + * @return List of String with parameters with invalid values. + */ + @Override + protected List getInvalidCommandLineOptions() { + List invalidParameters = new ArrayList<>(); + + for (String value : getInvalidValues()) { + invalidParameters.add(constructOption(value)); + } + + return invalidParameters; + } + + /** + * 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-05-18 12:17:27.059041439 +0300 +++ new/test/runtime/CommandLine/OptionsValidation/common/optionsvalidation/IntJVMOption.java 2015-05-18 18:30:09.865418482 +0300 @@ -0,0 +1,331 @@ +/* + * 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 { + + enum IntType { + + INTX, + UINTX, + UINT64_T + } + + 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 MAX_4_BYTE_INT_PLUS_ONE = new BigInteger("2147483648"); + 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; + + /** + * Is this value is signed or unsigned + */ + private boolean unsigned; + + /** + * Is this value is 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(new BigInteger("2")).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(new BigInteger("2")).add(BigInteger.ONE); + } + + MAX_UNSIGNED_LONG_64 = (new BigInteger(String.valueOf(Long.MAX_VALUE))) + .multiply(new BigInteger("2")).add(BigInteger.ONE); + } + + /** + * Default constructor. Init min and max with minimum int and maximum int. + */ + private IntJVMOption() { + min = MIN_LONG; + max = MAX_LONG; + unsigned = false; + uint64 = false; + } + + /** + * 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 + * @param type + */ + IntJVMOption(String name, IntType type) { + this(); + this.name = name; + + switch (type) { + case UINT64_T: + uint64 = true; + case UINTX: + unsigned = true; + break; + } + + if (unsigned) { + min = BigInteger.ZERO; + max = MAX_UNSIGNED_LONG; + } + + if (uint64) { + max = MAX_UNSIGNED_LONG_64; + } + } + + /** + * 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); + } + + /** + * 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; + } + + /** + * 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(MIN_LONG) == 0) && (max.compareTo(MAX_LONG) == 0)) { + validValues.add("-1"); + validValues.add("0"); + 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 (min.compareTo(MIN_LONG) != 0) { + invalidValues.add(min.subtract(BigInteger.ONE).toString()); + } + + if (!unsigned && (max.compareTo(MAX_LONG) != 0)) { + invalidValues.add(max.add(BigInteger.ONE).toString()); + } + + if (unsigned + && ((!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 list of strings with parameter with valid values. Valid values + * are: min, max Following values are added to valid if min and max not + * defined: -1, 0, 1 Following value is added if min is equal to 0 and max + * is greater than 0: 1 + * + * @return List of String with parameters with valid values. + */ + @Override + protected List getValidCommandLineOptions() { + List validParameters = new ArrayList<>(); + + for (String value : getValidValues()) { + validParameters.add(constructOption(value)); + } + + return validParameters; + } + + /** + * Return list of strings with parameter with invalid values. Invalid values + * are(if min, max are defined): min - 1, max + 1 + * + * @return List of String with parameters with invalid values. + */ + @Override + protected List getInvalidCommandLineOptions() { + List invalidParameters = new ArrayList<>(); + + for (String value : getInvalidValues()) { + invalidParameters.add(constructOption(value)); + } + + return invalidParameters; + } + + /** + * 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 catched 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 is outside the allowed range [ %s ... %s ]", + name, value, min.toString(), max.toString()); + } + } else { + errorMsg = ""; + } + + return errorMsg; + } +} --- /dev/null 2015-05-18 12:17:27.059041439 +0300 +++ new/test/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOption.java 2015-05-18 18:30:10.289418502 +0300 @@ -0,0 +1,445 @@ +/* + * 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; + } + + /** + * 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 which contain options with valid values which used + * for testing on command line + * + * @return List of strings which contain options with valid values + */ + abstract protected List getValidCommandLineOptions(); + + /** + * Return list of strings which contain options with invalid values which + * used for testing on command line + * + * @return List of strings which contain options with invalid values + */ + abstract protected List getInvalidCommandLineOptions(); + + /** + * 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 + */ + abstract protected 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 + */ + abstract protected 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 + */ + abstract protected String getErrorMessageCommandLine(String value); + + /** + * 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 "intx": + parameter = new IntJVMOption(name, IntJVMOption.IntType.INTX); + break; + case "size_t": + case "uintx": + parameter = new IntJVMOption(name, IntJVMOption.IntType.UINTX); + break; + case "uint64_t": + parameter = new IntJVMOption(name, IntJVMOption.IntType.UINT64_T); + break; + case "double": + parameter = new DoubleJVMOption(name); + break; + default: + throw new Error("Expected only \"intx\", \"size_t\", \"uintx\", \"uint64_t\"," + + " or \"boolean\" option types! Got " + type + " type!"); + } + + return parameter; + } + + /** + * 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() throws Exception { + 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 final 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 + */ + 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 java + * @param valid Indicates, should JVM failed or not + * @return true - if test passed + * @throws Exception Java process can not be started + */ + private boolean runJavaWithParam(String optionValue, boolean valid) throws Exception { + int returnCode; + 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()); + + returnCode = 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 about fatal error. JVM exit with code " + returnCode + "!"); + printOutputContent(out); + result = false; + } else if (valid == true) { + if ((returnCode != 0) && (returnCode != 1)) { + failedMessage(name, fullOptionString, valid, "JVM exit with unexpected error code = " + returnCode); + printOutputContent(out); + result = false; + } else if (returnCode == 1 && out.getOutput().isEmpty()) { + failedMessage(name, fullOptionString, valid, "JVM exit 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 (returnCode == 0) { + failedMessage(name, fullOptionString, valid, "JVM successfully exit"); + result = false; + } else if (returnCode != 1) { + failedMessage(name, fullOptionString, valid, "JVM exit with code " + + returnCode + " which not equal to 1"); + result = false; + } else if (!out.getOutput().contains(getErrorMessageCommandLine(value))) { + failedMessage(name, fullOptionString, valid, "JVM output not contains " + + "expected output \"" + getErrorMessageCommandLine(value) + "\""); + printOutputContent(out); + result = false; + } + } + + System.out.println(""); + + return result; + } + + /** + * Perform test of the parameter. Call java with valid option values and + * with invalid option values. + * + * @return Number of failed tests + * @throws Exception 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-05-18 12:17:27.059041439 +0300 +++ new/test/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOptionsUtils.java 2015-05-18 18:30:10.601418516 +0300 @@ -0,0 +1,401 @@ +/* + * 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.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 + */ + static private 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. ran java in + * compilation mode for compiler options. + * + * @param option Option + * @param type Type of the option + */ + static private 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 + * @param acceptOrigin predicate for option origins. Origins can be + * "product", "diagnostic" etc. Accept option only if acceptOrigin return + * true. + * @return Map from option name to the JVMOption object + * @throws IOException Error occurred while reading the data + */ + static private Map getJVMOptions(Reader inputReader, + boolean withRanges, Predicate acceptOrigin) throws IOException { + BufferedReader reader = new BufferedReader(inputReader); + String type; + String line; + String token; + String name; + JVMOption option; + Map allOptions = new HashMap<>(); + + // Skip first line + line = reader.readLine(); + + while ((line = reader.readLine()) != null) { + + type = line.substring(0, 9).trim(); + + name = line.substring(10, 61).trim(); + + option = JVMOption.createVMOption(type, name); + + /* Get min value */ + token = line.substring(63, 89).trim(); + if (!token.isEmpty()) { + 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); + + /* Get max value */ + token = line.substring(94, 119).trim(); + option.setMax(token); + } else if (withRanges) { + /* + * Option not have range, but asked for options with + * ranges => skip it + */ + continue; + } + + token = line.substring(141).trim(); + 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 + */ + static public 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 Java process can not be started + */ + public final 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. Tested only writeable options. + * + * @param options List of options to test + * @return Number of failed tests + * @throws Exception + */ + public final static int runDynamicTests(List options) throws Exception { + int failed = 0; + + for (JVMOption option : options) { + failed += option.testDynamic(); + } + + return failed; + } + + /** + * Test passed options using Jcmd. Tested only writeable options. + * + * @param options List of options to test + * @return Number of failed tests + * @throws Exception + */ + public final static int runJcmdTests(List options) throws Exception { + int failed = 0; + + for (JVMOption option : options) { + failed += option.testJcmd(); + } + + return failed; + } + + /** + * Test passed option using attach method. Tested only writeable options. + * + * @param options List of options to test + * @return Number of failed tests + * @throws Exception + */ + public final 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 + * @param acceptOrigin predicate for option origins. Origins can be + * "product", "diagnostic" etc. Accept option only if acceptOrigin return + * 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 + */ + 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(withRanges, runJava.toArray(new String[0])).start(); + + result = getJVMOptions(new InputStreamReader(p.getInputStream()), true, 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 + * @param acceptOrigin predicate for option origins. Origins can be + * "product", "diagnostic" etc. Accept option only if acceptOrigin return + * true. + * @param additionalArgs Additional arguments to the Java process which ran + * with "-XX:+PrintFlagsRanges" + * @return List of options + * @throws Exception + */ + 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 return + * true. + * @param additionalArgs Additional arguments to the Java process which ran + * with "-XX:+PrintFlagsRanges" + * @return List of options + * @throws Exception + */ + 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 + */ + 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 return + * 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 + */ + 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 + */ + 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. */ + static public void main(String[] args) { + System.out.print("Java start-up!"); + } +}