Path: blob/master/test/hotspot/jtreg/runtime/CommandLine/OptionsValidation/common/optionsvalidation/JVMOptionsUtils.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*/2223package optionsvalidation;2425import java.io.BufferedReader;26import java.io.IOException;27import java.io.InputStreamReader;28import java.io.Reader;29import java.lang.management.GarbageCollectorMXBean;30import java.lang.management.ManagementFactory;31import java.math.BigDecimal;32import java.util.ArrayList;33import java.util.Arrays;34import java.util.List;35import java.util.LinkedHashMap;36import java.util.Map;37import java.util.StringTokenizer;38import java.util.function.Predicate;39import jdk.test.lib.process.OutputAnalyzer;40import jdk.test.lib.Platform;41import jdk.test.lib.process.ProcessTools;4243public class JVMOptionsUtils {4445/* Java option which print options with ranges */46private static final String PRINT_FLAGS_RANGES = "-XX:+PrintFlagsRanges";4748private static final String UNLOCK_FLAG1 = "-XX:+UnlockDiagnosticVMOptions";49private static final String UNLOCK_FLAG2 = "-XX:+UnlockExperimentalVMOptions";5051/* StringBuilder to accumulate failed message */52private static final StringBuilder finalFailedMessage = new StringBuilder();5354/* Used to start the JVM with the same type as current */55static String VMType;5657/* Used to start the JVM with the same GC type as current */58static String GCType;5960private static Map<String, JVMOption> optionsAsMap;6162static {63if (Platform.isServer()) {64VMType = "-server";65} else if (Platform.isClient()) {66VMType = "-client";67} else if (Platform.isMinimal()) {68VMType = "-minimal";69} else {70VMType = null;71}7273List<GarbageCollectorMXBean> gcMxBeans = ManagementFactory.getGarbageCollectorMXBeans();7475GCType = null;7677for (GarbageCollectorMXBean gcMxBean : gcMxBeans) {78switch (gcMxBean.getName()) {79case "MarkSweepCompact":80GCType = "-XX:+UseSerialGC";81break;82case "PS Scavenge":83GCType = "-XX:+UseParallelGC";84break;85case "G1 Old Generation":86GCType = "-XX:+UseG1GC";87break;88}89}90}9192public static boolean fitsRange(String optionName, BigDecimal number) throws Exception {93JVMOption option;94String minRangeString = null;95String maxRangeString = null;96boolean fits = true;9798if (optionsAsMap == null) {99optionsAsMap = getOptionsWithRangeAsMap();100}101102option = optionsAsMap.get(optionName);103if (option != null) {104minRangeString = option.getMin();105if (minRangeString != null) {106fits = (number.compareTo(new BigDecimal(minRangeString)) >= 0);107}108maxRangeString = option.getMax();109if (maxRangeString != null) {110fits &= (number.compareTo(new BigDecimal(maxRangeString)) <= 0);111}112}113114return fits;115}116117public static boolean fitsRange(String optionName, String number) throws Exception {118String lowerCase = number.toLowerCase();119String multiplier = "1";120if (lowerCase.endsWith("k")) {121multiplier = "1024";122lowerCase = lowerCase.substring(0, lowerCase.length()-1);123} else if (lowerCase.endsWith("m")) {124multiplier = "1048576"; //1024*1024125lowerCase = lowerCase.substring(0, lowerCase.length()-1);126} else if (lowerCase.endsWith("g")) {127multiplier = "1073741824"; //1024*1024*1024128lowerCase = lowerCase.substring(0, lowerCase.length()-1);129} else if (lowerCase.endsWith("t")) {130multiplier = "1099511627776"; //1024*1024*1024*1024131lowerCase = lowerCase.substring(0, lowerCase.length()-1);132}133BigDecimal valueBig = new BigDecimal(lowerCase);134BigDecimal multiplierBig = new BigDecimal(multiplier);135return fitsRange(optionName, valueBig.multiply(multiplierBig));136}137138public static String getMinOptionRange(String optionName) throws Exception {139JVMOption option;140String minRange = null;141142if (optionsAsMap == null) {143optionsAsMap = getOptionsWithRangeAsMap();144}145146option = optionsAsMap.get(optionName);147if (option != null) {148minRange = option.getMin();149}150151return minRange;152}153154public static String getMaxOptionRange(String optionName) throws Exception {155JVMOption option;156String maxRange = null;157158if (optionsAsMap == null) {159optionsAsMap = getOptionsWithRangeAsMap();160}161162option = optionsAsMap.get(optionName);163if (option != null) {164maxRange = option.getMax();165}166167return maxRange;168}169170/**171* Add dependency for option depending on it's name. E.g. enable G1 GC for172* G1 options or add prepend options to not hit constraints.173*174* @param option option175*/176private static void addNameDependency(JVMOption option) {177String name = option.getName();178179if (name.startsWith("G1")) {180option.addPrepend("-XX:+UseG1GC");181}182183if (name.startsWith("NUMA")) {184option.addPrepend("-XX:+UseNUMA");185}186187if (name.contains("JVMCI")) {188option.addPrepend("-XX:+EnableJVMCI");189}190191switch (name) {192case "MinHeapFreeRatio":193option.addPrepend("-XX:MaxHeapFreeRatio=100");194break;195case "MaxHeapFreeRatio":196option.addPrepend("-XX:MinHeapFreeRatio=0");197break;198case "MinMetaspaceFreeRatio":199option.addPrepend("-XX:MaxMetaspaceFreeRatio=100");200break;201case "MaxMetaspaceFreeRatio":202option.addPrepend("-XX:MinMetaspaceFreeRatio=0");203break;204case "G1RefProcDrainInterval":205option.addPrepend("-XX:+ExplicitGCInvokesConcurrent");206break;207case "InitialTenuringThreshold":208option.addPrepend("-XX:MaxTenuringThreshold=" + option.getMax());209break;210case "NUMAInterleaveGranularity":211option.addPrepend("-XX:+UseNUMAInterleaving");212break;213case "VerifyGCStartAt":214option.addPrepend("-XX:+VerifyBeforeGC");215option.addPrepend("-XX:+VerifyAfterGC");216break;217case "NewSizeThreadIncrease":218option.addPrepend("-XX:+UseSerialGC");219break;220case "SharedBaseAddress":221case "SharedSymbolTableBucketSize":222option.addPrepend("-XX:+UnlockDiagnosticVMOptions");223option.addPrepend("-XX:SharedArchiveFile=TestOptionsWithRanges.jsa");224option.addPrepend("-Xshare:dump");225break;226case "TLABWasteIncrement":227option.addPrepend("-XX:+UseParallelGC");228break;229case "BootstrapJVMCI":230case "PrintBootstrap":231case "JVMCIThreads":232case "JVMCIHostThreads":233option.addPrepend("-XX:+UseJVMCICompiler");234break;235default:236/* Do nothing */237break;238}239}240241/**242* Parse JVM Options. Get input from "inputReader". Parse using243* "-XX:+PrintFlagsRanges" output format.244*245* @param inputReader input data for parsing246* @param withRanges true if needed options with defined ranges inside JVM247* @param acceptOrigin predicate for option origins. Origins can be248* "product", "diagnostic" etc. Accept option only if acceptOrigin evaluates249* to true.250* @return map from option name to the JVMOption object251* @throws IOException if an error occurred while reading the data252*/253private static Map<String, JVMOption> getJVMOptions(Reader inputReader,254boolean withRanges, Predicate<String> acceptOrigin) throws IOException {255BufferedReader reader = new BufferedReader(inputReader);256String type;257String line;258String token;259String name;260StringTokenizer st;261JVMOption option;262Map<String, JVMOption> allOptions = new LinkedHashMap<>();263264// Skip first line265line = reader.readLine();266267while ((line = reader.readLine()) != null) {268/*269* Parse option from following line:270* <type> <name> [ <min, optional> ... <max, optional> ] {<origin>}271*/272st = new StringTokenizer(line);273274type = st.nextToken();275276name = st.nextToken();277278option = JVMOption.createVMOption(type, name);279280/* Skip '[' */281token = st.nextToken();282283/* Read min range or "..." if range is absent */284token = st.nextToken();285286if (token.equals("...") == false) {287if (!withRanges) {288/*289* Option have range, but asked for options without290* ranges => skip it291*/292continue;293}294295/* Mark this option as option which range is defined in VM */296option.optionWithRange();297298option.setMin(token);299300/* Read "..." and skip it */301token = st.nextToken();302303/* Get max value */304token = st.nextToken();305option.setMax(token);306} else if (withRanges) {307/*308* Option not have range, but asked for options with309* ranges => skip it310*/311continue;312}313314/* Skip ']' */315token = st.nextToken();316317/* Read origin of the option */318token = st.nextToken();319320while (st.hasMoreTokens()) {321token += st.nextToken();322};323token = token.substring(1, token.indexOf("}"));324325if (acceptOrigin.test(token)) {326addNameDependency(option);327328allOptions.put(name, option);329}330}331332return allOptions;333}334335static void failedMessage(String optionName, String value, boolean valid, String message) {336String temp;337338if (valid) {339temp = "valid";340} else {341temp = "invalid";342}343344failedMessage(String.format("Error processing option %s with %s value '%s'! %s",345optionName, temp, value, message));346}347348static void failedMessage(String message) {349System.err.println("TEST FAILED: " + message);350finalFailedMessage.append(String.format("(%s)%n", message));351}352353static void printOutputContent(OutputAnalyzer output) {354System.err.println(String.format("stdout content[%s]", output.getStdout()));355System.err.println(String.format("stderr content[%s]%n", output.getStderr()));356}357358/**359* Return string with accumulated failure messages360*361* @return string with accumulated failure messages362*/363public static String getMessageWithFailures() {364return finalFailedMessage.toString();365}366367/**368* Run command line tests for options passed in the list369*370* @param options list of options to test371* @return number of failed tests372* @throws Exception if java process can not be started373*/374public static int runCommandLineTests(List<? extends JVMOption> options) throws Exception {375int failed = 0;376377for (JVMOption option : options) {378failed += option.testCommandLine();379}380381return failed;382}383384/**385* Test passed options using DynamicVMOption isValidValue and isInvalidValue386* methods. Only tests writeable options.387*388* @param options list of options to test389* @return number of failed tests390*/391public static int runDynamicTests(List<? extends JVMOption> options) {392int failed = 0;393394for (JVMOption option : options) {395failed += option.testDynamic();396}397398return failed;399}400401/**402* Test passed options using Jcmd. Only tests writeable options.403*404* @param options list of options to test405* @return number of failed tests406*/407public static int runJcmdTests(List<? extends JVMOption> options) {408int failed = 0;409410for (JVMOption option : options) {411failed += option.testJcmd();412}413414return failed;415}416417/**418* Test passed option using attach method. Only tests writeable options.419*420* @param options list of options to test421* @return number of failed tests422* @throws Exception if an error occurred while attaching to the target JVM423*/424public static int runAttachTests(List<? extends JVMOption> options) throws Exception {425int failed = 0;426427for (JVMOption option : options) {428failed += option.testAttach();429}430431return failed;432}433434/**435* Get JVM options as map. Can return options with defined ranges or options436* without range depending on "withRanges" argument. "acceptOrigin"437* predicate can be used to filter option origin.438*439* @param withRanges true if needed options with defined ranges inside JVM440* @param acceptOrigin predicate for option origins. Origins can be441* "product", "diagnostic" etc. Accept option only if acceptOrigin evaluates442* to true.443* @param additionalArgs additional arguments to the Java process which ran444* with "-XX:+PrintFlagsRanges"445* @return map from option name to the JVMOption object446* @throws Exception if a new process can not be created or an error447* occurred while reading the data448*/449private static Map<String, JVMOption> getOptionsAsMap(boolean withRanges, Predicate<String> acceptOrigin,450String... additionalArgs) throws Exception {451Map<String, JVMOption> result;452Process p;453List<String> runJava = new ArrayList<>();454455if (additionalArgs.length > 0) {456runJava.addAll(Arrays.asList(additionalArgs));457}458459if (VMType != null) {460runJava.add(VMType);461}462463if (GCType != null) {464runJava.add(GCType);465}466runJava.add(UNLOCK_FLAG1);467runJava.add(UNLOCK_FLAG2);468runJava.add(PRINT_FLAGS_RANGES);469runJava.add("-version");470471p = ProcessTools.createJavaProcessBuilder(runJava).start();472473result = getJVMOptions(new InputStreamReader(p.getInputStream()), withRanges, acceptOrigin);474475p.waitFor();476477return result;478}479480/**481* Get JVM options as list. Can return options with defined ranges or482* options without range depending on "withRanges" argument. "acceptOrigin"483* predicate can be used to filter option origin.484*485* @param withRanges true if needed options with defined ranges inside JVM486* @param acceptOrigin predicate for option origins. Origins can be487* "product", "diagnostic" etc. Accept option only if acceptOrigin evaluates488* to true.489* @param additionalArgs additional arguments to the Java process which ran490* with "-XX:+PrintFlagsRanges"491* @return List of options492* @throws Exception if a new process can not be created or an error493* occurred while reading the data494*/495public static List<JVMOption> getOptions(boolean withRanges, Predicate<String> acceptOrigin,496String... additionalArgs) throws Exception {497return new ArrayList<>(getOptionsAsMap(withRanges, acceptOrigin, additionalArgs).values());498}499500/**501* Get JVM options with ranges as list. "acceptOrigin" predicate can be used502* to filter option origin.503*504* @param acceptOrigin predicate for option origins. Origins can be505* "product", "diagnostic" etc. Accept option only if acceptOrigin evaluates506* to true.507* @param additionalArgs additional arguments to the Java process which ran508* with "-XX:+PrintFlagsRanges"509* @return List of options510* @throws Exception if a new process can not be created or an error511* occurred while reading the data512*/513public static List<JVMOption> getOptionsWithRange(Predicate<String> acceptOrigin, String... additionalArgs) throws Exception {514return getOptions(true, acceptOrigin, additionalArgs);515}516517/**518* Get JVM options with ranges as list.519*520* @param additionalArgs additional arguments to the Java process which ran521* with "-XX:+PrintFlagsRanges"522* @return list of options523* @throws Exception if a new process can not be created or an error524* occurred while reading the data525*/526public static List<JVMOption> getOptionsWithRange(String... additionalArgs) throws Exception {527return getOptionsWithRange(origin -> true, additionalArgs);528}529530/**531* Get JVM options with range as map. "acceptOrigin" predicate can be used532* to filter option origin.533*534* @param acceptOrigin predicate for option origins. Origins can be535* "product", "diagnostic" etc. Accept option only if acceptOrigin evaluates536* to true.537* @param additionalArgs additional arguments to the Java process which ran538* with "-XX:+PrintFlagsRanges"539* @return Map from option name to the JVMOption object540* @throws Exception if a new process can not be created or an error541* occurred while reading the data542*/543public static Map<String, JVMOption> getOptionsWithRangeAsMap(Predicate<String> acceptOrigin, String... additionalArgs) throws Exception {544return getOptionsAsMap(true, acceptOrigin, additionalArgs);545}546547/**548* Get JVM options with range as map549*550* @param additionalArgs additional arguments to the Java process which ran551* with "-XX:+PrintFlagsRanges"552* @return map from option name to the JVMOption object553* @throws Exception if a new process can not be created or an error554* occurred while reading the data555*/556public static Map<String, JVMOption> getOptionsWithRangeAsMap(String... additionalArgs) throws Exception {557return getOptionsWithRangeAsMap(origin -> true, additionalArgs);558}559}560561562