1/*
2 * Copyright (c) 2013, 2015, 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
24import com.sun.management.HotSpotDiagnosticMXBean;
25import com.sun.management.VMOption;
26
27import java.util.regex.Matcher;
28import java.util.regex.Pattern;
29import java.util.ArrayList;
30import java.util.Arrays;
31
32import jdk.test.lib.Asserts;
33import jdk.test.lib.process.ProcessTools;
34import jdk.test.lib.process.OutputAnalyzer;
35import java.lang.management.ManagementFactory;
36import sun.hotspot.WhiteBox;
37
38class DetermineMaxHeapForCompressedOops {
39  public static void main(String[] args) throws Exception {
40    WhiteBox wb = WhiteBox.getWhiteBox();
41    System.out.print(wb.getCompressedOopsMaxHeapSize());
42  }
43}
44
45class TestUseCompressedOopsErgoTools {
46
47  private static long getCompressedClassSpaceSize() {
48    HotSpotDiagnosticMXBean diagnostic =
49        ManagementFactory.getPlatformMXBean(HotSpotDiagnosticMXBean.class);
50
51    VMOption option = diagnostic.getVMOption("CompressedClassSpaceSize");
52    return Long.parseLong(option.getValue());
53  }
54
55
56  public static long getMaxHeapForCompressedOops(String[] vmargs) throws Exception {
57    OutputAnalyzer output = runWhiteBoxTest(vmargs, DetermineMaxHeapForCompressedOops.class.getName(), new String[] {}, false);
58    return Long.parseLong(output.getStdout());
59  }
60
61  public static boolean is64bitVM() {
62    String val = System.getProperty("sun.arch.data.model");
63    if (val == null) {
64      throw new RuntimeException("Could not read sun.arch.data.model");
65    }
66    if (val.equals("64")) {
67      return true;
68    } else if (val.equals("32")) {
69      return false;
70    }
71    throw new RuntimeException("Unexpected value " + val + " of sun.arch.data.model");
72  }
73
74  /**
75   * Executes a new VM process with the given class and parameters.
76   * @param vmargs Arguments to the VM to run
77   * @param classname Name of the class to run
78   * @param arguments Arguments to the class
79   * @param useTestDotJavaDotOpts Use test.java.opts as part of the VM argument string
80   * @return The OutputAnalyzer with the results for the invocation.
81   */
82  public static OutputAnalyzer runWhiteBoxTest(String[] vmargs, String classname, String[] arguments, boolean useTestDotJavaDotOpts) 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    if (useTestDotJavaDotOpts) {
92      // System.getProperty("test.java.opts") is '' if no options is set,
93      // we need to skip such a result
94      String[] externalVMOpts = new String[0];
95      if (System.getProperty("test.java.opts") != null && System.getProperty("test.java.opts").length() != 0) {
96        externalVMOpts = System.getProperty("test.java.opts").split(" ");
97      }
98      finalargs.addAll(Arrays.asList(externalVMOpts));
99    }
100
101    finalargs.addAll(Arrays.asList(vmargs));
102    finalargs.addAll(Arrays.asList(whiteboxOpts));
103    finalargs.add(classname);
104    finalargs.addAll(Arrays.asList(arguments));
105
106    ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(finalargs.toArray(new String[0]));
107    OutputAnalyzer output = new OutputAnalyzer(pb.start());
108    output.shouldHaveExitValue(0);
109    return output;
110  }
111
112  private static String[] join(String[] part1, String part2) {
113    ArrayList<String> result = new ArrayList<String>();
114    result.addAll(Arrays.asList(part1));
115    result.add(part2);
116    return result.toArray(new String[0]);
117  }
118
119  public static void checkCompressedOopsErgo(String[] gcflags) throws Exception {
120    long maxHeapForCompressedOops = getMaxHeapForCompressedOops(gcflags);
121
122    checkUseCompressedOops(gcflags, maxHeapForCompressedOops, true);
123    checkUseCompressedOops(gcflags, maxHeapForCompressedOops - 1, true);
124    checkUseCompressedOops(gcflags, maxHeapForCompressedOops + 1, false);
125
126    // the use of HeapBaseMinAddress should not change the outcome
127    checkUseCompressedOops(join(gcflags, "-XX:HeapBaseMinAddress=32G"), maxHeapForCompressedOops, true);
128    checkUseCompressedOops(join(gcflags, "-XX:HeapBaseMinAddress=32G"), maxHeapForCompressedOops - 1, true);
129    checkUseCompressedOops(join(gcflags, "-XX:HeapBaseMinAddress=32G"), maxHeapForCompressedOops + 1, false);
130
131    // use a different object alignment
132    maxHeapForCompressedOops = getMaxHeapForCompressedOops(join(gcflags, "-XX:ObjectAlignmentInBytes=16"));
133
134    checkUseCompressedOops(join(gcflags, "-XX:ObjectAlignmentInBytes=16"), maxHeapForCompressedOops, true);
135    checkUseCompressedOops(join(gcflags, "-XX:ObjectAlignmentInBytes=16"), maxHeapForCompressedOops - 1, true);
136    checkUseCompressedOops(join(gcflags, "-XX:ObjectAlignmentInBytes=16"), maxHeapForCompressedOops + 1, false);
137
138    // use a different CompressedClassSpaceSize
139    String compressedClassSpaceSizeArg = "-XX:CompressedClassSpaceSize=" + 2 * getCompressedClassSpaceSize();
140    maxHeapForCompressedOops = getMaxHeapForCompressedOops(join(gcflags, compressedClassSpaceSizeArg));
141
142    checkUseCompressedOops(join(gcflags, compressedClassSpaceSizeArg), maxHeapForCompressedOops, true);
143    checkUseCompressedOops(join(gcflags, compressedClassSpaceSizeArg), maxHeapForCompressedOops - 1, true);
144    checkUseCompressedOops(join(gcflags, compressedClassSpaceSizeArg), maxHeapForCompressedOops + 1, false);
145  }
146
147  private static void checkUseCompressedOops(String[] args, long heapsize, boolean expectUseCompressedOops) throws Exception {
148     ArrayList<String> finalargs = new ArrayList<String>();
149     finalargs.addAll(Arrays.asList(args));
150     finalargs.add("-Xmx" + heapsize);
151     finalargs.add("-XX:+PrintFlagsFinal");
152     finalargs.add("-version");
153
154     String output = expectValid(finalargs.toArray(new String[0]));
155
156     boolean actualUseCompressedOops = getFlagBoolValue(" UseCompressedOops", output);
157
158     Asserts.assertEQ(expectUseCompressedOops, actualUseCompressedOops);
159  }
160
161  private static boolean getFlagBoolValue(String flag, String where) {
162    Matcher m = Pattern.compile(flag + "\\s+:?= (true|false)").matcher(where);
163    if (!m.find()) {
164      throw new RuntimeException("Could not find value for flag " + flag + " in output string");
165    }
166    return m.group(1).equals("true");
167  }
168
169  private static String expect(String[] flags, boolean hasWarning, boolean hasError, int errorcode) throws Exception {
170    ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(flags);
171    OutputAnalyzer output = new OutputAnalyzer(pb.start());
172    output.shouldHaveExitValue(errorcode);
173    return output.getStdout();
174  }
175
176  private static String expectValid(String[] flags) throws Exception {
177    return expect(flags, false, false, 0);
178  }
179}
180