1 /* 2 * Copyright (c) 2016, 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 package requires; 24 25 import java.io.BufferedInputStream; 26 import java.io.FileInputStream; 27 import java.io.IOException; 28 import java.io.InputStream; 29 import java.nio.file.Files; 30 import java.nio.file.Path; 31 import java.nio.file.Paths; 32 import java.nio.file.StandardOpenOption; 33 import java.util.ArrayList; 34 import java.util.HashMap; 35 import java.util.List; 36 import java.util.Map; 37 import java.util.Properties; 38 import java.util.concurrent.Callable; 39 import java.util.concurrent.TimeUnit; 40 import java.util.function.Supplier; 41 import java.util.regex.Matcher; 42 import java.util.regex.Pattern; 43 44 import sun.hotspot.code.Compiler; 45 import sun.hotspot.cpuinfo.CPUInfo; 46 import sun.hotspot.gc.GC; 47 import sun.hotspot.WhiteBox; 48 import jdk.test.lib.Platform; 49 import jdk.test.lib.Container; 50 51 /** 52 * The Class to be invoked by jtreg prior Test Suite execution to 53 * collect information about VM. 54 * Do not use any APIs that may not be available in all target VMs. 55 * Properties set by this Class will be available in the @requires expressions. 56 */ 57 public class VMProps implements Callable<Map<String, String>> { 58 // value known to jtreg as an indicator of error state 59 private static final String ERROR_STATE = "__ERROR__"; 60 61 private static final WhiteBox WB = WhiteBox.getWhiteBox(); 62 63 private static class SafeMap { 64 private final Map<String, String> map = new HashMap<>(); 65 66 public void put(String key, Supplier<String> s) { 67 String value; 68 try { 69 value = s.get(); 70 } catch (Throwable t) { 71 System.err.println("failed to get value for " + key); 72 t.printStackTrace(System.err); 73 value = ERROR_STATE + t; 74 } 75 map.put(key, value); 76 } 77 } 78 79 /** 80 * Collects information about VM properties. 81 * This method will be invoked by jtreg. 82 * 83 * @return Map of property-value pairs. 84 */ 85 @Override 86 public Map<String, String> call() { 87 SafeMap map = new SafeMap(); 88 map.put("vm.flavor", this::vmFlavor); 89 map.put("vm.compMode", this::vmCompMode); 90 map.put("vm.bits", this::vmBits); 91 map.put("vm.flightRecorder", this::vmFlightRecorder); 92 map.put("vm.simpleArch", this::vmArch); 93 map.put("vm.debug", this::vmDebug); 94 map.put("vm.jvmci", this::vmJvmci); 95 map.put("vm.emulatedClient", this::vmEmulatedClient); 96 // vm.hasSA is "true" if the VM contains the serviceability agent 97 // and jhsdb. 98 map.put("vm.hasSA", this::vmHasSA); 99 // vm.hasJFR is "true" if JFR is included in the build of the VM and 100 // so tests can be executed. 101 map.put("vm.hasJFR", this::vmHasJFR); 102 map.put("vm.cpu.features", this::cpuFeatures); 103 map.put("vm.rtm.cpu", this::vmRTMCPU); 104 map.put("vm.rtm.compiler", this::vmRTMCompiler); 105 map.put("vm.aot", this::vmAOT); 106 map.put("vm.aot.enabled", this::vmAotEnabled); 107 // vm.cds is true if the VM is compiled with cds support. 108 map.put("vm.cds", this::vmCDS); 109 map.put("vm.cds.custom.loaders", this::vmCDSForCustomLoaders); 110 map.put("vm.cds.archived.java.heap", this::vmCDSForArchivedJavaHeap); 111 // vm.graal.enabled is true if Graal is used as JIT 112 map.put("vm.graal.enabled", this::isGraalEnabled); 113 map.put("vm.compiler1.enabled", this::isCompiler1Enabled); 114 map.put("vm.compiler2.enabled", this::isCompiler2Enabled); 115 map.put("docker.support", this::dockerSupport); 116 map.put("release.implementor", this::implementor); 117 map.put("test.vm.gc.nvdimm", this::isNvdimmTestEnabled); 118 vmGC(map); // vm.gc.X = true/false 119 vmOptFinalFlags(map); 120 121 dump(map.map); 122 return map.map; 123 } 124 125 /** 126 * Print a stack trace before returning error state; 127 * Used by the various helper functions which parse information from 128 * VM properties in the case where they don't find an expected property 129 * or a property doesn't conform to an expected format. 130 * 131 * @return {@link #ERROR_STATE} 132 */ 133 private String errorWithMessage(String message) { 134 new Exception(message).printStackTrace(); 135 return ERROR_STATE + message; 136 } 137 138 /** 139 * @return vm.simpleArch value of "os.simpleArch" property of tested JDK. 140 */ 141 protected String vmArch() { 142 String arch = System.getProperty("os.arch"); 143 if (arch.equals("x86_64") || arch.equals("amd64")) { 144 return "x64"; 145 } else if (arch.contains("86")) { 146 return "x86"; 147 } else { 148 return arch; 149 } 150 } 151 152 /** 153 * @return VM type value extracted from the "java.vm.name" property. 154 */ 155 protected String vmFlavor() { 156 // E.g. "Java HotSpot(TM) 64-Bit Server VM" 157 String vmName = System.getProperty("java.vm.name"); 158 if (vmName == null) { 159 return errorWithMessage("Can't get 'java.vm.name' property"); 160 } 161 162 Pattern startP = Pattern.compile(".* (\\S+) VM"); 163 Matcher m = startP.matcher(vmName); 164 if (m.matches()) { 165 return m.group(1).toLowerCase(); 166 } 167 return errorWithMessage("Can't get VM flavor from 'java.vm.name'"); 168 } 169 170 /** 171 * @return VM compilation mode extracted from the "java.vm.info" property. 172 */ 173 protected String vmCompMode() { 174 // E.g. "mixed mode" 175 String vmInfo = System.getProperty("java.vm.info"); 176 if (vmInfo == null) { 177 return errorWithMessage("Can't get 'java.vm.info' property"); 178 } 179 vmInfo = vmInfo.toLowerCase(); 180 if (vmInfo.contains("mixed mode")) { 181 return "Xmixed"; 182 } else if (vmInfo.contains("compiled mode")) { 183 return "Xcomp"; 184 } else if (vmInfo.contains("interpreted mode")) { 185 return "Xint"; 186 } else { 187 return errorWithMessage("Can't get compilation mode from 'java.vm.info'"); 188 } 189 } 190 191 /** 192 * @return VM bitness, the value of the "sun.arch.data.model" property. 193 */ 194 protected String vmBits() { 195 String dataModel = System.getProperty("sun.arch.data.model"); 196 if (dataModel != null) { 197 return dataModel; 198 } else { 199 return errorWithMessage("Can't get 'sun.arch.data.model' property"); 200 } 201 } 202 203 /** 204 * @return "true" if Flight Recorder is enabled, "false" if is disabled. 205 */ 206 protected String vmFlightRecorder() { 207 Boolean isFlightRecorder = WB.getBooleanVMFlag("FlightRecorder"); 208 String startFROptions = WB.getStringVMFlag("StartFlightRecording"); 209 if (isFlightRecorder != null && isFlightRecorder) { 210 return "true"; 211 } 212 if (startFROptions != null && !startFROptions.isEmpty()) { 213 return "true"; 214 } 215 return "false"; 216 } 217 218 /** 219 * @return debug level value extracted from the "jdk.debug" property. 220 */ 221 protected String vmDebug() { 222 String debug = System.getProperty("jdk.debug"); 223 if (debug != null) { 224 return "" + debug.contains("debug"); 225 } else { 226 return errorWithMessage("Can't get 'jdk.debug' property"); 227 } 228 } 229 230 /** 231 * @return true if VM supports JVMCI and false otherwise 232 */ 233 protected String vmJvmci() { 234 // builds with jvmci have this flag 235 if (WB.getBooleanVMFlag("EnableJVMCI") == null) { 236 return "false"; 237 } 238 239 switch (GC.selected()) { 240 case Serial: 241 case Parallel: 242 case G1: 243 // These GCs are supported with JVMCI 244 return "true"; 245 default: 246 break; 247 } 248 249 // Every other GC is not supported 250 return "false"; 251 } 252 253 /** 254 * @return true if VM runs in emulated-client mode and false otherwise. 255 */ 256 protected String vmEmulatedClient() { 257 String vmInfo = System.getProperty("java.vm.info"); 258 if (vmInfo == null) { 259 return errorWithMessage("Can't get 'java.vm.info' property"); 260 } 261 return "" + vmInfo.contains(" emulated-client"); 262 } 263 264 /** 265 * @return supported CPU features 266 */ 267 protected String cpuFeatures() { 268 return CPUInfo.getFeatures().toString(); 269 } 270 271 private boolean isGcSupportedByGraal(GC gc) { 272 switch (gc) { 273 case Serial: 274 case Parallel: 275 case G1: 276 return true; 277 case Epsilon: 278 case Z: 279 case Shenandoah: 280 return false; 281 default: 282 throw new IllegalStateException("Unknown GC " + gc.name()); 283 } 284 } 285 286 /** 287 * For all existing GC sets vm.gc.X property. 288 * Example vm.gc.G1=true means: 289 * VM supports G1 290 * User either set G1 explicitely (-XX:+UseG1GC) or did not set any GC 291 * G1 can be selected, i.e. it doesn't conflict with other VM flags 292 * 293 * @param map - property-value pairs 294 */ 295 protected void vmGC(SafeMap map) { 296 var isGraalEnabled = Compiler.isGraalEnabled(); 297 for (GC gc: GC.values()) { 298 map.put("vm.gc." + gc.name(), 299 () -> "" + (gc.isSupported() 300 && (!isGraalEnabled || isGcSupportedByGraal(gc)) 301 && (gc.isSelected() || GC.isSelectedErgonomically()))); 302 } 303 } 304 305 306 /** 307 * Selected final flag. 308 * 309 * @param map - property-value pairs 310 * @param flagName - flag name 311 */ 312 private void vmOptFinalFlag(SafeMap map, String flagName) { 313 map.put("vm.opt.final." + flagName, 314 () -> String.valueOf(WB.getBooleanVMFlag(flagName))); 315 } 316 317 /** 318 * Selected sets of final flags. 319 * 320 * @param map - property-value pairs 321 */ 322 protected void vmOptFinalFlags(SafeMap map) { 323 vmOptFinalFlag(map, "ClassUnloading"); 324 vmOptFinalFlag(map, "ClassUnloadingWithConcurrentMark"); 325 vmOptFinalFlag(map, "UseCompressedOops"); 326 vmOptFinalFlag(map, "EnableJVMCI"); 327 vmOptFinalFlag(map, "EliminateAllocations"); 328 } 329 330 /** 331 * @return "true" if VM has a serviceability agent. 332 */ 333 protected String vmHasSA() { 334 return "" + Platform.hasSA(); 335 } 336 337 /** 338 * @return "true" if the VM is compiled with Java Flight Recorder (JFR) 339 * support. 340 */ 341 protected String vmHasJFR() { 342 return "" + WB.isJFRIncludedInVmBuild(); 343 } 344 345 /** 346 * @return true if compiler in use supports RTM and false otherwise. 347 */ 348 protected String vmRTMCompiler() { 349 boolean isRTMCompiler = false; 350 351 if (Compiler.isC2Enabled() && 352 (Platform.isX86() || Platform.isX64() || Platform.isPPC())) { 353 isRTMCompiler = true; 354 } 355 return "" + isRTMCompiler; 356 } 357 358 /** 359 * @return true if VM runs RTM supported CPU and false otherwise. 360 */ 361 protected String vmRTMCPU() { 362 return "" + CPUInfo.hasFeature("rtm"); 363 } 364 365 /** 366 * @return true if VM supports AOT and false otherwise 367 */ 368 protected String vmAOT() { 369 // builds with aot have jaotc in <JDK>/bin 370 Path bin = Paths.get(System.getProperty("java.home")) 371 .resolve("bin"); 372 Path jaotc; 373 if (Platform.isWindows()) { 374 jaotc = bin.resolve("jaotc.exe"); 375 } else { 376 jaotc = bin.resolve("jaotc"); 377 } 378 379 if (!Files.exists(jaotc)) { 380 // No jaotc => no AOT 381 return "false"; 382 } 383 384 switch (GC.selected()) { 385 case Serial: 386 case Parallel: 387 case G1: 388 // These GCs are supported with AOT 389 return "true"; 390 default: 391 break; 392 } 393 394 // Every other GC is not supported 395 return "false"; 396 } 397 398 /* 399 * @return true if there is at least one loaded AOT'ed library. 400 */ 401 protected String vmAotEnabled() { 402 return "" + (WB.aotLibrariesCount() > 0); 403 } 404 405 /** 406 * Check for CDS support. 407 * 408 * @return true if CDS is supported by the VM to be tested. 409 */ 410 protected String vmCDS() { 411 return "" + WB.isCDSIncludedInVmBuild(); 412 } 413 414 /** 415 * Check for CDS support for custom loaders. 416 * 417 * @return true if CDS provides support for customer loader in the VM to be tested. 418 */ 419 protected String vmCDSForCustomLoaders() { 420 return "" + ("true".equals(vmCDS()) && Platform.areCustomLoadersSupportedForCDS()); 421 } 422 423 /** 424 * Check for CDS support for archived Java heap regions. 425 * 426 * @return true if CDS provides support for archive Java heap regions in the VM to be tested. 427 */ 428 protected String vmCDSForArchivedJavaHeap() { 429 return "" + ("true".equals(vmCDS()) && WB.isJavaHeapArchiveSupported()); 430 } 431 432 /** 433 * Check if Graal is used as JIT compiler. 434 * 435 * @return true if Graal is used as JIT compiler. 436 */ 437 protected String isGraalEnabled() { 438 return "" + Compiler.isGraalEnabled(); 439 } 440 441 /** 442 * Check if Compiler1 is present. 443 * 444 * @return true if Compiler1 is used as JIT compiler, either alone or as part of the tiered system. 445 */ 446 protected String isCompiler1Enabled() { 447 return "" + Compiler.isC1Enabled(); 448 } 449 450 /** 451 * Check if Compiler2 is present. 452 * 453 * @return true if Compiler2 is used as JIT compiler, either alone or as part of the tiered system. 454 */ 455 protected String isCompiler2Enabled() { 456 return "" + Compiler.isC2Enabled(); 457 } 458 459 /** 460 * A simple check for docker support 461 * 462 * @return true if docker is supported in a given environment 463 */ 464 protected String dockerSupport() { 465 boolean isSupported = false; 466 if (Platform.isLinux()) { 467 // currently docker testing is only supported for Linux, 468 // on certain platforms 469 470 String arch = System.getProperty("os.arch"); 471 472 if (Platform.isX64()) { 473 isSupported = true; 474 } else if (Platform.isAArch64()) { 475 isSupported = true; 476 } else if (Platform.isS390x()) { 477 isSupported = true; 478 } else if (arch.equals("ppc64le")) { 479 isSupported = true; 480 } 481 } 482 483 if (isSupported) { 484 try { 485 isSupported = checkDockerSupport(); 486 } catch (Exception e) { 487 isSupported = false; 488 } 489 } 490 491 return "" + isSupported; 492 } 493 494 private boolean checkDockerSupport() throws IOException, InterruptedException { 495 ProcessBuilder pb = new ProcessBuilder(Container.ENGINE_COMMAND, "ps"); 496 Process p = pb.start(); 497 p.waitFor(10, TimeUnit.SECONDS); 498 499 return (p.exitValue() == 0); 500 } 501 502 private String implementor() { 503 try (InputStream in = new BufferedInputStream(new FileInputStream( 504 System.getProperty("java.home") + "/release"))) { 505 Properties properties = new Properties(); 506 properties.load(in); 507 String implementorProperty = properties.getProperty("IMPLEMENTOR"); 508 if (implementorProperty != null) { 509 return implementorProperty.replace("\"", ""); 510 } 511 return errorWithMessage("Can't get 'IMPLEMENTOR' property from 'release' file"); 512 } catch (IOException e) { 513 e.printStackTrace(); 514 return errorWithMessage("Failed to read 'release' file " + e); 515 } 516 } 517 518 private String isNvdimmTestEnabled() { 519 String isEnabled = System.getenv("TEST_VM_GC_NVDIMM"); 520 return "" + "true".equalsIgnoreCase(isEnabled); 521 } 522 523 /** 524 * Dumps the map to the file if the file name is given as the property. 525 * This functionality could be helpful to know context in the real 526 * execution. 527 * 528 * @param map 529 */ 530 protected static void dump(Map<String, String> map) { 531 String dumpFileName = System.getProperty("vmprops.dump"); 532 if (dumpFileName == null) { 533 return; 534 } 535 List<String> lines = new ArrayList<>(); 536 map.forEach((k, v) -> lines.add(k + ":" + v)); 537 try { 538 Files.write(Paths.get(dumpFileName), lines, 539 StandardOpenOption.APPEND, StandardOpenOption.CREATE); 540 } catch (IOException e) { 541 throw new RuntimeException("Failed to dump properties into '" 542 + dumpFileName + "'", e); 543 } 544 } 545 546 /** 547 * This method is for the testing purpose only. 548 * 549 * @param args 550 */ 551 public static void main(String args[]) { 552 Map<String, String> map = new VMProps().call(); 553 map.forEach((k, v) -> System.out.println(k + ": '" + v + "'")); 554 } 555 }