1 /*
   2  * Copyright (c) 2019, 2020, 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.g1;
  25 
  26 /*
  27  * @test TestLargePageUseForHeap.java
  28  * @summary Test that Java heap is allocated using large pages of the appropriate size if available.
  29  * @bug 8221517
  30  * @key gc
  31  * @modules java.base/jdk.internal.misc
  32  * @library /test/lib
  33  * @requires vm.gc.G1
  34  * @requires os.family != "solaris"
  35  * @build sun.hotspot.WhiteBox
  36  * @run driver ClassFileInstaller sun.hotspot.WhiteBox
  37  * @run main/othervm -Xbootclasspath/a:. -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
  38         -XX:+IgnoreUnrecognizedVMOptions -XX:+UseLargePages gc.g1.TestLargePageUseForHeap
  39  */
  40 
  41 import jdk.test.lib.process.OutputAnalyzer;
  42 import jdk.test.lib.process.ProcessTools;
  43 import jtreg.SkippedException;
  44 import sun.hotspot.WhiteBox;
  45 
  46 public class TestLargePageUseForHeap {
  47     static long largePageSize;
  48     static long smallPageSize;
  49 
  50     static void checkSize(OutputAnalyzer output, long expectedSize, String pattern) {
  51         String pageSizeStr = output.firstMatch(pattern, 1);
  52 
  53         if (pageSizeStr == null) {
  54             output.reportDiagnosticSummary();
  55             throw new RuntimeException("Match from '" + pattern + "' got 'null' expected: " + expectedSize);
  56         }
  57 
  58         long size = parseMemoryString(pageSizeStr);
  59         if (size != expectedSize) {
  60             output.reportDiagnosticSummary();
  61             throw new RuntimeException("Match from '" + pattern + "' got " + size + " expected: " + expectedSize);
  62         }
  63     }
  64 
  65     static boolean checkLargePageEnabled(OutputAnalyzer output) {
  66         // This message is printed when tried to reserve a memory with large page but it failed.
  67         String errorStr = "Reserve regular memory without large pages";
  68         String heapPattern = ".*Heap: ";
  69         // If errorStr is printed just before heap page log, reservation for Java Heap is failed.
  70         String result = output.firstMatch(errorStr + "\n" +
  71                                           "(?:.*Heap address: .*\n)?" // Heap address: 0x00000000f8000000, size: 128 MB, Compressed Oops mode: 32-bit
  72                                           + heapPattern);
  73         if (result != null) {
  74             return false;
  75         }
  76         return true;
  77     }
  78 
  79     static void checkHeap(OutputAnalyzer output, long expectedPageSize) throws Exception {
  80         checkSize(output, expectedPageSize, "Heap: .*page_size=([^ ]+)");
  81     }
  82 
  83     static void testVM(long regionSize) throws Exception {
  84         ProcessBuilder pb;
  85         // Test with large page enabled.
  86         pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC",
  87                                                    "-XX:G1HeapRegionSize=" + regionSize,
  88                                                    "-Xmx128m",
  89                                                    "-Xlog:pagesize,gc+heap+coops=debug",
  90                                                    "-XX:+UseLargePages",
  91                                                    "-version");
  92 
  93         OutputAnalyzer output = new OutputAnalyzer(pb.start());
  94         boolean largePageEnabled = checkLargePageEnabled(output);
  95         checkHeap(output, largePageEnabled ? largePageSize : smallPageSize);
  96         output.shouldHaveExitValue(0);
  97 
  98         // Test with large page disabled.
  99         pb = ProcessTools.createJavaProcessBuilder("-XX:+UseG1GC",
 100                                                    "-XX:G1HeapRegionSize=" + regionSize,
 101                                                    "-Xmx128m",
 102                                                    "-Xlog:pagesize,gc+heap+coops=debug",
 103                                                    "-XX:-UseLargePages",
 104                                                    "-version");
 105 
 106         output = new OutputAnalyzer(pb.start());
 107         checkHeap(output, smallPageSize);
 108         output.shouldHaveExitValue(0);
 109     }
 110 
 111     public static void main(String[] args) throws Exception {
 112         WhiteBox wb = WhiteBox.getWhiteBox();
 113         smallPageSize = wb.getVMPageSize();
 114         largePageSize = wb.getVMLargePageSize();
 115 
 116         if (largePageSize == 0) {
 117             throw new SkippedException("Large page support does not seem to be available on this platform.");
 118         }
 119         if (largePageSize == smallPageSize) {
 120             throw new SkippedException("Large page support does not seem to be available on this platform."
 121                     + "Small and large page size are the same.");
 122         }
 123 
 124         // G1HeapRegionSize=1MB
 125         testVM(1 * 1024 * 1024);
 126 
 127         // G1HeapRegionSize=2MB
 128         testVM(2 * 1024 * 1024);
 129 
 130         // G1HeapRegionSize=8MB
 131         testVM(8 * 1024 * 1024);
 132     }
 133 
 134     public static long parseMemoryString(String value) {
 135         long multiplier = 1;
 136 
 137         if (value.endsWith("B")) {
 138             multiplier = 1;
 139         } else if (value.endsWith("K")) {
 140             multiplier = 1024;
 141         } else if (value.endsWith("M")) {
 142             multiplier = 1024 * 1024;
 143         } else if (value.endsWith("G")) {
 144             multiplier = 1024 * 1024 * 1024;
 145         } else {
 146             throw new IllegalArgumentException("Expected memory string '" + value + "'to end with either of: B, K, M, G");
 147         }
 148 
 149         long longValue = Long.parseUnsignedLong(value.substring(0, value.length() - 1));
 150 
 151         return longValue * multiplier;
 152     }
 153 }