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