1 /*
  2  * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 package gc.arguments;
 25 
 26 import com.sun.management.HotSpotDiagnosticMXBean;
 27 import com.sun.management.VMOption;
 28 
 29 import java.util.regex.Matcher;
 30 import java.util.regex.Pattern;
 31 import java.util.ArrayList;
 32 import java.util.Arrays;
 33 
 34 import jdk.test.lib.Asserts;
 35 import jdk.test.lib.process.OutputAnalyzer;
 36 import java.lang.management.ManagementFactory;
 37 import sun.hotspot.WhiteBox;
 38 
 39 class DetermineMaxHeapForCompressedOops {
 40   public static void main(String[] args) throws Exception {
 41     WhiteBox wb = WhiteBox.getWhiteBox();
 42     System.out.print(wb.getCompressedOopsMaxHeapSize());
 43   }
 44 }
 45 
 46 class TestUseCompressedOopsErgoTools {
 47 
 48   private static long getCompressedClassSpaceSize() {
 49     HotSpotDiagnosticMXBean diagnostic =
 50         ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class);
 51 
 52     VMOption option = diagnostic.getVMOption("CompressedClassSpaceSize");
 53     return Long.parseLong(option.getValue());
 54   }
 55 
 56 
 57   public static long getMaxHeapForCompressedOops(String[] vmargs) throws Exception {
 58     OutputAnalyzer output = runWhiteBoxTest(vmargs, DetermineMaxHeapForCompressedOops.class.getName(), new String[] {});
 59     return Long.parseLong(output.getStdout());
 60   }
 61 
 62   public static boolean is64bitVM() {
 63     String val = System.getProperty("sun.arch.data.model");
 64     if (val == null) {
 65       throw new RuntimeException("Could not read sun.arch.data.model");
 66     }
 67     if (val.equals("64")) {
 68       return true;
 69     } else if (val.equals("32")) {
 70       return false;
 71     }
 72     throw new RuntimeException("Unexpected value " + val + " of sun.arch.data.model");
 73   }
 74 
 75   /**
 76    * Executes a new VM process with the given class and parameters.
 77    * @param vmargs Arguments to the VM to run
 78    * @param classname Name of the class to run
 79    * @param arguments Arguments to the class
 80    * @return The OutputAnalyzer with the results for the invocation.
 81    */
 82   public static OutputAnalyzer runWhiteBoxTest(String[] vmargs, String classname, String[] arguments) throws Exception {
 83     ArrayList<String> finalargs = new ArrayList<String>();
 84 
 85     String[] whiteboxOpts = new String[] {
 86       "-Xbootclasspath/a:.",
 87       "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI",
 88       "-cp", System.getProperty("java.class.path"),
 89     };
 90 
 91     finalargs.addAll(Arrays.asList(vmargs));
 92     finalargs.addAll(Arrays.asList(whiteboxOpts));
 93     finalargs.add(classname);
 94     finalargs.addAll(Arrays.asList(arguments));
 95 
 96     ProcessBuilder pb = GCArguments.createJavaProcessBuilder(finalargs);
 97     OutputAnalyzer output = new OutputAnalyzer(pb.start());
 98     output.shouldHaveExitValue(0);
 99     return output;
100   }
101 
102   private static String[] join(String[] part1, String part2) {
103     ArrayList<String> result = new ArrayList<String>();
104     result.addAll(Arrays.asList(part1));
105     result.add(part2);
106     return result.toArray(String[]::new);
107   }
108 
109   public static void checkCompressedOopsErgo(String[] gcflags) throws Exception {
110     long maxHeapForCompressedOops = getMaxHeapForCompressedOops(gcflags);
111 
112     checkUseCompressedOops(gcflags, maxHeapForCompressedOops, true);
113     checkUseCompressedOops(gcflags, maxHeapForCompressedOops - 1, true);
114     checkUseCompressedOops(gcflags, maxHeapForCompressedOops + 1, false);
115 
116     // the use of HeapBaseMinAddress should not change the outcome
117     checkUseCompressedOops(join(gcflags, "-XX:HeapBaseMinAddress=32G"), maxHeapForCompressedOops, true);
118     checkUseCompressedOops(join(gcflags, "-XX:HeapBaseMinAddress=32G"), maxHeapForCompressedOops - 1, true);
119     checkUseCompressedOops(join(gcflags, "-XX:HeapBaseMinAddress=32G"), maxHeapForCompressedOops + 1, false);
120 
121     // use a different object alignment
122     maxHeapForCompressedOops = getMaxHeapForCompressedOops(join(gcflags, "-XX:ObjectAlignmentInBytes=16"));
123 
124     checkUseCompressedOops(join(gcflags, "-XX:ObjectAlignmentInBytes=16"), maxHeapForCompressedOops, true);
125     checkUseCompressedOops(join(gcflags, "-XX:ObjectAlignmentInBytes=16"), maxHeapForCompressedOops - 1, true);
126     checkUseCompressedOops(join(gcflags, "-XX:ObjectAlignmentInBytes=16"), maxHeapForCompressedOops + 1, false);
127 
128     // use a different CompressedClassSpaceSize
129     // Lilliput: do not assume a max. class space size, since that is subject to change. Instead, use a value slightly smaller
130     //  than what the parent VM runs with (which is the default size).
131     String compressedClassSpaceSizeArg = "-XX:CompressedClassSpaceSize=" + (getCompressedClassSpaceSize() - 1);
132     maxHeapForCompressedOops = getMaxHeapForCompressedOops(join(gcflags, compressedClassSpaceSizeArg));
133 
134     checkUseCompressedOops(join(gcflags, compressedClassSpaceSizeArg), maxHeapForCompressedOops, true);
135     checkUseCompressedOops(join(gcflags, compressedClassSpaceSizeArg), maxHeapForCompressedOops - 1, true);
136     checkUseCompressedOops(join(gcflags, compressedClassSpaceSizeArg), maxHeapForCompressedOops + 1, false);
137   }
138 
139   private static void checkUseCompressedOops(String[] args, long heapsize, boolean expectUseCompressedOops) throws Exception {
140      ArrayList<String> finalargs = new ArrayList<String>();
141      finalargs.addAll(Arrays.asList(args));
142      finalargs.add("-Xmx" + heapsize);
143      finalargs.add("-XX:+PrintFlagsFinal");
144      finalargs.add("-version");
145 
146      String output = expectValid(finalargs.toArray(new String[0]));
147 
148      boolean actualUseCompressedOops = getFlagBoolValue(" UseCompressedOops", output);
149 
150      Asserts.assertEQ(expectUseCompressedOops, actualUseCompressedOops);
151   }
152 
153   private static boolean getFlagBoolValue(String flag, String where) {
154     Matcher m = Pattern.compile(flag + "\\s+:?= (true|false)").matcher(where);
155     if (!m.find()) {
156       throw new RuntimeException("Could not find value for flag " + flag + " in output string");
157     }
158     return m.group(1).equals("true");
159   }
160 
161   private static String expect(String[] flags, boolean hasWarning, boolean hasError, int errorcode) throws Exception {
162     ProcessBuilder pb = GCArguments.createJavaProcessBuilder(flags);
163     OutputAnalyzer output = new OutputAnalyzer(pb.start());
164     output.shouldHaveExitValue(errorcode);
165     return output.getStdout();
166   }
167 
168   private static String expectValid(String[] flags) throws Exception {
169     return expect(flags, false, false, 0);
170   }
171 }