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