1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/test/gc/arguments/TestMaxHeapSizeTools.java Mon May 13 09:45:33 2013 +0200 1.3 @@ -0,0 +1,295 @@ 1.4 +/* 1.5 +* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 1.6 +* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 +* 1.8 +* This code is free software; you can redistribute it and/or modify it 1.9 +* under the terms of the GNU General Public License version 2 only, as 1.10 +* published by the Free Software Foundation. 1.11 +* 1.12 +* This code is distributed in the hope that it will be useful, but WITHOUT 1.13 +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.14 +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.15 +* version 2 for more details (a copy is included in the LICENSE file that 1.16 +* accompanied this code). 1.17 +* 1.18 +* You should have received a copy of the GNU General Public License version 1.19 +* 2 along with this work; if not, write to the Free Software Foundation, 1.20 +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.21 +* 1.22 +* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.23 +* or visit www.oracle.com if you need additional information or have any 1.24 +* questions. 1.25 +*/ 1.26 + 1.27 +import java.util.regex.Matcher; 1.28 +import java.util.regex.Pattern; 1.29 +import java.util.ArrayList; 1.30 +import java.util.Arrays; 1.31 + 1.32 +import com.oracle.java.testlibrary.*; 1.33 +import sun.hotspot.WhiteBox; 1.34 + 1.35 +class ErgoArgsPrinter { 1.36 + public static void main(String[] args) throws Exception { 1.37 + WhiteBox wb = WhiteBox.getWhiteBox(); 1.38 + wb.printHeapSizes(); 1.39 + } 1.40 +} 1.41 + 1.42 +final class MinInitialMaxValues { 1.43 + public long minHeapSize; 1.44 + public long initialHeapSize; 1.45 + public long maxHeapSize; 1.46 + 1.47 + public long minAlignment; 1.48 + public long maxAlignment; 1.49 +} 1.50 + 1.51 +class TestMaxHeapSizeTools { 1.52 + 1.53 + public static void checkMinInitialMaxHeapFlags(String gcflag) throws Exception { 1.54 + checkInvalidMinInitialHeapCombinations(gcflag); 1.55 + checkValidMinInitialHeapCombinations(gcflag); 1.56 + checkInvalidInitialMaxHeapCombinations(gcflag); 1.57 + checkValidInitialMaxHeapCombinations(gcflag); 1.58 + } 1.59 + 1.60 + public static void checkMinInitialErgonomics(String gcflag) throws Exception { 1.61 + // heap sizing ergonomics use the value NewSize + OldSize as default values 1.62 + // for ergonomics calculation. Retrieve these values. 1.63 + long[] values = new long[2]; 1.64 + getNewOldSize(gcflag, values); 1.65 + 1.66 + // we check cases with values smaller and larger than this default value. 1.67 + long newPlusOldSize = values[0] + values[1]; 1.68 + long smallValue = newPlusOldSize / 2; 1.69 + long largeValue = newPlusOldSize * 2; 1.70 + 1.71 + // -Xms is not set 1.72 + checkErgonomics(new String[] { gcflag, "-Xmx16M" }, values, -1, -1); 1.73 + checkErgonomics(new String[] { gcflag, "-Xmx16M", "-XX:InitialHeapSize=" + smallValue }, values, smallValue, smallValue); 1.74 + checkErgonomics(new String[] { gcflag, "-Xmx16M", "-XX:InitialHeapSize=" + largeValue }, values, -1, largeValue); 1.75 + checkErgonomics(new String[] { gcflag, "-Xmx16M", "-XX:InitialHeapSize=0" }, values, -1, -1); 1.76 + 1.77 + // -Xms is set to zero 1.78 + checkErgonomics(new String[] { gcflag, "-Xmx16M", "-Xms0" }, values, -1, -1); 1.79 + checkErgonomics(new String[] { gcflag, "-Xmx16M", "-Xms0", "-XX:InitialHeapSize=" + smallValue }, values, smallValue, smallValue); 1.80 + checkErgonomics(new String[] { gcflag, "-Xmx16M", "-Xms0", "-XX:InitialHeapSize=" + largeValue }, values, -1, largeValue); 1.81 + checkErgonomics(new String[] { gcflag, "-Xmx16M", "-Xms0", "-XX:InitialHeapSize=0" }, values, -1, -1); 1.82 + 1.83 + // -Xms is set to small value 1.84 + checkErgonomics(new String[] { gcflag, "-Xmx16M", "-Xms" + smallValue }, values, -1, -1); 1.85 + checkErgonomics(new String[] { gcflag, "-Xmx16M", "-Xms" + smallValue, "-XX:InitialHeapSize=" + smallValue }, values, smallValue, smallValue); 1.86 + checkErgonomics(new String[] { gcflag, "-Xmx16M", "-Xms" + smallValue, "-XX:InitialHeapSize=" + largeValue }, values, smallValue, largeValue); 1.87 + checkErgonomics(new String[] { gcflag, "-Xmx16M", "-Xms" + smallValue, "-XX:InitialHeapSize=0" }, values, smallValue, -1); 1.88 + 1.89 + // -Xms is set to large value 1.90 + checkErgonomics(new String[] { gcflag, "-Xmx16M", "-Xms" + largeValue }, values, largeValue, largeValue); 1.91 + // the next case has already been checked elsewhere and gives an error 1.92 + // checkErgonomics(new String[] { gcflag, "-Xmx16M", "-Xms" + largeValue, "-XX:InitialHeapSize=" + smallValue }, values, smallValue, smallValue); 1.93 + // the next case has already been checked elsewhere too 1.94 + // checkErgonomics(new String[] { gcflag, "-Xmx16M", "-Xms" + largeValue, "-XX:InitialHeapSize=" + largeValue }, values, values[0], largeValue); 1.95 + checkErgonomics(new String[] { gcflag, "-Xmx16M", "-Xms" + largeValue, "-XX:InitialHeapSize=0" }, values, largeValue, -1); 1.96 + } 1.97 + 1.98 + private static long align_up(long value, long alignment) { 1.99 + long alignmentMinusOne = alignment - 1; 1.100 + return (value + alignmentMinusOne) & ~alignmentMinusOne; 1.101 + } 1.102 + 1.103 + private static void getNewOldSize(String gcflag, long[] values) throws Exception { 1.104 + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(gcflag, 1.105 + "-XX:+PrintFlagsFinal", "-version"); 1.106 + OutputAnalyzer output = new OutputAnalyzer(pb.start()); 1.107 + output.shouldHaveExitValue(0); 1.108 + 1.109 + String stdout = output.getStdout(); 1.110 + values[0] = getFlagValue(" NewSize", stdout); 1.111 + values[1] = getFlagValue(" OldSize", stdout); 1.112 + } 1.113 + 1.114 + public static void checkGenMaxHeapErgo(String gcflag) throws Exception { 1.115 + TestMaxHeapSizeTools.checkGenMaxHeapSize(gcflag, 3); 1.116 + TestMaxHeapSizeTools.checkGenMaxHeapSize(gcflag, 4); 1.117 + TestMaxHeapSizeTools.checkGenMaxHeapSize(gcflag, 5); 1.118 + } 1.119 + 1.120 + private static void checkInvalidMinInitialHeapCombinations(String gcflag) throws Exception { 1.121 + expectError(new String[] { gcflag, "-Xms8M", "-XX:InitialHeapSize=4M", "-version" }); 1.122 + } 1.123 + 1.124 + private static void checkValidMinInitialHeapCombinations(String gcflag) throws Exception { 1.125 + expectValid(new String[] { gcflag, "-XX:InitialHeapSize=8M", "-Xms4M", "-version" }); 1.126 + expectValid(new String[] { gcflag, "-Xms4M", "-XX:InitialHeapSize=8M", "-version" }); 1.127 + expectValid(new String[] { gcflag, "-XX:InitialHeapSize=8M", "-Xms8M", "-version" }); 1.128 + // the following is not an error as -Xms sets both minimal and initial heap size 1.129 + expectValid(new String[] { gcflag, "-XX:InitialHeapSize=4M", "-Xms8M", "-version" }); 1.130 + } 1.131 + 1.132 + private static void checkInvalidInitialMaxHeapCombinations(String gcflag) throws Exception { 1.133 + expectError(new String[] { gcflag, "-XX:MaxHeapSize=4M", "-XX:InitialHeapSize=8M", "-version" }); 1.134 + expectError(new String[] { gcflag, "-XX:InitialHeapSize=8M", "-XX:MaxHeapSize=4M", "-version" }); 1.135 + } 1.136 + 1.137 + private static void checkValidInitialMaxHeapCombinations(String gcflag) throws Exception { 1.138 + expectValid(new String[] { gcflag, "-XX:InitialHeapSize=4M", "-XX:MaxHeapSize=8M", "-version" }); 1.139 + expectValid(new String[] { gcflag, "-XX:MaxHeapSize=8M", "-XX:InitialHeapSize=4M", "-version" }); 1.140 + expectValid(new String[] { gcflag, "-XX:MaxHeapSize=4M", "-XX:InitialHeapSize=4M", "-version" }); 1.141 + // a value of "0" for initial heap size means auto-detect 1.142 + expectValid(new String[] { gcflag, "-XX:MaxHeapSize=4M", "-XX:InitialHeapSize=0M", "-version" }); 1.143 + } 1.144 + 1.145 + private static long valueAfter(String source, String match) { 1.146 + int start = source.indexOf(match) + match.length(); 1.147 + String tail = source.substring(start).split(" ")[0]; 1.148 + return Long.parseLong(tail); 1.149 + } 1.150 + 1.151 + /** 1.152 + * Executes a new VM process with the given class and parameters. 1.153 + * @param vmargs Arguments to the VM to run 1.154 + * @param classname Name of the class to run 1.155 + * @param arguments Arguments to the class 1.156 + * @param useTestDotJavaDotOpts Use test.java.opts as part of the VM argument string 1.157 + * @return The OutputAnalyzer with the results for the invocation. 1.158 + */ 1.159 + public static OutputAnalyzer runWhiteBoxTest(String[] vmargs, String classname, String[] arguments, boolean useTestDotJavaDotOpts) throws Exception { 1.160 + ArrayList<String> finalargs = new ArrayList<String>(); 1.161 + 1.162 + String[] whiteboxOpts = new String[] { 1.163 + "-Xbootclasspath/a:.", 1.164 + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", 1.165 + "-cp", System.getProperty("java.class.path"), 1.166 + }; 1.167 + 1.168 + if (useTestDotJavaDotOpts) { 1.169 + // System.getProperty("test.java.opts") is '' if no options is set, 1.170 + // we need to skip such a result 1.171 + String[] externalVMOpts = new String[0]; 1.172 + if (System.getProperty("test.java.opts") != null && System.getProperty("test.java.opts").length() != 0) { 1.173 + externalVMOpts = System.getProperty("test.java.opts").split(" "); 1.174 + } 1.175 + finalargs.addAll(Arrays.asList(externalVMOpts)); 1.176 + } 1.177 + 1.178 + finalargs.addAll(Arrays.asList(vmargs)); 1.179 + finalargs.addAll(Arrays.asList(whiteboxOpts)); 1.180 + finalargs.add(classname); 1.181 + finalargs.addAll(Arrays.asList(arguments)); 1.182 + 1.183 + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(finalargs.toArray(new String[0])); 1.184 + OutputAnalyzer output = new OutputAnalyzer(pb.start()); 1.185 + output.shouldHaveExitValue(0); 1.186 + 1.187 + return output; 1.188 + } 1.189 + 1.190 + private static void getMinInitialMaxHeap(String[] args, MinInitialMaxValues val) throws Exception { 1.191 + OutputAnalyzer output = runWhiteBoxTest(args, ErgoArgsPrinter.class.getName(), new String[] {}, false); 1.192 + 1.193 + // the output we watch for has the following format: 1.194 + // 1.195 + // "Minimum heap X Initial heap Y Maximum heap Z Min alignment A Max Alignment B" 1.196 + // 1.197 + // where A, B, X, Y and Z are sizes in bytes. 1.198 + // Unfortunately there is no other way to retrieve the minimum heap size and 1.199 + // the alignments. 1.200 + 1.201 + Matcher m = Pattern.compile("Minimum heap \\d+ Initial heap \\d+ Maximum heap \\d+ Min alignment \\d+ Max alignment \\d+"). 1.202 + matcher(output.getStdout()); 1.203 + if (!m.find()) { 1.204 + throw new RuntimeException("Could not find heap size string."); 1.205 + } 1.206 + 1.207 + String match = m.group(); 1.208 + 1.209 + // actual values 1.210 + val.minHeapSize = valueAfter(match, "Minimum heap "); 1.211 + val.initialHeapSize = valueAfter(match, "Initial heap "); 1.212 + val.maxHeapSize = valueAfter(match, "Maximum heap "); 1.213 + val.minAlignment = valueAfter(match, "Min alignment "); 1.214 + val.maxAlignment = valueAfter(match, "Max alignment "); 1.215 + } 1.216 + 1.217 + /** 1.218 + * Verify whether the VM automatically synchronizes minimum and initial heap size if only 1.219 + * one is given for the GC specified. 1.220 + */ 1.221 + public static void checkErgonomics(String[] args, long[] newoldsize, 1.222 + long expectedMin, long expectedInitial) throws Exception { 1.223 + 1.224 + MinInitialMaxValues v = new MinInitialMaxValues(); 1.225 + getMinInitialMaxHeap(args, v); 1.226 + 1.227 + if ((expectedMin != -1) && (align_up(expectedMin, v.minAlignment) != v.minHeapSize)) { 1.228 + throw new RuntimeException("Actual minimum heap size of " + v.minHeapSize + 1.229 + " differs from expected minimum heap size of " + expectedMin); 1.230 + } 1.231 + 1.232 + if ((expectedInitial != -1) && (align_up(expectedInitial, v.minAlignment) != v.initialHeapSize)) { 1.233 + throw new RuntimeException("Actual initial heap size of " + v.initialHeapSize + 1.234 + " differs from expected initial heap size of " + expectedInitial); 1.235 + } 1.236 + 1.237 + // always check the invariant min <= initial <= max heap size 1.238 + if (!(v.minHeapSize <= v.initialHeapSize && v.initialHeapSize <= v.maxHeapSize)) { 1.239 + throw new RuntimeException("Inconsistent min/initial/max heap sizes, they are " + 1.240 + v.minHeapSize + "/" + v.initialHeapSize + "/" + v.maxHeapSize); 1.241 + } 1.242 + } 1.243 + 1.244 + /** 1.245 + * Verify whether the VM respects the given maximum heap size in MB for the 1.246 + * GC specified. 1.247 + * @param gcflag The garbage collector to test as command line flag. E.g. -XX:+UseG1GC 1.248 + * @param maxHeapSize the maximum heap size to verify, in MB. 1.249 + */ 1.250 + public static void checkGenMaxHeapSize(String gcflag, long maxHeapsize) throws Exception { 1.251 + final long K = 1024; 1.252 + 1.253 + MinInitialMaxValues v = new MinInitialMaxValues(); 1.254 + getMinInitialMaxHeap(new String[] { gcflag, "-XX:MaxHeapSize=" + maxHeapsize + "M" }, v); 1.255 + 1.256 + long expectedHeapSize = align_up(maxHeapsize * K * K, v.maxAlignment); 1.257 + long actualHeapSize = v.maxHeapSize; 1.258 + 1.259 + if (actualHeapSize > expectedHeapSize) { 1.260 + throw new RuntimeException("Heap has " + actualHeapSize + 1.261 + " bytes, expected to be less than " + expectedHeapSize); 1.262 + } 1.263 + } 1.264 + 1.265 + private static long getFlagValue(String flag, String where) { 1.266 + Matcher m = Pattern.compile(flag + "\\s+:?=\\s+\\d+").matcher(where); 1.267 + if (!m.find()) { 1.268 + throw new RuntimeException("Could not find value for flag " + flag + " in output string"); 1.269 + } 1.270 + String match = m.group(); 1.271 + return Long.parseLong(match.substring(match.lastIndexOf(" ") + 1, match.length())); 1.272 + } 1.273 + 1.274 + private static void shouldContainOrNot(OutputAnalyzer output, boolean contains, String message) throws Exception { 1.275 + if (contains) { 1.276 + output.shouldContain(message); 1.277 + } else { 1.278 + output.shouldNotContain(message); 1.279 + } 1.280 + } 1.281 + 1.282 + private static void expect(String[] flags, boolean hasWarning, boolean hasError, int errorcode) throws Exception { 1.283 + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(flags); 1.284 + OutputAnalyzer output = new OutputAnalyzer(pb.start()); 1.285 + shouldContainOrNot(output, hasWarning, "Warning"); 1.286 + shouldContainOrNot(output, hasError, "Error"); 1.287 + output.shouldHaveExitValue(errorcode); 1.288 + } 1.289 + 1.290 + private static void expectError(String[] flags) throws Exception { 1.291 + expect(flags, false, true, 1); 1.292 + } 1.293 + 1.294 + private static void expectValid(String[] flags) throws Exception { 1.295 + expect(flags, false, false, 0); 1.296 + } 1.297 +} 1.298 +