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