1 /*
   2  * Copyright (c) 2013, 2016, 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 /* @test TestLargePagesFlags
  25  * @summary Tests how large pages are choosen depending on the given large pages flag combinations.
  26  * @library /test/lib
  27  * @modules java.base/jdk.internal.misc
  28  *          java.management
  29  * @run main TestLargePagesFlags
  30  */
  31 
  32 import jdk.test.lib.process.OutputAnalyzer;
  33 import jdk.test.lib.Platform;
  34 import jdk.test.lib.process.ProcessTools;
  35 import java.util.ArrayList;
  36 
  37 public class TestLargePagesFlags {
  38 
  39   public static void main(String [] args) throws Exception {
  40     if (!Platform.isLinux()) {
  41       System.out.println("Skipping. TestLargePagesFlags has only been implemented for Linux.");
  42       return;
  43     }
  44 
  45     testUseTransparentHugePages();
  46     testUseHugeTLBFS();
  47     testUseSHM();
  48     testCombinations();
  49   }
  50 
  51   public static void testUseTransparentHugePages() throws Exception {
  52     if (!canUse(UseTransparentHugePages(true))) {
  53       System.out.println("Skipping testUseTransparentHugePages");
  54       return;
  55     }
  56 
  57     // -XX:-UseLargePages overrides all other flags.
  58     new FlagTester()
  59       .use(UseLargePages(false),
  60            UseTransparentHugePages(true))
  61       .expect(
  62            UseLargePages(false),
  63            UseTransparentHugePages(false),
  64            UseHugeTLBFS(false),
  65            UseSHM(false));
  66 
  67     // Explicitly turn on UseTransparentHugePages.
  68     new FlagTester()
  69       .use(UseTransparentHugePages(true))
  70       .expect(
  71            UseLargePages(true),
  72            UseTransparentHugePages(true),
  73            UseHugeTLBFS(false),
  74            UseSHM(false));
  75 
  76     new FlagTester()
  77       .use(UseLargePages(true),
  78            UseTransparentHugePages(true))
  79       .expect(
  80            UseLargePages(true),
  81            UseTransparentHugePages(true),
  82            UseHugeTLBFS(false),
  83            UseSHM(false));
  84 
  85     // Setting a specific large pages flag will turn
  86     // off heuristics to choose large pages type.
  87     new FlagTester()
  88       .use(UseLargePages(true),
  89            UseTransparentHugePages(false))
  90       .expect(
  91            UseLargePages(false),
  92            UseTransparentHugePages(false),
  93            UseHugeTLBFS(false),
  94            UseSHM(false));
  95 
  96     // Don't turn on UseTransparentHugePages
  97     // unless the user explicitly asks for them.
  98     new FlagTester()
  99       .use(UseLargePages(true))
 100       .expect(
 101            UseTransparentHugePages(false));
 102   }
 103 
 104   public static void testUseHugeTLBFS() throws Exception {
 105     if (!canUse(UseHugeTLBFS(true))) {
 106       System.out.println("Skipping testUseHugeTLBFS");
 107       return;
 108     }
 109 
 110     // -XX:-UseLargePages overrides all other flags.
 111     new FlagTester()
 112       .use(UseLargePages(false),
 113            UseHugeTLBFS(true))
 114       .expect(
 115            UseLargePages(false),
 116            UseTransparentHugePages(false),
 117            UseHugeTLBFS(false),
 118            UseSHM(false));
 119 
 120     // Explicitly turn on UseHugeTLBFS.
 121     new FlagTester()
 122       .use(UseHugeTLBFS(true))
 123       .expect(
 124            UseLargePages(true),
 125            UseTransparentHugePages(false),
 126            UseHugeTLBFS(true),
 127            UseSHM(false));
 128 
 129     new FlagTester()
 130       .use(UseLargePages(true),
 131            UseHugeTLBFS(true))
 132       .expect(
 133            UseLargePages(true),
 134            UseTransparentHugePages(false),
 135            UseHugeTLBFS(true),
 136            UseSHM(false));
 137 
 138     // Setting a specific large pages flag will turn
 139     // off heuristics to choose large pages type.
 140     new FlagTester()
 141       .use(UseLargePages(true),
 142            UseHugeTLBFS(false))
 143       .expect(
 144            UseLargePages(false),
 145            UseTransparentHugePages(false),
 146            UseHugeTLBFS(false),
 147            UseSHM(false));
 148 
 149     // Using UseLargePages will default to UseHugeTLBFS large pages.
 150     new FlagTester()
 151       .use(UseLargePages(true))
 152       .expect(
 153            UseLargePages(true),
 154            UseTransparentHugePages(false),
 155            UseHugeTLBFS(true),
 156            UseSHM(false));
 157   }
 158 
 159   public static void testUseSHM() throws Exception {
 160     if (!canUse(UseSHM(true))) {
 161       System.out.println("Skipping testUseSHM");
 162       return;
 163     }
 164 
 165     // -XX:-UseLargePages overrides all other flags.
 166     new FlagTester()
 167       .use(UseLargePages(false),
 168            UseSHM(true))
 169       .expect(
 170            UseLargePages(false),
 171            UseTransparentHugePages(false),
 172            UseHugeTLBFS(false),
 173            UseSHM(false));
 174 
 175     // Explicitly turn on UseSHM.
 176     new FlagTester()
 177       .use(UseSHM(true))
 178       .expect(
 179            UseLargePages(true),
 180            UseTransparentHugePages(false),
 181            UseHugeTLBFS(false),
 182            UseSHM(true)) ;
 183 
 184     new FlagTester()
 185       .use(UseLargePages(true),
 186            UseSHM(true))
 187       .expect(
 188            UseLargePages(true),
 189            UseTransparentHugePages(false),
 190            UseHugeTLBFS(false),
 191            UseSHM(true)) ;
 192 
 193     // Setting a specific large pages flag will turn
 194     // off heuristics to choose large pages type.
 195     new FlagTester()
 196       .use(UseLargePages(true),
 197            UseSHM(false))
 198       .expect(
 199            UseLargePages(false),
 200            UseTransparentHugePages(false),
 201            UseHugeTLBFS(false),
 202            UseSHM(false));
 203 
 204     // Setting UseLargePages can allow the system to choose
 205     // UseHugeTLBFS instead of UseSHM, but never UseTransparentHugePages.
 206     new FlagTester()
 207       .use(UseLargePages(true))
 208       .expect(
 209            UseLargePages(true),
 210            UseTransparentHugePages(false));
 211   }
 212 
 213   public static void testCombinations() throws Exception {
 214     if (!canUse(UseSHM(true)) || !canUse(UseHugeTLBFS(true))) {
 215       System.out.println("Skipping testUseHugeTLBFSAndUseSHMCombination");
 216       return;
 217     }
 218 
 219     // UseHugeTLBFS takes precedence over SHM.
 220 
 221     new FlagTester()
 222       .use(UseLargePages(true),
 223            UseHugeTLBFS(true),
 224            UseSHM(true))
 225       .expect(
 226            UseLargePages(true),
 227            UseTransparentHugePages(false),
 228            UseHugeTLBFS(true),
 229            UseSHM(false));
 230 
 231     new FlagTester()
 232       .use(UseLargePages(true),
 233            UseHugeTLBFS(false),
 234            UseSHM(true))
 235       .expect(
 236            UseLargePages(true),
 237            UseTransparentHugePages(false),
 238            UseHugeTLBFS(false),
 239            UseSHM(true));
 240 
 241     new FlagTester()
 242       .use(UseLargePages(true),
 243            UseHugeTLBFS(true),
 244            UseSHM(false))
 245       .expect(
 246            UseLargePages(true),
 247            UseTransparentHugePages(false),
 248            UseHugeTLBFS(true),
 249            UseSHM(false));
 250 
 251     new FlagTester()
 252       .use(UseLargePages(true),
 253            UseHugeTLBFS(false),
 254            UseSHM(false))
 255       .expect(
 256            UseLargePages(false),
 257            UseTransparentHugePages(false),
 258            UseHugeTLBFS(false),
 259            UseSHM(false));
 260 
 261 
 262     if (!canUse(UseTransparentHugePages(true))) {
 263       return;
 264     }
 265 
 266     // UseTransparentHugePages takes precedence.
 267 
 268     new FlagTester()
 269       .use(UseLargePages(true),
 270            UseTransparentHugePages(true),
 271            UseHugeTLBFS(true),
 272            UseSHM(true))
 273       .expect(
 274            UseLargePages(true),
 275            UseTransparentHugePages(true),
 276            UseHugeTLBFS(false),
 277            UseSHM(false));
 278 
 279     new FlagTester()
 280       .use(UseTransparentHugePages(true),
 281            UseHugeTLBFS(true),
 282            UseSHM(true))
 283       .expect(
 284            UseLargePages(true),
 285            UseTransparentHugePages(true),
 286            UseHugeTLBFS(false),
 287            UseSHM(false));
 288   }
 289 
 290   private static class FlagTester {
 291     private Flag [] useFlags;
 292 
 293     public FlagTester use(Flag... useFlags) {
 294       this.useFlags = useFlags;
 295       return this;
 296     }
 297 
 298     public void expect(Flag... expectedFlags) throws Exception {
 299       if (useFlags == null) {
 300         throw new IllegalStateException("Must run use() before expect()");
 301       }
 302 
 303       OutputAnalyzer output = executeNewJVM(useFlags);
 304 
 305       for (Flag flag : expectedFlags) {
 306         System.out.println("Looking for: " + flag.flagString());
 307         String strValue = output.firstMatch(".* " + flag.name() +  " .* :?= (\\S+).*", 1);
 308 
 309         if (strValue == null) {
 310           throw new RuntimeException("Flag " + flag.name() + " couldn't be found");
 311         }
 312 
 313         if (!flag.value().equals(strValue)) {
 314           throw new RuntimeException("Wrong value for: " + flag.name()
 315                                      + " expected: " + flag.value()
 316                                      + " got: " + strValue);
 317         }
 318       }
 319 
 320       output.shouldHaveExitValue(0);
 321     }
 322   }
 323 
 324   private static OutputAnalyzer executeNewJVM(Flag... flags) throws Exception {
 325     ArrayList<String> args = new ArrayList<>();
 326     for (Flag flag : flags) {
 327       args.add(flag.flagString());
 328     }
 329     args.add("-XX:+PrintFlagsFinal");
 330     args.add("-version");
 331 
 332     ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(args.toArray(new String[args.size()]));
 333     OutputAnalyzer output = new OutputAnalyzer(pb.start());
 334 
 335     return output;
 336   }
 337 
 338   private static boolean canUse(Flag flag) {
 339     try {
 340       new FlagTester().use(flag).expect(flag);
 341     } catch (Exception e) {
 342       return false;
 343     }
 344 
 345     return true;
 346   }
 347 
 348   private static Flag UseLargePages(boolean value) {
 349     return new BooleanFlag("UseLargePages", value);
 350   }
 351 
 352   private static Flag UseTransparentHugePages(boolean value) {
 353     return new BooleanFlag("UseTransparentHugePages", value);
 354   }
 355 
 356   private static Flag UseHugeTLBFS(boolean value) {
 357     return new BooleanFlag("UseHugeTLBFS", value);
 358   }
 359 
 360   private static Flag UseSHM(boolean value) {
 361     return new BooleanFlag("UseSHM", value);
 362   }
 363 
 364   private static class BooleanFlag implements Flag {
 365     private String name;
 366     private boolean value;
 367 
 368     BooleanFlag(String name, boolean value) {
 369       this.name = name;
 370       this.value = value;
 371     }
 372 
 373     public String flagString() {
 374       return "-XX:" + (value ? "+" : "-") + name;
 375     }
 376 
 377     public String name() {
 378       return name;
 379     }
 380 
 381     public String value() {
 382       return Boolean.toString(value);
 383     }
 384   }
 385 
 386   private static interface Flag {
 387     public String flagString();
 388     public String name();
 389     public String value();
 390   }
 391 }