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