1 /*
  2  * Copyright (c) 2013, 2023, 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 /*
 25  * @test
 26  * @bug 8024927
 27  * @summary Testing address of compressed class pointer space as best as possible.
 28  * @requires vm.bits == 64 & !vm.graal.enabled
 29  * @requires vm.flagless
 30  * @comment Testing compressed class pointers without compressed oops is not possible
 31  *          on MacOS because the heap is given an arbitrary address that occasionally
 32  *          collides with where we would ideally have placed the compressed class space.
 33  * @requires os.family != "mac"
 34  * @library /test/lib
 35  * @modules java.base/jdk.internal.misc
 36  *          java.management
 37  * @run driver CompressedClassPointers
 38  */
 39 
 40 import jdk.test.lib.Platform;
 41 import jdk.test.lib.process.ProcessTools;
 42 import jdk.test.lib.process.OutputAnalyzer;
 43 import jtreg.SkippedException;
 44 
 45 public class CompressedClassPointers {
 46 
 47     static final String logging_option = "-Xlog:gc+metaspace=trace,metaspace=info,cds=trace";
 48     static final String reserveCCSAnywhere = "Reserving compressed class space anywhere";
 49 
 50     // Returns true if we are to test the narrow klass base; we only do this on
 51     // platforms where we can be reasonably shure that we get reproducable placement).
 52     static boolean testNarrowKlassBase() {
 53         if (Platform.isWindows()) {
 54             return false;
 55         }
 56         return true;
 57 
 58     }
 59 
 60     // Returns true if the output indicates that the ccs is reserved anywhere.
 61     static boolean isCCSReservedAnywhere(OutputAnalyzer output) {
 62         if (output.getOutput().contains(reserveCCSAnywhere)) {
 63             return true;
 64         } else {
 65             return false;
 66         }
 67     }
 68 
 69     // CDS off, small heap, ccs size default (1G)
 70     // A small heap should allow us to place the ccs within the lower 32G and thus allow zero based encoding.
 71     public static void smallHeapTest() throws Exception {
 72         ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
 73             "-XX:+UnlockDiagnosticVMOptions",
 74             "-XX:SharedBaseAddress=8g",
 75             "-Xmx128m",
 76             logging_option,
 77             "-Xshare:off",
 78             "-XX:+VerifyBeforeGC", "-version");
 79         OutputAnalyzer output = new OutputAnalyzer(pb.start());
 80         if (testNarrowKlassBase() && !isCCSReservedAnywhere(output)) {
 81             output.shouldContain("Narrow klass base: 0x0000000000000000");
 82         }
 83         output.shouldHaveExitValue(0);
 84     }
 85 
 86     // CDS off, small heap, ccs size explicitely set to 1G
 87     // A small heap should allow us to place the ccs within the lower 32G and thus allow zero based encoding.
 88     public static void smallHeapTestWith1G() throws Exception {
 89         ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
 90             "-XX:+UnlockDiagnosticVMOptions",
 91             "-XX:CompressedClassSpaceSize=1g",
 92             "-Xmx128m",
 93             logging_option,
 94             "-Xshare:off",
 95             "-XX:+VerifyBeforeGC", "-version");
 96         OutputAnalyzer output = new OutputAnalyzer(pb.start());
 97         if (testNarrowKlassBase() && !isCCSReservedAnywhere(output)) {
 98             output.shouldContain("Narrow klass base: 0x0000000000000000, Narrow klass shift: 3");
 99         }
100         output.shouldHaveExitValue(0);
101     }
102 
103     // CDS off, a very large heap, ccs size left to 1G default.
104     // We expect the ccs to be mapped somewhere far beyond the heap, such that it is not possible
105     // to use zero based encoding.
106     public static void largeHeapTest() throws Exception {
107         ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
108             "-XX:+UnlockDiagnosticVMOptions",
109             "-XX:+UnlockExperimentalVMOptions",
110             "-Xmx30g",
111             logging_option,
112             "-Xshare:off",
113             "-XX:+VerifyBeforeGC", "-version");
114         OutputAnalyzer output = new OutputAnalyzer(pb.start());
115         if (testNarrowKlassBase() && !Platform.isPPC() && !Platform.isOSX() && !isCCSReservedAnywhere(output)) {
116             // PPC: in most cases the heap cannot be placed below 32g so there
117             // is room for ccs and narrow klass base will be 0x0. Exception:
118             // Linux 4.1.42 or earlier (see ELF_ET_DYN_BASE in JDK-8244847).
119             // For simplicity we exclude PPC.
120             // OSX: similar.
121             output.shouldNotContain("Narrow klass base: 0x0000000000000000");
122             output.shouldContain("Narrow klass shift: 0");
123         }
124         output.shouldHaveExitValue(0);
125     }
126 
127     // Settings as in largeHeapTest() except for max heap size. We make max heap
128     // size even larger such that it cannot fit into lower 32G but not too large
129     // for compressed oops.
130     // We expect a zerobased ccs.
131     public static void largeHeapAbove32GTest() throws Exception {
132         ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
133             "-XX:+UnlockDiagnosticVMOptions",
134             "-XX:+UnlockExperimentalVMOptions",
135             "-Xmx31g",
136             logging_option,
137             "-Xshare:off",
138             "-XX:+VerifyBeforeGC", "-version");
139         OutputAnalyzer output = new OutputAnalyzer(pb.start());
140         if (testNarrowKlassBase()) {
141             if (!(Platform.isAArch64() && Platform.isOSX())  && !isCCSReservedAnywhere(output)) { // see JDK-8262895
142                 output.shouldContain("Narrow klass base: 0x0000000000000000");
143                 if (!Platform.isAArch64() && !Platform.isPPC() && !Platform.isOSX()) {
144                     output.shouldContain("Narrow klass shift: 0");
145                 }
146             }
147         }
148         output.shouldHaveExitValue(0);
149     }
150 
151     // Using large paged heap, metaspace uses small pages.
152     public static void largePagesForHeapTest() throws Exception {
153         ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
154                 "-XX:+UnlockDiagnosticVMOptions",
155                 "-Xmx128m",
156                 "-XX:+UseLargePages",
157                 logging_option,
158                 "-XX:+VerifyBeforeGC", "-version");
159         OutputAnalyzer output = new OutputAnalyzer(pb.start());
160         if (testNarrowKlassBase()) {
161             output.shouldContain("Narrow klass base:");
162         }
163         output.shouldHaveExitValue(0);
164     }
165 
166     public static void heapBaseMinAddressTest() throws Exception {
167         ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
168             "-XX:HeapBaseMinAddress=1m",
169             "-Xlog:gc+heap+coops=debug",
170             "-version");
171         OutputAnalyzer output = new OutputAnalyzer(pb.start());
172         output.shouldContain("HeapBaseMinAddress must be at least");
173         output.shouldHaveExitValue(0);
174     }
175 
176     public static void sharingTest() throws Exception {
177         // Test small heaps
178         ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
179             "-XX:+UnlockDiagnosticVMOptions",
180             "-XX:SharedArchiveFile=./CompressedClassPointers.jsa",
181             "-Xmx128m",
182             "-XX:SharedBaseAddress=8g",
183             "-XX:+VerifyBeforeGC",
184             "-Xshare:dump",
185             "-Xlog:cds,gc+heap+coops=debug");
186         OutputAnalyzer output = new OutputAnalyzer(pb.start());
187         if (output.firstMatch("Shared spaces are not supported in this VM") != null) {
188             return;
189         }
190         try {
191           output.shouldContain("Loading classes to share");
192           output.shouldHaveExitValue(0);
193 
194           pb = ProcessTools.createLimitedTestJavaProcessBuilder(
195             "-XX:+UnlockDiagnosticVMOptions",
196             "-XX:SharedArchiveFile=./CompressedClassPointers.jsa",
197             "-Xmx128m",
198             "-XX:SharedBaseAddress=8g",
199             "-Xlog:gc+heap+coops=debug",
200             "-Xshare:on",
201             "-version");
202           output = new OutputAnalyzer(pb.start());
203           output.shouldContain("sharing");
204           output.shouldHaveExitValue(0);
205 
206         } catch (RuntimeException e) {
207           output.shouldContain("Unable to use shared archive");
208           output.shouldHaveExitValue(1);
209         }
210     }
211 
212     public static void smallHeapTestNoCoop() throws Exception {
213         ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
214             "-XX:-UseCompressedOops",
215             "-XX:+UseCompressedClassPointers",
216             "-XX:+UnlockDiagnosticVMOptions",
217             "-XX:SharedBaseAddress=8g",
218             "-Xmx128m",
219             "-Xlog:gc+metaspace=trace",
220             "-Xshare:off",
221             "-Xlog:cds=trace",
222             "-XX:+VerifyBeforeGC", "-version");
223         OutputAnalyzer output = new OutputAnalyzer(pb.start());
224         if (!isCCSReservedAnywhere(output)) {
225             output.shouldContain("Narrow klass base: 0x0000000000000000");
226         }
227         output.shouldHaveExitValue(0);
228     }
229 
230     public static void smallHeapTestWith1GNoCoop() throws Exception {
231         ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
232             "-XX:-UseCompressedOops",
233             "-XX:+UseCompressedClassPointers",
234             "-XX:+UnlockDiagnosticVMOptions",
235             "-XX:CompressedClassSpaceSize=1g",
236             "-Xmx128m",
237             "-Xlog:gc+metaspace=trace",
238             "-Xshare:off",
239             "-Xlog:cds=trace",
240             "-XX:+VerifyBeforeGC", "-version");
241         OutputAnalyzer output = new OutputAnalyzer(pb.start());
242         if (!isCCSReservedAnywhere(output)) {
243             output.shouldContain("Narrow klass base: 0x0000000000000000");
244         }
245         if (!Platform.isAArch64() && !Platform.isPPC()) {
246             // Currently relax this test for Aarch64 and ppc.
247             output.shouldContain("Narrow klass shift: 0");
248         }
249         output.shouldHaveExitValue(0);
250     }
251 
252     public static void largeHeapTestNoCoop() throws Exception {
253         ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
254             "-XX:-UseCompressedOops",
255             "-XX:+UseCompressedClassPointers",
256             "-XX:+UnlockDiagnosticVMOptions",
257             "-XX:+UnlockExperimentalVMOptions",
258             "-Xmx30g",
259             "-Xlog:gc+metaspace=trace",
260             "-Xshare:off",
261             "-Xlog:cds=trace",
262             "-XX:+VerifyBeforeGC", "-version");
263         OutputAnalyzer output = new OutputAnalyzer(pb.start());
264         if (!isCCSReservedAnywhere(output)) {
265             output.shouldContain("Narrow klass base: 0x0000000000000000");
266         }
267         if (!Platform.isAArch64() && !Platform.isPPC()) {
268             // Currently relax this test for Aarch64 and ppc.
269             output.shouldContain("Narrow klass shift: 0");
270         }
271         output.shouldHaveExitValue(0);
272     }
273 
274     public static void largePagesTestNoCoop() throws Exception {
275         ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
276             "-XX:-UseCompressedOops",
277             "-XX:+UseCompressedClassPointers",
278             "-XX:+UnlockDiagnosticVMOptions",
279             "-Xmx128m",
280             "-XX:+UseLargePages",
281             "-Xlog:gc+metaspace=trace",
282             "-XX:+VerifyBeforeGC", "-version");
283         OutputAnalyzer output = new OutputAnalyzer(pb.start());
284         output.shouldContain("Narrow klass base:");
285         output.shouldHaveExitValue(0);
286     }
287 
288     public static void heapBaseMinAddressTestNoCoop() throws Exception {
289         ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
290             "-XX:-UseCompressedOops",
291             "-XX:+UseCompressedClassPointers",
292             "-XX:HeapBaseMinAddress=1m",
293             "-Xlog:gc+heap+coops=debug",
294             "-version");
295         OutputAnalyzer output = new OutputAnalyzer(pb.start());
296         output.shouldContain("HeapBaseMinAddress must be at least");
297         output.shouldHaveExitValue(0);
298     }
299 
300     public static void sharingTestNoCoop() throws Exception {
301         // Test small heaps
302         ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(
303             "-XX:-UseCompressedOops",
304             "-XX:+UseCompressedClassPointers",
305             "-XX:+UnlockDiagnosticVMOptions",
306             "-XX:SharedArchiveFile=./CompressedClassPointers.jsa",
307             "-Xmx128m",
308             "-XX:SharedBaseAddress=8g",
309             "-XX:+VerifyBeforeGC",
310             "-Xshare:dump",
311             "-Xlog:cds,gc+heap+coops=debug");
312         OutputAnalyzer output = new OutputAnalyzer(pb.start());
313         if (output.firstMatch("Shared spaces are not supported in this VM") != null) {
314             return;
315         }
316         try {
317           output.shouldContain("Loading classes to share");
318           output.shouldHaveExitValue(0);
319 
320           pb = ProcessTools.createLimitedTestJavaProcessBuilder(
321             "-XX:-UseCompressedOops",
322             "-XX:+UseCompressedClassPointers",
323             "-XX:+UnlockDiagnosticVMOptions",
324             "-XX:SharedArchiveFile=./CompressedClassPointers.jsa",
325             "-Xmx128m",
326             "-XX:SharedBaseAddress=8g",
327             "-Xlog:gc+heap+coops=debug",
328             "-Xshare:on",
329             "-version");
330           output = new OutputAnalyzer(pb.start());
331           output.shouldContain("sharing");
332           output.shouldHaveExitValue(0);
333 
334         } catch (RuntimeException e) {
335           output.shouldContain("Unable to use shared archive");
336           output.shouldHaveExitValue(1);
337         }
338     }
339 
340     public static void main(String[] args) throws Exception {
341         smallHeapTest();
342         smallHeapTestWith1G();
343         largeHeapTest();
344         largeHeapAbove32GTest();
345         largePagesForHeapTest();
346         heapBaseMinAddressTest();
347         sharingTest();
348 
349         smallHeapTestNoCoop();
350         smallHeapTestWith1GNoCoop();
351         largeHeapTestNoCoop();
352         largePagesTestNoCoop();
353         heapBaseMinAddressTestNoCoop();
354         sharingTestNoCoop();
355     }
356 }