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 /** 272 * For all existing GC sets vm.gc.X property. 273 * Example vm.gc.G1=true means: 274 * VM supports G1 275 * User either set G1 explicitely (-XX:+UseG1GC) or did not set any GC 276 * 277 * @param map - property-value pairs 278 */ 279 protected void vmGC(SafeMap map) { 280 for (GC gc: GC.values()) { 281 map.put("vm.gc." + gc.name(), 282 () -> "" + (gc.isSupported() 283 && (gc.isSelected() || GC.isSelectedErgonomically()))); 284 } 285 } 286 287 /** 288 * Selected final flag. 289 * 290 * @param map - property-value pairs 291 * @param flagName - flag name 292 */ 293 private void vmOptFinalFlag(SafeMap map, String flagName) { 294 map.put("vm.opt.final." + flagName, 295 () -> String.valueOf(WB.getBooleanVMFlag(flagName))); 296 } 297 298 /** 299 * Selected sets of final flags. 300 * 301 * @param map - property-value pairs 302 */ 303 protected void vmOptFinalFlags(SafeMap map) { 304 vmOptFinalFlag(map, "ClassUnloading"); 305 vmOptFinalFlag(map, "ClassUnloadingWithConcurrentMark"); 306 vmOptFinalFlag(map, "UseCompressedOops"); 307 vmOptFinalFlag(map, "EnableJVMCI"); 308 vmOptFinalFlag(map, "EliminateAllocations"); 309 } 310 311 /** 312 * @return "true" if VM has a serviceability agent. 313 */ 314 protected String vmHasSA() { 315 return "" + Platform.hasSA(); 316 } 317 318 /** 319 * @return "true" if the VM is compiled with Java Flight Recorder (JFR) 320 * support. 321 */ 322 protected String vmHasJFR() { 323 return "" + WB.isJFRIncludedInVmBuild(); 324 } 325 326 /** 327 * @return true if compiler in use supports RTM and false otherwise. 328 */ 329 protected String vmRTMCompiler() { 330 boolean isRTMCompiler = false; 331 332 if (Compiler.isC2Enabled() && 333 (Platform.isX86() || Platform.isX64() || Platform.isPPC())) { 334 isRTMCompiler = true; 335 } 336 return "" + isRTMCompiler; 337 } 338 339 /** 340 * @return true if VM runs RTM supported CPU and false otherwise. 341 */ 342 protected String vmRTMCPU() { 343 return "" + CPUInfo.hasFeature("rtm"); 344 } 345 346 /** 347 * @return true if VM supports AOT and false otherwise 348 */ 349 protected String vmAOT() { 350 // builds with aot have jaotc in <JDK>/bin 351 Path bin = Paths.get(System.getProperty("java.home")) 352 .resolve("bin"); 353 Path jaotc; 354 if (Platform.isWindows()) { 355 jaotc = bin.resolve("jaotc.exe"); 356 } else { 357 jaotc = bin.resolve("jaotc"); 358 } 359 360 if (!Files.exists(jaotc)) { 361 // No jaotc => no AOT 362 return "false"; 363 } 364 365 switch (GC.selected()) { 366 case Serial: 367 case Parallel: 368 case G1: 369 // These GCs are supported with AOT 370 return "true"; 371 default: 372 break; 373 } 374 375 // Every other GC is not supported 376 return "false"; 377 } 378 379 /* 380 * @return true if there is at least one loaded AOT'ed library. 381 */ 382 protected String vmAotEnabled() { 383 return "" + (WB.aotLibrariesCount() > 0); 384 } 385 386 /** 387 * Check for CDS support. 388 * 389 * @return true if CDS is supported by the VM to be tested. 390 */ 391 protected String vmCDS() { 392 return "" + WB.isCDSIncludedInVmBuild(); 393 } 394 395 /** 396 * Check for CDS support for custom loaders. 397 * 398 * @return true if CDS provides support for customer loader in the VM to be tested. 399 */ 400 protected String vmCDSForCustomLoaders() { 401 return "" + ("true".equals(vmCDS()) && Platform.areCustomLoadersSupportedForCDS()); 402 } 403 404 /** 405 * Check for CDS support for archived Java heap regions. 406 * 407 * @return true if CDS provides support for archive Java heap regions in the VM to be tested. 408 */ 409 protected String vmCDSForArchivedJavaHeap() { 410 return "" + ("true".equals(vmCDS()) && WB.isJavaHeapArchiveSupported()); 411 } 412 413 /** 414 * Check if Graal is used as JIT compiler. 415 * 416 * @return true if Graal is used as JIT compiler. 417 */ 418 protected String isGraalEnabled() { 419 return "" + Compiler.isGraalEnabled(); 420 } 421 422 /** 423 * Check if Compiler1 is present. 424 * 425 * @return true if Compiler1 is used as JIT compiler, either alone or as part of the tiered system. 426 */ 427 protected String isCompiler1Enabled() { 428 return "" + Compiler.isC1Enabled(); 429 } 430 431 /** 432 * Check if Compiler2 is present. 433 * 434 * @return true if Compiler2 is used as JIT compiler, either alone or as part of the tiered system. 435 */ 436 protected String isCompiler2Enabled() { 437 return "" + Compiler.isC2Enabled(); 438 } 439 440 /** 441 * A simple check for docker support 442 * 443 * @return true if docker is supported in a given environment 444 */ 445 protected String dockerSupport() { 446 boolean isSupported = false; 447 if (Platform.isLinux()) { 448 // currently docker testing is only supported for Linux, 449 // on certain platforms 450 451 String arch = System.getProperty("os.arch"); 452 453 if (Platform.isX64()) { 454 isSupported = true; 455 } else if (Platform.isAArch64()) { 456 isSupported = true; 457 } else if (Platform.isS390x()) { 458 isSupported = true; 459 } else if (arch.equals("ppc64le")) { 460 isSupported = true; 461 } 462 } 463 464 if (isSupported) { 465 try { 466 isSupported = checkDockerSupport(); 467 } catch (Exception e) { 468 isSupported = false; 469 } 470 } 471 472 return "" + isSupported; 473 } 474 475 private boolean checkDockerSupport() throws IOException, InterruptedException { 476 ProcessBuilder pb = new ProcessBuilder(Container.ENGINE_COMMAND, "ps"); 477 Process p = pb.start(); 478 p.waitFor(10, TimeUnit.SECONDS); 479 480 return (p.exitValue() == 0); 481 } 482 483 private String implementor() { 484 try (InputStream in = new BufferedInputStream(new FileInputStream( 485 System.getProperty("java.home") + "/release"))) { 486 Properties properties = new Properties(); 487 properties.load(in); 488 String implementorProperty = properties.getProperty("IMPLEMENTOR"); 489 if (implementorProperty != null) { 490 return implementorProperty.replace("\"", ""); 491 } 492 return errorWithMessage("Can't get 'IMPLEMENTOR' property from 'release' file"); 493 } catch (IOException e) { 494 e.printStackTrace(); 495 return errorWithMessage("Failed to read 'release' file " + e); 496 } 497 } 498 499 private String isNvdimmTestEnabled() { 500 String isEnabled = System.getenv("TEST_VM_GC_NVDIMM"); 501 return "" + "true".equalsIgnoreCase(isEnabled); 502 } 503 504 /** 505 * Dumps the map to the file if the file name is given as the property. 506 * This functionality could be helpful to know context in the real 507 * execution. 508 * 509 * @param map 510 */ 511 protected static void dump(Map<String, String> map) { 512 String dumpFileName = System.getProperty("vmprops.dump"); 513 if (dumpFileName == null) { 514 return; 515 } 516 List<String> lines = new ArrayList<>(); 517 map.forEach((k, v) -> lines.add(k + ":" + v)); 518 try { 519 Files.write(Paths.get(dumpFileName), lines, 520 StandardOpenOption.APPEND, StandardOpenOption.CREATE); 521 } catch (IOException e) { 522 throw new RuntimeException("Failed to dump properties into '" 523 + dumpFileName + "'", e); 524 } 525 } 526 527 /** 528 * This method is for the testing purpose only. 529 * 530 * @param args 531 */ 532 public static void main(String args[]) { 533 Map<String, String> map = new VMProps().call(); 534 map.forEach((k, v) -> System.out.println(k + ": '" + v + "'")); 535 } 536 }