Path: blob/master/test/hotspot/jtreg/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOption.java
41161 views
/*1* Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/22package optionsvalidation;2324import com.sun.tools.attach.VirtualMachine;25import com.sun.tools.attach.AttachOperationFailedException;26import java.util.ArrayList;27import java.util.Arrays;28import java.util.HashSet;29import java.util.List;30import java.util.Set;31import jdk.test.lib.management.DynamicVMOption;32import jdk.test.lib.process.OutputAnalyzer;33import jdk.test.lib.process.ProcessTools;34import jdk.test.lib.dcmd.CommandExecutor;35import jdk.test.lib.dcmd.JMXExecutor;36import jdk.test.lib.Platform;37import sun.tools.attach.HotSpotVirtualMachine;3839import static optionsvalidation.JVMOptionsUtils.failedMessage;40import static optionsvalidation.JVMOptionsUtils.GCType;41import static optionsvalidation.JVMOptionsUtils.printOutputContent;42import static optionsvalidation.JVMOptionsUtils.VMType;4344public abstract class JVMOption {4546private static final String UNLOCK_FLAG1 = "-XX:+UnlockDiagnosticVMOptions";47private static final String UNLOCK_FLAG2 = "-XX:+UnlockExperimentalVMOptions";4849/**50* Executor for JCMD51*/52private final static CommandExecutor executor = new JMXExecutor();5354/**55* Name of the tested parameter56*/57protected String name;5859/**60* Range is defined for option inside VM61*/62protected boolean withRange;6364/**65* Test valid min range value and additional small values66*/67protected boolean testMinRange;6869/**70* Test valid max range value and additional big values71*/72protected boolean testMaxRange;7374private Set<Integer> allowedExitCodes;7576/**77* Prepend string which added before testing option to the command line78*/79private final List<String> prepend;80private final StringBuilder prependString;8182protected JVMOption() {83this.prepend = new ArrayList<>();84prependString = new StringBuilder();85allowedExitCodes = new HashSet<>();86allowedExitCodes.add(0);87allowedExitCodes.add(1);88withRange = false;89testMinRange = true;90testMaxRange = true;91}9293/**94* Create JVM Option with given type and name.95*96* @param type type: "intx", "size_t", "uintx", "uint64_t" or "double"97* @param name name of the option98* @return created JVMOption99*/100static JVMOption createVMOption(String type, String name) {101JVMOption parameter;102103switch (type) {104case "int":105case "intx":106case "size_t":107case "uint":108case "uintx":109case "uint64_t":110parameter = new IntJVMOption(name, type);111break;112case "double":113parameter = new DoubleJVMOption(name);114break;115default:116throw new Error("Expected only \"int\", \"intx\", \"size_t\", "117+ "\"uint\", \"uintx\", \"uint64_t\", or \"double\" "118+ "option types! Got " + type + " type!");119}120121return parameter;122}123124/**125* Add passed options to the prepend options of the option. Prepend options126* will be added before testing option to the command line.127*128* @param options array of prepend options129*/130public final void addPrepend(String... options) {131String toAdd;132133for (String option : options) {134if (option.startsWith("-")) {135toAdd = option;136} else {137/* Add "-" before parameter name */138toAdd = "-" + option;139140}141prepend.add(toAdd);142prependString.append(toAdd).append(" ");143}144}145146/**147* Get name of the option148*149* @return name of the option150*/151public final String getName() {152return name;153}154155/**156* Mark this option as option which range is defined inside VM157*/158final void optionWithRange() {159withRange = true;160}161162/**163* Exclude testing of min range value for this option164*/165public final void excludeTestMinRange() {166testMinRange = false;167}168169/**170* Exclude testing of max range value for this option171*/172public final void excludeTestMaxRange() {173testMaxRange = false;174}175176public final void setAllowedExitCodes(Integer... allowedExitCodes) {177this.allowedExitCodes.addAll(Arrays.asList(allowedExitCodes));178}179180/**181* Set new minimum option value182*183* @param min new minimum value184*/185abstract void setMin(String min);186187/**188* Get string with minimum value of the option189*190* @return string with minimum value of the option191*/192abstract String getMin();193194/**195* Set new maximum option value196*197* @param max new maximum value198*/199abstract void setMax(String min);200201/**202* Get string with maximum value of the option203*204* @return string with maximum value of the option205*/206abstract String getMax();207208/**209* Return list of strings with valid option values which used for testing210* using jcmd, attach and etc.211*212* @return list of strings which contain valid values for option213*/214protected abstract List<String> getValidValues();215216/**217* Return list of strings with invalid option values which used for testing218* using jcmd, attach and etc.219*220* @return list of strings which contain invalid values for option221*/222protected abstract List<String> getInvalidValues();223224/**225* Return expected error message for option with value "value" when it used226* on command line with passed value227*228* @param value option value229* @return expected error message230*/231protected abstract String getErrorMessageCommandLine(String value);232233/**234* Testing writeable option using DynamicVMOption isValidValue and235* isInvalidValue methods236*237* @return number of failed tests238*/239public int testDynamic() {240DynamicVMOption option = new DynamicVMOption(name);241int failedTests = 0;242String origValue;243244if (option.isWriteable()) {245246System.out.println("Testing " + name + " option dynamically by DynamicVMOption");247248origValue = option.getValue();249250for (String value : getValidValues()) {251if (!option.isValidValue(value)) {252failedMessage(String.format("Option %s: Valid value \"%s\" is invalid", name, value));253failedTests++;254}255}256257for (String value : getInvalidValues()) {258if (option.isValidValue(value)) {259failedMessage(String.format("Option %s: Invalid value \"%s\" is valid", name, value));260failedTests++;261}262}263264option.setValue(origValue);265}266267return failedTests;268}269270/**271* Testing writeable option using Jcmd272*273* @return number of failed tests274*/275public int testJcmd() {276DynamicVMOption option = new DynamicVMOption(name);277int failedTests = 0;278OutputAnalyzer out;279String origValue;280281if (option.isWriteable()) {282283System.out.println("Testing " + name + " option dynamically by jcmd");284285origValue = option.getValue();286287for (String value : getValidValues()) {288out = executor.execute(String.format("VM.set_flag %s %s", name, value), true);289290if (out.getOutput().contains(name + " error")) {291failedMessage(String.format("Option %s: Can not change "292+ "option to valid value \"%s\" via jcmd", name, value));293printOutputContent(out);294failedTests++;295}296}297298for (String value : getInvalidValues()) {299out = executor.execute(String.format("VM.set_flag %s %s", name, value), true);300301if (!out.getOutput().contains(name + " error")) {302failedMessage(String.format("Option %s: Error not reported for "303+ "option when it chagned to invalid value \"%s\" via jcmd", name, value));304printOutputContent(out);305failedTests++;306}307}308309option.setValue(origValue);310}311312return failedTests;313}314315private boolean setFlagAttach(HotSpotVirtualMachine vm, String flagName, String flagValue) throws Exception {316boolean result;317318try {319vm.setFlag(flagName, flagValue);320result = true;321} catch (AttachOperationFailedException e) {322result = false;323}324325return result;326}327328/**329* Testing writeable option using attach method330*331* @return number of failed tests332* @throws Exception if an error occurred while attaching to the target JVM333*/334public int testAttach() throws Exception {335DynamicVMOption option = new DynamicVMOption(name);336int failedTests = 0;337String origValue;338339if (option.isWriteable()) {340341System.out.println("Testing " + name + " option dynamically via attach");342343origValue = option.getValue();344345HotSpotVirtualMachine vm = (HotSpotVirtualMachine) VirtualMachine.attach(String.valueOf(ProcessTools.getProcessId()));346347for (String value : getValidValues()) {348if (!setFlagAttach(vm, name, value)) {349failedMessage(String.format("Option %s: Can not change option to valid value \"%s\" via attach", name, value));350failedTests++;351}352}353354for (String value : getInvalidValues()) {355if (setFlagAttach(vm, name, value)) {356failedMessage(String.format("Option %s: Option changed to invalid value \"%s\" via attach", name, value));357failedTests++;358}359}360361vm.detach();362363option.setValue(origValue);364}365366return failedTests;367}368369/**370* Run java with passed parameter and check the result depending on the371* 'valid' parameter372*373* @param param tested parameter passed to the JVM374* @param valid indicates whether the JVM should fail or not375* @return true - if test passed376* @throws Exception if java process can not be started377*/378private boolean runJavaWithParam(String optionValue, boolean valid) throws Exception {379int exitCode = 0;380boolean result = true;381String errorMessage = null;382String explicitGC = null;383List<String> runJava = new ArrayList<>();384OutputAnalyzer out = null;385386if (VMType != null) {387runJava.add(VMType);388}389390// Run with a small heap to avoid excessive execution time391long max = Runtime.getRuntime().maxMemory() / 1024 / 1024;392if (max > 1024) {393runJava.add("-Xmx1024m");394}395396if (Platform.isDebugBuild()) {397// Avoid excessive execution time.398runJava.add("-XX:-ZapUnusedHeapArea");399}400401if (GCType != null &&402!(prepend.contains("-XX:+UseSerialGC") ||403prepend.contains("-XX:+UseParallelGC") ||404prepend.contains("-XX:+UseG1GC"))) {405explicitGC = GCType;406}407408if (explicitGC != null) {409runJava.add(explicitGC);410}411412runJava.add(UNLOCK_FLAG1);413runJava.add(UNLOCK_FLAG2);414415runJava.addAll(prepend);416runJava.add(optionValue);417runJava.add(JVMStartup.class.getName());418419out = new OutputAnalyzer(ProcessTools.createJavaProcessBuilder(runJava).start());420421exitCode = out.getExitValue();422String exitCodeString = null;423if (exitCode != 0) {424exitCodeString = exitCode + " [0x" + Integer.toHexString(exitCode).toUpperCase() + "]";425}426427if (out.getOutput().contains("A fatal error has been detected by the Java Runtime Environment")) {428/* Always consider "fatal error" in output as fail */429errorMessage = "JVM output reports a fatal error. JVM exited with code " + exitCodeString + "!";430} else if (out.getStderr().contains("Ignoring option " + name)) {431// Watch for newly obsoleted, but not yet removed, flags432System.out.println("SKIPPED: Ignoring test result for obsolete flag " + name);433} else if (valid == true) {434if (!allowedExitCodes.contains(exitCode)) {435errorMessage = "JVM exited with unexpected error code = " + exitCodeString;436} else if ((exitCode != 0) && (out.getOutput().isEmpty() == true)) {437errorMessage = "JVM exited with error(exitcode == " + exitCodeString + "), but with empty stdout and stderr. " +438"Description of error is needed!";439} else if (out.getOutput().contains("is outside the allowed range")) {440errorMessage = "JVM output contains \"is outside the allowed range\"";441}442} else {443// valid == false444String value = optionValue.substring(optionValue.lastIndexOf("=") + 1);445String errorMessageCommandLineValue = getErrorMessageCommandLine(value);446if (exitCode == 0) {447errorMessage = "JVM successfully exit";448} else if (exitCode != 1) {449errorMessage = "JVM exited with code " + exitCodeString + " which does not equal to 1";450} else if (!out.getOutput().contains(errorMessageCommandLineValue)) {451errorMessage = "JVM output does not contain expected output \"" + errorMessageCommandLineValue + "\"";452}453}454455if (errorMessage != null) {456String fullOptionString = String.format("%s %s %s %s",457VMType == null ? "" : VMType, explicitGC == null ? "" : explicitGC, prependString.toString(), optionValue).trim().replaceAll(" +", " ");458failedMessage(name, fullOptionString, valid, errorMessage);459printOutputContent(out);460result = false;461}462463System.out.println("");464465return result;466}467468/**469* Construct option string with passed value470*471* @param value parameter value472* @return string containing option with passed value473*/474private String constructOption(String value) {475return "-XX:" + name + "=" + value;476}477478/**479* Return list of strings which contain options with valid values which can480* be used for testing on command line481*482* @return list of strings which contain options with valid values483*/484private List<String> getValidCommandLineOptions() {485List<String> validParameters = new ArrayList<>();486487for (String value : getValidValues()) {488validParameters.add(constructOption(value));489}490491return validParameters;492}493494/**495* Return list of strings which contain options with invalid values which496* can be used for testing on command line497*498* @return list of strings which contain options with invalid values499*/500private List<String> getInvalidCommandLineOptions() {501List<String> invalidParameters = new ArrayList<>();502503for (String value : getInvalidValues()) {504invalidParameters.add(constructOption(value));505}506507return invalidParameters;508}509510/**511* Perform test of the parameter. Call java with valid option values and512* with invalid option values.513*514* @return number of failed tests515* @throws Exception if java process can not be started516*/517public int testCommandLine() throws Exception {518ProcessBuilder pb;519int failed = 0;520List<String> optionValuesList;521522optionValuesList = getValidCommandLineOptions();523524if (optionValuesList.isEmpty() != true) {525System.out.println("Testing valid " + name + " values.");526for (String optionValid : optionValuesList) {527if (runJavaWithParam(optionValid, true) == false) {528failed++;529}530}531}532533optionValuesList = getInvalidCommandLineOptions();534535if (optionValuesList.isEmpty() != true) {536System.out.println("Testing invalid " + name + " values.");537538for (String optionInvalid : optionValuesList) {539if (runJavaWithParam(optionInvalid, false) == false) {540failed++;541}542}543}544545/* return number of failed tests for this option */546return failed;547}548549}550551552