1 /*
  2  * Copyright (c) 2013, 2024, 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 jdk.test.whitebox.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     OutputAnalyzer output = GCArguments.executeLimitedTestJava(finalargs);
 97     output.shouldHaveExitValue(0);
 98     return output;
 99   }
100 
101   private static String[] join(String[] part1, String part2) {
102     ArrayList<String> result = new ArrayList<String>();
103     result.addAll(Arrays.asList(part1));
104     result.add(part2);
105     return result.toArray(String[]::new);
106   }
107 
108   public static void checkCompressedOopsErgo(String[] gcflags) throws Exception {
109     long maxHeapForCompressedOops = getMaxHeapForCompressedOops(gcflags);
110 
111     checkUseCompressedOops(gcflags, maxHeapForCompressedOops, true);
112     checkUseCompressedOops(gcflags, maxHeapForCompressedOops - 1, true);
113     checkUseCompressedOops(gcflags, maxHeapForCompressedOops + 1, false);
114 
115     // the use of HeapBaseMinAddress should not change the outcome
116     checkUseCompressedOops(join(gcflags, "-XX:HeapBaseMinAddress=32G"), maxHeapForCompressedOops, true);
117     checkUseCompressedOops(join(gcflags, "-XX:HeapBaseMinAddress=32G"), maxHeapForCompressedOops - 1, true);
118     checkUseCompressedOops(join(gcflags, "-XX:HeapBaseMinAddress=32G"), maxHeapForCompressedOops + 1, false);
119 
120     // use a different object alignment
121     maxHeapForCompressedOops = getMaxHeapForCompressedOops(join(gcflags, "-XX:ObjectAlignmentInBytes=16"));
122 
123     checkUseCompressedOops(join(gcflags, "-XX:ObjectAlignmentInBytes=16"), maxHeapForCompressedOops, true);
124     checkUseCompressedOops(join(gcflags, "-XX:ObjectAlignmentInBytes=16"), maxHeapForCompressedOops - 1, true);
125     checkUseCompressedOops(join(gcflags, "-XX:ObjectAlignmentInBytes=16"), maxHeapForCompressedOops + 1, false);
126 
127     // use a different CompressedClassSpaceSize
128     String compressedClassSpaceSizeArg = "-XX:CompressedClassSpaceSize=" + getCompressedClassSpaceSize() / 2;
129     maxHeapForCompressedOops = getMaxHeapForCompressedOops(join(gcflags, compressedClassSpaceSizeArg));
130 
131     checkUseCompressedOops(join(gcflags, compressedClassSpaceSizeArg), maxHeapForCompressedOops, true);
132     checkUseCompressedOops(join(gcflags, compressedClassSpaceSizeArg), maxHeapForCompressedOops - 1, true);
133     checkUseCompressedOops(join(gcflags, compressedClassSpaceSizeArg), maxHeapForCompressedOops + 1, false);
134   }
135 
136   private static void checkUseCompressedOops(String[] args, long heapsize, boolean expectUseCompressedOops) throws Exception {
137      ArrayList<String> finalargs = new ArrayList<String>();
138      finalargs.addAll(Arrays.asList(args));
139      finalargs.add("-Xmx" + heapsize);
140      finalargs.add("-XX:+PrintFlagsFinal");
141      finalargs.add("-version");
142 
143      String output = expectValid(finalargs.toArray(new String[0]));
144 
145      boolean actualUseCompressedOops = getFlagBoolValue(" UseCompressedOops", output);
146 
147      Asserts.assertEQ(expectUseCompressedOops, actualUseCompressedOops);
148   }
149 
150   private static boolean getFlagBoolValue(String flag, String where) {
151     Matcher m = Pattern.compile(flag + "\\s+:?= (true|false)").matcher(where);
152     if (!m.find()) {
153       throw new RuntimeException("Could not find value for flag " + flag + " in output string");
154     }
155     return m.group(1).equals("true");
156   }
157 
158   private static String expect(String[] flags, boolean hasWarning, boolean hasError, int errorcode) throws Exception {
159     OutputAnalyzer output = GCArguments.executeLimitedTestJava(flags);
160     output.shouldHaveExitValue(errorcode);
161     return output.getStdout();
162   }
163 
164   private static String expectValid(String[] flags) throws Exception {
165     return expect(flags, false, false, 0);
166   }
167 }